From 176f3e91f154ba5fe8b4b79fd68809d419478372 Mon Sep 17 00:00:00 2001 From: Michaellan <67815438+chrisblue@users.noreply.github.com> Date: Sun, 8 May 2022 20:06:10 +0800 Subject: [PATCH 1/7] fill description --- src/main/resources/languages/zh-CN.json | 105 ++++++++++++++++-------- 1 file changed, 71 insertions(+), 34 deletions(-) diff --git a/src/main/resources/languages/zh-CN.json b/src/main/resources/languages/zh-CN.json index 8faa0e4ae..4e4929aee 100644 --- a/src/main/resources/languages/zh-CN.json +++ b/src/main/resources/languages/zh-CN.json @@ -95,17 +95,20 @@ "create": "已建立账号,UID 为 %s 。", "delete": "账号已刪除。", "no_account": "账号不存在。", - "command_usage": "用法:account [uid]" + "command_usage": "用法:account [uid]", + "description": "创建或删除账号。" }, "broadcast": { "command_usage": "用法:broadcast <消息>", - "message_sent": "公告已发送。" + "message_sent": "公告已发送。", + "description": "向所有玩家发送公告。" }, "changescene": { "usage": "用法:changescene ", "already_in_scene": "你已经在这个秘境中了。", "success": "已切换至秘境 %s.", - "exists_error": "此秘境不存在。" + "exists_error": "此秘境不存在。", + "description": "切换指定秘境。" }, "clear": { "command_usage": "用法: clear ", @@ -115,35 +118,41 @@ "furniture": "已将 %s 的尘歌壶家具清空。", "displays": "已清除 %s 的显示。", "virtuals": "已将 %s 的所有货币和经验值清空。", - "everything": "已将 %s 的所有物品清空。" + "everything": "已将 %s 的所有物品清空。", + "description": "从您的背包中删除所有未装备且已解锁的物品,包括稀有物品。" }, "coop": { "usage": "用法:coop ", - "success": "已强制召唤 %s 到 %s的世界" + "success": "已强制召唤 %s 到 %s的世界", + "description": "强制召唤指定用户到他人的世界。" }, "enter_dungeon": { "usage": "用法:enterdungeon ", "changed": "已进入秘境 %s", "not_found_error": "此秘境不存在。", - "in_dungeon_error": "你已经在秘境中了。" + "in_dungeon_error": "你已经在秘境中了。", + "description": "进入指定秘境。" }, "giveAll": { "usage": "用法:giveall [player] [amount]", "started": "正在给予全部物品...", "success": "已给予全部物品。", - "invalid_amount_or_playerId": "无效的数量/玩家ID。" + "invalid_amount_or_playerId": "无效的数量/玩家ID。", + "description": "给予所有物品。" }, "giveArtifact": { "usage": "用法:giveart|gart [player] [[,]]... [level]", "id_error": "无效的圣遗物ID。", - "success": "已将 %s 给予 %s。" + "success": "已将 %s 给予 %s。", + "description": "给予指定圣遗物。" }, "giveChar": { "usage": "用法:givechar [amount]", "given": "给予角色 %s 等级 %s 向UID %s.", "invalid_avatar_id": "无效的角色ID。", "invalid_avatar_level": "无效的角色等級。.", - "invalid_avatar_or_player_id": "无效的角色ID/玩家ID。" + "invalid_avatar_or_player_id": "无效的角色ID/玩家ID。", + "description": "给予指定角色。" }, "give": { "usage": "用法:give [amount] [level] [refinement]", @@ -151,29 +160,36 @@ "refinement_must_between_1_and_5": "精炼等阶必须在 1 到 5 之间。", "given": "已将 %s 个 %s 给予 %s。", "given_with_level_and_refinement": "已将 %s [等級%s, 精炼%s] %s个给予 %s", - "given_level": "已将 %s 等级 %s %s 个给予UID %s" + "given_level": "已将 %s 等级 %s %s 个给予UID %s", + "description": "给予指定物品。" }, "godmode": { - "success": "上帝模式已被设置为 %s 。 [用户:%s]" + "success": "上帝模式已被设置为 %s 。 [用户:%s]", + "description": "防止你受到伤害。" }, "heal": { - "success": "所有角色已被治疗。" + "success": "所有角色已被治疗。", + "description": "治疗所选队伍的角色。" }, "kick": { "player_kick_player": "玩家 [%s:%s] 已将 [%s:%s] 踢出", - "server_kick_player": "正在踢出玩家 [%s:%s]" + "server_kick_player": "正在踢出玩家 [%s:%s]", + "description": "从服务器内踢出指定玩家。" }, "kill": { "usage": "用法:killall [playerUid] [sceneId]", "scene_not_found_in_player_world": "未在玩家世界中找到此场景", - "kill_monsters_in_scene": "已杀死 %s 个怪物。 [场景ID: %s]" + "kill_monsters_in_scene": "已杀死 %s 个怪物。 [场景ID: %s]", + "description": "杀死所有怪物" }, "killCharacter": { "usage": "用法:/killcharacter [playerId]", - "success": "已杀死 %s 目前使用的角色。" + "success": "已杀死 %s 目前使用的角色。", + "description": "杀死目前使用的角色" }, "list": { - "success": "目前在线人数:%s" + "success": "目前在线人数:%s", + "description": "查看所有玩家" }, "permission": { "usage": "用法:permission ", @@ -181,21 +197,26 @@ "has_error": "此玩家已拥有此权限!", "remove": "权限已移除。", "not_have_error": "此玩家未拥有权限!", - "account_error": "账号不存在!" + "account_error": "账号不存在!", + "description": "给予或移除指定玩家的权限。" }, "position": { - "success": "坐标:%.3f, %.3f, %.3f\n场景ID:%d" + "success": "坐标:%.3f, %.3f, %.3f\n场景ID:%d", + "description": "获取所在位置。" }, "reload": { "reload_start": "正在重载配置文件和数据。", - "reload_done": "重载完毕。" + "reload_done": "重载完毕。", + "description": "重载配置文件和数据。" }, "resetConst": { "reset_all": "重置所有角色的命座。", - "success": "已重置 %s 的命座,重新登录后将会生效。" + "success": "已重置 %s 的命座,重新登录后将会生效。", + "description": "重置当前角色的命之座,执行命令后需重新登录以生效。" }, "resetShopLimit": { - "usage": "用法:/resetshop " + "usage": "用法:/resetshop ", + "description": "重置所选玩家的商店刷新时间。" }, "sendMail": { "usage": "用法:give [player] [amount]", @@ -217,17 +238,20 @@ "message": "<正文>", "sender": "<发件人>", "arguments": " [数量] [等级]", - "error": "错误:无效的编写阶段 %s。需要 StackTrace 请查看服务器控制台。" + "error": "错误:无效的编写阶段 %s。需要 StackTrace 请查看服务器控制台。", + "description": "向指定用户发送邮件。 此命令的用法可根据附加的参数而变化。" }, "sendMessage": { "usage": "用法:sendmessage ", - "success": "消息已发送。" + "success": "消息已发送。", + "description": "向指定玩家发送消息" }, "setFetterLevel": { "usage": "用法:setfetterlevel ", "range_error": "好感度等级必须在 0 到 10 之间。", "fetter_set_level": "好感度已设置为 %s 级", - "level_error": "无效的好感度等级。" + "level_error": "无效的好感度等级。", + "description": "设置当前角色的好感度等级。" }, "setStats": { "usage_console": "用法:setstats|stats @ ", @@ -238,20 +262,24 @@ "player_error": "玩家不存在或已离线。", "set_self": "%s 已经设置为 %s。", "set_for_uid": "%s 的使用者 %s 更改为 %s。", - "set_max_hp": "最大生命值更改为 %s。" + "set_max_hp": "最大生命值更改为 %s。", + "description": "设置当前角色的属性。" }, "setWorldLevel": { "usage": "用法:setworldlevel ", "value_error": "世界等级必须设置在0-8之间。", "success": "已将世界等级设为%s。", - "invalid_world_level": "无效的世界等级。" + "invalid_world_level": "无效的世界等级。", + "description": "设置世界等级,执行命令后需重新登录以生效。" }, "spawn": { "usage": "用法:spawn [amount] [level(仅限怪物]", - "success": "已生成 %s 个 %s。" + "success": "已生成 %s 个 %s。", + "description": "在你附近生成一个生物。" }, "stop": { - "success": "正在关闭服务器..." + "success": "正在关闭服务器...", + "description": "停止服务器" }, "talent": { "usage_1": "设置天赋等级:/talent set ", @@ -267,32 +295,41 @@ "invalid_level": "无效的天赋等级。", "normal_attack_id": "普通攻击的 ID 为 %s。", "e_skill_id": "元素战技ID %s。", - "q_skill_id": "元素爆发ID %s。" + "q_skill_id": "元素爆发ID %s。", + "description": "设置当前角色的天赋等级。" }, "teleportAll": { "success": "已将全部玩家传送到你的位置", - "error": "命令仅限处于多人游戏状态下使用。" + "error": "命令仅限处于多人游戏状态下使用。", + "description": "将你世界中的所有玩家传送到你所在的位置。" }, "teleport": { "usage_server": "用法:/tp @ [scene id]", "usage": "用法:/tp [@] [scene id]", "specify_player_id": "你必须指定一个玩家ID。", "invalid_position": "无效的位置。", - "success": "传送 %s 到坐标 %s,%s,%s,场景为 %s" + "success": "传送 %s 到坐标 %s,%s,%s,场景为 %s", + "description": "改变指定玩家的位置。" }, "weather": { "usage": "用法:weather [climateId]", "success": "已将当前天气设定为 %s,气候为 %s。", - "invalid_id": "无效的天气ID。" + "invalid_id": "无效的天气ID。", + "description": "改变天气" }, "drop": { "command_usage": "用法:drop [amount]", - "success": "已将 %s x %s 丟在附近。" + "success": "已将 %s x %s 丟在附近。", + "description": "在你附近丢一个物品。" }, "help": { "usage": "用法:", "aliases": "別名:", - "available_commands": "可用指令:" + "available_commands": "可用指令:", + "description": "发送帮助信息或显示指定命令的信息。" + }, + "restart": { + "description": "重新启动服务器。" } } } From 897f082b127933767c0d05dc1ecdde4cdea64cca Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Sun, 8 May 2022 04:40:01 -0700 Subject: [PATCH 2/7] Implement AbilityManager --- .../java/emu/grasscutter/data/GameData.java | 7 + .../emu/grasscutter/data/ResourceLoader.java | 69 +++++++ .../data/custom/AbilityModifier.java | 36 ++++ .../data/custom/AbilityModifierEntry.java | 37 ++++ .../game/ability/AbilityManager.java | 185 ++++++++++++++++++ .../grasscutter/game/entity/EntityAvatar.java | 17 ++ .../grasscutter/game/entity/GameEntity.java | 73 +++++++ .../emu/grasscutter/game/player/Player.java | 9 +- .../emu/grasscutter/game/world/Scene.java | 22 +-- .../recv/HandlerAbilityInvocationsNotify.java | 3 +- .../HandlerClientAbilityInitFinishNotify.java | 2 + .../recv/HandlerEvtCreateGadgetNotify.java | 5 - .../recv/HandlerEvtDestroyGadgetNotify.java | 5 - .../HandlerSetEntityClientDataNotify.java | 2 +- 14 files changed, 438 insertions(+), 34 deletions(-) create mode 100644 src/main/java/emu/grasscutter/data/custom/AbilityModifier.java create mode 100644 src/main/java/emu/grasscutter/data/custom/AbilityModifierEntry.java create mode 100644 src/main/java/emu/grasscutter/game/ability/AbilityManager.java diff --git a/src/main/java/emu/grasscutter/data/GameData.java b/src/main/java/emu/grasscutter/data/GameData.java index 76a7f1652..f06d1bda6 100644 --- a/src/main/java/emu/grasscutter/data/GameData.java +++ b/src/main/java/emu/grasscutter/data/GameData.java @@ -9,6 +9,8 @@ import java.util.Map; import emu.grasscutter.Grasscutter; import emu.grasscutter.utils.Utils; import emu.grasscutter.data.custom.AbilityEmbryoEntry; +import emu.grasscutter.data.custom.AbilityModifier; +import emu.grasscutter.data.custom.AbilityModifierEntry; import emu.grasscutter.data.custom.OpenConfigEntry; import emu.grasscutter.data.custom.ScenePointEntry; import emu.grasscutter.data.def.*; @@ -22,6 +24,7 @@ public class GameData { // BinOutputs private static final Int2ObjectMap abilityHashes = new Int2ObjectOpenHashMap<>(); private static final Map abilityEmbryos = new HashMap<>(); + private static final Map abilityModifiers = new HashMap<>(); private static final Map openConfigEntries = new HashMap<>(); private static final Map scenePointEntries = new HashMap<>(); @@ -101,6 +104,10 @@ public class GameData { return abilityEmbryos; } + public static Map getAbilityModifiers() { + return abilityModifiers; + } + public static Map getOpenConfigEntries() { return openConfigEntries; } diff --git a/src/main/java/emu/grasscutter/data/ResourceLoader.java b/src/main/java/emu/grasscutter/data/ResourceLoader.java index b1e3da9ff..c2708bd63 100644 --- a/src/main/java/emu/grasscutter/data/ResourceLoader.java +++ b/src/main/java/emu/grasscutter/data/ResourceLoader.java @@ -18,6 +18,11 @@ import emu.grasscutter.Grasscutter; import emu.grasscutter.data.common.PointData; import emu.grasscutter.data.common.ScenePointConfig; import emu.grasscutter.data.custom.AbilityEmbryoEntry; +import emu.grasscutter.data.custom.AbilityModifier; +import emu.grasscutter.data.custom.AbilityModifier.AbilityConfigData; +import emu.grasscutter.data.custom.AbilityModifier.AbilityModifierAction; +import emu.grasscutter.data.custom.AbilityModifier.AbilityModifierActionType; +import emu.grasscutter.data.custom.AbilityModifierEntry; import emu.grasscutter.data.custom.OpenConfigEntry; import emu.grasscutter.data.custom.ScenePointEntry; import emu.grasscutter.game.world.SpawnDataEntry; @@ -47,6 +52,7 @@ public class ResourceLoader { // Load ability lists loadAbilityEmbryos(); loadOpenConfig(); + loadAbilityModifiers(); // Load resources loadResources(); // Process into depots @@ -244,6 +250,69 @@ public class ResourceLoader { } } + private static void loadAbilityModifiers() { + // Load from BinOutput + File folder = new File(Utils.toFilePath(Grasscutter.getConfig().RESOURCE_FOLDER + "BinOutput/Ability/Temp/AvatarAbilities/")); + File[] files = folder.listFiles(); + if (files == null) { + Grasscutter.getLogger().error("Error loading ability modifiers: no files found in " + folder.getAbsolutePath()); + return; + } + + for (File file : files) { + List abilityConfigList = null; + + try (FileReader fileReader = new FileReader(file)) { + abilityConfigList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, AbilityConfigData.class).getType()); + } catch (Exception e) { + e.printStackTrace(); + continue; + } + + for (AbilityConfigData data : abilityConfigList) { + if (data.Default.modifiers == null || data.Default.modifiers.size() == 0) { + continue; + } + + AbilityModifierEntry modifierEntry = new AbilityModifierEntry(data.Default.abilityName); + + for (Entry entry : data.Default.modifiers.entrySet()) { + AbilityModifier modifier = entry.getValue(); + + // Stare. + if (modifier.onAdded != null) { + for (AbilityModifierAction action : modifier.onAdded) { + if (action.$type.contains("HealHP")) { + action.type = AbilityModifierActionType.HealHP; + modifierEntry.getOnAdded().add(action); + } + } + } + + if (modifier.onThinkInterval != null) { + for (AbilityModifierAction action : modifier.onThinkInterval) { + if (action.$type.contains("HealHP")) { + action.type = AbilityModifierActionType.HealHP; + modifierEntry.getOnThinkInterval().add(action); + } + } + } + + if (modifier.onRemoved != null) { + for (AbilityModifierAction action : modifier.onRemoved) { + if (action.$type.contains("HealHP")) { + action.type = AbilityModifierActionType.HealHP; + modifierEntry.getOnRemoved().add(action); + } + } + } + } + + GameData.getAbilityModifiers().put(modifierEntry.getName(), modifierEntry); + } + } + } + private static void loadSpawnData() { // Read from cached file if exists File spawnDataEntries = new File(Grasscutter.getConfig().DATA_FOLDER + "Spawns.json"); diff --git a/src/main/java/emu/grasscutter/data/custom/AbilityModifier.java b/src/main/java/emu/grasscutter/data/custom/AbilityModifier.java new file mode 100644 index 000000000..5a1394c65 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/custom/AbilityModifier.java @@ -0,0 +1,36 @@ +package emu.grasscutter.data.custom; + +import java.util.Map; + +public class AbilityModifier { + public AbilityModifierAction[] onAdded; + public AbilityModifierAction[] onThinkInterval; + public AbilityModifierAction[] onRemoved; + + public static class AbilityConfigData { + public AbilityData Default; + } + + public static class AbilityData { + public String abilityName; + public Map modifiers; + } + + public static class AbilityModifierAction { + public String $type; + public AbilityModifierActionType type; + public String target; + public AbilityModifierValue amount; + public AbilityModifierValue amountByTargetCurrentHPRatio; + } + + public static class AbilityModifierValue { + public boolean isFormula; + public boolean isDynamic; + public String dynamicKey; + } + + public enum AbilityModifierActionType { + HealHP, ApplyModifier, LoseHP; + } +} diff --git a/src/main/java/emu/grasscutter/data/custom/AbilityModifierEntry.java b/src/main/java/emu/grasscutter/data/custom/AbilityModifierEntry.java new file mode 100644 index 000000000..b31e0eefe --- /dev/null +++ b/src/main/java/emu/grasscutter/data/custom/AbilityModifierEntry.java @@ -0,0 +1,37 @@ +package emu.grasscutter.data.custom; + +import java.util.ArrayList; +import java.util.List; + +import emu.grasscutter.data.custom.AbilityModifier.AbilityModifierAction; + +public class AbilityModifierEntry { + private String name; // Custom value + public List onModifierAdded; + public List onThinkInterval; + public List onRemoved; + + public AbilityModifierEntry(String name) { + this.name = name; + this.onModifierAdded = new ArrayList<>(); + this.onThinkInterval = new ArrayList<>(); + this.onRemoved = new ArrayList<>(); + } + + public String getName() { + return name; + } + + public List getOnAdded() { + return onModifierAdded; + } + + public List getOnThinkInterval() { + return onThinkInterval; + } + + public List getOnRemoved() { + return onRemoved; + } + +} diff --git a/src/main/java/emu/grasscutter/game/ability/AbilityManager.java b/src/main/java/emu/grasscutter/game/ability/AbilityManager.java new file mode 100644 index 000000000..72c235b94 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/ability/AbilityManager.java @@ -0,0 +1,185 @@ +package emu.grasscutter.game.ability; + +import com.google.protobuf.InvalidProtocolBufferException; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.custom.AbilityModifier; +import emu.grasscutter.data.custom.AbilityModifier.AbilityModifierAction; +import emu.grasscutter.data.custom.AbilityModifier.AbilityModifierActionType; +import emu.grasscutter.data.custom.AbilityModifierEntry; +import emu.grasscutter.game.entity.GameEntity; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.net.proto.AbilityInvokeArgumentOuterClass.AbilityInvokeArgument; +import emu.grasscutter.net.proto.AbilityInvokeEntryHeadOuterClass.AbilityInvokeEntryHead; +import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry; +import emu.grasscutter.net.proto.AbilityMetaModifierChangeOuterClass.AbilityMetaModifierChange; +import emu.grasscutter.net.proto.AbilityMetaReInitOverrideMapOuterClass.AbilityMetaReInitOverrideMap; +import emu.grasscutter.net.proto.AbilityScalarTypeOuterClass.AbilityScalarType; +import emu.grasscutter.net.proto.AbilityScalarValueEntryOuterClass.AbilityScalarValueEntry; +import emu.grasscutter.net.proto.ModifierActionOuterClass.ModifierAction; +import emu.grasscutter.utils.Utils; + +public class AbilityManager { + private Player player; + + public AbilityManager(Player player) { + this.player = player; + } + + public Player getPlayer() { + return this.player; + } + + public void onAbilityInvoke(AbilityInvokeEntry invoke) throws Exception { + //System.out.println(invoke.getArgumentType() + " (" + invoke.getArgumentTypeValue() + "): " + Utils.bytesToHex(invoke.toByteArray())); + switch (invoke.getArgumentType()) { + case ABILITY_META_OVERRIDE_PARAM: + handleOverrideParam(invoke); + break; + case ABILITY_META_REINIT_OVERRIDEMAP: + handleReinitOverrideMap(invoke); + break; + case ABILITY_META_MODIFIER_CHANGE: + handleModifierChange(invoke); + break; + case ABILITY_MIXIN_COST_STAMINA: + handleMixinCostStamina(invoke); + break; + case ABILITY_ACTION_GENERATE_ELEM_BALL: + handleGenerateElemBall(invoke); + break; + default: + break; + } + } + + private void handleOverrideParam(AbilityInvokeEntry invoke) throws Exception { + GameEntity entity = player.getScene().getEntityById(invoke.getEntityId()); + + if (entity == null) { + return; + } + + AbilityScalarValueEntry entry = AbilityScalarValueEntry.parseFrom(invoke.getAbilityData()); + + entity.getMetaOverrideMap().put(entry.getKey().getStr(), entry.getFloatValue()); + } + + private void handleReinitOverrideMap(AbilityInvokeEntry invoke) throws Exception { + GameEntity entity = player.getScene().getEntityById(invoke.getEntityId()); + + if (entity == null) { + return; + } + + AbilityMetaReInitOverrideMap map = AbilityMetaReInitOverrideMap.parseFrom(invoke.getAbilityData()); + + for (AbilityScalarValueEntry entry : map.getOverrideMapList()) { + entity.getMetaOverrideMap().put(entry.getKey().getStr(), entry.getFloatValue()); + } + } + + private void handleModifierChange(AbilityInvokeEntry invoke) throws Exception { + GameEntity target = player.getScene().getEntityById(invoke.getEntityId()); + if (target == null) { + return; + } + + AbilityInvokeEntryHead head = invoke.getHead(); + if (head == null) { + return; + } + + AbilityMetaModifierChange data = AbilityMetaModifierChange.parseFrom(invoke.getAbilityData()); + if (data == null) { + return; + } + + GameEntity sourceEntity = player.getScene().getEntityById(data.getApplyEntityId()); + if (sourceEntity == null) { + return; + } + + // This is not how it works but we will keep it for now since healing abilities dont work properly anyways + if (data.getAction() == ModifierAction.ADDED && data.getParentAbilityName() != null) { + // Handle add modifier here + String modifierString = data.getParentAbilityName().getStr(); + AbilityModifierEntry modifier = GameData.getAbilityModifiers().get(modifierString); + + if (modifier != null && modifier.getOnAdded().size() > 0) { + for (AbilityModifierAction action : modifier.getOnAdded()) { + invokeAction(action, target, sourceEntity); + } + } + + // Add to meta modifier list + target.getMetaModifiers().put(head.getInstancedModifierId(), modifierString); + } else if (data.getAction() == ModifierAction.REMOVED) { + String modifierString = target.getMetaModifiers().get(head.getInstancedModifierId()); + + if (modifierString != null) { + // Get modifier and call on remove event + AbilityModifierEntry modifier = GameData.getAbilityModifiers().get(modifierString); + + if (modifier != null && modifier.getOnRemoved().size() > 0) { + for (AbilityModifierAction action : modifier.getOnRemoved()) { + invokeAction(action, target, sourceEntity); + } + } + + // Remove from meta modifiers + target.getMetaModifiers().remove(head.getInstancedModifierId()); + } + } + } + + private void handleMixinCostStamina(AbilityInvokeEntry invoke) { + // Not the right way of doing this + if (Grasscutter.getConfig().OpenStamina) { + // getPlayer().getStaminaManager().updateStamina(getPlayer().getSession(), -450); + // TODO + // set flag in stamina/movement manager that specifies the player is currently using an alternate sprint + } + } + + private void handleGenerateElemBall(AbilityInvokeEntry invoke) { + // TODO create elemental energy orbs + } + + private void invokeAction(AbilityModifierAction action, GameEntity target, GameEntity sourceEntity) { + switch (action.type) { + case HealHP -> { + if (action.amount == null) { + return; + } + + float healAmount = 0; + + if (action.amount.isDynamic && action.amount.dynamicKey != null) { + healAmount = sourceEntity.getMetaOverrideMap().getOrDefault(action.amount.dynamicKey, 0f); + } + + if (healAmount > 0) { + target.heal(healAmount); + } + } + case LoseHP -> { + if (action.amountByTargetCurrentHPRatio == null) { + return; + } + + float damageAmount = 0; + + if (action.amount.isDynamic && action.amount.dynamicKey != null) { + damageAmount = sourceEntity.getMetaOverrideMap().getOrDefault(action.amount.dynamicKey, 0f); + } + + if (damageAmount > 0) { + target.damage(damageAmount); + } + } + } + } +} + diff --git a/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java b/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java index 82efb795f..3c8ef2ba9 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java @@ -17,17 +17,21 @@ import emu.grasscutter.net.proto.AbilityControlBlockOuterClass.AbilityControlBlo import emu.grasscutter.net.proto.AbilityEmbryoOuterClass.AbilityEmbryo; import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo; import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair; +import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason; import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo; import emu.grasscutter.net.proto.EntityClientDataOuterClass.EntityClientData; import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo; import emu.grasscutter.net.proto.FightPropPairOuterClass.FightPropPair; import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType; +import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason; import emu.grasscutter.net.proto.PropPairOuterClass.PropPair; import emu.grasscutter.net.proto.ProtEntityTypeOuterClass.ProtEntityType; import emu.grasscutter.net.proto.SceneAvatarInfoOuterClass.SceneAvatarInfo; import emu.grasscutter.net.proto.SceneEntityAiInfoOuterClass.SceneEntityAiInfo; import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo; import emu.grasscutter.net.proto.VectorOuterClass.Vector; +import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify; +import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import emu.grasscutter.utils.Position; import emu.grasscutter.utils.ProtoHelper; import emu.grasscutter.utils.Utils; @@ -110,6 +114,19 @@ public class EntityAvatar extends GameEntity { this.killedBy = killerId; } + @Override + public float heal(float amount) { + float healed = super.heal(amount); + + if (healed > 0f) { + getScene().broadcastPacket( + new PacketEntityFightPropChangeReasonNotify(this, FightProperty.FIGHT_PROP_CUR_HP, healed, PropChangeReason.PROP_CHANGE_ABILITY, ChangeHpReason.ChangeHpAddAbility) + ); + } + + return healed; + } + public SceneAvatarInfo getSceneAvatarInfo() { SceneAvatarInfo.Builder avatarInfo = SceneAvatarInfo.newBuilder() .setUid(this.getPlayer().getUid()) diff --git a/src/main/java/emu/grasscutter/game/entity/GameEntity.java b/src/main/java/emu/grasscutter/game/entity/GameEntity.java index 627b41103..0cdcc3ebd 100644 --- a/src/main/java/emu/grasscutter/game/entity/GameEntity.java +++ b/src/main/java/emu/grasscutter/game/entity/GameEntity.java @@ -1,5 +1,8 @@ package emu.grasscutter.game.entity; +import java.util.HashMap; +import java.util.Map; + import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.LifeState; import emu.grasscutter.game.world.Scene; @@ -9,8 +12,11 @@ import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo; import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState; import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo; import emu.grasscutter.net.proto.VectorOuterClass.Vector; +import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import emu.grasscutter.utils.Position; import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; public abstract class GameEntity { protected int id; @@ -25,6 +31,10 @@ public abstract class GameEntity { private int lastMoveSceneTimeMs; private int lastMoveReliableSeq; + // Abilities + private Map metaOverrideMap; + private Int2ObjectMap metaModifiers; + public GameEntity(Scene scene) { this.scene = scene; this.moveState = MotionState.MOTION_NONE; @@ -54,6 +64,20 @@ public abstract class GameEntity { return isAlive() ? LifeState.LIFE_ALIVE : LifeState.LIFE_DEAD; } + public Map getMetaOverrideMap() { + if (this.metaOverrideMap == null) { + this.metaOverrideMap = new HashMap<>(); + } + return this.metaOverrideMap; + } + + public Int2ObjectMap getMetaModifiers() { + if (this.metaModifiers == null) { + this.metaModifiers = new Int2ObjectOpenHashMap<>(); + } + return this.metaModifiers; + } + public abstract Int2FloatOpenHashMap getFightProperties(); public abstract Position getPosition(); @@ -146,4 +170,53 @@ public abstract class GameEntity { public void setSpawnEntry(SpawnDataEntry spawnEntry) { this.spawnEntry = spawnEntry; } + + public float heal(float amount) { + if (this.getFightProperties() == null) { + return 0f; + } + + float curHp = getFightProperty(FightProperty.FIGHT_PROP_CUR_HP); + float maxHp = getFightProperty(FightProperty.FIGHT_PROP_MAX_HP); + + if (curHp >= maxHp) { + return 0f; + } + + float healed = Math.min(maxHp - curHp, amount); + this.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, healed); + + getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, FightProperty.FIGHT_PROP_CUR_HP)); + + return healed; + } + + public void damage(float amount) { + damage(amount, 0); + } + + public void damage(float amount, int killerId) { + // Sanity check + if (getFightProperties() == null) { + return; + } + + // Lose hp + addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -amount); + + // Check if dead + boolean isDead = false; + if (getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) { + setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f); + isDead = true; + } + + // Packets + this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, FightProperty.FIGHT_PROP_CUR_HP)); + + // Check if dead + if (isDead) { + getScene().killEntity(this, 0); + } + } } diff --git a/src/main/java/emu/grasscutter/game/player/Player.java b/src/main/java/emu/grasscutter/game/player/Player.java index 71ae9d8c6..477f974ea 100644 --- a/src/main/java/emu/grasscutter/game/player/Player.java +++ b/src/main/java/emu/grasscutter/game/player/Player.java @@ -8,6 +8,7 @@ import emu.grasscutter.data.def.PlayerLevelData; import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.game.Account; import emu.grasscutter.game.CoopRequest; +import emu.grasscutter.game.ability.AbilityManager; import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.avatar.AvatarProfileData; import emu.grasscutter.game.avatar.AvatarStorage; @@ -89,7 +90,8 @@ public class Player { @Transient private FriendsList friendsList; @Transient private MailHandler mailHandler; @Transient private MessageHandler messageHandler; - + @Transient private AbilityManager abilityManager; + @Transient private SotSManager sotsManager; private TeamManager teamManager; @@ -142,6 +144,7 @@ public class Player { this.friendsList = new FriendsList(this); this.mailHandler = new MailHandler(this); this.towerManager = new TowerManager(this); + this.abilityManager = new AbilityManager(this); this.pos = new Position(); this.rotation = new Position(); this.properties = new HashMap<>(); @@ -1025,6 +1028,10 @@ public class Player { public SotSManager getSotSManager() { return sotsManager; } + public AbilityManager getAbilityManager() { + return abilityManager; + } + public synchronized void onTick() { // Check ping if (this.getLastPingTime() > System.currentTimeMillis() + 60000) { diff --git a/src/main/java/emu/grasscutter/game/world/Scene.java b/src/main/java/emu/grasscutter/game/world/Scene.java index 82ce9139f..daed26e3e 100644 --- a/src/main/java/emu/grasscutter/game/world/Scene.java +++ b/src/main/java/emu/grasscutter/game/world/Scene.java @@ -385,27 +385,7 @@ public class Scene { } // Sanity check - if (target.getFightProperties() == null) { - return; - } - - // Lose hp - target.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -result.getDamage()); - - // Check if dead - boolean isDead = false; - if (target.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) { - target.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f); - isDead = true; - } - - // Packets - this.broadcastPacket(new PacketEntityFightPropUpdateNotify(target, FightProperty.FIGHT_PROP_CUR_HP)); - - // Check if dead - if (isDead) { - this.killEntity(target, result.getAttackerId()); - } + target.damage(result.getDamage(), result.getAttackerId()); } public void killEntity(GameEntity target, int attackerId) { diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAbilityInvocationsNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAbilityInvocationsNotify.java index 710ea0fea..8be2d1c1f 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAbilityInvocationsNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAbilityInvocationsNotify.java @@ -6,6 +6,7 @@ import emu.grasscutter.net.proto.AbilityInvocationsNotifyOuterClass.AbilityInvoc import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.utils.Utils; @Opcodes(PacketOpcodes.AbilityInvocationsNotify) public class HandlerAbilityInvocationsNotify extends PacketHandler { @@ -15,7 +16,7 @@ public class HandlerAbilityInvocationsNotify extends PacketHandler { AbilityInvocationsNotify notif = AbilityInvocationsNotify.parseFrom(payload); for (AbilityInvokeEntry entry : notif.getInvokesList()) { - //System.out.println(entry.getArgumentType() + ": " + Utils.bytesToHex(entry.getAbilityData().toByteArray())); + session.getPlayer().getAbilityManager().onAbilityInvoke(entry); session.getPlayer().getAbilityInvokeHandler().addEntry(entry.getForwardType(), entry); } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerClientAbilityInitFinishNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerClientAbilityInitFinishNotify.java index cfe697b91..a1035af85 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerClientAbilityInitFinishNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerClientAbilityInitFinishNotify.java @@ -6,6 +6,7 @@ import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry import emu.grasscutter.net.proto.ClientAbilityInitFinishNotifyOuterClass.ClientAbilityInitFinishNotify; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.utils.Utils; @Opcodes(PacketOpcodes.ClientAbilityInitFinishNotify) public class HandlerClientAbilityInitFinishNotify extends PacketHandler { @@ -15,6 +16,7 @@ public class HandlerClientAbilityInitFinishNotify extends PacketHandler { ClientAbilityInitFinishNotify notif = ClientAbilityInitFinishNotify.parseFrom(payload); for (AbilityInvokeEntry entry : notif.getInvokesList()) { + session.getPlayer().getAbilityManager().onAbilityInvoke(entry); session.getPlayer().getClientAbilityInitFinishHandler().addEntry(entry.getForwardType(), entry); } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtCreateGadgetNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtCreateGadgetNotify.java index 9b1cdb0fb..92229d400 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtCreateGadgetNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtCreateGadgetNotify.java @@ -14,11 +14,6 @@ public class HandlerEvtCreateGadgetNotify extends PacketHandler { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { EvtCreateGadgetNotify notify = EvtCreateGadgetNotify.parseFrom(payload); - // Dont handle in singleplayer - if (!session.getPlayer().getWorld().isMultiplayer()) { - return; - } - // Sanity check - dont add duplicate entities if (session.getPlayer().getScene().getEntityById(notify.getEntityId()) != null) { return; diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtDestroyGadgetNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtDestroyGadgetNotify.java index 608215d0a..7d1abe8ea 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtDestroyGadgetNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtDestroyGadgetNotify.java @@ -12,11 +12,6 @@ public class HandlerEvtDestroyGadgetNotify extends PacketHandler { @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { EvtDestroyGadgetNotify notify = EvtDestroyGadgetNotify.parseFrom(payload); - - // Dont handle in singleplayer - if (!session.getPlayer().getWorld().isMultiplayer()) { - return; - } session.getPlayer().getScene().onPlayerDestroyGadget(notify.getEntityId()); } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetEntityClientDataNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetEntityClientDataNotify.java index 5151034f2..6c4d86f7e 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetEntityClientDataNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetEntityClientDataNotify.java @@ -23,7 +23,7 @@ public class HandlerSetEntityClientDataNotify extends PacketHandler { BasePacket packet = new BasePacket(PacketOpcodes.SetEntityClientDataNotify, true); packet.setData(notif); - session.getPlayer().getScene().broadcastPacketToOthers(session.getPlayer(), packet); + session.getPlayer().getScene().broadcastPacket(packet); } } From 2dfdc62743dd1dc2e94fee1b2e2ffa1131663d57 Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Sun, 8 May 2022 05:31:53 -0700 Subject: [PATCH 3/7] Implement energy balls (orbs) --- .../game/ability/AbilityManager.java | 21 +++++++++- .../emu/grasscutter/game/avatar/Avatar.java | 26 ++++++++---- .../game/avatar/AvatarStorage.java | 5 ++- .../grasscutter/game/entity/EntityAvatar.java | 19 ++++++++- .../grasscutter/game/inventory/Inventory.java | 3 ++ .../grasscutter/game/player/TeamManager.java | 19 +++++++++ .../grasscutter/game/props/ElementType.java | 42 +++++++++++-------- .../recv/HandlerSetPlayerBornDataReq.java | 2 +- ...cketEntityFightPropChangeReasonNotify.java | 22 +++++++++- 9 files changed, 128 insertions(+), 31 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/ability/AbilityManager.java b/src/main/java/emu/grasscutter/game/ability/AbilityManager.java index 72c235b94..d1ae388ea 100644 --- a/src/main/java/emu/grasscutter/game/ability/AbilityManager.java +++ b/src/main/java/emu/grasscutter/game/ability/AbilityManager.java @@ -7,9 +7,12 @@ import emu.grasscutter.data.GameData; import emu.grasscutter.data.custom.AbilityModifier; import emu.grasscutter.data.custom.AbilityModifier.AbilityModifierAction; import emu.grasscutter.data.custom.AbilityModifier.AbilityModifierActionType; +import emu.grasscutter.data.def.ItemData; import emu.grasscutter.data.custom.AbilityModifierEntry; +import emu.grasscutter.game.entity.EntityItem; import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.player.Player; +import emu.grasscutter.net.proto.AbilityActionGenerateElemBallOuterClass.AbilityActionGenerateElemBall; import emu.grasscutter.net.proto.AbilityInvokeArgumentOuterClass.AbilityInvokeArgument; import emu.grasscutter.net.proto.AbilityInvokeEntryHeadOuterClass.AbilityInvokeEntryHead; import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry; @@ -18,6 +21,7 @@ import emu.grasscutter.net.proto.AbilityMetaReInitOverrideMapOuterClass.AbilityM import emu.grasscutter.net.proto.AbilityScalarTypeOuterClass.AbilityScalarType; import emu.grasscutter.net.proto.AbilityScalarValueEntryOuterClass.AbilityScalarValueEntry; import emu.grasscutter.net.proto.ModifierActionOuterClass.ModifierAction; +import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Utils; public class AbilityManager { @@ -143,8 +147,21 @@ public class AbilityManager { } } - private void handleGenerateElemBall(AbilityInvokeEntry invoke) { - // TODO create elemental energy orbs + private void handleGenerateElemBall(AbilityInvokeEntry invoke) throws InvalidProtocolBufferException { + AbilityActionGenerateElemBall action = AbilityActionGenerateElemBall.parseFrom(invoke.getAbilityData()); + if (action == null) { + return; + } + + ItemData itemData = GameData.getItemDataMap().get(2024); + if (itemData == null) { + return; // Should never happen + } + + EntityItem energyBall = new EntityItem(getPlayer().getScene(), getPlayer(), itemData, new Position(action.getPos()), 1); + energyBall.getRotation().set(action.getRot()); + + getPlayer().getScene().addEntity(energyBall); } private void invokeAction(AbilityModifierAction action, GameEntity target, GameEntity sourceEntity) { diff --git a/src/main/java/emu/grasscutter/game/avatar/Avatar.java b/src/main/java/emu/grasscutter/game/avatar/Avatar.java index b0bfb0801..c4fb85671 100644 --- a/src/main/java/emu/grasscutter/game/avatar/Avatar.java +++ b/src/main/java/emu/grasscutter/game/avatar/Avatar.java @@ -69,6 +69,7 @@ public class Avatar { @Transient private Player owner; @Transient private AvatarData data; + @Transient private AvatarSkillDepotData skillDepot; @Transient private long guid; // Player unique id private int avatarId; // Id of avatar @@ -103,8 +104,8 @@ public class Avatar { private int nameCardRewardId; private int nameCardId; + @Deprecated // Do not use. Morhpia only! public Avatar() { - // Morhpia only! this.equips = new Int2ObjectOpenHashMap<>(); this.fightProp = new Int2FloatOpenHashMap(); this.extraAbilityEmbryos = new HashSet<>(); @@ -140,7 +141,7 @@ public class Avatar { } // Skill depot - this.setSkillDepot(getAvatarData().getSkillDepot()); + this.setSkillDepotData(getAvatarData().getSkillDepot()); // Set stats this.recalcStats(); @@ -164,7 +165,8 @@ public class Avatar { } protected void setAvatarData(AvatarData data) { - this.data = data; + if (this.data != null) return; + this.data = data; // Used while loading this from the database } public int getOwnerId() { @@ -257,9 +259,19 @@ public class Avatar { return skillDepotId; } - public void setSkillDepot(AvatarSkillDepotData skillDepot) { - // Set id + public AvatarSkillDepotData getSkillDepot() { + return skillDepot; + } + + protected void setSkillDepot(AvatarSkillDepotData skillDepot) { + if (this.skillDepot != null) return; + this.skillDepot = skillDepot; // Used while loading this from the database + } + + public void setSkillDepotData(AvatarSkillDepotData skillDepot) { + // Set id and depot this.skillDepotId = skillDepot.getId(); + this.skillDepot = skillDepot; // Clear, then add skills getSkillLevelMap().clear(); if (skillDepot.getEnergySkill() > 0) { @@ -501,8 +513,8 @@ public class Avatar { // Set energy usage if (data.getSkillDepot() != null && data.getSkillDepot().getEnergySkillData() != null) { ElementType element = data.getSkillDepot().getElementType(); - this.setFightProperty(element.getEnergyProperty(), data.getSkillDepot().getEnergySkillData().getCostElemVal()); - this.setFightProperty((element.getEnergyProperty().getId() % 70) + 1000, data.getSkillDepot().getEnergySkillData().getCostElemVal()); + this.setFightProperty(element.getMaxEnergyProp(), data.getSkillDepot().getEnergySkillData().getCostElemVal()); + this.setFightProperty((element.getMaxEnergyProp().getId() % 70) + 1000, data.getSkillDepot().getEnergySkillData().getCostElemVal()); } // Artifacts diff --git a/src/main/java/emu/grasscutter/game/avatar/AvatarStorage.java b/src/main/java/emu/grasscutter/game/avatar/AvatarStorage.java index 2486e36ab..de3221473 100644 --- a/src/main/java/emu/grasscutter/game/avatar/AvatarStorage.java +++ b/src/main/java/emu/grasscutter/game/avatar/AvatarStorage.java @@ -5,6 +5,7 @@ import java.util.List; import emu.grasscutter.data.GameData; import emu.grasscutter.data.def.AvatarData; +import emu.grasscutter.data.def.AvatarSkillDepotData; import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.inventory.GameItem; @@ -139,12 +140,14 @@ public class AvatarStorage implements Iterable { } AvatarData avatarData = GameData.getAvatarDataMap().get(avatar.getAvatarId()); - if (avatarData == null) { + AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(avatar.getSkillDepotId()); + if (avatarData == null || skillDepot == null) { continue; } // Set ownerships avatar.setAvatarData(avatarData); + avatar.setSkillDepot(skillDepot); avatar.setOwner(getPlayer()); // Force recalc of const boosted skills diff --git a/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java b/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java index 3c8ef2ba9..858db3de2 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java @@ -30,6 +30,7 @@ import emu.grasscutter.net.proto.SceneAvatarInfoOuterClass.SceneAvatarInfo; import emu.grasscutter.net.proto.SceneEntityAiInfoOuterClass.SceneEntityAiInfo; import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo; import emu.grasscutter.net.proto.VectorOuterClass.Vector; +import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify; import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify; import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import emu.grasscutter.utils.Position; @@ -127,6 +128,22 @@ public class EntityAvatar extends GameEntity { return healed; } + public void addEnergy(float amount) { + FightProperty curEnergyProp = getAvatar().getSkillDepot().getElementType().getCurEnergyProp(); + FightProperty maxEnergyProp = getAvatar().getSkillDepot().getElementType().getMaxEnergyProp(); + + float curEnergy = this.getFightProperty(curEnergyProp); + float maxEnergy = this.getFightProperty(maxEnergyProp); + float newEnergy = Math.min(curEnergy + amount, maxEnergy); + + if (newEnergy != curEnergy) { + setFightProperty(curEnergyProp, newEnergy); + + getScene().broadcastPacket(new PacketAvatarFightPropUpdateNotify(getAvatar(), curEnergyProp)); + getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, newEnergy, PropChangeReason.PROP_CHANGE_ENERGY_BALL)); + } + } + public SceneAvatarInfo getSceneAvatarInfo() { SceneAvatarInfo.Builder avatarInfo = SceneAvatarInfo.newBuilder() .setUid(this.getPlayer().getUid()) @@ -258,5 +275,5 @@ public class EntityAvatar extends GameEntity { // return abilityControlBlock.build(); - } + } } diff --git a/src/main/java/emu/grasscutter/game/inventory/Inventory.java b/src/main/java/emu/grasscutter/game/inventory/Inventory.java index 14d1ae203..c4158ee6f 100644 --- a/src/main/java/emu/grasscutter/game/inventory/Inventory.java +++ b/src/main/java/emu/grasscutter/game/inventory/Inventory.java @@ -172,6 +172,9 @@ public class Inventory implements Iterable { // Handle this.addVirtualItem(item.getItemId(), item.getCount()); return item; + } else if (item.getItemData().getMaterialType() == MaterialType.MATERIAL_ADSORBATE) { + player.getTeamManager().addEnergyToTeam(item); + return null; } else if (item.getItemData().getMaterialType() == MaterialType.MATERIAL_AVATAR) { // Get avatar id int avatarId = (item.getItemId() % 1000) + 10000000; diff --git a/src/main/java/emu/grasscutter/game/player/TeamManager.java b/src/main/java/emu/grasscutter/game/player/TeamManager.java index 204af2976..891d0a215 100644 --- a/src/main/java/emu/grasscutter/game/player/TeamManager.java +++ b/src/main/java/emu/grasscutter/game/player/TeamManager.java @@ -10,6 +10,7 @@ import emu.grasscutter.data.def.AvatarSkillDepotData; import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.entity.EntityBaseGadget; +import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.props.ElementType; import emu.grasscutter.game.props.EnterReason; import emu.grasscutter.game.props.FightProperty; @@ -579,6 +580,24 @@ public class TeamManager { // Packets getPlayer().sendPacket(new BasePacket(PacketOpcodes.WorldPlayerReviveRsp)); } + + public synchronized void addEnergyToTeam(GameItem energyBall) { + // TODO + float baseEnergy = 2; + + for (int i = 0; i < getActiveTeam().size(); i++) { + EntityAvatar entity = getActiveTeam().get(i); + + float energyGain = baseEnergy; + + // Active character gets full hp + if (getCurrentCharacterIndex() != i) { + energyGain *= Math.max(1.0 - (getActiveTeam().size() * .1f), .6f); + } + + entity.addEnergy(energyGain); + } + } public void saveAvatars() { // Save all avatars from active team diff --git a/src/main/java/emu/grasscutter/game/props/ElementType.java b/src/main/java/emu/grasscutter/game/props/ElementType.java index 23362c39f..12a30f6fc 100644 --- a/src/main/java/emu/grasscutter/game/props/ElementType.java +++ b/src/main/java/emu/grasscutter/game/props/ElementType.java @@ -9,21 +9,22 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; public enum ElementType { - None (0, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY), - Fire (1, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY, 10101, "TeamResonance_Fire_Lv2"), - Water (2, FightProperty.FIGHT_PROP_MAX_WATER_ENERGY, 10201, "TeamResonance_Water_Lv2"), - Grass (3, FightProperty.FIGHT_PROP_MAX_GRASS_ENERGY), - Electric (4, FightProperty.FIGHT_PROP_MAX_ELEC_ENERGY, 10401, "TeamResonance_Electric_Lv2"), - Ice (5, FightProperty.FIGHT_PROP_MAX_ICE_ENERGY, 10601, "TeamResonance_Ice_Lv2"), - Frozen (6, FightProperty.FIGHT_PROP_MAX_ICE_ENERGY), - Wind (7, FightProperty.FIGHT_PROP_MAX_WIND_ENERGY, 10301, "TeamResonance_Wind_Lv2"), - Rock (8, FightProperty.FIGHT_PROP_MAX_ROCK_ENERGY, 10701, "TeamResonance_Rock_Lv2"), - AntiFire (9, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY), - Default (255, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY, 10801, "TeamResonance_AllDifferent"); + None (0, FightProperty.FIGHT_PROP_CUR_FIRE_ENERGY, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY), + Fire (1, FightProperty.FIGHT_PROP_CUR_FIRE_ENERGY, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY, 10101, "TeamResonance_Fire_Lv2"), + Water (2, FightProperty.FIGHT_PROP_CUR_WATER_ENERGY, FightProperty.FIGHT_PROP_MAX_WATER_ENERGY, 10201, "TeamResonance_Water_Lv2"), + Grass (3, FightProperty.FIGHT_PROP_CUR_GRASS_ENERGY, FightProperty.FIGHT_PROP_MAX_GRASS_ENERGY), + Electric (4, FightProperty.FIGHT_PROP_CUR_ELEC_ENERGY, FightProperty.FIGHT_PROP_MAX_ELEC_ENERGY, 10401, "TeamResonance_Electric_Lv2"), + Ice (5, FightProperty.FIGHT_PROP_CUR_ICE_ENERGY, FightProperty.FIGHT_PROP_MAX_ICE_ENERGY, 10601, "TeamResonance_Ice_Lv2"), + Frozen (6, FightProperty.FIGHT_PROP_CUR_ICE_ENERGY, FightProperty.FIGHT_PROP_MAX_ICE_ENERGY), + Wind (7, FightProperty.FIGHT_PROP_CUR_WIND_ENERGY, FightProperty.FIGHT_PROP_MAX_WIND_ENERGY, 10301, "TeamResonance_Wind_Lv2"), + Rock (8, FightProperty.FIGHT_PROP_CUR_ROCK_ENERGY, FightProperty.FIGHT_PROP_MAX_ROCK_ENERGY, 10701, "TeamResonance_Rock_Lv2"), + AntiFire (9, FightProperty.FIGHT_PROP_CUR_FIRE_ENERGY, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY), + Default (255, FightProperty.FIGHT_PROP_CUR_FIRE_ENERGY, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY, 10801, "TeamResonance_AllDifferent"); private final int value; private final int teamResonanceId; - private final FightProperty energyProperty; + private final FightProperty curEnergyProp; + private final FightProperty maxEnergyProp; private final int configHash; private static final Int2ObjectMap map = new Int2ObjectOpenHashMap<>(); private static final Map stringMap = new HashMap<>(); @@ -35,13 +36,14 @@ public enum ElementType { }); } - private ElementType(int value, FightProperty energyProperty) { - this(value, energyProperty, 0, null); + private ElementType(int value, FightProperty curEnergyProp, FightProperty maxEnergyProp) { + this(value, curEnergyProp, maxEnergyProp, 0, null); } - private ElementType(int value, FightProperty energyProperty, int teamResonanceId, String configName) { + private ElementType(int value, FightProperty curEnergyProp, FightProperty maxEnergyProp, int teamResonanceId, String configName) { this.value = value; - this.energyProperty = energyProperty; + this.curEnergyProp = curEnergyProp; + this.maxEnergyProp = maxEnergyProp; this.teamResonanceId = teamResonanceId; if (configName != null) { this.configHash = Utils.abilityHash(configName); @@ -54,8 +56,12 @@ public enum ElementType { return value; } - public FightProperty getEnergyProperty() { - return energyProperty; + public FightProperty getCurEnergyProp() { + return curEnergyProp; + } + + public FightProperty getMaxEnergyProp() { + return maxEnergyProp; } public int getTeamResonanceId() { diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java index 2487df063..53d141a99 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java @@ -62,7 +62,7 @@ public class HandlerSetPlayerBornDataReq extends PacketHandler { // Create avatar if (player.getAvatars().getAvatarCount() == 0) { Avatar mainCharacter = new Avatar(avatarId); - mainCharacter.setSkillDepot(GameData.getAvatarSkillDepotDataMap().get(startingSkillDepot)); + mainCharacter.setSkillDepotData(GameData.getAvatarSkillDepotDataMap().get(startingSkillDepot)); player.addAvatar(mainCharacter); player.setMainCharacterId(avatarId); player.setHeadImage(avatarId); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketEntityFightPropChangeReasonNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketEntityFightPropChangeReasonNotify.java index 5778f711a..366354a40 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketEntityFightPropChangeReasonNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketEntityFightPropChangeReasonNotify.java @@ -11,21 +11,27 @@ import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason; import java.util.List; public class PacketEntityFightPropChangeReasonNotify extends BasePacket { + public PacketEntityFightPropChangeReasonNotify(GameEntity entity, FightProperty prop, Float value, List param, PropChangeReason reason, ChangeHpReason changeHpReason) { super(PacketOpcodes.EntityFightPropChangeReasonNotify); + EntityFightPropChangeReasonNotify.Builder proto = EntityFightPropChangeReasonNotify.newBuilder() .setEntityId(entity.getId()) .setPropType(prop.getId()) .setPropDelta(value) .setReason(reason) .setChangeHpReason(changeHpReason); - for(int p: param){ + + for(int p : param){ proto.addParamList(p); } + this.setData(proto); } + public PacketEntityFightPropChangeReasonNotify(GameEntity entity, FightProperty prop, Float value, PropChangeReason reason, ChangeHpReason changeHpReason) { super(PacketOpcodes.EntityFightPropChangeReasonNotify); + EntityFightPropChangeReasonNotify proto = EntityFightPropChangeReasonNotify.newBuilder() .setEntityId(entity.getId()) .setPropType(prop.getId()) @@ -33,6 +39,20 @@ public class PacketEntityFightPropChangeReasonNotify extends BasePacket { .setReason(reason) .setChangeHpReason(changeHpReason) .build(); + + this.setData(proto); + } + + public PacketEntityFightPropChangeReasonNotify(GameEntity entity, FightProperty prop, Float value, PropChangeReason reason) { + super(PacketOpcodes.EntityFightPropChangeReasonNotify); + + EntityFightPropChangeReasonNotify proto = EntityFightPropChangeReasonNotify.newBuilder() + .setEntityId(entity.getId()) + .setPropType(prop.getId()) + .setPropDelta(value) + .setReason(reason) + .build(); + this.setData(proto); } } From 55389d3a5cd064f6ad23ebde7278bbdac3ea4c2c Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Sun, 8 May 2022 05:39:12 -0700 Subject: [PATCH 4/7] Optimize invoke packet handling --- .../java/emu/grasscutter/game/player/InvokeHandler.java | 2 +- .../packet/recv/HandlerAbilityInvocationsNotify.java | 4 ---- .../packet/recv/HandlerCombatInvocationsNotify.java | 8 -------- .../server/packet/recv/HandlerUnionCmdNotify.java | 9 +++++++++ 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/player/InvokeHandler.java b/src/main/java/emu/grasscutter/game/player/InvokeHandler.java index edfcbfc83..b8a9ed89f 100644 --- a/src/main/java/emu/grasscutter/game/player/InvokeHandler.java +++ b/src/main/java/emu/grasscutter/game/player/InvokeHandler.java @@ -30,7 +30,7 @@ public class InvokeHandler { } public synchronized void update(Player player) { - if (player.getWorld() == null) { + if (player.getWorld() == null || player.getScene() == null) { this.entryListForwardAll.clear(); this.entryListForwardAllExceptCur.clear(); this.entryListForwardHost.clear(); diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAbilityInvocationsNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAbilityInvocationsNotify.java index 8be2d1c1f..a5d4c7f36 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAbilityInvocationsNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAbilityInvocationsNotify.java @@ -19,10 +19,6 @@ public class HandlerAbilityInvocationsNotify extends PacketHandler { session.getPlayer().getAbilityManager().onAbilityInvoke(entry); session.getPlayer().getAbilityInvokeHandler().addEntry(entry.getForwardType(), entry); } - - if (notif.getInvokesList().size() > 0) { - session.getPlayer().getAbilityInvokeHandler().update(session.getPlayer()); - } } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerCombatInvocationsNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerCombatInvocationsNotify.java index 36252f828..50fca5101 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerCombatInvocationsNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerCombatInvocationsNotify.java @@ -74,14 +74,6 @@ public class HandlerCombatInvocationsNotify extends PacketHandler { session.getPlayer().getCombatInvokeHandler().addEntry(entry.getForwardType(), entry); } - - if (notif.getInvokeListList().size() > 0) { - session.getPlayer().getCombatInvokeHandler().update(session.getPlayer()); - } - // Handle attack results last - while (!session.getPlayer().getAttackResults().isEmpty()) { - session.getPlayer().getScene().handleAttack(session.getPlayer().getAttackResults().poll()); - } } private void handleFallOnGround(GameSession session, GameEntity entity, MotionState motionState) { diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerUnionCmdNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerUnionCmdNotify.java index 1f4a9e7f3..2468f675f 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerUnionCmdNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerUnionCmdNotify.java @@ -15,5 +15,14 @@ public class HandlerUnionCmdNotify extends PacketHandler { for (UnionCmd cmd : req.getCmdListList()) { session.getServer().getPacketHandler().handle(session, cmd.getMessageId(), EMPTY_BYTE_ARRAY, cmd.getBody().toByteArray()); } + + // Update + session.getPlayer().getCombatInvokeHandler().update(session.getPlayer()); + session.getPlayer().getAbilityInvokeHandler().update(session.getPlayer()); + + // Handle attack results last + while (!session.getPlayer().getAttackResults().isEmpty()) { + session.getPlayer().getScene().handleAttack(session.getPlayer().getAttackResults().poll()); + } } } From 9ad44f5c10abce4be6a6df7f11d619ac4a24d6f3 Mon Sep 17 00:00:00 2001 From: Akka <104902222+Akka0@users.noreply.github.com> Date: Sat, 7 May 2022 21:47:13 +0800 Subject: [PATCH 5/7] Monsters tide turn by turn && Ban User Skill && Lua functions --- src/main/resources/logback.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index bd0740fca..5ab1957e5 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -24,4 +24,6 @@ + + \ No newline at end of file From 9fc4b916c8be1a1cb0183e8d0b7a8cebbba8552d Mon Sep 17 00:00:00 2001 From: Akka <104902222+Akka0@users.noreply.github.com> Date: Sun, 8 May 2022 17:11:02 +0800 Subject: [PATCH 6/7] Persist Tower Data && Set The Tower Schedule --- build.gradle | 2 +- data/TowerSchedule.json | 5 ++ .../command/commands/UnlockTowerCommand.java | 32 +++++++ .../java/emu/grasscutter/data/GameData.java | 4 + .../data/def/TowerScheduleData.java | 70 ++++++++++++++++ .../dungeons/TowerDungeonSettleListener.java | 16 ++-- .../game/tower/TowerLevelRecord.java | 64 ++++++++++++++ .../grasscutter/game/tower/TowerManager.java | 83 +++++++++++++++---- .../game/tower/TowerScheduleConfig.java | 35 ++++++++ .../game/tower/TowerScheduleManager.java | 75 +++++++++++++++++ .../scripts/SceneScriptManager.java | 29 ++++--- .../emu/grasscutter/scripts/ScriptLib.java | 32 ++++--- .../grasscutter/server/game/GameServer.java | 8 +- .../packet/recv/HandlerTowerAllDataReq.java | 5 +- .../send/PacketDungeonSettleNotify.java | 2 +- .../packet/send/PacketTowerAllDataRsp.java | 57 +++++++++---- .../PacketTowerFloorRecordChangeNotify.java | 6 +- .../send/PacketTowerLevelStarCondNotify.java | 32 +++++++ .../emu/grasscutter/utils/DateHelper.java | 6 +- src/main/resources/languages/en-US.json | 3 + 20 files changed, 500 insertions(+), 66 deletions(-) create mode 100644 data/TowerSchedule.json create mode 100644 src/main/java/emu/grasscutter/command/commands/UnlockTowerCommand.java create mode 100644 src/main/java/emu/grasscutter/data/def/TowerScheduleData.java create mode 100644 src/main/java/emu/grasscutter/game/tower/TowerLevelRecord.java create mode 100644 src/main/java/emu/grasscutter/game/tower/TowerScheduleConfig.java create mode 100644 src/main/java/emu/grasscutter/game/tower/TowerScheduleManager.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketTowerLevelStarCondNotify.java diff --git a/build.gradle b/build.gradle index eefef5b48..dd8fa9fa0 100644 --- a/build.gradle +++ b/build.gradle @@ -70,7 +70,7 @@ dependencies { implementation group: 'io.netty', name: 'netty-all', version: '4.1.71.Final' - implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.8' + implementation group: 'com.google.code.gson', name: 'gson', version: '2.9.0' implementation group: 'com.google.protobuf', name: 'protobuf-java', version: '3.18.2' implementation group: 'org.reflections', name: 'reflections', version: '0.10.2' diff --git a/data/TowerSchedule.json b/data/TowerSchedule.json new file mode 100644 index 000000000..b93100645 --- /dev/null +++ b/data/TowerSchedule.json @@ -0,0 +1,5 @@ +{ + "scheduleId" : 1, + "scheduleStartTime" : "2022-05-01T00:00:00+08:00", + "nextScheduleChangeTime" : "2022-05-30T00:00:00+08:00" +} \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/command/commands/UnlockTowerCommand.java b/src/main/java/emu/grasscutter/command/commands/UnlockTowerCommand.java new file mode 100644 index 000000000..e0fce695c --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/UnlockTowerCommand.java @@ -0,0 +1,32 @@ +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.tower.TowerLevelRecord; + +import java.util.List; + +import static emu.grasscutter.utils.Language.translate; + +@Command(label = "unlocktower", usage = "unlocktower", aliases = {"ut"}, + description = "Unlock all levels of tower", permission = "player.tower") +public class UnlockTowerCommand implements CommandHandler { + + @Override + public void execute(Player sender, Player targetPlayer, List args) { + unlockFloor(sender, sender.getServer().getTowerScheduleManager() + .getCurrentTowerScheduleData().getEntranceFloorId()); + + unlockFloor(sender, sender.getServer().getTowerScheduleManager() + .getScheduleFloors()); + + CommandHandler.sendMessage(sender, translate("commands.tower.unlock_done")); + } + + public void unlockFloor(Player player, List floors){ + floors.stream() + .filter(id -> !player.getTowerManager().getRecordMap().containsKey(id)) + .forEach(id -> player.getTowerManager().getRecordMap().put(id, new TowerLevelRecord(id))); + } +} diff --git a/src/main/java/emu/grasscutter/data/GameData.java b/src/main/java/emu/grasscutter/data/GameData.java index f06d1bda6..ac2472192 100644 --- a/src/main/java/emu/grasscutter/data/GameData.java +++ b/src/main/java/emu/grasscutter/data/GameData.java @@ -73,6 +73,7 @@ public class GameData { private static final Int2ObjectMap rewardPreviewDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap towerFloorDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap towerLevelDataMap = new Int2ObjectOpenHashMap<>(); + private static final Int2ObjectMap towerScheduleDataMap = new Int2ObjectOpenHashMap<>(); // Cache private static Map> fetters = new HashMap<>(); @@ -327,4 +328,7 @@ public class GameData { public static Int2ObjectMap getTowerLevelDataMap(){ return towerLevelDataMap; } + public static Int2ObjectMap getTowerScheduleDataMap(){ + return towerScheduleDataMap; + } } diff --git a/src/main/java/emu/grasscutter/data/def/TowerScheduleData.java b/src/main/java/emu/grasscutter/data/def/TowerScheduleData.java new file mode 100644 index 000000000..017776c06 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/def/TowerScheduleData.java @@ -0,0 +1,70 @@ +package emu.grasscutter.data.def; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; + +import java.util.List; + +@ResourceType(name = "TowerScheduleExcelConfigData.json") +public class TowerScheduleData extends GameResource { + private int ScheduleId; + private List EntranceFloorId; + private List Schedules; + private int MonthlyLevelConfigId; + @Override + public int getId() { + return ScheduleId; + } + + @Override + public void onLoad() { + super.onLoad(); + this.Schedules = this.Schedules.stream() + .filter(item -> item.getFloorList().size() > 0) + .toList(); + } + + public int getScheduleId() { + return ScheduleId; + } + + public void setScheduleId(int scheduleId) { + ScheduleId = scheduleId; + } + + public List getEntranceFloorId() { + return EntranceFloorId; + } + + public void setEntranceFloorId(List entranceFloorId) { + EntranceFloorId = entranceFloorId; + } + + public List getSchedules() { + return Schedules; + } + + public void setSchedules(List schedules) { + Schedules = schedules; + } + + public int getMonthlyLevelConfigId() { + return MonthlyLevelConfigId; + } + + public void setMonthlyLevelConfigId(int monthlyLevelConfigId) { + MonthlyLevelConfigId = monthlyLevelConfigId; + } + + public static class ScheduleDetail{ + private List FloorList; + + public List getFloorList() { + return FloorList; + } + + public void setFloorList(List floorList) { + FloorList = floorList; + } + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/TowerDungeonSettleListener.java b/src/main/java/emu/grasscutter/game/dungeons/TowerDungeonSettleListener.java index 5b1ff7a30..c480d047f 100644 --- a/src/main/java/emu/grasscutter/game/dungeons/TowerDungeonSettleListener.java +++ b/src/main/java/emu/grasscutter/game/dungeons/TowerDungeonSettleListener.java @@ -12,12 +12,18 @@ public class TowerDungeonSettleListener implements DungeonSettleListener { scene.setAutoCloseTime(Utils.getCurrentSeconds() + 1000); var towerManager = scene.getPlayers().get(0).getTowerManager(); - towerManager.notifyCurLevelRecordChangeWhenDone(); - scene.broadcastPacket(new PacketTowerFloorRecordChangeNotify(towerManager.getCurrentFloorId())); - scene.broadcastPacket(new PacketDungeonSettleNotify(scene.getChallenge(), - true, + towerManager.notifyCurLevelRecordChangeWhenDone(3); + scene.broadcastPacket(new PacketTowerFloorRecordChangeNotify( + towerManager.getCurrentFloorId(), + 3, + towerManager.canEnterScheduleFloor() + )); + + scene.broadcastPacket(new PacketDungeonSettleNotify( + scene.getChallenge(), + towerManager.hasNextFloor(), towerManager.hasNextLevel(), - towerManager.getNextFloorId() + towerManager.hasNextLevel() ? 0 : towerManager.getNextFloorId() )); } diff --git a/src/main/java/emu/grasscutter/game/tower/TowerLevelRecord.java b/src/main/java/emu/grasscutter/game/tower/TowerLevelRecord.java new file mode 100644 index 000000000..5a65f63ed --- /dev/null +++ b/src/main/java/emu/grasscutter/game/tower/TowerLevelRecord.java @@ -0,0 +1,64 @@ +package emu.grasscutter.game.tower; + +import dev.morphia.annotations.Entity; + +import java.util.HashMap; +import java.util.Map; + +@Entity +public class TowerLevelRecord { + /** + * floorId in config + */ + private int floorId; + /** + * LevelId - Stars + */ + private Map passedLevelMap; + + private int floorStarRewardProgress; + + public TowerLevelRecord setLevelStars(int levelId, int stars){ + passedLevelMap.put(levelId, stars); + return this; + } + + public int getStarCount() { + return passedLevelMap.values().stream().mapToInt(Integer::intValue).sum(); + } + + public TowerLevelRecord(){ + + } + + public TowerLevelRecord(int floorId){ + this.floorId = floorId; + this.passedLevelMap = new HashMap<>(); + this.floorStarRewardProgress = 0; + } + + public int getFloorId() { + return floorId; + } + + public void setFloorId(int floorId) { + this.floorId = floorId; + } + + public Map getPassedLevelMap() { + return passedLevelMap; + } + + public void setPassedLevelMap(Map passedLevelMap) { + this.passedLevelMap = passedLevelMap; + } + + public int getFloorStarRewardProgress() { + return floorStarRewardProgress; + } + + public void setFloorStarRewardProgress(int floorStarRewardProgress) { + this.floorStarRewardProgress = floorStarRewardProgress; + } + +} diff --git a/src/main/java/emu/grasscutter/game/tower/TowerManager.java b/src/main/java/emu/grasscutter/game/tower/TowerManager.java index 409549a1f..9346ffead 100644 --- a/src/main/java/emu/grasscutter/game/tower/TowerManager.java +++ b/src/main/java/emu/grasscutter/game/tower/TowerManager.java @@ -9,10 +9,12 @@ import emu.grasscutter.game.dungeons.TowerDungeonSettleListener; import emu.grasscutter.game.player.Player; import emu.grasscutter.server.packet.send.PacketCanUseSkillNotify; import emu.grasscutter.server.packet.send.PacketTowerCurLevelRecordChangeNotify; - import emu.grasscutter.server.packet.send.PacketTowerEnterLevelRsp; +import emu.grasscutter.server.packet.send.PacketTowerLevelStarCondNotify; +import java.util.HashMap; import java.util.List; +import java.util.Map; @Entity public class TowerManager { @@ -26,11 +28,19 @@ public class TowerManager { this.player = player; } + /** + * the floor players chose + */ private int currentFloorId; private int currentLevel; @Transient private int currentLevelId; + /** + * floorId - Record + */ + private Map recordMap; + @Transient private int entryScene; @@ -38,7 +48,26 @@ public class TowerManager { return currentFloorId; } + public int getCurrentLevelId(){ + return this.currentLevelId + currentLevel; + } + + /** + * form 1-3 + */ + public int getCurrentLevel(){ + return currentLevel + 1; + } private static final List towerDungeonSettleListener = List.of(new TowerDungeonSettleListener()); + + public Map getRecordMap() { + if(recordMap == null){ + recordMap = new HashMap<>(); + recordMap.put(1001, new TowerLevelRecord(1001)); + } + return recordMap; + } + public void teamSelect(int floor, List> towerTeams) { var floorData = GameData.getTowerFloorDataMap().get(floor); @@ -54,51 +83,73 @@ public class TowerManager { entryScene = player.getSceneId(); } - player.getTeamManager().setupTemporaryTeam(towerTeams); } public void enterLevel(int enterPointId) { - var levelData = GameData.getTowerLevelDataMap().get(currentLevelId + currentLevel); + var levelData = GameData.getTowerLevelDataMap().get(getCurrentLevelId()); - this.currentLevel++; - var id = levelData.getDungeonId(); + var dungeonId = levelData.getDungeonId(); notifyCurLevelRecordChange(); // use team user choose player.getTeamManager().useTemporaryTeam(0); - player.getServer().getDungeonManager().handoffDungeon(player, id, + player.getServer().getDungeonManager().handoffDungeon(player, dungeonId, towerDungeonSettleListener); // make sure user can exit dungeon correctly player.getScene().setPrevScene(entryScene); player.getScene().setPrevScenePoint(enterPointId); - player.getSession().send(new PacketTowerEnterLevelRsp(currentFloorId, currentLevel)); + player.getSession().send(new PacketTowerEnterLevelRsp(currentFloorId, getCurrentLevel())); // stop using skill player.getSession().send(new PacketCanUseSkillNotify(false)); + // notify the cond of stars + player.getSession().send(new PacketTowerLevelStarCondNotify(currentFloorId, getCurrentLevel())); } public void notifyCurLevelRecordChange(){ - player.getSession().send(new PacketTowerCurLevelRecordChangeNotify(currentFloorId, currentLevel)); + player.getSession().send(new PacketTowerCurLevelRecordChangeNotify(currentFloorId, getCurrentLevel())); } - public void notifyCurLevelRecordChangeWhenDone(){ - player.getSession().send(new PacketTowerCurLevelRecordChangeNotify(currentFloorId, currentLevel + 1)); + public void notifyCurLevelRecordChangeWhenDone(int stars){ + if(!recordMap.containsKey(currentFloorId)){ + recordMap.put(currentFloorId, + new TowerLevelRecord(currentFloorId).setLevelStars(getCurrentLevelId(),stars)); + }else{ + recordMap.put(currentFloorId, + recordMap.get(currentFloorId).setLevelStars(getCurrentLevelId(),stars)); + } + + this.currentLevel++; + + if(!hasNextLevel()){ + // set up the next floor + recordMap.put(getNextFloorId(), new TowerLevelRecord(getNextFloorId())); + player.getSession().send(new PacketTowerCurLevelRecordChangeNotify(getNextFloorId(), 1)); + }else{ + player.getSession().send(new PacketTowerCurLevelRecordChangeNotify(currentFloorId, getCurrentLevel())); + } } public boolean hasNextLevel(){ return this.currentLevel < 3; } - public int getNextFloorId() { - if(hasNextLevel()){ - return 0; - } - this.currentFloorId++; - return this.currentFloorId; + return player.getServer().getTowerScheduleManager().getNextFloorId(this.currentFloorId); + } + public boolean hasNextFloor(){ + return player.getServer().getTowerScheduleManager().getNextFloorId(this.currentFloorId) > 0; } public void clearEntry() { this.entryScene = 0; } + + public boolean canEnterScheduleFloor(){ + if(!recordMap.containsKey(player.getServer().getTowerScheduleManager().getLastEntranceFloor())){ + return false; + } + return recordMap.get(player.getServer().getTowerScheduleManager().getLastEntranceFloor()) + .getStarCount() >= 6; + } } diff --git a/src/main/java/emu/grasscutter/game/tower/TowerScheduleConfig.java b/src/main/java/emu/grasscutter/game/tower/TowerScheduleConfig.java new file mode 100644 index 000000000..35afbc7ba --- /dev/null +++ b/src/main/java/emu/grasscutter/game/tower/TowerScheduleConfig.java @@ -0,0 +1,35 @@ +package emu.grasscutter.game.tower; + +import java.util.Date; + +public class TowerScheduleConfig { + private int scheduleId; + + private Date scheduleStartTime; + private Date nextScheduleChangeTime; + + + public int getScheduleId() { + return scheduleId; + } + + public void setScheduleId(int scheduleId) { + this.scheduleId = scheduleId; + } + + public Date getScheduleStartTime() { + return scheduleStartTime; + } + + public void setScheduleStartTime(Date scheduleStartTime) { + this.scheduleStartTime = scheduleStartTime; + } + + public Date getNextScheduleChangeTime() { + return nextScheduleChangeTime; + } + + public void setNextScheduleChangeTime(Date nextScheduleChangeTime) { + this.nextScheduleChangeTime = nextScheduleChangeTime; + } +} diff --git a/src/main/java/emu/grasscutter/game/tower/TowerScheduleManager.java b/src/main/java/emu/grasscutter/game/tower/TowerScheduleManager.java new file mode 100644 index 000000000..33f5c158d --- /dev/null +++ b/src/main/java/emu/grasscutter/game/tower/TowerScheduleManager.java @@ -0,0 +1,75 @@ +package emu.grasscutter.game.tower; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.def.TowerScheduleData; +import emu.grasscutter.server.game.GameServer; + +import java.io.FileReader; +import java.util.List; + +public class TowerScheduleManager { + private final GameServer gameServer; + + public GameServer getGameServer() { + return gameServer; + } + + public TowerScheduleManager(GameServer gameServer) { + this.gameServer = gameServer; + this.load(); + } + + private TowerScheduleConfig towerScheduleConfig; + + public synchronized void load(){ + try (FileReader fileReader = new FileReader(Grasscutter.getConfig().DATA_FOLDER + "TowerSchedule.json")) { + towerScheduleConfig = Grasscutter.getGsonFactory().fromJson(fileReader, TowerScheduleConfig.class); + + } catch (Exception e) { + Grasscutter.getLogger().error("Unable to load tower schedule config.", e); + } + } + + public TowerScheduleConfig getTowerScheduleConfig() { + return towerScheduleConfig; + } + + public TowerScheduleData getCurrentTowerScheduleData(){ + var data = GameData.getTowerScheduleDataMap().get(towerScheduleConfig.getScheduleId()); + if(data == null){ + Grasscutter.getLogger().error("Could not get current tower schedule data by config:{}", towerScheduleConfig); + } + return data; + } + + public List getScheduleFloors() { + return getCurrentTowerScheduleData().getSchedules().get(0).getFloorList(); + } + + public int getNextFloorId(int floorId){ + var entranceFloors = getCurrentTowerScheduleData().getEntranceFloorId(); + var nextId = 0; + // find in entrance floors first + for(int i=0;i getBlocks() { return blocks; } @@ -237,16 +241,16 @@ public class SceneScriptManager { for (SceneSuite suite : group.suites) { suite.sceneMonsters = new ArrayList<>(suite.monsters.size()); - for (int id : suite.monsters) { - try { - SceneMonster monster = (SceneMonster) map.get(id); - if (monster != null) { - suite.sceneMonsters.add(monster); + suite.monsters.forEach(id -> { + Object objEntry = map.get(id.intValue()); + if (objEntry instanceof Map.Entry monsterEntry) { + Object monster = monsterEntry.getValue(); + if(monster instanceof SceneMonster sceneMonster){ + suite.sceneMonsters.add(sceneMonster); } - } catch (Exception e) { - continue; } - } + }); + suite.sceneGadgets = new ArrayList<>(suite.gadgets.size()); for (int id : suite.gadgets) { try { @@ -320,13 +324,15 @@ public class SceneScriptManager { } public void spawnMonstersInGroup(SceneGroup group, int suiteIndex) { - this.currentGroup = group; - this.monsterSceneLimit = 0; var suite = group.getSuiteByIndex(suiteIndex); if(suite == null){ return; } - suite.sceneMonsters.forEach(mob -> spawnMonstersInGroup(group, mob)); + if(suite.sceneMonsters.size() > 0){ + this.currentGroup = group; + this.monsterSceneLimit = 0; + suite.sceneMonsters.forEach(mob -> spawnMonstersInGroup(group, mob)); + } } public void spawnMonstersInGroup(SceneGroup group) { @@ -401,6 +407,7 @@ public class SceneScriptManager { spawnMonstersInGroup(this.currentGroup, this.currentGroup.monsters.get(this.monsterOrders.poll())); }else if(this.monsterAlive.get() == 0){ // spawn the last turn of monsters + //callEvent(EventType.EVENT_MONSTER_TIDE_DIE, new ScriptArgs()); while(!this.monsterOrders.isEmpty()){ spawnMonstersInGroup(this.currentGroup, this.currentGroup.monsters.get(this.monsterOrders.poll())); } diff --git a/src/main/java/emu/grasscutter/scripts/ScriptLib.java b/src/main/java/emu/grasscutter/scripts/ScriptLib.java index 1b9badc11..1c4bbd0f2 100644 --- a/src/main/java/emu/grasscutter/scripts/ScriptLib.java +++ b/src/main/java/emu/grasscutter/scripts/ScriptLib.java @@ -118,7 +118,7 @@ public class ScriptLib { challengeIndex,groupId,ordersConfigId,tideCount,sceneLimit,param6); SceneGroup group = getSceneScriptManager().getGroupById(groupId); - + if (group == null || group.monsters == null) { return 1; } @@ -136,8 +136,7 @@ public class ScriptLib { if (group == null || group.monsters == null) { return 1; } - - // TODO just spawn all from group for now + this.getSceneScriptManager().spawnMonstersInGroup(group, suite); return 0; @@ -159,7 +158,13 @@ public class ScriptLib { if (group == null || group.monsters == null) { return 1; } - + + if(getSceneScriptManager().getScene().getChallenge() != null && + getSceneScriptManager().getScene().getChallenge().inProgress()) + { + return 0; + } + DungeonChallenge challenge = new DungeonChallenge(getSceneScriptManager().getScene(), group); challenge.setChallengeId(challengeId); challenge.setChallengeIndex(challengeIndex); @@ -249,7 +254,7 @@ public class ScriptLib { var1); return (int) getSceneScriptManager().getScene().getEntities().values().stream() - .filter(e -> e instanceof EntityMonster) + .filter(e -> e instanceof EntityMonster && e.getGroupId() == getSceneScriptManager().getCurrentGroup().id) .count(); } public int SetMonsterBattleByGroup(int var1, int var2, int var3){ @@ -266,13 +271,11 @@ public class ScriptLib { return 0; } // 8-1 - public int GetGroupVariableValueByGroup(int var1, String var2, int var3){ - logger.debug("[LUA] Call GetGroupVariableValueByGroup with {},{},{}", - var1,var2,var3); + public int GetGroupVariableValueByGroup(String name, int groupId){ + logger.debug("[LUA] Call GetGroupVariableValueByGroup with {},{}", + name,groupId); - //TODO - - return getSceneScriptManager().getVariables().getOrDefault(var2, 0); + return getSceneScriptManager().getVariables().getOrDefault(name, 0); } public int SetIsAllowUseSkill(int canUse, int var2){ @@ -299,4 +302,11 @@ public class ScriptLib { return 0; } + public int SetGroupVariableValueByGroup(String key, int value, int groupId){ + logger.debug("[LUA] Call SetGroupVariableValueByGroup with {},{},{}", + key,value,groupId); + + return 0; + } + } diff --git a/src/main/java/emu/grasscutter/server/game/GameServer.java b/src/main/java/emu/grasscutter/server/game/GameServer.java index 7ce8488ef..cb0e4965d 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServer.java +++ b/src/main/java/emu/grasscutter/server/game/GameServer.java @@ -15,6 +15,7 @@ import emu.grasscutter.game.managers.InventoryManager; import emu.grasscutter.game.managers.MultiplayerManager; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.shop.ShopManager; +import emu.grasscutter.game.tower.TowerScheduleManager; import emu.grasscutter.game.world.World; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail; @@ -54,6 +55,7 @@ public final class GameServer extends KcpServer { private final DropManager dropManager; private final CombineManger combineManger; + private final TowerScheduleManager towerScheduleManager; public GameServer() { this(new InetSocketAddress( @@ -82,7 +84,7 @@ public final class GameServer extends KcpServer { this.dropManager = new DropManager(this); this.expeditionManager = new ExpeditionManager(this); this.combineManger = new CombineManger(this); - + this.towerScheduleManager = new TowerScheduleManager(this); // Hook into shutdown event. Runtime.getRuntime().addShutdownHook(new Thread(this::onServerShutdown)); } @@ -139,6 +141,10 @@ public final class GameServer extends KcpServer { return this.combineManger; } + public TowerScheduleManager getTowerScheduleManager() { + return towerScheduleManager; + } + public TaskMap getTaskMap() { return this.taskMap; } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerTowerAllDataReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerTowerAllDataReq.java index 2a9ef2004..38462882f 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerTowerAllDataReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerTowerAllDataReq.java @@ -11,7 +11,10 @@ public class HandlerTowerAllDataReq extends PacketHandler { @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - session.send(new PacketTowerAllDataRsp()); + session.send(new PacketTowerAllDataRsp( + session.getServer().getTowerScheduleManager(), + session.getPlayer().getTowerManager() + )); } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonSettleNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonSettleNotify.java index 479029243..56d844d8d 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonSettleNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonSettleNotify.java @@ -46,7 +46,7 @@ public class PacketDungeonSettleNotify extends BasePacket { .setCount(1000) .build()) ; - if(nextFloorId > 0){ + if(nextFloorId > 0 && canJump){ towerLevelEndNotify.setNextFloorId(nextFloorId); } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketTowerAllDataRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketTowerAllDataRsp.java index d2d2376e6..654aa4a07 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketTowerAllDataRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketTowerAllDataRsp.java @@ -1,37 +1,64 @@ package emu.grasscutter.server.packet.send; -import emu.grasscutter.data.GameData; -import emu.grasscutter.data.def.TowerFloorData; +import emu.grasscutter.game.tower.TowerManager; +import emu.grasscutter.game.tower.TowerScheduleManager; import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.TowerAllDataRspOuterClass.TowerAllDataRsp; import emu.grasscutter.net.proto.TowerCurLevelRecordOuterClass.TowerCurLevelRecord; import emu.grasscutter.net.proto.TowerFloorRecordOuterClass.TowerFloorRecord; +import emu.grasscutter.net.proto.TowerLevelRecordOuterClass; +import emu.grasscutter.utils.DateHelper; +import java.util.List; +import java.util.Map; import java.util.stream.Collectors; +import java.util.stream.IntStream; public class PacketTowerAllDataRsp extends BasePacket { - public PacketTowerAllDataRsp() { + public PacketTowerAllDataRsp(TowerScheduleManager towerScheduleManager, TowerManager towerManager) { super(PacketOpcodes.TowerAllDataRsp); - var list = GameData.getTowerFloorDataMap().values().stream() - .map(TowerFloorData::getFloorId) - .map(id -> TowerFloorRecord.newBuilder().setFloorId(id).build()) - .collect(Collectors.toList()); + var recordList = towerManager.getRecordMap().values().stream() + .map(rec -> TowerFloorRecord.newBuilder() + .setFloorId(rec.getFloorId()) + .setFloorStarRewardProgress(rec.getFloorStarRewardProgress()) + .putAllPassedLevelMap(rec.getPassedLevelMap()) + .addAllPassedLevelRecordList(buildFromPassedLevelMap(rec.getPassedLevelMap())) + .build() + ) + .toList(); + + var openTimeMap = towerScheduleManager.getScheduleFloors().stream() + .collect(Collectors.toMap(x -> x, + y -> DateHelper.getUnixTime(towerScheduleManager.getTowerScheduleConfig() + .getScheduleStartTime())) + ); TowerAllDataRsp proto = TowerAllDataRsp.newBuilder() - .setTowerScheduleId(29) - .addAllTowerFloorRecordList(list) + .setTowerScheduleId(towerScheduleManager.getCurrentTowerScheduleData().getScheduleId()) + .addAllTowerFloorRecordList(recordList) .setCurLevelRecord(TowerCurLevelRecord.newBuilder().setIsEmpty(true)) - .setNextScheduleChangeTime(Integer.MAX_VALUE) - .putFloorOpenTimeMap(1024, 1630486800) - .putFloorOpenTimeMap(1025, 1630486800) - .putFloorOpenTimeMap(1026, 1630486800) - .putFloorOpenTimeMap(1027, 1630486800) - .setScheduleStartTime(1630486800) + .setScheduleStartTime(DateHelper.getUnixTime(towerScheduleManager.getTowerScheduleConfig() + .getScheduleStartTime())) + .setNextScheduleChangeTime(DateHelper.getUnixTime(towerScheduleManager.getTowerScheduleConfig() + .getNextScheduleChangeTime())) + .putAllFloorOpenTimeMap(openTimeMap) + .setIsFinishedEntranceFloor(towerManager.canEnterScheduleFloor()) .build(); this.setData(proto); } + + private List buildFromPassedLevelMap(Map map){ + return map.entrySet().stream() + .map(item -> TowerLevelRecordOuterClass.TowerLevelRecord.newBuilder() + .setLevelId(item.getKey()) + .addAllSatisfiedCondList(IntStream.range(1, item.getValue() + 1).boxed().toList()) + .build()) + .toList(); + + } + } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketTowerFloorRecordChangeNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketTowerFloorRecordChangeNotify.java index c0ed414a8..5ab091901 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketTowerFloorRecordChangeNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketTowerFloorRecordChangeNotify.java @@ -8,13 +8,13 @@ import emu.grasscutter.net.proto.TowerLevelRecordOuterClass.TowerLevelRecord; public class PacketTowerFloorRecordChangeNotify extends BasePacket { - public PacketTowerFloorRecordChangeNotify(int floorId) { + public PacketTowerFloorRecordChangeNotify(int floorId, int stars, boolean canEnterScheduleFloor) { super(PacketOpcodes.TowerFloorRecordChangeNotify); TowerFloorRecordChangeNotify proto = TowerFloorRecordChangeNotify.newBuilder() .addTowerFloorRecordList(TowerFloorRecord.newBuilder() .setFloorId(floorId) - .setFloorStarRewardProgress(3) + .setFloorStarRewardProgress(stars) .addPassedLevelRecordList(TowerLevelRecord.newBuilder() .setLevelId(1) .addSatisfiedCondList(1) @@ -22,7 +22,7 @@ public class PacketTowerFloorRecordChangeNotify extends BasePacket { .addSatisfiedCondList(3) .build()) .build()) - .setIsFinishedEntranceFloor(true) + .setIsFinishedEntranceFloor(canEnterScheduleFloor) .build(); this.setData(proto); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketTowerLevelStarCondNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketTowerLevelStarCondNotify.java new file mode 100644 index 000000000..c2c301e4e --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketTowerLevelStarCondNotify.java @@ -0,0 +1,32 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.TowerLevelStarCondDataOuterClass.TowerLevelStarCondData; +import emu.grasscutter.net.proto.TowerLevelStarCondNotifyOuterClass.TowerLevelStarCondNotify; + +public class PacketTowerLevelStarCondNotify extends BasePacket { + + public PacketTowerLevelStarCondNotify(int floorId, int levelIndex) { + super(PacketOpcodes.TowerLevelStarCondNotify); + + TowerLevelStarCondNotify proto = TowerLevelStarCondNotify.newBuilder() + .setFloorId(floorId) + .setLevelIndex(levelIndex) + .addCondDataList(TowerLevelStarCondData.newBuilder() + .setCondValue(1) + .build() + ) + .addCondDataList(TowerLevelStarCondData.newBuilder() + .setCondValue(2) + .build() + ) + .addCondDataList(TowerLevelStarCondData.newBuilder() + .setCondValue(3) + .build() + ) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/utils/DateHelper.java b/src/main/java/emu/grasscutter/utils/DateHelper.java index 7005d9457..1f1393760 100644 --- a/src/main/java/emu/grasscutter/utils/DateHelper.java +++ b/src/main/java/emu/grasscutter/utils/DateHelper.java @@ -1,7 +1,7 @@ package emu.grasscutter.utils; -import java.util.Date; import java.util.Calendar; +import java.util.Date; public final class DateHelper { public static Date onlyYearMonthDay(Date now) { @@ -13,4 +13,8 @@ public final class DateHelper { calendar.set(Calendar.MILLISECOND, 0); return calendar.getTime(); } + + public static int getUnixTime(Date localDateTime){ + return (int)(localDateTime.getTime() / 1000L); + } } diff --git a/src/main/resources/languages/en-US.json b/src/main/resources/languages/en-US.json index 9a7485608..4ec17a214 100644 --- a/src/main/resources/languages/en-US.json +++ b/src/main/resources/languages/en-US.json @@ -312,6 +312,9 @@ "success": "Teleported %s to %s, %s, %s in scene %s", "description": "Change the player's position." }, + "tower": { + "unlock_done": "Abyss Corridor's Floors are all unlocked now." + }, "weather": { "usage": "Usage: weather [climateId]", "success": "Changed weather to %s with climate %s", From 5d1f49579bd6311165762bb4bd8c7e0769749ad1 Mon Sep 17 00:00:00 2001 From: Yazawazi <47273265+Yazawazi@users.noreply.github.com> Date: Sun, 8 May 2022 21:07:37 +0800 Subject: [PATCH 7/7] feature(task): Implement pause, resume and cancel Use as `pauseTask(taskName)`. They return boolean values to tell the developer if a timed task can be paused/resumed/cancelled properly. A little bit of testing shows that pausing and then resuming may execute the task multiple times. --- src/main/java/emu/grasscutter/task/Task.java | 2 -- .../java/emu/grasscutter/task/TaskMap.java | 36 ++++++++++++++++++- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/main/java/emu/grasscutter/task/Task.java b/src/main/java/emu/grasscutter/task/Task.java index 1f35d16ce..2c930c0e3 100644 --- a/src/main/java/emu/grasscutter/task/Task.java +++ b/src/main/java/emu/grasscutter/task/Task.java @@ -1,7 +1,5 @@ package emu.grasscutter.task; -import org.quartz.JobDataMap; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/src/main/java/emu/grasscutter/task/TaskMap.java b/src/main/java/emu/grasscutter/task/TaskMap.java index fe067e795..a4b2ff02f 100644 --- a/src/main/java/emu/grasscutter/task/TaskMap.java +++ b/src/main/java/emu/grasscutter/task/TaskMap.java @@ -67,6 +67,40 @@ public final class TaskMap { return this; } + public boolean pauseTask(String taskName) { + try { + Scheduler scheduler = schedulerFactory.getScheduler(); + scheduler.pauseJob(new JobKey(taskName)); + } catch (SchedulerException e) { + e.printStackTrace(); + return false; + } + return true; + } + + public boolean resumeTask(String taskName) { + try { + Scheduler scheduler = schedulerFactory.getScheduler(); + scheduler.resumeJob(new JobKey(taskName)); + } catch (SchedulerException e) { + e.printStackTrace(); + return false; + } + return true; + } + + public boolean cancelTask(String taskName) { + Task task = this.annotations.get(taskName); + if (task == null) return false; + try { + this.unregisterTask(this.tasks.get(taskName)); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return true; + } + public TaskMap registerTask(String taskName, TaskHandler task) { Task annotation = task.getClass().getAnnotation(Task.class); this.annotations.put(taskName, annotation); @@ -116,7 +150,7 @@ public final class TaskMap { classes.forEach(annotated -> { try { Task taskData = annotated.getAnnotation(Task.class); - Object object = annotated.newInstance(); + Object object = annotated.getDeclaredConstructor().newInstance(); if (object instanceof TaskHandler) { this.registerTask(taskData.taskName(), (TaskHandler) object); if (taskData.executeImmediatelyAfterReset()) {