Merge pull request #74 from Anime-Game-Servers/refactor_quest_cont

QuestContent refactor+some more quest fixes and changes
This commit is contained in:
Alexander Hartmann 2023-08-31 19:40:20 +02:00 committed by GitHub
commit b28e4c65eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
72 changed files with 747 additions and 396 deletions

View File

@ -31,6 +31,7 @@ import emu.grasscutter.game.quest.RewindData;
import emu.grasscutter.game.quest.TeleportData;
import emu.grasscutter.game.world.GroupReplacementData;
import emu.grasscutter.game.quest.enums.QuestCond;
import emu.grasscutter.scripts.data.DummyPoint;
import emu.grasscutter.utils.Utils;
import emu.grasscutter.data.excels.*;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
@ -168,6 +169,7 @@ public class GameData {
private static final Int2ObjectMap<ShopGoodsData> shopGoodsDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<RewindData> rewindDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<TeleportData> teleportDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<Map<String, DummyPoint>> dummyPointMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<RefreshPolicyExcelConfigData> refreshPolicyExcelConfigDataMap = new Int2ObjectOpenHashMap<>();
// The following are accessed via getMapByResourceDef, and will show as unused
private static final Int2ObjectMap<CodexMaterialData> codexMaterialDataMap = new Int2ObjectOpenHashMap<>();

View File

@ -37,6 +37,7 @@ import emu.grasscutter.game.world.SpawnDataEntry.SpawnGroupEntry;
import emu.grasscutter.scripts.EntityControllerScriptManager;
import emu.grasscutter.scripts.SceneIndexManager;
import emu.grasscutter.scripts.ScriptLoader;
import emu.grasscutter.scripts.data.DummyPoint;
import emu.grasscutter.utils.FileUtils;
import emu.grasscutter.utils.JsonUtils;
import emu.grasscutter.utils.TsvUtils;
@ -50,7 +51,9 @@ import org.reflections.Reflections;
import org.slf4j.Logger;
import java.io.*;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
@ -138,12 +141,11 @@ public class ResourceLoader {
cacheTalentLevelSets();
// Load special ability in certain scene/dungeon
loadConfigLevelEntityData();
loadQuestShareConfig();
loadScriptData();
loadGadgetMappings();
loadSubfieldMappings();
loadMonsterMappings();
loadActivityCondGroups();
loadGroupReplacements();
loadTrialAvatarCustomData();
loadGlobalCombatConfig();
EntityControllerScriptManager.load();
@ -749,9 +751,14 @@ public class ResourceLoader {
logger.error("No config level entity loaded!");
}
}
private static void loadScriptData(){
loadQuestShareConfig();
loadGroupReplacements();
loadDummyPoints();
}
private static void loadQuestShareConfig(){
// Load from BinOutput
// Load from Shared Quest scripts
val pattern = Pattern.compile("Q(.+?)\\ShareConfig.lua");
try(val stream = Files.newDirectoryStream(getResourcePath("Scripts/Quest/Share/"), "Q*ShareConfig.lua")) {
@ -785,6 +792,38 @@ public class ResourceLoader {
}
}
private static void loadDummyPoints(){
// Load from scene scripts
try(val stream = Files.newDirectoryStream(getResourcePath("Scripts/Scene/"), p -> Files.isDirectory(p, LinkOption.NOFOLLOW_LINKS))) {
stream.forEach(path -> {
int sceneId = Integer.parseInt(path.getFileName().toString());
val targetScript = "Scene/"+sceneId+"/scene"+sceneId+"_dummy_points.lua";
val targetPath = FileUtils.getScriptPath(targetScript);
if(Files.notExists(targetPath)) return;
val cs = ScriptLoader.getScript(targetScript);
if (cs == null) return;
try{
cs.evaluate();
val teleportDataMap = cs.getGlobalVariableMap("dummy_points",DummyPoint.class);
GameData.getDummyPointMap().put(sceneId, teleportDataMap);
logger.info("Loaded {} dummy points for scene {}.", teleportDataMap.size(), sceneId);
} catch (Throwable e){
logger.error("Error while loading Quest Share Config: {}", path.getFileName().toString());
}
});
} catch (IOException e) {
logger.error("Error loading Quest Share Config: no files found");
return;
}
if (GameData.getDummyPointMap() == null || GameData.getDummyPointMap().isEmpty()) {
logger.error("No scene dummy points loaded!");
} else {
logger.info("Loaded dummy points for {} scenes.", GameData.getDummyPointMap().size());
}
}
private static void loadGadgetMappings() {
try {
val gadgetMap = GameData.getGadgetMappingMap();

View File

@ -188,6 +188,7 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
private void triggerAddItemEvents(GameItem result){
getPlayer().getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_OBTAIN_MATERIAL_NUM, result.getItemId(), result.getCount());
getPlayer().getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_OBTAIN_ITEM, result.getItemId(), result.getCount());
getPlayer().getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_OBTAIN_VARIOUS_ITEM, result.getItemId(), result.getCount());
}
private void triggerRemItemEvents(GameItem item, int removeCount){
getPlayer().getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_COST_MATERIAL, item.getItemId(), removeCount);

View File

@ -4,7 +4,7 @@ import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import dev.morphia.annotations.Indexed;
import dev.morphia.annotations.Transient;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.Loggers;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.quest.MainQuestData;
import emu.grasscutter.data.common.quest.MainQuestData.TalkData;
@ -20,17 +20,20 @@ import emu.grasscutter.net.proto.ChildQuestOuterClass.ChildQuest;
import emu.grasscutter.net.proto.ParentQuestOuterClass.ParentQuest;
import emu.grasscutter.server.packet.send.PacketCodexDataUpdateNotify;
import emu.grasscutter.server.packet.send.PacketFinishedParentQuestUpdateNotify;
import emu.grasscutter.server.packet.send.PacketQuestProgressUpdateNotify;
import emu.grasscutter.server.packet.send.PacketQuestUpdateQuestVarNotify;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.Utils;
import lombok.Getter;
import lombok.val;
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import javax.annotation.Nullable;
import java.util.*;
@Entity(value = "quests", useDiscriminator = false)
public class GameMainQuest {
public static final Logger logger = Loggers.getQuestSystem();
@Id private ObjectId id;
@Indexed @Getter private int ownerUid;
@Transient @Getter private Player owner;
@ -76,7 +79,7 @@ public class GameMainQuest {
private void addAllChildQuests() {
val mainQuestData = getMainQuestData();
if(mainQuestData== null) {
Grasscutter.getLogger().error("mainQuestData is null for parentQuestId {}", parentQuestId);
logger.error("mainQuestData is null for parentQuestId {}", parentQuestId);
return;
}
val subQuests = Arrays.stream(mainQuestData.getSubQuests()).toList();
@ -102,19 +105,45 @@ public class GameMainQuest {
public void setQuestVar(int i, int value) {
int previousValue = this.questVars[i];
this.questVars[i] = value;
Grasscutter.getLogger().debug("questVar {} value changed from {} to {}", i, previousValue, value);
logger.debug("questVar {} value changed from {} to {}", i, previousValue, value);
triggerQuestVarAction(i, this.questVars[i]);
}
public void incQuestVar(int i, int inc) {
int previousValue = this.questVars[i];
this.questVars[i] += inc;
Grasscutter.getLogger().debug("questVar {} value incremented from {} to {}", i, previousValue, previousValue + inc);
logger.debug("questVar {} value incremented from {} to {}", i, previousValue, previousValue + inc);
triggerQuestVarAction(i, this.questVars[i]);
}
public void decQuestVar(int i, int dec) {
int previousValue = this.questVars[i];
this.questVars[i] -= dec;
Grasscutter.getLogger().debug("questVar {} value decremented from {} to {}", i, previousValue, previousValue - dec);
logger.debug("questVar {} value decremented from {} to {}", i, previousValue, previousValue - dec);
triggerQuestVarAction(i, this.questVars[i]);
}
public void setRandomQuestVar(int i, int low, int high){
int previousValue = this.questVars[i];
this.questVars[i] = Utils.random.nextInt(low, high);
logger.debug("questVar {} value random changed from {} to {}", i, previousValue, this.questVars[i]);
triggerQuestVarAction(i, this.questVars[i]);
}
private void triggerQuestVarAction(int index, int value) {
var questManager = this.getQuestManager();
questManager.queueEvent(QuestCond.QUEST_COND_QUEST_VAR_EQUAL, index, value);
questManager.queueEvent(QuestCond.QUEST_COND_QUEST_VAR_GREATER, index, value);
questManager.queueEvent(QuestCond.QUEST_COND_QUEST_VAR_LESS, index, value);
questManager.queueEvent(QuestContent.QUEST_CONTENT_QUEST_VAR_EQUAL, index, value);
questManager.queueEvent(QuestContent.QUEST_CONTENT_QUEST_VAR_GREATER, index, value);
questManager.queueEvent(QuestContent.QUEST_CONTENT_QUEST_VAR_LESS, index, value);
this.getOwner()
.sendPacket(new PacketQuestUpdateQuestVarNotify(this.getParentQuestId(), this.questVars));
}
@ -135,7 +164,7 @@ public class GameMainQuest {
// Avoid recursion from child finish() in GameQuest
// when auto finishing all child quests with QUEST_STATE_UNFINISHED (below)
if (this.isFinished) {
Grasscutter.getLogger().debug("Skip main quest finishing because it's already finished");
logger.debug("Skip main quest finishing because it's already finished");
return;
}
@ -283,7 +312,7 @@ public class GameMainQuest {
posAndRot.add(0, new Position(avatarPosPos.get(0),avatarPosPos.get(1),avatarPosPos.get(2))); // position
posAndRot.add(1, new Position(avatarPosRot.get(0),avatarPosRot.get(1),avatarPosRot.get(2))); //rotation
Grasscutter.getLogger().info("Succesfully loaded rewind data for subQuest {}", subId);
logger.info("Succesfully loaded rewind data for subQuest {}", subId);
return true;
}
@ -311,7 +340,7 @@ public class GameMainQuest {
posAndRot.add(0, new Position(transmitPosPos.get(0), transmitPosPos.get(1), transmitPosPos.get(2))); // position
posAndRot.add(1, new Position(transmitPosRot.get(0), transmitPosRot.get(1), transmitPosRot.get(2))); // rotation
Grasscutter.getLogger().info("Succesfully loaded teleport data for subQuest {}", subId);
logger.info("Succesfully loaded teleport data for subQuest {}", subId);
return true;
}
@ -329,29 +358,23 @@ public class GameMainQuest {
.filter(p -> p.getState() == QuestState.QUEST_STATE_UNFINISHED)
.filter(p -> p.getQuestData().getFailCond().stream().anyMatch(q -> q.getType() == condType))
.toList();
val questSystem = this.getOwner().getServer().getQuestSystem();
for (GameQuest subQuestWithCond : subQuestsWithCond) {
val failCond = subQuestWithCond.getQuestData().getFailCond();
val failCondComb = subQuestWithCond.getQuestData().getFailCondComb();
val failProgressList = subQuestWithCond.getFailProgressList();
for (int i = 0; i < subQuestWithCond.getQuestData().getFailCond().size(); i++) {
val condition = failCond.get(i);
if (condition.getType() == condType) {
boolean result = this.getOwner().getServer().getQuestSystem().triggerContent(subQuestWithCond, condition, paramStr, params);
subQuestWithCond.getFailProgressList()[i] = result ? 1 : 0;
if (result) {
getOwner().getSession().send(new PacketQuestProgressUpdateNotify(subQuestWithCond));
}
}
}
boolean shouldFail = LogicType.calculate(subQuestWithCond.getQuestData().getFailCondComb(), subQuestWithCond.getFailProgressList());
val shouldFail = questSystem.checkAndUpdateContent(
subQuestWithCond, failProgressList, failCond, failCondComb,
condType, paramStr, params);
if (shouldFail)
subQuestWithCond.fail();
}
} catch (Exception e) {
Grasscutter.getLogger().error("An error occurred while trying to fail quest.", e);
logger.error("An error occurred while trying to fail quest.", e);
}
}
@ -362,28 +385,22 @@ public class GameMainQuest {
.filter(p -> p.getState() == QuestState.QUEST_STATE_UNFINISHED && p.getQuestData().getAcceptCond() != null)
.filter(p -> p.getQuestData().getFinishCond().stream().anyMatch(q -> q.getType() == condType))
.toList();
val questSystem = this.getOwner().getServer().getQuestSystem();
for (GameQuest subQuestWithCond : subQuestsWithCond) {
val finishCond = subQuestWithCond.getQuestData().getFinishCond();
val finishCondComb = subQuestWithCond.getQuestData().getFinishCondComb();
val finishProgressList = subQuestWithCond.getFinishProgressList();
for (int i = 0; i < finishCond.size(); i++) {
val condition = finishCond.get(i);
if (condition.getType() == condType) {
boolean result = this.getOwner().getServer().getQuestSystem().triggerContent(subQuestWithCond, condition, paramStr, params);
subQuestWithCond.getFinishProgressList()[i] = result ? 1 : 0;
if (result) {
getOwner().getSession().send(new PacketQuestProgressUpdateNotify(subQuestWithCond));
}
}
}
boolean shouldFinish = LogicType.calculate(subQuestWithCond.getQuestData().getFinishCondComb(), subQuestWithCond.getFinishProgressList());
val shouldFinish = questSystem.checkAndUpdateContent(subQuestWithCond,
finishProgressList, finishCond, finishCondComb,
condType, paramStr, params);
if (shouldFinish)
subQuestWithCond.finish();
}
} catch (Exception e) {
Grasscutter.getLogger().debug("An error occurred while trying to finish quest.", e);
logger.debug("An error occurred while trying to finish quest.", e);
}
}
@ -425,7 +442,7 @@ public class GameMainQuest {
// TimeVar handling TODO check if ingame or irl time
public boolean initTimeVar(int index){
if(index >= this.timeVar.length){
Grasscutter.getLogger().error("Trying to init out of bounds time var {} for quest {}", index, this.parentQuestId);
logger.error("Trying to init out of bounds time var {} for quest {}", index, this.parentQuestId);
return false;
}
this.timeVar[index] = owner.getWorld().getTotalGameTimeMinutes();
@ -435,7 +452,7 @@ public class GameMainQuest {
public boolean clearTimeVar(int index){
if(index >= this.timeVar.length){
Grasscutter.getLogger().error("Trying to clear out of bounds time var {} for quest {}", index, this.parentQuestId);
logger.error("Trying to clear out of bounds time var {} for quest {}", index, this.parentQuestId);
return false;
}
this.timeVar[index] = -1;
@ -451,7 +468,7 @@ public class GameMainQuest {
public long getDaysSinceTimeVar(int index){
if(index >= this.timeVar.length){
Grasscutter.getLogger().error("Trying to get days for out of bounds time var {} for quest {}", index, this.parentQuestId);
logger.error("Trying to get days for out of bounds time var {} for quest {}", index, this.parentQuestId);
return -1;
}
val varTime = timeVar[index];
@ -465,7 +482,7 @@ public class GameMainQuest {
public long getHoursSinceTimeVar(int index){
if(index >= this.timeVar.length){
Grasscutter.getLogger().error("Trying to get hours for out of bounds time var {} for quest {}", index, this.parentQuestId);
logger.error("Trying to get hours for out of bounds time var {} for quest {}", index, this.parentQuestId);
return -1;
}
val varTime = timeVar[index];

View File

@ -98,8 +98,7 @@ public class GameQuest {
}
//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);
triggerStateEvents();
getQuestData().getBeginExec().forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
getOwner().getQuestManager().checkQuestAlreadyFullfilled(this);
@ -185,9 +184,7 @@ public class GameQuest {
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);
triggerStateEvents();
getOwner().getScene().triggerDungeonEvent(DungeonPassConditionType.DUNGEON_COND_FINISH_QUEST, getSubQuestId());
getOwner().getProgressManager().tryUnlockOpenStates();
@ -219,16 +216,28 @@ public class GameQuest {
getOwner().sendPacket(new PacketQuestListUpdateNotify(this));
//Some subQuests have conditions that subQuests fail (even from different MainQuests)
getOwner().getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_QUEST_STATE_EQUAL, this.subQuestId, this.state.getValue(),0,0,0);
getOwner().getQuestManager().queueEvent(QuestCond.QUEST_COND_STATE_EQUAL, this.subQuestId, this.state.getValue(),0,0,0);
triggerStateEvents();
getQuestData().getFailExec().forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
if (getQuestData().getTrialAvatarList() != null) {
getQuestData().getTrialAvatarList().forEach(t -> getOwner().removeTrialAvatarForQuest(t));
}
Grasscutter.getLogger().debug("Quest {} is failed", subQuestId);
}
/**
* Triggers events: 'QUEST_COND_STATE_EQUAL', 'QUEST_COND_STATE_NOT_EQUAL',
* 'QUEST_CONTENT_QUEST_STATE_EQUAL', 'QUEST_CONTENT_QUEST_STATE_NOT_EQUAL'
*/
public void triggerStateEvents(){
val questManager = getOwner().getQuestManager();
val questId = this.subQuestId;
val state = this.state.getValue();
questManager.queueEvent(QuestCond.QUEST_COND_STATE_EQUAL, questId, state);
questManager.queueEvent(QuestCond.QUEST_COND_STATE_NOT_EQUAL, questId, state);
questManager.queueEvent(QuestContent.QUEST_CONTENT_QUEST_STATE_EQUAL, questId, state);
questManager.queueEvent(QuestContent.QUEST_CONTENT_QUEST_STATE_NOT_EQUAL, questId, state);
}

View File

@ -184,20 +184,20 @@ public class QuestManager extends BasePlayerManager {
public void setQuestGlobalVarValue(Integer variable, Integer value) {
Integer previousValue = getPlayer().getQuestGlobalVariables().put(variable,value);
Grasscutter.getLogger().debug("Changed questGlobalVar {} value from {} to {}", variable, previousValue==null ? 0: previousValue, value);
QuestSystem.getLogger().debug("Changed questGlobalVar {} value from {} to {}", variable, previousValue==null ? 0: previousValue, value);
}
public void incQuestGlobalVarValue(Integer variable, Integer inc) {
//
Integer previousValue = getPlayer().getQuestGlobalVariables().getOrDefault(variable,0);
getPlayer().getQuestGlobalVariables().put(variable,previousValue + inc);
Grasscutter.getLogger().debug("Incremented questGlobalVar {} value from {} to {}", variable, previousValue, previousValue + inc);
QuestSystem.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) {
//
Integer previousValue = getPlayer().getQuestGlobalVariables().getOrDefault(variable,0);
getPlayer().getQuestGlobalVariables().put(variable,previousValue - dec);
Grasscutter.getLogger().debug("Decremented questGlobalVar {} value from {} to {}", variable, previousValue, previousValue - dec);
QuestSystem.getLogger().debug("Decremented questGlobalVar {} value from {} to {}", variable, previousValue, previousValue - dec);
}
public GameMainQuest getMainQuestById(int mainQuestId) {
@ -334,7 +334,7 @@ public class QuestManager extends BasePlayerManager {
//QUEST_EXEC are handled directly by each subQuest
public void triggerEvent(QuestCond condType, String paramStr, int... params) {
Grasscutter.getLogger().debug("Trigger Event {}, {}, {}", condType, paramStr, params);
QuestSystem.getLogger().debug("Trigger Event {}, {}, {}", condType, paramStr, params);
val potentialQuests = GameData.getQuestDataByConditions(condType, params[0], paramStr);
if(potentialQuests == null){
return;
@ -358,7 +358,7 @@ public class QuestManager extends BasePlayerManager {
if (shouldAccept){
GameQuest quest = owner.getQuestManager().addQuest(questData);
Grasscutter.getLogger().debug("Added quest {} result {}", questData.getSubId(), quest !=null);
QuestSystem.getLogger().debug("Added quest {} result {}", questData.getSubId(), quest !=null);
}
});
}
@ -372,7 +372,7 @@ public class QuestManager extends BasePlayerManager {
}
public void triggerEvent(QuestContent condType, String paramStr, int... params) {
Grasscutter.getLogger().debug("Trigger Event {}, {}, {}", condType, paramStr, params);
QuestSystem.getLogger().debug("Trigger Event {}, {}, {}", condType, paramStr, params);
List<GameMainQuest> checkMainQuests = this.getMainQuests().values().stream()
.filter(i -> i.getState() != ParentQuestState.PARENT_QUEST_STATE_FINISHED)
.toList();
@ -389,26 +389,22 @@ public class QuestManager extends BasePlayerManager {
*/
public void checkQuestAlreadyFullfilled(GameQuest quest){
Grasscutter.getGameServer().getScheduler().scheduleDelayedTask(() -> {
for(var condition : quest.getQuestData().getFinishCond()){
switch (condition.getType()) {
case QUEST_CONTENT_OBTAIN_ITEM, QUEST_CONTENT_ITEM_LESS_THAN -> {
//check if we already own enough of the item
var item = getPlayer().getInventory().getItemByGuid(condition.getParam()[0]);
queueEvent(condition.getType(), condition.getParam()[0], item != null ? item.getCount() : 0);
}
case QUEST_CONTENT_UNLOCK_TRANS_POINT -> {
var scenePoints = getPlayer().getUnlockedScenePoints().get(condition.getParam()[0]);
if (scenePoints != null && scenePoints.contains(condition.getParam()[1])) {
queueEvent(condition.getType(), condition.getParam()[0], condition.getParam()[1]);
}
}
case QUEST_CONTENT_UNLOCK_AREA -> {
var sceneAreas = getPlayer().getUnlockedSceneAreas().get(condition.getParam()[0]);
if (sceneAreas != null && sceneAreas.contains(condition.getParam()[1])) {
queueEvent(condition.getType(), condition.getParam()[0], condition.getParam()[1]);
}
}
case QUEST_CONTENT_PLAYER_LEVEL_UP -> queueEvent(condition.getType(), player.getLevel());
val questSystem = getPlayer().getServer().getQuestSystem();
val questData = quest.getQuestData();
if (questData == null) {
QuestSystem.getLogger().error("Quest {} has no data", quest.getSubQuestId());
return;
}
val shouldFinish = questSystem.initialCheckContent(quest, quest.getFinishProgressList(), questData.getFinishCond(), questData.getFinishCondComb());
if (shouldFinish) {
quest.finish(false);
return;
}
if(!questData.getFailCond().isEmpty()) {
val shouldFail = questSystem.initialCheckContent(quest, quest.getFailProgressList(), questData.getFailCond(), questData.getFailCondComb());
if (shouldFail) {
quest.fail();
}
}
}, 1);

View File

@ -1,19 +1,29 @@
package emu.grasscutter.game.quest;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.Loggers;
import emu.grasscutter.data.common.quest.SubQuestData.*;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.conditions.BaseCondition;
import emu.grasscutter.game.quest.content.BaseContent;
import emu.grasscutter.game.quest.enums.LogicType;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.game.quest.handlers.QuestExecHandler;
import emu.grasscutter.server.game.BaseGameSystem;
import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.packet.send.PacketQuestProgressUpdateNotify;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter;
import lombok.val;
import org.reflections.Reflections;
import org.slf4j.Logger;
import java.util.List;
public class QuestSystem extends BaseGameSystem {
@Getter
private static final Logger logger = Loggers.getQuestSystem();
private final Int2ObjectMap<BaseCondition> condHandlers;
private final Int2ObjectMap<BaseContent> contHandlers;
private final Int2ObjectMap<QuestExecHandler> execHandlers;
@ -75,39 +85,87 @@ public class QuestSystem extends BaseGameSystem {
BaseCondition handler = condHandlers.get(condition.getType().getValue());
if (handler == null || questData == null) {
Grasscutter.getLogger().debug("Could not trigger condition {} at {}", condition.getType().getValue(), questData);
getLogger().debug("Could not trigger condition {} at {}", condition.getType().getValue(), questData);
return false;
}
return handler.execute(owner, questData, condition, paramStr, params);
}
public boolean triggerContent(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
BaseContent handler = contHandlers.get(condition.getType().getValue());
if (handler == null || quest.getQuestData() == null) {
Grasscutter.getLogger().debug("Could not trigger content {} at {}", condition.getType().getValue(), quest.getQuestData());
return false;
private BaseContent getContentHandler(QuestContent type, SubQuestData questData){
val handler = contHandlers.get(type.getValue());
if(handler == null){
getLogger().debug("Could not get handler for content {} in quest {}", type.getValue(), questData);
return contHandlers.get(QuestContent.QUEST_CONTENT_UNKNOWN.getValue());
}
return handler;
}
return handler.execute(quest, condition, paramStr, params);
public boolean initialCheckContent(GameQuest quest, int[] curProgress,
List<QuestContentCondition> conditions, LogicType logicType){
val owner = quest.getOwner();
var changed = false;
int[] finished = new int[conditions.size()];
for (int i = 0; i < conditions.size(); i++) {
val condition = conditions.get(i);
BaseContent handler = getContentHandler(condition.getType(), quest.getQuestData());
val startingProgress = curProgress[i];
int result = handler.initialCheck(quest, quest.getQuestData(), condition);
curProgress[i] = result;
if (startingProgress != result) {
changed = true;
}
finished[i] = handler.checkProgress(quest, condition, result) ? 1 : 0;
}
if(changed) {
owner.getSession().send(new PacketQuestProgressUpdateNotify(quest));
}
return LogicType.calculate(logicType, finished);
}
public boolean checkAndUpdateContent(GameQuest quest, int[] curProgress,
List<QuestContentCondition> conditions, LogicType logicType,
QuestContent condType, String paramStr, int... params){
val owner = quest.getOwner();
var changed = false;
int[] finished = new int[conditions.size()];
for (int i = 0; i < conditions.size(); i++) {
val condition = conditions.get(i);
BaseContent handler = getContentHandler(condition.getType(), quest.getQuestData());
// only update progress if its actually affected by the current event
if(handler.isEvent(quest.getQuestData(), condition, condType, paramStr, params)){
val startingProgress = curProgress[i];
int result = handler.updateProgress(quest, startingProgress, condition, paramStr, params);
curProgress[i] = result;
if (startingProgress != result) {
changed = true;
}
}
finished[i] = handler.checkProgress(quest, condition, curProgress[i]) ? 1 : 0;
}
if(changed) {
owner.getSession().send(new PacketQuestProgressUpdateNotify(quest));
}
return LogicType.calculate(logicType, finished);
}
public void triggerExec(GameQuest quest, QuestExecParam execParam, String... params) {
if(execParam.getType() == null) {
Grasscutter.getLogger().error("execParam.getType() is null {}", quest.getSubQuestId());
getLogger().error("execParam.getType() is null {}", quest.getSubQuestId());
return;
}
QuestExecHandler handler = execHandlers.get(execParam.getType().getValue());
if (handler == null || quest.getQuestData() == null) {
Grasscutter.getLogger().debug("Could not trigger exec {} at {}", execParam.getType().getValue(), quest.getQuestData());
getLogger().debug("Could not trigger exec {} at {}", execParam.getType().getValue(), quest.getQuestData());
return;
}
QuestManager.eventExecutor.submit(() -> {
if (!handler.execute(quest, execParam, params)) {
Grasscutter.getLogger().debug("exec trigger failed {} at {}", execParam.getType().getValue(), quest.getQuestData());
getLogger().debug("exec trigger failed {} at {}", execParam.getType().getValue(), quest.getQuestData());
}
});
}

View File

@ -5,6 +5,7 @@ import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.common.quest.SubQuestData.*;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.QuestSystem;
import emu.grasscutter.game.quest.QuestValueCond;
import static emu.grasscutter.game.quest.enums.QuestCond.QUEST_COND_UNKNOWN;
@ -13,7 +14,7 @@ import static emu.grasscutter.game.quest.enums.QuestCond.QUEST_COND_UNKNOWN;
public class BaseCondition {
public boolean execute(Player owner, SubQuestData questData, QuestAcceptCondition condition, @Nullable String paramStr, int... params) {
Grasscutter.getLogger().error("Unknown condition {} at {}", condition.getType().name(), questData.getSubId());
QuestSystem.getLogger().error("Unknown condition {} at {}", condition.getType().name(), questData.getSubId());
return false;
}

View File

@ -4,6 +4,7 @@ import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.common.quest.SubQuestData.*;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.QuestSystem;
import lombok.val;
public abstract class BaseConditionQuestVar extends BaseCondition {
@ -16,7 +17,7 @@ public abstract class BaseConditionQuestVar extends BaseCondition {
val targetValue = condition.getParam()[1];
val questVarValue = getQuestVar(owner, questData, index);
Grasscutter.getLogger().debug("questVar {} : {}", index, questVarValue);
QuestSystem.getLogger().debug("questVar {} : {}", index, questVarValue);
if (questVarValue < 0) {
return false;
@ -27,12 +28,12 @@ public abstract class BaseConditionQuestVar extends BaseCondition {
protected int getQuestVar(Player owner, SubQuestData questData, int index) {
val mainQuest = owner.getQuestManager().getMainQuestById(questData.getMainId());
if (mainQuest == null) {
Grasscutter.getLogger().debug("mainQuest for quest var not available yet");
QuestSystem.getLogger().debug("mainQuest for quest var not available yet");
return -1;
}
val questVars = mainQuest.getQuestVars();
if (index >= questVars.length) {
Grasscutter.getLogger().error("questVar out of bounds for {} index {} size {}", questData.getSubId(), index, questVars.length);
QuestSystem.getLogger().error("questVar out of bounds for {} index {} size {}", questData.getSubId(), index, questVars.length);
return -2;
}
return questVars[index];

View File

@ -5,6 +5,7 @@ import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.common.quest.SubQuestData.*;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.QuestSystem;
import emu.grasscutter.game.quest.QuestValueCond;
import lombok.val;
@ -15,15 +16,21 @@ public class ConditionCompleteTalk extends BaseCondition {
@Override
public boolean execute(Player owner, SubQuestData questData, QuestAcceptCondition condition, String paramStr, int... params) {
val talkId = condition.getParam()[0];
val requiredTalkId = condition.getParam()[0];
val unknownParam = condition.getParam()[1]; // e.g. 3 for 7081601
val checkMainQuest = owner.getQuestManager().getMainQuestByTalkId(talkId);
val eventTalkId = params[0];
if(requiredTalkId == eventTalkId) {
return true;
}
val checkMainQuest = owner.getQuestManager().getMainQuestByTalkId(requiredTalkId);
if (checkMainQuest == null || GameData.getMainQuestDataMap().get(checkMainQuest.getParentQuestId()).getTalks() == null) {
Grasscutter.getLogger().debug("Warning: mainQuest {} hasn't been started yet, or has no talks", talkId / 100);
QuestSystem.getLogger().debug("Warning: mainQuest {} hasn't been started yet, or has no talks", requiredTalkId / 100);
return false;
}
val talkData = checkMainQuest.getTalks().get(talkId);
return talkData != null || checkMainQuest.getChildQuestById(talkId) != null;
val talkData = checkMainQuest.getTalks().get(requiredTalkId);
return talkData != null || checkMainQuest.getChildQuestById(requiredTalkId) != null;
}
}

View File

@ -15,8 +15,8 @@ public class ConditionItemNumLessThan extends BaseCondition {
public boolean execute(Player owner, SubQuestData questData, QuestAcceptCondition condition, String paramStr, int... params) {
val itemId = condition.getParam()[0];
val amount = condition.getParam()[1];
val checkItem = owner.getInventory().getItemByGuid(itemId);
return checkItem == null || checkItem.getCount() < amount;
val checkItem = owner.getInventory().getItemCountById(itemId);
return checkItem < amount;
}
}

View File

@ -15,8 +15,8 @@ public class ConditionPackHaveItem extends BaseCondition {
public boolean execute(Player owner, SubQuestData questData, QuestAcceptCondition condition, String paramStr, int... params) {
val itemId = condition.getParam()[0];
val targetAmount = condition.getParam()[1];
val checkItem = owner.getInventory().getItemByGuid(itemId);
return checkItem != null && checkItem.getCount() >= targetAmount;
val itemCount = owner.getInventory().getItemCountById(itemId);
return itemCount >= targetAmount;
}
}

View File

@ -4,6 +4,7 @@ import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.common.quest.SubQuestData.*;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.QuestSystem;
import emu.grasscutter.game.quest.QuestValueCond;
import lombok.val;
@ -17,7 +18,7 @@ public class ConditionQuestGlobalVarEqual extends BaseCondition {
val questId = condition.getParam()[0];
val targetValue = condition.getParam()[1];
Integer questGlobalVarValue = owner.getQuestManager().getQuestGlobalVarValue(questId);
Grasscutter.getLogger().debug("questGlobarVar {} {} : {}", questId, targetValue, questGlobalVarValue);
QuestSystem.getLogger().debug("questGlobarVar {} {} : {}", questId, targetValue, questGlobalVarValue);
return questGlobalVarValue == targetValue;
}

View File

@ -4,6 +4,7 @@ import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.common.quest.SubQuestData.*;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.QuestSystem;
import emu.grasscutter.game.quest.QuestValueCond;
import lombok.val;
@ -17,7 +18,7 @@ public class ConditionQuestGlobalVarGreater extends BaseCondition {
val questId = condition.getParam()[0];
val minValue = condition.getParam()[1];
Integer questGlobalVarValue = owner.getQuestManager().getQuestGlobalVarValue(questId);
Grasscutter.getLogger().debug("questGlobarVar {} {} : {}", questId, minValue, questGlobalVarValue);
QuestSystem.getLogger().debug("questGlobarVar {} {} : {}", questId, minValue, questGlobalVarValue);
return questGlobalVarValue > minValue;
}

View File

@ -4,6 +4,7 @@ import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.common.quest.SubQuestData.*;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.QuestSystem;
import emu.grasscutter.game.quest.QuestValueCond;
import lombok.val;
@ -17,7 +18,7 @@ public class ConditionQuestGlobalVarLess extends BaseCondition {
val questId = condition.getParam()[0];
val maxValue = condition.getParam()[1];
Integer questGlobalVarValue = owner.getQuestManager().getQuestGlobalVarValue(questId);
Grasscutter.getLogger().debug("questGlobarVar {} {} : {}", questId, maxValue, questGlobalVarValue);
QuestSystem.getLogger().debug("questGlobarVar {} {} : {}", questId, maxValue, questGlobalVarValue);
return questGlobalVarValue < maxValue;
}

View File

@ -4,6 +4,7 @@ import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.common.quest.SubQuestData.*;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestSystem;
import emu.grasscutter.game.quest.QuestValueCond;
import lombok.val;
@ -16,12 +17,12 @@ public class ConditionStateEqual extends BaseCondition {
public boolean execute(Player owner, SubQuestData questData, QuestAcceptCondition condition, String paramStr, int... params) {
val questId = condition.getParam()[0];
val questStateValue = condition.getParam()[1];
GameQuest checkQuest = owner.getQuestManager().getQuestById(questId);
val checkQuest = owner.getQuestManager().getQuestById(questId);
if (checkQuest == null) {
/*
Will spam the console
*/
//Grasscutter.getLogger().debug("Warning: quest {} hasn't been started yet!", condition.getParam()[0]);
//QuestSystem.getLogger().debug("Warning: quest {} hasn't been started yet!", condition.getParam()[0]);
return false;
}
return checkQuest.getState().getValue() == questStateValue;

View File

@ -4,6 +4,7 @@ import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.common.quest.SubQuestData.*;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestSystem;
import emu.grasscutter.game.quest.QuestValueCond;
import lombok.val;
@ -21,7 +22,7 @@ public class ConditionStateNotEqual extends BaseCondition {
/*
Will spam the console
*/
//Grasscutter.getLogger().debug("Warning: quest {} hasn't been started yet!", condition.getParam()[0]);
//QuestSystem.getLogger().debug("Warning: quest {} hasn't been started yet!", condition.getParam()[0]);
return false;
}

View File

@ -1,20 +1,28 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.game.quest.handlers.QuestBaseHandler;
import lombok.val;
@QuestValueContent(QuestContent.QUEST_CONTENT_NONE)
public class BaseContent extends QuestBaseHandler<QuestContentCondition> {
public abstract class BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
// TODO Auto-generated method stub
Grasscutter.getLogger().error("Unknown condition {} at {}", condition.getType().name(), quest.getSubQuestId());
return false;
}
public boolean isEvent(SubQuestData questData, QuestContentCondition condition, QuestContent type, String paramStr, int... params){
return condition.getType() == type && condition.getParam()[0] == params[0];
}
public int initialCheck(GameQuest quest, SubQuestData questData, QuestContentCondition condition) {
return 0;
}
public int updateProgress(GameQuest quest, int currentProgress, QuestContentCondition condition,
String paramStr, int... params){
return currentProgress+1;
}
public boolean checkProgress(GameQuest quest, QuestContentCondition condition, int currentProgress){
val target = condition.getCount() > 0 ? condition.getCount() : 1;
return currentProgress >= target;
}
}

View File

@ -1,5 +1,6 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
@ -11,13 +12,14 @@ import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_ADD_QU
public class ContentAddQuestProgress extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
public int initialCheck(GameQuest quest, SubQuestData questData, QuestContentCondition condition) {
val progressId = condition.getParam()[0];
val currentCount = quest.getOwner().getPlayerProgress().getCurrentProgress(progressId);
// if the condition count is 0 I think it is safe to assume that the
// condition count from EXEC only needs to be 1
return currentCount >= condition.getCount();
return quest.getOwner().getPlayerProgress().getCurrentProgress(progressId);
}
@Override
public int updateProgress(GameQuest quest, int currentProgress, QuestContentCondition condition,
String paramStr, int... params) {
return params[1];
}
}

View File

@ -1,15 +1,17 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import emu.grasscutter.game.quest.enums.QuestContent;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_ANY_MANUAL_TRANSPORT;
@QuestValueContent(QUEST_CONTENT_ANY_MANUAL_TRANSPORT)
public class ContentAnyManualTransport extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
return true;
public boolean isEvent(SubQuestData questData, QuestContentCondition condition, QuestContent type,
String paramStr, int... params) {
return type == condition.getType();
}
}

View File

@ -1,19 +1,10 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import lombok.val;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_CLEAR_GROUP_MONSTER;
@QuestValueContent(QUEST_CONTENT_CLEAR_GROUP_MONSTER)
public class ContentClearGroupMonster extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
val groupId = condition.getParam()[0];
return quest.getOwner().getScene().getScriptManager().isClearedGroupMonsters(groupId);
}
// param[0]: groupId
}

View File

@ -1,12 +1,14 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestSystem;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import emu.grasscutter.game.quest.enums.QuestContent;
import lombok.val;
import javax.annotation.Nullable;
import java.util.Arrays;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_COMPLETE_ANY_TALK;
@ -15,25 +17,37 @@ import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_COMPLE
public class ContentCompleteAnyTalk extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, @Nullable String paramStr, int... params) {
public int initialCheck(GameQuest quest, SubQuestData questData, QuestContentCondition condition) {
if(condition.getParamString() == null) {
Grasscutter.getLogger().warn("Quest {} has no param string for QUEST_CONTENT_COMPLETE_ANY_TALK!", quest.getSubQuestId());
return false;
QuestSystem.getLogger().error("Quest {} has no param string for QUEST_CONTENT_COMPLETE_ANY_TALK!", questData.getSubId());
return 0;
}
val talkId = params[0];
val conditionTalk = Arrays.stream(condition.getParamString().split(","))
.mapToInt(Integer::parseInt)
.toArray();
return Arrays.stream(conditionTalk).anyMatch(tids -> tids == talkId)
|| Arrays.stream(conditionTalk).anyMatch(tids -> {
val checkMainQuest = quest.getOwner().getQuestManager().getMainQuestByTalkId(tids);
if (checkMainQuest == null) {
return Arrays.stream(conditionTalk).anyMatch(talkId -> {
val checkMainQuest = quest.getOwner().getQuestManager().getMainQuestByTalkId(talkId);
if (checkMainQuest == null || checkMainQuest.getParentQuestId() != questData.getMainId()) {
return false;
}
val talkData = checkMainQuest.getTalks().get(talkId);
return talkData != null;
});
}) ? 1 : 0;
}
@Override
public boolean isEvent(SubQuestData questData, QuestContentCondition condition, QuestContent type,
String paramStr, int... params) {
if(condition.getType() != type){
return false;
}
if(condition.getParamString() == null) {
QuestSystem.getLogger().error("Quest {} has no param string for QUEST_CONTENT_COMPLETE_ANY_TALK!", questData.getSubId());
return false;
}
val talkId = params[0];
val conditionTalk = Arrays.stream(condition.getParamString().split(","))
.mapToInt(Integer::parseInt)
.toArray();
return Arrays.stream(conditionTalk).anyMatch(tids -> tids == talkId);
}
}

View File

@ -1,5 +1,6 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
@ -11,14 +12,14 @@ import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_COMPLE
public class ContentCompleteTalk extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
public int initialCheck(GameQuest quest, SubQuestData questData, QuestContentCondition condition) {
val talkId = condition.getParam()[0];
val checkMainQuest = quest.getOwner().getQuestManager().getMainQuestByTalkId(talkId);
if (checkMainQuest == null) {
return false;
if (checkMainQuest == null || checkMainQuest.getParentQuestId() != questData.getMainId()) {
return 0;
}
val talkData = checkMainQuest.getTalks().get(talkId);
return talkData != null;
return talkData != null? 1 : 0;
}
}

View File

@ -1,15 +1,9 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_DESTROY_GADGET;
@QuestValueContent(QUEST_CONTENT_DESTROY_GADGET)
public class ContentDestroyGadget extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
return condition.getParam()[0] == params[0];
}
}

View File

@ -1,17 +1,11 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_ENTER_DUNGEON;
@QuestValueContent(QUEST_CONTENT_ENTER_DUNGEON)
public class ContentEnterDungeon extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
return condition.getParam()[0] == params[0]; //missing params[1]
}
// TODO should we also check param1?
}

View File

@ -1,17 +1,10 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_ENTER_MY_WORLD;
@QuestValueContent(QUEST_CONTENT_ENTER_MY_WORLD)
public class ContentEnterMyWorld extends BaseContent {
// params[0] scene ID
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
return condition.getParam()[0] == params[0];
}
}

View File

@ -1,17 +1,10 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_ENTER_MY_WORLD_SCENE;
@QuestValueContent(QUEST_CONTENT_ENTER_MY_WORLD_SCENE)
public class ContentEnterMyWorldScene extends BaseContent {
// params[0] scene ID
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
return condition.getParam()[0] == params[0];
}
}

View File

@ -1,17 +1,10 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_ENTER_ROOM;
@QuestValueContent(QUEST_CONTENT_ENTER_ROOM)
public class ContentEnterRoom extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
return condition.getParam()[0] == params[0];
}
}

View File

@ -1,15 +1,21 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.game.quest.enums.QuestContent;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_ENTER_VEHICLE;
@QuestValueContent(QUEST_CONTENT_ENTER_VEHICLE)
public class ContentEnterVehicle extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
return condition.getParam()[0] == params[0];
public boolean isEvent(SubQuestData questData, QuestContentCondition condition, QuestContent type,
String paramStr, int... params) {
if(condition.getType() != type){
return false;
}
// one is 0, so probably any vehicle?
return (condition.getParam().length == 0 || condition.getParam()[0] == params[0] || condition.getParam()[0] == 0);
}
}

View File

@ -1,18 +1,10 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_FAIL_DUNGEON;
@QuestValueContent(QUEST_CONTENT_FAIL_DUNGEON)
public class ContentFailDungeon extends BaseContent {
// params[0] dungeon ID
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
return condition.getParam()[0] == params[0];
}
}

View File

@ -1,18 +1,10 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_FINISH_DUNGEON;
@QuestValueContent(QUEST_CONTENT_FINISH_DUNGEON)
public class ContentFinishDungeon extends BaseContent {
// params[0] dungeon ID, params[1] unknown
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
return condition.getParam()[0] == params[0];
}
}

View File

@ -1,23 +1,13 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.data.common.quest.MainQuestData;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_FINISH_PLOT;
/**
* This is triggered by the client
*/
@QuestValueContent(QUEST_CONTENT_FINISH_PLOT)
public class ContentFinishPlot extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
if(params.length == 0) {
return false;
}
MainQuestData.TalkData talkData = quest.getMainQuest().getTalks().get(params[0]);
GameQuest subQuest = quest.getMainQuest().getChildQuestById(params[0]);
return talkData != null && subQuest != null || condition.getParam()[0] == params[0];
}
// params[0] plot ID
}

View File

@ -1,9 +1,12 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestSystem;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.game.quest.enums.QuestContent;
import lombok.val;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_GAME_TIME_TICK;
@ -12,9 +15,23 @@ import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_GAME_T
public class ContentGameTimeTick extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
if(condition.getParamString()==null) {
Grasscutter.getLogger().warn("Quest {} has no param string for QUEST_CONTENT_GAME_TIME_TICK!", quest.getSubQuestId());
public boolean isEvent(SubQuestData questData, QuestContentCondition condition, QuestContent type, String paramStr, int... params) {
return condition.getType() == type;
}
@Override
public int initialCheck(GameQuest quest, SubQuestData questData, QuestContentCondition condition) {
return isTimeMet(quest, condition) ? 1 : 0;
}
@Override
public int updateProgress(GameQuest quest, int currentProgress, QuestContentCondition condition, String paramStr, int... params) {
return isTimeMet(quest, condition) ? 1 : 0;
}
private boolean isTimeMet(GameQuest quest, QuestContentCondition condition) {
if (condition.getParamString() == null) {
QuestSystem.getLogger().error("Quest {} has no param string for QUEST_CONTENT_GAME_TIME_TICK!", quest.getSubQuestId());
return false;
}
val daysSinceStart = quest.getOwner().getWorld().getTotalGameTimeDays() - quest.getStartGameDay();
@ -32,7 +49,7 @@ public class ContentGameTimeTick extends BaseContent {
val isTimeMet = from < to ? currentHour >= from && currentHour < to
: currentHour < to || currentHour >= from;
val isDaysSinceMet = daysSinceStart >= daysToPass+daysMod;
val isDaysSinceMet = daysSinceStart >= daysToPass + daysMod;
return isTimeMet && isDaysSinceMet;
}

View File

@ -1,17 +1,10 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_INTERACT_GADGET;
@QuestValueContent(QUEST_CONTENT_INTERACT_GADGET)
public class ContentInteractGadget extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
return params[0] == condition.getParam()[0];
}
// params[0] gadget ID
}

View File

@ -1,16 +1,27 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import lombok.val;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_ITEM_LESS_THAN;
@QuestValueContent(QUEST_CONTENT_ITEM_LESS_THAN)
public class ContentItemLessThan extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
return condition.getParam()[0] == params[0] && condition.getCount() > params[1];
public int initialCheck(GameQuest quest, SubQuestData questData, QuestContentCondition condition) {
val itemId = condition.getParam()[0];
val targetCount = condition.getCount();
val itemCount = quest.getOwner().getInventory().getItemCountById(itemId);
return itemCount < targetCount ? 1 : 0;
}
@Override
public int updateProgress(GameQuest quest, int currentProgress, QuestContentCondition condition,
String paramStr, int... params) {
val targetCount = condition.getCount();
return params[1] < targetCount ? 1 : 0;
}
}

View File

@ -1,15 +1,9 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_KILL_MONSTER;
@QuestValueContent(QUEST_CONTENT_KILL_MONSTER)
public class ContentKillMonster extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
return condition.getParam()[0] == params[0];
}
}

View File

@ -1,17 +1,10 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_LEAVE_SCENE;
@QuestValueContent(QUEST_CONTENT_LEAVE_SCENE)
public class ContentLeaveScene extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
return quest.getOwner().getScene().getPrevScene() == params[0];
}
}

View File

@ -1,9 +1,11 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import emu.grasscutter.game.quest.QuestSystem;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.game.quest.enums.QuestContent;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_LUA_NOTIFY;
@ -11,12 +13,14 @@ import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_LUA_NO
public class ContentLuaNotify extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
public boolean isEvent(SubQuestData questData, QuestContentCondition condition, QuestContent type, String paramStr, int... params) {
if (condition.getType() != type) {
return false;
}
if (condition.getParamString() == null) {
Grasscutter.getLogger().warn("Quest {} has no param string for QUEST_CONTENT_LUA_NOTIFY!", quest.getSubQuestId());
QuestSystem.getLogger().error("Quest {} has no param string for QUEST_CONTENT_LUA_NOTIFY!", questData.getSubId());
return false;
}
return condition.getParamString().equals(paramStr);
}
}

View File

@ -1,15 +1,10 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_MONSTER_DIE;
@QuestValueContent(QUEST_CONTENT_MONSTER_DIE)
public class ContentMonsterDie extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
return condition.getParam()[0] == params[0];
}
// params[0] monster ID
}

View File

@ -0,0 +1,21 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.game.quest.enums.QuestContent;
@QuestValueContent(QuestContent.QUEST_CONTENT_NONE)
public class ContentNone extends BaseContent {
@Override
public int updateProgress(GameQuest quest, int currentProgress, QuestContentCondition condition,
String paramStr, int... params) {
return 1;
}
@Override
public boolean checkProgress(GameQuest quest, QuestContentCondition condition, int currentProgress) {
return true;
}
}

View File

@ -1,16 +1,13 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import lombok.val;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_NOT_FINISH_PLOT;
@QuestValueContent(QUEST_CONTENT_NOT_FINISH_PLOT)
public class ContentNotFinishPlot extends BaseContent {
@Override
/*@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
val talkId = condition.getParam()[0];
val checkMainQuest = quest.getOwner().getQuestManager().getMainQuestByTalkId(talkId);
@ -19,6 +16,6 @@ public class ContentNotFinishPlot extends BaseContent {
}
val talkData = checkMainQuest.getTalks().get(talkId);
return talkData == null;
}
}*/
}

View File

@ -1,19 +1,25 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import lombok.val;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_OBTAIN_ITEM;
@QuestValueContent(QUEST_CONTENT_OBTAIN_ITEM)
public class ContentObtainItem extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
var targetCount = condition.getCount();
if (targetCount == 0) {
targetCount = 1;
}
return condition.getParam()[0] == params[0] && targetCount <= params[1];
public int initialCheck(GameQuest quest, SubQuestData questData, QuestContentCondition condition) {
val itemId = condition.getParam()[0];
return quest.getOwner().getInventory().getItemCountById(itemId);
}
@Override
public int updateProgress(GameQuest quest, int currentProgress, QuestContentCondition condition,
String paramStr, int... params) {
return params[1];
}
}

View File

@ -0,0 +1,57 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestSystem;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.game.quest.enums.QuestContent;
import lombok.val;
import java.util.Arrays;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_OBTAIN_VARIOUS_ITEM;
@QuestValueContent(QUEST_CONTENT_OBTAIN_VARIOUS_ITEM)
public class ContentObtainVariousItem extends BaseContent {
@Override
public boolean isEvent(SubQuestData questData, QuestContentCondition condition, QuestContent type, String paramStr, int... params) {
if(condition.getType() != type){
return false;
}
if (condition.getParamString() == null || condition.getParamString().isEmpty()) {
QuestSystem.getLogger().error("Quest {} has no param string for QUEST_CONTENT_OBTAIN_VARIOUS_ITEM!", questData.getSubId());
return false;
}
val eventItemId = params[0];
val itemIds = Arrays.stream(condition.getParamString().split(",")).mapToInt(Integer::parseInt).toArray();
return Arrays.stream(itemIds).anyMatch(id -> id == eventItemId);
}
@Override
public int initialCheck(GameQuest quest, SubQuestData questData, QuestContentCondition condition) {
return getAllItemCount(quest, condition.getParamString());
}
@Override
public int updateProgress(GameQuest quest, int currentProgress, QuestContentCondition condition,
String paramStr, int... params) {
return getAllItemCount(quest, condition.getParamString());
}
// TODO we probably need to collect count if each item
private int getAllItemCount(GameQuest quest, String paramString) {
if (paramString == null || paramString.isEmpty()) {
return 0;
}
int[] count = {0};
val inventory = quest.getOwner().getInventory();
Arrays.stream(paramString.split(",")).mapToInt(Integer::parseInt).forEach(id -> {
val itemCount = inventory.getItemCountById(id);
count[0] += itemCount;
});
return count[0];
}
}

View File

@ -1,15 +1,23 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_PLAYER_LEVEL_UP;
@QuestValueContent(QUEST_CONTENT_PLAYER_LEVEL_UP)
public class ContentPlayerLevelUp extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
return quest.getOwner().getLevel() >= condition.getCount();
public int initialCheck(GameQuest quest, SubQuestData questData, QuestContentCondition condition) {
return quest.getOwner().getLevel();
}
@Override
public int updateProgress(GameQuest quest, int currentProgress, QuestContentCondition condition,
String paramStr, int... params) {
return params[0];
}
}

View File

@ -1,8 +1,10 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import lombok.val;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_QUEST_STATE_EQUAL;
@ -10,12 +12,20 @@ import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_QUEST_
public class ContentQuestStateEqual extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
GameQuest checkQuest = quest.getOwner().getQuestManager().getQuestById(condition.getParam()[0]);
public int initialCheck(GameQuest quest, SubQuestData questData, QuestContentCondition condition) {
val questId = condition.getParam()[0];
val questState = condition.getParam()[1];
val checkQuest = quest.getOwner().getQuestManager().getQuestById(questId);
if (checkQuest == null) {
return false;
return 0;
}
return checkQuest.getState().getValue() == params[1];
return checkQuest.getState().getValue() == questState ? 1 : 0;
}
@Override
public int updateProgress(GameQuest quest, int currentProgress, QuestContentCondition condition, String paramStr, int... params) {
val requiredQuestState = condition.getParam()[1];
val currentQuestState = params[1];
return requiredQuestState == currentQuestState ? 1 : 0;
}
}

View File

@ -1,23 +1,31 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import lombok.val;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_QUEST_STATE_NOT_EQUAL;
@QuestValueContent(QUEST_CONTENT_QUEST_STATE_NOT_EQUAL)
public class ContentQuestStateNotEqual extends BaseContent {
@Override
public int initialCheck(GameQuest quest, SubQuestData questData, QuestContentCondition condition) {
val questId = condition.getParam()[0];
val questState = condition.getParam()[1];
GameQuest checkQuest = quest.getOwner().getQuestManager().getQuestById(questId);
if (checkQuest == null) {
return 0;
}
return checkQuest.getState().getValue() != questState ? 1 : 0;
}
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
GameQuest checkQuest = quest.getOwner().getQuestManager().getQuestById(params[0]);
if (checkQuest != null) {
return checkQuest.getState().getValue() != params[1];
}
return false;
public int updateProgress(GameQuest quest, int currentProgress, QuestContentCondition condition, String paramStr, int... params) {
val requiredQuestState = condition.getParam()[1];
val currentQuestState = params[1];
return requiredQuestState != currentQuestState ? 1 : 0;
}
}

View File

@ -1,9 +1,10 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import lombok.val;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_QUEST_VAR_EQUAL;
@ -11,9 +12,17 @@ import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_QUEST_
public class ContentQuestVarEqual extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
int questVarValue = quest.getMainQuest().getQuestVars()[params[0]];
Grasscutter.getLogger().debug("questVar {} : {}", params[0], questVarValue);
return questVarValue == params[1];
public int initialCheck(GameQuest quest, SubQuestData questData, QuestContentCondition condition) {
val questVarIndex = condition.getParam()[0];
val questVarValueTarget = condition.getParam()[1];
val questVarValue = quest.getMainQuest().getQuestVars()[questVarIndex];
return questVarValue == questVarValueTarget ? 1 : 0;
}
@Override
public int updateProgress(GameQuest quest, int currentProgress, QuestContentCondition condition, String paramStr, int... params) {
val questVarValueTarget = condition.getParam()[1];
val currentValue = params[1];
return currentValue == questVarValueTarget ? 1 : 0;
}
}

