QuestManager.java 14.2 KB
Newer Older
Melledy's avatar
Melledy committed
1
2
package emu.grasscutter.game.quest;

akatatsu27's avatar
akatatsu27 committed
3
import java.beans.Transient;
Akka's avatar
Akka committed
4
import java.util.*;
Melledy's avatar
Melledy committed
5
import java.util.function.Consumer;
Akka's avatar
Akka committed
6
import emu.grasscutter.Grasscutter;
Melledy's avatar
Melledy committed
7
import emu.grasscutter.data.GameData;
Akka's avatar
Akka committed
8
import emu.grasscutter.data.binout.MainQuestData;
Melledy's avatar
Melledy committed
9
10
import emu.grasscutter.data.excels.QuestData;
import emu.grasscutter.data.excels.QuestData.QuestCondition;
Melledy's avatar
Melledy committed
11
import emu.grasscutter.database.DatabaseHelper;
Melledy's avatar
Melledy committed
12
import emu.grasscutter.game.player.BasePlayerManager;
Melledy's avatar
Melledy committed
13
import emu.grasscutter.game.player.Player;
Akka's avatar
Akka committed
14
import emu.grasscutter.game.quest.enums.ParentQuestState;
Melledy's avatar
Melledy committed
15
import emu.grasscutter.game.quest.enums.QuestTrigger;
16
import emu.grasscutter.game.quest.enums.LogicType;
Melledy's avatar
Melledy committed
17
import emu.grasscutter.game.quest.enums.QuestState;
Akka's avatar
Akka committed
18
import emu.grasscutter.server.packet.send.*;
akatatsu27's avatar
akatatsu27 committed
19
import emu.grasscutter.utils.Position;
Melledy's avatar
Melledy committed
20
21
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
akatatsu27's avatar
akatatsu27 committed
22
23
import jdk.jshell.spi.ExecutionControl;
import lombok.Getter;
Melledy's avatar
Melledy committed
24

Melledy's avatar
Melledy committed
25
public class QuestManager extends BasePlayerManager {
github-actions's avatar
github-actions committed
26

github-actions's avatar
github-actions committed
27
    @Getter private final Player player;
akatatsu27's avatar
akatatsu27 committed
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
    @Getter private final Int2ObjectMap<GameMainQuest> mainQuests;
    @Getter private List<GameQuest> addToQuestListUpdateNotify;
    /*
        On SetPlayerBornDataReq, the server sends FinishedParentQuestNotify, with this exact
        parentQuestList. Captured on Game version 2.7
        Note: quest 40063 is already set to finished, with childQuest 4006406's state set to 3
    */

    private static Set<Integer> newPlayerMainQuests = Set.of(303,318,348,349,350,351,416,500,
        501,502,503,504,505,506,507,508,509,20000,20507,20509,21004,21005,21010,21011,21016,21017,
        21020,21021,21025,40063,70121,70124,70511,71010,71012,71013,71015,71016,71017,71555);

