diff --git a/buildSrc/src/main/java/emu/grasscutter/gen/GenerateActivityConditions.java b/buildSrc/src/main/java/emu/grasscutter/gen/GenerateActivityConditions.java new file mode 100644 index 000000000..f81a2de91 --- /dev/null +++ b/buildSrc/src/main/java/emu/grasscutter/gen/GenerateActivityConditions.java @@ -0,0 +1,81 @@ +package emu.grasscutter.gen; + +import org.gradle.api.DefaultTask; +import org.gradle.api.tasks.TaskAction; +import org.gradle.api.tasks.options.Option; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import static java.lang.System.lineSeparator; +import static java.nio.file.Files.readAllLines; +import static java.nio.file.Files.writeString; + +/** + * Task that can be used for generating/updating activity conditions enum. These + * activities come from Resources/ExcelBinOutput/NewActivityCondExcelConfigData.json + * resource file. Format file with formatter after this job is executed + *
+ * Usage example: ./gradlew generateActivityConditions --conf-file=/Users/xxx/IdeaProjects/Grasscutter_Resources/Resources/ExcelBinOutput/NewActivityCondExcelConfigData.json + */ +public class GenerateActivityConditions extends DefaultTask { + + private static final Logger log = LoggerFactory.getLogger(GenerateActivityConditions.class); + private static final String ACTIVITY_CONDITIONS_SRC = "/src/main/java/emu/grasscutter/game/activity/condition/ActivityConditions.java"; + + private static final String activityClassStart = """ + package emu.grasscutter.game.activity; + + public enum ActivityConditions { + """; + @Option(option = "conf-file", description = "Path to NewActivityCondExcelConfigData.json") + String confFile; + + @SuppressWarnings("unused") //Used by Gradle + public void setConfFile(String confFile) { + this.confFile = confFile; + } + + @TaskAction + void run() { + List configFileContent = getFileContent(confFile); + + Set configEnums = configFileContent.stream() + .filter(s -> s.contains("\"type\":")) + .map(s -> s.split("\"")[3]) + .map(s -> " " + s) + .collect(Collectors.toSet()); + + String finalActivityClass = + activityClassStart + + String.join("," + lineSeparator(), configEnums) + lineSeparator() + "}"; + + writeFile(finalActivityClass, Path.of(getProject().getProjectDir() + ACTIVITY_CONDITIONS_SRC)); + + log.info("Successfully added {} enums to {}", configEnums.size(), ACTIVITY_CONDITIONS_SRC); + } + + private List getFileContent(String path) { + try { + return readAllLines(Path.of(confFile)); + } catch (IOException e) { + log.error("Cannot read file: {}", path); + throw new RuntimeException(e); + } + } + + private void writeFile(String content, Path path) { + try { + writeString(path, content, StandardCharsets.UTF_8); + } catch (IOException e) { + log.error("Cannot read file: {}", path); + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/EntityCommand.java b/src/main/java/emu/grasscutter/command/commands/EntityCommand.java new file mode 100644 index 000000000..bd6a930a4 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/EntityCommand.java @@ -0,0 +1,137 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.entity.*; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.props.ElementType; +import emu.grasscutter.game.props.FightProperty; +import emu.grasscutter.game.world.Scene; +import emu.grasscutter.server.event.entity.EntityDamageEvent; +import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.regex.Pattern; + +import static emu.grasscutter.command.CommandHelpers.*; +import static emu.grasscutter.utils.Language.translate; + +@Command( + label = "entity", + usage = { + " [state] [maxhp] [hp(0 for infinite)] [atk] [def]", + " [ai] [maxhp] [hp(0 for infinite)] [atk] [def]"}, + permission = "server.entity") +public final class EntityCommand implements CommandHandler { + private static final Map> intCommandHandlers = Map.ofEntries( + Map.entry(stateRegex, EntityParameters::setState), + Map.entry(maxHPRegex, EntityParameters::setMaxHP), + Map.entry(hpRegex, EntityParameters::setHp), + Map.entry(defRegex, EntityParameters::setDef), + Map.entry(atkRegex, EntityParameters::setAtk), + Map.entry(aiRegex, EntityParameters::setAi) + ); + + @Override + public void execute(Player sender, Player targetPlayer, List args) { + EntityParameters param = new EntityParameters(); + + parseIntParameters(args, param, intCommandHandlers); + + // At this point, first remaining argument MUST be the id and the rest the pos + if (args.size() != 1) { + sendUsageMessage(sender); // Reachable if someone does `/give lv90` or similar + throw new IllegalArgumentException(); + } + + try { + param.configId = Integer.parseInt(args.get(0)); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.cfgId")); + } + + param.scene = targetPlayer.getScene(); + var entity = param.scene.getEntityByConfigId(param.configId); + + if(entity == null){ + CommandHandler.sendMessage(sender, translate(sender, "commands.entity.not_found_error")); + return; + } + applyFightProps(entity, param); + applyGadgetParams(entity, param); + applyMonsterParams(entity, param); + + CommandHandler.sendMessage(sender, translate(sender, "commands.status.success")); + } + + + + private void applyGadgetParams(GameEntity entity, EntityParameters param) { + if(!(entity instanceof EntityGadget)){ + return; + } + if(param.state != -1 ){ + ((EntityGadget) entity).updateState(param.state); + } + + } + private void applyMonsterParams(GameEntity entity, EntityParameters param) { + if(!(entity instanceof EntityMonster)){ + return; + } + + if(param.ai != -1 ){ + ((EntityMonster) entity).setAiId(param.ai); + //TODO notify + } + } + + private void applyFightProps(GameEntity entity, EntityParameters param) { + var changedFields = new ArrayList(); + if (param.maxHP != -1) { + setFightProperty(entity, FightProperty.FIGHT_PROP_MAX_HP, param.maxHP, changedFields); + } + if (param.hp != -1) { + float targetHp = param.hp == 0 ? Float.MAX_VALUE : param.hp; + float oldHp = entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP); + setFightProperty(entity, FightProperty.FIGHT_PROP_CUR_HP, targetHp, changedFields); + EntityDamageEvent event = new EntityDamageEvent(entity, oldHp-targetHp, ElementType.None, null); + callHPEvents(entity, event); + } + if (param.atk != -1) { + setFightProperty(entity, FightProperty.FIGHT_PROP_ATTACK, param.atk, changedFields); + setFightProperty(entity, FightProperty.FIGHT_PROP_CUR_ATTACK, param.atk, changedFields); + } + if (param.def != -1) { + setFightProperty(entity, FightProperty.FIGHT_PROP_DEFENSE, param.def, changedFields); + setFightProperty(entity, FightProperty.FIGHT_PROP_CUR_DEFENSE, param.def, changedFields); + } + if(!changedFields.isEmpty()) { + entity.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, changedFields)); + } + } + + private void callHPEvents(GameEntity entity, EntityDamageEvent event){ + entity.callLuaHPEvent(event); + } + + private void setFightProperty(GameEntity entity, FightProperty property, float value, List modifiedProps){ + entity.setFightProperty(property, value); + modifiedProps.add(property); + } + + private static class EntityParameters { + @Setter public int configId = -1; + @Setter public int state = -1; + @Setter public int hp = -1; + @Setter public int maxHP = -1; + @Setter public int atk = -1; + @Setter public int def = -1; + @Setter public int ai = -1; + public Scene scene = null; + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/GroupCommand.java b/src/main/java/emu/grasscutter/command/commands/GroupCommand.java new file mode 100644 index 000000000..a200dbdf4 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/GroupCommand.java @@ -0,0 +1,72 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.world.SceneGroupInstance; + +import java.util.List; + +import static emu.grasscutter.utils.Language.translate; + +@Command(label = "group", + aliases = {"g"}, + usage = {"(refresh) [] []"}, + permission = "player.group", + permissionTargeted = "player.group.others") +public final class GroupCommand implements CommandHandler { + + @Override + public void execute(Player sender, Player targetPlayer, List args) { + if (args.isEmpty()) { + return; + } + String cmd = args.remove(0).toLowerCase(); + + int groupId = 0; + int suiteId = 0; + switch (args.size()){ + case 2: + try { + suiteId = Integer.parseInt(args.get(1)); + } + catch (Exception e) { + CommandHandler.sendMessage(sender, translate(sender, "commands.group.invalid_suiteid")); + return; + } // Fallthrough + case 1: + try { + groupId = Integer.parseInt(args.get(0)); + } + catch (Exception e) { + CommandHandler.sendMessage(sender, translate(sender, "commands.group.invalid_groupid")); + return; + } + break; + default: + sendUsageMessage(sender); + return; + } + + switch (cmd) { + case "refresh" -> { + SceneGroupInstance groupInstance = targetPlayer.getScene().getScriptManager().getGroupInstanceById(groupId); + if(groupInstance == null) { + CommandHandler.sendMessage(sender, translate(sender, "commands.group.group_not_found", groupId)); + return; + } + + if(args.size() >= 2) { + targetPlayer.getScene().getScriptManager().refreshGroup(groupInstance, suiteId, false); + } else { + targetPlayer.getScene().getScriptManager().refreshGroup(groupInstance); + } + + CommandHandler.sendMessage(sender, translate(sender, "commands.group.refreshed", groupId)); + } + default -> { + sendUsageMessage(sender); + } + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/TrialAvatarActivityCommand.java b/src/main/java/emu/grasscutter/command/commands/TrialAvatarActivityCommand.java new file mode 100644 index 000000000..981a5d2dd --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/TrialAvatarActivityCommand.java @@ -0,0 +1,125 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.activity.PlayerActivityData; +import emu.grasscutter.game.activity.trialavatar.TrialAvatarActivityHandler; +import emu.grasscutter.game.activity.trialavatar.TrialAvatarPlayerData; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.props.ActivityType; +import emu.grasscutter.net.proto.ActivityInfoOuterClass.ActivityInfo; +import emu.grasscutter.server.packet.send.PacketActivityInfoNotify; +import emu.grasscutter.utils.JsonUtils; + +import java.util.List; +import lombok.val; + +import static emu.grasscutter.utils.Language.translate; + +@Command( + label = "trialAvatarActivity", + aliases = {"taa"}, + usage = {"change ", "toggleDungeon ", "toggleReward "}, + permission = "player.trialavataractivity", + permissionTargeted = "player.trialavataractivity.others") +public final class TrialAvatarActivityCommand implements CommandHandler { + @Override + public void execute(Player sender, Player targetPlayer, List args) { + if (args.size() < 2) { + sendUsageMessage(sender); + return; + } + + String action = args.get(0).toLowerCase(); + String param = args.get(1); + + val playerDataOption = targetPlayer.getActivityManager() + .getPlayerActivityDataByActivityType(ActivityType.NEW_ACTIVITY_TRIAL_AVATAR); + if (playerDataOption == null) { + CommandHandler.sendMessage(sender, translate(sender, "commands.trialAvatarActivity.not_found")); + return; + } + val playerData = playerDataOption.get(); + if (playerData == null) { + CommandHandler.sendMessage(sender, translate(sender, "commands.trialAvatarActivity.not_found")); + return; + } + var handler = (TrialAvatarActivityHandler) playerData.getActivityHandler(); + if (handler == null) { + CommandHandler.sendMessage(sender, translate(sender, "commands.trialAvatarActivity.not_found")); + return; + } + TrialAvatarPlayerData trialAvatarPlayerData = JsonUtils.decode(playerData.getDetail(), TrialAvatarPlayerData.class); + if (trialAvatarPlayerData == null) { + CommandHandler.sendMessage(sender, translate(sender, "commands.trialAvatarActivity.not_found")); + return; + } + + switch (action) { + default -> { + sendUsageMessage(sender); + } + case "change" -> { + if (!param.chars().allMatch(Character::isDigit)) { // if its not number + CommandHandler.sendMessage(sender, translate(sender, "commands.trialAvatarActivity.invalid_param")); + return; + } + if (TrialAvatarPlayerData.getAvatarIdList(Integer.parseInt(param)).isEmpty()) { + CommandHandler.sendMessage(sender, translate(sender, "commands.trialAvatarActivity.schedule_not_found", Integer.parseInt(param))); + return; + } + playerData.setDetail(TrialAvatarPlayerData.create(Integer.parseInt(param))); + playerData.save(); + CommandHandler.sendMessage(sender, translate(sender, "commands.trialAvatarActivity.success_schedule", Integer.parseInt(param))); + } + case "toggledungeon" -> { + if (param.chars().allMatch(Character::isDigit)) { // if its number + if (Integer.parseInt(param)-1 >= trialAvatarPlayerData.getRewardInfoList().size() + || Integer.parseInt(param)-1 <= 0) { + CommandHandler.sendMessage(sender, translate(sender, "commands.trialAvatarActivity.invalid_param")); + return; + } + TrialAvatarPlayerData.RewardInfoItem rewardInfo = trialAvatarPlayerData.getRewardInfoList().get(Integer.parseInt(param)-1); + rewardInfo.setPassedDungeon(!rewardInfo.isPassedDungeon()); + playerData.setDetail(trialAvatarPlayerData); + playerData.save(); + CommandHandler.sendMessage(sender, translate(sender, "commands.trialAvatarActivity.success_dungeon", Integer.parseInt(param))); + } else { + if (!param.equals("all")) { + CommandHandler.sendMessage(sender, translate(sender, "commands.trialAvatarActivity.invalid_param")); + return; + } + trialAvatarPlayerData.getRewardInfoList().forEach(r -> r.setPassedDungeon(!r.isPassedDungeon())); + playerData.setDetail(trialAvatarPlayerData); + playerData.save(); + CommandHandler.sendMessage(sender, translate(sender, "commands.trialAvatarActivity.success_dungeon_all")); + } + } + case "togglereward" -> { + if (param.chars().allMatch(Character::isDigit)) { // if its number + if (Integer.parseInt(param)-1 >= trialAvatarPlayerData.getRewardInfoList().size() + || Integer.parseInt(param)-1 <= 0) { + CommandHandler.sendMessage(sender, translate(sender, "commands.trialAvatarActivity.invalid_param")); + return; + } + TrialAvatarPlayerData.RewardInfoItem rewardInfo = trialAvatarPlayerData.getRewardInfoList().get(Integer.parseInt(param)-1); + rewardInfo.setReceivedReward(!rewardInfo.isReceivedReward()); + playerData.setDetail(trialAvatarPlayerData); + playerData.save(); + CommandHandler.sendMessage(sender, translate(sender, "commands.trialAvatarActivity.success_reward", Integer.parseInt(param))); + } else { + if (!param.toLowerCase().equals("all")) { + CommandHandler.sendMessage(sender, translate(sender, "commands.trialAvatarActivity.invalid_param")); + return; + } + trialAvatarPlayerData.getRewardInfoList().forEach(r -> r.setReceivedReward(!r.isReceivedReward())); + playerData.setDetail(trialAvatarPlayerData); + playerData.save(); + CommandHandler.sendMessage(sender, translate(sender, "commands.trialAvatarActivity.success_reward_all")); + } + } + } + targetPlayer.sendPacket(new PacketActivityInfoNotify(handler.toProto(playerData, targetPlayer.getActivityManager().getConditionExecutor()))); + } +} diff --git a/src/main/java/emu/grasscutter/data/binout/config/ConfigEntityAvatar.java b/src/main/java/emu/grasscutter/data/binout/config/ConfigEntityAvatar.java new file mode 100644 index 000000000..e99d08238 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/binout/config/ConfigEntityAvatar.java @@ -0,0 +1,4 @@ +package emu.grasscutter.data.binout.config; + +public class ConfigEntityAvatar extends ConfigEntityBase { +} diff --git a/src/main/java/emu/grasscutter/data/binout/config/ConfigEntityBase.java b/src/main/java/emu/grasscutter/data/binout/config/ConfigEntityBase.java new file mode 100644 index 000000000..c44cd2b9b --- /dev/null +++ b/src/main/java/emu/grasscutter/data/binout/config/ConfigEntityBase.java @@ -0,0 +1,21 @@ +package emu.grasscutter.data.binout.config; + +import emu.grasscutter.data.binout.config.fields.ConfigAbilityData; +import emu.grasscutter.data.binout.config.fields.ConfigCombat; +import emu.grasscutter.data.binout.config.fields.ConfigCommon; +import emu.grasscutter.data.binout.config.fields.ConfigGlobalValue; +import lombok.Data; + +import javax.annotation.Nullable; +import java.util.Collection; + +@Data +public class ConfigEntityBase { + @Nullable + ConfigCommon configCommon; + @Nullable + ConfigCombat combat; + Collection abilities; + ConfigGlobalValue globalValue; // used for SGV in monsters and Gadgets + +} diff --git a/src/main/java/emu/grasscutter/data/binout/config/ConfigEntityGadget.java b/src/main/java/emu/grasscutter/data/binout/config/ConfigEntityGadget.java new file mode 100644 index 000000000..cdd2fcd99 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/binout/config/ConfigEntityGadget.java @@ -0,0 +1,11 @@ +package emu.grasscutter.data.binout.config; + +import lombok.AccessLevel; +import lombok.Data; +import lombok.experimental.FieldDefaults; + +@Data +@FieldDefaults(level = AccessLevel.PRIVATE) +public class ConfigEntityGadget extends ConfigEntityBase { + // There are more values that can be added that might be useful in the json +} diff --git a/src/main/java/emu/grasscutter/data/binout/config/ConfigEntityMonster.java b/src/main/java/emu/grasscutter/data/binout/config/ConfigEntityMonster.java new file mode 100644 index 000000000..93cad56c9 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/binout/config/ConfigEntityMonster.java @@ -0,0 +1,7 @@ +package emu.grasscutter.data.binout.config; + +import lombok.Data; + +@Data +public class ConfigEntityMonster extends ConfigEntityBase { +} diff --git a/src/main/java/emu/grasscutter/data/binout/config/ConfigLevelEntity.java b/src/main/java/emu/grasscutter/data/binout/config/ConfigLevelEntity.java new file mode 100644 index 000000000..4b5b582a2 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/binout/config/ConfigLevelEntity.java @@ -0,0 +1,14 @@ +package emu.grasscutter.data.binout.config; + +import java.util.List; + +import emu.grasscutter.data.binout.config.fields.ConfigAbilityData; +import lombok.Getter; + +public class ConfigLevelEntity { + + @Getter private List abilities; //monster abilities + @Getter private List avatarAbilities; + @Getter private List teamAbilities; + @Getter private List preloadMonsterEntityIDs; +} diff --git a/src/main/java/emu/grasscutter/data/binout/config/fields/ConfigAbilityData.java b/src/main/java/emu/grasscutter/data/binout/config/fields/ConfigAbilityData.java new file mode 100644 index 000000000..b2dfed900 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/binout/config/fields/ConfigAbilityData.java @@ -0,0 +1,10 @@ +package emu.grasscutter.data.binout.config.fields; + +import lombok.Data; + +@Data +public class ConfigAbilityData { + public String abilityID; + public String abilityName; + public String abilityOverride; +} diff --git a/src/main/java/emu/grasscutter/data/binout/config/fields/ConfigAiBeta.java b/src/main/java/emu/grasscutter/data/binout/config/fields/ConfigAiBeta.java new file mode 100644 index 000000000..5707e4e3d --- /dev/null +++ b/src/main/java/emu/grasscutter/data/binout/config/fields/ConfigAiBeta.java @@ -0,0 +1,8 @@ +package emu.grasscutter.data.binout.config.fields; + +import lombok.Data; + +@Data +public class ConfigAiBeta { + boolean enable; +} diff --git a/src/main/java/emu/grasscutter/data/binout/config/fields/ConfigCombat.java b/src/main/java/emu/grasscutter/data/binout/config/fields/ConfigCombat.java new file mode 100644 index 000000000..011e43606 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/binout/config/fields/ConfigCombat.java @@ -0,0 +1,12 @@ +package emu.grasscutter.data.binout.config.fields; + +import lombok.AccessLevel; +import lombok.Data; +import lombok.experimental.FieldDefaults; + +@Data +@FieldDefaults(level = AccessLevel.PRIVATE) +public class ConfigCombat { + // There are more values that can be added that might be useful in the json + ConfigCombatProperty property; +} diff --git a/src/main/java/emu/grasscutter/data/binout/config/fields/ConfigCombatDie.java b/src/main/java/emu/grasscutter/data/binout/config/fields/ConfigCombatDie.java new file mode 100644 index 000000000..ae1d96247 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/binout/config/fields/ConfigCombatDie.java @@ -0,0 +1,15 @@ +package emu.grasscutter.data.binout.config.fields; + +import com.google.gson.annotations.SerializedName; +import lombok.AccessLevel; +import lombok.Data; +import lombok.experimental.FieldDefaults; + +@Data +@FieldDefaults(level = AccessLevel.PRIVATE) +public class ConfigCombatDie { + @SerializedName(value="dieEndTime", alternate={"HGGPMFGGBNC"}) + double dieEndTime; + double dieForceDisappearTime; + boolean hasAnimatorDie; +} diff --git a/src/main/java/emu/grasscutter/data/binout/config/fields/ConfigCombatProperty.java b/src/main/java/emu/grasscutter/data/binout/config/fields/ConfigCombatProperty.java new file mode 100644 index 000000000..995747762 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/binout/config/fields/ConfigCombatProperty.java @@ -0,0 +1,18 @@ +package emu.grasscutter.data.binout.config.fields; + +import lombok.AccessLevel; +import lombok.Data; +import lombok.experimental.FieldDefaults; + +@Data +@FieldDefaults(level = AccessLevel.PRIVATE) +public class ConfigCombatProperty { + float HP; + boolean isLockHP; + boolean isInvincible; + boolean isGhostToAllied; + float attack; + float defence; + float weight; + boolean useCreatorProperty; +} diff --git a/src/main/java/emu/grasscutter/data/binout/config/fields/ConfigCommon.java b/src/main/java/emu/grasscutter/data/binout/config/fields/ConfigCommon.java new file mode 100644 index 000000000..bc11e257e --- /dev/null +++ b/src/main/java/emu/grasscutter/data/binout/config/fields/ConfigCommon.java @@ -0,0 +1,7 @@ +package emu.grasscutter.data.binout.config.fields; + +import lombok.Data; + +@Data +public class ConfigCommon { +} diff --git a/src/main/java/emu/grasscutter/data/binout/config/fields/ConfigGlobalValue.java b/src/main/java/emu/grasscutter/data/binout/config/fields/ConfigGlobalValue.java new file mode 100644 index 000000000..660adb35e --- /dev/null +++ b/src/main/java/emu/grasscutter/data/binout/config/fields/ConfigGlobalValue.java @@ -0,0 +1,15 @@ +package emu.grasscutter.data.binout.config.fields; + +import lombok.Data; + +import java.util.Map; +import java.util.Set; + +/** + * Contains information about the entities SGVs + */ +@Data +public class ConfigGlobalValue { + Set serverGlobalValues; + Map initServerGlobalValues; +} diff --git a/src/main/java/emu/grasscutter/data/binout/routes/RotAngleType.java b/src/main/java/emu/grasscutter/data/binout/routes/RotAngleType.java new file mode 100644 index 000000000..8d3e56ee9 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/binout/routes/RotAngleType.java @@ -0,0 +1,23 @@ +package emu.grasscutter.data.binout.routes; + +//import emu.grasscutter.scripts.constants.IntValueEnum; +import lombok.Getter; + +public enum RotAngleType /*implements IntValueEnum */{ + ROT_NONE(-1), + ROT_ANGLE_X(0), + ROT_ANGLE_Y(1), + ROT_ANGLE_Z(2); + + @Getter + private final int id; + + RotAngleType(int id) { + this.id = id; + } + + //@Override + public int getValue() { + return id; + } +} diff --git a/src/main/java/emu/grasscutter/data/binout/routes/RotType.java b/src/main/java/emu/grasscutter/data/binout/routes/RotType.java new file mode 100644 index 000000000..fc7c7621f --- /dev/null +++ b/src/main/java/emu/grasscutter/data/binout/routes/RotType.java @@ -0,0 +1,7 @@ +package emu.grasscutter.data.binout.routes; + +public enum RotType { + ROT_NONE, + ROT_ANGLE, + ROT_ROUND +} diff --git a/src/main/java/emu/grasscutter/data/binout/routes/Route.java b/src/main/java/emu/grasscutter/data/binout/routes/Route.java new file mode 100644 index 000000000..b78fd31b4 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/binout/routes/Route.java @@ -0,0 +1,31 @@ +package emu.grasscutter.data.binout.routes; + +import emu.grasscutter.net.proto.RouteOuterClass; +import lombok.AccessLevel; +import lombok.Data; +import lombok.experimental.FieldDefaults; +import lombok.val; + +@Data +@FieldDefaults(level = AccessLevel.PRIVATE) +public class Route { + private int localId; + private String name; + private RouteType type = RouteType.Unknown; + private RoutePoint[] points; + private float arriveRange; // optional + private RotType rotType; // optional + private RotAngleType rotAngleType; // optional + + public RouteOuterClass.Route toProto(){ + val builder = RouteOuterClass.Route.newBuilder() + .setRouteType(type.getValue()); + if(points !=null){ + for(var routePoint : points){ + builder.addRoutePoints(routePoint.toProto() + .setArriveRange(arriveRange)); + } + } + return builder.build(); + } +} diff --git a/src/main/java/emu/grasscutter/data/binout/routes/RoutePoint.java b/src/main/java/emu/grasscutter/data/binout/routes/RoutePoint.java new file mode 100644 index 000000000..9c98c9faa --- /dev/null +++ b/src/main/java/emu/grasscutter/data/binout/routes/RoutePoint.java @@ -0,0 +1,34 @@ +package emu.grasscutter.data.binout.routes; + +import emu.grasscutter.net.proto.RoutePointOuterClass; +import emu.grasscutter.utils.Position; +import lombok.AccessLevel; +import lombok.Data; +import lombok.Getter; +import lombok.experimental.FieldDefaults; +import lombok.val; + +@Data +@FieldDefaults(level = AccessLevel.PRIVATE) +public class RoutePoint { + private Position pos; + private int speedLevel; //optional + private float waitTime; //optional + private float targetVelocity; //optional + private boolean hasReachEvent; //optional + // rotRoundReachDir //optional Pos with optional values + // rotRoundLeaveDir //optional Pos with optional values + + public RoutePointOuterClass.RoutePoint.Builder toProto(){ + val builder = RoutePointOuterClass.RoutePoint.newBuilder() + .setPosition(pos.toProto()); + if(waitTime!=0){ + builder.setTime(waitTime); + } else if(targetVelocity!=0){ + builder.setVelocity(targetVelocity); + } + + + return builder; + } +} diff --git a/src/main/java/emu/grasscutter/data/binout/routes/RouteType.java b/src/main/java/emu/grasscutter/data/binout/routes/RouteType.java new file mode 100644 index 000000000..afea31311 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/binout/routes/RouteType.java @@ -0,0 +1,23 @@ +package emu.grasscutter.data.binout.routes; + +//import emu.grasscutter.scripts.constants.IntValueEnum; +import lombok.Getter; + +public enum RouteType /*implements IntValueEnum*/ { + Unknown(-1), + OneWay(0), + Reciprocate(1), + Loop(2); + + @Getter + private final int id; + + RouteType(int id) { + this.id = id; + } + + //@Override + public int getValue() { + return id; + } +} diff --git a/src/main/java/emu/grasscutter/data/binout/routes/SceneRoutes.java b/src/main/java/emu/grasscutter/data/binout/routes/SceneRoutes.java new file mode 100644 index 000000000..38e0ab95f --- /dev/null +++ b/src/main/java/emu/grasscutter/data/binout/routes/SceneRoutes.java @@ -0,0 +1,15 @@ +package emu.grasscutter.data.binout.routes; + +import lombok.AccessLevel; +import lombok.Data; +import lombok.Getter; +import lombok.experimental.FieldDefaults; + +import javax.annotation.Nullable; + +@Data +@FieldDefaults(level = AccessLevel.PRIVATE) +public class SceneRoutes { + private int sceneId; + @Nullable private Route[] routes; +} diff --git a/src/main/java/emu/grasscutter/data/common/BaseTrialActivityData.java b/src/main/java/emu/grasscutter/data/common/BaseTrialActivityData.java new file mode 100644 index 000000000..aad93a03c --- /dev/null +++ b/src/main/java/emu/grasscutter/data/common/BaseTrialActivityData.java @@ -0,0 +1,8 @@ +package emu.grasscutter.data.common; + +import java.util.List; + +public interface BaseTrialActivityData { + List getAvatarIndexIdList(); + List getRewardIdList(); +} diff --git a/src/main/java/emu/grasscutter/data/custom/TrialAvatarActivityCustomData.java b/src/main/java/emu/grasscutter/data/custom/TrialAvatarActivityCustomData.java new file mode 100644 index 000000000..5ed215826 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/custom/TrialAvatarActivityCustomData.java @@ -0,0 +1,17 @@ +package emu.grasscutter.data.custom; + +import emu.grasscutter.data.common.BaseTrialActivityData; +import lombok.*; +import java.util.List; + +@Data +public class TrialAvatarActivityCustomData implements BaseTrialActivityData { + private int ScheduleId; + private List AvatarIndexIdList; + private List RewardIdList; + + public void onLoad() { + this.AvatarIndexIdList = AvatarIndexIdList.stream().filter(x -> x > 0).toList(); + this.RewardIdList = RewardIdList.stream().filter(x -> x > 0).toList(); + } +} diff --git a/src/main/java/emu/grasscutter/data/custom/TrialAvatarCustomData.java b/src/main/java/emu/grasscutter/data/custom/TrialAvatarCustomData.java new file mode 100644 index 000000000..bcee82fc2 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/custom/TrialAvatarCustomData.java @@ -0,0 +1,16 @@ +package emu.grasscutter.data.custom; + +import lombok.*; +import java.util.List; + +@Data +public class TrialAvatarCustomData { + private int trialAvatarId; + private List trialAvatarParamList; + private int coreProudSkillLevel; + private int skillDepotId; + + public void onLoad() { + this.trialAvatarParamList = trialAvatarParamList.stream().filter(x -> !x.isBlank()).toList(); + } +} diff --git a/src/main/java/emu/grasscutter/data/excels/ActivityCondExcelConfigData.java b/src/main/java/emu/grasscutter/data/excels/ActivityCondExcelConfigData.java new file mode 100644 index 000000000..54defc4f8 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/ActivityCondExcelConfigData.java @@ -0,0 +1,41 @@ +package emu.grasscutter.data.excels; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; +import emu.grasscutter.game.activity.condition.ActivityConditions; +import emu.grasscutter.game.quest.enums.LogicType; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.experimental.FieldDefaults; + +import java.util.List; + +@ResourceType(name = "NewActivityCondExcelConfigData.json") +@Getter +@FieldDefaults(level = AccessLevel.PRIVATE) +public class ActivityCondExcelConfigData extends GameResource { + int condId; + LogicType condComb; + List cond; + + public static class ActivityConfigCondition { + @Getter + private ActivityConditions type; + @Getter + private List param; + + public int[] paramArray() { + return param.stream().mapToInt(Integer::intValue).toArray(); + } + } + + @Override + public int getId() { + return condId; + } + + @Override + public void onLoad() { + cond.removeIf(c -> c.type == null); + } +} diff --git a/src/main/java/emu/grasscutter/data/excels/AvatarReplaceCostumeData.java b/src/main/java/emu/grasscutter/data/excels/AvatarReplaceCostumeData.java new file mode 100644 index 000000000..18bff17b3 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/AvatarReplaceCostumeData.java @@ -0,0 +1,22 @@ +package emu.grasscutter.data.excels; + +import com.google.gson.annotations.SerializedName; +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@ResourceType(name = "AvatarReplaceCostumeExcelConfigData.json") +@EqualsAndHashCode(callSuper=false) +public class AvatarReplaceCostumeData extends GameResource { + private int avatarId; + @SerializedName(value = "costumeId", alternate={"MGLCOPOIJIC", "BDBMOBGKIAP"}) + private int costumeId; + + @Override + public int getId() { + return costumeId; + } +} diff --git a/src/main/java/emu/grasscutter/data/excels/DungeonChallengeConfigData.java b/src/main/java/emu/grasscutter/data/excels/DungeonChallengeConfigData.java new file mode 100644 index 000000000..8186e26ff --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/DungeonChallengeConfigData.java @@ -0,0 +1,72 @@ +package emu.grasscutter.data.excels; + +import com.google.gson.annotations.SerializedName; +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; + +import emu.grasscutter.game.dungeons.challenge.enums.ChallengeType; +import lombok.Getter; + +import java.util.HashSet; + +@Getter +@ResourceType(name = "DungeonChallengeConfigData.json") +public class DungeonChallengeConfigData extends GameResource { + private int id; + private ChallengeType challengeType; + private boolean noSuccessHint; + private boolean noFailHint; + private boolean isBlockTopTimer; + private int subChallengeFadeOutDelayTime; + private int activitySkillId; + private HashSet teamAbilityGroupList; + + private SubChallengeFadeOutType subChallengeFadeOutRule; + private SubChallengeBannerType subChallengeBannerRule; + private InterruptButtonType interruptButtonType; + + @SerializedName(value = "subChallengeSortType", alternate={"PNCLDNBHKDJ"}) + private SubChallengeSortType subChallengeSortType; + @SerializedName(value = "animationOnSubStart", alternate={"DNFAFNMMMDP"}) + private AllowAnimationType animationOnSubStart; + @SerializedName(value = "animationOnSubSuccess", alternate={"ENONHOGJDDN"}) + private AllowAnimationType animationOnSubSuccess; + @SerializedName(value = "animationOnSubFail", alternate={"NJBJIKAIENN"}) + private AllowAnimationType animationOnSubFail; + + public int getId() { + return id; + } + + public enum InterruptButtonType{ + INTERRUPT_BUTTON_TYPE_NONE, + INTERRUPT_BUTTON_TYPE_HOST, + INTERRUPT_BUTTON_TYPE_ALL + } + + public enum SubChallengeFadeOutType{ + SUBCHALLENGE_FADEOUT_TYPE_NONE, + SUBCHALLENGE_FADEOUT_TYPE_SUCCESS, + SUBCHALLENGE_FADEOUT_TYPE_FAIL, + SUBCHALLENGE_FADEOUT_TYPE_FINISH + } + + public enum SubChallengeBannerType{ + SUBCHALLENGE_BANNER_TYPE_NONE, + SUBCHALLENGE_BANNER_TYPE_SUCCESS, + SUBCHALLENGE_BANNER_TYPE_FAIL, + SUBCHALLENGE_BANNER_TYPE_HIDE_FINAL, + SUBCHALLENGE_BANNER_TYPE_SHOW_FINAL + } + public enum SubChallengeSortType{ + SUB_CHALLENGE_SORT_TYPE_DEFAULT, + SUB_CHALLENGE_SORT_TYPE_CHALLENGEINDEX + } + + public enum AllowAnimationType{ + SUB_CHALLENGE_ANIM_TYPE_DEFAULT, + SUB_CHALLENGE_ANIM_TYPE_FORBID, + SUB_CHALLENGE_ANIM_TYPE_SUCCESS, + SUB_CHALLENGE_ANIM_TYPE_FAIL + } +} diff --git a/src/main/java/emu/grasscutter/data/excels/DungeonPassConfigData.java b/src/main/java/emu/grasscutter/data/excels/DungeonPassConfigData.java new file mode 100644 index 000000000..2702483a8 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/DungeonPassConfigData.java @@ -0,0 +1,28 @@ +package emu.grasscutter.data.excels; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; + +import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType; +import emu.grasscutter.game.quest.enums.LogicType; +import lombok.Getter; + +import java.util.List; + +@ResourceType(name = "DungeonPassExcelConfigData.json") +public class DungeonPassConfigData extends GameResource { + @Getter private int id; + @Getter private LogicType logicType; + @Getter private List conds; + + public static class DungeonPassCondition{ + @Getter private DungeonPassConditionType condType; + @Getter int[] param; + } + + @Override + public void onLoad() { + super.onLoad(); + conds = conds.stream().filter(condition -> condition.getCondType()!=null).toList(); + } +} diff --git a/src/main/java/emu/grasscutter/data/excels/GuideTriggerData.java b/src/main/java/emu/grasscutter/data/excels/GuideTriggerData.java new file mode 100644 index 000000000..ee6c0e47a --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/GuideTriggerData.java @@ -0,0 +1,26 @@ +package emu.grasscutter.data.excels; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data @EqualsAndHashCode(callSuper=false) +@ResourceType(name = "GuideTriggerExcelConfigData.json") +public class GuideTriggerData extends GameResource { + // more like open state guide than quest guide + private int id; // dont use, just to prevent resource loader from not functioning + private String guideName; + private String type; + private String openState; + + @Override + public int getId() { + return this.id; + } + + public void onLoad() { + GameData.getGuideTriggerDataStringMap().put(getGuideName(), this); + } +} diff --git a/src/main/java/emu/grasscutter/data/excels/MonsterSpecialNameData.java b/src/main/java/emu/grasscutter/data/excels/MonsterSpecialNameData.java new file mode 100644 index 000000000..698bd0ac3 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/MonsterSpecialNameData.java @@ -0,0 +1,21 @@ +package emu.grasscutter.data.excels; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; +import emu.grasscutter.data.ResourceType.LoadPriority; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@ResourceType(name = "MonsterSpecialNameExcelConfigData.json", loadPriority = LoadPriority.HIGH) +@EqualsAndHashCode(callSuper=false) +@Data +public class MonsterSpecialNameData extends GameResource { + private int specialNameId; + private int specialNameLabId; + private long specialNameTextMapHash; + + @Override + public int getId() { + return specialNameId; + } +} diff --git a/src/main/java/emu/grasscutter/data/excels/RefreshPolicyExcelConfigData.java b/src/main/java/emu/grasscutter/data/excels/RefreshPolicyExcelConfigData.java new file mode 100644 index 000000000..70dc56806 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/RefreshPolicyExcelConfigData.java @@ -0,0 +1,81 @@ +package emu.grasscutter.data.excels; + +import java.util.Arrays; +import java.util.List; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; +import emu.grasscutter.game.props.RefreshType; +import emu.grasscutter.game.world.World; +import lombok.Getter; + +@ResourceType(name = "RefreshPolicyExcelConfigData.json") +public class RefreshPolicyExcelConfigData extends GameResource { + @Getter private int id; + @Getter private RefreshType type; + @Getter private String time; + + private static int upperBound(List list, int low, int high, int value) { + while (low < high) { + int middle = (high + low) / 2; + if(list.size() >= middle) return low; //Just in case + if (list.get(middle) > value) { + high = middle; + } else { + low = middle + 1; + } + } + return low; + } + + public int getIntervalInSeconds(World world) { + if(time.isEmpty()) return -1; + + var currentTimestamp = world.getTotalGameTimeMinutes(); + + try { + List paramsStr = Arrays.asList(time.split(";")); + List params = paramsStr.stream().map(Integer::parseInt).toList(); + + switch(type) { + case REFRESH_NONE: + return -1; + case REFRESH_INTERVAL: + if(params.isEmpty()) return -1; + return params.get(0); + case REFRESH_DAILY: + { + var dayTime = (world.getTotalGameTimeMinutes() / (24 * 60)) * 24 * 60 * 60; + var temp = currentTimestamp - dayTime; + var upper_bound_idx = upperBound(params, (int)params.get(0), (int)params.get(params.size() - 1), (int)temp); + var upper_bound = params.get(upper_bound_idx); + if(params.get(params.size() - 1) == upper_bound) { + return (params.get(params.size() - 1) - params.get(0)) + 60 * 60 * 24 * 7; + } else if(params.get(0) == upper_bound) { + return (params.get(params.size() - 1) - params.get(0)) + 60 * 60 * 24 * 7; + } + return (params.get(upper_bound_idx - 1) - params.get(0)); + } + case REFRESH_WEEKlY: + if(params.size() < 2) return -1; + { + var weekTime = (world.getTotalGameTimeDays() / 7) * 60 * 60 * 24 * 7; + var temp = currentTimestamp - weekTime; + var upper_bound_idx = upperBound(params, (int)params.get(0), (int)params.get(params.size() - 1), (int)temp); + var upper_bound = params.get(upper_bound_idx); + if(params.get(params.size() - 1) == upper_bound) { + return (params.get(params.size() - 1) - params.get(0)) + 60 * 60 * 24 * 7; + } else if(params.get(0) == upper_bound) { + return (params.get(params.size() - 1) - params.get(0)) + 60 * 60 * 24 * 7; + } + return (params.get(upper_bound_idx - 1) - params.get(0)); + } + case REFRESH_DAYBEGIN_INTERVAL: + if(params.size() == 0) return -1; + return params.get(0) * 60 * 60 * 24; + } + } catch(Exception e) {} + + return -1; + } +} diff --git a/src/main/java/emu/grasscutter/data/excels/TrialAvatarActivityData.java b/src/main/java/emu/grasscutter/data/excels/TrialAvatarActivityData.java new file mode 100644 index 000000000..8c718c46e --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/TrialAvatarActivityData.java @@ -0,0 +1,21 @@ +package emu.grasscutter.data.excels; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; +import emu.grasscutter.data.common.BaseTrialActivityData; +import lombok.*; +import java.util.List; + +@ResourceType(name = "TrialAvatarActivityExcelConfigData.json") +@EqualsAndHashCode(callSuper=false) +@Data +public class TrialAvatarActivityData extends GameResource implements BaseTrialActivityData { + private int ScheduleId; + private List AvatarIndexIdList; + private List RewardIdList; + + @Override + public int getId() { + return ScheduleId; + } +} diff --git a/src/main/java/emu/grasscutter/data/excels/TrialAvatarActivityDataData.java b/src/main/java/emu/grasscutter/data/excels/TrialAvatarActivityDataData.java new file mode 100644 index 000000000..1ec230f97 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/TrialAvatarActivityDataData.java @@ -0,0 +1,29 @@ +package emu.grasscutter.data.excels; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; +import lombok.*; +import lombok.experimental.FieldDefaults; + +@ResourceType(name = "TrialAvatarActivityDataExcelConfigData.json") +@EqualsAndHashCode(callSuper=false) +@Data +@FieldDefaults(level = AccessLevel.PRIVATE) +public class TrialAvatarActivityDataData extends GameResource { + @Getter(onMethod = @__(@Override)) + private int id; + private int trialAvatarIndexId; + private int trialAvatarId; + private int dungeonId; + private String battleAvatarsList; + private int firstPassReward; + private ActivityWatcherData.WatcherTrigger triggerConfig; + private int progress; + + @Override + public void onLoad() { + triggerConfig.onLoad(); + GameData.getTrialAvatarIndexIdTrialActivityDataDataMap().put(trialAvatarIndexId, id); + } +} diff --git a/src/main/java/emu/grasscutter/data/excels/TrialAvatarData.java b/src/main/java/emu/grasscutter/data/excels/TrialAvatarData.java new file mode 100644 index 000000000..165f1a78c --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/TrialAvatarData.java @@ -0,0 +1,19 @@ +package emu.grasscutter.data.excels; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; +import lombok.*; +import java.util.List; + +@ResourceType(name = "TrialAvatarExcelConfigData.json") +@EqualsAndHashCode(callSuper=false) +@Data +public class TrialAvatarData extends GameResource { + private int trialAvatarId; + private List trialAvatarParamList; + + @Override + public int getId() { + return trialAvatarId; + } +} diff --git a/src/main/java/emu/grasscutter/data/excels/TrialAvatarTemplateData.java b/src/main/java/emu/grasscutter/data/excels/TrialAvatarTemplateData.java new file mode 100644 index 000000000..63c08c6fa --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/TrialAvatarTemplateData.java @@ -0,0 +1,20 @@ +package emu.grasscutter.data.excels; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; +import lombok.*; +import java.util.List; + +@ResourceType(name = "TrialAvatarTemplateExcelConfigData.json") +@EqualsAndHashCode(callSuper=false) +@Data +public class TrialAvatarTemplateData extends GameResource { + private int TrialAvatarLevel; + private List TrialReliquaryList; + private int TrialAvatarSkillLevel; + + @Override + public int getId() { + return TrialAvatarLevel; + } +} diff --git a/src/main/java/emu/grasscutter/data/excels/TrialReliquaryData.java b/src/main/java/emu/grasscutter/data/excels/TrialReliquaryData.java new file mode 100644 index 000000000..3dcfb8297 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/TrialReliquaryData.java @@ -0,0 +1,22 @@ +package emu.grasscutter.data.excels; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; +import lombok.*; +import java.util.List; + +@ResourceType(name = "TrialReliquaryExcelConfigData.json") +@EqualsAndHashCode(callSuper=false) +@Data +public class TrialReliquaryData extends GameResource { + private int Id; + private int ReliquaryId; + private int Level; + private int MainPropId; + private List AppendPropList; + + @Override + public int getId() { + return Id; + } +} diff --git a/src/main/java/emu/grasscutter/data/server/ActivityCondGroup.java b/src/main/java/emu/grasscutter/data/server/ActivityCondGroup.java new file mode 100644 index 000000000..1d0087249 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/server/ActivityCondGroup.java @@ -0,0 +1,11 @@ +package emu.grasscutter.data.server; + +import lombok.Data; + +import java.util.List; + +@Data +public class ActivityCondGroup { + int condGroupId; + List condIds; +} diff --git a/src/main/java/emu/grasscutter/data/server/GadgetMapping.java b/src/main/java/emu/grasscutter/data/server/GadgetMapping.java new file mode 100644 index 000000000..f3c125419 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/server/GadgetMapping.java @@ -0,0 +1,9 @@ +package emu.grasscutter.data.server; + +import lombok.Data; + +@Data +public class GadgetMapping { + private int gadgetId; + private String serverController; +} diff --git a/src/main/java/emu/grasscutter/data/server/Grid.java b/src/main/java/emu/grasscutter/data/server/Grid.java new file mode 100644 index 000000000..e4379bf60 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/server/Grid.java @@ -0,0 +1,34 @@ +package emu.grasscutter.data.server; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.utils.GridPosition; +import emu.grasscutter.utils.Position; + +public class Grid { + public Map> grid; + + public Set getNearbyGroups(int vision_level, Position position) { + int width = Grasscutter.getConfig().server.game.visionOptions[vision_level].gridWidth; + int vision_range = Grasscutter.getConfig().server.game.visionOptions[vision_level].visionRange; + int vision_range_grid = vision_range / width; + + GridPosition pos = new GridPosition(position, width); + + Set nearbyGroups = new HashSet<>(); + //construct a nearby pisition list, add 1 more because a player can be in an edge case, this should not affect much the loading + for(int x = 0; x < vision_range_grid + 1; x++) { + for(int z = 0; z < vision_range_grid + 1; z++) { + nearbyGroups.addAll(grid.getOrDefault(pos.addClone( x, z), new HashSet<>())); + nearbyGroups.addAll(grid.getOrDefault(pos.addClone(-x, z), new HashSet<>())); + nearbyGroups.addAll(grid.getOrDefault(pos.addClone( x, -z), new HashSet<>())); + nearbyGroups.addAll(grid.getOrDefault(pos.addClone(-x, -z), new HashSet<>())); + } + } + + return nearbyGroups; + } +} diff --git a/src/main/java/emu/grasscutter/game/activity/condition/ActivityCondition.java b/src/main/java/emu/grasscutter/game/activity/condition/ActivityCondition.java new file mode 100644 index 000000000..46b6f739a --- /dev/null +++ b/src/main/java/emu/grasscutter/game/activity/condition/ActivityCondition.java @@ -0,0 +1,17 @@ +package emu.grasscutter.game.activity.condition; + +import emu.grasscutter.data.excels.ActivityCondExcelConfigData; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + + +/** + * This annotation marks condition types for NewActivityCondExcelConfigData.json ({@link ActivityCondExcelConfigData}). To use it you should mark + * class that extends ActivityConditionBaseHandler, and it will be automatically picked during activity manager initiation. + * + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface ActivityCondition { + ActivityConditions value(); +} diff --git a/src/main/java/emu/grasscutter/game/activity/condition/ActivityConditionBaseHandler.java b/src/main/java/emu/grasscutter/game/activity/condition/ActivityConditionBaseHandler.java new file mode 100644 index 000000000..ef7a2181c --- /dev/null +++ b/src/main/java/emu/grasscutter/game/activity/condition/ActivityConditionBaseHandler.java @@ -0,0 +1,21 @@ +package emu.grasscutter.game.activity.condition; + +import emu.grasscutter.data.excels.ActivityCondExcelConfigData; +import emu.grasscutter.game.activity.ActivityConfigItem; +import emu.grasscutter.game.activity.PlayerActivityData; + +/** + * Base handler for all activity conditions that are listed in NewActivityCondExcelConfigData.json ({@link ActivityCondExcelConfigData}) + */ +public abstract class ActivityConditionBaseHandler { + + /** + * Execute activity condition handler and return result of it's calculation + * + * @param activityData {@link PlayerActivityData} object containing info about activity + * @param activityConfig + * @param params params for handler + * @return result of condition calculation + */ + public abstract boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params); +} diff --git a/src/main/java/emu/grasscutter/game/activity/condition/ActivityConditionExecutor.java b/src/main/java/emu/grasscutter/game/activity/condition/ActivityConditionExecutor.java new file mode 100644 index 000000000..f2f512118 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/activity/condition/ActivityConditionExecutor.java @@ -0,0 +1,10 @@ +package emu.grasscutter.game.activity.condition; + +import java.util.List; + +public interface ActivityConditionExecutor { + + List getMeetActivitiesConditions(List condIds); + + boolean meetsCondition(int activityCondId); +} diff --git a/src/main/java/emu/grasscutter/game/activity/condition/ActivityConditions.java b/src/main/java/emu/grasscutter/game/activity/condition/ActivityConditions.java new file mode 100644 index 000000000..c42efa5b7 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/activity/condition/ActivityConditions.java @@ -0,0 +1,51 @@ +package emu.grasscutter.game.activity.condition; + +public enum ActivityConditions { + NEW_ACTIVITY_COND_PLAYER_LEVEL_GREAT_EQUAL, + NEW_ACTIVITY_COND_NOT_FINISH_TALK, + NEW_ACTIVITY_COND_SALESMAN_CAN_DELIVER, + NEW_ACTIVITY_COND_FINISH_PHOTO_POS_ID, + NEW_ACTIVITY_COND_HACHI_FINISH_STEALTH_STAGE_EQUAL, + NEW_ACTIVITY_COND_UNLOCKED_ALL_LISTED_SCENE_POINTS, + NEW_ACTIVITY_COND_DAYS_GREAT_EQUAL, + NEW_ACTIVITY_COND_FINISH_BARTENDER_LEVEL, + NEW_ACTIVITY_COND_FINISH_HACHI_STAGE, + NEW_ACTIVITY_COND_FINISH_ANY_INSTABLE_SPRAY_CHALLENGE_STAGE, + NEW_ACTIVITY_COND_HACHI_FINISH_BATTLE_STAGE_EQUAL, + NEW_ACTIVITY_COND_FINISH_CHANNELLER_SLAB_APPOINTED_STAGE_ALL_CAMP, + NEW_ACTIVITY_COND_FINISH_WATCHER, + NEW_ACTIVITY_COND_FINISH_REGION_SEARCH, + NEW_ACTIVITY_COND_FINISH_WATER_SPIRIT_PHASE, + NEW_ACTIVITY_COND_SEA_LAMP_POPULARIT, + NEW_ACTIVITY_COND_FINISH_DIG_ACTIVITY, + NEW_ACTIVITY_COND_FINISH_FIND_HILICHURL_LEVEL_EQUAL, + NEW_ACTIVITY_COND_GACHA_CAN_CREATE_ROBOT, + NEW_ACTIVITY_COND_FINISH_SALVAGE_STAGE, + NEW_ACTIVITY_COND_FINISH_MUSIC_GAME_ALL_LEVEL, + NEW_ACTIVITY_COND_DAYS_LESS, + NEW_ACTIVITY_COND_QUEST_FINISH, + NEW_ACTIVITY_COND_QUEST_GLOBAL_VAR_EQUAL, + NEW_ACTIVITY_COND_GROUP_BUNDLE_FINISHED, + NEW_ACTIVITY_COND_SEA_LAMP_PHASE, + NEW_ACTIVITY_COND_FINISH_CHANNELLER_SLAB_ANY_STAGE_ALL_CAMP, + NEW_ACTIVITY_COND_LUMINANCE_STONE_CHALLENGE_FINAL_GALLERY_COMPLETE, + NEW_ACTIVITY_COND_PLANT_FLOWER_CAN_DELIVER, + NEW_ACTIVITY_COND_LUMINANCE_STONE_CHALLENGE_STAGE_GREAT_EQUAL, + NEW_ACTIVITY_COND_FINISH_ANY_ARENA_CHALLENGE_LEVEL, + NEW_ACTIVITY_COND_FINISH_CUSTOM_DUNGEON_OFFICIAL, + NEW_ACTIVITY_COND_SCENE_MP_PLAY_ACTIVATED, + NEW_ACTIVITY_COND_FINISH_FIND_HILICHURL_LEVEL_LESS, + NEW_ACTIVITY_COND_TIME_GREATER, + NEW_ACTIVITY_COND_CREATE_NPC, + NEW_ACTIVITY_COND_TREASURE_SEELIE_FINISH_REGION, + NEW_ACTIVITY_COND_LUNA_RITE_ATMOSPHERE, + NEW_ACTIVITY_COND_OFFERING_LEVEL_GREAT_EQUAL, + NEW_ACTIVITY_COND_FINISH_CHANNELLER_SLAB_ANY_ONEOFF_DUNGEON, + NEW_ACTIVITY_COND_QUEST_FINISH_ALLOW_QUICK_OPEN, + NEW_ACTIVITY_COND_FINISH_POTION_ANY_LEVEL, + NEW_ACTIVITY_COND_MECHANICUS_OPEN, + NEW_ACTIVITY_COND_PLAYER_LEVEL_GREATER, + NEW_ACTIVITY_COND_SALESMAN_CAN_GET_REWARD, + NEW_ACTIVITY_COND_FINISH_REGION_SEARCH_LOGIC, + NEW_ACTIVITY_COND_FINISH_CHANNELLER_SLAB_ONEOFF_DUNGEON_IN_STAGE +} diff --git a/src/main/java/emu/grasscutter/game/activity/condition/AllActivityConditionBuilder.java b/src/main/java/emu/grasscutter/game/activity/condition/AllActivityConditionBuilder.java new file mode 100644 index 000000000..d5eddc2d4 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/activity/condition/AllActivityConditionBuilder.java @@ -0,0 +1,60 @@ +package emu.grasscutter.game.activity.condition; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.excels.ActivityCondExcelConfigData; +import emu.grasscutter.game.quest.handlers.QuestBaseHandler; +import org.reflections.Reflections; + +import java.util.AbstractMap; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Class that used for scanning classpath, picking up all activity conditions (for NewActivityCondExcelConfigData.json {@link ActivityCondExcelConfigData}) + * and saving them to map. Check for more info {@link ActivityCondition} + */ +public class AllActivityConditionBuilder { + + /** + * Build activity conditions handlers + * + * @return map containing all condition handlers for NewActivityCondExcelConfigData.json + */ + static public Map buildActivityConditions() { + return new AllActivityConditionBuilder().initActivityConditions(); + } + + private Map initActivityConditions() { + Reflections reflector = Grasscutter.reflector; + return reflector.getTypesAnnotatedWith(ActivityCondition.class).stream() + .map(this::newInstance) + .map(h -> new AbstractMap.SimpleEntry<>(extractActionType(h), h)) + .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue)); + } + + private ActivityConditions extractActionType(ActivityConditionBaseHandler e) { + ActivityCondition condition = e.getClass().getAnnotation(ActivityCondition.class); + if (condition == null) { + Grasscutter.getLogger().error("Failed to read command type for class {}", e.getClass().getName()); + return null; + } + + return condition.value(); + } + + private ActivityConditionBaseHandler newInstance(Class clazz) { + try { + Object result = clazz.getDeclaredConstructor().newInstance(); + if (result instanceof ActivityConditionBaseHandler) { + return (ActivityConditionBaseHandler) result; + } + Grasscutter.getLogger().error("Failed to initiate activity condition: {}, object have wrong type", clazz.getName()); + } catch (Exception e) { + String message = String.format("Failed to initiate activity condition: %s, %s", clazz.getName(), e.getMessage()); + Grasscutter.getLogger().error(message, e); + } + return null; + } + + +} diff --git a/src/main/java/emu/grasscutter/game/activity/condition/BasicActivityConditionExecutor.java b/src/main/java/emu/grasscutter/game/activity/condition/BasicActivityConditionExecutor.java new file mode 100644 index 000000000..ac06b9036 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/activity/condition/BasicActivityConditionExecutor.java @@ -0,0 +1,71 @@ +package emu.grasscutter.game.activity.condition; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.excels.ActivityCondExcelConfigData; +import emu.grasscutter.game.activity.ActivityConfigItem; +import emu.grasscutter.game.activity.PlayerActivityData; +import emu.grasscutter.game.activity.condition.all.UnknownActivityConditionHandler; +import emu.grasscutter.game.quest.enums.LogicType; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; + +import java.util.List; +import java.util.Map; +import java.util.function.BooleanSupplier; +import java.util.stream.Collectors; + +public class BasicActivityConditionExecutor implements ActivityConditionExecutor { + + private final Map activityConfigItemMap; + private final Int2ObjectMap activityConditions; + + private final Int2ObjectMap playerActivityDataByActivityCondId; + private final Map activityConditionsHandlers; + + private static final UnknownActivityConditionHandler UNKNOWN_CONDITION_HANDLER = new UnknownActivityConditionHandler(); + + public BasicActivityConditionExecutor(Map activityConfigItemMap, + Int2ObjectMap activityConditions, + Int2ObjectMap playerActivityDataByActivityCondId, + Map activityConditionsHandlers) { + this.activityConfigItemMap = activityConfigItemMap; + this.activityConditions = activityConditions; + this.playerActivityDataByActivityCondId = playerActivityDataByActivityCondId; + this.activityConditionsHandlers = activityConditionsHandlers; + } + + @Override + public List getMeetActivitiesConditions(List condIds) { + return condIds.stream() + .filter(this::meetsCondition) + .collect(Collectors.toList()); + } + + @Override + public boolean meetsCondition(int activityCondId) { + ActivityCondExcelConfigData condData = activityConditions.get(activityCondId); + + if (condData == null) { + Grasscutter.getLogger().error("Could not find condition for activity with id = {}", activityCondId); + return false; + } + + LogicType condComb = condData.getCondComb(); + if (condComb == null) { + condComb = LogicType.LOGIC_AND; + } + + PlayerActivityData activity = playerActivityDataByActivityCondId.get(activityCondId); + if(activity==null){ + return false; + } + ActivityConfigItem activityConfig = activityConfigItemMap.get(activity.getActivityId()); + List predicates = condData.getCond() + .stream() + .map(c -> (BooleanSupplier) () -> + activityConditionsHandlers + .getOrDefault(c.getType(), UNKNOWN_CONDITION_HANDLER).execute(activity, activityConfig, c.paramArray())) + .collect(Collectors.toList()); + + return LogicType.calculate(condComb, predicates); + } +} diff --git a/src/main/java/emu/grasscutter/game/activity/condition/PlayerActivityDataMappingBuilder.java b/src/main/java/emu/grasscutter/game/activity/condition/PlayerActivityDataMappingBuilder.java new file mode 100644 index 000000000..6c0bba497 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/activity/condition/PlayerActivityDataMappingBuilder.java @@ -0,0 +1,80 @@ +package emu.grasscutter.game.activity.condition; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.excels.ActivityCondExcelConfigData; +import emu.grasscutter.game.activity.PlayerActivityData; +import it.unimi.dsi.fastutil.ints.AbstractInt2ObjectMap.BasicEntry; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap; + +import java.util.Map; + +/** + * This class is used for building mapping for PlayerActivityData + */ +public class PlayerActivityDataMappingBuilder { + + + private final Map playerActivityDataMap; + + private final Int2ObjectMap activityCondMap; + + /** + * Build mapping for PlayerActivityData. + * + * @return mapping for activity data. Key is condId from NewActivityCondExcelConfigData.json ({@link ActivityCondExcelConfigData}) resource, + * value is {@link PlayerActivityData} class for related activity. + */ + public static Int2ObjectMap buildPlayerActivityDataByActivityCondId(Map activities) { + return new PlayerActivityDataMappingBuilder(activities).buildMappings(); + } + + public PlayerActivityDataMappingBuilder(Map playerActivityDataMap) { + this.playerActivityDataMap = playerActivityDataMap; + activityCondMap = GameData.getActivityCondExcelConfigDataMap(); + } + + private Int2ObjectMap buildMappings() { + Int2ObjectMap result = new Int2ObjectRBTreeMap<>(); + + activityCondMap + .int2ObjectEntrySet() + .stream() + .map(entry -> new BasicEntry<>(entry.getIntKey(), getPlayerActivityDataByCondId(entry.getIntKey()))) + .filter(entry -> entry.getValue() != null) + .forEach(entry -> result.put(entry.getIntKey(), entry.getValue())); + + return result; + } + + private PlayerActivityData getPlayerActivityDataByCondId(Integer key) { + return playerActivityDataMap.get(detectActivityDataIdByCondId(key)); + } + + /** + * Detect activity data id by cond id. Cond id comes from condId field from NewActivityCondExcelConfigData.json. + * See {@link ActivityCondExcelConfigData} for condId. + *

+ * Generally, there are 3 cases: + *

    + *
  1. Activity data id >= 5003. Then cond id will be activity data id plus 3 additional digits. + * For example: activity data id = 5087, cond id = 5087xxx (x - any digit)
  2. + *
  3. Activity data id = 5001. Then cond id will be activity data id plus 2 additional digits. + * For example: activity data id = 5001, cond id = 5001xx (x - any digit)
  4. + *
  5. Activity data id one of [1001]. Then cond id will be activity data id plus 2 additional digits. + * This also applied to activity data id = 1002. For example: activity data id = 1001, cond id = 1001x (x - any digit>
  6. + *
+ * + * @param key cond id for which activity data id should be defined + * @return activity data for given cond id. Returns -1 if activity was not found. + */ + private Integer detectActivityDataIdByCondId(Integer key) { + if (key / 10 == 1001 || key / 10 == 1002) { + return 1001; + } else if (key / 100 == 5001) { + return key / 100; + } else { + return key / 1000; + } + } +} diff --git a/src/main/java/emu/grasscutter/game/activity/condition/all/DayLess.java b/src/main/java/emu/grasscutter/game/activity/condition/all/DayLess.java new file mode 100644 index 000000000..ef6c8263f --- /dev/null +++ b/src/main/java/emu/grasscutter/game/activity/condition/all/DayLess.java @@ -0,0 +1,16 @@ +package emu.grasscutter.game.activity.condition.all; + +import emu.grasscutter.game.activity.ActivityConfigItem; +import emu.grasscutter.game.activity.PlayerActivityData; +import emu.grasscutter.game.activity.condition.ActivityCondition; +import emu.grasscutter.game.activity.condition.ActivityConditionBaseHandler; + +import static emu.grasscutter.game.activity.condition.ActivityConditions.NEW_ACTIVITY_COND_DAYS_LESS; + +@ActivityCondition(NEW_ACTIVITY_COND_DAYS_LESS) +public class DayLess extends ActivityConditionBaseHandler { + @Override + public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params) { + return true; //TODO implement this and add possibility to always return true + } +} diff --git a/src/main/java/emu/grasscutter/game/activity/condition/all/DaysGreatEqual.java b/src/main/java/emu/grasscutter/game/activity/condition/all/DaysGreatEqual.java new file mode 100644 index 000000000..0377372ce --- /dev/null +++ b/src/main/java/emu/grasscutter/game/activity/condition/all/DaysGreatEqual.java @@ -0,0 +1,21 @@ +package emu.grasscutter.game.activity.condition.all; + +import emu.grasscutter.game.activity.ActivityConfigItem; +import emu.grasscutter.game.activity.PlayerActivityData; +import emu.grasscutter.game.activity.condition.ActivityCondition; +import emu.grasscutter.game.activity.condition.ActivityConditionBaseHandler; + +import java.util.Date; + +import static emu.grasscutter.game.activity.condition.ActivityConditions.NEW_ACTIVITY_COND_DAYS_GREAT_EQUAL; + +@ActivityCondition(NEW_ACTIVITY_COND_DAYS_GREAT_EQUAL) +public class DaysGreatEqual extends ActivityConditionBaseHandler { + @Override + public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params) { + Date activityBeginTime = activityConfig.getBeginTime(); + long timeDiff = System.currentTimeMillis() - activityBeginTime.getTime(); + int days = (int) (timeDiff / (1000 * 60 * 60 * 24L)); + return days + 1 >= params[0]; + } +} diff --git a/src/main/java/emu/grasscutter/game/activity/condition/all/FinishWatcher.java b/src/main/java/emu/grasscutter/game/activity/condition/all/FinishWatcher.java new file mode 100644 index 000000000..5b775f4a9 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/activity/condition/all/FinishWatcher.java @@ -0,0 +1,24 @@ +package emu.grasscutter.game.activity.condition.all; + +import emu.grasscutter.game.activity.ActivityConfigItem; +import emu.grasscutter.game.activity.PlayerActivityData; +import emu.grasscutter.game.activity.condition.ActivityCondition; +import emu.grasscutter.game.activity.condition.ActivityConditionBaseHandler; +import emu.grasscutter.game.activity.condition.ActivityConditions; +import lombok.val; + +@ActivityCondition(ActivityConditions.NEW_ACTIVITY_COND_FINISH_WATCHER) +public class FinishWatcher extends ActivityConditionBaseHandler { + + @Override + public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params) { + val watcherMap = activityData.getWatcherInfoMap(); + for (int param : params) { + val watcher = watcherMap.get(param); + if(watcher == null || !watcher.isFinished()){ + return false; + } + } + return true; + } +} diff --git a/src/main/java/emu/grasscutter/game/activity/condition/all/NotFinishTalk.java b/src/main/java/emu/grasscutter/game/activity/condition/all/NotFinishTalk.java new file mode 100644 index 000000000..b863b3885 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/activity/condition/all/NotFinishTalk.java @@ -0,0 +1,22 @@ +package emu.grasscutter.game.activity.condition.all; + +import emu.grasscutter.game.activity.ActivityConfigItem; +import emu.grasscutter.game.activity.PlayerActivityData; +import emu.grasscutter.game.activity.condition.ActivityCondition; +import emu.grasscutter.game.activity.condition.ActivityConditionBaseHandler; + +import static emu.grasscutter.game.activity.condition.ActivityConditions.NEW_ACTIVITY_COND_NOT_FINISH_TALK; + +@ActivityCondition(NEW_ACTIVITY_COND_NOT_FINISH_TALK) +public class NotFinishTalk extends ActivityConditionBaseHandler { + @Override + public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params) { + return activityData + .getPlayer() + .getQuestManager() + .getMainQuests() + .int2ObjectEntrySet() + .stream() + .noneMatch(q -> q.getValue().getTalks().get(params[0]) != null); //FIXME taken from ContentCompleteTalk + } +} diff --git a/src/main/java/emu/grasscutter/game/activity/condition/all/PlayerLevelGreatEqualActivityActivityCondition.java b/src/main/java/emu/grasscutter/game/activity/condition/all/PlayerLevelGreatEqualActivityActivityCondition.java new file mode 100644 index 000000000..9d03da1a1 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/activity/condition/all/PlayerLevelGreatEqualActivityActivityCondition.java @@ -0,0 +1,17 @@ +package emu.grasscutter.game.activity.condition.all; + +import emu.grasscutter.game.activity.ActivityConfigItem; +import emu.grasscutter.game.activity.PlayerActivityData; +import emu.grasscutter.game.activity.condition.ActivityCondition; +import emu.grasscutter.game.activity.condition.ActivityConditionBaseHandler; + +import static emu.grasscutter.game.activity.condition.ActivityConditions.NEW_ACTIVITY_COND_PLAYER_LEVEL_GREAT_EQUAL; + +@ActivityCondition(NEW_ACTIVITY_COND_PLAYER_LEVEL_GREAT_EQUAL) +public class PlayerLevelGreatEqualActivityActivityCondition extends ActivityConditionBaseHandler { + + @Override + public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params) { + return activityData.getPlayer().getLevel() >= params[0]; + } +} diff --git a/src/main/java/emu/grasscutter/game/activity/condition/all/QuestFinished.java b/src/main/java/emu/grasscutter/game/activity/condition/all/QuestFinished.java new file mode 100644 index 000000000..cb2bea414 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/activity/condition/all/QuestFinished.java @@ -0,0 +1,23 @@ +package emu.grasscutter.game.activity.condition.all; + +import emu.grasscutter.game.activity.ActivityConfigItem; +import emu.grasscutter.game.activity.PlayerActivityData; +import emu.grasscutter.game.activity.condition.ActivityCondition; +import emu.grasscutter.game.activity.condition.ActivityConditionBaseHandler; +import emu.grasscutter.game.quest.GameQuest; +import emu.grasscutter.game.quest.enums.QuestState; + +import static emu.grasscutter.game.activity.condition.ActivityConditions.NEW_ACTIVITY_COND_QUEST_FINISH; + +@ActivityCondition(NEW_ACTIVITY_COND_QUEST_FINISH) +public class QuestFinished extends ActivityConditionBaseHandler { + @Override + public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params) { + GameQuest quest = activityData + .getPlayer() + .getQuestManager() + .getQuestById(params[0]); + + return quest != null && quest.getState() == QuestState.QUEST_STATE_FINISHED; + } +} diff --git a/src/main/java/emu/grasscutter/game/activity/condition/all/SalesmanCanDeliver.java b/src/main/java/emu/grasscutter/game/activity/condition/all/SalesmanCanDeliver.java new file mode 100644 index 000000000..52e24f7d2 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/activity/condition/all/SalesmanCanDeliver.java @@ -0,0 +1,19 @@ +package emu.grasscutter.game.activity.condition.all; + +import emu.grasscutter.game.activity.ActivityConfigItem; +import emu.grasscutter.game.activity.PlayerActivityData; +import emu.grasscutter.game.activity.condition.ActivityCondition; +import emu.grasscutter.game.activity.condition.ActivityConditionBaseHandler; + +import static emu.grasscutter.game.activity.condition.ActivityConditions.NEW_ACTIVITY_COND_SALESMAN_CAN_DELIVER; + +@ActivityCondition(NEW_ACTIVITY_COND_SALESMAN_CAN_DELIVER) +public class SalesmanCanDeliver extends ActivityConditionBaseHandler { + @Override + public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params) { + //TODO need to reverse engineer this logic. + //This condition appears only in one condition "condId": 5003001 + //and this condition accept no params. I have no idea how to implement it + return false; + } +} diff --git a/src/main/java/emu/grasscutter/game/activity/condition/all/UnknownActivityConditionHandler.java b/src/main/java/emu/grasscutter/game/activity/condition/all/UnknownActivityConditionHandler.java new file mode 100644 index 000000000..740cabf59 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/activity/condition/all/UnknownActivityConditionHandler.java @@ -0,0 +1,18 @@ +package emu.grasscutter.game.activity.condition.all; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.game.activity.ActivityConfigItem; +import emu.grasscutter.game.activity.PlayerActivityData; +import emu.grasscutter.game.activity.condition.ActivityConditionBaseHandler; + +/** + * This class is used when condition was not found + */ +public class UnknownActivityConditionHandler extends ActivityConditionBaseHandler { + + @Override + public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params) { + Grasscutter.getLogger().error("Called unknown condition handler"); + return false; + } +} diff --git a/src/main/java/emu/grasscutter/game/activity/trialavatar/TrialAvatarActivityChallengeTrigger.java b/src/main/java/emu/grasscutter/game/activity/trialavatar/TrialAvatarActivityChallengeTrigger.java new file mode 100644 index 000000000..b470b046d --- /dev/null +++ b/src/main/java/emu/grasscutter/game/activity/trialavatar/TrialAvatarActivityChallengeTrigger.java @@ -0,0 +1,36 @@ +package emu.grasscutter.game.activity.trialavatar; + +import emu.grasscutter.game.activity.ActivityWatcher; +import emu.grasscutter.game.activity.ActivityWatcherType; +import emu.grasscutter.game.activity.PlayerActivityData; +import emu.grasscutter.game.props.WatcherTriggerType; + +import lombok.val; +import java.util.stream.Stream; + +@ActivityWatcherType(WatcherTriggerType.TRIGGER_FINISH_CHALLENGE) +public class TrialAvatarActivityChallengeTrigger extends ActivityWatcher { + @Override + protected boolean isMeet(String... param) { + if(param.length < 3) return false; + + val handler = (TrialAvatarActivityHandler) getActivityHandler(); + if(handler == null) return false; + + val paramList = handler.getTriggerParamList(); + if(paramList.isEmpty()) return false; + + val paramCond = Stream.of(paramList.get(0).split(",")).toList(); + return Stream.of(param).allMatch(x -> paramCond.contains(x)); + } + + @Override + public void trigger(PlayerActivityData playerActivityData, String... param) { + if (!isMeet(param)) return; + + val handler = (TrialAvatarActivityHandler) getActivityHandler(); + if(handler == null) return; + + handler.setPassDungeon(playerActivityData); + } +} diff --git a/src/main/java/emu/grasscutter/game/activity/trialavatar/TrialAvatarActivityHandler.java b/src/main/java/emu/grasscutter/game/activity/trialavatar/TrialAvatarActivityHandler.java new file mode 100644 index 000000000..1c20991a4 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/activity/trialavatar/TrialAvatarActivityHandler.java @@ -0,0 +1,144 @@ +package emu.grasscutter.game.activity.trialavatar; + +import com.esotericsoftware.reflectasm.ConstructorAccess; +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.excels.RewardData; +import emu.grasscutter.game.activity.ActivityWatcher; +import emu.grasscutter.game.activity.DefaultWatcher; +import emu.grasscutter.game.dungeons.DungeonTrialTeam; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.props.ActionReason; +import emu.grasscutter.game.props.WatcherTriggerType; +import emu.grasscutter.game.activity.ActivityHandler; +import emu.grasscutter.game.activity.GameActivity; +import emu.grasscutter.game.activity.PlayerActivityData; +import emu.grasscutter.game.props.ActivityType; +import emu.grasscutter.net.proto.ActivityInfoOuterClass.ActivityInfo; +import emu.grasscutter.net.proto.TrialAvatarGrantRecordOuterClass.TrialAvatarGrantRecord.GrantReason; +import emu.grasscutter.server.packet.send.PacketActivityInfoNotify; +import emu.grasscutter.server.packet.send.PacketScenePlayerLocationNotify; +import emu.grasscutter.utils.JsonUtils; + +import java.util.*; +import java.util.stream.*; +import lombok.*; + +@GameActivity(ActivityType.NEW_ACTIVITY_TRIAL_AVATAR) +public class TrialAvatarActivityHandler extends ActivityHandler { + @Getter @Setter private int selectedTrialAvatarIndex; + + @Override + public void onInitPlayerActivityData(PlayerActivityData playerActivityData) { + TrialAvatarPlayerData trialAvatarPlayerData = TrialAvatarPlayerData.create(getActivityConfigItem().getScheduleId()); + + playerActivityData.setDetail(trialAvatarPlayerData); + } + + @Override + public void onProtoBuild(PlayerActivityData playerActivityData, ActivityInfo.Builder activityInfo) { + TrialAvatarPlayerData trialAvatarPlayerData = getTrialAvatarPlayerData(playerActivityData); + + activityInfo.setTrialAvatarInfo(trialAvatarPlayerData.toProto()); + } + + @Override + public void initWatchers(Map> activityWatcherTypeMap) { + var watcherType = activityWatcherTypeMap.get(WatcherTriggerType.TRIGGER_FINISH_CHALLENGE); + ActivityWatcher watcher; + if(watcherType != null){ + watcher = (ActivityWatcher) watcherType.newInstance(); + }else{ + watcher = new DefaultWatcher(); + } + + watcher.setActivityHandler(this); + getWatchersMap().computeIfAbsent(WatcherTriggerType.TRIGGER_FINISH_CHALLENGE, k -> new ArrayList<>()); + getWatchersMap().get(WatcherTriggerType.TRIGGER_FINISH_CHALLENGE).add(watcher); + } + + public TrialAvatarPlayerData getTrialAvatarPlayerData(PlayerActivityData playerActivityData) { + if (playerActivityData.getDetail() == null || playerActivityData.getDetail().isBlank()) { + onInitPlayerActivityData(playerActivityData); + playerActivityData.save(); + } + + return JsonUtils.decode(playerActivityData.getDetail(), TrialAvatarPlayerData.class); + } + + public int getTrialActivityDungeonId(int trialAvatarIndexId) { + val data = GameData.getTrialAvatarActivityDataByAvatarIndex(trialAvatarIndexId); + return data!=null ? data.getDungeonId() : -1; + } + + public List getTriggerParamList() { + val data = GameData.getTrialAvatarActivityDataByAvatarIndex(getSelectedTrialAvatarIndex()); + return data!=null ? data.getTriggerConfig().getParamList() : Collections.emptyList(); + } + + public boolean enterTrialDungeon(Player player, int trialAvatarIndexId, int enterPointId) { + // TODO, not sure if this will cause problem in MP, since we are entering trial activity dungeon + player.sendPacket(new PacketScenePlayerLocationNotify(player.getScene())); // official does send this + + if (!player.getServer().getDungeonSystem().enterDungeon( + player, + enterPointId, + getTrialActivityDungeonId(trialAvatarIndexId))) return false; + + setSelectedTrialAvatarIndex(trialAvatarIndexId); + + return true; + } + + public List getBattleAvatarsList() { + val activityData = GameData.getTrialAvatarActivityDataByAvatarIndex(getSelectedTrialAvatarIndex()); + if (activityData == null || activityData.getBattleAvatarsList().isBlank()) return List.of(); + return Stream.of(activityData.getBattleAvatarsList().split(",")).map(Integer::parseInt).toList(); + } + + public DungeonTrialTeam getTrialAvatarDungeonTeam(){ + List battleAvatarsList = getBattleAvatarsList(); + if (battleAvatarsList.isEmpty()) return null; + + return new DungeonTrialTeam(battleAvatarsList, GrantReason.GRANT_REASON_BY_TRIAL_AVATAR_ACTIVITY); + } + + public void unsetTrialAvatarTeam(Player player) { + if (getSelectedTrialAvatarIndex() <= 0) return; + player.removeTrialAvatarForActivity(); + setSelectedTrialAvatarIndex(0); + } + + public boolean getReward(Player player, int trialAvatarIndexId) { + val playerActivityData = player.getActivityManager().getPlayerActivityDataByActivityType(ActivityType.NEW_ACTIVITY_TRIAL_AVATAR); + + if(playerActivityData.isEmpty()){ + return false; + } + + TrialAvatarPlayerData trialAvatarPlayerData = getTrialAvatarPlayerData(playerActivityData.get()); + TrialAvatarPlayerData.RewardInfoItem rewardInfo = trialAvatarPlayerData.getRewardInfo(trialAvatarIndexId); + if (rewardInfo == null) return false; + + RewardData rewardParam = GameData.getRewardDataMap().get(rewardInfo.getRewardId()); + if (rewardParam == null) return false; + + player.getInventory().addItemParamDatas(rewardParam.getRewardItemList(), ActionReason.TrialAvatarActivityFirstPassReward); + rewardInfo.setReceivedReward(true); + playerActivityData.get().setDetail(trialAvatarPlayerData); + playerActivityData.get().save(); + return true; + } + + public void setPassDungeon(PlayerActivityData playerActivityData) { + TrialAvatarPlayerData trialAvatarPlayerData = getTrialAvatarPlayerData(playerActivityData); + TrialAvatarPlayerData.RewardInfoItem rewardInfo = trialAvatarPlayerData.getRewardInfo(getSelectedTrialAvatarIndex()); + if (rewardInfo == null) return; + + rewardInfo.setPassedDungeon(true); + playerActivityData.setDetail(trialAvatarPlayerData); + playerActivityData.save(); + Player player = Grasscutter.getGameServer().getPlayerByUid(playerActivityData.getUid()); + player.sendPacket(new PacketActivityInfoNotify(toProto(playerActivityData, player.getActivityManager().getConditionExecutor()))); + } +} diff --git a/src/main/java/emu/grasscutter/game/activity/trialavatar/TrialAvatarPlayerData.java b/src/main/java/emu/grasscutter/game/activity/trialavatar/TrialAvatarPlayerData.java new file mode 100644 index 000000000..1001d1858 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/activity/trialavatar/TrialAvatarPlayerData.java @@ -0,0 +1,90 @@ +package emu.grasscutter.game.activity.trialavatar; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.common.BaseTrialActivityData; +import emu.grasscutter.net.proto.TrialAvatarActivityDetailInfoOuterClass.TrialAvatarActivityDetailInfo; +import emu.grasscutter.net.proto.TrialAvatarActivityRewardDetailInfoOuterClass.TrialAvatarActivityRewardDetailInfo; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Data; +import lombok.experimental.FieldDefaults; +import lombok.val; + +import java.util.List; +import java.util.stream.*; + +@Data +@FieldDefaults(level = AccessLevel.PRIVATE) +@Builder(builderMethodName = "of") +public class TrialAvatarPlayerData { + List rewardInfoList; + + private static BaseTrialActivityData getActivityData(int scheduleId){ + // prefer custom data over official data + return GameData.getTrialAvatarActivityCustomData().isEmpty() ? GameData.getTrialAvatarActivityDataMap().get(scheduleId) + : GameData.getTrialAvatarActivityCustomData().get(scheduleId); + } + + public static List getAvatarIdList(int scheduleId) { + val activityData = getActivityData(scheduleId); + return activityData != null ? activityData.getAvatarIndexIdList() : List.of(); + } + + public static List getRewardIdList(int scheduleId) { + val activityData = getActivityData(scheduleId); + return activityData != null ? activityData.getRewardIdList() : List.of(); + } + + public static TrialAvatarPlayerData create(int scheduleId) { + List avatarIds = getAvatarIdList(scheduleId); + List rewardIds = getRewardIdList(scheduleId); + return TrialAvatarPlayerData.of() + .rewardInfoList(IntStream.range(0, avatarIds.size()) + .filter(i -> avatarIds.get(i) > 0 && rewardIds.get(i) > 0) + .mapToObj(i -> RewardInfoItem.create( + avatarIds.get(i), + rewardIds.get(i))) + .collect(Collectors.toList())) + .build(); + } + + public TrialAvatarActivityDetailInfo toProto() { + return TrialAvatarActivityDetailInfo.newBuilder() + .addAllRewardInfoList(getRewardInfoList().stream() + .map(RewardInfoItem::toProto).toList()) + .build(); + } + + public RewardInfoItem getRewardInfo(int trialAvatarIndexId) { + return getRewardInfoList().stream().filter(x -> x.getTrialAvatarIndexId() == trialAvatarIndexId) + .findFirst().orElse(null); + } + + @Data + @FieldDefaults(level = AccessLevel.PRIVATE) + @Builder(builderMethodName = "of") + public static class RewardInfoItem { + int trialAvatarIndexId; + int rewardId; + boolean passedDungeon; + boolean receivedReward; + + public static RewardInfoItem create(int trialAvatarIndexId, int rewardId) { + return RewardInfoItem.of() + .trialAvatarIndexId(trialAvatarIndexId) + .rewardId(rewardId) + .passedDungeon(false) + .receivedReward(false) + .build(); + } + + public TrialAvatarActivityRewardDetailInfo toProto() { + return TrialAvatarActivityRewardDetailInfo.newBuilder() + .setTrialAvatarIndexId(getTrialAvatarIndexId()) + .setRewardId(getRewardId()) + .setPassedDungeon(isPassedDungeon()) + .setReceivedReward(isReceivedReward()) + .build(); + } + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/DungeonEndStats.java b/src/main/java/emu/grasscutter/game/dungeons/DungeonEndStats.java new file mode 100644 index 000000000..6943ab5a3 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/DungeonEndStats.java @@ -0,0 +1,18 @@ +package emu.grasscutter.game.dungeons; + +import emu.grasscutter.game.dungeons.dungeon_results.BaseDungeonResult; +import lombok.Getter; + +public class DungeonEndStats { + @Getter private int killedMonsters; + @Getter private int timeTaken; + @Getter private int openChestCount; + @Getter private BaseDungeonResult.DungeonEndReason dungeonResult; + + public DungeonEndStats(int killedMonsters, int timeTaken, int openChestCount, BaseDungeonResult.DungeonEndReason dungeonResult){ + this.killedMonsters = killedMonsters; + this.timeTaken = timeTaken; + this.dungeonResult = dungeonResult; + this.openChestCount = openChestCount; + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/DungeonManager.java b/src/main/java/emu/grasscutter/game/dungeons/DungeonManager.java new file mode 100644 index 000000000..c6ba88b02 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/DungeonManager.java @@ -0,0 +1,315 @@ +package emu.grasscutter.game.dungeons; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.common.ItemParamData; +import emu.grasscutter.data.excels.DungeonData; +import emu.grasscutter.data.excels.DungeonPassConfigData; +import emu.grasscutter.game.activity.trialavatar.TrialAvatarActivityHandler; +import emu.grasscutter.game.dungeons.dungeon_results.BaseDungeonResult; +import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType; +import emu.grasscutter.game.inventory.GameItem; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.props.ActionReason; +import emu.grasscutter.game.props.ActivityType; +import emu.grasscutter.game.props.WatcherTriggerType; +import emu.grasscutter.game.quest.enums.LogicType; +import emu.grasscutter.game.quest.enums.QuestContent; +import emu.grasscutter.game.world.Scene; +import emu.grasscutter.scripts.constants.EventType; +import emu.grasscutter.scripts.data.ScriptArgs; +import emu.grasscutter.server.packet.send.PacketDungeonWayPointNotify; +import emu.grasscutter.server.packet.send.PacketGadgetAutoPickDropInfoNotify; +import emu.grasscutter.utils.Position; +import emu.grasscutter.utils.Utils; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; +import lombok.Getter; +import lombok.NonNull; +import lombok.val; + +import javax.annotation.Nullable; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * TODO handle time limits + * TODO handle respawn points + * TODO handle team wipes and respawns + * TODO check monster level and levelConfigMap + */ +public class DungeonManager { + + @Getter private final Scene scene; + @Getter private final DungeonData dungeonData; + @Getter private final DungeonPassConfigData passConfigData; + + @Getter private final int[] finishedConditions; + private final IntSet rewardedPlayers = new IntOpenHashSet(); + private final Set activeDungeonWayPoints = new HashSet<>(); + private boolean ended = false; + private int newestWayPoint = 0; + @Getter private int startSceneTime = 0; + + DungeonTrialTeam trialTeam = null; + + public DungeonManager(@NonNull Scene scene, @NonNull DungeonData dungeonData) { + this.scene = scene; + this.dungeonData = dungeonData; + this.passConfigData = GameData.getDungeonPassConfigDataMap().get(dungeonData.getPassCond()); + this.finishedConditions = new int[passConfigData.getConds().size()]; + this.scene.setDungeonManager(this); + } + + public void triggerEvent(DungeonPassConditionType conditionType, int... params) { + if (ended) { + return; + } + for (int i = 0; i < passConfigData.getConds().size(); i++) { + var cond = passConfigData.getConds().get(i); + if (conditionType == cond.getCondType()) { + if (getScene().getWorld().getServer().getDungeonSystem().triggerCondition(cond, params)) { + finishedConditions[i] = 1; + } + + } + } + + if (isFinishedSuccessfully()) { + finishDungeon(); + } + + } + + public boolean isFinishedSuccessfully() { + return LogicType.calculate(passConfigData.getLogicType(), finishedConditions); + } + + public int getLevelForMonster(int id) { + //TODO should use levelConfigMap? and how? + return dungeonData.getShowLevel(); + } + + public boolean activateRespawnPoint(int pointId) { + val respawnPoint = GameData.getScenePointEntryById(scene.getId(), pointId); + + if (respawnPoint == null) { + Grasscutter.getLogger().warn("trying to activate unknown respawn point {}", pointId); + return false; + } + + scene.broadcastPacket(new PacketDungeonWayPointNotify(activeDungeonWayPoints.add(pointId), activeDungeonWayPoints)); + newestWayPoint = pointId; + + Grasscutter.getLogger().debug("[unimplemented respawn] activated respawn point {}", pointId); + return true; + } + + @Nullable + public Position getRespawnLocation() { + if (newestWayPoint == 0) { // validity is checked before setting it, so if != 0 its always valid + return null; + } + val pointData = GameData.getScenePointEntryById(scene.getId(), newestWayPoint).getPointData(); + return pointData.getTranPos() != null ? pointData.getTranPos() : pointData.getPos(); + } + + public Position getRespawnRotation() { + if (newestWayPoint == 0) { // validity is checked before setting it, so if != 0 its always valid + return null; + } + val pointData = GameData.getScenePointEntryById(scene.getId(), newestWayPoint).getPointData(); + return pointData.getRot() != null ? pointData.getRot() : null; + } + + public boolean getStatueDrops(Player player, boolean useCondensed, int groupId) { + if (!isFinishedSuccessfully() || dungeonData.getRewardPreviewData() == null || dungeonData.getRewardPreviewData().getPreviewItems().length == 0) { + return false; + } + + // Already rewarded + if (rewardedPlayers.contains(player.getUid())) { + return false; + } + + + if (!handleCost(player, useCondensed)) { + return false; + } + + // Get and roll rewards. + List rewards = new ArrayList<>(this.rollRewards(useCondensed)); + // Add rewards to player and send notification. + player.getInventory().addItems(rewards, ActionReason.DungeonStatueDrop); + player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards)); + + rewardedPlayers.add(player.getUid()); + + scene.getScriptManager().callEvent(new ScriptArgs(groupId, EventType.EVENT_DUNGEON_REWARD_GET)); + return true; + } + + public boolean handleCost(Player player, boolean useCondensed) { + int resinCost = dungeonData.getStatueCostCount() != 0 ? dungeonData.getStatueCostCount() : 20; + if (resinCost == 0) { + return true; + } + if (useCondensed) { + // Check if condensed resin is usable here. + // For this, we use the following logic for now: + // The normal resin cost of the dungeon has to be 20. + if (resinCost != 20) { + return false; + } + + // Spend the condensed resin and only proceed if the transaction succeeds. + return player.getResinManager().useCondensedResin(1); + } else if (dungeonData.getStatueCostID() == 106) { + // Spend the resin and only proceed if the transaction succeeds. + return player.getResinManager().useResin(resinCost); + } + return true; + } + + private List rollRewards(boolean useCondensed) { + List rewards = new ArrayList<>(); + int dungeonId = this.dungeonData.getId(); + // If we have specific drop data for this dungeon, we use it. + if (GameData.getDungeonDropDataMap().containsKey(dungeonId)) { + List dropEntries = GameData.getDungeonDropDataMap().get(dungeonId); + + // Roll for each drop group. + for (var entry : dropEntries) { + // Determine the number of drops we get for this entry. + int start = entry.getCounts().get(0); + int end = entry.getCounts().get(entry.getCounts().size() - 1); + var candidateAmounts = IntStream.range(start, end + 1).boxed().collect(Collectors.toList()); + + int amount = Utils.drawRandomListElement(candidateAmounts, entry.getProbabilities()); + + if (useCondensed) { + amount += Utils.drawRandomListElement(candidateAmounts, entry.getProbabilities()); + } + + // Double rewards in multiplay mode, if specified. + if (entry.isMpDouble() && this.getScene().getPlayerCount() > 1) { + amount *= 2; + } + + // Roll items for this group. + // Here, we have to handle stacking, or the client will not display results correctly. + // For now, we use the following logic: If the possible drop item are a list of multiple items, + // we roll them separately. If not, we stack them. This should work out in practice, at least + // for the currently existing set of dungeons. + if (entry.getItems().size() == 1) { + rewards.add(new GameItem(entry.getItems().get(0), amount)); + } else { + for (int i = 0; i < amount; i++) { + // int itemIndex = ThreadLocalRandom.current().nextInt(0, entry.getItems().size()); + // int itemId = entry.getItems().get(itemIndex); + int itemId = Utils.drawRandomListElement(entry.getItems(), entry.getItemProbabilities()); + rewards.add(new GameItem(itemId, 1)); + } + } + } + } + // Otherwise, we fall back to the preview data. + else { + Grasscutter.getLogger().info("No drop data found or dungeon {}, falling back to preview data ...", dungeonId); + for (ItemParamData param : dungeonData.getRewardPreviewData().getPreviewItems()) { + rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1))); + } + } + + return rewards; + } + + public void applyTrialTeam(Player player) { + if (getDungeonData() == null) return; + + switch (getDungeonData().getType()) { + // case DUNGEON_PLOT is handled by quest execs + case DUNGEON_ACTIVITY -> { + switch (getDungeonData().getPlayType()) { + case DUNGEON_PLAY_TYPE_TRIAL_AVATAR -> { + val activityHandler = player.getActivityManager() + .getActivityHandlerAs(ActivityType.NEW_ACTIVITY_TRIAL_AVATAR, TrialAvatarActivityHandler.class); + activityHandler.ifPresent(trialAvatarActivityHandler -> + this.trialTeam = trialAvatarActivityHandler.getTrialAvatarDungeonTeam()); + } + } + } + case DUNGEON_ELEMENT_CHALLENGE -> {} // TODO + } + if(this.trialTeam != null) { + player.addTrialAvatarsForActivity(trialTeam.trialAvatarIds); + } + } + + public void unsetTrialTeam(Player player){ + if(this.trialTeam==null){ + return; + } + player.removeTrialAvatarForActivity(); + this.trialTeam = null; + } + + public void startDungeon() { + this.startSceneTime = scene.getSceneTimeSeconds(); + scene.getPlayers().forEach(p -> { + p.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_ENTER_DUNGEON, dungeonData.getId()); + applyTrialTeam(p); + }); + } + + public void finishDungeon() { + notifyEndDungeon(true); + endDungeon(BaseDungeonResult.DungeonEndReason.COMPLETED); + } + + public void notifyEndDungeon(boolean successfully) { + scene.getPlayers().forEach(p -> { + // Quest trigger + p.getQuestManager().queueEvent(successfully ? + QuestContent.QUEST_CONTENT_FINISH_DUNGEON : QuestContent.QUEST_CONTENT_FAIL_DUNGEON, + dungeonData.getId()); + + // Battle pass trigger + if (dungeonData.getType().isCountsToBattlepass() && successfully) { + p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_FINISH_DUNGEON); + } + }); + scene.getScriptManager().callEvent(new ScriptArgs(0, EventType.EVENT_DUNGEON_SETTLE, successfully ? 1 : 0)); + } + + public void quitDungeon() { + notifyEndDungeon(false); + endDungeon(BaseDungeonResult.DungeonEndReason.QUIT); + } + + public void failDungeon() { + notifyEndDungeon(false); + endDungeon(BaseDungeonResult.DungeonEndReason.FAILED); + } + + public void endDungeon(BaseDungeonResult.DungeonEndReason endReason) { + if (scene.getDungeonSettleListeners() != null) { + scene.getDungeonSettleListeners().forEach(o -> o.onDungeonSettle(this, endReason)); + } + ended = true; + } + + public void restartDungeon() { + this.scene.setKilledMonsterCount(0); + this.rewardedPlayers.clear(); + Arrays.fill(finishedConditions, 0); + this.ended = false; + this.activeDungeonWayPoints.clear(); + } + + public void cleanUpScene() { + this.scene.setDungeonManager(null); + this.scene.setKilledMonsterCount(0); + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/DungeonTrialTeam.java b/src/main/java/emu/grasscutter/game/dungeons/DungeonTrialTeam.java new file mode 100644 index 000000000..74f1bea47 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/DungeonTrialTeam.java @@ -0,0 +1,14 @@ +package emu.grasscutter.game.dungeons; + +import emu.grasscutter.net.proto.TrialAvatarGrantRecordOuterClass.TrialAvatarGrantRecord; +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.util.List; + +@Data +@AllArgsConstructor +public class DungeonTrialTeam { + List trialAvatarIds; + TrialAvatarGrantRecord.GrantReason grantReason; +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/DungeonValue.java b/src/main/java/emu/grasscutter/game/dungeons/DungeonValue.java new file mode 100644 index 000000000..d9788cc41 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/DungeonValue.java @@ -0,0 +1,11 @@ +package emu.grasscutter.game.dungeons; + +import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface DungeonValue { + DungeonPassConditionType value(); +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/challenge/enums/ChallengeCondType.java b/src/main/java/emu/grasscutter/game/dungeons/challenge/enums/ChallengeCondType.java new file mode 100644 index 000000000..fa5c38adc --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/challenge/enums/ChallengeCondType.java @@ -0,0 +1,19 @@ +package emu.grasscutter.game.dungeons.challenge.enums; + +public enum ChallengeCondType { + CHALLENGE_COND_NONE, //00 + CHALLENGE_COND_IN_TIME, //01 + CHALLENGE_COND_ALL_TIME, //02 + CHALLENGE_COND_KILL_COUNT, //03 + CHALLENGE_COND_SURVIVE, //04 + CHALLENGE_COND_TIME_INC, //05 + CHALLENGE_COND_KILL_FAST, //06 + CHALLENGE_COND_DOWN_LESS, //07 + CHALLENGE_COND_BEATEN_LESS , //08 + CHALLENGE_COND_UNNATURAL_COUNT , //09 + CHALLENGE_COND_FROZEN_LESS , //10 + CHALLENGE_COND_KILL_MONSTER , //11 + CHALLENGE_COND_TRIGGER , //12 + CHALLENGE_COND_GUARD_HP , //13 + CHALLENGE_COND_TIME_DEC , //14 +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/challenge/enums/ChallengeEventMarkType.java b/src/main/java/emu/grasscutter/game/dungeons/challenge/enums/ChallengeEventMarkType.java new file mode 100644 index 000000000..2674df680 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/challenge/enums/ChallengeEventMarkType.java @@ -0,0 +1,9 @@ +package emu.grasscutter.game.dungeons.challenge.enums; + +public enum ChallengeEventMarkType { + CHALLENGE_EVENT_NONE, + FLIGHT_TIME, + FLIGHT_GATHER_POINT, + SUMMER_TIME_SPRINT_BOAT_TIME, + SUMMER_TIME_SPRINT_BOAT_GATHER_POINT, +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/challenge/enums/ChallengeRecordType.java b/src/main/java/emu/grasscutter/game/dungeons/challenge/enums/ChallengeRecordType.java new file mode 100644 index 000000000..c763903b1 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/challenge/enums/ChallengeRecordType.java @@ -0,0 +1,6 @@ +package emu.grasscutter.game.dungeons.challenge.enums; + +public enum ChallengeRecordType { + CHALLENGE_RECORD_TYPE_NONE, + CHALLENGE_RECORD_TYPE_IN_TIME +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/challenge/enums/ChallengeType.java b/src/main/java/emu/grasscutter/game/dungeons/challenge/enums/ChallengeType.java new file mode 100644 index 000000000..c59dbd240 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/challenge/enums/ChallengeType.java @@ -0,0 +1,27 @@ +package emu.grasscutter.game.dungeons.challenge.enums; + +public enum ChallengeType { + CHALLENGE_NONE, //00 + CHALLENGE_KILL_COUNT, //01 + CHALLENGE_KILL_COUNT_IN_TIME, //02 + CHALLENGE_SURVIVE, //03 + CHALLENGE_TIME_FLY, //04 + CHALLENGE_KILL_COUNT_FAST, //05 + CHALLENGE_KILL_COUNT_FROZEN_LESS, //06 + CHALLENGE_KILL_MONSTER_IN_TIME, //07 + CHALLENGE_TRIGGER_IN_TIME, //08 + CHALLENGE_GUARD_HP, //09 + CHALLENGE_KILL_COUNT_GUARD_HP, //10 + CHALLENGE_TRIGGER_IN_TIME_FLY , //11 + //unknown if position and time match from here on + CHALLENGE_TRIGGER2_AVOID_TRIGGER1, + CHALLENGE_FATHER_SUCC_IN_TIME, + CHALLENGE_MONSTER_DAMAGE_COUNT, + CHALLENGE_ELEMENT_REACTION_COUNT, + CHALLENGE_FREEZE_ENEMY_IN_TIME, + CHALLENGE_CRYSTAL_ELEMENT_REACTION_COUNT, + CHALLENGE_SHEILD_ABSORB_DAMAGE_COUNT, + CHALLENGE_SWIRL_ELEMENT_REACTION_COUNT, + CHALLENGE_DIE_LESS_IN_TIME, + CHALLENGE_TRIGGER_COUNT, +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/challenge/enums/FatherChallengeProperty.java b/src/main/java/emu/grasscutter/game/dungeons/challenge/enums/FatherChallengeProperty.java new file mode 100644 index 000000000..8a4763831 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/challenge/enums/FatherChallengeProperty.java @@ -0,0 +1,9 @@ +package emu.grasscutter.game.dungeons.challenge.enums; + +public enum FatherChallengeProperty { + DURATION, + CUR_SUCC, + CUR_FAIL, + SUM_SUCC, + SUM_FAIL +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/challenge/factory/KillAndGuardChallengeFactoryHandler.java b/src/main/java/emu/grasscutter/game/dungeons/challenge/factory/KillAndGuardChallengeFactoryHandler.java new file mode 100644 index 000000000..69857f42e --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/challenge/factory/KillAndGuardChallengeFactoryHandler.java @@ -0,0 +1,34 @@ +package emu.grasscutter.game.dungeons.challenge.factory; + +import emu.grasscutter.game.dungeons.challenge.WorldChallenge; +import emu.grasscutter.game.dungeons.challenge.enums.ChallengeType; +import emu.grasscutter.game.dungeons.challenge.trigger.GuardTrigger; +import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterCountTrigger; +import emu.grasscutter.game.world.Scene; +import emu.grasscutter.scripts.data.SceneGroup; +import lombok.val; + +import java.util.List; + +import static emu.grasscutter.game.dungeons.challenge.enums.ChallengeType.CHALLENGE_KILL_COUNT_GUARD_HP; + +public class KillAndGuardChallengeFactoryHandler implements ChallengeFactoryHandler{ + @Override + public boolean isThisType(ChallengeType challengeType) { + // ActiveChallenge with 1,188,234101003,12,3030,0 + return challengeType == CHALLENGE_KILL_COUNT_GUARD_HP; + } + + @Override /*TODO check param4 == monstesToKill*/ + public WorldChallenge build(int challengeIndex, int challengeId, int groupId, int monstersToKill, int gadgetCFGId, int unused, Scene scene, SceneGroup group) { + val realGroup = scene.getScriptManager().getGroupById(groupId); + return new WorldChallenge( + scene, realGroup, + challengeId, // Id + challengeIndex, // Index + List.of(monstersToKill, 0), + 0, // Limit + monstersToKill, // Goal + List.of(new KillMonsterCountTrigger(), new GuardTrigger(gadgetCFGId))); + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/challenge/factory/KillMonsterCountChallengeFactoryHandler.java b/src/main/java/emu/grasscutter/game/dungeons/challenge/factory/KillMonsterCountChallengeFactoryHandler.java new file mode 100644 index 000000000..0866a38d7 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/challenge/factory/KillMonsterCountChallengeFactoryHandler.java @@ -0,0 +1,32 @@ +package emu.grasscutter.game.dungeons.challenge.factory; + +import emu.grasscutter.game.dungeons.challenge.WorldChallenge; +import emu.grasscutter.game.dungeons.challenge.enums.ChallengeType; +import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterCountTrigger; +import emu.grasscutter.game.world.Scene; +import emu.grasscutter.scripts.data.SceneGroup; +import lombok.val; + +import java.util.List; + +public class KillMonsterCountChallengeFactoryHandler implements ChallengeFactoryHandler{ + @Override + public boolean isThisType(ChallengeType challengeType) { + // ActiveChallenge with 1, 1, 241033003, 15, 0, 0 + return challengeType == ChallengeType.CHALLENGE_KILL_COUNT; + } + + @Override + public WorldChallenge build(int challengeIndex, int challengeId, int groupId, int goal, int param5, int param6, Scene scene, SceneGroup group) { + val realGroup = scene.getScriptManager().getGroupById(groupId); + return new WorldChallenge( + scene, realGroup, + challengeId, // Id + challengeIndex, // Index + List.of(goal, groupId), + 0, // Limit + goal, // Goal + List.of(new KillMonsterCountTrigger()) + ); + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/challenge/factory/KillMonsterInTimeChallengeFactoryHandler.java b/src/main/java/emu/grasscutter/game/dungeons/challenge/factory/KillMonsterInTimeChallengeFactoryHandler.java new file mode 100644 index 000000000..38b617ebb --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/challenge/factory/KillMonsterInTimeChallengeFactoryHandler.java @@ -0,0 +1,33 @@ +package emu.grasscutter.game.dungeons.challenge.factory; + +import emu.grasscutter.game.dungeons.challenge.WorldChallenge; +import emu.grasscutter.game.dungeons.challenge.enums.ChallengeType; +import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger; +import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterTrigger; +import emu.grasscutter.game.world.Scene; +import emu.grasscutter.scripts.data.SceneGroup; +import lombok.val; + +import java.util.List; + +public class KillMonsterInTimeChallengeFactoryHandler implements ChallengeFactoryHandler{ + @Override + public boolean isThisType(ChallengeType challengeType) { + // ActiveChallenge with 180, 72, 240, 133220161, 133220161, 0 + return challengeType == ChallengeType.CHALLENGE_KILL_MONSTER_IN_TIME; + } + + @Override + public WorldChallenge build(int challengeIndex, int challengeId, int timeLimit, int groupId, int targetCfgId, int param6, Scene scene, SceneGroup group) { + val realGroup = scene.getScriptManager().getGroupById(groupId); + return new WorldChallenge( + scene, realGroup, + challengeId, // Id + challengeIndex, // Index + List.of(timeLimit), + timeLimit, // Limit + 0, // Goal + List.of(new KillMonsterTrigger(targetCfgId), new InTimeTrigger()) + ); + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/challenge/factory/KillMonsterTimeChallengeFactoryHandler.java b/src/main/java/emu/grasscutter/game/dungeons/challenge/factory/KillMonsterTimeChallengeFactoryHandler.java new file mode 100644 index 000000000..8ca87518b --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/challenge/factory/KillMonsterTimeChallengeFactoryHandler.java @@ -0,0 +1,35 @@ +package emu.grasscutter.game.dungeons.challenge.factory; + +import emu.grasscutter.game.dungeons.challenge.WorldChallenge; +import emu.grasscutter.game.dungeons.challenge.enums.ChallengeType; +import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger; +import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterCountTrigger; +import emu.grasscutter.game.world.Scene; +import emu.grasscutter.scripts.data.SceneGroup; +import lombok.val; + +import java.util.List; + +public class KillMonsterTimeChallengeFactoryHandler implements ChallengeFactoryHandler{ + @Override + public boolean isThisType(ChallengeType challengeType) { + // ActiveChallenge with 180,180,45,133108061,1,0 + // ActiveChallenge Fast with 1001, 5, 15, 240004005, 10, 0 + return challengeType == ChallengeType.CHALLENGE_KILL_COUNT_IN_TIME || + challengeType == ChallengeType.CHALLENGE_KILL_COUNT_FAST; + } + + @Override + public WorldChallenge build(int challengeIndex, int challengeId, int timeLimit, int groupId, int targetCount, int param6, Scene scene, SceneGroup group) { + val realGroup = scene.getScriptManager().getGroupById(groupId); + return new WorldChallenge( + scene, realGroup, + challengeId, // Id + challengeIndex, // Index + List.of(targetCount, timeLimit), + timeLimit, // Limit + targetCount, // Goal + List.of(new KillMonsterCountTrigger(), new InTimeTrigger()) + ); + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/challenge/factory/SurviveChallengeFactoryHandler.java b/src/main/java/emu/grasscutter/game/dungeons/challenge/factory/SurviveChallengeFactoryHandler.java new file mode 100644 index 000000000..5a1934ff5 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/challenge/factory/SurviveChallengeFactoryHandler.java @@ -0,0 +1,33 @@ +package emu.grasscutter.game.dungeons.challenge.factory; + +import emu.grasscutter.game.dungeons.challenge.WorldChallenge; +import emu.grasscutter.game.dungeons.challenge.enums.ChallengeType; +import emu.grasscutter.game.dungeons.challenge.trigger.ForTimeTrigger; +import emu.grasscutter.game.world.Scene; +import emu.grasscutter.scripts.data.SceneGroup; + +import java.util.List; + +import static emu.grasscutter.game.dungeons.challenge.enums.ChallengeType.CHALLENGE_SURVIVE; + +public class SurviveChallengeFactoryHandler implements ChallengeFactoryHandler { + @Override + public boolean isThisType(ChallengeType challengeType) { + // grp 201055005 + // ActiveChallenge with 100, 56, 60, 0, 0, 0 + return challengeType == CHALLENGE_SURVIVE; + } + + @Override + public WorldChallenge build(int challengeIndex, int challengeId, int timeToSurvive, int unused4, int unused5, int unused6, Scene scene, SceneGroup group) { + return new WorldChallenge( + scene, group, + challengeId, // Id + challengeIndex, // Index + List.of(timeToSurvive), + timeToSurvive, // Limit + 0, // Goal + List.of(new ForTimeTrigger()) + ); + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/challenge/factory/TriggerInTimeChallengeFactoryHandler.java b/src/main/java/emu/grasscutter/game/dungeons/challenge/factory/TriggerInTimeChallengeFactoryHandler.java new file mode 100644 index 000000000..15aeccbec --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/challenge/factory/TriggerInTimeChallengeFactoryHandler.java @@ -0,0 +1,36 @@ +package emu.grasscutter.game.dungeons.challenge.factory; + +import emu.grasscutter.game.dungeons.challenge.WorldChallenge; +import emu.grasscutter.game.dungeons.challenge.enums.ChallengeType; +import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger; +import emu.grasscutter.game.dungeons.challenge.trigger.TriggerGroupTriggerTrigger; +import emu.grasscutter.game.world.Scene; +import emu.grasscutter.scripts.data.SceneGroup; + +import java.util.List; + +import static emu.grasscutter.game.dungeons.challenge.enums.ChallengeType.CHALLENGE_TRIGGER_IN_TIME; + +public class TriggerInTimeChallengeFactoryHandler implements ChallengeFactoryHandler { + @Override + public boolean isThisType(ChallengeType challengeType) { + // kill gadgets(explosive barrel) in time + // ActiveChallenge with 56,201,20,2,201,4 + // open chest in time + // ActiveChallenge with 666,202,30,7,202,1 + return challengeType == CHALLENGE_TRIGGER_IN_TIME; + } + + @Override + public WorldChallenge build(int challengeIndex, int challengeId, int timeLimit, int param4, int triggerTag, int triggerCount, Scene scene, SceneGroup group) { + return new WorldChallenge( + scene, group, + challengeId, // Id + challengeIndex, // Index + List.of(timeLimit, triggerCount), + timeLimit, // Limit + triggerCount, // Goal + List.of(new InTimeTrigger(), new TriggerGroupTriggerTrigger(Integer.toString(triggerTag))) + ); + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/challenge/trigger/ForTimeTrigger.java b/src/main/java/emu/grasscutter/game/dungeons/challenge/trigger/ForTimeTrigger.java new file mode 100644 index 000000000..6b272c34b --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/challenge/trigger/ForTimeTrigger.java @@ -0,0 +1,13 @@ +package emu.grasscutter.game.dungeons.challenge.trigger; + +import emu.grasscutter.game.dungeons.challenge.WorldChallenge; + +public class ForTimeTrigger extends ChallengeTrigger{ + @Override + public void onCheckTimeout(WorldChallenge challenge) { + var current = challenge.getScene().getSceneTimeSeconds(); + if(current - challenge.getStartedAt() > challenge.getTimeLimit()){ + challenge.done(); + } + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/challenge/trigger/KillMonsterCountTrigger.java b/src/main/java/emu/grasscutter/game/dungeons/challenge/trigger/KillMonsterCountTrigger.java new file mode 100644 index 000000000..cc1b02cf0 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/challenge/trigger/KillMonsterCountTrigger.java @@ -0,0 +1,23 @@ +package emu.grasscutter.game.dungeons.challenge.trigger; + +import emu.grasscutter.game.dungeons.challenge.WorldChallenge; +import emu.grasscutter.game.entity.EntityMonster; +import emu.grasscutter.server.packet.send.PacketChallengeDataNotify; + +public class KillMonsterCountTrigger extends ChallengeTrigger{ + @Override + public void onBegin(WorldChallenge challenge) { + challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 1, challenge.getScore().get())); + } + + @Override + public void onMonsterDeath(WorldChallenge challenge, EntityMonster monster) { + var newScore = challenge.increaseScore(); + challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 1, newScore)); + + if(newScore >= challenge.getGoal()){ + challenge.done(); + } + + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/challenge/trigger/TriggerGroupTriggerTrigger.java b/src/main/java/emu/grasscutter/game/dungeons/challenge/trigger/TriggerGroupTriggerTrigger.java new file mode 100644 index 000000000..82aae8140 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/challenge/trigger/TriggerGroupTriggerTrigger.java @@ -0,0 +1,30 @@ +package emu.grasscutter.game.dungeons.challenge.trigger; + +import emu.grasscutter.game.dungeons.challenge.WorldChallenge; +import emu.grasscutter.game.entity.EntityGadget; +import emu.grasscutter.scripts.data.SceneTrigger; +import emu.grasscutter.server.packet.send.PacketChallengeDataNotify; +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public class TriggerGroupTriggerTrigger extends ChallengeTrigger{ + String triggerTag; + + @Override + public void onBegin(WorldChallenge challenge) { + challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 2, challenge.getScore().get())); + } + + @Override + public void onGroupTrigger(WorldChallenge challenge, SceneTrigger trigger) { + if(!triggerTag.equals(trigger.getTag())) { + return; + } + + var newScore = challenge.increaseScore(); + challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 2, newScore)); + if(newScore >= challenge.getGoal()){ + challenge.done(); + } + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/dungeon_results/BaseDungeonResult.java b/src/main/java/emu/grasscutter/game/dungeons/dungeon_results/BaseDungeonResult.java new file mode 100644 index 000000000..9cf616159 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/dungeon_results/BaseDungeonResult.java @@ -0,0 +1,74 @@ +package emu.grasscutter.game.dungeons.dungeon_results; + +import emu.grasscutter.data.excels.DungeonData; +import emu.grasscutter.game.dungeons.DungeonEndStats; +import emu.grasscutter.net.proto.DungeonSettleNotifyOuterClass.DungeonSettleNotify; +import emu.grasscutter.net.proto.ParamListOuterClass; +import emu.grasscutter.utils.Utils; +import lombok.Getter; + +public class BaseDungeonResult { + @Getter DungeonData dungeonData; + @Getter + DungeonEndStats dungeonStats; + + public BaseDungeonResult(DungeonData dungeonData, DungeonEndStats dungeonStats){ + this.dungeonData = dungeonData; + this.dungeonStats = dungeonStats; + } + + protected void onProto(DungeonSettleNotify.Builder builder){ } + + public final DungeonSettleNotify.Builder getProto(){ + var success = dungeonStats.getDungeonResult().isSuccess(); + var builder = DungeonSettleNotify.newBuilder() + .setDungeonId(dungeonData.getId()) + .setIsSuccess(success) + .setCloseTime(getCloseTime()) + .setResult(success ? 1 : 0); + + // TODO check + if(dungeonData.getSettleShows()!=null) { + for (int i = 0; i < dungeonData.getSettleShows().size(); i++) { + var settle = dungeonData.getSettleShows().get(i); + builder.putSettleShow(i + 1,switch (settle) { + case SETTLE_SHOW_TIME_COST -> ParamListOuterClass.ParamList.newBuilder() + .addParamList(settle.getId()) + .addParamList(dungeonStats.getTimeTaken()) + .build(); + case SETTLE_SHOW_KILL_MONSTER_COUNT -> ParamListOuterClass.ParamList.newBuilder() + .addParamList(settle.getId()) + .addParamList(dungeonStats.getKilledMonsters()) + .build(); + default -> ParamListOuterClass.ParamList.newBuilder() + .addParamList(settle.getId()) + .build(); + }); + } + } + + //TODO handle settle show + + onProto(builder); + + return builder; + } + + public int getCloseTime(){ + return Utils.getCurrentSeconds() + switch (dungeonStats.getDungeonResult()){ + case COMPLETED -> dungeonData.getSettleCountdownTime(); + case FAILED -> dungeonData.getFailSettleCountdownTime(); + case QUIT -> dungeonData.getQuitSettleCountdownTime(); + }; + } + + public enum DungeonEndReason{ + COMPLETED, + FAILED, + QUIT; + + public boolean isSuccess(){ + return this == COMPLETED; + } + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/dungeon_results/TowerResult.java b/src/main/java/emu/grasscutter/game/dungeons/dungeon_results/TowerResult.java new file mode 100644 index 000000000..46543a9bd --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/dungeon_results/TowerResult.java @@ -0,0 +1,50 @@ +package emu.grasscutter.game.dungeons.dungeon_results; + +import emu.grasscutter.data.excels.DungeonData; +import emu.grasscutter.game.dungeons.DungeonEndStats; +import emu.grasscutter.game.dungeons.challenge.WorldChallenge; +import emu.grasscutter.game.tower.TowerManager; +import emu.grasscutter.net.proto.DungeonSettleExhibitionInfoOuterClass; +import emu.grasscutter.net.proto.DungeonSettleNotifyOuterClass; +import emu.grasscutter.net.proto.ItemParamOuterClass; +import emu.grasscutter.net.proto.TowerLevelEndNotifyOuterClass.TowerLevelEndNotify.ContinueStateType; +import emu.grasscutter.net.proto.TowerLevelEndNotifyOuterClass.TowerLevelEndNotify; + +public class TowerResult extends BaseDungeonResult{ + WorldChallenge challenge; + boolean canJump; + boolean hasNextLevel; + int nextFloorId; + public TowerResult(DungeonData dungeonData, DungeonEndStats dungeonStats, TowerManager towerManager, WorldChallenge challenge) { + super(dungeonData, dungeonStats); + this.challenge = challenge; + this.canJump = towerManager.hasNextFloor(); + this.hasNextLevel = towerManager.hasNextLevel(); + this.nextFloorId = hasNextLevel ? 0 : towerManager.getNextFloorId(); + } + + @Override + protected void onProto(DungeonSettleNotifyOuterClass.DungeonSettleNotify.Builder builder) { + var continueStatus = ContinueStateType.CONTINUE_STATE_TYPE_CAN_NOT_CONTINUE_VALUE; + if(challenge.isSuccess() && canJump){ + continueStatus = hasNextLevel ? ContinueStateType.CONTINUE_STATE_TYPE_CAN_ENTER_NEXT_LEVEL_VALUE + : ContinueStateType.CONTINUE_STATE_TYPE_CAN_ENTER_NEXT_FLOOR_VALUE; + } + + var towerLevelEndNotify = TowerLevelEndNotify.newBuilder() + .setIsSuccess(challenge.isSuccess()) + .setContinueState(continueStatus) + .addFinishedStarCondList(1) + .addFinishedStarCondList(2) + .addFinishedStarCondList(3) + .addRewardItemList(ItemParamOuterClass.ItemParam.newBuilder() + .setItemId(201) + .setCount(1000) + .build()) + ; + if(nextFloorId > 0 && canJump){ + towerLevelEndNotify.setNextFloorId(nextFloorId); + } + builder.setTowerLevelEndNotify(towerLevelEndNotify); + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/dungeon_results/TrialAvatarDungeonResult.java b/src/main/java/emu/grasscutter/game/dungeons/dungeon_results/TrialAvatarDungeonResult.java new file mode 100644 index 000000000..298925a8f --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/dungeon_results/TrialAvatarDungeonResult.java @@ -0,0 +1,23 @@ +package emu.grasscutter.game.dungeons.dungeon_results; + +import emu.grasscutter.data.excels.DungeonData; +import emu.grasscutter.game.dungeons.DungeonEndStats; +import emu.grasscutter.net.proto.DungeonSettleNotifyOuterClass; +import emu.grasscutter.net.proto.TrialAvatarFirstPassDungeonNotifyOuterClass.TrialAvatarFirstPassDungeonNotify; + +public class TrialAvatarDungeonResult extends BaseDungeonResult { + int trialCharacterIndexId; + + public TrialAvatarDungeonResult(DungeonData dungeonData, DungeonEndStats dungeonStats, int trialCharacterIndexId) { + super(dungeonData, dungeonStats); + this.trialCharacterIndexId = trialCharacterIndexId; + } + + @Override + protected void onProto(DungeonSettleNotifyOuterClass.DungeonSettleNotify.Builder builder) { + if (dungeonStats.getDungeonResult() == DungeonEndReason.COMPLETED) { //TODO check if its the first pass(?) + builder.setTrialAvatarFirstPassDungeonNotify(TrialAvatarFirstPassDungeonNotify.newBuilder() + .setTrialAvatarIndexId(trialCharacterIndexId)); + } + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/enums/DungeonEntrySatisfiedConditionType.java b/src/main/java/emu/grasscutter/game/dungeons/enums/DungeonEntrySatisfiedConditionType.java new file mode 100644 index 000000000..fce7da285 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/enums/DungeonEntrySatisfiedConditionType.java @@ -0,0 +1,7 @@ +package emu.grasscutter.game.dungeons.enums; + +public enum DungeonEntrySatisfiedConditionType { + DUNGEON_ENTRY_CONDITION_NONE, + DUNGEON_ENTRY_CONDITION_LEVEL, + DUNGEON_ENTRY_CONDITION_QUEST +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/enums/DungeonInvolveType.java b/src/main/java/emu/grasscutter/game/dungeons/enums/DungeonInvolveType.java new file mode 100644 index 000000000..2e32e3496 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/enums/DungeonInvolveType.java @@ -0,0 +1,7 @@ +package emu.grasscutter.game.dungeons.enums; + +public enum DungeonInvolveType { + INVOLVE_NONE, + INVOLVE_ONLY_SINGLE, + INVOLVE_SINGLE_MULTIPLE +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/enums/DungeonPassConditionType.java b/src/main/java/emu/grasscutter/game/dungeons/enums/DungeonPassConditionType.java new file mode 100644 index 000000000..33bac9fd3 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/enums/DungeonPassConditionType.java @@ -0,0 +1,27 @@ +package emu.grasscutter.game.dungeons.enums; + +import emu.grasscutter.scripts.constants.IntValueEnum; +import lombok.Getter; + +public enum DungeonPassConditionType implements IntValueEnum { + DUNGEON_COND_NONE(0), + DUNGEON_COND_KILL_MONSTER(3), + DUNGEON_COND_KILL_GROUP_MONSTER(5), + DUNGEON_COND_KILL_TYPE_MONSTER(7), + DUNGEON_COND_FINISH_QUEST(9), + DUNGEON_COND_KILL_MONSTER_COUNT(11), // TODO handle count + DUNGEON_COND_IN_TIME(13), // Missing triggers and tracking + DUNGEON_COND_FINISH_CHALLENGE(14), + DUNGEON_COND_END_MULTISTAGE_PLAY(15) // Missing + ; + + @Getter private final int id; + DungeonPassConditionType(int id){ + this.id = id; + } + + @Override + public int getValue() { + return id; + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/enums/DungeonPlayType.java b/src/main/java/emu/grasscutter/game/dungeons/enums/DungeonPlayType.java new file mode 100644 index 000000000..e2d287894 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/enums/DungeonPlayType.java @@ -0,0 +1,7 @@ +package emu.grasscutter.game.dungeons.enums; +public enum DungeonPlayType { + DUNGEON_PLAY_TYPE_NONE, + DUNGEON_PLAY_TYPE_FOGGY_MAZE, + DUNGEON_PLAY_TYPE_MIST_TRIAL, + DUNGEON_PLAY_TYPE_TRIAL_AVATAR +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/enums/DungeonSubType.java b/src/main/java/emu/grasscutter/game/dungeons/enums/DungeonSubType.java new file mode 100644 index 000000000..111b9e2f3 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/enums/DungeonSubType.java @@ -0,0 +1,9 @@ +package emu.grasscutter.game.dungeons.enums; + +public enum DungeonSubType { + DUNGEON_SUB_NONE, + DUNGEON_SUB_BOSS, + DUNGEON_SUB_TALENT, + DUNGEON_SUB_WEAPON, + DUNGEON_SUB_RELIQUARY +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/enums/DungeonType.java b/src/main/java/emu/grasscutter/game/dungeons/enums/DungeonType.java new file mode 100644 index 000000000..0864c2dce --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/enums/DungeonType.java @@ -0,0 +1,49 @@ +package emu.grasscutter.game.dungeons.enums; + +import lombok.Getter; + +public enum DungeonType { + DUNGEON_NONE(false), + DUNGEON_PLOT(true), + DUNGEON_FIGHT(true), + DUNGEON_DAILY_FIGHT(false), + DUNGEON_WEEKLY_FIGHT(true), + DUNGEON_DISCARDED(false), + DUNGEON_TOWER(false), + DUNGEON_BOSS(true), + DUNGEON_ACTIVITY(false), + DUNGEON_EFFIGY(false), + DUNGEON_ELEMENT_CHALLENGE(true), + DUNGEON_THEATRE_MECHANICUS(false), + DUNGEON_FLEUR_FAIR(false), + DUNGEON_CHANNELLER_SLAB_LOOP(false), + DUNGEON_CHANNELLER_SLAB_ONE_OFF(false), + DUNGEON_BLITZ_RUSH(true), + DUNGEON_CHESS(false), + DUNGEON_SUMO_COMBAT(false), + DUNGEON_ROGUELIKE(false), + DUNGEON_HACHI(false), + DUNGEON_POTION(false), + DUNGEON_MINI_ELDRITCH(false), + DUNGEON_UGC(false), + DUNGEON_GCG(false), + DUNGEON_CRYSTAL_LINK(false), + DUNGEON_IRODORI_CHESS(false), + DUNGEON_ROGUE_DIARY(false), + DUNGEON_DREAMLAND(false), + DUNGEON_SUMMER_V2(true), + DUNGEON_MUQADAS_POTION(false), + DUNGEON_INSTABLE_SPRAY(false), + DUNGEON_WIND_FIELD(false), + DUNGEON_BIGWORLD_MIRROR(false), + DUNGEON_FUNGUS_FIGHTER_TRAINING(false), + DUNGEON_FUNGUS_FIGHTER_PLOT(false), + DUNGEON_EFFIGY_CHALLENGE_V2(false), + DUNGEON_CHAR_AMUSEMENT(false); + + @Getter private final boolean countsToBattlepass; + + DungeonType(boolean countsToBattlepass){ + this.countsToBattlepass = countsToBattlepass; + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/enums/DungunEntryType.java b/src/main/java/emu/grasscutter/game/dungeons/enums/DungunEntryType.java new file mode 100644 index 000000000..5b7295c58 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/enums/DungunEntryType.java @@ -0,0 +1,12 @@ +package emu.grasscutter.game.dungeons.enums; + +public enum DungunEntryType { + DUNGEN_ENTRY_TYPE_NONE , + DUNGEN_ENTRY_TYPE_AVATAR_EXP , + DUNGEN_ENTRY_TYPE_WEAPON_PROMOTE, + DUNGEN_ENTRY_TYPE_AVATAR_TALENT , + DUNGEN_ENTRY_TYPE_RELIQUARY , + DUNGEN_ENTRY_TYPE_SCOIN , + DUNGEON_ENTRY_TYPE_OBSCURAE , + DUNGEON_ENTRY_TYPE_NORMAL +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/enums/SettleShowType.java b/src/main/java/emu/grasscutter/game/dungeons/enums/SettleShowType.java new file mode 100644 index 000000000..3d5af54a8 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/enums/SettleShowType.java @@ -0,0 +1,17 @@ +package emu.grasscutter.game.dungeons.enums; + +import lombok.Getter; + +public enum SettleShowType { + SETTLE_SHOW_NONE(0), + SETTLE_SHOW_TIME_COST(1), + SETTLE_SHOW_OPEN_CHEST_COUNT(2), + SETTLE_SHOW_KILL_MONSTER_COUNT(3), + SETTLE_SHOW_BLACKSCREEN(4); + + @Getter private final int id; + + SettleShowType(int id){ + this.id = id; + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/handlers/DungeonBaseHandler.java b/src/main/java/emu/grasscutter/game/dungeons/handlers/DungeonBaseHandler.java new file mode 100644 index 000000000..7c02b6199 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/handlers/DungeonBaseHandler.java @@ -0,0 +1,9 @@ +package emu.grasscutter.game.dungeons.handlers; + +import emu.grasscutter.data.excels.DungeonPassConfigData; + +public abstract class DungeonBaseHandler { + + public abstract boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params); + +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/pass_condition/BaseCondition.java b/src/main/java/emu/grasscutter/game/dungeons/pass_condition/BaseCondition.java new file mode 100644 index 000000000..edc795d41 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/pass_condition/BaseCondition.java @@ -0,0 +1,17 @@ +package emu.grasscutter.game.dungeons.pass_condition; + +import emu.grasscutter.data.excels.DungeonPassConfigData; +import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType; +import emu.grasscutter.game.dungeons.DungeonValue; +import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler; + +@DungeonValue(DungeonPassConditionType.DUNGEON_COND_NONE) +public class BaseCondition extends DungeonBaseHandler { + + @Override + public boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params) { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/pass_condition/ConditionFinishChallenge.java b/src/main/java/emu/grasscutter/game/dungeons/pass_condition/ConditionFinishChallenge.java new file mode 100644 index 000000000..40dd40e29 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/pass_condition/ConditionFinishChallenge.java @@ -0,0 +1,15 @@ +package emu.grasscutter.game.dungeons.pass_condition; + +import emu.grasscutter.data.excels.DungeonPassConfigData; +import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType; +import emu.grasscutter.game.dungeons.DungeonValue; +import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler; + +@DungeonValue(DungeonPassConditionType.DUNGEON_COND_FINISH_CHALLENGE) +public class ConditionFinishChallenge extends DungeonBaseHandler { + + @Override + public boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params) { + return params[0] == condition.getParam()[0] || params[1] == condition.getParam()[0]; + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/pass_condition/ConditionFinishQuest.java b/src/main/java/emu/grasscutter/game/dungeons/pass_condition/ConditionFinishQuest.java new file mode 100644 index 000000000..0e18dddeb --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/pass_condition/ConditionFinishQuest.java @@ -0,0 +1,15 @@ +package emu.grasscutter.game.dungeons.pass_condition; + +import emu.grasscutter.data.excels.DungeonPassConfigData; +import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType; +import emu.grasscutter.game.dungeons.DungeonValue; +import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler; + +@DungeonValue(DungeonPassConditionType.DUNGEON_COND_FINISH_QUEST) +public class ConditionFinishQuest extends DungeonBaseHandler { + + @Override + public boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params) { + return params[0] == condition.getParam()[0]; + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/pass_condition/ConditionInTime.java b/src/main/java/emu/grasscutter/game/dungeons/pass_condition/ConditionInTime.java new file mode 100644 index 000000000..b387cde26 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/pass_condition/ConditionInTime.java @@ -0,0 +1,15 @@ +package emu.grasscutter.game.dungeons.pass_condition; + +import emu.grasscutter.data.excels.DungeonPassConfigData; +import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType; +import emu.grasscutter.game.dungeons.DungeonValue; +import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler; + +@DungeonValue(DungeonPassConditionType.DUNGEON_COND_IN_TIME) +public class ConditionInTime extends DungeonBaseHandler { + + @Override + public boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params) { + return params[0] <= condition.getParam()[0]; + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/pass_condition/ConditionKillGroupMonster.java b/src/main/java/emu/grasscutter/game/dungeons/pass_condition/ConditionKillGroupMonster.java new file mode 100644 index 000000000..a72eef9da --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/pass_condition/ConditionKillGroupMonster.java @@ -0,0 +1,15 @@ +package emu.grasscutter.game.dungeons.pass_condition; + +import emu.grasscutter.data.excels.DungeonPassConfigData; +import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType; +import emu.grasscutter.game.dungeons.DungeonValue; +import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler; + +@DungeonValue(DungeonPassConditionType.DUNGEON_COND_KILL_GROUP_MONSTER) +public class ConditionKillGroupMonster extends DungeonBaseHandler { + + @Override + public boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params) { + return params[0] == condition.getParam()[0]; + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/pass_condition/ConditionKillMonster.java b/src/main/java/emu/grasscutter/game/dungeons/pass_condition/ConditionKillMonster.java new file mode 100644 index 000000000..8faea3740 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/pass_condition/ConditionKillMonster.java @@ -0,0 +1,15 @@ +package emu.grasscutter.game.dungeons.pass_condition; + +import emu.grasscutter.data.excels.DungeonPassConfigData; +import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType; +import emu.grasscutter.game.dungeons.DungeonValue; +import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler; + +@DungeonValue(DungeonPassConditionType.DUNGEON_COND_KILL_MONSTER) +public class ConditionKillMonster extends DungeonBaseHandler { + + @Override + public boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params) { + return params[0] == condition.getParam()[0]; + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/pass_condition/ConditionKillMonsterCount.java b/src/main/java/emu/grasscutter/game/dungeons/pass_condition/ConditionKillMonsterCount.java new file mode 100644 index 000000000..e12342a52 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/pass_condition/ConditionKillMonsterCount.java @@ -0,0 +1,15 @@ +package emu.grasscutter.game.dungeons.pass_condition; + +import emu.grasscutter.data.excels.DungeonPassConfigData; +import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType; +import emu.grasscutter.game.dungeons.DungeonValue; +import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler; + +@DungeonValue(DungeonPassConditionType.DUNGEON_COND_KILL_MONSTER_COUNT) +public class ConditionKillMonsterCount extends DungeonBaseHandler { + + @Override + public boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params) { + return params[0] >= condition.getParam()[0]; + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/pass_condition/ConditionKillTypeMonster.java b/src/main/java/emu/grasscutter/game/dungeons/pass_condition/ConditionKillTypeMonster.java new file mode 100644 index 000000000..c5a834528 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/pass_condition/ConditionKillTypeMonster.java @@ -0,0 +1,15 @@ +package emu.grasscutter.game.dungeons.pass_condition; + +import emu.grasscutter.data.excels.DungeonPassConfigData; +import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType; +import emu.grasscutter.game.dungeons.DungeonValue; +import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler; + +@DungeonValue(DungeonPassConditionType.DUNGEON_COND_KILL_TYPE_MONSTER) +public class ConditionKillTypeMonster extends DungeonBaseHandler { + + @Override + public boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params) { + return params[0] == condition.getParam()[0]; + } +} diff --git a/src/main/java/emu/grasscutter/game/entity/gadget/GadgetAbility.java b/src/main/java/emu/grasscutter/game/entity/gadget/GadgetAbility.java new file mode 100644 index 000000000..30f03e574 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/entity/gadget/GadgetAbility.java @@ -0,0 +1,39 @@ +package emu.grasscutter.game.entity.gadget; + +import java.util.Arrays; + +import emu.grasscutter.game.entity.EntityClientGadget; +import emu.grasscutter.game.entity.EntityGadget; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.net.proto.AbilityGadgetInfoOuterClass; +import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq; +import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo; +import lombok.val; + +public class GadgetAbility extends GadgetContent { + private EntityClientGadget parent; + + public GadgetAbility(EntityGadget gadget, EntityClientGadget parent) { + super(gadget); + this.parent = parent; + } + + public boolean onInteract(Player player, GadgetInteractReq req) { + return false; + } + + public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) { + if (this.parent == null) { + return; + } + + val abilityGadgetInfo = AbilityGadgetInfoOuterClass.AbilityGadgetInfo.newBuilder() + .setCampId(parent.getCampId()) + .setCampTargetType(parent.getCampType()) + .setTargetEntityId(parent.getId()) + .build(); + + gadgetInfo.setAbilityGadget(abilityGadgetInfo); + } + +} diff --git a/src/main/java/emu/grasscutter/game/entity/gadget/platform/AbilityRoute.java b/src/main/java/emu/grasscutter/game/entity/gadget/platform/AbilityRoute.java new file mode 100644 index 000000000..44f07cb0b --- /dev/null +++ b/src/main/java/emu/grasscutter/game/entity/gadget/platform/AbilityRoute.java @@ -0,0 +1,28 @@ +package emu.grasscutter.game.entity.gadget.platform; + +import emu.grasscutter.net.proto.MathQuaternionOuterClass.MathQuaternion; +import emu.grasscutter.net.proto.MovingPlatformTypeOuterClass; +import emu.grasscutter.net.proto.PlatformInfoOuterClass; +import emu.grasscutter.utils.Position; + +/** + * TODO mostly hardcoded for EntitySolarIsotomaElevatorPlatform, should be more generic + */ +public class AbilityRoute extends BaseRoute { + + private final Position basePosition; + + public AbilityRoute(Position startRot, boolean startRoute, boolean isActive, Position basePosition) { + super(startRot, startRoute, isActive); + this.basePosition = basePosition; + } + + @Override + public PlatformInfoOuterClass.PlatformInfo.Builder toProto() { + return super.toProto() + .setStartRot(MathQuaternion.newBuilder().setW(1.0F)) + .setPosOffset(basePosition.toProto()) + .setRotOffset(MathQuaternion.newBuilder().setW(1.0F)) + .setMovingPlatformType(MovingPlatformTypeOuterClass.MovingPlatformType.MOVING_PLATFORM_TYPE_ABILITY); + } +} diff --git a/src/main/java/emu/grasscutter/game/entity/gadget/platform/BaseRoute.java b/src/main/java/emu/grasscutter/game/entity/gadget/platform/BaseRoute.java new file mode 100644 index 000000000..6ea1ddd58 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/entity/gadget/platform/BaseRoute.java @@ -0,0 +1,84 @@ +package emu.grasscutter.game.entity.gadget.platform; + +import emu.grasscutter.game.world.Scene; +import emu.grasscutter.net.proto.MathQuaternionOuterClass.MathQuaternion; +import emu.grasscutter.net.proto.PlatformInfoOuterClass.PlatformInfo; +import emu.grasscutter.scripts.data.SceneGadget; +import emu.grasscutter.utils.Position; +import lombok.Getter; +import lombok.Setter; +import lombok.val; + +public abstract class BaseRoute { + @Getter @Setter private boolean isStarted; + @Getter @Setter private boolean isActive; + @Getter @Setter private Position startRot; + @Getter @Setter private int startSceneTime; + @Getter @Setter private int stopSceneTime; + + BaseRoute(Position startRot, boolean isStarted, boolean isActive) { + this.startRot = startRot; + this.isStarted = isStarted; + this.isActive = isActive; + } + + BaseRoute(SceneGadget gadget) { + this.startRot = gadget.rot; + this.isStarted = gadget.start_route; + this.isActive = gadget.start_route; + } + + public static BaseRoute fromSceneGadget(SceneGadget sceneGadget) { + if (sceneGadget.route_id != 0) { + return new ConfigRoute(sceneGadget); + } else if (sceneGadget.is_use_point_array) { + return new PointArrayRoute(sceneGadget); + } + return null; + } + + public boolean startRoute(Scene scene) { + if (this.isStarted) { + return false; + } + this.isStarted = true; + this.isActive = true; + this.startSceneTime = scene.getSceneTime()+300; + + return true; + } + + public boolean stopRoute(Scene scene) { + if (!this.isStarted) { + return false; + } + this.isStarted = false; + this.isActive = false; + this.startSceneTime = scene.getSceneTime(); + this.stopSceneTime = scene.getSceneTime(); + + return true; + } + + private MathQuaternion.Builder rotAsMathQuaternion() { + val result = MathQuaternion.newBuilder(); + if (startRot != null) { + result.setX(startRot.getX()) + .setY(startRot.getY()) + .setZ(startRot.getZ()); + } + return result; + } + + public PlatformInfo.Builder toProto() { + val result = PlatformInfo.newBuilder() + .setIsStarted(isStarted) + .setIsActive(isActive) + .setStartRot(rotAsMathQuaternion()) + .setStartSceneTime(startSceneTime); + if (!isStarted) { + result.setStopSceneTime(stopSceneTime); + } + return result; + } +} diff --git a/src/main/java/emu/grasscutter/game/entity/gadget/platform/ConfigRoute.java b/src/main/java/emu/grasscutter/game/entity/gadget/platform/ConfigRoute.java new file mode 100644 index 000000000..60659e2c3 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/entity/gadget/platform/ConfigRoute.java @@ -0,0 +1,30 @@ +package emu.grasscutter.game.entity.gadget.platform; + +import emu.grasscutter.net.proto.MovingPlatformTypeOuterClass; +import emu.grasscutter.net.proto.PlatformInfoOuterClass; +import emu.grasscutter.scripts.data.SceneGadget; +import emu.grasscutter.utils.Position; +import lombok.Getter; +import lombok.Setter; + +public class ConfigRoute extends BaseRoute { + + @Getter @Setter private int routeId; + + public ConfigRoute(SceneGadget gadget) { + super(gadget); + this.routeId = gadget.route_id; + } + + public ConfigRoute(Position startRot, boolean startRoute, boolean isActive, int routeId) { + super(startRot, startRoute, isActive); + this.routeId = routeId; + } + + @Override + public PlatformInfoOuterClass.PlatformInfo.Builder toProto() { + return super.toProto() + .setRouteId(routeId) + .setMovingPlatformType(MovingPlatformTypeOuterClass.MovingPlatformType.MOVING_PLATFORM_TYPE_USE_CONFIG); + } +} diff --git a/src/main/java/emu/grasscutter/game/entity/gadget/platform/PointArrayRoute.java b/src/main/java/emu/grasscutter/game/entity/gadget/platform/PointArrayRoute.java new file mode 100644 index 000000000..84b1c6620 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/entity/gadget/platform/PointArrayRoute.java @@ -0,0 +1,32 @@ +package emu.grasscutter.game.entity.gadget.platform; + +import emu.grasscutter.net.proto.MovingPlatformTypeOuterClass; +import emu.grasscutter.net.proto.PlatformInfoOuterClass; +import emu.grasscutter.scripts.data.SceneGadget; +import emu.grasscutter.utils.Position; +import lombok.Getter; +import lombok.Setter; + +/** + * TODO implement point array routes, read from missing resources + */ +public class PointArrayRoute extends BaseRoute { + + @Getter @Setter int currentPoint; + @Getter @Setter int pointArrayId; + + public PointArrayRoute(SceneGadget gadget) { + super(gadget); + } + + public PointArrayRoute(Position startRot, boolean startRoute, boolean isActive, int pointArrayId) { + super(startRot, startRoute, isActive); + this.pointArrayId = pointArrayId; + } + + @Override + public PlatformInfoOuterClass.PlatformInfo.Builder toProto() { + return super.toProto() + .setMovingPlatformType(MovingPlatformTypeOuterClass.MovingPlatformType.MOVING_PLATFORM_TYPE_ROUTE); + } +} diff --git a/src/main/java/emu/grasscutter/game/player/PlayerProgress.java b/src/main/java/emu/grasscutter/game/player/PlayerProgress.java new file mode 100644 index 000000000..ee08959dd --- /dev/null +++ b/src/main/java/emu/grasscutter/game/player/PlayerProgress.java @@ -0,0 +1,63 @@ +package emu.grasscutter.game.player; + +import dev.morphia.annotations.Entity; +import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.val; + +import java.util.Map; + +/** + * Tracks progress the player made in the world, like obtained items, seen characters and more + */ +@Entity +public class PlayerProgress { + + @Getter private Map itemHistory; + + // keep track of EXEC_ADD_QUEST_PROGRESS count, will be used in CONTENT_ADD_QUEST_PROGRESS + // not sure where to put this, this should be saved to DB but not to individual quest, since + // it will be hard to loop and compare + private Map questProgressCountMap; + + public PlayerProgress(){ + this.questProgressCountMap = new Int2IntOpenHashMap(); + this.itemHistory = new Int2ObjectOpenHashMap<>(); + } + + public boolean hasPlayerObtainedItemHistorically(int itemId){ + return itemHistory.containsKey(itemId); + } + + public int addToItemHistory(int itemId, int count){ + val itemEntry = itemHistory.computeIfAbsent(itemId, (key) -> new ItemEntry(itemId)); + return itemEntry.addToObtainedCount(count); + } + + public int getCurrentProgress(int progressId){ + return questProgressCountMap.getOrDefault(progressId, -1); + } + + public int addToCurrentProgress(int progressId, int count){ + return questProgressCountMap.merge(progressId, count, Integer::sum); + } + + @Entity + @NoArgsConstructor + public static class ItemEntry{ + @Getter private int itemId; + @Getter @Setter private int obtainedCount; + + ItemEntry(int itemId){ + this.itemId = itemId; + } + + int addToObtainedCount(int amount){ + this.obtainedCount+=amount; + return this.obtainedCount; + } + } +} diff --git a/src/main/java/emu/grasscutter/game/props/RefreshType.java b/src/main/java/emu/grasscutter/game/props/RefreshType.java new file mode 100644 index 000000000..cb88dbba3 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/props/RefreshType.java @@ -0,0 +1,43 @@ +package emu.grasscutter.game.props; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + +public enum RefreshType { + REFRESH_NONE (0), + REFRESH_INTERVAL (1), + REFRESH_DAILY (2), + REFRESH_WEEKlY (3), + REFRESH_DAYBEGIN_INTERVAL (4); + + private final int value; + private static final Int2ObjectMap map = new Int2ObjectOpenHashMap<>(); + private static final Map stringMap = new HashMap<>(); + + static { + Stream.of(values()).forEach(e -> { + map.put(e.getValue(), e); + stringMap.put(e.name(), e); + }); + } + + private RefreshType(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static RefreshType getTypeByValue(int value) { + return map.getOrDefault(value, REFRESH_NONE); + } + + public static RefreshType getTypeByName(String name) { + return stringMap.getOrDefault(name, REFRESH_NONE); + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/QuestValueCond.java b/src/main/java/emu/grasscutter/game/quest/QuestValueCond.java new file mode 100644 index 000000000..3701a9fa0 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/QuestValueCond.java @@ -0,0 +1,11 @@ +package emu.grasscutter.game.quest; + +import emu.grasscutter.game.quest.enums.QuestCond; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface QuestValueCond { + QuestCond value(); +} diff --git a/src/main/java/emu/grasscutter/game/quest/QuestValueContent.java b/src/main/java/emu/grasscutter/game/quest/QuestValueContent.java new file mode 100644 index 000000000..751db4a55 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/QuestValueContent.java @@ -0,0 +1,11 @@ +package emu.grasscutter.game.quest; + +import emu.grasscutter.game.quest.enums.QuestContent; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface QuestValueContent { + QuestContent value(); +} diff --git a/src/main/java/emu/grasscutter/game/quest/QuestValueExec.java b/src/main/java/emu/grasscutter/game/quest/QuestValueExec.java new file mode 100644 index 000000000..bd5f0ffcc --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/QuestValueExec.java @@ -0,0 +1,11 @@ +package emu.grasscutter.game.quest; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import emu.grasscutter.game.quest.enums.QuestExec; + +@Retention(RetentionPolicy.RUNTIME) +public @interface QuestValueExec { + QuestExec value(); +} diff --git a/src/main/java/emu/grasscutter/game/quest/TeleportData.java b/src/main/java/emu/grasscutter/game/quest/TeleportData.java new file mode 100644 index 000000000..83c143f5c --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/TeleportData.java @@ -0,0 +1,35 @@ +package emu.grasscutter.game.quest; + +import java.util.List; +import lombok.Data; + +@Data +public class TeleportData { + List transmit_points; + List npcs; + List gadgets; + + @Data + public static class TransmitPoint { + private int point_id; + private int scene_id; + private String pos; + } + + @Data + public static class Npc { + private int data_index; + private int room_id; + private int scene_id; + private int id; + private String alias; + private String script; + private String pos; + } + + @Data + public static class Gadget { + private int id; + private String pos; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/conditions/BaseConditionQuestVar.java b/src/main/java/emu/grasscutter/game/quest/conditions/BaseConditionQuestVar.java new file mode 100644 index 000000000..9d94317c7 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/conditions/BaseConditionQuestVar.java @@ -0,0 +1,40 @@ +package emu.grasscutter.game.quest.conditions; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.excels.QuestData; +import emu.grasscutter.game.player.Player; +import lombok.val; + +public abstract class BaseConditionQuestVar extends BaseCondition { + + protected abstract boolean doCompare(int variable, int cond); + + @Override + public boolean execute(Player owner, QuestData questData, QuestData.QuestAcceptCondition condition, String paramStr, int... params) { + val index = condition.getParam()[0]; + val targetValue = condition.getParam()[1]; + val questVarValue = getQuestVar(owner, questData, index); + + Grasscutter.getLogger().debug("questVar {} : {}", index, questVarValue); + + if (questVarValue < 0) { + return false; + } + return doCompare(questVarValue, targetValue); + } + + protected int getQuestVar(Player owner, QuestData questData, int index) { + val mainQuest = owner.getQuestManager().getMainQuestById(questData.getMainId()); + if (mainQuest == null) { + Grasscutter.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); + return -2; + } + return questVars[index]; + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/conditions/ConditionActivityCond.java b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionActivityCond.java new file mode 100644 index 000000000..4ec0603d5 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionActivityCond.java @@ -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 lombok.val; + +import static emu.grasscutter.game.quest.enums.QuestCond.QUEST_COND_ACTIVITY_COND; + +@QuestValueCond(QUEST_COND_ACTIVITY_COND) +public class ConditionActivityCond extends BaseCondition { + + @Override + public boolean execute(Player owner, QuestData questData, QuestData.QuestAcceptCondition condition, String paramStr, int... params) { + val activityCondId = condition.getParam()[0]; + val targetState = condition.getParam()[1]; // only 1 for now + return owner.getActivityManager().meetsCondition(activityCondId) == (targetState == 1); + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/conditions/ConditionActivityEnd.java b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionActivityEnd.java new file mode 100644 index 000000000..34c31be7f --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionActivityEnd.java @@ -0,0 +1,19 @@ +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 lombok.val; + +import static emu.grasscutter.game.quest.enums.QuestCond.QUEST_COND_ACTIVITY_END; + +@QuestValueCond(QUEST_COND_ACTIVITY_END) +public class ConditionActivityEnd extends BaseCondition { + + @Override + public boolean execute(Player owner, QuestData questData, QuestData.QuestAcceptCondition condition, String paramStr, int... params) { + val activityId = condition.getParam()[0]; + return owner.getActivityManager().hasActivityEnded(activityId); + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/conditions/ConditionActivityOpen.java b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionActivityOpen.java new file mode 100644 index 000000000..10d857261 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionActivityOpen.java @@ -0,0 +1,19 @@ +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 lombok.val; + +import static emu.grasscutter.game.quest.enums.QuestCond.QUEST_COND_ACTIVITY_OPEN; + +@QuestValueCond(QUEST_COND_ACTIVITY_OPEN) +public class ConditionActivityOpen extends BaseCondition { + + @Override + public boolean execute(Player owner, QuestData questData, QuestData.QuestAcceptCondition condition, String paramStr, int... params) { + val activityId = condition.getParam()[0]; + return owner.getActivityManager().isActivityActive(activityId); + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/conditions/ConditionHistoryGotAnyItem.java b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionHistoryGotAnyItem.java new file mode 100644 index 000000000..bc8997326 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionHistoryGotAnyItem.java @@ -0,0 +1,18 @@ +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_HISTORY_GOT_ANY_ITEM) +public class ConditionHistoryGotAnyItem extends BaseCondition { + + @Override + public boolean execute(Player owner, QuestData questData, QuestData.QuestAcceptCondition condition, String paramStr, int... params) { + val itemId = condition.getParam()[0]; + return owner.getPlayerProgress().hasPlayerObtainedItemHistorically(itemId); + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/conditions/ConditionIsDaytime.java b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionIsDaytime.java new file mode 100644 index 000000000..c51c9398d --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionIsDaytime.java @@ -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; + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/conditions/ConditionItemNumLessThan.java b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionItemNumLessThan.java new file mode 100644 index 000000000..3c4d6b7c2 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionItemNumLessThan.java @@ -0,0 +1,21 @@ +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 lombok.val; + +import static emu.grasscutter.game.quest.enums.QuestCond.QUEST_COND_ITEM_NUM_LESS_THAN; + +@QuestValueCond(QUEST_COND_ITEM_NUM_LESS_THAN) +public class ConditionItemNumLessThan extends BaseCondition { + + @Override + public boolean execute(Player owner, QuestData questData, 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; + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/conditions/ConditionNone.java b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionNone.java new file mode 100644 index 000000000..23dd4a07a --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionNone.java @@ -0,0 +1,16 @@ +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; + +@QuestValueCond(QuestCond.QUEST_COND_NONE) +public class ConditionNone extends BaseCondition{ + + @Override + public boolean execute(Player owner, QuestData questData, QuestData.QuestAcceptCondition condition, String paramStr, int... params) { + return true; + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/conditions/ConditionOpenStateEqual.java b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionOpenStateEqual.java new file mode 100644 index 000000000..1869f7e48 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionOpenStateEqual.java @@ -0,0 +1,19 @@ +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_OPEN_STATE_EQUAL) +public class ConditionOpenStateEqual extends BaseCondition { + + @Override + public boolean execute(Player owner, QuestData questData, QuestData.QuestAcceptCondition condition, String paramStr, int... params) { + val openStateId = condition.getParam()[0]; + val requiredState = condition.getParam()[1]; + return owner.getProgressManager().getOpenState(openStateId) == requiredState; + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/conditions/ConditionPackHaveItem.java b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionPackHaveItem.java new file mode 100644 index 000000000..86e40bf33 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionPackHaveItem.java @@ -0,0 +1,21 @@ +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 lombok.val; + +import static emu.grasscutter.game.quest.enums.QuestCond.QUEST_COND_PACK_HAVE_ITEM; + +@QuestValueCond(QUEST_COND_PACK_HAVE_ITEM) +public class ConditionPackHaveItem extends BaseCondition { + + @Override + public boolean execute(Player owner, QuestData questData, 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; + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/conditions/ConditionPersonalLineUnlock.java b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionPersonalLineUnlock.java new file mode 100644 index 000000000..739420cb2 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionPersonalLineUnlock.java @@ -0,0 +1,18 @@ +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_PERSONAL_LINE_UNLOCK) +public class ConditionPersonalLineUnlock extends BaseCondition { + + @Override + public boolean execute(Player owner, QuestData questData, QuestData.QuestAcceptCondition condition, String paramStr, int... params) { + val personalLineId = condition.getParam()[0]; + return owner.getPersonalLineList().contains(personalLineId); + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/conditions/ConditionTimeVarGreaterOrEqual.java b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionTimeVarGreaterOrEqual.java new file mode 100644 index 000000000..8484a42af --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionTimeVarGreaterOrEqual.java @@ -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; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/conditions/ConditionTimeVarPassDay.java b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionTimeVarPassDay.java new file mode 100644 index 000000000..5dbd75cc6 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionTimeVarPassDay.java @@ -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; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/content/ContentAnyManualTransport.java b/src/main/java/emu/grasscutter/game/quest/content/ContentAnyManualTransport.java new file mode 100644 index 000000000..9da4fcb41 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/content/ContentAnyManualTransport.java @@ -0,0 +1,15 @@ +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 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, QuestData.QuestContentCondition condition, String paramStr, int... params) { + return true; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/content/ContentClearGroupMonster.java b/src/main/java/emu/grasscutter/game/quest/content/ContentClearGroupMonster.java new file mode 100644 index 000000000..b9ca961a7 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/content/ContentClearGroupMonster.java @@ -0,0 +1,19 @@ +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 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, QuestData.QuestContentCondition condition, String paramStr, int... params) { + val groupId = condition.getParam()[0]; + + return quest.getOwner().getScene().getScriptManager().isClearedGroupMonsters(groupId); + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/content/ContentDestroyGadget.java b/src/main/java/emu/grasscutter/game/quest/content/ContentDestroyGadget.java new file mode 100644 index 000000000..61981436f --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/content/ContentDestroyGadget.java @@ -0,0 +1,15 @@ +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 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, QuestData.QuestContentCondition condition, String paramStr, int... params) { + return condition.getParam()[0] == params[0]; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/content/ContentEnterMyWorld.java b/src/main/java/emu/grasscutter/game/quest/content/ContentEnterMyWorld.java new file mode 100644 index 000000000..d214f926e --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/content/ContentEnterMyWorld.java @@ -0,0 +1,17 @@ +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 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, QuestData.QuestContentCondition condition, String paramStr, int... params) { + return condition.getParam()[0] == params[0]; + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/content/ContentEnterMyWorldScene.java b/src/main/java/emu/grasscutter/game/quest/content/ContentEnterMyWorldScene.java new file mode 100644 index 000000000..43a06e425 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/content/ContentEnterMyWorldScene.java @@ -0,0 +1,17 @@ +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 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, QuestData.QuestContentCondition condition, String paramStr, int... params) { + return condition.getParam()[0] == params[0]; + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/content/ContentEnterVehicle.java b/src/main/java/emu/grasscutter/game/quest/content/ContentEnterVehicle.java new file mode 100644 index 000000000..e027ec2dc --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/content/ContentEnterVehicle.java @@ -0,0 +1,15 @@ +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 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, QuestData.QuestContentCondition condition, String paramStr, int... params) { + return condition.getParam()[0] == params[0]; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/content/ContentFailDungeon.java b/src/main/java/emu/grasscutter/game/quest/content/ContentFailDungeon.java new file mode 100644 index 000000000..29da1d0fb --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/content/ContentFailDungeon.java @@ -0,0 +1,18 @@ +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 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, QuestData.QuestContentCondition condition, String paramStr, int... params) { + return condition.getParam()[0] == params[0]; + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/content/ContentFinishDungeon.java b/src/main/java/emu/grasscutter/game/quest/content/ContentFinishDungeon.java new file mode 100644 index 000000000..a0a787c09 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/content/ContentFinishDungeon.java @@ -0,0 +1,18 @@ +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 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, QuestData.QuestContentCondition condition, String paramStr, int... params) { + return condition.getParam()[0] == params[0]; + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/content/ContentItemLessThan.java b/src/main/java/emu/grasscutter/game/quest/content/ContentItemLessThan.java new file mode 100644 index 000000000..9384252d3 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/content/ContentItemLessThan.java @@ -0,0 +1,15 @@ +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 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, QuestData.QuestContentCondition condition, String paramStr, int... params) { + return condition.getParam()[0] == params[0] && condition.getCount() > params[1]; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/content/ContentKillMonster.java b/src/main/java/emu/grasscutter/game/quest/content/ContentKillMonster.java new file mode 100644 index 000000000..7739be1e9 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/content/ContentKillMonster.java @@ -0,0 +1,15 @@ +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 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, QuestData.QuestContentCondition condition, String paramStr, int... params) { + return condition.getParam()[0] == params[0]; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/content/ContentMonsterDie.java b/src/main/java/emu/grasscutter/game/quest/content/ContentMonsterDie.java new file mode 100644 index 000000000..72de7f4c0 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/content/ContentMonsterDie.java @@ -0,0 +1,15 @@ +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 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, QuestData.QuestContentCondition condition, String paramStr, int... params) { + return condition.getParam()[0] == params[0]; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/content/ContentObtainItem.java b/src/main/java/emu/grasscutter/game/quest/content/ContentObtainItem.java new file mode 100644 index 000000000..93c3ea2ac --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/content/ContentObtainItem.java @@ -0,0 +1,19 @@ +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 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, QuestData.QuestContentCondition condition, String paramStr, int... params) { + var targetCount = condition.getCount(); + if (targetCount == 0) { + targetCount = 1; + } + return condition.getParam()[0] == params[0] && targetCount <= params[1]; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/content/ContentPlayerLevelUp.java b/src/main/java/emu/grasscutter/game/quest/content/ContentPlayerLevelUp.java new file mode 100644 index 000000000..f9ae864be --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/content/ContentPlayerLevelUp.java @@ -0,0 +1,15 @@ +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 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, QuestData.QuestContentCondition condition, String paramStr, int... params) { + return quest.getOwner().getLevel() >= condition.getCount(); + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/content/ContentTimeVarMoreOrEqual.java b/src/main/java/emu/grasscutter/game/quest/content/ContentTimeVarMoreOrEqual.java new file mode 100644 index 000000000..cd3b330da --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/content/ContentTimeVarMoreOrEqual.java @@ -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; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/content/ContentTimeVarPassDay.java b/src/main/java/emu/grasscutter/game/quest/content/ContentTimeVarPassDay.java new file mode 100644 index 000000000..e0b814820 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/content/ContentTimeVarPassDay.java @@ -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; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/content/ContentUnlockArea.java b/src/main/java/emu/grasscutter/game/quest/content/ContentUnlockArea.java new file mode 100644 index 000000000..60f245b6f --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/content/ContentUnlockArea.java @@ -0,0 +1,15 @@ +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 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, QuestData.QuestContentCondition condition, String paramStr, int... params) { + return condition.getParam()[0] == params[0] || condition.getParam()[1] == params[1]; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/content/ContentUseItem.java b/src/main/java/emu/grasscutter/game/quest/content/ContentUseItem.java new file mode 100644 index 000000000..e0d68fd9b --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/content/ContentUseItem.java @@ -0,0 +1,15 @@ +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 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, QuestData.QuestContentCondition condition, String paramStr, int... params) { + return condition.getParam()[0] == params[0]; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/content/ContentWorktopSelect.java b/src/main/java/emu/grasscutter/game/quest/content/ContentWorktopSelect.java new file mode 100644 index 000000000..3a5a8b202 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/content/ContentWorktopSelect.java @@ -0,0 +1,15 @@ +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 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, QuestData.QuestContentCondition condition, String paramStr, int... params) { + return condition.getParam()[0] == params[0] || condition.getParam()[1] == params[1]; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/enums/QuestCond.java b/src/main/java/emu/grasscutter/game/quest/enums/QuestCond.java new file mode 100644 index 000000000..95c5c955d --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/enums/QuestCond.java @@ -0,0 +1,121 @@ +package emu.grasscutter.game.quest.enums; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +public enum QuestCond implements QuestTrigger { + QUEST_COND_NONE (0), + QUEST_COND_STATE_EQUAL (1), + QUEST_COND_STATE_NOT_EQUAL (2), + QUEST_COND_PACK_HAVE_ITEM (3), + QUEST_COND_AVATAR_ELEMENT_EQUAL (4), // missing, currently unused + QUEST_COND_AVATAR_ELEMENT_NOT_EQUAL (5), // missing, only NPC groups + QUEST_COND_AVATAR_CAN_CHANGE_ELEMENT (6), // missing, only NPC groups + QUEST_COND_CITY_LEVEL_EQUAL_GREATER (7), // missing, currently unused + QUEST_COND_ITEM_NUM_LESS_THAN (8), + QUEST_COND_DAILY_TASK_START (9), // missing + QUEST_COND_OPEN_STATE_EQUAL (10), + QUEST_COND_DAILY_TASK_OPEN (11), // missing, only NPC groups + QUEST_COND_DAILY_TASK_REWARD_CAN_GET (12), // missing, only NPC groups/talks + QUEST_COND_DAILY_TASK_REWARD_RECEIVED (13), // missing, only NPC groups/talks + QUEST_COND_PLAYER_LEVEL_REWARD_CAN_GET (14), // missing, only NPC groups/talks + QUEST_COND_EXPLORATION_REWARD_CAN_GET (15), // missing, only NPC groups/talks + QUEST_COND_IS_WORLD_OWNER (16), // missing, only NPC groups/talks + QUEST_COND_PLAYER_LEVEL_EQUAL_GREATER (17), + 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), // only NPC groups + QUEST_COND_CURRENT_AVATAR (22), // missing + QUEST_COND_CURRENT_AREA (23), // missing + QUEST_COND_QUEST_VAR_EQUAL (24), + QUEST_COND_QUEST_VAR_GREATER (25), + QUEST_COND_QUEST_VAR_LESS (26), + QUEST_COND_FORGE_HAVE_FINISH (27), // missing, only NPC groups + QUEST_COND_DAILY_TASK_IN_PROGRESS (28), // missing + QUEST_COND_DAILY_TASK_FINISHED (29), // missing, currently unused + QUEST_COND_ACTIVITY_COND (30), + QUEST_COND_ACTIVITY_OPEN (31), + QUEST_COND_DAILY_TASK_VAR_GT (32), // missing + QUEST_COND_DAILY_TASK_VAR_EQ (33), // missing + QUEST_COND_DAILY_TASK_VAR_LT (34), // missing + QUEST_COND_BARGAIN_ITEM_GT (35), // missing, currently unused + QUEST_COND_BARGAIN_ITEM_EQ (36), // missing, currently unused + QUEST_COND_BARGAIN_ITEM_LT (37), // missing, currently unused + QUEST_COND_COMPLETE_TALK (38), + QUEST_COND_NOT_HAVE_BLOSSOM_TALK (39), // missing, only NPC groups + QUEST_COND_IS_CUR_BLOSSOM_TALK (40), // missing, only Blossom groups + QUEST_COND_QUEST_NOT_RECEIVE (41), // missing + QUEST_COND_QUEST_SERVER_COND_VALID (42), // missing, only NPC groups + QUEST_COND_ACTIVITY_CLIENT_COND (43), // missing, only NPC and Activity groups + QUEST_COND_QUEST_GLOBAL_VAR_EQUAL (44), + QUEST_COND_QUEST_GLOBAL_VAR_GREATER (45), + QUEST_COND_QUEST_GLOBAL_VAR_LESS (46), + QUEST_COND_PERSONAL_LINE_UNLOCK (47), + QUEST_COND_CITY_REPUTATION_REQUEST (48), // missing + QUEST_COND_MAIN_COOP_START (49), // missing + QUEST_COND_MAIN_COOP_ENTER_SAVE_POINT (50), // missing + QUEST_COND_CITY_REPUTATION_LEVEL (51), // missing, only NPC groups + QUEST_COND_CITY_REPUTATION_UNLOCK (52), // missing, currently unused + QUEST_COND_LUA_NOTIFY (53), + QUEST_COND_CUR_CLIMATE (54), + QUEST_COND_ACTIVITY_END (55), + QUEST_COND_COOP_POINT_RUNNING (56), // missing, currently unused + QUEST_COND_GADGET_TALK_STATE_EQUAL (57), // missing, only Gadget groups + QUEST_COND_AVATAR_FETTER_GT (58), // missing, only NPC groups/talks + QUEST_COND_AVATAR_FETTER_EQ (59), // missing, only talks + QUEST_COND_AVATAR_FETTER_LT (60), // missing, only talks + QUEST_COND_NEW_HOMEWORLD_MOUDLE_UNLOCK (61), // missing, only Gadget groups + 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), // 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), + QUEST_COND_LEARNED_RECIPE (70), // missing, currently unused + QUEST_COND_LUNARITE_REGION_UNLOCKED (71), // missing, only NPC groups + QUEST_COND_LUNARITE_HAS_REGION_HINT_COUNT (72), // missing, only NPC groups + QUEST_COND_LUNARITE_COLLECT_FINISH (73), // missing, only NPC groups + QUEST_COND_LUNARITE_MARK_ALL_FINISH (74), // missing, only NPC groups + QUEST_COND_NEW_HOMEWORLD_SHOP_ITEM (75), // missing, only Gadget groups + QUEST_COND_SCENE_POINT_UNLOCK (76), // missing, only NPC groups + QUEST_COND_SCENE_LEVEL_TAG_EQ (77), // missing + QUEST_COND_PLAYER_ENTER_REGION (78), // missing + QUEST_COND_UNKNOWN (9999); + + private final int value; + + QuestCond(int id) { + this.value = id; + } + + public int getValue() { + return value; + } + + + private static final Int2ObjectMap contentMap = new Int2ObjectOpenHashMap<>(); + private static final Map contentStringMap = new HashMap<>(); + + static { + Stream.of(values()) + .forEach(e -> { + contentMap.put(e.getValue(), e); + contentStringMap.put(e.name(), e); + }); + } + + public static QuestCond getContentTriggerByValue(int value) { + return contentMap.getOrDefault(value, QUEST_COND_NONE); + } + + public static QuestCond getContentTriggerByName(String name) { + return contentStringMap.getOrDefault(name, QUEST_COND_NONE); + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/enums/QuestContent.java b/src/main/java/emu/grasscutter/game/quest/enums/QuestContent.java new file mode 100644 index 000000000..32d67bd55 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/enums/QuestContent.java @@ -0,0 +1,117 @@ +package emu.grasscutter.game.quest.enums; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +public enum QuestContent implements QuestTrigger { + QUEST_CONTENT_NONE (0), + QUEST_CONTENT_KILL_MONSTER (1), // currently unused + QUEST_CONTENT_COMPLETE_TALK (2), + QUEST_CONTENT_MONSTER_DIE (3), + QUEST_CONTENT_FINISH_PLOT (4), + QUEST_CONTENT_OBTAIN_ITEM (5), + QUEST_CONTENT_TRIGGER_FIRE (6), + 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), + QUEST_CONTENT_FINISH_DUNGEON (11), + QUEST_CONTENT_DESTROY_GADGET (12), + QUEST_CONTENT_OBTAIN_MATERIAL_WITH_SUBTYPE (13), // missing, finish + QUEST_CONTENT_NICK_NAME (14), // missing, currently unused + QUEST_CONTENT_WORKTOP_SELECT (15), // currently unused + QUEST_CONTENT_SEAL_BATTLE_RESULT (16), // missing, currently unused + QUEST_CONTENT_ENTER_ROOM (17), + QUEST_CONTENT_GAME_TIME_TICK (18), + QUEST_CONTENT_FAIL_DUNGEON (19), + QUEST_CONTENT_LUA_NOTIFY (20), + QUEST_CONTENT_TEAM_DEAD (21), // missing, fail + QUEST_CONTENT_COMPLETE_ANY_TALK (22), + QUEST_CONTENT_UNLOCK_TRANS_POINT (23), + QUEST_CONTENT_ADD_QUEST_PROGRESS (24), + QUEST_CONTENT_INTERACT_GADGET (25), + QUEST_CONTENT_DAILY_TASK_COMP_FINISH (26), // missing, currently unused + QUEST_CONTENT_FINISH_ITEM_GIVING (27), // missing, finish + QUEST_CONTENT_SKILL (107), + QUEST_CONTENT_CITY_LEVEL_UP (109), // missing, finish + QUEST_CONTENT_PATTERN_GROUP_CLEAR_MONSTER (110), // missing, finish, for random quests + QUEST_CONTENT_ITEM_LESS_THAN (111), + QUEST_CONTENT_PLAYER_LEVEL_UP (112), + QUEST_CONTENT_DUNGEON_OPEN_STATUE (113), // missing, currently unused + QUEST_CONTENT_UNLOCK_AREA (114), // currently unused + QUEST_CONTENT_OPEN_CHEST_WITH_GADGET_ID (115), // missing, currently unused + QUEST_CONTENT_UNLOCK_TRANS_POINT_WITH_TYPE (116), // missing, currently unused + QUEST_CONTENT_FINISH_DAILY_DUNGEON (117), // missing, currently unused + QUEST_CONTENT_FINISH_WEEKLY_DUNGEON (118), // missing, currently unused + QUEST_CONTENT_QUEST_VAR_EQUAL (119), + QUEST_CONTENT_QUEST_VAR_GREATER (120), + QUEST_CONTENT_QUEST_VAR_LESS (121), + QUEST_CONTENT_OBTAIN_VARIOUS_ITEM (122), // missing, finish + QUEST_CONTENT_FINISH_TOWER_LEVEL (123), // missing, currently unused + QUEST_CONTENT_BARGAIN_SUCC (124), // missing, finish + QUEST_CONTENT_BARGAIN_FAIL (125),// missing, fail + QUEST_CONTENT_ITEM_LESS_THAN_BARGAIN (126),// missing, fail + QUEST_CONTENT_ACTIVITY_TRIGGER_FAILED (127),// missing, fail + QUEST_CONTENT_MAIN_COOP_ENTER_SAVE_POINT (128),// missing, finish + QUEST_CONTENT_ANY_MANUAL_TRANSPORT (129), + 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_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 + QUEST_CONTENT_NOT_UNLOCKED_RECIPE (139),// missing, finish + QUEST_CONTENT_FISHING_SUCC (140),// missing, finish + QUEST_CONTENT_ENTER_ROGUE_DUNGEON (141),// missing, finish + QUEST_CONTENT_USE_WIDGET (142),// missing, finish, only in unreleased quest + QUEST_CONTENT_CAPTURE_SUCC (143),// missing, currently unused + QUEST_CONTENT_CAPTURE_USE_CAPTURETAG_LIST (144),// missing, currently unused + QUEST_CONTENT_CAPTURE_USE_MATERIAL_LIST (145),// missing, finish + QUEST_CONTENT_ENTER_VEHICLE (147), + QUEST_CONTENT_SCENE_LEVEL_TAG_EQ (148),// missing, finish + QUEST_CONTENT_LEAVE_SCENE (149), + QUEST_CONTENT_LEAVE_SCENE_RANGE (150),// missing, fail + QUEST_CONTENT_IRODORI_FINISH_FLOWER_COMBINATION (151),// missing, finish + QUEST_CONTENT_IRODORI_POETRY_REACH_MIN_PROGRESS (152),// missing, finish + QUEST_CONTENT_IRODORI_POETRY_FINISH_FILL_POETRY (153),// missing, finish + QUEST_CONTENT_ACTIVITY_TRIGGER_UPDATE(154), // missing + QUEST_CONTENT_GADGET_STATE_CHANGE(155), // missing + QUEST_CONTENT_UNKNOWN (9999); + + private final int value; + + QuestContent(int id) { + this.value = id; + } + + public int getValue() { + return value; + } + + + private static final Int2ObjectMap contentMap = new Int2ObjectOpenHashMap<>(); + private static final Map contentStringMap = new HashMap<>(); + + static { + Stream.of(values()) + .forEach(e -> { + contentMap.put(e.getValue(), e); + contentStringMap.put(e.name(), e); + }); + } + + public static QuestContent getContentTriggerByValue(int value) { + return contentMap.getOrDefault(value, QUEST_CONTENT_NONE); + } + + public static QuestContent getContentTriggerByName(String name) { + return contentStringMap.getOrDefault(name, QUEST_CONTENT_NONE); + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/enums/QuestExec.java b/src/main/java/emu/grasscutter/game/quest/enums/QuestExec.java new file mode 100644 index 000000000..0e67b0030 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/enums/QuestExec.java @@ -0,0 +1,112 @@ +package emu.grasscutter.game.quest.enums; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +public enum QuestExec implements QuestTrigger { + QUEST_EXEC_NONE (0), + QUEST_EXEC_DEL_PACK_ITEM (1), + QUEST_EXEC_UNLOCK_POINT (2), + QUEST_EXEC_UNLOCK_AREA (3), + QUEST_EXEC_UNLOCK_FORCE (4), // missing, currently unused + QUEST_EXEC_LOCK_FORCE (5), // missing, currently unused + QUEST_EXEC_CHANGE_AVATAR_ELEMET (6), + QUEST_EXEC_REFRESH_GROUP_MONSTER (7), + QUEST_EXEC_SET_IS_FLYABLE (8), // missing, maybe gives glider + QUEST_EXEC_SET_IS_WEATHER_LOCKED (9), // missing + QUEST_EXEC_SET_IS_GAME_TIME_LOCKED (10), // missing + QUEST_EXEC_SET_IS_TRANSFERABLE (11), // missing, currently unused + QUEST_EXEC_GRANT_TRIAL_AVATAR (12), + QUEST_EXEC_OPEN_BORED (13), // missing, currently unused + QUEST_EXEC_ROLLBACK_QUEST (14), + QUEST_EXEC_NOTIFY_GROUP_LUA (15), + QUEST_EXEC_SET_OPEN_STATE (16), + QUEST_EXEC_LOCK_POINT (17), // missing + QUEST_EXEC_DEL_PACK_ITEM_BATCH (18), + QUEST_EXEC_REFRESH_GROUP_SUITE (19), + QUEST_EXEC_REMOVE_TRIAL_AVATAR (20), + QUEST_EXEC_SET_GAME_TIME (21), // missing + QUEST_EXEC_SET_WEATHER_GADGET (22), // missing + QUEST_EXEC_ADD_QUEST_PROGRESS (23), + QUEST_EXEC_NOTIFY_DAILY_TASK (24), // missing + QUEST_EXEC_CREATE_PATTERN_GROUP (25), // missing, used for random quests + QUEST_EXEC_REMOVE_PATTERN_GROUP (26), // missing, used for random quests + QUEST_EXEC_REFRESH_GROUP_SUITE_RANDOM (27), // missing + QUEST_EXEC_ACTIVE_ITEM_GIVING (28), // missing + QUEST_EXEC_DEL_ALL_SPECIFIC_PACK_ITEM (29), // missing + QUEST_EXEC_ROLLBACK_PARENT_QUEST (30), + QUEST_EXEC_LOCK_AVATAR_TEAM (31), // missing + QUEST_EXEC_UNLOCK_AVATAR_TEAM (32), // missing + QUEST_EXEC_UPDATE_PARENT_QUEST_REWARD_INDEX (33), // missing + QUEST_EXEC_SET_DAILY_TASK_VAR (34), // missing + QUEST_EXEC_INC_DAILY_TASK_VAR (35), // missing + QUEST_EXEC_DEC_DAILY_TASK_VAR (36), // missing, currently unused + QUEST_EXEC_ACTIVE_ACTIVITY_COND_STATE (37), // missing + QUEST_EXEC_INACTIVE_ACTIVITY_COND_STATE (38), // missing + QUEST_EXEC_ADD_CUR_AVATAR_ENERGY (39), + QUEST_EXEC_START_BARGAIN (41), // missing + QUEST_EXEC_STOP_BARGAIN (42), // missing + QUEST_EXEC_SET_QUEST_GLOBAL_VAR (43), + QUEST_EXEC_INC_QUEST_GLOBAL_VAR (44), + QUEST_EXEC_DEC_QUEST_GLOBAL_VAR (45), + QUEST_EXEC_REGISTER_DYNAMIC_GROUP (46), // test, maybe the dynamic should be saved on a list and when you enter the view range this loads it again + QUEST_EXEC_UNREGISTER_DYNAMIC_GROUP (47), // test, same for this + QUEST_EXEC_SET_QUEST_VAR (48), + QUEST_EXEC_INC_QUEST_VAR (49), + QUEST_EXEC_DEC_QUEST_VAR (50), + QUEST_EXEC_RANDOM_QUEST_VAR (51), // missing + QUEST_EXEC_ACTIVATE_SCANNING_PIC (52), // missing, currently unused + QUEST_EXEC_RELOAD_SCENE_TAG (53), // missing + QUEST_EXEC_REGISTER_DYNAMIC_GROUP_ONLY (54), // missing + 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), + 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 + QUEST_EXEC_DEACTIVE_ITEM_GIVING (63), // missing + QUEST_EXEC_CHANGE_SCENE_LEVEL_TAG (64), // missing + QUEST_EXEC_UNLOCK_PLAYER_WORLD_SCENE (65), // missing + QUEST_EXEC_LOCK_PLAYER_WORLD_SCENE (66), // missing + QUEST_EXEC_FAIL_MAINCOOP (67), // missing + QUEST_EXEC_MODIFY_WEATHER_AREA (68), // missing + QUEST_EXEC_MODIFY_ARANARA_COLLECTION_STATE (69), // missing + QUEST_EXEC_GRANT_TRIAL_AVATAR_BATCH_AND_LOCK_TEAM (70), // missing + QUEST_EXEC_UNKNOWN (9999); + + private final int value; + + QuestExec(int id) { + this.value = id; + } + + public int getValue() { + return value; + } + + private static final Int2ObjectMap contentMap = new Int2ObjectOpenHashMap<>(); + private static final Map contentStringMap = new HashMap<>(); + + static { + Stream.of(values()) + .filter(e -> e.name().startsWith("QUEST_CONTENT_")) + .forEach(e -> { + contentMap.put(e.getValue(), e); + contentStringMap.put(e.name(), e); + }); + } + + public static QuestExec getContentTriggerByValue(int value) { + return contentMap.getOrDefault(value, QUEST_EXEC_NONE); + } + + public static QuestExec getContentTriggerByName(String name) { + return contentStringMap.getOrDefault(name, QUEST_EXEC_NONE); + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/exec/ExecAddCurAvatarEnergy.java b/src/main/java/emu/grasscutter/game/quest/exec/ExecAddCurAvatarEnergy.java new file mode 100644 index 000000000..45ba3d572 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/exec/ExecAddCurAvatarEnergy.java @@ -0,0 +1,17 @@ +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 emu.grasscutter.Grasscutter; + +@QuestValueExec(QuestExec.QUEST_EXEC_ADD_CUR_AVATAR_ENERGY) +public class ExecAddCurAvatarEnergy extends QuestExecHandler { + @Override + public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) { + Grasscutter.getLogger().info("Energy refilled"); + return quest.getOwner().getEnergyManager().refillEntityAvatarEnergy(); + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/exec/ExecChangeAvatarElemet.java b/src/main/java/emu/grasscutter/game/quest/exec/ExecChangeAvatarElemet.java new file mode 100644 index 000000000..803722b89 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/exec/ExecChangeAvatarElemet.java @@ -0,0 +1,31 @@ +package emu.grasscutter.game.quest.exec; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.excels.QuestData; +import emu.grasscutter.game.props.ElementType; +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; + +/** + * Changes the main avatar's element. First parameter is the elementType id + */ +@QuestValueExec(QuestExec.QUEST_EXEC_CHANGE_AVATAR_ELEMET) +public class ExecChangeAvatarElemet extends QuestExecHandler { + @Override + public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) { + val targetElement = ElementType.getTypeByValue(Integer.parseInt(paramStr[0])); + val owner = quest.getOwner(); + val mainAvatar = owner.getAvatars().getAvatarById(owner.getMainCharacterId()); + + if(mainAvatar == null){ + Grasscutter.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()); + return mainAvatar.changeElement(targetElement); + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/exec/ExecClearTimeVar.java b/src/main/java/emu/grasscutter/game/quest/exec/ExecClearTimeVar.java new file mode 100644 index 000000000..e84c61bf5 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/exec/ExecClearTimeVar.java @@ -0,0 +1,19 @@ +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 mainQuestId = Integer.parseInt(condition.getParam()[0]); + val timeVarId = Integer.parseInt(condition.getParam()[1]); + val mainQuest = quest.getOwner().getQuestManager().getMainQuestById(mainQuestId); + return mainQuest.clearTimeVar(timeVarId); + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/exec/ExecDelPackItem.java b/src/main/java/emu/grasscutter/game/quest/exec/ExecDelPackItem.java new file mode 100644 index 000000000..cd2286d1f --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/exec/ExecDelPackItem.java @@ -0,0 +1,17 @@ +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; + +@QuestValueExec(QuestExec.QUEST_EXEC_DEL_PACK_ITEM) +public class ExecDelPackItem extends QuestExecHandler { + @Override + public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) { + int itemId = Integer.parseInt(paramStr[0]); + int amount = Integer.parseInt(paramStr[1]); + return quest.getOwner().getInventory().removeItem(itemId, amount); + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/exec/ExecDelPackItemBatch.java b/src/main/java/emu/grasscutter/game/quest/exec/ExecDelPackItemBatch.java new file mode 100644 index 000000000..f06b52fe3 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/exec/ExecDelPackItemBatch.java @@ -0,0 +1,26 @@ +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; + +@QuestValueExec(QuestExec.QUEST_EXEC_DEL_PACK_ITEM_BATCH) +public class ExecDelPackItemBatch extends QuestExecHandler { + @Override + public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) { + // input is like this: "100497:999,100498:999,100499:999" + var items = paramStr[0].split(","); + boolean success = true; + for (var itemString : items){ + var itemFields = itemString.split(":"); + var itemId = Integer.parseInt(itemFields[0]); + var amount = Integer.parseInt(itemFields[1]); + if(!quest.getOwner().getInventory().removeItem(itemId, amount)){ + success = false; + } + } + return success; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/exec/ExecGrantTrialAvatar.java b/src/main/java/emu/grasscutter/game/quest/exec/ExecGrantTrialAvatar.java new file mode 100644 index 000000000..6b88e7333 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/exec/ExecGrantTrialAvatar.java @@ -0,0 +1,21 @@ +package emu.grasscutter.game.quest.exec; + +import emu.grasscutter.Grasscutter; + +import emu.grasscutter.data.excels.QuestData; +import emu.grasscutter.game.quest.GameQuest; +import emu.grasscutter.game.quest.QuestValueExec; +import emu.grasscutter.game.quest.enums.QuestExec; +import emu.grasscutter.game.quest.handlers.QuestExecHandler; + +@QuestValueExec(QuestExec.QUEST_EXEC_GRANT_TRIAL_AVATAR) +public class ExecGrantTrialAvatar extends QuestExecHandler { + @Override + public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) { + if (quest.getOwner().addTrialAvatarForQuest(Integer.parseInt(paramStr[0]), quest.getMainQuestId())) { + Grasscutter.getLogger().info("Added trial avatar to team for quest {}", quest.getSubQuestId()); + return true; + } + return false; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/exec/ExecInitTimeVar.java b/src/main/java/emu/grasscutter/game/quest/exec/ExecInitTimeVar.java new file mode 100644 index 000000000..476ea4326 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/exec/ExecInitTimeVar.java @@ -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); + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/exec/ExecRefreshGroupMonster.java b/src/main/java/emu/grasscutter/game/quest/exec/ExecRefreshGroupMonster.java new file mode 100644 index 000000000..b0cf0bf1e --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/exec/ExecRefreshGroupMonster.java @@ -0,0 +1,19 @@ +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; + +@QuestValueExec(QuestExec.QUEST_EXEC_REFRESH_GROUP_MONSTER) +public class ExecRefreshGroupMonster extends QuestExecHandler { + + @Override + public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) { + var groupId = Integer.parseInt(paramStr[0]); + + return quest.getOwner().getScene().getScriptManager().refreshGroupMonster(groupId); + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/exec/ExecRegisterDynamicGroup.java b/src/main/java/emu/grasscutter/game/quest/exec/ExecRegisterDynamicGroup.java new file mode 100644 index 000000000..37beb9550 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/exec/ExecRegisterDynamicGroup.java @@ -0,0 +1,39 @@ +package emu.grasscutter.game.quest.exec; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.excels.QuestData; +import emu.grasscutter.game.quest.GameQuest; +import emu.grasscutter.game.quest.QuestGroupSuite; +import emu.grasscutter.game.quest.QuestValueExec; +import emu.grasscutter.game.quest.enums.QuestExec; +import emu.grasscutter.game.quest.handlers.QuestExecHandler; +import emu.grasscutter.game.world.Scene; + +@QuestValueExec(QuestExec.QUEST_EXEC_REGISTER_DYNAMIC_GROUP) +public class ExecRegisterDynamicGroup extends QuestExecHandler { + + @Override + public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) { + var sceneId = Integer.parseInt(paramStr[0]); + var groupId = Integer.parseInt(paramStr[1]); + + Grasscutter.getLogger().warn("Registering group {}", groupId); + + Scene scene = quest.getOwner().getWorld().getSceneById(sceneId); + if(scene == null) return false; + + int suiteId = scene.loadDynamicGroup(groupId); + if(suiteId == -1) return false; + + quest.getMainQuest().getQuestGroupSuites().add(QuestGroupSuite.of() + .scene(sceneId) + .group(groupId) + .suite(suiteId) + .build()); + + Grasscutter.getLogger().warn("Registered group {}, suite {} in scene {}", groupId, suiteId, scene.getId()); + + return true; + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/exec/ExecRemoveTrialAvatar.java b/src/main/java/emu/grasscutter/game/quest/exec/ExecRemoveTrialAvatar.java new file mode 100644 index 000000000..afa425406 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/exec/ExecRemoveTrialAvatar.java @@ -0,0 +1,21 @@ +package emu.grasscutter.game.quest.exec; + +import emu.grasscutter.Grasscutter; + +import emu.grasscutter.data.excels.QuestData; +import emu.grasscutter.game.quest.GameQuest; +import emu.grasscutter.game.quest.QuestValueExec; +import emu.grasscutter.game.quest.enums.QuestExec; +import emu.grasscutter.game.quest.handlers.QuestExecHandler; + +@QuestValueExec(QuestExec.QUEST_EXEC_REMOVE_TRIAL_AVATAR) +public class ExecRemoveTrialAvatar extends QuestExecHandler { + @Override + public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) { + if (quest.getOwner().removeTrialAvatarForQuest(Integer.parseInt(paramStr[0]))) { + Grasscutter.getLogger().info("Removed trial avatar from team for quest {}", quest.getSubQuestId()); + return true; + } + return false; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/exec/ExecRollbackParentQuest.java b/src/main/java/emu/grasscutter/game/quest/exec/ExecRollbackParentQuest.java new file mode 100644 index 000000000..55622b5fd --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/exec/ExecRollbackParentQuest.java @@ -0,0 +1,24 @@ +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 emu.grasscutter.server.packet.send.PacketScenePlayerLocationNotify; + +@QuestValueExec(QuestExec.QUEST_EXEC_ROLLBACK_PARENT_QUEST) +public class ExecRollbackParentQuest extends QuestExecHandler { + @Override + public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) { + var targetPosition = quest.getMainQuest().rewind(); + if(targetPosition == null){ + return false; + } + quest.getOwner().getPosition().set(targetPosition.get(0)); + quest.getOwner().getRotation().set(targetPosition.get(1)); + quest.getOwner().sendPacket(new PacketScenePlayerLocationNotify(quest.getOwner().getScene())); + // todo proper reset and warp + return true; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/exec/ExecRollbackQuest.java b/src/main/java/emu/grasscutter/game/quest/exec/ExecRollbackQuest.java new file mode 100644 index 000000000..109010e63 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/exec/ExecRollbackQuest.java @@ -0,0 +1,26 @@ +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 emu.grasscutter.server.packet.send.PacketScenePlayerLocationNotify; + +@QuestValueExec(QuestExec.QUEST_EXEC_ROLLBACK_QUEST) +public class ExecRollbackQuest extends QuestExecHandler { + @Override + public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) { + var targetQuestId = Integer.parseInt(paramStr[0]); + var targetQuest = quest.getOwner().getQuestManager().getQuestById(targetQuestId); + var targetPosition = targetQuest.getMainQuest().rewindTo(targetQuest, true); + if(targetPosition == null){ + return false; + } + quest.getOwner().getPosition().set(targetPosition.get(0)); + quest.getOwner().getRotation().set(targetPosition.get(1)); + quest.getOwner().sendPacket(new PacketScenePlayerLocationNotify(quest.getOwner().getScene())); + // todo proper reset and warp + return true; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/exec/ExecSetOpenState.java b/src/main/java/emu/grasscutter/game/quest/exec/ExecSetOpenState.java new file mode 100644 index 000000000..5c2ca5005 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/exec/ExecSetOpenState.java @@ -0,0 +1,24 @@ +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; + +import java.util.Arrays; + +@QuestValueExec(QuestExec.QUEST_EXEC_SET_OPEN_STATE) +public class ExecSetOpenState extends QuestExecHandler { + @Override + public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) { + val param = Arrays.stream(paramStr) + .filter(i -> !i.isBlank()) + .mapToInt(Integer::parseInt) + .toArray(); + + quest.getOwner().getProgressManager().forceSetOpenState(param[0], param[1]); + return true; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/exec/ExecUnregisterDynamicGroup.java b/src/main/java/emu/grasscutter/game/quest/exec/ExecUnregisterDynamicGroup.java new file mode 100644 index 000000000..6c5ebc180 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/exec/ExecUnregisterDynamicGroup.java @@ -0,0 +1,37 @@ +package emu.grasscutter.game.quest.exec; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.excels.QuestData; +import emu.grasscutter.game.quest.GameQuest; +import emu.grasscutter.game.quest.QuestValueExec; +import emu.grasscutter.game.quest.enums.QuestExec; +import emu.grasscutter.game.quest.handlers.QuestExecHandler; +import emu.grasscutter.game.world.Scene; +import emu.grasscutter.scripts.data.SceneBlock; +import emu.grasscutter.scripts.data.SceneGroup; +import lombok.val; + +@QuestValueExec(QuestExec.QUEST_EXEC_UNREGISTER_DYNAMIC_GROUP) +public class ExecUnregisterDynamicGroup extends QuestExecHandler { + + @Override + public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) { + val groupId = Integer.parseInt(paramStr[0]); + 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); + + if(!scene.unregisterDynamicGroup(groupId)){ + return false; + } + + //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()); + + return true; + } + +} diff --git a/src/main/java/emu/grasscutter/game/world/GroupReplacementData.java b/src/main/java/emu/grasscutter/game/world/GroupReplacementData.java new file mode 100644 index 000000000..13fd157de --- /dev/null +++ b/src/main/java/emu/grasscutter/game/world/GroupReplacementData.java @@ -0,0 +1,10 @@ +package emu.grasscutter.game.world; + +import java.util.List; +import lombok.Data; + +@Data +public class GroupReplacementData { + int id; + List replace_groups; +} diff --git a/src/main/java/emu/grasscutter/game/world/SceneGroupInstance.java b/src/main/java/emu/grasscutter/game/world/SceneGroupInstance.java new file mode 100644 index 000000000..18fc40ff8 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/world/SceneGroupInstance.java @@ -0,0 +1,86 @@ +package emu.grasscutter.game.world; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.bson.types.ObjectId; + +import dev.morphia.annotations.Entity; +import dev.morphia.annotations.Id; +import dev.morphia.annotations.Indexed; +import emu.grasscutter.database.DatabaseHelper; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.scripts.data.SceneGadget; +import emu.grasscutter.scripts.data.SceneGroup; +import lombok.Getter; +import lombok.Setter; + +@Entity(value = "group_instances", useDiscriminator = false) +public class SceneGroupInstance { + @Id private ObjectId id; + + @Indexed private int ownerUid; //This group is owned by the host player + @Getter private int groupId; + + @Getter private transient SceneGroup luaGroup; + @Getter @Setter private int targetSuiteId; + @Getter @Setter private int activeSuiteId; + @Getter private Set deadEntities; //Config_ids + private boolean isCached; + + @Getter private Map cachedGadgetStates; + @Getter private Map cachedVariables; + + @Getter @Setter private int lastTimeRefreshed; + + public SceneGroupInstance(SceneGroup group, Player owner) { + this.luaGroup = group; + this.groupId = group.id; + this.targetSuiteId = 0; + this.activeSuiteId = 0; + this.lastTimeRefreshed = 0; + this.ownerUid = owner.getUid(); + this.deadEntities = new HashSet<>(); + this.cachedGadgetStates = new ConcurrentHashMap<>(); + this.cachedVariables = new ConcurrentHashMap<>(); + + this.isCached = false; //This is true when the group is not loaded on scene but caches suite data + } + + @Deprecated // Morphia only! + SceneGroupInstance(){ + this.cachedVariables = new ConcurrentHashMap<>(); + this.deadEntities = new HashSet<>(); + this.cachedGadgetStates = new ConcurrentHashMap<>(); + } + + public void setLuaGroup(SceneGroup group) { + this.luaGroup = group; + this.groupId = group.id; + } + + public boolean isCached() { + return this.isCached; + } + + public void setCached(boolean value) { + this.isCached = value; + save(); //Save each time a group is registered or unregistered + } + + public void cacheGadgetState(SceneGadget g, int state) { + if(g.persistent) //Only cache when is persistent + cachedGadgetStates.put(g.config_id, state); + } + + public int getCachedGadgetState(SceneGadget g) { + Integer state = cachedGadgetStates.getOrDefault(g.config_id, null); + return (state == null) ? g.state : state; + } + + public void save() { + DatabaseHelper.saveGroupInstance(this); + } +} diff --git a/src/main/java/emu/grasscutter/game/world/data/TeleportProperties.java b/src/main/java/emu/grasscutter/game/world/data/TeleportProperties.java new file mode 100644 index 000000000..283651120 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/world/data/TeleportProperties.java @@ -0,0 +1,19 @@ +package emu.grasscutter.game.world.data; + +import emu.grasscutter.game.props.EnterReason; +import emu.grasscutter.net.proto.EnterTypeOuterClass; +import emu.grasscutter.server.event.player.PlayerTeleportEvent; +import emu.grasscutter.utils.Position; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class TeleportProperties { + private final int sceneId; + private final PlayerTeleportEvent.TeleportType teleportType; + private final EnterReason enterReason; + private Position teleportTo; + private Position teleportRot; + private EnterTypeOuterClass.EnterType enterType; +} diff --git a/src/main/java/emu/grasscutter/scripts/EntityControllerScriptManager.java b/src/main/java/emu/grasscutter/scripts/EntityControllerScriptManager.java new file mode 100644 index 000000000..fa3321bc0 --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/EntityControllerScriptManager.java @@ -0,0 +1,53 @@ +package emu.grasscutter.scripts; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.scripts.data.controller.EntityController; +import lombok.val; + +import javax.script.Bindings; +import javax.script.CompiledScript; +import java.io.IOException; +import java.nio.file.Files; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import static emu.grasscutter.utils.FileUtils.getScriptPath; + +public class EntityControllerScriptManager { + private static final Map gadgetController = new ConcurrentHashMap<>(); + + public static void load(){ + cacheGadgetControllers(); + } + + private static void cacheGadgetControllers(){ + try { + Files.newDirectoryStream(getScriptPath("Gadget/"), "*.lua").forEach(path -> { + val fileName = path.getFileName().toString(); + + if(!fileName.endsWith(".lua")) return; + + val controllerName = fileName.substring(0, fileName.length()-4); + CompiledScript cs = ScriptLoader.getScript("Gadget/"+fileName); + Bindings bindings = ScriptLoader.getEngine().createBindings(); + if (cs == null) return; + + try{ + cs.eval(bindings); + gadgetController.put(controllerName, new EntityController(cs, bindings)); + } catch (Throwable e){ + Grasscutter.getLogger().error("Error while loading gadget controller: {}", fileName); + } + }); + + Grasscutter.getLogger().info("Loaded {} gadget controllers", gadgetController.size()); + } catch (IOException e) { + Grasscutter.getLogger().error("Error loading gadget controller luas"); + } + } + + + public static EntityController getGadgetController(String name) { + return gadgetController.get(name); + } +} diff --git a/src/main/java/emu/grasscutter/scripts/constants/GroupKillPolicy.java b/src/main/java/emu/grasscutter/scripts/constants/GroupKillPolicy.java new file mode 100644 index 000000000..adc6485c9 --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/constants/GroupKillPolicy.java @@ -0,0 +1,9 @@ +package emu.grasscutter.scripts.constants; + +public enum GroupKillPolicy { + GROUP_KILL_NONE, + GROUP_KILL_ALL, + GROUP_KILL_MONSTER, + GROUP_KILL_GADGET, + GROUP_KILL_NPC +} diff --git a/src/main/java/emu/grasscutter/scripts/constants/IntValueEnum.java b/src/main/java/emu/grasscutter/scripts/constants/IntValueEnum.java new file mode 100644 index 000000000..065465129 --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/constants/IntValueEnum.java @@ -0,0 +1,5 @@ +package emu.grasscutter.scripts.constants; + +public interface IntValueEnum { + int getValue(); +} diff --git a/src/main/java/emu/grasscutter/scripts/constants/SealBattleType.java b/src/main/java/emu/grasscutter/scripts/constants/SealBattleType.java new file mode 100644 index 000000000..49af43c6b --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/constants/SealBattleType.java @@ -0,0 +1,7 @@ +package emu.grasscutter.scripts.constants; + +public enum SealBattleType { + NONE, + ENERGY_CHARGE, + KILL_MONSTER +} diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneReplaceable.java b/src/main/java/emu/grasscutter/scripts/data/SceneReplaceable.java new file mode 100644 index 000000000..1ffa1623e --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/data/SceneReplaceable.java @@ -0,0 +1,12 @@ +package emu.grasscutter.scripts.data; + +import lombok.Setter; +import lombok.ToString; + +@ToString +@Setter +public class SceneReplaceable { + public boolean value; + public int version; + public boolean new_bin_only; +} diff --git a/src/main/java/emu/grasscutter/scripts/data/controller/EntityController.java b/src/main/java/emu/grasscutter/scripts/data/controller/EntityController.java new file mode 100644 index 000000000..e48831f94 --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/data/controller/EntityController.java @@ -0,0 +1,67 @@ +package emu.grasscutter.scripts.data.controller; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.game.entity.GameEntity; +import emu.grasscutter.game.props.ElementType; +import emu.grasscutter.scripts.ScriptLib; +import emu.grasscutter.scripts.ScriptLoader; +import org.luaj.vm2.LuaError; +import org.luaj.vm2.LuaValue; + +import javax.script.Bindings; +import javax.script.CompiledScript; + +public class EntityController { + private transient CompiledScript entityController; + private transient Bindings entityControllerBindings; + + public EntityController(CompiledScript entityController, Bindings entityControllerBindings){ + this.entityController = entityController; + this.entityControllerBindings = entityControllerBindings; + } + + public void onBeHurt(GameEntity entity, ElementType elementType, boolean isHost) { + callControllerScriptFunc(entity, "OnBeHurt", LuaValue.valueOf(elementType.getValue()), LuaValue.valueOf(0), LuaValue.valueOf(isHost)); + } + + public void onDie(GameEntity entity, ElementType elementType) { + callControllerScriptFunc(entity, "OnDie", LuaValue.valueOf(elementType.getValue()), LuaValue.valueOf(0)); + } + + public void onTimer(GameEntity entity, int now) { + callControllerScriptFunc(entity, "OnTimer", LuaValue.valueOf(now)); + } + + public int onClientExecuteRequest(GameEntity entity, int param1, int param2, int param3) { + Grasscutter.getLogger().debug("Request on {}, {}: {}", entity.getGroupId(), param1, entity.getPosition().toString()); + LuaValue value = callControllerScriptFunc(entity, "OnClientExecuteReq", LuaValue.valueOf(param1), LuaValue.valueOf(param2), LuaValue.valueOf(param3)); + if(value.isint() && value.toint() == 1) return 1; + + return 0; + } + + // TODO actual execution should probably be handle by EntityControllerScriptManager + private LuaValue callControllerScriptFunc(GameEntity entity, String funcName, LuaValue arg1) { return callControllerScriptFunc(entity, funcName, arg1, LuaValue.NIL, LuaValue.NIL); } + private LuaValue callControllerScriptFunc(GameEntity entity, String funcName, LuaValue arg1, LuaValue arg2) { return callControllerScriptFunc(entity, funcName, arg1, arg2, LuaValue.NIL); } + private LuaValue callControllerScriptFunc(GameEntity entity, String funcName, LuaValue arg1, LuaValue arg2, LuaValue arg3) { + LuaValue funcLua = null; + if (funcName != null && !funcName.isEmpty()) { + funcLua = (LuaValue) entityControllerBindings.get(funcName); + } + + LuaValue ret = LuaValue.ONE; + + if (funcLua != null) { + try { + ScriptLoader.getScriptLib().setCurrentEntity(entity); + ret = funcLua.invoke(new LuaValue[]{ScriptLoader.getScriptLibLua(), arg1, arg2, arg3}).arg1(); + }catch (LuaError error) { + ScriptLib.logger.error("[LUA] call function failed in gadget {} with {} {} {},{}", entity.getEntityTypeId(), funcName, arg1, arg2, arg3, error); + ret = LuaValue.valueOf(-1); + } + } else if(funcName != null && !funcName.equals("OnTimer")) { + ScriptLib.logger.error("[LUA] unknown func in gadget {} with {} {} {} {}", entity.getEntityTypeId(), funcName, arg1, arg2, arg3); + } + return ret; + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAddCustomTeamReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAddCustomTeamReq.java new file mode 100644 index 000000000..1f056ec0f --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAddCustomTeamReq.java @@ -0,0 +1,14 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.server.game.GameSession; + +@Opcodes(PacketOpcodes.AddCustomTeamReq) +public class HandlerAddCustomTeamReq extends PacketHandler { + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + session.getPlayer().getTeamManager().addNewCustomTeam(); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerChangeHomeBgmReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerChangeHomeBgmReq.java new file mode 100644 index 000000000..0ce35064e --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerChangeHomeBgmReq.java @@ -0,0 +1,26 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.Unk2700BEDLIGJANCJClientReq; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketChangeHomeBgmNotify; +import emu.grasscutter.server.packet.send.PacketChangeHomeBgmRsp; + +@Opcodes(PacketOpcodes.Unk2700_BEDLIGJANCJ_ClientReq) +public class HandlerChangeHomeBgmReq extends PacketHandler { + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + var req = Unk2700BEDLIGJANCJClientReq.Unk2700_BEDLIGJANCJ_ClientReq.parseFrom(payload); + + int homeBgmId = req.getUnk2700BJHAMKKECEI(); + var home = session.getPlayer().getHome(); + + home.getHomeSceneItem(session.getPlayer().getSceneId()).setHomeBgmId(homeBgmId); + home.save(); + + session.send(new PacketChangeHomeBgmNotify(homeBgmId)); + session.send(new PacketChangeHomeBgmRsp()); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerCheckUgcStateReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerCheckUgcStateReq.java new file mode 100644 index 000000000..174a022d9 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerCheckUgcStateReq.java @@ -0,0 +1,23 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.CheckUgcStateReqOuterClass.CheckUgcStateReq; +import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketCheckUgcStateRsp; +import lombok.val; + +@Opcodes(PacketOpcodes.CheckUgcStateReq) +public class HandlerCheckUgcStateReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + val req = CheckUgcStateReq.parseFrom(payload); + + session.send(new PacketCheckUgcStateRsp(Retcode.RET_SUCC)); + + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerCheckUgcUpdateReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerCheckUgcUpdateReq.java new file mode 100644 index 000000000..59f034180 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerCheckUgcUpdateReq.java @@ -0,0 +1,20 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.CheckUgcUpdateReqOuterClass.CheckUgcUpdateReq; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketCheckUgcUpdateRsp; + +@Opcodes(PacketOpcodes.CheckUgcUpdateReq) +public class HandlerCheckUgcUpdateReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + var req = CheckUgcUpdateReq.parseFrom(payload); + + session.send(new PacketCheckUgcUpdateRsp(req.getUgcType())); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerDungeonPlayerDieReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerDungeonPlayerDieReq.java new file mode 100644 index 000000000..03996439b --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerDungeonPlayerDieReq.java @@ -0,0 +1,28 @@ + +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.game.player.Player; +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.DungeonPlayerDieReqOuterClass.DungeonPlayerDieReq; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketDungeonPlayerDieRsp; +import lombok.val; + +@Opcodes(PacketOpcodes.DungeonPlayerDieReq) +public class HandlerDungeonPlayerDieReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + DungeonPlayerDieReq req = DungeonPlayerDieReq.parseFrom(payload); + + Player player = session.getPlayer(); + + boolean result = player.getScene().respawnPlayer(player); + + player.sendPacket(new PacketDungeonPlayerDieRsp(result ? Retcode.RET_SUCC : Retcode.RET_FAIL)); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerDungeonSlipRevivePointActivateReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerDungeonSlipRevivePointActivateReq.java new file mode 100644 index 000000000..211029fcf --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerDungeonSlipRevivePointActivateReq.java @@ -0,0 +1,26 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.DungeonSlipRevivePointActivateReqOuterClass.DungeonSlipRevivePointActivateReq; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketDungeonSlipRevivePointActivateRsp; + +@Opcodes(PacketOpcodes.DungeonSlipRevivePointActivateReq) +public class HandlerDungeonSlipRevivePointActivateReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + var req = DungeonSlipRevivePointActivateReq.parseFrom(payload); + var dungeonManager = session.getPlayer().getScene().getDungeonManager(); + + boolean success = false; + if (dungeonManager != null) { + success = dungeonManager.activateRespawnPoint(req.getSlipRevivePointId()); + } + + session.send(new PacketDungeonSlipRevivePointActivateRsp(success, req.getSlipRevivePointId())); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerDungeonWayPointActivateReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerDungeonWayPointActivateReq.java new file mode 100644 index 000000000..cbea5854d --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerDungeonWayPointActivateReq.java @@ -0,0 +1,26 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.DungeonWayPointActivateReqOuterClass.DungeonWayPointActivateReq; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketDungeonWayPointActivateRsp; + +@Opcodes(PacketOpcodes.DungeonWayPointActivateReq) +public class HandlerDungeonWayPointActivateReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + var req = DungeonWayPointActivateReq.parseFrom(payload); + var dungeonManager = session.getPlayer().getScene().getDungeonManager(); + + boolean success = false; + if(dungeonManager != null){ + success = dungeonManager.activateRespawnPoint(req.getWayPointId()); + } + + session.send(new PacketDungeonWayPointActivateRsp(success, req.getWayPointId())); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerEnterTrialAvatarActivityDungeonReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerEnterTrialAvatarActivityDungeonReq.java new file mode 100644 index 000000000..c93f9c651 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerEnterTrialAvatarActivityDungeonReq.java @@ -0,0 +1,33 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.game.activity.trialavatar.TrialAvatarActivityHandler; +import emu.grasscutter.game.props.ActivityType; +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.server.packet.send.PacketEnterTrialAvatarActivityDungeonRsp; +import emu.grasscutter.server.packet.send.PacketScenePlayerLocationNotify; +import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode; +import emu.grasscutter.net.proto.EnterTrialAvatarActivityDungeonReqOuterClass.EnterTrialAvatarActivityDungeonReq; +import emu.grasscutter.server.game.GameSession; +import lombok.val; + +@Opcodes(PacketOpcodes.EnterTrialAvatarActivityDungeonReq) +public class HandlerEnterTrialAvatarActivityDungeonReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + val req = EnterTrialAvatarActivityDungeonReq.parseFrom(payload); + + val handler = session.getPlayer().getActivityManager() + .getActivityHandlerAs(ActivityType.NEW_ACTIVITY_TRIAL_AVATAR, TrialAvatarActivityHandler.class); + + boolean result = handler.isPresent() && handler.get().enterTrialDungeon(session.getPlayer(), req.getTrialAvatarIndexId(), req.getEnterPointId()); + + session.getPlayer().sendPacket(new PacketEnterTrialAvatarActivityDungeonRsp( + req.getActivityId(), + req.getTrialAvatarIndexId(), + result)); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerExecuteGadgetLuaReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerExecuteGadgetLuaReq.java new file mode 100644 index 000000000..fcaccbdf6 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerExecuteGadgetLuaReq.java @@ -0,0 +1,30 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.game.entity.EntityGadget; +import emu.grasscutter.game.entity.GameEntity; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.ExecuteGadgetLuaReqOuterClass.ExecuteGadgetLuaReq; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketExecuteGadgetLuaRsp; + +@Opcodes(PacketOpcodes.ExecuteGadgetLuaReq) +public class HandlerExecuteGadgetLuaReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + ExecuteGadgetLuaReq req = ExecuteGadgetLuaReq.parseFrom(payload); + + Player player = session.getPlayer(); + GameEntity entity = player.getScene().getEntities().get(req.getSourceEntityId()); + + int result = 1; + if(entity instanceof EntityGadget gadget) result = gadget.onClientExecuteRequest(req.getParam1(), req.getParam2(), req.getParam3()); + + player.sendPacket(new PacketExecuteGadgetLuaRsp(result)); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerFireWorkReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerFireWorkReq.java new file mode 100644 index 000000000..aa7ea26e2 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerFireWorkReq.java @@ -0,0 +1,21 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.FireWorkReqOuterClass; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketFireworkNotify; +import emu.grasscutter.server.packet.send.PacketFireworkRsp; + +@Opcodes(PacketOpcodes.FireworkReq) +public class HandlerFireWorkReq extends PacketHandler { + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + + var req + = FireWorkReqOuterClass.FireWorkReq.parseFrom(payload); + session.send(new PacketFireworkNotify(req.getFireWorkData())); + session.send(new PacketFireworkRsp()); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerFireworkSetReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerFireworkSetReq.java new file mode 100644 index 000000000..ade4f4ced --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerFireworkSetReq.java @@ -0,0 +1,23 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.FireworkSetReqOuterClass; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketFireworkSetNotify; +import emu.grasscutter.server.packet.send.PacketFireworkSetRsp; + +@Opcodes(PacketOpcodes.FireworkSetReq) +public class HandlerFireworkSetReq extends PacketHandler { + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + + var req + = FireworkSetReqOuterClass.FireworkSetReq.parseFrom(payload); + + + session.send(new PacketFireworkSetNotify(req.getData())); + session.send(new PacketFireworkSetRsp()); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetUgcBriefInfoReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetUgcBriefInfoReq.java new file mode 100644 index 000000000..4d7656ea7 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetUgcBriefInfoReq.java @@ -0,0 +1,33 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.game.activity.musicgame.MusicGameBeatmap; +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.GetUgcBriefInfoReqOuterClass; +import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode; +import emu.grasscutter.net.proto.UgcTypeOuterClass; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketGetUgcBriefInfoRsp; + +@Opcodes(PacketOpcodes.GetUgcBriefInfoReq) +public class HandlerGetUgcBriefInfoReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + var req = GetUgcBriefInfoReqOuterClass.GetUgcBriefInfoReq.parseFrom(payload); + + if(req.getUgcType() == UgcTypeOuterClass.UgcType.UGC_TYPE_MUSIC_GAME){ + var musicGameBeatmap = MusicGameBeatmap.getByShareId(req.getUgcGuid()); + + if(musicGameBeatmap != null){ + session.send(new PacketGetUgcBriefInfoRsp(musicGameBeatmap.toBriefProto().build(), req.getUgcType())); + return; + } + } + + session.send(new PacketGetUgcBriefInfoRsp(Retcode.RET_UGC_BRIEF_NOT_FOUND, req.getUgcType())); + + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetUgcReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetUgcReq.java new file mode 100644 index 000000000..ff6ddd02b --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetUgcReq.java @@ -0,0 +1,48 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.game.activity.musicgame.MusicGameBeatmap; +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.RetcodeOuterClass; +import emu.grasscutter.net.proto.UgcTypeOuterClass; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketGetUgcRsp; +import emu.grasscutter.net.proto.GetUgcReqOuterClass.GetUgcReq; +import lombok.val; + +@Opcodes(PacketOpcodes.GetUgcReq) +public class HandlerGetUgcReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + val req = GetUgcReq.parseFrom(payload); + + PacketGetUgcRsp rsp = null; + + if(req.getUgcType() == UgcTypeOuterClass.UgcType.UGC_TYPE_MUSIC_GAME) { + val musicGameBeatmap = MusicGameBeatmap.getByShareId(req.getUgcGuid()); + + if (musicGameBeatmap != null) { + rsp = new PacketGetUgcRsp( + musicGameBeatmap.toBriefProto().build(), + musicGameBeatmap.toProto(), + req + ); + } else { + rsp = new PacketGetUgcRsp( + RetcodeOuterClass.Retcode.RET_UGC_DATA_NOT_FOUND, + req + ); + } + }else { + rsp = new PacketGetUgcRsp( + RetcodeOuterClass.Retcode.RET_UGC_DISABLED, + req + ); + } + + session.send(rsp); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeUnknown2Req.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeUnknown2Req.java new file mode 100644 index 000000000..467449067 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeUnknown2Req.java @@ -0,0 +1,20 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketHomeUnknown2Rsp; + +@Opcodes(PacketOpcodes.Unk2700_ACILPONNGGK_ClientReq) +public class HandlerHomeUnknown2Req extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + /* + * This packet is about the edit mode + */ + session.send(new PacketHomeUnknown2Rsp()); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerQuestCreateEntityReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerQuestCreateEntityReq.java new file mode 100644 index 000000000..ff2c93e04 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerQuestCreateEntityReq.java @@ -0,0 +1,63 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.excels.GadgetData; +import emu.grasscutter.data.excels.ItemData; +import emu.grasscutter.data.excels.MonsterData; +import emu.grasscutter.game.entity.*; +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketQuestCreateEntityRsp; +import emu.grasscutter.utils.Position; +import lombok.val; +import emu.grasscutter.net.proto.QuestCreateEntityReqOuterClass.QuestCreateEntityReq; + +@Opcodes(PacketOpcodes.QuestCreateEntityReq) +public class HandlerQuestCreateEntityReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + val req = QuestCreateEntityReq.parseFrom(payload); + val entity = req.getEntity(); + val scene = session.getPlayer().getWorld().getSceneById(entity.getSceneId()); + + val pos = new Position(entity.getPos()); + val rot = new Position(entity.getRot()); + GameEntity gameEntity = null; + switch (entity.getEntityCase()){ + case GADGET_ID -> { + val gadgetId = entity.getGadgetId(); + val gadgetInfo = entity.getGadget(); + GadgetData gadgetData = GameData.getGadgetDataMap().get(gadgetId); + gameEntity = switch (gadgetData.getType()){ + case Vehicle -> new EntityVehicle(scene, session.getPlayer(), gadgetId, 0, pos, rot); + default -> new EntityGadget(scene, gadgetId, pos, rot); + }; + } + case ITEM_ID -> { + val itemId = entity.getItemId(); + ItemData itemData = GameData.getItemDataMap().get(itemId); + gameEntity = new EntityItem(scene, null, itemData, pos, 1, true); + } + case MONSTER_ID -> { + val monsterId = entity.getMonsterId(); + val level = entity.getLevel(); + MonsterData monsterData = GameData.getMonsterDataMap().get(monsterId); + gameEntity = new EntityMonster(scene, monsterData, pos, level); + } + case NPC_ID -> { + } + } + + if(gameEntity != null){ + scene.addEntity(gameEntity); + } + + val createdEntityId = gameEntity!=null ? gameEntity.getId() : -1; + + session.send(new PacketQuestCreateEntityRsp(createdEntityId, req)); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerQuestDestroyEntityReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerQuestDestroyEntityReq.java new file mode 100644 index 000000000..1e0f9b703 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerQuestDestroyEntityReq.java @@ -0,0 +1,27 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketQuestDestroyEntityRsp; +import lombok.val; +import emu.grasscutter.net.proto.QuestDestroyEntityReqOuterClass.QuestDestroyEntityReq; + +@Opcodes(PacketOpcodes.QuestDestroyEntityReq) +public class HandlerQuestDestroyEntityReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + val req = QuestDestroyEntityReq.parseFrom(payload); + val scene = session.getPlayer().getWorld().getSceneById(req.getSceneId()); + val entity = scene.getEntityById(req.getEntityId()); + + if(entity!=null){ + scene.removeEntity(entity); + } + + session.send(new PacketQuestDestroyEntityRsp(entity!=null, req)); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerQuestDestroyNpcReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerQuestDestroyNpcReq.java new file mode 100644 index 000000000..4e334724c --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerQuestDestroyNpcReq.java @@ -0,0 +1,20 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.game.entity.EntityNPC; +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketQuestDestroyNpcRsp; +import lombok.val; +import emu.grasscutter.net.proto.QuestDestroyNpcReqOuterClass.QuestDestroyNpcReq; + +@Opcodes(PacketOpcodes.QuestDestroyNpcReq) +public class HandlerQuestDestroyNpcReq extends PacketHandler { + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + val req = QuestDestroyNpcReq.parseFrom(payload); + + session.send(new PacketQuestDestroyNpcRsp(req.getNpcId(), req.getParentQuestId(), 0)); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerQuestTransmitReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerQuestTransmitReq.java new file mode 100644 index 000000000..67979a344 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerQuestTransmitReq.java @@ -0,0 +1,32 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.game.quest.GameMainQuest; +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketQuestTransmitRsp; +import emu.grasscutter.utils.Position; +import emu.grasscutter.net.proto.QuestTransmitReqOuterClass.QuestTransmitReq; + +import java.util.List; +import java.util.ArrayList; +import lombok.val; + +@Opcodes(PacketOpcodes.QuestTransmitReq) +public class HandlerQuestTransmitReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + val req = QuestTransmitReq.parseFrom(payload); + GameMainQuest mainQuest = session.getPlayer().getQuestManager().getMainQuestById(req.getQuestId() / 100); + List posAndRot = new ArrayList<>(); + boolean result = false; + if(mainQuest.hasTeleportPostion(req.getQuestId(), posAndRot)){ + int sceneId = GameData.getTeleportDataMap().get(req.getQuestId()).getTransmit_points().get(0).getScene_id(); + result = session.getPlayer().getWorld().transferPlayerToScene(session.getPlayer(), sceneId, posAndRot.get(0)); + } + session.send(new PacketQuestTransmitRsp(result, req)); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerReceivedTrialAvatarActivityRewardReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerReceivedTrialAvatarActivityRewardReq.java new file mode 100644 index 000000000..b0ce377e0 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerReceivedTrialAvatarActivityRewardReq.java @@ -0,0 +1,32 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.game.activity.trialavatar.TrialAvatarActivityHandler; +import emu.grasscutter.game.props.ActivityType; +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode; +import emu.grasscutter.net.proto.ReceivedTrialAvatarActivityRewardReqOuterClass.ReceivedTrialAvatarActivityRewardReq; +import emu.grasscutter.server.packet.send.PacketReceivedTrialAvatarActivityRewardRsp; + +import lombok.val; + +@Opcodes(PacketOpcodes.ReceivedTrialAvatarActivityRewardReq) +public class HandlerReceivedTrialAvatarActivityRewardReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + val req = ReceivedTrialAvatarActivityRewardReq.parseFrom(payload); + val player = session.getPlayer(); + val handler = player.getActivityManager().getActivityHandlerAs(ActivityType.NEW_ACTIVITY_TRIAL_AVATAR, TrialAvatarActivityHandler.class); + + boolean result = handler.isPresent() && handler.get().getReward(player, req.getTrialAvatarIndexId()); + + session.getPlayer().sendPacket(new PacketReceivedTrialAvatarActivityRewardRsp( + 5002, // trial activity id + req.getTrialAvatarIndexId(), + result)); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerRemoveCustomTeamReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerRemoveCustomTeamReq.java new file mode 100644 index 000000000..9ea1fe4c0 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerRemoveCustomTeamReq.java @@ -0,0 +1,16 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.RemoveCustomTeamReqOuterClass.RemoveCustomTeamReq; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.server.game.GameSession; + +@Opcodes(PacketOpcodes.RemoveCustomTeamReq) +public class HandlerRemoveCustomTeamReq extends PacketHandler { + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + RemoveCustomTeamReq req = RemoveCustomTeamReq.parseFrom(payload); + session.getPlayer().getTeamManager().removeCustomTeam(req.getId()); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSaveUgcReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSaveUgcReq.java new file mode 100644 index 000000000..619394a58 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSaveUgcReq.java @@ -0,0 +1,84 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.database.DatabaseHelper; +import emu.grasscutter.game.activity.musicgame.MusicGameActivityHandler; +import emu.grasscutter.game.activity.musicgame.MusicGameBeatmap; +import emu.grasscutter.game.activity.musicgame.MusicGamePlayerData; +import emu.grasscutter.game.props.ActivityType; +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.RetcodeOuterClass; +import emu.grasscutter.net.proto.SaveUgcReqOuterClass; +import emu.grasscutter.net.proto.UgcTypeOuterClass; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketActivityInfoNotify; +import emu.grasscutter.server.packet.send.PacketMusicGameCreateBeatmapRsp; +import emu.grasscutter.utils.Utils; +import lombok.val; + +import java.util.Objects; + +@Opcodes(PacketOpcodes.SaveUgcReq) +public class HandlerSaveUgcReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + val req = SaveUgcReqOuterClass.SaveUgcReq.parseFrom(payload); + + // We only support music game user generated content + if(req.getUgcType() != UgcTypeOuterClass.UgcType.UGC_TYPE_MUSIC_GAME){ + session.send(new PacketMusicGameCreateBeatmapRsp(RetcodeOuterClass.Retcode.RET_UGC_DISABLED, req.getUgcType())); + return; + } + val briefInfo = req.getMusicBriefInfo(); + + val musicGameBeatmap = MusicGameBeatmap.of() + .musicId(briefInfo.getMusicId()) + .musicNoteCount(briefInfo.getNoteCount()) + .savePosition(briefInfo.getSaveIdx()) + .savePageType(briefInfo.getSavePageType()) + .version(briefInfo.getVersion()) + .afterNoteList(briefInfo.getAfterNoteListList()) + .beforeNoteList(briefInfo.getBeforeNoteListList()) + .timeLineEditTime(briefInfo.getTimeLineEditTime()) + .publishTime(briefInfo.getPublishTime()) + .realTimeEditTime(briefInfo.getRealTimeEditTime()) + .maxScore(briefInfo.getMaxScore()) + .authorUid(session.getPlayer().getUid()) + .beatmap(MusicGameBeatmap.parse(req.getMusicRecord().getMusicTrackListList())) + .createTime(Utils.getCurrentSeconds()) + .build(); + + musicGameBeatmap.save(); + + val playerData = session.getPlayer().getActivityManager().getPlayerActivityDataByActivityType(ActivityType.NEW_ACTIVITY_MUSIC_GAME); + if(playerData.isEmpty()){ + session.send(new PacketMusicGameCreateBeatmapRsp(RetcodeOuterClass.Retcode.RET_UGC_DATA_NOT_FOUND, req.getUgcType())); + return; + } + + val handler = (MusicGameActivityHandler) playerData.get().getActivityHandler(); + val musicGamePlayerData = handler.getMusicGamePlayerData(playerData.get()); + + val oldBeatmap = musicGamePlayerData.getPersonalCustomBeatmapRecord().values().stream() + .map(MusicGamePlayerData.CustomBeatmapRecord::getMusicShareId) + .map(DatabaseHelper::getMusicGameBeatmap) + .filter(Objects::nonNull) + .filter(item -> item.getAuthorUid() == session.getPlayer().getUid()) + .filter(item -> item.getMusicId() == req.getMusicBriefInfo().getMusicId()) + .filter(item -> item.getSavePosition() == req.getMusicBriefInfo().getSaveIdx()) + .findFirst(); + + // delete old beatmap for player + // the old beatmap is still in database so that others can still play. + oldBeatmap.ifPresent(i -> handler.removePersonalBeatmap(playerData.get(), i)); + + // link this beatmap to player's personal data + handler.addPersonalBeatmap(playerData.get(), musicGameBeatmap); + + session.send(new PacketActivityInfoNotify(handler.toProto(playerData.get(), session.getPlayer().getActivityManager().getConditionExecutor()))); + session.send(new PacketMusicGameCreateBeatmapRsp(musicGameBeatmap.getMusicShareId(), req.getUgcType())); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketAddCustomTeamRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketAddCustomTeamRsp.java new file mode 100644 index 000000000..54cc022c9 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketAddCustomTeamRsp.java @@ -0,0 +1,22 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode; +import emu.grasscutter.net.proto.AddCustomTeamRspOuterClass.AddCustomTeamRsp; + +public class PacketAddCustomTeamRsp extends BasePacket { + public PacketAddCustomTeamRsp(Retcode retcode) { + super(PacketOpcodes.AddCustomTeamRsp); + + AddCustomTeamRsp proto = AddCustomTeamRsp.newBuilder() + .setRetcode(retcode.getNumber()) + .build(); + + this.setData(proto); + } + + public PacketAddCustomTeamRsp() { + this(Retcode.RET_SUCC); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketAddNoGachaAvatarCardNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketAddNoGachaAvatarCardNotify.java new file mode 100644 index 000000000..f2b73af96 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketAddNoGachaAvatarCardNotify.java @@ -0,0 +1,40 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.avatar.Avatar; +import emu.grasscutter.game.inventory.GameItem; +import emu.grasscutter.game.props.ActionReason; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.AddNoGachaAvatarCardNotifyOuterClass.AddNoGachaAvatarCardNotify; + +public class PacketAddNoGachaAvatarCardNotify extends BasePacket { + + public PacketAddNoGachaAvatarCardNotify(Avatar avatar, ActionReason reason, GameItem item) { + super(PacketOpcodes.AddNoGachaAvatarCardNotify, true); + + AddNoGachaAvatarCardNotify proto = AddNoGachaAvatarCardNotify.newBuilder() + .setAvatarId(avatar.getAvatarId()) + .setReason(reason.getValue()) + .setInitialLevel(1) + .setItemId(item.getItemId()) + .setInitialPromoteLevel(0) + .build(); + + this.setData(proto); + } + + public PacketAddNoGachaAvatarCardNotify(int avatarId, ActionReason reason, GameItem item) { + super(PacketOpcodes.AddNoGachaAvatarCardNotify, true); + + AddNoGachaAvatarCardNotify proto = AddNoGachaAvatarCardNotify.newBuilder() + .setAvatarId(avatarId) + .setReason(reason.getValue()) + .setInitialLevel(1) + .setItemId(item.getItemId()) + .setInitialPromoteLevel(0) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarDelNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarDelNotify.java new file mode 100644 index 000000000..1c4229161 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarDelNotify.java @@ -0,0 +1,20 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.AvatarDelNotifyOuterClass.AvatarDelNotify; + +import java.util.List; + +public class PacketAvatarDelNotify extends BasePacket { + + public PacketAvatarDelNotify(List avatarGuidList) { + super(PacketOpcodes.AvatarDelNotify); + + AvatarDelNotify proto = AvatarDelNotify.newBuilder() + .addAllAvatarGuidList(avatarGuidList) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketBeginCameraSceneLookNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketBeginCameraSceneLookNotify.java new file mode 100644 index 000000000..c1e2133fb --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketBeginCameraSceneLookNotify.java @@ -0,0 +1,61 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.BeginCameraSceneLookNotifyOuterClass.BeginCameraSceneLookNotify; +import emu.grasscutter.utils.Position; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.val; + +import java.util.ArrayList; +import java.util.Collection; + +public class PacketBeginCameraSceneLookNotify extends BasePacket { + + public PacketBeginCameraSceneLookNotify(CameraSceneLookNotify parameters) { + super(PacketOpcodes.BeginCameraSceneLookNotify); + val builder = BeginCameraSceneLookNotify.newBuilder() + .setLookPos(parameters.lookPos.toProto()) + .setFollowPos(parameters.followPos.toProto()) + .setDuration(parameters.duration) + .setIsAllowInput(parameters.isAllowInput) + .setIsSetFollowPos(parameters.setFollowPos) + .setIsSetScreenXy(parameters.isScreenXY) + .setIsRecoverKeepCurrent(parameters.recoverKeepCurrent) + .setIsChangePlayMode(parameters.isChangePlayMode) + .setScreenY(parameters.screenY) + .setScreenX(parameters.screenX) + .setIsForce(parameters.isForce) + .setIsForce(parameters.isForceWalk) + .setEntityId(parameters.entityId) + .addAllOtherParams(parameters.otherParams); + this.setData(builder); + } + + // TODO check default values + // todo find missing field usages: + // enum Unk2700_HIAKNNCKHJB (Unk2700_LNCHDDOOECD) + // Unk3000_MNLLCJMPMNH (uint32) + // Unk2700_DHAHEKOGHBJ (float) + // Unk3000_IEFIKMHCKDH (uint32) + // Unk3000_OGCLMFFADBD (float) + + @Data @NoArgsConstructor + public static class CameraSceneLookNotify{ + Position lookPos = new Position(); + Position followPos = new Position(); + float duration = 0.0f; + boolean isAllowInput = true; + boolean setFollowPos = false; + boolean isScreenXY = false; + boolean recoverKeepCurrent = true; + boolean isForceWalk = false; + boolean isForce = false; + boolean isChangePlayMode = false; + float screenY = 0.0f; + float screenX = 0.0f; + int entityId = 0; + Collection otherParams = new ArrayList<>(0); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketChangeHomeBgmNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketChangeHomeBgmNotify.java new file mode 100644 index 000000000..f61b05d25 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketChangeHomeBgmNotify.java @@ -0,0 +1,17 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.Unk2700FJEHHCPCBLGServerNotify; + +public class PacketChangeHomeBgmNotify extends BasePacket { + public PacketChangeHomeBgmNotify(int homeBgmId) { + super(PacketOpcodes.Unk2700_FJEHHCPCBLG_ServerNotify); + + var notify = Unk2700FJEHHCPCBLGServerNotify.Unk2700_FJEHHCPCBLG_ServerNotify.newBuilder() + .setUnk2700BJHAMKKECEI(homeBgmId) + .build(); + + this.setData(notify); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketChangeHomeBgmRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketChangeHomeBgmRsp.java new file mode 100644 index 000000000..19fb43c92 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketChangeHomeBgmRsp.java @@ -0,0 +1,17 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.Unk2700OGHMHELMBNNServerRsp; + +public class PacketChangeHomeBgmRsp extends BasePacket { + public PacketChangeHomeBgmRsp() { + super(PacketOpcodes.Unk2700_OGHMHELMBNN_ServerRsp); + + var rsp = Unk2700OGHMHELMBNNServerRsp.Unk2700_OGHMHELMBNN_ServerRsp.newBuilder() + .setRetcode(0) + .build(); + + this.setData(rsp); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketCheckUgcStateRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketCheckUgcStateRsp.java new file mode 100644 index 000000000..3b9884ea3 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketCheckUgcStateRsp.java @@ -0,0 +1,18 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.CheckUgcStateRspOuterClass.CheckUgcStateRsp; +import emu.grasscutter.net.proto.RetcodeOuterClass; + +public class PacketCheckUgcStateRsp extends BasePacket { + + public PacketCheckUgcStateRsp(RetcodeOuterClass.Retcode ret) { + super(PacketOpcodes.CheckUgcStateRsp); + + this.setData(CheckUgcStateRsp.newBuilder() + .setRetcode(ret.getNumber()) + ); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketCheckUgcUpdateRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketCheckUgcUpdateRsp.java new file mode 100644 index 000000000..87b491df4 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketCheckUgcUpdateRsp.java @@ -0,0 +1,19 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.CheckUgcUpdateRspOuterClass.CheckUgcUpdateRsp; +import emu.grasscutter.net.proto.UgcTypeOuterClass.UgcType; + +public class PacketCheckUgcUpdateRsp extends BasePacket { + + public PacketCheckUgcUpdateRsp(UgcType ugcType) { + super(PacketOpcodes.CheckUgcUpdateRsp); + + var proto = CheckUgcUpdateRsp.newBuilder(); + + proto.setUgcType(ugcType); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketCloseCommonTipsNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketCloseCommonTipsNotify.java new file mode 100644 index 000000000..663aeb314 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketCloseCommonTipsNotify.java @@ -0,0 +1,13 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.CloseCommonTipsNotifyOuterClass.CloseCommonTipsNotify; + +public class PacketCloseCommonTipsNotify extends BasePacket { + + public PacketCloseCommonTipsNotify() { + super(PacketOpcodes.CloseCommonTipsNotify); + this.setData(CloseCommonTipsNotify.newBuilder().build()); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketCustomTeamListNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketCustomTeamListNotify.java new file mode 100644 index 000000000..182adb0fb --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketCustomTeamListNotify.java @@ -0,0 +1,26 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.player.Player; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.CustomTeamListNotifyOuterClass.CustomTeamListNotify; + +public class PacketCustomTeamListNotify extends BasePacket { + public PacketCustomTeamListNotify(Player player) { + super(PacketOpcodes.CustomTeamListNotify); + + CustomTeamListNotify.Builder proto = CustomTeamListNotify.newBuilder(); + + // Add the id list for custom teams. + for (int id : player.getTeamManager().getTeams().keySet()) { + if (id > 4) { + proto.addCustomTeamIds(id); + } + } + + // Add the avatar lists for all the teams the player has. + player.getTeamManager().getTeams().forEach((id, teamInfo) -> proto.putAvatarTeamMap(id, teamInfo.toProto(player))); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketCutsceneBeginNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketCutsceneBeginNotify.java new file mode 100644 index 000000000..2ccb7032a --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketCutsceneBeginNotify.java @@ -0,0 +1,16 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.CutSceneBeginNotifyOuterClass.CutSceneBeginNotify; + +public class PacketCutsceneBeginNotify extends BasePacket { + + public PacketCutsceneBeginNotify(int cutsceneId) { + super(PacketOpcodes.CutSceneBeginNotify); + + setData(CutSceneBeginNotify.newBuilder() + .setCutsceneId(cutsceneId)); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketDelQuestNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketDelQuestNotify.java new file mode 100644 index 000000000..c11a10b6c --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketDelQuestNotify.java @@ -0,0 +1,21 @@ +package emu.grasscutter.server.packet.send; + +import java.util.List; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.QuestDelNotifyOuterClass.QuestDelNotify; + +public class PacketDelQuestNotify extends BasePacket { + + public PacketDelQuestNotify(int questId) { + super(PacketOpcodes.QuestDelNotify); + + QuestDelNotify proto = QuestDelNotify.newBuilder() + .setQuestId(questId) + .build(); + + this.setData(proto); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonPlayerDieNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonPlayerDieNotify.java new file mode 100644 index 000000000..d98e576f6 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonPlayerDieNotify.java @@ -0,0 +1,25 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType; +import emu.grasscutter.net.proto.DungeonPlayerDieNotifyOuterClass.DungeonPlayerDieNotify; + +public class PacketDungeonPlayerDieNotify extends BasePacket { + + public PacketDungeonPlayerDieNotify(PlayerDieType playerDieType, int killerId, int dungeonId, int waitTime, int reviveCount, boolean isGadget) { + super(PacketOpcodes.DungeonPlayerDieNotify); + + DungeonPlayerDieNotify.Builder proto = DungeonPlayerDieNotify.newBuilder() + .setDieType(playerDieType) + .setReviveCount(reviveCount) + .setWaitTime(waitTime) + .setDungeonId(dungeonId) + .setMurdererEntityId(killerId); + + if(isGadget) proto.setGadgetId(killerId); + else proto.setMonsterId(killerId); + + this.setData(proto.build()); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonPlayerDieRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonPlayerDieRsp.java new file mode 100644 index 000000000..44cf6c641 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonPlayerDieRsp.java @@ -0,0 +1,19 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.DungeonPlayerDieRspOuterClass.DungeonPlayerDieRsp; +import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode; + +public class PacketDungeonPlayerDieRsp extends BasePacket { + + public PacketDungeonPlayerDieRsp(Retcode retcode) { + super(PacketOpcodes.DungeonPlayerDieRsp); + + DungeonPlayerDieRsp proto = DungeonPlayerDieRsp.newBuilder() + .setRetcode(retcode.getNumber()) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonSlipRevivePointActivateRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonSlipRevivePointActivateRsp.java new file mode 100644 index 000000000..2cd679963 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonSlipRevivePointActivateRsp.java @@ -0,0 +1,16 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.DungeonSlipRevivePointActivateRspOuterClass.DungeonSlipRevivePointActivateRsp; +import emu.grasscutter.net.proto.RetcodeOuterClass; + +public class PacketDungeonSlipRevivePointActivateRsp extends BasePacket { + public PacketDungeonSlipRevivePointActivateRsp(boolean success, int pointId) { + super(PacketOpcodes.DungeonSlipRevivePointActivateRsp); + + this.setData(DungeonSlipRevivePointActivateRsp.newBuilder() + .setSlipRevivePointId(pointId) + .setRetcode(success ? RetcodeOuterClass.Retcode.RET_SUCC_VALUE : RetcodeOuterClass.Retcode.RET_FAIL_VALUE)); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonWayPointActivateRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonWayPointActivateRsp.java new file mode 100644 index 000000000..7ec40a92c --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonWayPointActivateRsp.java @@ -0,0 +1,16 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.DungeonWayPointActivateRspOuterClass; +import emu.grasscutter.net.proto.RetcodeOuterClass; + +public class PacketDungeonWayPointActivateRsp extends BasePacket { + public PacketDungeonWayPointActivateRsp(boolean success, int pointId) { + super(PacketOpcodes.DungeonWayPointActivateRsp); + + this.setData(DungeonWayPointActivateRspOuterClass.DungeonWayPointActivateRsp.newBuilder() + .setWayPointId(pointId) + .setRetcode(success ? RetcodeOuterClass.Retcode.RET_SUCC_VALUE : RetcodeOuterClass.Retcode.RET_FAIL_VALUE)); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonWayPointNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonWayPointNotify.java new file mode 100644 index 000000000..29bb640fa --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonWayPointNotify.java @@ -0,0 +1,19 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.DungeonWayPointActivateRspOuterClass; +import emu.grasscutter.net.proto.DungeonWayPointNotifyOuterClass; +import emu.grasscutter.net.proto.RetcodeOuterClass; + +import java.util.Set; + +public class PacketDungeonWayPointNotify extends BasePacket { + public PacketDungeonWayPointNotify(boolean added, Set activePointIds) { + super(PacketOpcodes.DungeonWayPointNotify); + + this.setData(DungeonWayPointNotifyOuterClass.DungeonWayPointNotify.newBuilder() + .addAllActiveWayPointList(activePointIds) + .setIsAdd(added)); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketEndCameraSceneLookNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketEndCameraSceneLookNotify.java new file mode 100644 index 000000000..de8914154 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketEndCameraSceneLookNotify.java @@ -0,0 +1,14 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.EndCameraSceneLookNotifyOuterClass.EndCameraSceneLookNotify; + +public class PacketEndCameraSceneLookNotify extends BasePacket { + + public PacketEndCameraSceneLookNotify() { + super(PacketOpcodes.EndCameraSceneLookNotify); + + this.setData(EndCameraSceneLookNotify.newBuilder()); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketEnterTrialAvatarActivityDungeonRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketEnterTrialAvatarActivityDungeonRsp.java new file mode 100644 index 000000000..1a5744635 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketEnterTrialAvatarActivityDungeonRsp.java @@ -0,0 +1,20 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.EnterTrialAvatarActivityDungeonRspOuterClass.EnterTrialAvatarActivityDungeonRsp; +import emu.grasscutter.net.proto.RetcodeOuterClass; + +public class PacketEnterTrialAvatarActivityDungeonRsp extends BasePacket { + + public PacketEnterTrialAvatarActivityDungeonRsp(int activityId, int trialAvatarIndexId, boolean success) { + this(activityId, trialAvatarIndexId, success ? RetcodeOuterClass.Retcode.RET_SUCC_VALUE : RetcodeOuterClass.Retcode.RET_FAIL_VALUE); + } + public PacketEnterTrialAvatarActivityDungeonRsp(int activityId, int trialAvatarIndexId, int retcodeVal) { + super(PacketOpcodes.EnterTrialAvatarActivityDungeonRsp); + this.setData(EnterTrialAvatarActivityDungeonRsp.newBuilder() + .setActivityId(activityId) + .setTrialAvatarIndexId(trialAvatarIndexId) + .setRetcode(retcodeVal)); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketExecuteGadgetLuaRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketExecuteGadgetLuaRsp.java new file mode 100644 index 000000000..3ae98e150 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketExecuteGadgetLuaRsp.java @@ -0,0 +1,19 @@ + +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.ExecuteGadgetLuaRspOuterClass.ExecuteGadgetLuaRsp; + +public class PacketExecuteGadgetLuaRsp extends BasePacket { + + public PacketExecuteGadgetLuaRsp(int result) { + super(PacketOpcodes.ExecuteGadgetLuaRsp, true); + + ExecuteGadgetLuaRsp proto = ExecuteGadgetLuaRsp.newBuilder() + .setRetcode(result) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketFireworkNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketFireworkNotify.java new file mode 100644 index 000000000..059194b02 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketFireworkNotify.java @@ -0,0 +1,21 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.FireWorkNotifyOuterClass; +import emu.grasscutter.net.proto.FireWorkDataOuterClass; + +public class PacketFireworkNotify extends BasePacket { + + public PacketFireworkNotify(FireWorkDataOuterClass.FireWorkData pinfo) { + super(PacketOpcodes.FireworkNotify); + + var proto + = FireWorkNotifyOuterClass.FireWorkNotify.newBuilder(); + + proto.addFireWorkData(pinfo); + + setData(proto.build()); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketFireworkRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketFireworkRsp.java new file mode 100644 index 000000000..e5c7b3498 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketFireworkRsp.java @@ -0,0 +1,12 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; + +public class PacketFireworkRsp extends BasePacket { + + public PacketFireworkRsp() { + super(PacketOpcodes.FireworkRsp); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketFireworkSetNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketFireworkSetNotify.java new file mode 100644 index 000000000..678573380 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketFireworkSetNotify.java @@ -0,0 +1,21 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.FireworkSetNotifyOuterClass; +import emu.grasscutter.net.proto.FireworkSetDataOuterClass; + +public class PacketFireworkSetNotify extends BasePacket { + + public PacketFireworkSetNotify(FireworkSetDataOuterClass.FireworkSetData notify) { + super(PacketOpcodes.FireworkSetNotify); + + var proto + = FireworkSetNotifyOuterClass.FireworkSetNotify.newBuilder(); + + proto.setCode(1).addData(notify); + + setData(proto.build()); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketFireworkSetRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketFireworkSetRsp.java new file mode 100644 index 000000000..aad309164 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketFireworkSetRsp.java @@ -0,0 +1,13 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; + +public class PacketFireworkSetRsp extends BasePacket { + + public PacketFireworkSetRsp() { + super(PacketOpcodes.FireworkSetRsp); + + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetUgcBriefInfoRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetUgcBriefInfoRsp.java new file mode 100644 index 000000000..a884be7a6 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetUgcBriefInfoRsp.java @@ -0,0 +1,35 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.GetUgcBriefInfoRspOuterClass.GetUgcBriefInfoRsp; +import emu.grasscutter.net.proto.RetcodeOuterClass; +import emu.grasscutter.net.proto.UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo; +import emu.grasscutter.net.proto.UgcTypeOuterClass.UgcType; + +public class PacketGetUgcBriefInfoRsp extends BasePacket { + + public PacketGetUgcBriefInfoRsp(RetcodeOuterClass.Retcode ret, UgcType unknownEnum1) { + super(PacketOpcodes.GetUgcBriefInfoRsp); + + var proto = GetUgcBriefInfoRsp.newBuilder(); + + proto.setRetcode(ret.getNumber()) + .setUgcType(unknownEnum1); + + this.setData(proto); + } + + public PacketGetUgcBriefInfoRsp(UgcMusicBriefInfo briefInfo, UgcType ugcType) { + super(PacketOpcodes.GetUgcBriefInfoRsp); + + var proto = GetUgcBriefInfoRsp.newBuilder(); + + proto.setMusicBriefInfo(briefInfo) + .setUgcType(ugcType); + + this.setData(proto); + } + + +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetUgcRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetUgcRsp.java new file mode 100644 index 000000000..6db0890ec --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetUgcRsp.java @@ -0,0 +1,40 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.GetUgcReqOuterClass.GetUgcReq; +import emu.grasscutter.net.proto.GetUgcRspOuterClass.GetUgcRsp; +import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode; +import emu.grasscutter.net.proto.UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo; +import emu.grasscutter.net.proto.UgcMusicRecordOuterClass.UgcMusicRecord; + +public class PacketGetUgcRsp extends BasePacket { + + public PacketGetUgcRsp(UgcMusicBriefInfo briefInfo, UgcMusicRecord musicRecord, GetUgcReq req) { + super(PacketOpcodes.GetUgcRsp); + + var proto = GetUgcRsp.newBuilder(); + + proto + .setUgcGuid(briefInfo.getUgcGuid()) + .setUgcType(req.getUgcType()) + .setUgcRecordUsageValue(req.getUgcRecordUsageValue()) + .setMusicRecord(musicRecord) + .setMusicBriefInfo(briefInfo); + + this.setData(proto); + } + public PacketGetUgcRsp(Retcode errorCode, GetUgcReq req) { + super(PacketOpcodes.GetUgcRsp); + + var proto = GetUgcRsp.newBuilder(); + + proto + .setUgcGuid(req.getUgcGuid()) + .setUgcType(req.getUgcType()) + .setUgcRecordUsageValue(req.getUgcRecordUsageValue()) + .setRetcode(errorCode.getNumber()); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketHomeUnknown1Notify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeUnknown1Notify.java new file mode 100644 index 000000000..8fdaca533 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeUnknown1Notify.java @@ -0,0 +1,18 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.HomeUnknown1NotifyOuterClass; + +public class PacketHomeUnknown1Notify extends BasePacket { + + public PacketHomeUnknown1Notify(boolean isEnterEditMode) { + super(PacketOpcodes.Unk2700_JDMPECKFGIG_ServerNotify); + + var proto = HomeUnknown1NotifyOuterClass.HomeUnknown1Notify.newBuilder(); + + proto.setIsEnterEditMode(isEnterEditMode); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketHomeUnknown2Rsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeUnknown2Rsp.java new file mode 100644 index 000000000..1aac1f721 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeUnknown2Rsp.java @@ -0,0 +1,12 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; + +public class PacketHomeUnknown2Rsp extends BasePacket { + + public PacketHomeUnknown2Rsp() { + super(PacketOpcodes.Unk2700_KIIOGMKFNNP_ServerRsp); + + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlatformChangeRouteNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlatformChangeRouteNotify.java new file mode 100644 index 000000000..c27275b8f --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlatformChangeRouteNotify.java @@ -0,0 +1,23 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.entity.EntityGadget; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.PlatformChangeRouteNotifyOuterClass.PlatformChangeRouteNotify; +import lombok.val; + +public class PacketPlatformChangeRouteNotify extends BasePacket { + + public PacketPlatformChangeRouteNotify(EntityGadget gadgetEntity) { + super(PacketOpcodes.PlatformChangeRouteNotify); + + val proto = PlatformChangeRouteNotify.newBuilder() + .setEntityId(gadgetEntity.getId()) + .setSceneTime(gadgetEntity.getScene().getSceneTime()) + .setPlatform(gadgetEntity.getPlatformInfo()) + .build(); + + this.setData(proto); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketQuestCreateEntityRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketQuestCreateEntityRsp.java new file mode 100644 index 000000000..e9dd110fc --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketQuestCreateEntityRsp.java @@ -0,0 +1,23 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode; +import emu.grasscutter.net.proto.QuestCreateEntityReqOuterClass.QuestCreateEntityReq; +import emu.grasscutter.net.proto.QuestCreateEntityRspOuterClass.QuestCreateEntityRsp; + +public class PacketQuestCreateEntityRsp extends BasePacket { + + public PacketQuestCreateEntityRsp(int entityId, QuestCreateEntityReq req) { + super(PacketOpcodes.QuestCreateEntityRsp); + + this.setData(QuestCreateEntityRsp.newBuilder() + .setQuestId(req.getQuestId()) + .setEntity(req.getEntity()) + .setParentQuestId(req.getParentQuestId()) + .setIsRewind(req.getIsRewind()) + .setEntityId(entityId).setRetcode( + entityId!=-1 ? Retcode.RET_SUCC_VALUE : Retcode.RET_FAIL_VALUE)); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketQuestDestroyEntityRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketQuestDestroyEntityRsp.java new file mode 100644 index 000000000..7fad0edb1 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketQuestDestroyEntityRsp.java @@ -0,0 +1,21 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode; +import emu.grasscutter.net.proto.QuestDestroyEntityRspOuterClass.QuestDestroyEntityRsp; +import emu.grasscutter.net.proto.QuestDestroyEntityReqOuterClass.QuestDestroyEntityReq; + +public class PacketQuestDestroyEntityRsp extends BasePacket { + + public PacketQuestDestroyEntityRsp(boolean success, QuestDestroyEntityReq req) { + super(PacketOpcodes.QuestDestroyEntityRsp); + + this.setData(QuestDestroyEntityRsp.newBuilder() + .setQuestId(req.getQuestId()) + .setEntityId(req.getEntityId()) + .setSceneId(req.getSceneId()) + .setRetcode(success ? Retcode.RET_SUCC_VALUE : Retcode.RET_FAIL_VALUE)); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketQuestDestroyNpcRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketQuestDestroyNpcRsp.java new file mode 100644 index 000000000..c0c9d1c4a --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketQuestDestroyNpcRsp.java @@ -0,0 +1,20 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.QuestDestroyNpcRspOuterClass.QuestDestroyNpcRsp; + +public class PacketQuestDestroyNpcRsp extends BasePacket { + + public PacketQuestDestroyNpcRsp(int npcId, int parentQuestId, int retCode) { + super(PacketOpcodes.QuestDestroyNpcRsp, true); + + QuestDestroyNpcRsp proto = QuestDestroyNpcRsp.newBuilder() + .setNpcId(npcId) + .setParentQuestId(parentQuestId) + .setRetcode(retCode) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketQuestTransmitRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketQuestTransmitRsp.java new file mode 100644 index 000000000..4a47e1b78 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketQuestTransmitRsp.java @@ -0,0 +1,18 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode; +import emu.grasscutter.net.proto.QuestTransmitRspOuterClass.QuestTransmitRsp; +import emu.grasscutter.net.proto.QuestTransmitReqOuterClass.QuestTransmitReq; + +public class PacketQuestTransmitRsp extends BasePacket { + + public PacketQuestTransmitRsp(boolean result, QuestTransmitReq req) { + super(PacketOpcodes.QuestTransmitRsp); + this.setData(QuestTransmitRsp.newBuilder() + .setQuestId(req.getQuestId()) + .setPointId(req.getPointId()) + .setRetcode(result ? Retcode.RET_SUCC_VALUE : Retcode.RET_FAIL_VALUE)); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketReceivedTrialAvatarActivityRewardRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketReceivedTrialAvatarActivityRewardRsp.java new file mode 100644 index 000000000..783fa25f6 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketReceivedTrialAvatarActivityRewardRsp.java @@ -0,0 +1,21 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode; +import emu.grasscutter.net.proto.ReceivedTrialAvatarActivityRewardRspOuterClass.ReceivedTrialAvatarActivityRewardRsp; + +public class PacketReceivedTrialAvatarActivityRewardRsp extends BasePacket { + + public PacketReceivedTrialAvatarActivityRewardRsp(int activityId, int trialAvatarId, boolean success) { + this(activityId, trialAvatarId, success ? Retcode.RET_SUCC_VALUE : Retcode.RET_FAIL_VALUE); + } + public PacketReceivedTrialAvatarActivityRewardRsp(int activityId, int trialAvatarId, int retcodeVal) { + super(PacketOpcodes.ReceivedTrialAvatarActivityRewardRsp); + this.setData(ReceivedTrialAvatarActivityRewardRsp.newBuilder() + .setActivityId(activityId) + .setTrialAvatarIndexId(trialAvatarId) + .setRetcode(retcodeVal) + .build()); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketRemoveCustomTeamRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketRemoveCustomTeamRsp.java new file mode 100644 index 000000000..bbfa4b429 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketRemoveCustomTeamRsp.java @@ -0,0 +1,23 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode; +import emu.grasscutter.net.proto.RemoveCustomTeamRspOuterClass.RemoveCustomTeamRsp; + +public class PacketRemoveCustomTeamRsp extends BasePacket { + public PacketRemoveCustomTeamRsp(Retcode retcode, int id) { + super(PacketOpcodes.RemoveCustomTeamRsp); + + RemoveCustomTeamRsp proto = RemoveCustomTeamRsp.newBuilder() + .setRetcode(retcode.getNumber()) + .setId(id) + .build(); + + this.setData(proto); + } + + public PacketRemoveCustomTeamRsp(int id) { + this(Retcode.RET_SUCC, id); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneForceLockNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneForceLockNotify.java new file mode 100644 index 000000000..790517a65 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneForceLockNotify.java @@ -0,0 +1,25 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.SceneForceLockNotifyOuterClass.SceneForceLockNotify; +import lombok.val; + +import java.util.Collection; + +public class PacketSceneForceLockNotify extends BasePacket { + public PacketSceneForceLockNotify(Collection locked) { + super(PacketOpcodes.SceneForceLockNotify); + val builder = SceneForceLockNotify.newBuilder() + .addAllForceIdList(locked); + + this.setData(builder); + } + public PacketSceneForceLockNotify(int locked) { + super(PacketOpcodes.SceneForceLockNotify); + val builder = SceneForceLockNotify.newBuilder() + .addForceIdList(locked); + + this.setData(builder); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneForceUnlockNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneForceUnlockNotify.java new file mode 100644 index 000000000..d5be59986 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneForceUnlockNotify.java @@ -0,0 +1,30 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.SceneForceUnlockNotifyOuterClass.SceneForceUnlockNotify; +import lombok.val; + +import java.util.Collection; + +public class PacketSceneForceUnlockNotify extends BasePacket { + public PacketSceneForceUnlockNotify(Collection unlocked, boolean isAdd) { + super(PacketOpcodes.SceneForceUnlockNotify); + + val builder = SceneForceUnlockNotify.newBuilder() + .addAllForceIdList(unlocked) + .setIsAdd(isAdd); + + this.setData(builder); + } + + public PacketSceneForceUnlockNotify(int unlocked, boolean isAdd) { + super(PacketOpcodes.SceneForceUnlockNotify); + + val builder = SceneForceUnlockNotify.newBuilder() + .addForceIdList(unlocked) + .setIsAdd(isAdd); + + this.setData(builder); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketScenePlayerSoundNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketScenePlayerSoundNotify.java new file mode 100644 index 000000000..f32807c79 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketScenePlayerSoundNotify.java @@ -0,0 +1,31 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.utils.Position; +import emu.grasscutter.net.proto.ScenePlayerSoundNotifyOuterClass.ScenePlayerSoundNotify; +import emu.grasscutter.net.proto.ScenePlayerSoundNotifyOuterClass.ScenePlayerSoundNotify.PlaySoundType; +import emu.grasscutter.net.proto.VectorOuterClass.Vector; +import java.util.Objects; + +public class PacketScenePlayerSoundNotify extends BasePacket { + + public PacketScenePlayerSoundNotify(Position playPosition, String soundName, int playType) { + super(PacketOpcodes.ScenePlayerSoundNotify, true); + + ScenePlayerSoundNotify.Builder proto = ScenePlayerSoundNotify.newBuilder(); + if (!Objects.equals(playPosition, null)) { + proto.setPlayPos(Vector.newBuilder() + .setX(playPosition.getX()) + .setY(playPosition.getY()) + .setZ(playPosition.getZ()) + .build()); + } + if (!Objects.equals(soundName, null)) { + proto.setSoundName(soundName); + } + proto.setPlayType(PlaySoundType.forNumber(playType)); + + this.setData(proto.build()); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketShowClientGuideNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketShowClientGuideNotify.java new file mode 100644 index 000000000..68db81974 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketShowClientGuideNotify.java @@ -0,0 +1,17 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.ShowClientGuideNotifyOuterClass.ShowClientGuideNotify; + +public class PacketShowClientGuideNotify extends BasePacket { + + public PacketShowClientGuideNotify(String guideName) { + super(PacketOpcodes.ShowClientGuideNotify, true); + + ShowClientGuideNotify proto = ShowClientGuideNotify.newBuilder() + .setGuideName(guideName) + .build(); + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketShowCommonTipsNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketShowCommonTipsNotify.java new file mode 100644 index 000000000..7cf96abd0 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketShowCommonTipsNotify.java @@ -0,0 +1,17 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.ShowCommonTipsNotifyOuterClass.ShowCommonTipsNotify; + +public class PacketShowCommonTipsNotify extends BasePacket { + + public PacketShowCommonTipsNotify(String title, String content, int closeTime) { + super(PacketOpcodes.ShowCommonTipsNotify); + this.setData(ShowCommonTipsNotify.newBuilder() + .setTitle(title) + .setContent(content) + .setCloseTime(closeTime) + .build()); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketUnlockHomeBgmNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketUnlockHomeBgmNotify.java new file mode 100644 index 000000000..2bd5a7868 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketUnlockHomeBgmNotify.java @@ -0,0 +1,17 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.Unk2700MEBFPBDNPGOServerNotify; + +public class PacketUnlockHomeBgmNotify extends BasePacket { + public PacketUnlockHomeBgmNotify(int homeBgmId) { + super(PacketOpcodes.Unk2700_MEBFPBDNPGO_ServerNotify); + + var notify = Unk2700MEBFPBDNPGOServerNotify.Unk2700_MEBFPBDNPGO_ServerNotify.newBuilder() + .addUnk2700ELJPLMIHNIP(homeBgmId) + .build(); + + this.setData(notify); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketUnlockedHomeBgmNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketUnlockedHomeBgmNotify.java new file mode 100644 index 000000000..ad8c1e7c7 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketUnlockedHomeBgmNotify.java @@ -0,0 +1,24 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.player.Player; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.Unk2700LOHBMOKOPLHServerNotify; + +public class PacketUnlockedHomeBgmNotify extends BasePacket { + public PacketUnlockedHomeBgmNotify(Player player) { + super(PacketOpcodes.Unk2700_LOHBMOKOPLH_ServerNotify); + + if (player.getRealmList() == null) { + return; + } + + var unlocked = player.getHome().getUnlockedHomeBgmList(); + + var notify = Unk2700LOHBMOKOPLHServerNotify.Unk2700_LOHBMOKOPLH_ServerNotify.newBuilder() + .addAllUnk2700KMEKMNONMGE(unlocked) + .build(); + + this.setData(notify); + } +} diff --git a/src/main/java/emu/grasscutter/utils/GridPosition.java b/src/main/java/emu/grasscutter/utils/GridPosition.java new file mode 100644 index 000000000..a2ba3c41d --- /dev/null +++ b/src/main/java/emu/grasscutter/utils/GridPosition.java @@ -0,0 +1,118 @@ +package emu.grasscutter.utils; + +import java.io.IOException; +import java.io.Serializable; +import java.util.List; + +import dev.morphia.annotations.Entity; +import lombok.Getter; +import lombok.Setter; + +@Entity +public class GridPosition implements Serializable { + private static final long serialVersionUID = -2001232300615923575L; + + @Getter @Setter private int x; + + @Getter @Setter private int z; + + @Getter @Setter private int width; + + public GridPosition() {} + + public GridPosition(int x, int y, int width) { + set(x, y, width); + } + + public GridPosition(GridPosition pos) { + this.set(pos); + } + + public GridPosition(Position pos, int width) { + this.set((int)(pos.getX() / width), (int)(pos.getZ() / width), width); + } + + public GridPosition(List xzwidth) { + this.width = xzwidth.get(2); + this.z = xzwidth.get(1); + this.x = xzwidth.get(0); + } + + public GridPosition(String str) throws IOException { + String[] listOfParams = str.replace(" ", "").replace("(", "").replace(")", "").split(","); + if(listOfParams.length != 3) + throw new IOException("invalid size on GridPosition definition - "); + try { + this.x = Integer.parseInt(listOfParams[0]); + this.z = Integer.parseInt(listOfParams[1]); + this.width = Integer.parseInt(listOfParams[2]); + } catch(NumberFormatException ignored) { + throw new IOException("invalid number on GridPosition definition - "); + } + } + + public GridPosition set(int x, int z) { + this.x = x; + this.z = z; + return this; + } + + public GridPosition set(int x, int z, int width) { + this.x = x; + this.z = z; + this.width = width; + return this; + } + + // Deep copy + public GridPosition set(GridPosition pos) { + return this.set(pos.getX(), pos.getZ(), pos.getWidth()); + } + + public GridPosition addClone(int x, int z) { + GridPosition pos = clone(); + pos.x += x; + pos.z += z; + return pos; + } + + @Override + public GridPosition clone() { + return new GridPosition(x, z, width); + } + + @Override + public String toString() { + return "(" + this.getX() + ", " + this.getZ() + ", " + this.getWidth() + ")"; + } + + public int[] toIntArray() { + return new int[]{ x, z, width }; + } + + public int[] toXZIntArray() { + return new int[]{ x, z }; + } + + @Override + public int hashCode() + { + int result = (int) (x ^ (x >>> 32)); + result = 31 * result + (int) (z ^ (z >>> 32)); + result = 31 * result + (int) (width ^ (width >>> 32)); + return result; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null) + return false; + if (getClass() != o.getClass()) + return false; + GridPosition pos = (GridPosition) o; + // field comparison + return pos.x == x && pos.z == z && pos.width == width; + } +} diff --git a/src/main/java/emu/grasscutter/utils/KahnsSort.java b/src/main/java/emu/grasscutter/utils/KahnsSort.java new file mode 100644 index 000000000..7ee71bd76 --- /dev/null +++ b/src/main/java/emu/grasscutter/utils/KahnsSort.java @@ -0,0 +1,66 @@ +package emu.grasscutter.utils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Stack; + +public class KahnsSort { + public static class Node { + int source, dest; //Dest is a value, and source too + + public Node(int source, int dest) { + this.source = source; + this.dest = dest; + } + } + + public static class Graph { + Map> mainList; + Map degreeList; + + List nodeList; + + public Graph(List nodes, List nodeList) { + mainList = new HashMap<>(); + this.nodeList = nodeList; + + for(int i = 0; i < nodeList.size(); i++) mainList.put(nodeList.get(i), new ArrayList<>()); + + degreeList = new HashMap<>(); + for(int i = 0; i < nodeList.size(); i++) degreeList.put(nodeList.get(i), 0); + + for(Node node : nodes) { + mainList.get(node.source).add(node.dest); + degreeList.replace(node.dest, degreeList.get(node.dest) + 1); + } + } + } + + public static List doSort(Graph graph) { + List orderedList = new ArrayList<>(); + Map degreeList = graph.degreeList; + + Stack zeroStack = new Stack<>(); + degreeList.forEach((key, value) -> { + if(value == 0) zeroStack.add(key); + }); + + while(!zeroStack.isEmpty()) { + int element = zeroStack.pop(); + + //If the list is empty then this node + if(!graph.mainList.get(element).isEmpty()) orderedList.add(element); + for(int topElement : graph.mainList.get(element)) { + degreeList.replace(topElement, degreeList.get(topElement) - 1); + + if(degreeList.get(topElement) == 0) zeroStack.add(topElement); + } + } + + if(degreeList.values().stream().filter(value -> value != 0).count() != 0) return null; //Loop found + + return orderedList; + } +}