mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-02-05 01:25:34 +08:00
Implement AbilityManager
This commit is contained in:
parent
342cf33661
commit
6d1ef0d841
11
proto/AbilityActionGenerateElemBall.proto
Normal file
11
proto/AbilityActionGenerateElemBall.proto
Normal file
@ -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;
|
||||||
|
}
|
21
proto/AbilityMetaModifierChange.proto
Normal file
21
proto/AbilityMetaModifierChange.proto
Normal file
@ -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;
|
||||||
|
}
|
9
proto/AbilityMetaReInitOverrideMap.proto
Normal file
9
proto/AbilityMetaReInitOverrideMap.proto
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
option java_package = "emu.grasscutter.net.proto";
|
||||||
|
|
||||||
|
import "AbilityScalarValueEntry.proto";
|
||||||
|
|
||||||
|
message AbilityMetaReInitOverrideMap {
|
||||||
|
repeated AbilityScalarValueEntry override_map = 1;
|
||||||
|
}
|
8
proto/ModifierAction.proto
Normal file
8
proto/ModifierAction.proto
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
option java_package = "emu.grasscutter.net.proto";
|
||||||
|
|
||||||
|
enum ModifierAction {
|
||||||
|
ADDED = 0;
|
||||||
|
REMOVED = 1;
|
||||||
|
}
|
10
proto/ModifierProperty.proto
Normal file
10
proto/ModifierProperty.proto
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
option java_package = "emu.grasscutter.net.proto";
|
||||||
|
|
||||||
|
import "AbilityString.proto";
|
||||||
|
|
||||||
|
message ModifierProperty {
|
||||||
|
AbilityString key = 1;
|
||||||
|
float value = 2;
|
||||||
|
}
|
@ -9,6 +9,8 @@ import java.util.Map;
|
|||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.utils.Utils;
|
import emu.grasscutter.utils.Utils;
|
||||||
import emu.grasscutter.data.custom.AbilityEmbryoEntry;
|
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.OpenConfigEntry;
|
||||||
import emu.grasscutter.data.custom.ScenePointEntry;
|
import emu.grasscutter.data.custom.ScenePointEntry;
|
||||||
import emu.grasscutter.data.def.*;
|
import emu.grasscutter.data.def.*;
|
||||||
@ -22,6 +24,7 @@ public class GameData {
|
|||||||
// BinOutputs
|
// BinOutputs
|
||||||
private static final Int2ObjectMap<String> abilityHashes = new Int2ObjectOpenHashMap<>();
|
private static final Int2ObjectMap<String> abilityHashes = new Int2ObjectOpenHashMap<>();
|
||||||
private static final Map<String, AbilityEmbryoEntry> abilityEmbryos = new HashMap<>();
|
private static final Map<String, AbilityEmbryoEntry> abilityEmbryos = new HashMap<>();
|
||||||
|
private static final Map<String, AbilityModifierEntry> abilityModifiers = new HashMap<>();
|
||||||
private static final Map<String, OpenConfigEntry> openConfigEntries = new HashMap<>();
|
private static final Map<String, OpenConfigEntry> openConfigEntries = new HashMap<>();
|
||||||
private static final Map<String, ScenePointEntry> scenePointEntries = new HashMap<>();
|
private static final Map<String, ScenePointEntry> scenePointEntries = new HashMap<>();
|
||||||
|
|
||||||
@ -101,6 +104,10 @@ public class GameData {
|
|||||||
return abilityEmbryos;
|
return abilityEmbryos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Map<String, AbilityModifierEntry> getAbilityModifiers() {
|
||||||
|
return abilityModifiers;
|
||||||
|
}
|
||||||
|
|
||||||
public static Map<String, OpenConfigEntry> getOpenConfigEntries() {
|
public static Map<String, OpenConfigEntry> getOpenConfigEntries() {
|
||||||
return openConfigEntries;
|
return openConfigEntries;
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,11 @@ import emu.grasscutter.Grasscutter;
|
|||||||
import emu.grasscutter.data.common.PointData;
|
import emu.grasscutter.data.common.PointData;
|
||||||
import emu.grasscutter.data.common.ScenePointConfig;
|
import emu.grasscutter.data.common.ScenePointConfig;
|
||||||
import emu.grasscutter.data.custom.AbilityEmbryoEntry;
|
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.OpenConfigEntry;
|
||||||
import emu.grasscutter.data.custom.ScenePointEntry;
|
import emu.grasscutter.data.custom.ScenePointEntry;
|
||||||
import emu.grasscutter.game.world.SpawnDataEntry;
|
import emu.grasscutter.game.world.SpawnDataEntry;
|
||||||
@ -47,6 +52,7 @@ public class ResourceLoader {
|
|||||||
// Load ability lists
|
// Load ability lists
|
||||||
loadAbilityEmbryos();
|
loadAbilityEmbryos();
|
||||||
loadOpenConfig();
|
loadOpenConfig();
|
||||||
|
loadAbilityModifiers();
|
||||||
// Load resources
|
// Load resources
|
||||||
loadResources();
|
loadResources();
|
||||||
// Process into depots
|
// 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<AbilityConfigData> 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<String, AbilityModifier> 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() {
|
private static void loadSpawnData() {
|
||||||
// Read from cached file if exists
|
// Read from cached file if exists
|
||||||
File spawnDataEntries = new File(Grasscutter.getConfig().DATA_FOLDER + "Spawns.json");
|
File spawnDataEntries = new File(Grasscutter.getConfig().DATA_FOLDER + "Spawns.json");
|
||||||
|
@ -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<String, AbilityModifier> 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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<AbilityModifierAction> onModifierAdded;
|
||||||
|
public List<AbilityModifierAction> onThinkInterval;
|
||||||
|
public List<AbilityModifierAction> 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<AbilityModifierAction> getOnAdded() {
|
||||||
|
return onModifierAdded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<AbilityModifierAction> getOnThinkInterval() {
|
||||||
|
return onThinkInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<AbilityModifierAction> getOnRemoved() {
|
||||||
|
return onRemoved;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
185
src/main/java/emu/grasscutter/game/ability/AbilityManager.java
Normal file
185
src/main/java/emu/grasscutter/game/ability/AbilityManager.java
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,17 +17,21 @@ import emu.grasscutter.net.proto.AbilityControlBlockOuterClass.AbilityControlBlo
|
|||||||
import emu.grasscutter.net.proto.AbilityEmbryoOuterClass.AbilityEmbryo;
|
import emu.grasscutter.net.proto.AbilityEmbryoOuterClass.AbilityEmbryo;
|
||||||
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
|
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
|
||||||
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
|
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.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
|
||||||
import emu.grasscutter.net.proto.EntityClientDataOuterClass.EntityClientData;
|
import emu.grasscutter.net.proto.EntityClientDataOuterClass.EntityClientData;
|
||||||
import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo;
|
import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo;
|
||||||
import emu.grasscutter.net.proto.FightPropPairOuterClass.FightPropPair;
|
import emu.grasscutter.net.proto.FightPropPairOuterClass.FightPropPair;
|
||||||
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
|
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.PropPairOuterClass.PropPair;
|
||||||
import emu.grasscutter.net.proto.ProtEntityTypeOuterClass.ProtEntityType;
|
import emu.grasscutter.net.proto.ProtEntityTypeOuterClass.ProtEntityType;
|
||||||
import emu.grasscutter.net.proto.SceneAvatarInfoOuterClass.SceneAvatarInfo;
|
import emu.grasscutter.net.proto.SceneAvatarInfoOuterClass.SceneAvatarInfo;
|
||||||
import emu.grasscutter.net.proto.SceneEntityAiInfoOuterClass.SceneEntityAiInfo;
|
import emu.grasscutter.net.proto.SceneEntityAiInfoOuterClass.SceneEntityAiInfo;
|
||||||
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
|
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
|
||||||
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
|
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.Position;
|
||||||
import emu.grasscutter.utils.ProtoHelper;
|
import emu.grasscutter.utils.ProtoHelper;
|
||||||
import emu.grasscutter.utils.Utils;
|
import emu.grasscutter.utils.Utils;
|
||||||
@ -110,6 +114,19 @@ public class EntityAvatar extends GameEntity {
|
|||||||
this.killedBy = killerId;
|
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() {
|
public SceneAvatarInfo getSceneAvatarInfo() {
|
||||||
SceneAvatarInfo.Builder avatarInfo = SceneAvatarInfo.newBuilder()
|
SceneAvatarInfo.Builder avatarInfo = SceneAvatarInfo.newBuilder()
|
||||||
.setUid(this.getPlayer().getUid())
|
.setUid(this.getPlayer().getUid())
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
package emu.grasscutter.game.entity;
|
package emu.grasscutter.game.entity;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import emu.grasscutter.game.props.FightProperty;
|
import emu.grasscutter.game.props.FightProperty;
|
||||||
import emu.grasscutter.game.props.LifeState;
|
import emu.grasscutter.game.props.LifeState;
|
||||||
import emu.grasscutter.game.world.Scene;
|
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.MotionStateOuterClass.MotionState;
|
||||||
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
|
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
|
||||||
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
|
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
||||||
import emu.grasscutter.utils.Position;
|
import emu.grasscutter.utils.Position;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
|
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 {
|
public abstract class GameEntity {
|
||||||
protected int id;
|
protected int id;
|
||||||
@ -25,6 +31,10 @@ public abstract class GameEntity {
|
|||||||
private int lastMoveSceneTimeMs;
|
private int lastMoveSceneTimeMs;
|
||||||
private int lastMoveReliableSeq;
|
private int lastMoveReliableSeq;
|
||||||
|
|
||||||
|
// Abilities
|
||||||
|
private Map<String, Float> metaOverrideMap;
|
||||||
|
private Int2ObjectMap<String> metaModifiers;
|
||||||
|
|
||||||
public GameEntity(Scene scene) {
|
public GameEntity(Scene scene) {
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
this.moveState = MotionState.MOTION_NONE;
|
this.moveState = MotionState.MOTION_NONE;
|
||||||
@ -54,6 +64,20 @@ public abstract class GameEntity {
|
|||||||
return isAlive() ? LifeState.LIFE_ALIVE : LifeState.LIFE_DEAD;
|
return isAlive() ? LifeState.LIFE_ALIVE : LifeState.LIFE_DEAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<String, Float> getMetaOverrideMap() {
|
||||||
|
if (this.metaOverrideMap == null) {
|
||||||
|
this.metaOverrideMap = new HashMap<>();
|
||||||
|
}
|
||||||
|
return this.metaOverrideMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Int2ObjectMap<String> getMetaModifiers() {
|
||||||
|
if (this.metaModifiers == null) {
|
||||||
|
this.metaModifiers = new Int2ObjectOpenHashMap<>();
|
||||||
|
}
|
||||||
|
return this.metaModifiers;
|
||||||
|
}
|
||||||
|
|
||||||
public abstract Int2FloatOpenHashMap getFightProperties();
|
public abstract Int2FloatOpenHashMap getFightProperties();
|
||||||
|
|
||||||
public abstract Position getPosition();
|
public abstract Position getPosition();
|
||||||
@ -146,4 +170,53 @@ public abstract class GameEntity {
|
|||||||
public void setSpawnEntry(SpawnDataEntry spawnEntry) {
|
public void setSpawnEntry(SpawnDataEntry spawnEntry) {
|
||||||
this.spawnEntry = 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import emu.grasscutter.data.def.PlayerLevelData;
|
|||||||
import emu.grasscutter.database.DatabaseHelper;
|
import emu.grasscutter.database.DatabaseHelper;
|
||||||
import emu.grasscutter.game.Account;
|
import emu.grasscutter.game.Account;
|
||||||
import emu.grasscutter.game.CoopRequest;
|
import emu.grasscutter.game.CoopRequest;
|
||||||
|
import emu.grasscutter.game.ability.AbilityManager;
|
||||||
import emu.grasscutter.game.avatar.Avatar;
|
import emu.grasscutter.game.avatar.Avatar;
|
||||||
import emu.grasscutter.game.avatar.AvatarProfileData;
|
import emu.grasscutter.game.avatar.AvatarProfileData;
|
||||||
import emu.grasscutter.game.avatar.AvatarStorage;
|
import emu.grasscutter.game.avatar.AvatarStorage;
|
||||||
@ -89,7 +90,8 @@ public class Player {
|
|||||||
@Transient private FriendsList friendsList;
|
@Transient private FriendsList friendsList;
|
||||||
@Transient private MailHandler mailHandler;
|
@Transient private MailHandler mailHandler;
|
||||||
@Transient private MessageHandler messageHandler;
|
@Transient private MessageHandler messageHandler;
|
||||||
|
@Transient private AbilityManager abilityManager;
|
||||||
|
|
||||||
@Transient private SotSManager sotsManager;
|
@Transient private SotSManager sotsManager;
|
||||||
|
|
||||||
private TeamManager teamManager;
|
private TeamManager teamManager;
|
||||||
@ -142,6 +144,7 @@ public class Player {
|
|||||||
this.friendsList = new FriendsList(this);
|
this.friendsList = new FriendsList(this);
|
||||||
this.mailHandler = new MailHandler(this);
|
this.mailHandler = new MailHandler(this);
|
||||||
this.towerManager = new TowerManager(this);
|
this.towerManager = new TowerManager(this);
|
||||||
|
this.abilityManager = new AbilityManager(this);
|
||||||
this.pos = new Position();
|
this.pos = new Position();
|
||||||
this.rotation = new Position();
|
this.rotation = new Position();
|
||||||
this.properties = new HashMap<>();
|
this.properties = new HashMap<>();
|
||||||
@ -1025,6 +1028,10 @@ public class Player {
|
|||||||
|
|
||||||
public SotSManager getSotSManager() { return sotsManager; }
|
public SotSManager getSotSManager() { return sotsManager; }
|
||||||
|
|
||||||
|
public AbilityManager getAbilityManager() {
|
||||||
|
return abilityManager;
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized void onTick() {
|
public synchronized void onTick() {
|
||||||
// Check ping
|
// Check ping
|
||||||
if (this.getLastPingTime() > System.currentTimeMillis() + 60000) {
|
if (this.getLastPingTime() > System.currentTimeMillis() + 60000) {
|
||||||
|
@ -385,27 +385,7 @@ public class Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sanity check
|
// Sanity check
|
||||||
if (target.getFightProperties() == null) {
|
target.damage(result.getDamage(), result.getAttackerId());
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void killEntity(GameEntity target, int attackerId) {
|
public void killEntity(GameEntity target, int attackerId) {
|
||||||
|
@ -6,6 +6,7 @@ import emu.grasscutter.net.proto.AbilityInvocationsNotifyOuterClass.AbilityInvoc
|
|||||||
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
|
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
|
||||||
import emu.grasscutter.net.packet.PacketHandler;
|
import emu.grasscutter.net.packet.PacketHandler;
|
||||||
import emu.grasscutter.server.game.GameSession;
|
import emu.grasscutter.server.game.GameSession;
|
||||||
|
import emu.grasscutter.utils.Utils;
|
||||||
|
|
||||||
@Opcodes(PacketOpcodes.AbilityInvocationsNotify)
|
@Opcodes(PacketOpcodes.AbilityInvocationsNotify)
|
||||||
public class HandlerAbilityInvocationsNotify extends PacketHandler {
|
public class HandlerAbilityInvocationsNotify extends PacketHandler {
|
||||||
@ -15,7 +16,7 @@ public class HandlerAbilityInvocationsNotify extends PacketHandler {
|
|||||||
AbilityInvocationsNotify notif = AbilityInvocationsNotify.parseFrom(payload);
|
AbilityInvocationsNotify notif = AbilityInvocationsNotify.parseFrom(payload);
|
||||||
|
|
||||||
for (AbilityInvokeEntry entry : notif.getInvokesList()) {
|
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);
|
session.getPlayer().getAbilityInvokeHandler().addEntry(entry.getForwardType(), entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry
|
|||||||
import emu.grasscutter.net.proto.ClientAbilityInitFinishNotifyOuterClass.ClientAbilityInitFinishNotify;
|
import emu.grasscutter.net.proto.ClientAbilityInitFinishNotifyOuterClass.ClientAbilityInitFinishNotify;
|
||||||
import emu.grasscutter.net.packet.PacketHandler;
|
import emu.grasscutter.net.packet.PacketHandler;
|
||||||
import emu.grasscutter.server.game.GameSession;
|
import emu.grasscutter.server.game.GameSession;
|
||||||
|
import emu.grasscutter.utils.Utils;
|
||||||
|
|
||||||
@Opcodes(PacketOpcodes.ClientAbilityInitFinishNotify)
|
@Opcodes(PacketOpcodes.ClientAbilityInitFinishNotify)
|
||||||
public class HandlerClientAbilityInitFinishNotify extends PacketHandler {
|
public class HandlerClientAbilityInitFinishNotify extends PacketHandler {
|
||||||
@ -15,6 +16,7 @@ public class HandlerClientAbilityInitFinishNotify extends PacketHandler {
|
|||||||
ClientAbilityInitFinishNotify notif = ClientAbilityInitFinishNotify.parseFrom(payload);
|
ClientAbilityInitFinishNotify notif = ClientAbilityInitFinishNotify.parseFrom(payload);
|
||||||
|
|
||||||
for (AbilityInvokeEntry entry : notif.getInvokesList()) {
|
for (AbilityInvokeEntry entry : notif.getInvokesList()) {
|
||||||
|
session.getPlayer().getAbilityManager().onAbilityInvoke(entry);
|
||||||
session.getPlayer().getClientAbilityInitFinishHandler().addEntry(entry.getForwardType(), entry);
|
session.getPlayer().getClientAbilityInitFinishHandler().addEntry(entry.getForwardType(), entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,11 +14,6 @@ public class HandlerEvtCreateGadgetNotify extends PacketHandler {
|
|||||||
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||||
EvtCreateGadgetNotify notify = EvtCreateGadgetNotify.parseFrom(payload);
|
EvtCreateGadgetNotify notify = EvtCreateGadgetNotify.parseFrom(payload);
|
||||||
|
|
||||||
// Dont handle in singleplayer
|
|
||||||
if (!session.getPlayer().getWorld().isMultiplayer()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanity check - dont add duplicate entities
|
// Sanity check - dont add duplicate entities
|
||||||
if (session.getPlayer().getScene().getEntityById(notify.getEntityId()) != null) {
|
if (session.getPlayer().getScene().getEntityById(notify.getEntityId()) != null) {
|
||||||
return;
|
return;
|
||||||
|
@ -12,11 +12,6 @@ public class HandlerEvtDestroyGadgetNotify extends PacketHandler {
|
|||||||
@Override
|
@Override
|
||||||
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||||
EvtDestroyGadgetNotify notify = EvtDestroyGadgetNotify.parseFrom(payload);
|
EvtDestroyGadgetNotify notify = EvtDestroyGadgetNotify.parseFrom(payload);
|
||||||
|
|
||||||
// Dont handle in singleplayer
|
|
||||||
if (!session.getPlayer().getWorld().isMultiplayer()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
session.getPlayer().getScene().onPlayerDestroyGadget(notify.getEntityId());
|
session.getPlayer().getScene().onPlayerDestroyGadget(notify.getEntityId());
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ public class HandlerSetEntityClientDataNotify extends PacketHandler {
|
|||||||
BasePacket packet = new BasePacket(PacketOpcodes.SetEntityClientDataNotify, true);
|
BasePacket packet = new BasePacket(PacketOpcodes.SetEntityClientDataNotify, true);
|
||||||
packet.setData(notif);
|
packet.setData(notif);
|
||||||
|
|
||||||
session.getPlayer().getScene().broadcastPacketToOthers(session.getPlayer(), packet);
|
session.getPlayer().getScene().broadcastPacket(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user