    /*
        On SetPlayerBornDataReq, the server sends ServerCondMeetQuestListUpdateNotify, with this exact
        addQuestIdList. Captured on Game version 2.7
        Total of 161...
     */
    /*
    private static Set<Integer> newPlayerServerCondMeetQuestListUpdateNotify = Set.of(3100101, 7104405, 2201601,
        7100801, 1907002, 7293301, 7193801, 7293401, 7193901, 7091001, 7190501, 7090901, 7190401, 7090801, 7190301,
        7195301, 7294801, 7195201, 7293001, 7094001, 7193501, 7293501, 7194001, 7293701, 7194201, 7194301, 7293801,
        7194901, 7194101, 7195001, 7294501, 7294101, 7194601, 7294301, 7194801, 7091301, 7290301, 2102401, 7216801,
        7190201, 7090701, 7093801, 7193301, 7292801, 7227828, 7093901, 7193401, 7292901, 7093701, 7193201, 7292701,
        7082402, 7093601, 7292601, 7193101, 2102301, 7093501, 7292501, 7193001, 7093401, 7292401, 7192901, 7093301,
        7292301, 7192801, 7294201, 7194701, 2100301, 7093201, 7212402, 7292201, 7192701, 7280001, 7293901, 7194401,
        7093101, 7212302, 7292101, 7192601, 7093001, 7292001, 7192501, 7216001, 7195101, 7294601, 2100900, 7092901,
        7291901, 7192401, 7092801, 7291801, 7192301, 2101501, 7092701, 7291701, 7192201, 7106401, 2100716, 7091801,
        7290801, 7191301, 7293201, 7193701, 7094201, 7294001, 7194501, 2102290, 7227829, 7193601, 7094101, 7091401,
        7290401, 7190901, 7106605, 7291601, 7192101, 7092601, 7291501, 7192001, 7092501, 7291401, 7191901, 7092401,
        7291301, 7191801, 7092301, 7211402, 7291201, 7191701, 7092201, 7291101, 7191601, 7092101, 7291001, 7191501,
        7092001, 7290901, 7191401, 7091901, 7290701, 7191201, 7091701, 7290601, 7191101, 7091601, 7290501, 7191001,
        7091501, 7290201, 7190701, 7091201, 7190601, 7091101, 7190101, 7090601, 7090501, 7090401, 7010701, 7090301,
        7090201, 7010103, 7090101
        );

    */
akatatsu27's avatar
akatatsu27 committed
64

github-actions's avatar
github-actions committed
65
        public static long getQuestKey(int mainQuestId) {
hartie95's avatar
hartie95 committed
66
67
68
        QuestEncryptionKey questEncryptionKey = GameData.getMainQuestEncryptionMap().get(mainQuestId);
        return questEncryptionKey != null ? questEncryptionKey.getEncryptionKey() : 0L;
    }
github-actions's avatar
github-actions committed
69
    public QuestManager(Player player) {
hartie95's avatar
hartie95 committed
70

github-actions's avatar
github-actions committed
71
        super(player);
github-actions's avatar
github-actions committed
72
73
        this.player = player;
        this.mainQuests = new Int2ObjectOpenHashMap<>();
akatatsu27's avatar
akatatsu27 committed
74
        this.addToQuestListUpdateNotify = new ArrayList<>();
github-actions's avatar
github-actions committed
75
    }
akatatsu27's avatar
akatatsu27 committed
76
77
78
79
80

    public void onNewPlayerCreate() {

        List<GameMainQuest> newQuests = this.addMultMainQuests(newPlayerMainQuests);
        //getPlayer().sendPacket(new PacketServerCondMeetQuestListUpdateNotify(newPlayerServerCondMeetQuestListUpdateNotify));
akatatsu27's avatar
akatatsu27 committed
81
        //getPlayer().sendPacket(new PacketFinishedParentQuestUpdateNotify(newQuests));
akatatsu27's avatar
akatatsu27 committed
82
83
84
85
86
87
88


    }

    public void onLogin() {

        List<GameMainQuest> activeQuests = getActiveMainQuests();
github-actions's avatar
github-actions committed
89
        for (GameMainQuest quest : activeQuests) {
akatatsu27's avatar
akatatsu27 committed
90
            List<Position> rewindPos = quest.rewind(); // <pos, rotation>
github-actions's avatar
github-actions committed
91
            if (rewindPos != null) {
akatatsu27's avatar
akatatsu27 committed
92
93
94
95
96
97
98
99
                getPlayer().getPosition().set(rewindPos.get(0));
                getPlayer().getRotation().set(rewindPos.get(1));
            }
        }
    }

