Skip to content
Snippets Groups Projects
Commit 95bc6558 authored by logictc's avatar logictc Committed by Melledy
Browse files

implement skill particle generation

parent 2a76e904
Branches
Tags
No related merge requests found
...@@ -19,6 +19,8 @@ import emu.grasscutter.game.entity.GameEntity; ...@@ -19,6 +19,8 @@ import emu.grasscutter.game.entity.GameEntity;
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.net.proto.AbilityActionGenerateElemBallOuterClass.AbilityActionGenerateElemBall; import emu.grasscutter.net.proto.AbilityActionGenerateElemBallOuterClass.AbilityActionGenerateElemBall;
import emu.grasscutter.net.proto.AbilityIdentifierOuterClass.AbilityIdentifier;
import emu.grasscutter.net.proto.AbilityInvocationsNotifyOuterClass.AbilityInvocationsNotify;
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;
import emu.grasscutter.net.proto.AbilityMetaModifierChangeOuterClass.AbilityMetaModifierChange; import emu.grasscutter.net.proto.AbilityMetaModifierChangeOuterClass.AbilityMetaModifierChange;
...@@ -42,6 +44,14 @@ public class AbilityManager { ...@@ -42,6 +44,14 @@ public class AbilityManager {
public void onAbilityInvoke(AbilityInvokeEntry invoke) throws Exception { public void onAbilityInvoke(AbilityInvokeEntry invoke) throws Exception {
// Grasscutter.getLogger().info(invoke.getArgumentType() + " (" + invoke.getArgumentTypeValue() + "): " + Utils.bytesToHex(invoke.toByteArray())); // Grasscutter.getLogger().info(invoke.getArgumentType() + " (" + invoke.getArgumentTypeValue() + "): " + Utils.bytesToHex(invoke.toByteArray()));
//AbilityIdentifier identifier = AbilityIdentifier.parseFrom(invoke.getAbilityData());
//AbilityInvocationsNotify notify = AbilityInvocationsNotify.parseFrom(invoke.getAbilityData());
//Grasscutter.getLogger().info("Ability id: " + Integer.toString(invoke.getEntityId()));
//Grasscutter.getLogger().info("invoke count: " + Double.toString(invoke.getTotalTickTime()));
switch (invoke.getArgumentType()) { switch (invoke.getArgumentType()) {
case ABILITY_META_OVERRIDE_PARAM: case ABILITY_META_OVERRIDE_PARAM:
handleOverrideParam(invoke); handleOverrideParam(invoke);
......
...@@ -31,6 +31,7 @@ import java.io.Reader; ...@@ -31,6 +31,7 @@ import java.io.Reader;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.InvalidProtocolBufferException;
...@@ -38,6 +39,7 @@ import com.google.protobuf.InvalidProtocolBufferException; ...@@ -38,6 +39,7 @@ 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<>(); private final static Int2ObjectMap<List<EnergyDropInfo>> energyDropData = new Int2ObjectOpenHashMap<>();
private final static Int2ObjectMap<List<SkillParticleGenerationInfo>> skillParticleGenerationData = new Int2ObjectOpenHashMap<>();
public EnergyManager(Player player) { public EnergyManager(Player player) {
this.player = player; this.player = player;
...@@ -61,12 +63,26 @@ public class EnergyManager { ...@@ -61,12 +63,26 @@ public class EnergyManager {
catch (Exception ex) { catch (Exception ex) {
Grasscutter.getLogger().error("Unable to load energy drop data.", ex); Grasscutter.getLogger().error("Unable to load energy drop data.", ex);
} }
// Read the data for particle generation from skills
try (Reader fileReader = new InputStreamReader(DataLoader.load("SkillParticleGeneration.json"))) {
List<SkillParticleGenerationEntry> skillParticleGenerationList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, SkillParticleGenerationEntry.class).getType());
for (SkillParticleGenerationEntry entry : skillParticleGenerationList) {
skillParticleGenerationData.put(entry.getAvatarId(), entry.getAmountList());
}
Grasscutter.getLogger().info("Skill particle generation data successfully loaded.");
}
catch (Exception ex) {
Grasscutter.getLogger().error("Unable to load skill particle generation data data.", ex);
}
} }
/********** /**********
Particle creation for elemental skills. Particle creation for elemental skills.
**********/ **********/
private int getCastingAvatarIdForElemBall(int invokeEntityId) { private int getCastingAvatarEntityIdForElemBall(int invokeEntityId) {
// To determine the avatar that has cast the skill that caused the energy particle to be generated, // To determine the avatar that has cast the skill that caused the energy particle to be generated,
// we have to look at the entity that has invoked the ability. This can either be that avatar directly, // we have to look at the entity that has invoked the ability. This can either be that avatar directly,
// or it can be an `EntityClientGadget`, owned (some way up the owner hierarchy) by the avatar // or it can be an `EntityClientGadget`, owned (some way up the owner hierarchy) by the avatar
...@@ -111,19 +127,41 @@ public class EnergyManager { ...@@ -111,19 +127,41 @@ public class EnergyManager {
// We can get that from the avatar's skill depot. // We can get that from the avatar's skill depot.
int itemId = 2024; int itemId = 2024;
// Generate 2 particles by default
int amount = 2;
// Try to fetch the avatar from the player's party and determine their element. // Try to fetch the avatar from the player's party and determine their element.
// ToDo: Does this work in co-op? // ToDo: Does this work in co-op?
int avatarId = getCastingAvatarIdForElemBall(invoke.getEntityId()); int avatarEntityId = getCastingAvatarEntityIdForElemBall(invoke.getEntityId());
Optional<EntityAvatar> avatarEntity = player.getTeamManager().getActiveTeam() Optional<EntityAvatar> avatarEntity = player.getTeamManager().getActiveTeam()
.stream() .stream()
.filter(character -> character.getId() == avatarId) .filter(character -> character.getId() == avatarEntityId)
.findFirst(); .findFirst();
// Bug: invokes twice sometimes, Ayato, Keqing
// Amber not getting element properly
// ToDo: deal with press, hold difference. deal with charge(Beidou, Yunjin)
if (avatarEntity.isPresent()) { if (avatarEntity.isPresent()) {
Avatar avatar = avatarEntity.get().getAvatar(); Avatar avatar = avatarEntity.get().getAvatar();
if (avatar != null) { if (avatar != null) {
int avatarId = avatar.getAvatarId();
AvatarSkillDepotData skillDepotData = avatar.getSkillDepot(); AvatarSkillDepotData skillDepotData = avatar.getSkillDepot();
if (!skillParticleGenerationData.containsKey(avatarId)) {
Grasscutter.getLogger().warn("No particle generation data for avatarId {} found.", avatarId);
}
else {
int roll = ThreadLocalRandom.current().nextInt(0, 100);
int percentageStack = 0;
for (SkillParticleGenerationInfo info : skillParticleGenerationData.get(avatarId)) {
int chance = info.getChance();
percentageStack += chance;
if (roll < percentageStack) {
amount = info.getValue();
break;
}
}
}
if (skillDepotData != null) { if (skillDepotData != null) {
ElementType element = skillDepotData.getElementType(); ElementType element = skillDepotData.getElementType();
...@@ -147,7 +185,8 @@ public class EnergyManager { ...@@ -147,7 +185,8 @@ public class EnergyManager {
} }
// Generate the particle/orb. // Generate the particle/orb.
generateElemBall(itemId, new Position(action.getPos()), 1); for (int i = 0; i < amount; i++)
generateElemBall(itemId, new Position(action.getPos()), 1);
} }
/********** /**********
......
package emu.grasscutter.game.managers.EnergyManager;
import java.util.List;
public class SkillParticleGenerationEntry {
private int avatarId;
private List<SkillParticleGenerationInfo> amountList;
public int getAvatarId() {
return this.avatarId;
}
public List<SkillParticleGenerationInfo> getAmountList() {
return this.amountList;
}
}
package emu.grasscutter.game.managers.EnergyManager;
public class SkillParticleGenerationInfo {
private int value;
private int chance;
public int getValue() {
return this.value;
}
public int getChance() {
return this.chance;
}
}
[
{
"avatarId": 10000002,
"name": "Kamisato Ayaka",
"amountList": [
{
"value": 4,
"chance": 50
},
{
"value": 5,
"chance": 50
}
]
},
{
"avatarId": 10000003,
"name": "Jean",
"amountList": [
{
"value": 2,
"chance": 33
},
{
"value": 3,
"chance": 67
}
]
},
{
"avatarId": 10000005,
"name": "Traveler",
"amountList": [
{
"value": 3,
"chance": 67
},
{
"value": 4,
"chance": 33
}
]
},
{
"avatarId": 10000006,
"name": "Lisa",
"amountList": [
{
"value": 5,
"chance": 100
}
]
},
{
"avatarId": 10000007,
"name": "Traveler",
"amountList": [
{
"value": 3,
"chance": 67
},
{
"value": 4,
"chance": 33
}
]
},
{
"avatarId": 10000014,
"name": "Barbara",
"amountList": [
{
"value": 0,
"chance": 100
}
]
},
{
"avatarId": 10000015,
"name": "Kaeya",
"amountList": [
{
"value": 2,
"chance": 33
},
{
"value": 3,
"chance": 67
}
]
},
{
"avatarId": 10000016,
"name": "Diluc",
"amountList": [
{
"value": 1,
"chance": 33
},
{
"value": 2,
"chance": 67
}
]
},
{
"avatarId": 10000020,
"name": "Razor",
"amountList": [
{
"value": 3,
"chance": 100
}
]
},
{
"avatarId": 10000021,
"name": "Amber",
"amountList": [
{
"value": 4,
"chance": 100
}
]
},
{
"avatarId": 10000022,
"name": "Venti",
"amountList": [
{
"value": 3,
"chance": 100
}
]
},
{
"avatarId": 10000023,
"name": "Xiangling",
"amountList": [
{
"value": 1,
"chance": 100
}
]
},
{
"avatarId": 10000024,
"name": "Beidou",
"amountList": [
{
"value": 2,
"chance": 100
}
]
},
{
"avatarId": 10000025,
"name": "Xingqiu",
"amountList": [
{
"value": 5,
"chance": 100
}
]
},
{
"avatarId": 10000026,
"name": "Xiao",
"amountList": [
{
"value": 3,
"chance": 100
}
]
},
{
"avatarId": 10000027,
"name": "Ningguang",
"amountList": [
{
"value": 3,
"chance": 33
},
{
"value": 4,
"chance": 67
}
]
},
{
"avatarId": 10000029,
"name": "Klee",
"amountList": [
{
"value": 4,
"chance": 100
}
]
},
{
"avatarId": 10000030,
"name": "Zhongli",
"amountList": [
{
"value": 0,
"chance": 50
},
{
"value": 1,
"chance": 50
}
]
},
{
"avatarId": 10000031,
"name": "Fischl",
"amountList": [
{
"value": 0,
"chance": 33
},
{
"value": 1,
"chance": 67
}
]
},
{
"avatarId": 10000032,
"name": "Bennett",
"amountList": [
{
"value": 2,
"chance": 75
},
{
"value": 3,
"chance": 25
}
]
},
{
"avatarId": 10000033,
"name": "Tartaglia",
"amountList": [
{
"value": 1,
"chance": 100
}
]
},
{
"avatarId": 10000034,
"name": "Noelle",
"amountList": [
{
"value": 0,
"chance": 100
}
]
},
{
"avatarId": 10000035,
"name": "Qiqi",
"amountList": [
{
"value": 0,
"chance": 100
}
]
},
{
"avatarId": 10000036,
"name": "Chongyun",
"amountList": [
{
"value": 4,
"chance": 100
}
]
},
{
"avatarId": 10000037,
"name": "Ganyu",
"amountList": [
{
"value": 2,
"chance": 100
}
]
},
{
"avatarId": 10000038,
"name": "Albedo",
"amountList": [
{
"value": 0,
"chance": 33
},
{
"value": 1,
"chance": 67
}
]
},
{
"avatarId": 10000039,
"name": "Diona",
"amountList": [
{
"value": 0,
"chance": 20
},
{
"value": 1,
"chance": 80
}
]
},
{
"avatarId": 10000041,
"name": "Mona",
"amountList": [
{
"value": 3,
"chance": 67
},
{
"value": 4,
"chance": 33
}
]
},
{
"avatarId": 10000042,
"name": "Keqing",
"amountList": [
{
"value": 2,
"chance": 50
},
{
"value": 3,
"chance": 50
}
]
},
{
"avatarId": 10000043,
"name": "Sucrose",
"amountList": [
{
"value": 4,
"chance": 100
}
]
},
{
"avatarId": 10000044,
"name": "Xinyan",
"amountList": [
{
"value": 4,
"chance": 100
}
]
},
{
"avatarId": 10000045,
"name": "Rosaria",
"amountList": [
{
"value": 3,
"chance": 100
}
]
},
{
"avatarId": 10000046,
"name": "Hu Tao",
"amountList": [
{
"value": 2,
"chance": 50
},
{
"value": 3,
"chance": 50
}
]
},
{
"avatarId": 10000047,
"name": "Kaedehara Kazuha",
"amountList": [
{
"value": 3,
"chance": 50
},
{
"value": 4,
"chance": 50
}
]
},
{
"avatarId": 10000048,
"name": "Yanfei",
"amountList": [
{
"value": 3,
"chance": 100
}
]
},
{
"avatarId": 10000049,
"name": "Yoimiya",
"amountList": [
{
"value": 1,
"chance": 100
}
]
},
{
"avatarId": 10000050,
"name": "Thoma",
"amountList": [
{
"value": 3,
"chance": 50
},
{
"value": 4,
"chance": 50
}
]
},
{
"avatarId": 10000051,
"name": "Eula",
"amountList": [
{
"value": 1,
"chance": 50
},
{
"value": 2,
"chance": 50
}
]
},
{
"avatarId": 10000052,
"name": "Raiden Shogun",
"amountList": [
{
"value": 0,
"chance": 50
},
{
"value": 1,
"chance": 50
}
]
},
{
"avatarId": 10000053,
"name": "Sayu",
"amountList": [
{
"value": 2,
"chance": 100
}
]
},
{
"avatarId": 10000054,
"name": "Sangonomiya Kokomi",
"amountList": [
{
"value": 0,
"chance": 33
},
{
"value": 1,
"chance": 67
}
]
},
{
"avatarId": 10000055,
"name": "Gorou",
"amountList": [
{
"value": 2,
"chance": 100
}
]
},
{
"avatarId": 10000056,
"name": "Kujou Sara",
"amountList": [
{
"value": 3,
"chance": 100
}
]
},
{
"avatarId": 10000057,
"name": "Arataki Itto",
"amountList": [
{
"value": 3,
"chance": 50
},
{
"value": 4,
"chance": 50
}
]
},
{
"avatarId": 10000058,
"name": "Yae Miko",
"amountList": [
{
"value": 1,
"chance": 100
}
]
},
{
"avatarId": 10000062,
"name": "Aloy",
"amountList": [
{
"value": 5,
"chance": 100
}
]
},
{
"avatarId": 10000063,
"name": "Shenhe",
"amountList": [
{
"value": 3,
"chance": 100
}
]
},
{
"avatarId": 10000064,
"name": "Yun Jin",
"amountList": [
{
"value": 2,
"chance": 100
}
]
},
{
"avatarId": 10000066,
"name": "Kamisato Ayato",
"amountList": [
{
"value": 1,
"chance": 50
},
{
"value": 2,
"chance": 50
}
]
}
]
\ No newline at end of file
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