mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2026-03-28 02:23:31 +01:00
Continue merging quests (pt. 2)
This commit is contained in:
@@ -16,7 +16,6 @@ import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.game.quest.enums.*;
|
||||
import emu.grasscutter.game.world.World;
|
||||
import emu.grasscutter.net.proto.ChildQuestOuterClass.ChildQuest;
|
||||
import emu.grasscutter.net.proto.ParentQuestOuterClass.ParentQuest;
|
||||
import emu.grasscutter.server.packet.send.PacketCodexDataUpdateNotify;
|
||||
@@ -41,7 +40,7 @@ public class GameMainQuest {
|
||||
@Getter private int[] questVars;
|
||||
@Getter private long[] timeVar;
|
||||
//QuestUpdateQuestVarReq is sent in two stages...
|
||||
@Getter private List<Integer> questVarsUpdate;
|
||||
private List<Integer> questVarsUpdate;
|
||||
@Getter private ParentQuestState state;
|
||||
@Getter private boolean isFinished;
|
||||
@Getter List<QuestGroupSuite> questGroupSuites;
|
||||
@@ -67,6 +66,13 @@ public class GameMainQuest {
|
||||
addAllChildQuests();
|
||||
}
|
||||
|
||||
public List<Integer> getQuestVarsUpdate() {
|
||||
if(questVarsUpdate == null){
|
||||
questVarsUpdate = new ArrayList<>();
|
||||
}
|
||||
return questVarsUpdate;
|
||||
}
|
||||
|
||||
private void addAllChildQuests() {
|
||||
List<Integer> subQuestIds = Arrays.stream(GameData.getMainQuestDataMap().get(this.parentQuestId).getSubQuests()).map(SubQuestData::getSubId).toList();
|
||||
for (Integer subQuestId : subQuestIds) {
|
||||
@@ -166,10 +172,10 @@ public class GameMainQuest {
|
||||
}
|
||||
|
||||
// handoff main quest
|
||||
if (mainQuestData.getSuggestTrackMainQuestList() != null) {
|
||||
Arrays.stream(mainQuestData.getSuggestTrackMainQuestList())
|
||||
.forEach(getQuestManager()::startMainQuest);
|
||||
}
|
||||
// if (mainQuestData.getSuggestTrackMainQuestList() != null) {
|
||||
// Arrays.stream(mainQuestData.getSuggestTrackMainQuestList())
|
||||
// .forEach(getQuestManager()::startMainQuest);
|
||||
// }
|
||||
}
|
||||
//TODO
|
||||
public void fail() {}
|
||||
@@ -181,9 +187,9 @@ public class GameMainQuest {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*if(rewindPositions.isEmpty()){
|
||||
addRewindPoints();
|
||||
}*/
|
||||
// if(rewindPositions.isEmpty()){
|
||||
// this.addRewindPoints();
|
||||
// }
|
||||
|
||||
List<Position> posAndRot = new ArrayList<>();
|
||||
if(hasRewindPosition(targetQuest.getSubQuestId(), posAndRot)){
|
||||
@@ -198,8 +204,8 @@ public class GameMainQuest {
|
||||
if (hasRewindPosition(quest.getSubQuestId(), posAndRot)) {
|
||||
return posAndRot;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,308 +1,312 @@
|
||||
package emu.grasscutter.game.quest;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Transient;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.ChapterData;
|
||||
import emu.grasscutter.data.excels.QuestData;
|
||||
import emu.grasscutter.data.excels.TriggerExcelConfigData;
|
||||
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.game.quest.enums.QuestCond;
|
||||
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||
import emu.grasscutter.game.quest.enums.QuestState;
|
||||
import emu.grasscutter.net.proto.ChapterStateOuterClass;
|
||||
import emu.grasscutter.net.proto.QuestOuterClass.Quest;
|
||||
import emu.grasscutter.scripts.data.SceneGroup;
|
||||
import emu.grasscutter.server.packet.send.PacketChapterStateNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketDelQuestNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketQuestListUpdateNotify;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.script.Bindings;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.val;
|
||||
|
||||
@Entity
|
||||
public class GameQuest {
|
||||
@Transient @Getter @Setter private GameMainQuest mainQuest;
|
||||
@Transient @Getter private QuestData questData;
|
||||
|
||||
@Getter private int subQuestId;
|
||||
@Getter private int mainQuestId;
|
||||
@Getter @Setter public QuestState state;
|
||||
|
||||
@Getter @Setter private int startTime;
|
||||
@Getter @Setter private int acceptTime;
|
||||
@Getter @Setter private int finishTime;
|
||||
|
||||
@Getter @Setter private long startGameDay;
|
||||
|
||||
@Getter private int[] finishProgressList;
|
||||
@Getter private int[] failProgressList;
|
||||
@Transient @Getter private Map<String, TriggerExcelConfigData> triggerData;
|
||||
@Getter private Map<String, Boolean> triggers;
|
||||
private transient Bindings bindings;
|
||||
|
||||
@Deprecated // Morphia only. Do not use.
|
||||
public GameQuest() {}
|
||||
|
||||
public GameQuest(GameMainQuest mainQuest, QuestData questData) {
|
||||
this.mainQuest = mainQuest;
|
||||
this.subQuestId = questData.getId();
|
||||
this.mainQuestId = questData.getMainId();
|
||||
this.questData = questData;
|
||||
this.state = QuestState.QUEST_STATE_UNSTARTED;
|
||||
this.triggerData = new HashMap<>();
|
||||
this.triggers = new HashMap<>();
|
||||
}
|
||||
|
||||
public void start() {
|
||||
clearProgress(false);
|
||||
this.acceptTime = Utils.getCurrentSeconds();
|
||||
this.startTime = this.acceptTime;
|
||||
this.startGameDay = getOwner().getWorld().getTotalGameTimeDays();
|
||||
this.state = QuestState.QUEST_STATE_UNFINISHED;
|
||||
val triggerCond =
|
||||
questData.getFinishCond().stream()
|
||||
.filter(p -> p.getType() == QuestContent.QUEST_CONTENT_TRIGGER_FIRE)
|
||||
.toList();
|
||||
if (triggerCond.size() > 0) {
|
||||
for (val cond : triggerCond) {
|
||||
TriggerExcelConfigData newTrigger =
|
||||
GameData.getTriggerExcelConfigDataMap().get(cond.getParam()[0]);
|
||||
if (newTrigger != null) {
|
||||
if (this.triggerData == null) {
|
||||
this.triggerData = new HashMap<>();
|
||||
}
|
||||
triggerData.put(newTrigger.getTriggerName(), newTrigger);
|
||||
triggers.put(newTrigger.getTriggerName(), false);
|
||||
SceneGroup group = SceneGroup.of(newTrigger.getGroupId()).load(newTrigger.getSceneId());
|
||||
getOwner()
|
||||
.getWorld()
|
||||
.getSceneById(newTrigger.getSceneId())
|
||||
.loadTriggerFromGroup(group, newTrigger.getTriggerName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getOwner().sendPacket(new PacketQuestListUpdateNotify(this));
|
||||
|
||||
if (ChapterData.beginQuestChapterMap.containsKey(subQuestId)) {
|
||||
getOwner()
|
||||
.sendPacket(
|
||||
new PacketChapterStateNotify(
|
||||
ChapterData.beginQuestChapterMap.get(subQuestId).getId(),
|
||||
ChapterStateOuterClass.ChapterState.CHAPTER_STATE_BEGIN));
|
||||
}
|
||||
|
||||
// Some subQuests and talks become active when some other subQuests are unfinished (even from
|
||||
// different MainQuests)
|
||||
getOwner()
|
||||
.getQuestManager()
|
||||
.queueEvent(
|
||||
QuestContent.QUEST_CONTENT_QUEST_STATE_EQUAL,
|
||||
getSubQuestId(),
|
||||
getState().getValue(),
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
getOwner()
|
||||
.getQuestManager()
|
||||
.queueEvent(
|
||||
QuestCond.QUEST_COND_STATE_EQUAL, getSubQuestId(), getState().getValue(), 0, 0, 0);
|
||||
|
||||
getQuestData()
|
||||
.getBeginExec()
|
||||
.forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
|
||||
getOwner().getQuestManager().checkQuestAlreadyFullfilled(this);
|
||||
|
||||
Grasscutter.getLogger().debug("Quest {} is started", subQuestId);
|
||||
}
|
||||
|
||||
public String getTriggerNameById(int id) {
|
||||
TriggerExcelConfigData trigger = GameData.getTriggerExcelConfigDataMap().get(id);
|
||||
if (trigger != null) {
|
||||
String triggerName = trigger.getTriggerName();
|
||||
return triggerName;
|
||||
}
|
||||
// return empty string if can't find trigger
|
||||
return "";
|
||||
}
|
||||
|
||||
public Player getOwner() {
|
||||
return this.getMainQuest().getOwner();
|
||||
}
|
||||
|
||||
public void setConfig(QuestData config) {
|
||||
if (config == null || getSubQuestId() != config.getId()) return;
|
||||
this.questData = config;
|
||||
}
|
||||
|
||||
public void setFinishProgress(int index, int value) {
|
||||
finishProgressList[index] = value;
|
||||
}
|
||||
|
||||
public void setFailProgress(int index, int value) {
|
||||
failProgressList[index] = value;
|
||||
}
|
||||
|
||||
public boolean clearProgress(boolean notifyDelete) {
|
||||
// TODO improve
|
||||
var oldState = state;
|
||||
if (questData.getFinishCond() != null && questData.getFinishCond().size() != 0) {
|
||||
this.finishProgressList = new int[questData.getFinishCond().size()];
|
||||
}
|
||||
|
||||
if (questData.getFailCond() != null && questData.getFailCond().size() != 0) {
|
||||
this.failProgressList = new int[questData.getFailCond().size()];
|
||||
}
|
||||
setState(QuestState.QUEST_STATE_UNSTARTED);
|
||||
finishTime = 0;
|
||||
acceptTime = 0;
|
||||
startTime = 0;
|
||||
if (oldState == QuestState.QUEST_STATE_UNSTARTED) {
|
||||
return false;
|
||||
}
|
||||
if (notifyDelete) {
|
||||
getOwner().sendPacket(new PacketDelQuestNotify(getSubQuestId()));
|
||||
}
|
||||
save();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
this.state = QuestState.QUEST_STATE_FINISHED;
|
||||
this.finishTime = Utils.getCurrentSeconds();
|
||||
|
||||
getOwner().sendPacket(new PacketQuestListUpdateNotify(this));
|
||||
|
||||
if (getQuestData().isFinishParent()) {
|
||||
// This quest finishes the questline - the main quest will also save the quest to db, so we
|
||||
// don't have to call save() here
|
||||
getMainQuest().finish();
|
||||
}
|
||||
|
||||
getQuestData()
|
||||
.getFinishExec()
|
||||
.forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
|
||||
// Some subQuests have conditions that subQuests are finished (even from different MainQuests)
|
||||
getOwner()
|
||||
.getQuestManager()
|
||||
.queueEvent(
|
||||
QuestContent.QUEST_CONTENT_QUEST_STATE_EQUAL,
|
||||
this.subQuestId,
|
||||
this.state.getValue(),
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
getOwner()
|
||||
.getQuestManager()
|
||||
.queueEvent(QuestContent.QUEST_CONTENT_FINISH_PLOT, this.subQuestId, 0);
|
||||
getOwner()
|
||||
.getQuestManager()
|
||||
.queueEvent(
|
||||
QuestCond.QUEST_COND_STATE_EQUAL, this.subQuestId, this.state.getValue(), 0, 0, 0);
|
||||
getOwner()
|
||||
.getScene()
|
||||
.triggerDungeonEvent(DungeonPassConditionType.DUNGEON_COND_FINISH_QUEST, getSubQuestId());
|
||||
|
||||
getOwner().getProgressManager().tryUnlockOpenStates();
|
||||
|
||||
if (ChapterData.endQuestChapterMap.containsKey(subQuestId)) {
|
||||
mainQuest
|
||||
.getOwner()
|
||||
.sendPacket(
|
||||
new PacketChapterStateNotify(
|
||||
ChapterData.endQuestChapterMap.get(subQuestId).getId(),
|
||||
ChapterStateOuterClass.ChapterState.CHAPTER_STATE_END));
|
||||
}
|
||||
|
||||
// hard coding to give amber
|
||||
if (getQuestData().getSubId() == 35402) {
|
||||
getOwner().getInventory().addItem(1021, 1, ActionReason.QuestItem); // amber item id
|
||||
}
|
||||
Grasscutter.getLogger().debug("Quest {} is finished", subQuestId);
|
||||
}
|
||||
|
||||
// TODO
|
||||
public void fail() {
|
||||
this.state = QuestState.QUEST_STATE_FAILED;
|
||||
this.finishTime = Utils.getCurrentSeconds();
|
||||
|
||||
this.getOwner().sendPacket(new PacketQuestListUpdateNotify(this));
|
||||
|
||||
// Some subQuests have conditions that subQuests fail (even from different MainQuests)
|
||||
this.getOwner()
|
||||
.getQuestManager()
|
||||
.queueEvent(
|
||||
QuestContent.QUEST_CONTENT_QUEST_STATE_EQUAL,
|
||||
this.subQuestId,
|
||||
this.state.getValue(),
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
this.getOwner()
|
||||
.getQuestManager()
|
||||
.queueEvent(
|
||||
QuestCond.QUEST_COND_STATE_EQUAL, this.subQuestId, this.state.getValue(), 0, 0, 0);
|
||||
|
||||
this.getQuestData()
|
||||
.getFailExec()
|
||||
.forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
|
||||
|
||||
if (this.getQuestData().getTrialAvatarList() != null) {
|
||||
this.getQuestData()
|
||||
.getTrialAvatarList()
|
||||
.forEach(t -> this.getOwner().getTeamManager().removeTrialAvatar(t));
|
||||
}
|
||||
|
||||
Grasscutter.getLogger().debug("Quest {} is failed", subQuestId);
|
||||
}
|
||||
|
||||
// Return true if it did the rewind
|
||||
public boolean rewind(boolean notifyDelete) {
|
||||
getMainQuest().getChildQuests().values().stream()
|
||||
.filter(p -> p.getQuestData().getOrder() > this.getQuestData().getOrder())
|
||||
.forEach(
|
||||
q -> {
|
||||
q.clearProgress(notifyDelete);
|
||||
});
|
||||
clearProgress(notifyDelete);
|
||||
this.start();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void save() {
|
||||
getMainQuest().save();
|
||||
}
|
||||
|
||||
public Quest toProto() {
|
||||
Quest.Builder proto =
|
||||
Quest.newBuilder()
|
||||
.setQuestId(getSubQuestId())
|
||||
.setState(getState().getValue())
|
||||
.setParentQuestId(getMainQuestId())
|
||||
.setStartTime(getStartTime())
|
||||
.setStartGameTime(438)
|
||||
.setAcceptTime(getAcceptTime());
|
||||
|
||||
if (getFinishProgressList() != null) {
|
||||
for (int i : getFinishProgressList()) {
|
||||
proto.addFinishProgressList(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (getFailProgressList() != null) {
|
||||
for (int i : getFailProgressList()) {
|
||||
proto.addFailProgressList(i);
|
||||
}
|
||||
}
|
||||
|
||||
return proto.build();
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.quest;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Transient;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.ChapterData;
|
||||
import emu.grasscutter.data.excels.QuestData;
|
||||
import emu.grasscutter.data.excels.TriggerExcelConfigData;
|
||||
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.game.quest.enums.QuestCond;
|
||||
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||
import emu.grasscutter.game.quest.enums.QuestState;
|
||||
import emu.grasscutter.net.proto.ChapterStateOuterClass;
|
||||
import emu.grasscutter.net.proto.QuestOuterClass.Quest;
|
||||
import emu.grasscutter.scripts.data.SceneGroup;
|
||||
import emu.grasscutter.server.packet.send.PacketChapterStateNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketDelQuestNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketQuestListUpdateNotify;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.script.Bindings;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.val;
|
||||
|
||||
@Entity
|
||||
public class GameQuest {
|
||||
@Transient @Getter @Setter private GameMainQuest mainQuest;
|
||||
@Transient @Getter private QuestData questData;
|
||||
|
||||
@Getter private int subQuestId;
|
||||
@Getter private int mainQuestId;
|
||||
@Getter @Setter public QuestState state;
|
||||
|
||||
@Getter @Setter private int startTime;
|
||||
@Getter @Setter private int acceptTime;
|
||||
@Getter @Setter private int finishTime;
|
||||
|
||||
@Getter @Setter private long startGameDay;
|
||||
|
||||
@Getter private int[] finishProgressList;
|
||||
@Getter private int[] failProgressList;
|
||||
@Transient @Getter private Map<String, TriggerExcelConfigData> triggerData;
|
||||
@Getter private Map<String, Boolean> triggers;
|
||||
private transient Bindings bindings;
|
||||
|
||||
@Deprecated // Morphia only. Do not use.
|
||||
public GameQuest() {}
|
||||
|
||||
public GameQuest(GameMainQuest mainQuest, QuestData questData) {
|
||||
this.mainQuest = mainQuest;
|
||||
this.subQuestId = questData.getId();
|
||||
this.mainQuestId = questData.getMainId();
|
||||
this.questData = questData;
|
||||
this.state = QuestState.QUEST_STATE_UNSTARTED;
|
||||
this.triggerData = new HashMap<>();
|
||||
this.triggers = new HashMap<>();
|
||||
}
|
||||
|
||||
public void start() {
|
||||
clearProgress(false);
|
||||
this.acceptTime = Utils.getCurrentSeconds();
|
||||
this.startTime = this.acceptTime;
|
||||
this.startGameDay = getOwner().getWorld().getTotalGameTimeDays();
|
||||
this.state = QuestState.QUEST_STATE_UNFINISHED;
|
||||
val triggerCond =
|
||||
questData.getFinishCond().stream()
|
||||
.filter(p -> p.getType() == QuestContent.QUEST_CONTENT_TRIGGER_FIRE)
|
||||
.toList();
|
||||
if (triggerCond.size() > 0) {
|
||||
for (val cond : triggerCond) {
|
||||
TriggerExcelConfigData newTrigger =
|
||||
GameData.getTriggerExcelConfigDataMap().get(cond.getParam()[0]);
|
||||
if (newTrigger != null) {
|
||||
if (this.triggerData == null) {
|
||||
this.triggerData = new HashMap<>();
|
||||
}
|
||||
triggerData.put(newTrigger.getTriggerName(), newTrigger);
|
||||
triggers.put(newTrigger.getTriggerName(), false);
|
||||
SceneGroup group = SceneGroup.of(newTrigger.getGroupId()).load(newTrigger.getSceneId());
|
||||
getOwner()
|
||||
.getWorld()
|
||||
.getSceneById(newTrigger.getSceneId())
|
||||
.loadTriggerFromGroup(group, newTrigger.getTriggerName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getOwner().sendPacket(new PacketQuestListUpdateNotify(this));
|
||||
|
||||
if (ChapterData.beginQuestChapterMap.containsKey(subQuestId)) {
|
||||
getOwner()
|
||||
.sendPacket(
|
||||
new PacketChapterStateNotify(
|
||||
ChapterData.beginQuestChapterMap.get(subQuestId).getId(),
|
||||
ChapterStateOuterClass.ChapterState.CHAPTER_STATE_BEGIN));
|
||||
}
|
||||
|
||||
// Some subQuests and talks become active when some other subQuests are unfinished (even from
|
||||
// different MainQuests)
|
||||
getOwner()
|
||||
.getQuestManager()
|
||||
.queueEvent(
|
||||
QuestContent.QUEST_CONTENT_QUEST_STATE_EQUAL,
|
||||
getSubQuestId(),
|
||||
getState().getValue(),
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
getOwner()
|
||||
.getQuestManager()
|
||||
.queueEvent(
|
||||
QuestCond.QUEST_COND_STATE_EQUAL, getSubQuestId(), getState().getValue(), 0, 0, 0);
|
||||
|
||||
getQuestData()
|
||||
.getBeginExec()
|
||||
.forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
|
||||
getOwner().getQuestManager().checkQuestAlreadyFullfilled(this);
|
||||
|
||||
Grasscutter.getLogger().debug("Quest {} is started", subQuestId);
|
||||
this.save();
|
||||
}
|
||||
|
||||
public String getTriggerNameById(int id) {
|
||||
TriggerExcelConfigData trigger = GameData.getTriggerExcelConfigDataMap().get(id);
|
||||
if (trigger != null) {
|
||||
String triggerName = trigger.getTriggerName();
|
||||
return triggerName;
|
||||
}
|
||||
// return empty string if can't find trigger
|
||||
return "";
|
||||
}
|
||||
|
||||
public Player getOwner() {
|
||||
return this.getMainQuest().getOwner();
|
||||
}
|
||||
|
||||
public void setConfig(QuestData config) {
|
||||
if (config == null || getSubQuestId() != config.getId()) return;
|
||||
this.questData = config;
|
||||
}
|
||||
|
||||
public void setFinishProgress(int index, int value) {
|
||||
finishProgressList[index] = value;
|
||||
}
|
||||
|
||||
public void setFailProgress(int index, int value) {
|
||||
failProgressList[index] = value;
|
||||
}
|
||||
|
||||
public boolean clearProgress(boolean notifyDelete) {
|
||||
// TODO improve
|
||||
var oldState = state;
|
||||
if (questData.getFinishCond() != null && questData.getFinishCond().size() != 0) {
|
||||
this.finishProgressList = new int[questData.getFinishCond().size()];
|
||||
}
|
||||
|
||||
if (questData.getFailCond() != null && questData.getFailCond().size() != 0) {
|
||||
this.failProgressList = new int[questData.getFailCond().size()];
|
||||
}
|
||||
setState(QuestState.QUEST_STATE_UNSTARTED);
|
||||
finishTime = 0;
|
||||
acceptTime = 0;
|
||||
startTime = 0;
|
||||
if (oldState == QuestState.QUEST_STATE_UNSTARTED) {
|
||||
return false;
|
||||
}
|
||||
if (notifyDelete) {
|
||||
getOwner().sendPacket(new PacketDelQuestNotify(getSubQuestId()));
|
||||
}
|
||||
save();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
this.state = QuestState.QUEST_STATE_FINISHED;
|
||||
this.finishTime = Utils.getCurrentSeconds();
|
||||
|
||||
getOwner().sendPacket(new PacketQuestListUpdateNotify(this));
|
||||
|
||||
if (getQuestData().isFinishParent()) {
|
||||
// This quest finishes the questline - the main quest will also save the quest to db, so we
|
||||
// don't have to call save() here
|
||||
getMainQuest().finish();
|
||||
}
|
||||
|
||||
getQuestData()
|
||||
.getFinishExec()
|
||||
.forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
|
||||
// Some subQuests have conditions that subQuests are finished (even from different MainQuests)
|
||||
getOwner()
|
||||
.getQuestManager()
|
||||
.queueEvent(
|
||||
QuestContent.QUEST_CONTENT_QUEST_STATE_EQUAL,
|
||||
this.subQuestId,
|
||||
this.state.getValue(),
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
getOwner()
|
||||
.getQuestManager()
|
||||
.queueEvent(QuestContent.QUEST_CONTENT_FINISH_PLOT, this.subQuestId, 0);
|
||||
getOwner()
|
||||
.getQuestManager()
|
||||
.queueEvent(
|
||||
QuestCond.QUEST_COND_STATE_EQUAL, this.subQuestId, this.state.getValue(), 0, 0, 0);
|
||||
getOwner()
|
||||
.getScene()
|
||||
.triggerDungeonEvent(DungeonPassConditionType.DUNGEON_COND_FINISH_QUEST, getSubQuestId());
|
||||
|
||||
getOwner().getProgressManager().tryUnlockOpenStates();
|
||||
|
||||
if (ChapterData.endQuestChapterMap.containsKey(subQuestId)) {
|
||||
mainQuest
|
||||
.getOwner()
|
||||
.sendPacket(
|
||||
new PacketChapterStateNotify(
|
||||
ChapterData.endQuestChapterMap.get(subQuestId).getId(),
|
||||
ChapterStateOuterClass.ChapterState.CHAPTER_STATE_END));
|
||||
}
|
||||
|
||||
// hard coding to give amber
|
||||
if (getQuestData().getSubId() == 35402) {
|
||||
getOwner().getInventory().addItem(1021, 1, ActionReason.QuestItem); // amber item id
|
||||
}
|
||||
|
||||
this.save();
|
||||
|
||||
Grasscutter.getLogger().debug("Quest {} is finished", subQuestId);
|
||||
}
|
||||
|
||||
// TODO
|
||||
public void fail() {
|
||||
this.state = QuestState.QUEST_STATE_FAILED;
|
||||
this.finishTime = Utils.getCurrentSeconds();
|
||||
|
||||
this.getOwner().sendPacket(new PacketQuestListUpdateNotify(this));
|
||||
|
||||
// Some subQuests have conditions that subQuests fail (even from different MainQuests)
|
||||
this.getOwner()
|
||||
.getQuestManager()
|
||||
.queueEvent(
|
||||
QuestContent.QUEST_CONTENT_QUEST_STATE_EQUAL,
|
||||
this.subQuestId,
|
||||
this.state.getValue(),
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
this.getOwner()
|
||||
.getQuestManager()
|
||||
.queueEvent(
|
||||
QuestCond.QUEST_COND_STATE_EQUAL, this.subQuestId, this.state.getValue(), 0, 0, 0);
|
||||
|
||||
this.getQuestData()
|
||||
.getFailExec()
|
||||
.forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
|
||||
|
||||
if (this.getQuestData().getTrialAvatarList() != null) {
|
||||
this.getQuestData()
|
||||
.getTrialAvatarList()
|
||||
.forEach(t -> this.getOwner().getTeamManager().removeTrialAvatar(t));
|
||||
}
|
||||
|
||||
Grasscutter.getLogger().debug("Quest {} is failed", subQuestId);
|
||||
}
|
||||
|
||||
// Return true if it did the rewind
|
||||
public boolean rewind(boolean notifyDelete) {
|
||||
getMainQuest().getChildQuests().values().stream()
|
||||
.filter(p -> p.getQuestData().getOrder() > this.getQuestData().getOrder())
|
||||
.forEach(
|
||||
q -> {
|
||||
q.clearProgress(notifyDelete);
|
||||
});
|
||||
clearProgress(notifyDelete);
|
||||
this.start();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void save() {
|
||||
getMainQuest().save();
|
||||
}
|
||||
|
||||
public Quest toProto() {
|
||||
Quest.Builder proto =
|
||||
Quest.newBuilder()
|
||||
.setQuestId(getSubQuestId())
|
||||
.setState(getState().getValue())
|
||||
.setParentQuestId(getMainQuestId())
|
||||
.setStartTime(getStartTime())
|
||||
.setStartGameTime(438)
|
||||
.setAcceptTime(getAcceptTime());
|
||||
|
||||
if (getFinishProgressList() != null) {
|
||||
for (int i : getFinishProgressList()) {
|
||||
proto.addFinishProgressList(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (getFailProgressList() != null) {
|
||||
for (int i : getFailProgressList()) {
|
||||
proto.addFailProgressList(i);
|
||||
}
|
||||
}
|
||||
|
||||
return proto.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,7 @@ import emu.grasscutter.data.excels.QuestData;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.player.BasePlayerManager;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.quest.enums.ParentQuestState;
|
||||
import emu.grasscutter.game.quest.enums.QuestCond;
|
||||
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||
import emu.grasscutter.game.quest.enums.QuestState;
|
||||
import emu.grasscutter.game.quest.enums.*;
|
||||
import emu.grasscutter.server.packet.send.PacketFinishedParentQuestUpdateNotify;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import io.netty.util.concurrent.FastThreadLocalThread;
|
||||
@@ -19,6 +16,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
import lombok.val;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
@@ -86,13 +84,17 @@ public class QuestManager extends BasePlayerManager {
|
||||
this.mainQuests = new Int2ObjectOpenHashMap<>();
|
||||
}
|
||||
|
||||
// TODO store user value set on enable
|
||||
public boolean isQuestingEnabled() {
|
||||
return Grasscutter.getConfig().server.game.gameOptions.questing;
|
||||
}
|
||||
|
||||
public void onPlayerBorn() {
|
||||
// TODO scan the quest and start the quest with acceptCond fulfilled
|
||||
// The off send 3 request in that order: 1. FinishedParentQuestNotify, 2. QuestListNotify, 3. ServerCondMeetQuestListUpdateNotify
|
||||
|
||||
List<GameMainQuest> newQuests = this.addMultMainQuests(newPlayerMainQuests);
|
||||
for (GameMainQuest mainQuest : newQuests) {
|
||||
startMainQuest(mainQuest.getParentQuestId());
|
||||
if(this.isQuestingEnabled()) {
|
||||
this.enableQuests();
|
||||
}
|
||||
|
||||
//getPlayer().sendPacket(new PacketFinishedParentQuestUpdateNotify(newQuests));
|
||||
@@ -117,6 +119,8 @@ public class QuestManager extends BasePlayerManager {
|
||||
}
|
||||
quest.checkProgress();
|
||||
}
|
||||
|
||||
player.getActivityManager().triggerActivityConditions();
|
||||
}
|
||||
|
||||
public void onTick(){
|
||||
@@ -163,7 +167,8 @@ public class QuestManager extends BasePlayerManager {
|
||||
}
|
||||
|
||||
public void enableQuests() {
|
||||
onPlayerBorn();
|
||||
this.triggerEvent(QuestCond.QUEST_COND_NONE, null, 0);
|
||||
this.triggerEvent(QuestCond.QUEST_COND_PLAYER_LEVEL_EQUAL_GREATER, null, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -256,6 +261,11 @@ public class QuestManager extends BasePlayerManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
return addQuest(questConfig);
|
||||
}
|
||||
|
||||
public GameQuest addQuest(@Nonnull QuestData questConfig) {
|
||||
|
||||
// Main quest
|
||||
GameMainQuest mainQuest = this.getMainQuestById(questConfig.getMainId());
|
||||
|
||||
@@ -265,7 +275,7 @@ public class QuestManager extends BasePlayerManager {
|
||||
}
|
||||
|
||||
// Sub quest
|
||||
GameQuest quest = mainQuest.getChildQuestById(questId);
|
||||
GameQuest quest = mainQuest.getChildQuestById(questConfig.getSubId());
|
||||
|
||||
// Forcefully start
|
||||
quest.start();
|
||||
@@ -286,13 +296,12 @@ public class QuestManager extends BasePlayerManager {
|
||||
.map(MainQuestData.SubQuestData::getSubId)
|
||||
.ifPresent(this::addQuest);
|
||||
//TODO find a better way then hardcoding to detect needed required quests
|
||||
if(mainQuestId == 355){
|
||||
startMainQuest(361);
|
||||
startMainQuest(418);
|
||||
startMainQuest(423);
|
||||
startMainQuest(20509);
|
||||
|
||||
}
|
||||
// if (mainQuestId == 355){
|
||||
// startMainQuest(361);
|
||||
// startMainQuest(418);
|
||||
// startMainQuest(423);
|
||||
// startMainQuest(20509);
|
||||
// }
|
||||
}
|
||||
public void queueEvent(QuestCond condType, int... params) {
|
||||
queueEvent(condType, "", params);
|
||||
@@ -312,13 +321,42 @@ public class QuestManager extends BasePlayerManager {
|
||||
|
||||
public void triggerEvent(QuestCond condType, String paramStr, int... params) {
|
||||
Grasscutter.getLogger().debug("Trigger Event {}, {}, {}", condType, paramStr, params);
|
||||
List<GameMainQuest> checkMainQuests = this.getMainQuests().values().stream()
|
||||
.filter(i -> i.getState() != ParentQuestState.PARENT_QUEST_STATE_FINISHED)
|
||||
.toList();
|
||||
for (GameMainQuest mainquest : checkMainQuests) {
|
||||
mainquest.tryAcceptSubQuests(condType, paramStr, params);
|
||||
var potentialQuests = GameData.getQuestDataByConditions(condType, params[0], paramStr);
|
||||
if(potentialQuests == null){
|
||||
return;
|
||||
}
|
||||
|
||||
var questSystem = getPlayer().getServer().getQuestSystem();
|
||||
var owner = getPlayer();
|
||||
|
||||
potentialQuests.forEach(questData -> {
|
||||
if(this.wasSubQuestStarted(questData)){
|
||||
return;
|
||||
}
|
||||
val acceptCond = questData.getAcceptCond();
|
||||
int[] accept = new int[acceptCond.size()];
|
||||
for (int i = 0; i < acceptCond.size(); i++) {
|
||||
val condition = acceptCond.get(i);
|
||||
boolean result = questSystem.triggerCondition(owner, questData, condition, paramStr, params);
|
||||
accept[i] = result ? 1 : 0;
|
||||
}
|
||||
|
||||
boolean shouldAccept = LogicType.calculate(questData.getAcceptCondComb(), accept);
|
||||
|
||||
if (shouldAccept){
|
||||
GameQuest quest = owner.getQuestManager().addQuest(questData);
|
||||
Grasscutter.getLogger().debug("Added quest {} result {}", questData.getSubId(), quest !=null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean wasSubQuestStarted(QuestData questData) {
|
||||
var quest = getQuestById(questData.getId());
|
||||
if(quest == null) return false;
|
||||
|
||||
return quest.state != QuestState.QUEST_STATE_UNSTARTED;
|
||||
}
|
||||
|
||||
public void triggerEvent(QuestContent condType, String paramStr, int... params) {
|
||||
Grasscutter.getLogger().debug("Trigger Event {}, {}, {}", condType, paramStr, params);
|
||||
List<GameMainQuest> checkMainQuests = this.getMainQuests().values().stream()
|
||||
@@ -332,6 +370,7 @@ public class QuestManager extends BasePlayerManager {
|
||||
|
||||
/**
|
||||
* TODO maybe trigger them delayed to allow basic communication finish first
|
||||
* TODO move content checks to use static informations where possible to allow direct already fulfilled checking
|
||||
* @param quest
|
||||
*/
|
||||
public void checkQuestAlreadyFullfilled(GameQuest quest){
|
||||
@@ -355,6 +394,7 @@ public class QuestManager extends BasePlayerManager {
|
||||
queueEvent(condition.getType(), condition.getParam()[0], condition.getParam()[1]);
|
||||
}
|
||||
}
|
||||
case QUEST_CONTENT_PLAYER_LEVEL_UP -> queueEvent(condition.getType(), player.getLevel());
|
||||
}
|
||||
}
|
||||
}, 1);
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
package emu.grasscutter.game.quest.content;
|
||||
|
||||
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_UNLOCK_TRANS_POINT;
|
||||
|
||||
import emu.grasscutter.data.excels.QuestData;
|
||||
import emu.grasscutter.game.quest.GameQuest;
|
||||
import emu.grasscutter.game.quest.QuestValueContent;
|
||||
|
||||
@QuestValueContent(QUEST_CONTENT_UNLOCK_TRANS_POINT)
|
||||
public class ContentUnlockTransPoint extends BaseContent {
|
||||
@Override
|
||||
public boolean execute(
|
||||
GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
|
||||
return condition.getParam()[0] == params[0] && condition.getParam()[1] == params[1];
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.quest.content;
|
||||
|
||||
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_UNLOCK_TRANS_POINT;
|
||||
|
||||
import emu.grasscutter.data.excels.QuestData;
|
||||
import emu.grasscutter.game.quest.GameQuest;
|
||||
import emu.grasscutter.game.quest.QuestValueContent;
|
||||
|
||||
@QuestValueContent(QUEST_CONTENT_UNLOCK_TRANS_POINT)
|
||||
public class ContentUnlockTransPoint extends BaseContent {
|
||||
@Override
|
||||
public boolean execute(
|
||||
GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
|
||||
var sceneId = condition.getParam()[0];
|
||||
var scenePointId = condition.getParam()[1];
|
||||
var scenePoints = quest.getOwner().getUnlockedScenePoints().get(sceneId);
|
||||
return scenePoints != null && scenePoints.contains(scenePointId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
package emu.grasscutter.game.quest.exec;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.excels.QuestData;
|
||||
import emu.grasscutter.game.quest.GameQuest;
|
||||
import emu.grasscutter.game.quest.QuestValueExec;
|
||||
import emu.grasscutter.game.quest.enums.QuestExec;
|
||||
import emu.grasscutter.game.quest.handlers.QuestExecHandler;
|
||||
|
||||
@QuestValueExec(QuestExec.QUEST_EXEC_ADD_CUR_AVATAR_ENERGY)
|
||||
public class ExecAddCurAvatarEnergy extends QuestExecHandler {
|
||||
@Override
|
||||
public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) {
|
||||
Grasscutter.getLogger().info("Energy refilled");
|
||||
return quest.getOwner().getEnergyManager().refillActiveEnergy();
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.quest.exec;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.excels.QuestData;
|
||||
import emu.grasscutter.game.quest.GameQuest;
|
||||
import emu.grasscutter.game.quest.QuestValueExec;
|
||||
import emu.grasscutter.game.quest.enums.QuestExec;
|
||||
import emu.grasscutter.game.quest.handlers.QuestExecHandler;
|
||||
|
||||
@QuestValueExec(QuestExec.QUEST_EXEC_ADD_CUR_AVATAR_ENERGY)
|
||||
public class ExecAddCurAvatarEnergy extends QuestExecHandler {
|
||||
@Override
|
||||
public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) {
|
||||
Grasscutter.getLogger().debug("Energy refilled");
|
||||
return quest.getOwner().getEnergyManager().refillActiveEnergy();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user