Commit 2a76e904 authored by ImmuState's avatar ImmuState Committed by GitHub
Browse files

Energy: Particle/Orb Drops from Monsters (#1054)

* Generate energy drops for monsters + make sure picking up particles honors their count.

* Use drop info from excels instead.

* Remove double newline.

* Remove commented code.
parent 58115566
...@@ -6,6 +6,7 @@ import java.util.Calendar; ...@@ -6,6 +6,7 @@ import java.util.Calendar;
import emu.grasscutter.auth.AuthenticationSystem; import emu.grasscutter.auth.AuthenticationSystem;
import emu.grasscutter.auth.DefaultAuthentication; import emu.grasscutter.auth.DefaultAuthentication;
import emu.grasscutter.command.CommandMap; import emu.grasscutter.command.CommandMap;
import emu.grasscutter.game.managers.EnergyManager.EnergyManager;
import emu.grasscutter.game.managers.StaminaManager.StaminaManager; import emu.grasscutter.game.managers.StaminaManager.StaminaManager;
import emu.grasscutter.plugin.PluginManager; import emu.grasscutter.plugin.PluginManager;
import emu.grasscutter.plugin.api.ServerHook; import emu.grasscutter.plugin.api.ServerHook;
...@@ -106,6 +107,7 @@ public final class Grasscutter { ...@@ -106,6 +107,7 @@ public final class Grasscutter {
Grasscutter.updateDayOfWeek(); Grasscutter.updateDayOfWeek();
ResourceLoader.loadAll(); ResourceLoader.loadAll();
ScriptLoader.init(); ScriptLoader.init();
EnergyManager.initialize();
// Initialize database. // Initialize database.
DatabaseManager.initialize(); DatabaseManager.initialize();
......
...@@ -5,6 +5,7 @@ import emu.grasscutter.data.common.PropGrowCurve; ...@@ -5,6 +5,7 @@ import emu.grasscutter.data.common.PropGrowCurve;
import emu.grasscutter.data.def.MonsterCurveData; import emu.grasscutter.data.def.MonsterCurveData;
import emu.grasscutter.data.def.MonsterData; import emu.grasscutter.data.def.MonsterData;
import emu.grasscutter.game.dungeons.DungeonChallenge; import emu.grasscutter.game.dungeons.DungeonChallenge;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.EntityIdType; import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.props.PlayerProperty;
...@@ -111,6 +112,23 @@ public class EntityMonster extends GameEntity { ...@@ -111,6 +112,23 @@ public class EntityMonster extends GameEntity {
this.poseId = poseId; this.poseId = poseId;
} }
@Override
public void damage(float amount, int killerId) {
// Get HP before damage.
float hpBeforeDamage = this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
// Apply damage.
super.damage(amount, killerId);
// Get HP after damage.
float hpAfterDamage = this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
// Invoke energy drop logic.
for (Player player : this.getScene().getPlayers()) {
player.getEnergyManager().handleMonsterEnergyDrop(this, hpBeforeDamage, hpAfterDamage);
}
}
@Override @Override
public void onDeath(int killerId) { public void onDeath(int killerId) {
if (this.getSpawnEntry() != null) { if (this.getSpawnEntry() != null) {
......
package emu.grasscutter.game.managers.EnergyManager;
import java.util.List;
public class EnergyDropEntry {
private int dropId;
private List<EnergyDropInfo> dropList;
public int getDropId() {
return this.dropId;
}
public List<EnergyDropInfo> getDropList() {
return this.dropList;
}
}
package emu.grasscutter.game.managers.EnergyManager;
public class EnergyDropInfo {
private int ballId;
private int count;
public int getBallId() {
return this.ballId;
}
public int getCount() {
return this.count;
}
}
package emu.grasscutter.game.managers; package emu.grasscutter.game.managers.EnergyManager;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.DataLoader;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.def.AvatarSkillDepotData; import emu.grasscutter.data.def.AvatarSkillDepotData;
import emu.grasscutter.data.def.ItemData; import emu.grasscutter.data.def.ItemData;
import emu.grasscutter.data.def.MonsterData.HpDrops;
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.EntityClientGadget; import emu.grasscutter.game.entity.EntityClientGadget;
import emu.grasscutter.game.entity.EntityItem; import emu.grasscutter.game.entity.EntityItem;
import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ElementType; import emu.grasscutter.game.props.ElementType;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.net.proto.AbilityActionGenerateElemBallOuterClass.AbilityActionGenerateElemBall; import emu.grasscutter.net.proto.AbilityActionGenerateElemBallOuterClass.AbilityActionGenerateElemBall;
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry; import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason; import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import static emu.grasscutter.Configuration.GAME_OPTIONS; import static emu.grasscutter.Configuration.GAME_OPTIONS;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Collection;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import com.google.gson.reflect.TypeToken;
import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.InvalidProtocolBufferException;
public class EnergyManager { public class EnergyManager {
private final Player player; private final Player player;
private final static Int2ObjectMap<List<EnergyDropInfo>> energyDropData = new Int2ObjectOpenHashMap<>();
public EnergyManager(Player player) { public EnergyManager(Player player) {
this.player = player; this.player = player;
...@@ -34,6 +47,22 @@ public class EnergyManager { ...@@ -34,6 +47,22 @@ public class EnergyManager {
return this.player; return this.player;
} }
public static void initialize() {
// Read the data we need for monster energy drops.
try (Reader fileReader = new InputStreamReader(DataLoader.load("EnergyDrop.json"))) {
List<EnergyDropEntry> energyDropList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, EnergyDropEntry.class).getType());
for (EnergyDropEntry entry : energyDropList) {
energyDropData.put(entry.getDropId(), entry.getDropList());
}
Grasscutter.getLogger().info("Energy drop data successfully loaded.");
}
catch (Exception ex) {
Grasscutter.getLogger().error("Unable to load energy drop data.", ex);
}
}
/********** /**********
Particle creation for elemental skills. Particle creation for elemental skills.
**********/ **********/
...@@ -117,17 +146,8 @@ public class EnergyManager { ...@@ -117,17 +146,8 @@ public class EnergyManager {
} }
} }
// Get the item data for an energy particle of the correct element. // Generate the particle/orb.
ItemData itemData = GameData.getItemDataMap().get(itemId); generateElemBall(itemId, new Position(action.getPos()), 1);
if (itemData == null) {
return; // Should never happen
}
// Generate entity.
EntityItem energyBall = new EntityItem(getPlayer().getScene(), getPlayer(), itemData, new Position(action.getPos()), 1);
energyBall.getRotation().set(action.getRot());
this.getPlayer().getScene().addEntity(energyBall);
} }
/********** /**********
...@@ -183,11 +203,10 @@ public class EnergyManager { ...@@ -183,11 +203,10 @@ public class EnergyManager {
float elementBonus = (ballElement == null) ? 2.0f : (avatarElement == ballElement) ? 3.0f : 1.0f; float elementBonus = (ballElement == null) ? 2.0f : (avatarElement == ballElement) ? 3.0f : 1.0f;
// Add the energy. // Add the energy.
entity.addEnergy(baseEnergy * elementBonus * offFieldPenalty, PropChangeReason.PROP_CHANGE_ENERGY_BALL); entity.addEnergy(baseEnergy * elementBonus * offFieldPenalty * elemBall.getCount(), PropChangeReason.PROP_CHANGE_ENERGY_BALL);
} }
} }
/********** /**********
Energy logic related to using skills. Energy logic related to using skills.
**********/ **********/
...@@ -218,4 +237,56 @@ public class EnergyManager { ...@@ -218,4 +237,56 @@ public class EnergyManager {
// Handle elemental burst. // Handle elemental burst.
this.handleBurstCast(avatar, skillId); this.handleBurstCast(avatar, skillId);
} }
/**********
Monster energy drops.
**********/
private void generateElemBallDrops(EntityMonster monster, int dropId) {
// Generate all drops specified for the given drop id.
if (!energyDropData.containsKey(dropId)) {
Grasscutter.getLogger().warn("No drop data for dropId {} found.", dropId);
return;
}
for (EnergyDropInfo info : energyDropData.get(dropId)) {
this.generateElemBall(info.getBallId(), monster.getPosition(), info.getCount());
}
}
public void handleMonsterEnergyDrop(EntityMonster monster, float hpBeforeDamage, float hpAfterDamage) {
// Calculate the HP tresholds for before and after the damage was taken.
float maxHp = monster.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
float thresholdBefore = hpBeforeDamage / maxHp;
float thresholdAfter = hpAfterDamage / maxHp;
// Determine the thresholds the monster has passed, and generate drops based on that.
for (HpDrops drop : monster.getMonsterData().getHpDrops()) {
if (drop.getDropId() == 0) {
continue;
}
float threshold = drop.getHpPercent() / 100.0f;
if (threshold < thresholdBefore && threshold >= thresholdAfter) {
generateElemBallDrops(monster, drop.getDropId());
}
}
// Handle kill drops.
if (hpAfterDamage <= 0 && monster.getMonsterData().getKillDropId() != 0) {
generateElemBallDrops(monster, monster.getMonsterData().getKillDropId());
}
}
/**********
Utility.
**********/
private void generateElemBall(int ballId, Position position, int count) {
// Generate a particle/orb with the specified parameters.
ItemData itemData = GameData.getItemDataMap().get(ballId);
if (itemData == null) {
return;
}
EntityItem energyBall = new EntityItem(this.getPlayer().getScene(), this.getPlayer(), itemData, position, count);
this.getPlayer().getScene().addEntity(energyBall);
}
} }
...@@ -23,8 +23,8 @@ import emu.grasscutter.game.inventory.Inventory; ...@@ -23,8 +23,8 @@ import emu.grasscutter.game.inventory.Inventory;
import emu.grasscutter.game.mail.Mail; import emu.grasscutter.game.mail.Mail;
import emu.grasscutter.game.mail.MailHandler; import emu.grasscutter.game.mail.MailHandler;
import emu.grasscutter.game.managers.StaminaManager.StaminaManager; import emu.grasscutter.game.managers.StaminaManager.StaminaManager;
import emu.grasscutter.game.managers.EnergyManager;
import emu.grasscutter.game.managers.SotSManager; import emu.grasscutter.game.managers.SotSManager;
import emu.grasscutter.game.managers.EnergyManager.EnergyManager;
import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.EntityType; import emu.grasscutter.game.props.EntityType;
import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.props.PlayerProperty;
......
[
{
"dropId": 22010010,
"dropList": [
{ "ballId": 2024, "count": 1 }
]
},
{
"dropId": 22010030,
"dropList": [
{ "ballId": 2008, "count": 1 }
]
},
{
"dropId": 22010050,
"dropList": [
{ "ballId": 2024, "count": 3 },
{ "ballId": 2008, "count": 1 }
]
},
{
"dropId": 22010013,
"dropList": [
{ "ballId": 2019, "count": 1 }
]
},
{
"dropId": 22010033,
"dropList": [
{ "ballId": 2003, "count": 1 }
]
},
{
"dropId": 22010015,
"dropList": [
{ "ballId": 2021, "count": 1 }
]
},
{
"dropId": 22010035,
"dropList": [
{ "ballId": 2005, "count": 1 }
]
},
{
"dropId": 22010034,
"dropList": [
{ "ballId": 2004, "count": 1 }
]
},
{
"dropId": 22010037,
"dropList": [
{ "ballId": 2007, "count": 1 }
]
},
{
"dropId": 22010032,
"dropList": [
{ "ballId": 2002, "count": 1 }
]
},
{
"dropId": 22010022,
"dropList": [
{ "ballId": 2018, "count": 1 }
]
},
{
"dropId": 22010036,
"dropList": [
{ "ballId": 2006, "count": 1 }
]
},
{
"dropId": 22010026,
"dropList": [
{ "ballId": 2022, "count": 1 }
]
},
{
"dropId": 22010031,
"dropList": [
{ "ballId": 2001, "count": 1 }
]
},
{
"dropId": 22010014,
"dropList": [
{ "ballId": 2020, "count": 1 }
]
},
{
"dropId": 22010016,
"dropList": [
{ "ballId": 2022, "count": 1 }
]
},
{
"dropId": 22010012,
"dropList": [
{ "ballId": 2018, "count": 1 }
]
},
{
"dropId": 22010024,
"dropList": [
{ "ballId": 2004, "count": 1 }
]
},
{
"dropId": 22010011,
"dropList": [
{ "ballId": 2017, "count": 1 }
]
},
{
"dropId": 22010017,
"dropList": [
{ "ballId": 2023, "count": 1 }
]
},
{
"dropId": 22010021,
"dropList": [
{ "ballId": 2017, "count": 1 }
]
},
{
"dropId": 22010027,
"dropList": [
{ "ballId": 2007, "count": 1 }
]
},
{
"dropId": 22010040,
"dropList": [
{ "ballId": 2024, "count": 1 },
{ "ballId": 2008, "count": 1 }
]
},
{
"dropId": 22010025,
"dropList": [
{ "ballId": 2021, "count": 2 }
]
},
{
"dropId": 22010020,
"dropList": [
{ "ballId": 2024, "count": 1 }
]
},
{
"dropId": 22003100,
"dropList": [
]
}
]
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment