[Test] add some more quest conditions, content and exec

* QUEST_COND_IS_DAYTIME (unused)
* QUEST_COND_TIME_VAR_GT_EQ (unused)
* QUEST_COND_TIME_VAR_PASS_DAY
* QUEST_CONTENT_TIME_VAR_GT_EQ
* QUEST_CONTENT_TIME_VAR_PASS_DAY
* QUEST_EXEC_INIT_TIME_VAR
* QUEST_EXEC_CLEAR_TIME_VAR
* QUEST_CONTENT_CLEAR_GROUP_MONSTER (needs propper implementation for the check with new group management)
This commit is contained in:
hartie95 2023-01-20 23:08:17 +01:00
parent b245272874
commit d8c3ed3b85
16 changed files with 272 additions and 92 deletions

View File

@ -159,6 +159,7 @@ public class EntityMonster extends GameEntity {
scene.getPlayers().forEach(p -> p.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_MONSTER_DIE, this.getMonsterId()));
scene.getPlayers().forEach(p -> p.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_KILL_MONSTER, this.getMonsterId()));
scene.getPlayers().forEach(p -> p.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_CLEAR_GROUP_MONSTER, this.getGroupId()));
scene.triggerDungeonEvent(DungeonPassConditionType.DUNGEON_COND_KILL_GROUP_MONSTER, this.getGroupId());
scene.triggerDungeonEvent(DungeonPassConditionType.DUNGEON_COND_KILL_TYPE_MONSTER, this.getMonsterData().getType().getValue());

View File

@ -16,6 +16,7 @@ import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.quest.enums.*;
import emu.grasscutter.game.world.World;
import emu.grasscutter.net.proto.ChildQuestOuterClass.ChildQuest;
import emu.grasscutter.net.proto.ParentQuestOuterClass.ParentQuest;
import emu.grasscutter.server.packet.send.PacketCodexDataUpdateNotify;
@ -37,6 +38,7 @@ public class GameMainQuest {
@Getter private Map<Integer, GameQuest> childQuests;
@Getter private int parentQuestId;
@Getter private int[] questVars;
@Getter private long[] timeVar;
//QuestUpdateQuestVarReq is sent in two stages...
@Getter private List<Integer> questVarsUpdate;
@Getter private ParentQuestState state;
@ -58,6 +60,7 @@ public class GameMainQuest {
this.talks = new HashMap<>();
//official server always has a list of 5 questVars, with default value 0
this.questVars = new int[] {0,0,0,0,0};
this.timeVar = new long[] {-1,-1,-1,-1,-1};
this.state = ParentQuestState.PARENT_QUEST_STATE_NONE;
this.questGroupSuites = new ArrayList<>();
addAllChildQuests();
@ -428,4 +431,50 @@ public class GameMainQuest {
return proto.build();
}
// 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);
return false;
}
this.timeVar[index] = owner.getWorld().getWorldTimeSeconds();
return true;
}
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);
return false;
}
this.timeVar[index] = -1;
return true;
}
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);
return -1;
}
val varTime = timeVar[index];
if(varTime == -1){
return 0;
}
return owner.getWorld().getGameTimeDays() - World.getDaysForGameTime(varTime);
}
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);
return -1;
}
val varTime = timeVar[index];
if(varTime == -1){
return 0;
}
return owner.getWorld().getGameTimeDays() - World.getHoursForGameTime(varTime);
}
}

View File