View File

@ -1,19 +1,27 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import lombok.val;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_QUEST_VAR_GREATER;
@QuestValueContent(QUEST_CONTENT_QUEST_VAR_GREATER)
public class ContentQuestVarGreater extends BaseContent {
@Override
public int initialCheck(GameQuest quest, SubQuestData questData, QuestContentCondition condition) {
val questVarIndex = condition.getParam()[0];
val questVarValueTarget = condition.getParam()[1];
val questVarValue = quest.getMainQuest().getQuestVars()[questVarIndex];
return questVarValue > questVarValueTarget ? 1 : 0;
}
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
int questVarValue = quest.getMainQuest().getQuestVars()[params[0]];
Grasscutter.getLogger().debug("questVar {} : {}", params[0], questVarValue);
return questVarValue > params[1];
public int updateProgress(GameQuest quest, int currentProgress, QuestContentCondition condition, String paramStr, int... params) {
val questVarValueTarget = condition.getParam()[1];
val currentValue = params[1];
return currentValue > questVarValueTarget ? 1 : 0;
}
}

View File

@ -1,9 +1,10 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import lombok.val;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_QUEST_VAR_LESS;
@ -11,9 +12,17 @@ import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_QUEST_
public class ContentQuestVarLess extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
int questVarValue = quest.getMainQuest().getQuestVars()[params[0]];
Grasscutter.getLogger().debug("questVar {} : {}", params[0], questVarValue);
return questVarValue < params[1];
public int initialCheck(GameQuest quest, SubQuestData questData, QuestContentCondition condition) {
val questVarIndex = condition.getParam()[0];
val questVarValueTarget = condition.getParam()[1];
val questVarValue = quest.getMainQuest().getQuestVars()[questVarIndex];
return questVarValue < questVarValueTarget ? 1 : 0;
}
@Override
public int updateProgress(GameQuest quest, int currentProgress, QuestContentCondition condition, String paramStr, int... params) {
val questVarValueTarget = condition.getParam()[1];
val currentValue = params[1];
return currentValue < questVarValueTarget ? 1 : 0;
}
}

