diff --git a/proto/AbilityActionGenerateElemBall.proto b/proto/AbilityActionGenerateElemBall.proto new file mode 100644 index 000000000..d048d75cd --- /dev/null +++ b/proto/AbilityActionGenerateElemBall.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +option java_package = "emu.grasscutter.net.proto"; + +import "Vector.proto"; + +message AbilityActionGenerateElemBall { + Vector pos = 1; + Vector rot = 2; + uint32 room_id = 3; +} diff --git a/proto/AbilityMetaModifierChange.proto b/proto/AbilityMetaModifierChange.proto new file mode 100644 index 000000000..43d87a819 --- /dev/null +++ b/proto/AbilityMetaModifierChange.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +option java_package = "emu.grasscutter.net.proto"; + +import "ModifierAction.proto"; +import "AbilityString.proto"; +import "AbilityAttachedModifier.proto"; +import "ModifierProperty.proto"; + +message AbilityMetaModifierChange { + ModifierAction action = 1; + AbilityString parent_ability_name = 2; + AbilityString parent_ability_override = 3; + AbilityAttachedModifier attached_instanced_modifier = 4; + repeated ModifierProperty properties = 5; + int32 modifier_local_id = 6; + bool is_mute_remote = 7; + uint32 apply_entity_id = 8; + bool is_attached_parent_ability = 9; + uint32 server_buff_uid = 10; +} diff --git a/proto/AbilityMetaReInitOverrideMap.proto b/proto/AbilityMetaReInitOverrideMap.proto new file mode 100644 index 000000000..3a8554004 --- /dev/null +++ b/proto/AbilityMetaReInitOverrideMap.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +option java_package = "emu.grasscutter.net.proto"; + +import "AbilityScalarValueEntry.proto"; + +message AbilityMetaReInitOverrideMap { + repeated AbilityScalarValueEntry override_map = 1; +} diff --git a/proto/ModifierAction.proto b/proto/ModifierAction.proto new file mode 100644 index 000000000..a7a1c37a7 --- /dev/null +++ b/proto/ModifierAction.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; + +option java_package = "emu.grasscutter.net.proto"; + +enum ModifierAction { + ADDED = 0; + REMOVED = 1; +} diff --git a/proto/ModifierProperty.proto b/proto/ModifierProperty.proto new file mode 100644 index 000000000..cc0aa742c --- /dev/null +++ b/proto/ModifierProperty.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +option java_package = "emu.grasscutter.net.proto"; + +import "AbilityString.proto"; + +message ModifierProperty { + AbilityString key = 1; + float value = 2; +} 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); } }