    private List<GameMainQuest> addMultMainQuests(Set<Integer> mainQuestIds) {
        List<GameMainQuest> newQuests = new ArrayList<>();
github-actions's avatar
github-actions committed
100
        for (Integer id : mainQuestIds) {
akatatsu27's avatar
akatatsu27 committed
101
102
103
104
105
            getMainQuests().put(id.intValue(),new GameMainQuest(this.player, id));
            getMainQuestById(id).save();
            newQuests.add(getMainQuestById(id));
        }
        return newQuests;
github-actions's avatar
github-actions committed
106
107
    }

akatatsu27's avatar
akatatsu27 committed
108
109
110
111
    /*
        Looking through mainQuests 72201-72208 and 72174, we can infer that a questGlobalVar's default value is 0
    */
    public Integer getQuestGlobalVarValue(Integer variable) {
akatatsu27's avatar
akatatsu27 committed
112
        return getPlayer().getQuestGlobalVariables().getOrDefault(variable,0);
github-actions's avatar
github-actions committed
113
114
    }

akatatsu27's avatar
akatatsu27 committed
115
    public void setQuestGlobalVarValue(Integer variable, Integer value) {
akatatsu27's avatar
akatatsu27 committed
116
        Integer previousValue = getPlayer().getQuestGlobalVariables().put(variable,value);
akatatsu27's avatar
akatatsu27 committed
117
118
119
120
        Grasscutter.getLogger().debug("Changed questGlobalVar {} value from {} to {}", variable, previousValue==null ? 0: previousValue, value);
    }
    public void incQuestGlobalVarValue(Integer variable, Integer inc) {
        //
akatatsu27's avatar
akatatsu27 committed
121
122
        Integer previousValue = getPlayer().getQuestGlobalVariables().getOrDefault(variable,0);
        getPlayer().getQuestGlobalVariables().put(variable,previousValue + inc);
akatatsu27's avatar
akatatsu27 committed
123
124
125
126
127
        Grasscutter.getLogger().debug("Incremented questGlobalVar {} value from {} to {}", variable, previousValue, previousValue + inc);
    }
    //In MainQuest 998, dec is passed as a positive integer
    public void decQuestGlobalVarValue(Integer variable, Integer dec) {
        //
akatatsu27's avatar
akatatsu27 committed
128
129
        Integer previousValue = getPlayer().getQuestGlobalVariables().getOrDefault(variable,0);
        getPlayer().getQuestGlobalVariables().put(variable,previousValue - dec);
akatatsu27's avatar
akatatsu27 committed
130
        Grasscutter.getLogger().debug("Decremented questGlobalVar {} value from {} to {}", variable, previousValue, previousValue - dec);
github-actions's avatar
github-actions committed
131
132
    }

github-actions's avatar
github-actions committed
133
134
135
    public GameMainQuest getMainQuestById(int mainQuestId) {
        return getMainQuests().get(mainQuestId);
    }
akatatsu27's avatar
akatatsu27 committed
136

github-actions's avatar
github-actions committed
137
138
139
140
141
142
    public GameQuest getQuestById(int questId) {
        QuestData questConfig = GameData.getQuestDataMap().get(questId);
        if (questConfig == null) {
            return null;
        }

github-actions's avatar
github-actions committed
143
        GameMainQuest mainQuest = getMainQuests().get(questConfig.getMainId());
github-actions's avatar
github-actions committed
144
145
146
147
148
149
150
151

        if (mainQuest == null) {
            return null;
        }

        return mainQuest.getChildQuests().get(questId);
    }

github-actions's avatar
github-actions committed
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
    public void forEachQuest(Consumer<GameQuest> callback) {
        for (GameMainQuest mainQuest : getMainQuests().values()) {
            for (GameQuest quest : mainQuest.getChildQuests().values()) {
                callback.accept(quest);
            }
        }
    }

    public void forEachMainQuest(Consumer<GameMainQuest> callback) {
        for (GameMainQuest mainQuest : getMainQuests().values()) {
            callback.accept(mainQuest);
        }
    }

    // TODO
    public void forEachActiveQuest(Consumer<GameQuest> callback) {
        for (GameMainQuest mainQuest : getMainQuests().values()) {
            for (GameQuest quest : mainQuest.getChildQuests().values()) {
                if (quest.getState() != QuestState.QUEST_STATE_FINISHED) {
                    callback.accept(quest);
                }
            }
        }
    }

    public GameMainQuest addMainQuest(QuestData questConfig) {
        GameMainQuest mainQuest = new GameMainQuest(getPlayer(), questConfig.getMainId());
        getMainQuests().put(mainQuest.getParentQuestId(), mainQuest);
github-actions's avatar
github-actions committed
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202

        getPlayer().sendPacket(new PacketFinishedParentQuestUpdateNotify(mainQuest));

        return mainQuest;
    }