@ -20,6 +20,7 @@ import io.netty.util.concurrent.FastThreadLocalThread;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter;
import lombok.val;
public class QuestManager extends BasePlayerManager {
@ -110,6 +111,9 @@ public class QuestManager extends BasePlayerManager {
}
}
public void onTick(){
}
private List<GameMainQuest> addMultMainQuests(Set<Integer> mainQuestIds) {
List<GameMainQuest> newQuests = new ArrayList<>();
for (Integer id : mainQuestIds) {
@ -273,33 +277,8 @@ public class QuestManager extends BasePlayerManager {
List<GameMainQuest> checkMainQuests = this.getMainQuests().values().stream()
.filter(i -> i.getState() != ParentQuestState.PARENT_QUEST_STATE_FINISHED)
.toList();
switch (condType) {
//accept Conds
case QUEST_COND_STATE_EQUAL:
case QUEST_COND_STATE_NOT_EQUAL:
case QUEST_COND_COMPLETE_TALK:
case QUEST_COND_LUA_NOTIFY:
case QUEST_COND_QUEST_VAR_EQUAL:
case QUEST_COND_QUEST_VAR_GREATER:
case QUEST_COND_QUEST_VAR_LESS:
case QUEST_COND_PLAYER_LEVEL_EQUAL_GREATER:
case QUEST_COND_QUEST_GLOBAL_VAR_EQUAL:
case QUEST_COND_QUEST_GLOBAL_VAR_GREATER:
case QUEST_COND_QUEST_GLOBAL_VAR_LESS:
case QUEST_COND_PACK_HAVE_ITEM:
case QUEST_COND_ITEM_NUM_LESS_THAN:
case QUEST_COND_ACTIVITY_OPEN:
case QUEST_COND_ACTIVITY_END:
case QUEST_COND_ACTIVITY_COND:
for (GameMainQuest mainquest : checkMainQuests) {
mainquest.tryAcceptSubQuests(condType, paramStr, params);
}
break;
// unused
case QUEST_COND_PLAYER_CHOOSE_MALE:
default:
Grasscutter.getLogger().error("Unhandled QuestCondition {}", condType);
for (GameMainQuest mainquest : checkMainQuests) {
mainquest.tryAcceptSubQuests(condType, paramStr, params);
}
}
public void triggerEvent(QuestContent condType, String paramStr, int... params) {
@ -307,62 +286,9 @@ public class QuestManager extends BasePlayerManager {
List<GameMainQuest> checkMainQuests = this.getMainQuests().values().stream()
.filter(i -> i.getState() != ParentQuestState.PARENT_QUEST_STATE_FINISHED)
.toList();
switch (condType) {
//fail Conds
case QUEST_CONTENT_NOT_FINISH_PLOT:
case QUEST_CONTENT_ANY_MANUAL_TRANSPORT:
for (GameMainQuest mainquest : checkMainQuests) {
mainquest.tryFailSubQuests(condType, paramStr, params);
}
break;
//finish Conds
case QUEST_CONTENT_COMPLETE_TALK:
case QUEST_CONTENT_FINISH_PLOT:
case QUEST_CONTENT_COMPLETE_ANY_TALK:
case QUEST_CONTENT_QUEST_VAR_EQUAL:
case QUEST_CONTENT_QUEST_VAR_GREATER:
case QUEST_CONTENT_QUEST_VAR_LESS:
case QUEST_CONTENT_ENTER_DUNGEON:
case QUEST_CONTENT_ENTER_MY_WORLD_SCENE:
case QUEST_CONTENT_INTERACT_GADGET:
case QUEST_CONTENT_TRIGGER_FIRE:
case QUEST_CONTENT_UNLOCK_TRANS_POINT:
case QUEST_CONTENT_UNLOCK_AREA:
case QUEST_CONTENT_SKILL:
case QUEST_CONTENT_OBTAIN_ITEM:
case QUEST_CONTENT_MONSTER_DIE:
case QUEST_CONTENT_DESTROY_GADGET:
case QUEST_CONTENT_PLAYER_LEVEL_UP:
case QUEST_CONTENT_USE_ITEM:
case QUEST_CONTENT_ENTER_VEHICLE:
case QUEST_CONTENT_FINISH_DUNGEON:
for (GameMainQuest mainQuest : checkMainQuests) {
mainQuest.tryFinishSubQuests(condType, paramStr, params);
}
break;
//finish Or Fail Conds
case QUEST_CONTENT_GAME_TIME_TICK:
case QUEST_CONTENT_QUEST_STATE_EQUAL:
case QUEST_CONTENT_ADD_QUEST_PROGRESS:
case QUEST_CONTENT_LEAVE_SCENE:
case QUEST_CONTENT_ITEM_LESS_THAN:
case QUEST_CONTENT_KILL_MONSTER:
case QUEST_CONTENT_LUA_NOTIFY:
case QUEST_CONTENT_ENTER_MY_WORLD:
case QUEST_CONTENT_ENTER_ROOM:
case QUEST_CONTENT_FAIL_DUNGEON:
for (GameMainQuest mainQuest : checkMainQuests) {
mainQuest.tryFailSubQuests(condType, paramStr, params);
mainQuest.tryFinishSubQuests(condType, paramStr, params);
}
break;
//Unused
case QUEST_CONTENT_QUEST_STATE_NOT_EQUAL:
case QUEST_CONTENT_WORKTOP_SELECT:
default:
Grasscutter.getLogger().error("Unhandled QuestTrigger {}", condType);
for (GameMainQuest mainQuest : checkMainQuests) {
mainQuest.tryFailSubQuests(condType, paramStr, params);
mainQuest.tryFinishSubQuests(condType, paramStr, params);
}
}

View File

@ -0,0 +1,20 @@
package emu.grasscutter.game.quest.conditions;
import emu.grasscutter.data.excels.QuestData;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.QuestValueCond;
import emu.grasscutter.game.quest.enums.QuestCond;
import lombok.val;
@QuestValueCond(QuestCond.QUEST_COND_IS_DAYTIME)
public class ConditionIsDaytime extends BaseCondition{
@Override
public boolean execute(Player owner, QuestData questData, QuestData.QuestAcceptCondition condition, String paramStr, int... params) {
val daytime = condition.getParam()[0] == 1;
val currentTime = owner.getWorld().getGameTimeHours();
// TODO is this the real timeframe?
return (currentTime >=6 && currentTime<=18) == daytime;
}
}

View File

@ -0,0 +1,25 @@
package emu.grasscutter.game.quest.conditions;
import emu.grasscutter.data.excels.QuestData;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.QuestValueCond;
import emu.grasscutter.game.quest.enums.QuestCond;
import lombok.val;
@QuestValueCond(QuestCond.QUEST_COND_TIME_VAR_GT_EQ)
public class ConditionTimeVarGreaterOrEqual extends BaseCondition{
@Override
public boolean execute(Player owner, QuestData questData, QuestData.QuestAcceptCondition condition, String paramStr, int... params) {
val mainQuestId = condition.getParam()[0];
val timeVarIndex = condition.getParam()[1];
val minTime = condition.getParam()[2];
val mainQuest = owner.getQuestManager().getMainQuestById(mainQuestId);
if(mainQuest == null){
return false;
}
return mainQuest.getHoursSinceTimeVar(timeVarIndex) >= minTime;
}
}

View File

@ -0,0 +1,30 @@
package emu.grasscutter.game.quest.conditions;
import emu.grasscutter.data.excels.QuestData;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.QuestValueCond;
import emu.grasscutter.game.quest.enums.QuestCond;
import lombok.val;
@QuestValueCond(QuestCond.QUEST_COND_TIME_VAR_PASS_DAY)
public class ConditionTimeVarPassDay extends BaseCondition{
@Override
public boolean execute(Player owner, QuestData questData, QuestData.QuestAcceptCondition condition, String paramStr, int... params) {
val mainQuestId = condition.getParam()[0];
val timeVarIndex = condition.getParam()[1];
val minDays = condition.getParam()[2];
val mainQuest = owner.getQuestManager().getMainQuestById(mainQuestId);
if(mainQuest == null){
return false;
}
val daysSinceTimeVar = mainQuest.getDaysSinceTimeVar(timeVarIndex);
if(daysSinceTimeVar == -1){
return false;
}
return daysSinceTimeVar >= minDays;
}
}

View File

@ -0,0 +1,16 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.data.excels.QuestData;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueContent;
import emu.grasscutter.game.quest.enums.QuestContent;
import lombok.val;
@QuestValueContent(QuestContent.QUEST_CONTENT_CLEAR_GROUP_MONSTER)
public class ContentClearGroupMonster extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
val groupId = condition.getParam()[0];
return quest.getOwner().getScene().getScriptManager().hasClearedGroupMonsters(groupId);
}
}

View File

@ -0,0 +1,25 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.data.excels.QuestData;
import emu.grasscutter.game.quest.GameQuest;
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{
@Override
public boolean execute(GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
val mainQuestId = condition.getParam()[0];
val timeVarIndex = condition.getParam()[1];
val minTime = Integer.parseInt(condition.getParamStr());
val mainQuest = quest.getOwner().getQuestManager().getMainQuestById(mainQuestId);
if(mainQuest == null){
return false;
}
return mainQuest.getHoursSinceTimeVar(timeVarIndex) >= minTime;
}
}

View File

@ -0,0 +1,30 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.data.excels.QuestData;
import emu.grasscutter.game.quest.GameQuest;
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{
@Override
public boolean execute(GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
val mainQuestId = condition.getParam()[0];
val timeVarIndex = condition.getParam()[1];
val minDays = Integer.parseInt(condition.getParamStr());
val mainQuest = quest.getOwner().getQuestManager().getMainQuestById(mainQuestId);
if(mainQuest == null){
return false;
}
val daysSinceTimeVar = mainQuest.getDaysSinceTimeVar(timeVarIndex);
if(daysSinceTimeVar == -1){
return false;
}
return daysSinceTimeVar >= minDays;
}
}

View File

@ -29,7 +29,7 @@ public enum QuestCond implements QuestTrigger {
QUEST_COND_SCENE_AREA_UNLOCKED (18), // missing, only NPC groups/talks
QUEST_COND_ITEM_GIVING_ACTIVED (19), // missing
QUEST_COND_ITEM_GIVING_FINISHED (20), // missing
QUEST_COND_IS_DAYTIME (21), // missing, only NPC groups
QUEST_COND_IS_DAYTIME (21), // only NPC groups
QUEST_COND_CURRENT_AVATAR (22), // missing
QUEST_COND_CURRENT_AREA (23), // missing
QUEST_COND_QUEST_VAR_EQUAL (24),
@ -73,8 +73,8 @@ public enum QuestCond implements QuestTrigger {
QUEST_COND_NEW_HOMEWORLD_LEVEL_REWARD (62), // missing, only Gadget groups
QUEST_COND_NEW_HOMEWORLD_MAKE_FINISH (63), // missing, only Gadget groups
QUEST_COND_HOMEWORLD_NPC_EVENT (64), // missing, only NPC groups
QUEST_COND_TIME_VAR_GT_EQ (65), // missing, currently unused
QUEST_COND_TIME_VAR_PASS_DAY (66), // missing
QUEST_COND_TIME_VAR_GT_EQ (65), // currently unused
QUEST_COND_TIME_VAR_PASS_DAY (66),
QUEST_COND_HOMEWORLD_NPC_NEW_TALK (67), // missing, only NPC groups
QUEST_COND_PLAYER_CHOOSE_MALE (68), // missing, only talks
QUEST_COND_HISTORY_GOT_ANY_ITEM (69),

View File

@ -15,7 +15,7 @@ public enum QuestContent implements QuestTrigger {
QUEST_CONTENT_FINISH_PLOT (4),
QUEST_CONTENT_OBTAIN_ITEM (5),
QUEST_CONTENT_TRIGGER_FIRE (6),
QUEST_CONTENT_CLEAR_GROUP_MONSTER (7), // missing, finish
QUEST_CONTENT_CLEAR_GROUP_MONSTER (7),
QUEST_CONTENT_NOT_FINISH_PLOT (8), // missing triggers, fail
QUEST_CONTENT_ENTER_DUNGEON (9),
QUEST_CONTENT_ENTER_MY_WORLD (10),
@ -62,8 +62,8 @@ public enum QuestContent implements QuestTrigger {
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_TIME_VAR_GT_EQ (134),// missing, finish
QUEST_CONTENT_TIME_VAR_PASS_DAY (135),// missing, finish
QUEST_CONTENT_TIME_VAR_GT_EQ (134),
QUEST_CONTENT_TIME_VAR_PASS_DAY (135),
QUEST_CONTENT_QUEST_STATE_EQUAL (136),
QUEST_CONTENT_QUEST_STATE_NOT_EQUAL (137),
QUEST_CONTENT_UNLOCKED_RECIPE (138),// missing, finish

View File

@ -65,8 +65,8 @@ public enum QuestExec implements QuestTrigger {
QUEST_EXEC_CHANGE_SKILL_DEPOT (55), // missing
QUEST_EXEC_ADD_SCENE_TAG (56), // missing
QUEST_EXEC_DEL_SCENE_TAG (57), // missing
QUEST_EXEC_INIT_TIME_VAR (58), // missing
QUEST_EXEC_CLEAR_TIME_VAR (59), // missing
QUEST_EXEC_INIT_TIME_VAR (58),
QUEST_EXEC_CLEAR_TIME_VAR (59),
QUEST_EXEC_MODIFY_CLIMATE_AREA (60), // missing
QUEST_EXEC_GRANT_TRIAL_AVATAR_AND_LOCK_TEAM (61), // missing
QUEST_EXEC_CHANGE_MAP_AREA_STATE (62), // missing

View File

@ -0,0 +1,18 @@
package emu.grasscutter.game.quest.exec;
import emu.grasscutter.data.excels.QuestData;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueExec;
import emu.grasscutter.game.quest.enums.QuestExec;
import emu.grasscutter.game.quest.handlers.QuestExecHandler;
import lombok.val;
@QuestValueExec(QuestExec.QUEST_EXEC_CLEAR_TIME_VAR)
public class ExecClearTimeVar extends QuestExecHandler {
@Override
public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) {
val timeVarId = Integer.parseInt(condition.getParam()[0]);
val mainQuest = quest.getMainQuest();
return mainQuest.clearTimeVar(timeVarId);
}
}

View File

@ -0,0 +1,18 @@
package emu.grasscutter.game.quest.exec;
import emu.grasscutter.data.excels.QuestData;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueExec;
import emu.grasscutter.game.quest.enums.QuestExec;
import emu.grasscutter.game.quest.handlers.QuestExecHandler;
import lombok.val;
@QuestValueExec(QuestExec.QUEST_EXEC_INIT_TIME_VAR)
public class ExecInitTimeVar extends QuestExecHandler {
@Override
public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) {
val timeVarId = Integer.parseInt(condition.getParam()[0]);
val mainQuest = quest.getMainQuest();
return mainQuest.initTimeVar(timeVarId);
}
}

View File

@ -418,7 +418,15 @@ public class World implements Iterable<Player> {
}
public long getGameTimeDays() {
return getWorldTimeSeconds() / 1440 ;
return getDaysForGameTime(getWorldTimeSeconds());
}
public static long getDaysForGameTime(long time){
return time / 1440;
}
public static long getHoursForGameTime(long time){
return time / 60;
}
public void setPaused(boolean paused) {

View File

@ -647,4 +647,18 @@ public class SceneScriptManager {
return 1;
}
// todo implement properly with proper group loading
public boolean hasClearedGroupMonsters(int groupId){
val group = getGroupById(groupId);
if(group == null || !group.isLoaded() || group.monsters == null){
return false;
}
for(val monster : group.monsters.values()) {
if(scene.getEntityByConfigId(monster.config_id, groupId) !=null){
return false;
}
}
return true;
}
}