View File

@ -1,15 +1,9 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_SKILL;
@QuestValueContent(QUEST_CONTENT_SKILL)
public class ContentSkill extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
return condition.getParam()[0] == params[0];
}
}

View File

@ -1,18 +1,30 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestSystem;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.game.quest.enums.QuestContent;
import lombok.val;
@QuestValueContent(QuestContent.QUEST_CONTENT_TIME_VAR_GT_EQ)
public class ContentTimeVarMoreOrEqual extends BaseContent{
public class ContentTimeVarMoreOrEqual extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
public int initialCheck(GameQuest quest, SubQuestData questData, QuestContentCondition condition) {
return checkHoursSinceTimeVarCompleted(quest, condition) ? 1 : 0;
}
@Override
public int updateProgress(GameQuest quest, int currentProgress, QuestContentCondition condition,
String paramStr, int... params) {
return checkHoursSinceTimeVarCompleted(quest, condition) ? 1 : 0;
}
public boolean checkHoursSinceTimeVarCompleted(GameQuest quest, QuestContentCondition condition) {
if (condition.getParamString() == null) {
Grasscutter.getLogger().warn("Quest {} has no param string for QUEST_CONTENT_TIME_VAR_GT_EQ!", quest.getSubQuestId());
QuestSystem.getLogger().error("Quest {} has no param string for QUEST_CONTENT_TIME_VAR_GT_EQ!", quest.getSubQuestId());
return false;
}
val mainQuestId = condition.getParam()[0];
@ -21,7 +33,7 @@ public class ContentTimeVarMoreOrEqual extends BaseContent{
val mainQuest = quest.getOwner().getQuestManager().getMainQuestById(mainQuestId);
if(mainQuest == null){
if (mainQuest == null) {
return false;
}

View File

@ -1,18 +1,31 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestSystem;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.game.quest.enums.QuestContent;
import lombok.val;
@QuestValueContent(QuestContent.QUEST_CONTENT_TIME_VAR_PASS_DAY)
public class ContentTimeVarPassDay extends BaseContent{
public class ContentTimeVarPassDay extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
public int initialCheck(GameQuest quest, SubQuestData questData, QuestContentCondition condition) {
return checkDaysSinceTimeVarCompleted(quest, condition) ? 1 : 0;
}
@Override
public int updateProgress(GameQuest quest, int currentProgress, QuestContentCondition condition,
String paramStr, int... params) {
return checkDaysSinceTimeVarCompleted(quest, condition) ? 1 : 0;
}
private boolean checkDaysSinceTimeVarCompleted(GameQuest quest, QuestContentCondition condition) {
if (condition.getParamString() == null) {
Grasscutter.getLogger().warn("Quest {} has no param string for QUEST_CONTENT_TIME_VAR_PASS_DAY!", quest.getSubQuestId());
QuestSystem.getLogger().error("Quest {} has no param string for QUEST_CONTENT_TIME_VAR_PASS_DAY!", quest.getSubQuestId());
return false;
}
val mainQuestId = condition.getParam()[0];
@ -21,12 +34,12 @@ public class ContentTimeVarPassDay extends BaseContent{
val mainQuest = quest.getOwner().getQuestManager().getMainQuestById(mainQuestId);
if(mainQuest == null){
if (mainQuest == null) {
return false;
}
val daysSinceTimeVar = mainQuest.getDaysSinceTimeVar(timeVarIndex);
if(daysSinceTimeVar == -1){
if (daysSinceTimeVar == -1) {
return false;
}

View File

@ -1,23 +1,9 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_TRIGGER_FIRE;
@QuestValueContent(QUEST_CONTENT_TRIGGER_FIRE)
public class ContentTriggerFire extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
if (quest.getTriggers().containsKey(quest.getTriggerNameById(params[0]))) {
//We don't want to put a new key here
return quest.getTriggers().get(quest.getTriggerNameById(params[0]));
} else {
Grasscutter.getLogger().error("quest {} doesn't have trigger {}", quest.getSubQuestId(), params[0]);
return false;
}
}
}

View File

@ -0,0 +1,24 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestSystem;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.game.quest.enums.QuestContent;
@QuestValueContent(QuestContent.QUEST_CONTENT_UNKNOWN)
public class ContentUnknown extends BaseContent {
@Override
public int updateProgress(GameQuest quest, int currentProgress, QuestContentCondition condition,
String paramStr, int... params) {
return 0;
}
@Override
public boolean checkProgress(GameQuest quest, QuestContentCondition condition, int currentProgress) {
QuestSystem.getLogger().error("Unknown condition {} at {}", condition.getType().name(), quest.getSubQuestId());
return false;
}
}

View File

@ -1,15 +1,33 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import emu.grasscutter.game.quest.enums.QuestContent;
import lombok.val;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_UNLOCK_AREA;
@QuestValueContent(QUEST_CONTENT_UNLOCK_AREA)
public class ContentUnlockArea extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
return condition.getParam()[0] == params[0] || condition.getParam()[1] == params[1];
public int initialCheck(GameQuest quest, SubQuestData questData, QuestContentCondition condition) {
val sceneId = condition.getParam()[0];
val areaId = condition.getParam()[1];
val unlockedAreas = quest.getOwner().getUnlockedSceneAreas(sceneId);
return unlockedAreas != null && unlockedAreas.contains(areaId) ? 1 : 0;
}
@Override
public boolean isEvent(SubQuestData questData, QuestContentCondition condition, QuestContent type,
String paramStr, int... params) {
if(condition.getType() != type){
return false;
}
val sceneId = condition.getParam()[0];
val areaId = condition.getParam()[1];
return params[0] == sceneId && params[1] == areaId;
}
}

View File

@ -1,19 +1,33 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import emu.grasscutter.game.quest.enums.QuestContent;
import lombok.val;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_UNLOCK_TRANS_POINT;
@QuestValueContent(QUEST_CONTENT_UNLOCK_TRANS_POINT)
public class ContentUnlockTransPoint extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
public int initialCheck(GameQuest quest, SubQuestData questData, QuestContentCondition condition) {
val sceneId = condition.getParam()[0];
val scenePointId = condition.getParam()[1];
val scenePoints = quest.getOwner().getUnlockedScenePoints().get(sceneId);
return scenePoints != null && scenePoints.contains(scenePointId);
return scenePoints != null && scenePoints.contains(scenePointId) ? 1 : 0;
}
@Override
public boolean isEvent(SubQuestData questData, QuestContentCondition condition, QuestContent type,
String paramStr, int... params) {
if(condition.getType() != type){
return false;
}
val sceneId = condition.getParam()[0];
val scenePointId = condition.getParam()[1];
return params[0] == sceneId && params[1] == scenePointId;
}
}

View File

@ -1,15 +1,9 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_USE_ITEM;
@QuestValueContent(QUEST_CONTENT_USE_ITEM)
public class ContentUseItem extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
return condition.getParam()[0] == params[0];
}
}

View File

@ -1,15 +1,17 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.common.quest.SubQuestData.QuestContentCondition;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.game.quest.enums.QuestContent;
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_WORKTOP_SELECT;
@QuestValueContent(QUEST_CONTENT_WORKTOP_SELECT)
public class ContentWorktopSelect extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
return condition.getParam()[0] == params[0] || condition.getParam()[1] == params[1];
public boolean isEvent(SubQuestData questData, QuestContentCondition condition, QuestContent type,
String paramStr, int... params) {
return condition.getType() == type && condition.getParam()[0] == params[0] && condition.getParam()[1] == params[1];
}
}

View File

@ -61,7 +61,7 @@ public enum QuestContent implements QuestTrigger {
QUEST_CONTENT_USE_ITEM (130),
QUEST_CONTENT_MAIN_COOP_ENTER_ANY_SAVE_POINT (131),// missing, finish and fail
QUEST_CONTENT_ENTER_MY_HOME_WORLD (132),// missing, finish and fail
QUEST_CONTENT_ENTER_MY_WORLD_SCENE (133),// missing, finish
QUEST_CONTENT_ENTER_MY_WORLD_SCENE (133),// finish
QUEST_CONTENT_TIME_VAR_GT_EQ (134),
QUEST_CONTENT_TIME_VAR_PASS_DAY (135),
QUEST_CONTENT_QUEST_STATE_EQUAL (136),

View File

@ -1,6 +1,7 @@
package emu.grasscutter.game.quest.exec;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestSystem;
import emu.grasscutter.game.quest.QuestValueExec;
import emu.grasscutter.game.quest.enums.QuestExec;
import emu.grasscutter.game.quest.handlers.QuestExecHandler;
@ -11,7 +12,7 @@ import emu.grasscutter.Grasscutter;
public class ExecAddCurAvatarEnergy extends QuestExecHandler {
@Override
public boolean execute(GameQuest quest, QuestExecParam condition, String... paramStr) {
Grasscutter.getLogger().info("Energy refilled");
QuestSystem.getLogger().info("Energy refilled");
return quest.getOwner().getEnergyManager().refillEntityAvatarEnergy();
}
}

View File

@ -3,6 +3,7 @@ package emu.grasscutter.game.quest.exec;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.props.ElementType;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestSystem;
import emu.grasscutter.game.quest.QuestValueExec;
import emu.grasscutter.game.quest.enums.QuestExec;
import emu.grasscutter.game.quest.handlers.QuestExecHandler;
@ -21,11 +22,11 @@ public class ExecChangeAvatarElemet extends QuestExecHandler {
val mainAvatar = owner.getAvatars().getAvatarById(owner.getMainCharacterId());
if(mainAvatar == null){
Grasscutter.getLogger().error("Failed to get main avatar for use {}", quest.getOwner().getUid());
QuestSystem.getLogger().error("Failed to get main avatar for use {}", quest.getOwner().getUid());
return false;
}
Grasscutter.getLogger().info("Changing avatar element to {} for quest {}", targetElement.name(), quest.getSubQuestId());
QuestSystem.getLogger().info("Changing avatar element to {} for quest {}", targetElement.name(), quest.getSubQuestId());
return mainAvatar.changeElement(targetElement);
}
}

View File

@ -2,6 +2,7 @@ package emu.grasscutter.game.quest.exec;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestSystem;
import emu.grasscutter.game.quest.QuestValueExec;
import emu.grasscutter.game.quest.enums.QuestState;
import emu.grasscutter.game.quest.enums.QuestExec;
@ -29,7 +30,7 @@ public class ExecNotifyGroupLua extends QuestExecHandler {
val groupInstance = scriptManager.getGroupInstanceById(groupId);
if(groupInstance==null) {
Grasscutter.getLogger().warn("notify, no group instance for:\n group: {} \ncondition: {} \nparamStr {}", groupId, condition, paramStr);
QuestSystem.getLogger().warn("notify, no group instance for:\n group: {} \ncondition: {} \nparamStr {}", groupId, condition, paramStr);
}
val eventType = quest.getState() == QuestState.QUEST_STATE_FINISHED ?

View File

@ -3,6 +3,7 @@ package emu.grasscutter.game.quest.exec;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestGroupSuite;
import emu.grasscutter.game.quest.QuestSystem;
import emu.grasscutter.game.quest.QuestValueExec;
import emu.grasscutter.game.quest.enums.QuestExec;
import emu.grasscutter.game.quest.handlers.QuestExecHandler;
@ -17,7 +18,7 @@ public class ExecRegisterDynamicGroup extends QuestExecHandler {
var sceneId = Integer.parseInt(paramStr[0]);
var groupId = Integer.parseInt(paramStr[1]);
Grasscutter.getLogger().warn("Registering group {}", groupId);
QuestSystem.getLogger().info("Registering group {}", groupId);
Scene scene = quest.getOwner().getWorld().getSceneById(sceneId);
if(scene == null) return false;
@ -31,7 +32,7 @@ public class ExecRegisterDynamicGroup extends QuestExecHandler {
.suite(suiteId)
.build());
Grasscutter.getLogger().warn("Registered group {}, suite {} in scene {}", groupId, suiteId, scene.getId());
QuestSystem.getLogger().debug("Registered group {}, suite {} in scene {}", groupId, suiteId, scene.getId());
return true;
}

View File

@ -3,6 +3,7 @@ package emu.grasscutter.game.quest.exec;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestSystem;
import emu.grasscutter.game.quest.QuestValueExec;
import emu.grasscutter.game.quest.enums.QuestExec;
import emu.grasscutter.game.quest.handlers.QuestExecHandler;
@ -13,7 +14,7 @@ public class ExecRemoveTrialAvatar extends QuestExecHandler {
@Override
public boolean execute(GameQuest quest, QuestExecParam condition, String... paramStr) {
if (quest.getOwner().removeTrialAvatarForQuest(Integer.parseInt(paramStr[0]))) {
Grasscutter.getLogger().info("Removed trial avatar from team for quest {}", quest.getSubQuestId());
QuestSystem.getLogger().info("Removed trial avatar from team for quest {}", quest.getSubQuestId());
return true;
}
return false;

View File

@ -3,6 +3,7 @@ package emu.grasscutter.game.quest.exec;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.common.quest.SubQuestData.QuestExecParam;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestSystem;
import emu.grasscutter.game.quest.QuestValueExec;
import emu.grasscutter.game.quest.enums.QuestExec;
import emu.grasscutter.game.quest.handlers.QuestExecHandler;
@ -13,12 +14,12 @@ public class ExecSetGameTime extends QuestExecHandler {
@Override
public boolean execute(GameQuest quest, QuestExecParam condition, String... paramStr) {
if (paramStr.length < 1) {
Grasscutter.getLogger().warn("no game Time specified for QUEST_EXEC_SET_GAME_TIME in quest {}", quest.getSubQuestId());
QuestSystem.getLogger().error("no game Time specified for QUEST_EXEC_SET_GAME_TIME in quest {}", quest.getSubQuestId());
return false;
}
val lockTimeArgs = paramStr[0].split("\\.");
if (lockTimeArgs.length < 1) {
Grasscutter.getLogger().warn("no game Time specified for QUEST_EXEC_SET_GAME_TIME in quest {}: {}", quest.getSubQuestId(), paramStr[0]);
QuestSystem.getLogger().error("no game Time specified for QUEST_EXEC_SET_GAME_TIME in quest {}: {}", quest.getSubQuestId(), paramStr[0]);
return false;
}
try {
@ -27,7 +28,7 @@ public class ExecSetGameTime extends QuestExecHandler {
val targetTime = hours * 60 + minutes;
return quest.getOwner().getWorld().changeTime(targetTime, 0, true);
} catch (NumberFormatException e) {
Grasscutter.getLogger().warn("invalid game Time specified for QUEST_EXEC_SET_GAME_TIME in quest {}: {}", quest.getSubQuestId(), paramStr[0]);
QuestSystem.getLogger().error("invalid game Time specified for QUEST_EXEC_SET_GAME_TIME in quest {}: {}", quest.getSubQuestId(), paramStr[0]);
return false;
}
}

View File

@ -0,0 +1,20 @@
package emu.grasscutter.game.quest.exec;
import emu.grasscutter.data.common.quest.SubQuestData.QuestExecParam;
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;
import lombok.val;
@QuestValueExec(QuestExec.QUEST_EXEC_RANDOM_QUEST_VAR)
public class ExecSetRandomQuestVar extends QuestExecHandler {
@Override
public boolean execute(GameQuest quest, QuestExecParam condition, String... paramStr) {
val varIndex = Integer.parseInt(paramStr[0]);
val min = Integer.parseInt(paramStr[1]);
val max = Integer.parseInt(paramStr[2]);
quest.getMainQuest().setRandomQuestVar(varIndex, min, max);
return true;
}
}

View File

@ -2,6 +2,7 @@ package emu.grasscutter.game.quest.exec;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestSystem;
import emu.grasscutter.game.quest.QuestValueExec;
import emu.grasscutter.game.quest.enums.QuestExec;
import emu.grasscutter.game.quest.handlers.QuestExecHandler;
@ -17,7 +18,7 @@ public class ExecUnregisterDynamicGroup extends QuestExecHandler {
val unknownParam = Integer.parseInt(paramStr[1]); //TODO: Goes from 0 to 1, maybe is a boolean. Investigate
val scene = quest.getOwner().getScene();
Grasscutter.getLogger().warn("Unregistering group {}", groupId);
QuestSystem.getLogger().info("Unregistering group {}", groupId);
if(!scene.unregisterDynamicGroup(groupId)){
return false;
@ -26,7 +27,7 @@ public class ExecUnregisterDynamicGroup extends QuestExecHandler {
//Remove suites if they are registered
quest.getMainQuest().getQuestGroupSuites().removeIf(gs -> gs.getGroup() == groupId && gs.getScene() == scene.getId());
Grasscutter.getLogger().warn("Unregistered group {} in scene {}", groupId, scene.getId());
QuestSystem.getLogger().debug("Unregistered group {} in scene {}", groupId, scene.getId());
return true;
}

View File

@ -1,10 +0,0 @@
package emu.grasscutter.game.quest.handlers;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.game.quest.GameQuest;
public abstract class QuestBaseHandler<T extends SubQuestData.QuestCondition<?>> {
public abstract boolean execute(GameQuest quest, T condition, String paramStr, int... params);
}

View File

@ -0,0 +1,10 @@
package emu.grasscutter.scripts.data;
import emu.grasscutter.utils.Position;
import lombok.Data;
@Data
public class DummyPoint {
private Position pos;
private Position rot;
}

View File

@ -17,18 +17,19 @@ import lombok.val;
public class HandlerNpcTalkReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
NpcTalkReq req = NpcTalkReq.parseFrom(payload);
val req = NpcTalkReq.parseFrom(payload);
//Check if mainQuest exists
//remove last 2 digits to get a mainQuestId
int talkId = req.getTalkId();
int mainQuestId = GameData.getQuestTalkMap().getOrDefault(talkId, talkId / 100);
MainQuestData mainQuestData = GameData.getMainQuestDataMap().get(mainQuestId);
val mainQuestData = GameData.getMainQuestDataMap().get(mainQuestId);
val questManager = session.getPlayer().getQuestManager();
if (mainQuestData != null) {
// This talk is associated with a quest. Handle it.
// If the quest has no talk data defined on it, create one.
TalkData talkForQuest = new TalkData(talkId, "");
var talkForQuest = new TalkData(talkId, "");
if (mainQuestData.getTalks() != null) {
val talks = mainQuestData.getTalks().stream().filter(p -> p.getId() == talkId).toList();
@ -38,18 +39,16 @@ public class HandlerNpcTalkReq extends PacketHandler {
}
// Add to the list of done talks for this quest.
val questManager = session.getPlayer().getQuestManager();
val mainQuest = questManager.getMainQuestByTalkId(talkId);
if (mainQuest != null) {
mainQuest.getTalks().put(talkId, talkForQuest);
}
// Fire quest triggers.
questManager.queueEvent(QuestContent.QUEST_CONTENT_COMPLETE_ANY_TALK, talkId, 0, 0);
questManager.queueEvent(QuestContent.QUEST_CONTENT_COMPLETE_TALK, talkId, 0);
questManager.queueEvent(QuestContent.QUEST_CONTENT_FINISH_PLOT, talkId, 0);
questManager.queueEvent(QuestCond.QUEST_COND_COMPLETE_TALK, talkId, 0);
}
// Fire quest triggers either way, since a quest might depend on a talk not mapped to a quest.
questManager.queueEvent(QuestContent.QUEST_CONTENT_COMPLETE_ANY_TALK, talkId, 0, 0);
questManager.queueEvent(QuestContent.QUEST_CONTENT_COMPLETE_TALK, talkId, 0);
questManager.queueEvent(QuestCond.QUEST_COND_COMPLETE_TALK, talkId, 0);
session.send(new PacketNpcTalkRsp(req.getNpcEntityId(), req.getTalkId(), req.getEntityId()));
}

View File

@ -14,22 +14,28 @@ public class HandlerPostEnterSceneReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
val sceneId = session.getPlayer().getSceneId();
val questManager = session.getPlayer().getQuestManager();
switch (session.getPlayer().getScene().getSceneType()){
val player = session.getPlayer();
val sceneId = player.getSceneId();
val scene = player.getScene();
val questManager = player.getQuestManager();
switch (scene.getSceneType()){
case SCENE_ROOM -> questManager.queueEvent(QuestContent.QUEST_CONTENT_ENTER_ROOM, sceneId,0);
case SCENE_WORLD -> {
questManager.queueEvent(QuestContent.QUEST_CONTENT_ENTER_MY_WORLD, sceneId);
questManager.queueEvent(QuestContent.QUEST_CONTENT_ENTER_MY_WORLD_SCENE, sceneId);
}
case SCENE_DUNGEON -> {
val dungeonManager = session.getPlayer().getScene().getDungeonManager();
val dungeonManager = scene.getDungeonManager();
if (dungeonManager != null) dungeonManager.startDungeon();
}
}
if(scene.getPrevScene() != sceneId){
questManager.queueEvent(QuestContent.QUEST_CONTENT_LEAVE_SCENE, scene.getPrevScene());
}
session.send(new PacketPostEnterSceneRsp(session.getPlayer()));
session.send(new PacketPostEnterSceneRsp(player));
}
}

View File

@ -0,0 +1,21 @@
package emu.grasscutter.server.packet.send;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.QuestUpdateQuestVarNotifyOuterClass.QuestUpdateQuestVarNotify;
import lombok.val;
import java.util.stream.IntStream;
@Opcodes(PacketOpcodes.QuestUpdateQuestVarNotify)
public class PacketQuestUpdateQuestVarNotify extends BasePacket {
public PacketQuestUpdateQuestVarNotify(int mainQuestId, int... questVars) {
super(PacketOpcodes.QuestUpdateQuestVarNotify);
val questVarList = IntStream.of(questVars).boxed().toList();
this.setData(QuestUpdateQuestVarNotify.newBuilder()
.setParentQuestId(mainQuestId)
.addAllQuestVar(questVarList));
}
}