    public GameQuest addQuest(int questId) {
        QuestData questConfig = GameData.getQuestDataMap().get(questId);
        if (questConfig == null) {
            return null;
        }

        // Main quest
        GameMainQuest mainQuest = this.getMainQuestById(questConfig.getMainId());

        // Create main quest if it doesnt exist
        if (mainQuest == null) {
            mainQuest = addMainQuest(questConfig);
        }

        // Sub quest
        GameQuest quest = mainQuest.getChildQuestById(questId);

github-actions's avatar
github-actions committed
203
204
        // Forcefully start
        quest.start();
github-actions's avatar
github-actions committed
205
206
207
208

        // Save main quest
        mainQuest.save();

github-actions's avatar
github-actions committed
209
210
        // Send packet
        getPlayer().sendPacket(new PacketQuestListUpdateNotify(mainQuest.getChildQuests().values().stream()
akatatsu27's avatar
akatatsu27 committed
211
212
            .filter(p -> p.getState() != QuestState.QUEST_STATE_UNSTARTED)
            .toList()));
github-actions's avatar
github-actions committed
213
214
215
216

        return quest;
    }
    public void startMainQuest(int mainQuestId) {
Akka's avatar
Akka committed
217
218
        var mainQuestData = GameData.getMainQuestDataMap().get(mainQuestId);

github-actions's avatar
github-actions committed
219
        if (mainQuestData == null) {
Akka's avatar
Akka committed
220
221
222
223
224
225
226
227
228
229
230
231
            return;
        }

        Arrays.stream(mainQuestData.getSubQuests())
            .min(Comparator.comparingInt(MainQuestData.SubQuestData::getOrder))
            .map(MainQuestData.SubQuestData::getSubId)
            .ifPresent(this::addQuest);
    }
    public void triggerEvent(QuestTrigger condType, int... params) {
        triggerEvent(condType, "", params);
    }

akatatsu27's avatar
akatatsu27 committed
232
    //TODO
github-actions's avatar
github-actions committed
233
    public void triggerEvent(QuestTrigger condType, String paramStr, int... params) {
Akka's avatar
Akka committed
234
        Grasscutter.getLogger().debug("Trigger Event {}, {}, {}", condType, paramStr, params);
akatatsu27's avatar
akatatsu27 committed
235
236
237
        List<GameMainQuest> checkMainQuests = this.getMainQuests().values().stream()
            .filter(i -> i.getState() != ParentQuestState.PARENT_QUEST_STATE_FINISHED)
            .toList();
github-actions's avatar
github-actions committed
238
        switch (condType) {
akatatsu27's avatar
akatatsu27 committed
239
240
241
242
243
244
245
246
247
248
249
250
251
252
            //accept Conds
            case QUEST_COND_STATE_EQUAL:
            case QUEST_COND_STATE_NOT_EQUAL:
            case QUEST_COND_COMPLETE_TALK:
            case QUEST_COND_LUA_NOTIFY:
            case QUEST_COND_QUEST_VAR_EQUAL:
            case QUEST_COND_QUEST_VAR_GREATER:
            case QUEST_COND_QUEST_VAR_LESS:
            case QUEST_COND_PLAYER_LEVEL_EQUAL_GREATER:
            case QUEST_COND_QUEST_GLOBAL_VAR_EQUAL:
            case QUEST_COND_QUEST_GLOBAL_VAR_GREATER:
            case QUEST_COND_QUEST_GLOBAL_VAR_LESS:
                for (GameMainQuest mainquest : checkMainQuests) {
                    mainquest.tryAcceptSubQuests(condType, paramStr, params);
github-actions's avatar
github-actions committed
253
                }
akatatsu27's avatar
akatatsu27 committed
254
                break;
Akka's avatar
Akka committed
255

akatatsu27's avatar
akatatsu27 committed
256
257
258
259
            //fail Conds
            case QUEST_CONTENT_NOT_FINISH_PLOT:
                for (GameMainQuest mainquest : checkMainQuests) {
                    mainquest.tryFailSubQuests(condType, paramStr, params);
github-actions's avatar
github-actions committed
260
                }
akatatsu27's avatar
akatatsu27 committed
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
                break;
            //finish Conds
            case QUEST_CONTENT_COMPLETE_TALK:
            case QUEST_CONTENT_FINISH_PLOT:
            case QUEST_CONTENT_COMPLETE_ANY_TALK:
            case QUEST_CONTENT_LUA_NOTIFY:
            case QUEST_CONTENT_QUEST_VAR_EQUAL:
            case QUEST_CONTENT_QUEST_VAR_GREATER:
            case QUEST_CONTENT_QUEST_VAR_LESS:
            case QUEST_CONTENT_ENTER_DUNGEON:
            case QUEST_CONTENT_ENTER_ROOM:
            case QUEST_CONTENT_INTERACT_GADGET:
            case QUEST_CONTENT_TRIGGER_FIRE:
            case QUEST_CONTENT_UNLOCK_TRANS_POINT:
                for (GameMainQuest mainQuest : checkMainQuests) {
                    mainQuest.tryFinishSubQuests(condType, paramStr, params);
github-actions's avatar
github-actions committed
277
                }
akatatsu27's avatar
akatatsu27 committed
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
                break;

            //finish Or Fail Conds
            case QUEST_CONTENT_GAME_TIME_TICK:
            case QUEST_CONTENT_QUEST_STATE_EQUAL:
            case QUEST_CONTENT_ADD_QUEST_PROGRESS:
            case QUEST_CONTENT_LEAVE_SCENE:
                for (GameMainQuest mainQuest : checkMainQuests) {
                    mainQuest.tryFailSubQuests(condType, paramStr, params);
                    mainQuest.tryFinishSubQuests(condType, paramStr, params);
                }
                break;
            //QUEST_EXEC are handled directly by each subQuest

            //Unused
            case QUEST_CONTENT_QUEST_STATE_NOT_EQUAL:
            case QUEST_COND_PLAYER_CHOOSE_MALE:
            default:
                Grasscutter.getLogger().error("Unhandled QuestTrigger {}", condType);
github-actions's avatar
github-actions committed
297
        }
github-actions's avatar
github-actions committed
298
        if (this.addToQuestListUpdateNotify.size() != 0) {
akatatsu27's avatar
akatatsu27 committed
299
300
301
302
            this.getPlayer().getSession().send(new PacketQuestListUpdateNotify(this.addToQuestListUpdateNotify));
            this.addToQuestListUpdateNotify.clear();
        }

github-actions's avatar
github-actions committed
303
    }
Melledy's avatar
Melledy committed
304

Akka's avatar
Akka committed
305
    public List<QuestGroupSuite> getSceneGroupSuite(int sceneId) {
akatatsu27's avatar
akatatsu27 committed
306
        return getMainQuests().values().stream()
Akka's avatar
Akka committed
307
308
309
310
311
312
313
            .filter(i -> i.getState() != ParentQuestState.PARENT_QUEST_STATE_FINISHED)
            .map(GameMainQuest::getQuestGroupSuites)
            .filter(Objects::nonNull)
            .flatMap(Collection::stream)
            .filter(i -> i.getScene() == sceneId)
            .toList();
    }
github-actions's avatar
github-actions committed
314
315
    public void loadFromDatabase() {
        List<GameMainQuest> quests = DatabaseHelper.getAllQuests(getPlayer());
316
        
github-actions's avatar
github-actions committed
317
        for (GameMainQuest mainQuest : quests) {
318
            boolean cancelAdd = false;
github-actions's avatar
github-actions committed
319
320
            mainQuest.setOwner(this.getPlayer());

github-actions's avatar
github-actions committed
321
            for (GameQuest quest : mainQuest.getChildQuests().values()) {
322
323
324
325
326
327
328
329
                QuestData questConfig = GameData.getQuestDataMap().get(quest.getSubQuestId());
                
                if (questConfig == null) {
                    mainQuest.delete();
                    cancelAdd = true;
                    break;
                }
                
github-actions's avatar
github-actions committed
330
                quest.setMainQuest(mainQuest);
331
                quest.setConfig(questConfig);
github-actions's avatar
github-actions committed
332
            }
github-actions's avatar
github-actions committed
333

334
335
336
            if (!cancelAdd) {
                this.getMainQuests().put(mainQuest.getParentQuestId(), mainQuest);
            }
github-actions's avatar
github-actions committed
337
338
        }
    }
akatatsu27's avatar
akatatsu27 committed
339
340
341

    public List<GameMainQuest> getActiveMainQuests() {
        return getMainQuests().values().stream().filter(p -> !p.isFinished()).toList();
github-actions's avatar
github-actions committed
342
    }
Melledy's avatar
Melledy committed
343
}