Implement energy balls (orbs)

This commit is contained in:
Melledy 2022-05-08 05:31:53 -07:00
parent 897f082b12
commit 2dfdc62743
9 changed files with 128 additions and 31 deletions

View File

@ -7,9 +7,12 @@ import emu.grasscutter.data.GameData;
import emu.grasscutter.data.custom.AbilityModifier; import emu.grasscutter.data.custom.AbilityModifier;
import emu.grasscutter.data.custom.AbilityModifier.AbilityModifierAction; import emu.grasscutter.data.custom.AbilityModifier.AbilityModifierAction;
import emu.grasscutter.data.custom.AbilityModifier.AbilityModifierActionType; import emu.grasscutter.data.custom.AbilityModifier.AbilityModifierActionType;
import emu.grasscutter.data.def.ItemData;
import emu.grasscutter.data.custom.AbilityModifierEntry; import emu.grasscutter.data.custom.AbilityModifierEntry;
import emu.grasscutter.game.entity.EntityItem;
import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.player.Player; 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.AbilityInvokeArgumentOuterClass.AbilityInvokeArgument;
import emu.grasscutter.net.proto.AbilityInvokeEntryHeadOuterClass.AbilityInvokeEntryHead; import emu.grasscutter.net.proto.AbilityInvokeEntryHeadOuterClass.AbilityInvokeEntryHead;
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry; 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.AbilityScalarTypeOuterClass.AbilityScalarType;
import emu.grasscutter.net.proto.AbilityScalarValueEntryOuterClass.AbilityScalarValueEntry; import emu.grasscutter.net.proto.AbilityScalarValueEntryOuterClass.AbilityScalarValueEntry;
import emu.grasscutter.net.proto.ModifierActionOuterClass.ModifierAction; import emu.grasscutter.net.proto.ModifierActionOuterClass.ModifierAction;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.Utils; import emu.grasscutter.utils.Utils;
public class AbilityManager { public class AbilityManager {
@ -143,8 +147,21 @@ public class AbilityManager {
} }
} }
private void handleGenerateElemBall(AbilityInvokeEntry invoke) { private void handleGenerateElemBall(AbilityInvokeEntry invoke) throws InvalidProtocolBufferException {
// TODO create elemental energy orbs 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) { private void invokeAction(AbilityModifierAction action, GameEntity target, GameEntity sourceEntity) {

View File

@ -69,6 +69,7 @@ public class Avatar {
@Transient private Player owner; @Transient private Player owner;
@Transient private AvatarData data; @Transient private AvatarData data;
@Transient private AvatarSkillDepotData skillDepot;
@Transient private long guid; // Player unique id @Transient private long guid; // Player unique id
private int avatarId; // Id of avatar private int avatarId; // Id of avatar
@ -103,8 +104,8 @@ public class Avatar {
private int nameCardRewardId; private int nameCardRewardId;
private int nameCardId; private int nameCardId;
@Deprecated // Do not use. Morhpia only!
public Avatar() { public Avatar() {
// Morhpia only!
this.equips = new Int2ObjectOpenHashMap<>(); this.equips = new Int2ObjectOpenHashMap<>();
this.fightProp = new Int2FloatOpenHashMap(); this.fightProp = new Int2FloatOpenHashMap();
this.extraAbilityEmbryos = new HashSet<>(); this.extraAbilityEmbryos = new HashSet<>();
@ -140,7 +141,7 @@ public class Avatar {
} }
// Skill depot // Skill depot
this.setSkillDepot(getAvatarData().getSkillDepot()); this.setSkillDepotData(getAvatarData().getSkillDepot());
// Set stats // Set stats
this.recalcStats(); this.recalcStats();
@ -164,7 +165,8 @@ public class Avatar {
} }
protected void setAvatarData(AvatarData data) { 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() { public int getOwnerId() {
@ -257,9 +259,19 @@ public class Avatar {
return skillDepotId; return skillDepotId;
} }
public void setSkillDepot(AvatarSkillDepotData skillDepot) { public AvatarSkillDepotData getSkillDepot() {
// Set id 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.skillDepotId = skillDepot.getId();
this.skillDepot = skillDepot;
// Clear, then add skills // Clear, then add skills
getSkillLevelMap().clear(); getSkillLevelMap().clear();
if (skillDepot.getEnergySkill() > 0) { if (skillDepot.getEnergySkill() > 0) {
@ -501,8 +513,8 @@ public class Avatar {
// Set energy usage // Set energy usage
if (data.getSkillDepot() != null && data.getSkillDepot().getEnergySkillData() != null) { if (data.getSkillDepot() != null && data.getSkillDepot().getEnergySkillData() != null) {
ElementType element = data.getSkillDepot().getElementType(); ElementType element = data.getSkillDepot().getElementType();
this.setFightProperty(element.getEnergyProperty(), data.getSkillDepot().getEnergySkillData().getCostElemVal()); this.setFightProperty(element.getMaxEnergyProp(), data.getSkillDepot().getEnergySkillData().getCostElemVal());
this.setFightProperty((element.getEnergyProperty().getId() % 70) + 1000, data.getSkillDepot().getEnergySkillData().getCostElemVal()); this.setFightProperty((element.getMaxEnergyProp().getId() % 70) + 1000, data.getSkillDepot().getEnergySkillData().getCostElemVal());
} }
// Artifacts // Artifacts

View File

@ -5,6 +5,7 @@ import java.util.List;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.def.AvatarData; import emu.grasscutter.data.def.AvatarData;
import emu.grasscutter.data.def.AvatarSkillDepotData;
import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.GameItem;
@ -139,12 +140,14 @@ public class AvatarStorage implements Iterable<Avatar> {
} }
AvatarData avatarData = GameData.getAvatarDataMap().get(avatar.getAvatarId()); AvatarData avatarData = GameData.getAvatarDataMap().get(avatar.getAvatarId());
if (avatarData == null) { AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(avatar.getSkillDepotId());
if (avatarData == null || skillDepot == null) {
continue; continue;
} }
// Set ownerships // Set ownerships
avatar.setAvatarData(avatarData); avatar.setAvatarData(avatarData);
avatar.setSkillDepot(skillDepot);
avatar.setOwner(getPlayer()); avatar.setOwner(getPlayer());
// Force recalc of const boosted skills // Force recalc of const boosted skills

View File

@ -30,6 +30,7 @@ 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.PacketAvatarFightPropUpdateNotify;
import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify; import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
@ -127,6 +128,22 @@ public class EntityAvatar extends GameEntity {
return healed; 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() { public SceneAvatarInfo getSceneAvatarInfo() {
SceneAvatarInfo.Builder avatarInfo = SceneAvatarInfo.newBuilder() SceneAvatarInfo.Builder avatarInfo = SceneAvatarInfo.newBuilder()
.setUid(this.getPlayer().getUid()) .setUid(this.getPlayer().getUid())

View File

@ -172,6 +172,9 @@ public class Inventory implements Iterable<GameItem> {
// Handle // Handle
this.addVirtualItem(item.getItemId(), item.getCount()); this.addVirtualItem(item.getItemId(), item.getCount());
return item; return item;
} else if (item.getItemData().getMaterialType() == MaterialType.MATERIAL_ADSORBATE) {
player.getTeamManager().addEnergyToTeam(item);
return null;
} else if (item.getItemData().getMaterialType() == MaterialType.MATERIAL_AVATAR) { } else if (item.getItemData().getMaterialType() == MaterialType.MATERIAL_AVATAR) {
// Get avatar id // Get avatar id
int avatarId = (item.getItemId() % 1000) + 10000000; int avatarId = (item.getItemId() % 1000) + 10000000;

View File

@ -10,6 +10,7 @@ import emu.grasscutter.data.def.AvatarSkillDepotData;
import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.entity.EntityBaseGadget; import emu.grasscutter.game.entity.EntityBaseGadget;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.props.ElementType; import emu.grasscutter.game.props.ElementType;
import emu.grasscutter.game.props.EnterReason; import emu.grasscutter.game.props.EnterReason;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
@ -580,6 +581,24 @@ public class TeamManager {
getPlayer().sendPacket(new BasePacket(PacketOpcodes.WorldPlayerReviveRsp)); 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() { public void saveAvatars() {
// Save all avatars from active team // Save all avatars from active team
for (EntityAvatar entity : getActiveTeam()) { for (EntityAvatar entity : getActiveTeam()) {

View File

@ -9,21 +9,22 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
public enum ElementType { public enum ElementType {
None (0, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY), None (0, FightProperty.FIGHT_PROP_CUR_FIRE_ENERGY, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY),
Fire (1, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY, 10101, "TeamResonance_Fire_Lv2"), Fire (1, FightProperty.FIGHT_PROP_CUR_FIRE_ENERGY, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY, 10101, "TeamResonance_Fire_Lv2"),
Water (2, FightProperty.FIGHT_PROP_MAX_WATER_ENERGY, 10201, "TeamResonance_Water_Lv2"), Water (2, FightProperty.FIGHT_PROP_CUR_WATER_ENERGY, FightProperty.FIGHT_PROP_MAX_WATER_ENERGY, 10201, "TeamResonance_Water_Lv2"),
Grass (3, FightProperty.FIGHT_PROP_MAX_GRASS_ENERGY), Grass (3, FightProperty.FIGHT_PROP_CUR_GRASS_ENERGY, FightProperty.FIGHT_PROP_MAX_GRASS_ENERGY),
Electric (4, FightProperty.FIGHT_PROP_MAX_ELEC_ENERGY, 10401, "TeamResonance_Electric_Lv2"), Electric (4, FightProperty.FIGHT_PROP_CUR_ELEC_ENERGY, FightProperty.FIGHT_PROP_MAX_ELEC_ENERGY, 10401, "TeamResonance_Electric_Lv2"),
Ice (5, FightProperty.FIGHT_PROP_MAX_ICE_ENERGY, 10601, "TeamResonance_Ice_Lv2"), Ice (5, FightProperty.FIGHT_PROP_CUR_ICE_ENERGY, FightProperty.FIGHT_PROP_MAX_ICE_ENERGY, 10601, "TeamResonance_Ice_Lv2"),
Frozen (6, FightProperty.FIGHT_PROP_MAX_ICE_ENERGY), Frozen (6, FightProperty.FIGHT_PROP_CUR_ICE_ENERGY, FightProperty.FIGHT_PROP_MAX_ICE_ENERGY),
Wind (7, FightProperty.FIGHT_PROP_MAX_WIND_ENERGY, 10301, "TeamResonance_Wind_Lv2"), Wind (7, FightProperty.FIGHT_PROP_CUR_WIND_ENERGY, FightProperty.FIGHT_PROP_MAX_WIND_ENERGY, 10301, "TeamResonance_Wind_Lv2"),
Rock (8, FightProperty.FIGHT_PROP_MAX_ROCK_ENERGY, 10701, "TeamResonance_Rock_Lv2"), Rock (8, FightProperty.FIGHT_PROP_CUR_ROCK_ENERGY, FightProperty.FIGHT_PROP_MAX_ROCK_ENERGY, 10701, "TeamResonance_Rock_Lv2"),
AntiFire (9, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY), AntiFire (9, FightProperty.FIGHT_PROP_CUR_FIRE_ENERGY, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY),
Default (255, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY, 10801, "TeamResonance_AllDifferent"); Default (255, FightProperty.FIGHT_PROP_CUR_FIRE_ENERGY, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY, 10801, "TeamResonance_AllDifferent");
private final int value; private final int value;
private final int teamResonanceId; private final int teamResonanceId;
private final FightProperty energyProperty; private final FightProperty curEnergyProp;
private final FightProperty maxEnergyProp;
private final int configHash; private final int configHash;
private static final Int2ObjectMap<ElementType> map = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<ElementType> map = new Int2ObjectOpenHashMap<>();
private static final Map<String, ElementType> stringMap = new HashMap<>(); private static final Map<String, ElementType> stringMap = new HashMap<>();
@ -35,13 +36,14 @@ public enum ElementType {
}); });
} }
private ElementType(int value, FightProperty energyProperty) { private ElementType(int value, FightProperty curEnergyProp, FightProperty maxEnergyProp) {
this(value, energyProperty, 0, null); 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.value = value;
this.energyProperty = energyProperty; this.curEnergyProp = curEnergyProp;
this.maxEnergyProp = maxEnergyProp;
this.teamResonanceId = teamResonanceId; this.teamResonanceId = teamResonanceId;
if (configName != null) { if (configName != null) {
this.configHash = Utils.abilityHash(configName); this.configHash = Utils.abilityHash(configName);
@ -54,8 +56,12 @@ public enum ElementType {
return value; return value;
} }
public FightProperty getEnergyProperty() { public FightProperty getCurEnergyProp() {
return energyProperty; return curEnergyProp;
}
public FightProperty getMaxEnergyProp() {
return maxEnergyProp;
} }
public int getTeamResonanceId() { public int getTeamResonanceId() {

View File

@ -62,7 +62,7 @@ public class HandlerSetPlayerBornDataReq extends PacketHandler {
// Create avatar // Create avatar
if (player.getAvatars().getAvatarCount() == 0) { if (player.getAvatars().getAvatarCount() == 0) {
Avatar mainCharacter = new Avatar(avatarId); Avatar mainCharacter = new Avatar(avatarId);
mainCharacter.setSkillDepot(GameData.getAvatarSkillDepotDataMap().get(startingSkillDepot)); mainCharacter.setSkillDepotData(GameData.getAvatarSkillDepotDataMap().get(startingSkillDepot));
player.addAvatar(mainCharacter); player.addAvatar(mainCharacter);
player.setMainCharacterId(avatarId); player.setMainCharacterId(avatarId);
player.setHeadImage(avatarId); player.setHeadImage(avatarId);

View File

@ -11,21 +11,27 @@ import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
import java.util.List; import java.util.List;
public class PacketEntityFightPropChangeReasonNotify extends BasePacket { public class PacketEntityFightPropChangeReasonNotify extends BasePacket {
public PacketEntityFightPropChangeReasonNotify(GameEntity entity, FightProperty prop, Float value, List<Integer> param, PropChangeReason reason, ChangeHpReason changeHpReason) { public PacketEntityFightPropChangeReasonNotify(GameEntity entity, FightProperty prop, Float value, List<Integer> param, PropChangeReason reason, ChangeHpReason changeHpReason) {
super(PacketOpcodes.EntityFightPropChangeReasonNotify); super(PacketOpcodes.EntityFightPropChangeReasonNotify);
EntityFightPropChangeReasonNotify.Builder proto = EntityFightPropChangeReasonNotify.newBuilder() EntityFightPropChangeReasonNotify.Builder proto = EntityFightPropChangeReasonNotify.newBuilder()
.setEntityId(entity.getId()) .setEntityId(entity.getId())
.setPropType(prop.getId()) .setPropType(prop.getId())
.setPropDelta(value) .setPropDelta(value)
.setReason(reason) .setReason(reason)
.setChangeHpReason(changeHpReason); .setChangeHpReason(changeHpReason);
for(int p: param){
for(int p : param){
proto.addParamList(p); proto.addParamList(p);
} }
this.setData(proto); this.setData(proto);
} }
public PacketEntityFightPropChangeReasonNotify(GameEntity entity, FightProperty prop, Float value, PropChangeReason reason, ChangeHpReason changeHpReason) { public PacketEntityFightPropChangeReasonNotify(GameEntity entity, FightProperty prop, Float value, PropChangeReason reason, ChangeHpReason changeHpReason) {
super(PacketOpcodes.EntityFightPropChangeReasonNotify); super(PacketOpcodes.EntityFightPropChangeReasonNotify);
EntityFightPropChangeReasonNotify proto = EntityFightPropChangeReasonNotify.newBuilder() EntityFightPropChangeReasonNotify proto = EntityFightPropChangeReasonNotify.newBuilder()
.setEntityId(entity.getId()) .setEntityId(entity.getId())
.setPropType(prop.getId()) .setPropType(prop.getId())
@ -33,6 +39,20 @@ public class PacketEntityFightPropChangeReasonNotify extends BasePacket {
.setReason(reason) .setReason(reason)
.setChangeHpReason(changeHpReason) .setChangeHpReason(changeHpReason)
.build(); .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); this.setData(proto);
} }
} }