refactor the challenge

This commit is contained in:
Akka 2022-05-23 20:55:22 +08:00 committed by Melledy
parent 233b46b2f9
commit 57260415b0
30 changed files with 713 additions and 280 deletions

View File

@ -1,180 +0,0 @@
package emu.grasscutter.game.dungeons;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.def.DungeonData;
import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.SceneGroup;
import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.server.packet.send.PacketChallengeDataNotify;
import emu.grasscutter.server.packet.send.PacketDungeonChallengeBeginNotify;
import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify;
import emu.grasscutter.server.packet.send.PacketGadgetAutoPickDropInfoNotify;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.ArrayList;
import java.util.List;
public class DungeonChallenge {
private final Scene scene;
private final SceneGroup group;
private int challengeIndex;
private int challengeId;
private boolean success;
private boolean progress;
/**
* has more challenge
*/
private boolean stage;
private int score;
private int objective = 0;
private IntSet rewardedPlayers;
public DungeonChallenge(Scene scene, SceneGroup group, int challengeId, int challengeIndex, int objective) {
this.scene = scene;
this.group = group;
this.challengeId = challengeId;
this.challengeIndex = challengeIndex;
this.objective = objective;
this.setRewardedPlayers(new IntOpenHashSet());
}
public Scene getScene() {
return scene;
}
public SceneGroup getGroup() {
return group;
}
public int getChallengeIndex() {
return challengeIndex;
}
public void setChallengeIndex(int challengeIndex) {
this.challengeIndex = challengeIndex;
}
public int getChallengeId() {
return challengeId;
}
public void setChallengeId(int challengeId) {
this.challengeId = challengeId;
}
public int getObjective() {
return objective;
}
public void setObjective(int objective) {
this.objective = objective;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean isSuccess) {
this.success = isSuccess;
}
public boolean inProgress() {
return progress;
}
public int getScore() {
return score;
}
public boolean isStage() {
return stage;
}
public void setStage(boolean stage) {
this.stage = stage;
}
public int getTimeLimit() {
return 600;
}
public IntSet getRewardedPlayers() {
return rewardedPlayers;
}
public void setRewardedPlayers(IntSet rewardedPlayers) {
this.rewardedPlayers = rewardedPlayers;
}
public void start() {
this.progress = true;
getScene().broadcastPacket(new PacketDungeonChallengeBeginNotify(this));
}
public void finish() {
this.progress = false;
getScene().broadcastPacket(new PacketDungeonChallengeFinishNotify(this));
if (this.isSuccess()) {
// Call success script event
this.getScene().getScriptManager().callEvent(EventType.EVENT_CHALLENGE_SUCCESS,
// TODO record the time in PARAM2 and used in action
new ScriptArgs().setParam2(100));
// Settle
settle();
} else {
this.getScene().getScriptManager().callEvent(EventType.EVENT_CHALLENGE_FAIL, null);
}
}
private void settle() {
getScene().getDungeonSettleObservers().forEach(o -> o.onDungeonSettle(getScene()));
if(!stage){
getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE,
new ScriptArgs(this.isSuccess() ? 1 : 0));
}
}
public void onMonsterDie(EntityMonster entity) {
score = getScore() + 1;
getScene().broadcastPacket(new PacketChallengeDataNotify(this, 1, getScore()));
if (getScore() >= getObjective() && this.progress) {
this.setSuccess(true);
finish();
}
}
public void getStatueDrops(Player player) {
DungeonData dungeonData = getScene().getDungeonData();
if (!isSuccess() || dungeonData == null || dungeonData.getRewardPreview() == null || dungeonData.getRewardPreview().getPreviewItems().length == 0) {
return;
}
// Already rewarded
if (getRewardedPlayers().contains(player.getUid())) {
return;
}
List<GameItem> rewards = new ArrayList<>();
for (ItemParamData param : getScene().getDungeonData().getRewardPreview().getPreviewItems()) {
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
}
player.getInventory().addItems(rewards, ActionReason.DungeonStatueDrop);
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
getRewardedPlayers().add(player.getUid());
}
}

View File

@ -0,0 +1,91 @@
package emu.grasscutter.game.dungeons.challenge;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.def.DungeonData;
import emu.grasscutter.game.dungeons.challenge.trigger.ChallengeTrigger;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.SceneGroup;
import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.server.packet.send.PacketGadgetAutoPickDropInfoNotify;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.ArrayList;
import java.util.List;
public class DungeonChallenge extends WorldChallenge {
/**
* has more challenge
*/
private boolean stage;
private IntSet rewardedPlayers;
public DungeonChallenge(Scene scene, SceneGroup group,
int challengeId, int challengeIndex,
List<Integer> paramList,
int timeLimit, int goal,
List<ChallengeTrigger> challengeTriggers) {
super(scene, group, challengeId, challengeIndex, paramList, timeLimit, goal, challengeTriggers);
this.setRewardedPlayers(new IntOpenHashSet());
}
public boolean isStage() {
return stage;
}
public void setStage(boolean stage) {
this.stage = stage;
}
public IntSet getRewardedPlayers() {
return rewardedPlayers;
}
public void setRewardedPlayers(IntSet rewardedPlayers) {
this.rewardedPlayers = rewardedPlayers;
}
@Override
public void done() {
super.done();
if (this.isSuccess()) {
// Settle
settle();
}
}
private void settle() {
if(!stage){
getScene().getDungeonSettleObservers().forEach(o -> o.onDungeonSettle(getScene()));
getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE,
new ScriptArgs(this.isSuccess() ? 1 : 0));
}
}
public void getStatueDrops(Player player) {
DungeonData dungeonData = getScene().getDungeonData();
if (!isSuccess() || dungeonData == null || dungeonData.getRewardPreview() == null || dungeonData.getRewardPreview().getPreviewItems().length == 0) {
return;
}
// Already rewarded
if (getRewardedPlayers().contains(player.getUid())) {
return;
}
List<GameItem> rewards = new ArrayList<>();
for (ItemParamData param : getScene().getDungeonData().getRewardPreview().getPreviewItems()) {
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
}
player.getInventory().addItems(rewards, ActionReason.DungeonStatueDrop);
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
getRewardedPlayers().add(player.getUid());
}
}

View File

@ -0,0 +1,132 @@
package emu.grasscutter.game.dungeons.challenge;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.dungeons.challenge.trigger.ChallengeTrigger;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.SceneGroup;
import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.server.packet.send.PacketDungeonChallengeBeginNotify;
import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@Getter
@Setter
public class WorldChallenge {
private final Scene scene;
private final SceneGroup group;
private final int challengeId;
private final int challengeIndex;
private final List<Integer> paramList;
private final int timeLimit;
private final List<ChallengeTrigger> challengeTriggers;
private boolean progress;
private boolean success;
private long startedAt;
private int finishedTime;
private final int goal;
private final AtomicInteger score;
public WorldChallenge(Scene scene, SceneGroup group,
int challengeId, int challengeIndex, List<Integer> paramList,
int timeLimit, int goal,
List<ChallengeTrigger> challengeTriggers){
this.scene = scene;
this.group = group;
this.challengeId = challengeId;
this.challengeIndex = challengeIndex;
this.paramList = paramList;
this.timeLimit = timeLimit;
this.challengeTriggers = challengeTriggers;
this.goal = goal;
this.score = new AtomicInteger(0);
}
public boolean inProgress(){
return this.progress;
}
public void onCheckTimeOut(){
if(!inProgress()){
return;
}
if(timeLimit <= 0){
return;
}
challengeTriggers.forEach(t -> t.onCheckTimeout(this));
}
public void start(){
if(inProgress()){
Grasscutter.getLogger().info("Could not start a in progress challenge.");
return;
}
this.progress = true;
this.startedAt = System.currentTimeMillis();
getScene().broadcastPacket(new PacketDungeonChallengeBeginNotify(this));
challengeTriggers.forEach(t -> t.onBegin(this));
}
public void done(){
if(!inProgress()){
return;
}
finish(true);
this.getScene().getScriptManager().callEvent(EventType.EVENT_CHALLENGE_SUCCESS,
// TODO record the time in PARAM2 and used in action
new ScriptArgs().setParam2(finishedTime));
challengeTriggers.forEach(t -> t.onFinish(this));
}
public void fail(){
if(!inProgress()){
return;
}
finish(false);
this.getScene().getScriptManager().callEvent(EventType.EVENT_CHALLENGE_FAIL, null);
challengeTriggers.forEach(t -> t.onFinish(this));
}
private void finish(boolean success){
this.progress = false;
this.success = success;
this.finishedTime = (int)((System.currentTimeMillis() - this.startedAt) / 1000L);
getScene().broadcastPacket(new PacketDungeonChallengeFinishNotify(this));
}
public int increaseScore(){
return score.incrementAndGet();
}
public void onMonsterDeath(EntityMonster monster){
if(!inProgress()){
return;
}
if(monster.getGroupId() != getGroup().id){
return;
}
this.challengeTriggers.forEach(t -> t.onMonsterDeath(this, monster));
}
public void onGadgetDeath(EntityGadget gadget){
if(!inProgress()){
return;
}
if(gadget.getGroupId() != getGroup().id){
return;
}
this.challengeTriggers.forEach(t -> t.onGadgetDeath(this, gadget));
}
public void onGadgetDamage(EntityGadget gadget){
if(!inProgress()){
return;
}
if(gadget.getGroupId() != getGroup().id){
return;
}
this.challengeTriggers.forEach(t -> t.onGadgetDamage(this, gadget));
}
}

View File

@ -0,0 +1,30 @@
package emu.grasscutter.game.dungeons.challenge.factory;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.data.SceneGroup;
import java.util.ArrayList;
import java.util.List;
public class ChallengeFactory {
private static final List<ChallengeFactoryHandler> challengeFactoryHandlers = new ArrayList<>();
static {
challengeFactoryHandlers.add(new DungeonChallengeFactoryHandler());
challengeFactoryHandlers.add(new DungeonGuardChallengeFactoryHandler());
challengeFactoryHandlers.add(new KillGadgetChallengeFactoryHandler());
challengeFactoryHandlers.add(new KillMonsterChallengeFactoryHandler());
}
public static WorldChallenge getChallenge(int param1, int param2, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group){
for(var handler : challengeFactoryHandlers){
if(!handler.isThisType(param1, param2, param3, param4, param5, param6, scene, group)){
continue;
}
return handler.build(param1, param2, param3, param4, param5, param6, scene, group);
}
return null;
}
}

View File

@ -0,0 +1,10 @@
package emu.grasscutter.game.dungeons.challenge.factory;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.data.SceneGroup;
public interface ChallengeFactoryHandler {
boolean isThisType(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group);
WorldChallenge build(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group);
}

View File

@ -0,0 +1,33 @@
package emu.grasscutter.game.dungeons.challenge.factory;
import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger;
import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterTrigger;
import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.scripts.data.SceneGroup;
import java.util.List;
public class DungeonChallengeFactoryHandler implements ChallengeFactoryHandler{
@Override
public boolean isThisType(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
// ActiveChallenge with 1,1000,300,233101003,15,0
return scene.getSceneType() == SceneType.SCENE_DUNGEON
&& param4 == group.id;
}
@Override
public WorldChallenge build(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
var realGroup = scene.getScriptManager().getGroupById(param4);
return new DungeonChallenge(
scene, realGroup,
challengeId, // Id
challengeIndex, // Index
List.of(param5, param3),
param3, // Limit
param5, // Goal
List.of(new InTimeTrigger(), new KillMonsterTrigger()));
}
}

View File

@ -0,0 +1,34 @@
package emu.grasscutter.game.dungeons.challenge.factory;
import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.dungeons.challenge.trigger.GuardTrigger;
import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger;
import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterTrigger;
import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.data.SceneGroup;
import java.util.List;
public class DungeonGuardChallengeFactoryHandler implements ChallengeFactoryHandler{
@Override
public boolean isThisType(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
// ActiveChallenge with 1,188,234101003,12,3030,0
return scene.getSceneType() == SceneType.SCENE_DUNGEON
&& param3 == group.id;
}
@Override
public WorldChallenge build(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
var realGroup = scene.getScriptManager().getGroupById(param3);
return new DungeonChallenge(
scene, realGroup,
challengeId, // Id
challengeIndex, // Index
List.of(param4, 0),
0, // Limit
param5, // Goal
List.of(new GuardTrigger()));
}
}

View File

@ -0,0 +1,34 @@
package emu.grasscutter.game.dungeons.challenge.factory;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.dungeons.challenge.factory.ChallengeFactoryHandler;
import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger;
import emu.grasscutter.game.dungeons.challenge.trigger.KillGadgetTrigger;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.data.SceneGroup;
import java.util.List;
public class KillGadgetChallengeFactoryHandler implements ChallengeFactoryHandler {
@Override
public boolean isThisType(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
// kill gadgets(explosive barrel) in time
// ActiveChallenge with 56,201,20,2,201,4
// open chest in time
// ActiveChallenge with 666,202,30,7,202,1
return challengeId == 201 || challengeId == 202;
}
@Override
public WorldChallenge build(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
return new WorldChallenge(
scene, group,
challengeId, // Id
challengeIndex, // Index
List.of(param3, param6, 0),
param3, // Limit
param6, // Goal
List.of(new InTimeTrigger(), new KillGadgetTrigger())
);
}
}

View File

@ -0,0 +1,31 @@
package emu.grasscutter.game.dungeons.challenge.factory;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger;
import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterTrigger;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.data.SceneGroup;
import java.util.List;
public class KillMonsterChallengeFactoryHandler implements ChallengeFactoryHandler{
@Override
public boolean isThisType(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
// ActiveChallenge with 180,180,45,133108061,1,0
return challengeId == 180;
}
@Override
public WorldChallenge build(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
var realGroup = scene.getScriptManager().getGroupById(param4);
return new WorldChallenge(
scene, realGroup,
challengeId, // Id
challengeIndex, // Index
List.of(param5, param3),
param3, // Limit
param5, // Goal
List.of(new KillMonsterTrigger(), new InTimeTrigger())
);
}
}

View File

@ -0,0 +1,14 @@
package emu.grasscutter.game.dungeons.challenge.trigger;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityMonster;
public abstract class ChallengeTrigger {
public void onBegin(WorldChallenge challenge){}
public void onFinish(WorldChallenge challenge){}
public void onMonsterDeath(WorldChallenge challenge, EntityMonster monster){}
public void onGadgetDeath(WorldChallenge challenge, EntityGadget gadget){}
public void onCheckTimeout(WorldChallenge challenge){}
public void onGadgetDamage(WorldChallenge challenge, EntityGadget gadget){}
}

View File

@ -0,0 +1,27 @@
package emu.grasscutter.game.dungeons.challenge.trigger;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.server.packet.send.PacketChallengeDataNotify;
public class GuardTrigger extends KillMonsterTrigger{
@Override
public void onBegin(WorldChallenge challenge) {
super.onBegin(challenge);
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 2, 100));
}
@Override
public void onGadgetDamage(WorldChallenge challenge, EntityGadget gadget) {
var curHp = gadget.getFightProperties().get(FightProperty.FIGHT_PROP_CUR_HP.getId());
var maxHp = gadget.getFightProperties().get(FightProperty.FIGHT_PROP_BASE_HP.getId());
int percent = (int) (curHp / maxHp);
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 2, percent));
if(percent <= 0){
challenge.fail();
}
}
}

View File

@ -0,0 +1,13 @@
package emu.grasscutter.game.dungeons.challenge.trigger;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
public class InTimeTrigger extends ChallengeTrigger{
@Override
public void onCheckTimeout(WorldChallenge challenge) {
var current = System.currentTimeMillis();
if(current - challenge.getStartedAt() > challenge.getTimeLimit() * 1000L){
challenge.fail();
}
}
}

View File

@ -0,0 +1,23 @@
package emu.grasscutter.game.dungeons.challenge.trigger;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.server.packet.send.PacketChallengeDataNotify;
public class KillGadgetTrigger extends ChallengeTrigger{
@Override
public void onBegin(WorldChallenge challenge) {
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 2, challenge.getScore().get()));
}
@Override
public void onGadgetDeath(WorldChallenge challenge, EntityGadget gadget) {
var newScore = challenge.increaseScore();
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 2, newScore));
if(newScore >= challenge.getGoal()){
challenge.done();
}
}
}

View File

@ -0,0 +1,23 @@
package emu.grasscutter.game.dungeons.challenge.trigger;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.server.packet.send.PacketChallengeDataNotify;
public class KillMonsterTrigger extends ChallengeTrigger{
@Override
public void onBegin(WorldChallenge challenge) {
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 1, challenge.getScore().get()));
}
@Override
public void onMonsterDeath(WorldChallenge challenge, EntityMonster monster) {
var newScore = challenge.increaseScore();
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 1, newScore));
if(newScore >= challenge.getGoal()){
challenge.done();
}
}
}

View File

@ -1,18 +1,10 @@
package emu.grasscutter.game.entity;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.def.GadgetData;
import emu.grasscutter.game.entity.gadget.GadgetChest;
import emu.grasscutter.game.entity.gadget.GadgetContent;
import emu.grasscutter.game.entity.gadget.GadgetGatherPoint;
import emu.grasscutter.game.entity.gadget.GadgetRewardStatue;
import emu.grasscutter.game.entity.gadget.GadgetWorktop;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.EntityType;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.data.def.GadgetPropData;
import emu.grasscutter.game.entity.gadget.*;
import emu.grasscutter.game.props.*;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
@ -32,6 +24,7 @@ import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.SceneGadget;
import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.server.packet.send.PacketGadgetStateNotify;
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
@ -160,7 +153,10 @@ public class EntityGadget extends EntityBaseGadget {
@Override
public void onDeath(int killerId) {
if(getScene().getChallenge() != null){
getScene().getChallenge().onGadgetDeath(this);
}
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_GADGET_DIE, new ScriptArgs(this.getConfigId()));
}
@Override
@ -203,4 +199,8 @@ public class EntityGadget extends EntityBaseGadget {
return entityInfo.build();
}
public void die() {
getScene().broadcastPacket(new PacketLifeStateChangeNotify(this, LifeState.LIFE_DEAD));
this.onDeath(0);
}
}

View File

@ -122,8 +122,8 @@ public class EntityMonster extends GameEntity {
this.getScene().getDeadSpawnedEntities().add(getSpawnEntry());
}
// first set the challenge data
if (getScene().getChallenge() != null && getScene().getChallenge().getGroup().id == this.getGroupId()) {
getScene().getChallenge().onMonsterDie(this);
if (getScene().getChallenge() != null) {
getScene().getChallenge().onMonsterDeath(this);
}
if (getScene().getScriptManager().isInit() && this.getGroupId() > 0) {
if(getScene().getScriptManager().getScriptMonsterSpawnService() != null){

View File

@ -38,6 +38,8 @@ public class GadgetChest extends GadgetContent {
getGadget().updateState(ScriptGadgetState.ChestOpened);
player.sendPacket(new PacketGadgetInteractRsp(this.getGadget(), InteractTypeOuterClass.InteractType.INTERACT_OPEN_CHEST));
// let the chest disappear
getGadget().die();
return true;
}
}

View File

@ -1,5 +1,6 @@
package emu.grasscutter.game.entity.gadget;
import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
@ -14,8 +15,8 @@ public class GadgetRewardStatue extends GadgetContent {
}
public boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType) {
if (player.getScene().getChallenge() != null) {
player.getScene().getChallenge().getStatueDrops(player);
if (player.getScene().getChallenge() != null && player.getScene().getChallenge() instanceof DungeonChallenge dungeonChallenge) {
dungeonChallenge.getStatueDrops(player);
}
player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_OPEN_STATUE));

View File

@ -1,5 +1,6 @@
package emu.grasscutter.game.entity.gadget.chest;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.game.entity.gadget.GadgetChest;
import emu.grasscutter.game.inventory.GameItem;
@ -22,6 +23,10 @@ public class BossChestInteractHandler implements ChestInteractHandler{
var monster = chest.getGadget().getMetaGadget().group.monsters.get(chest.getGadget().getMetaGadget().boss_chest.monster_config_id);
var reward = worldDataManager.getRewardByBossId(monster.monster_id);
if(reward == null){
Grasscutter.getLogger().warn("Could not found the reward of boss monster {}", monster.monster_id);
return false;
}
List<GameItem> rewards = new ArrayList<>();
for (ItemParamData param : reward.getPreviewItems()) {
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));

View File

@ -4,7 +4,6 @@ import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameDepot;
import emu.grasscutter.data.def.*;
import emu.grasscutter.game.dungeons.DungeonChallenge;
import emu.grasscutter.game.dungeons.DungeonSettleListener;
import emu.grasscutter.game.entity.*;
import emu.grasscutter.game.player.Player;
@ -14,6 +13,7 @@ import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.LifeState;
import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.game.world.SpawnDataEntry.SpawnGroupEntry;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType;
@ -32,8 +32,6 @@ import org.danilopianini.util.SpatialIndex;
import java.util.*;
import java.util.stream.Collectors;
import static emu.grasscutter.utils.Language.translate;
public class Scene {
private final World world;
private final SceneData sceneData;
@ -51,7 +49,7 @@ public class Scene {
private int weather;
private SceneScriptManager scriptManager;
private DungeonChallenge challenge;
private WorldChallenge challenge;
private List<DungeonSettleListener> dungeonSettleListeners;
private DungeonData dungeonData;
private int prevScene; // Id of the previous scene
@ -199,11 +197,11 @@ public class Scene {
this.dungeonData = dungeonData;
}
public DungeonChallenge getChallenge() {
public WorldChallenge getChallenge() {
return challenge;
}
public void setChallenge(DungeonChallenge challenge) {
public void setChallenge(WorldChallenge challenge) {
this.challenge = challenge;
}
@ -353,7 +351,14 @@ public class Scene {
this.broadcastPacket(new PacketSceneEntityDisappearNotify(removed, visionType));
}
}
public synchronized void removeEntities(List<GameEntity> entity, VisionType visionType) {
var toRemove = entity.stream()
.map(this::removeEntityDirectly)
.toList();
if (toRemove.size() > 0) {
this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, visionType));
}
}
public synchronized void replaceEntity(EntityAvatar oldEntity, EntityAvatar newEntity) {
this.removeEntityDirectly(oldEntity);
this.addEntityDirectly(newEntity);
@ -418,6 +423,10 @@ public class Scene {
}
// Triggers
this.scriptManager.checkRegions();
if(challenge != null){
challenge.onCheckTimeOut();
}
}
// TODO - Test
@ -591,20 +600,20 @@ public class Scene {
continue;
}
do {
var suiteData = group.getSuiteByIndex(suite);
suiteData.sceneTriggers.forEach(getScriptManager()::registerTrigger);
// just load the 'init' suite, avoid spawn the suite added by AddExtraGroupSuite etc.
var suiteData = group.getSuiteByIndex(suite);
suiteData.sceneTriggers.forEach(getScriptManager()::registerTrigger);
entities.addAll(suiteData.sceneGadgets.stream()
.map(g -> scriptManager.createGadget(group.id, group.block_id, g)).toList());
entities.addAll(suiteData.sceneMonsters.stream()
.map(mob -> scriptManager.createMonster(group.id, group.block_id, mob)).toList());
entities.addAll(suiteData.sceneGadgets.stream()
.map(g -> scriptManager.createGadget(group.id, group.block_id, g)).toList());
entities.addAll(suiteData.sceneMonsters.stream()
.map(mob -> scriptManager.createMonster(group.id, group.block_id, mob)).toList());
suite++;
} while (suite < group.init_config.end_suite);
}
scriptManager.meetEntities(entities);
//scriptManager.callEvent(EventType.EVENT_GROUP_LOAD, null);
//groups.forEach(g -> scriptManager.callEvent(EventType.EVENT_GROUP_LOAD, null));
Grasscutter.getLogger().info("Scene {} loaded {} group(s)", this.getId(), groups.size());
}

View File

@ -26,16 +26,13 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
public class SceneScriptManager {
private final Scene scene;
private final Map<String, Integer> variables;
private SceneMeta meta;
private boolean isInit;
/**
* SceneTrigger Set
*/
private final Map<String, SceneTrigger> triggers;
/**
* current triggers controlled by RefreshGroup
*/
@ -51,12 +48,11 @@ public class SceneScriptManager {
public static final ExecutorService eventExecutor;
static {
eventExecutor = new ThreadPoolExecutor(4, 4,
60, TimeUnit.SECONDS, new LinkedBlockingDeque<>(100),
60, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1000),
FastThreadLocalThread::new, new ThreadPoolExecutor.AbortPolicy());
}
public SceneScriptManager(Scene scene) {
this.scene = scene;
this.triggers = new HashMap<>();
this.currentTriggers = new Int2ObjectOpenHashMap<>();
this.regions = new Int2ObjectOpenHashMap<>();
@ -96,13 +92,16 @@ public class SceneScriptManager {
public Set<SceneTrigger> getTriggersByEvent(int eventId) {
return currentTriggers.computeIfAbsent(eventId, e -> new HashSet<>());
}
public void registerTrigger(List<SceneTrigger> triggers) {
triggers.forEach(this::registerTrigger);
}
public void registerTrigger(SceneTrigger trigger) {
this.triggers.put(trigger.name, trigger);
getTriggersByEvent(trigger.event).add(trigger);
}
public void deregisterTrigger(List<SceneTrigger> triggers) {
triggers.forEach(this::deregisterTrigger);
}
public void deregisterTrigger(SceneTrigger trigger) {
this.triggers.remove(trigger.name);
getTriggersByEvent(trigger.event).remove(trigger);
}
public void resetTriggers(int eventId) {
@ -206,6 +205,16 @@ public class SceneScriptManager {
}
}
public void addGroupSuite(SceneGroup group, SceneSuite suite){
spawnMonstersInGroup(group, suite);
spawnGadgetsInGroup(group, suite);
registerTrigger(suite.sceneTriggers);
}
public void removeGroupSuite(SceneGroup group, SceneSuite suite){
removeMonstersInGroup(group, suite);
removeGadgetsInGroup(group, suite);
deregisterTrigger(suite.sceneTriggers);
}
public void spawnGadgetsInGroup(SceneGroup group, int suiteIndex) {
spawnGadgetsInGroup(group, group.getSuiteByIndex(suiteIndex));
}
@ -241,7 +250,6 @@ public class SceneScriptManager {
}
this.addEntities(suite.sceneMonsters.stream()
.map(mob -> createMonster(group.id, group.block_id, mob)).toList());
}
public void spawnMonstersInGroup(SceneGroup group) {
@ -326,7 +334,7 @@ public class SceneScriptManager {
try{
return func.call(ScriptLoader.getScriptLibLua(), args);
}catch (LuaError error){
ScriptLib.logger.error("[LUA] call trigger failed {},{},{}",name,args,error.getMessage());
ScriptLib.logger.error("[LUA] call trigger failed {},{}",name,args,error);
return LuaValue.valueOf(-1);
}
}
@ -388,6 +396,7 @@ public class SceneScriptManager {
entity.setGroupId(groupId);
entity.setBlockId(blockId);
entity.setConfigId(monster.config_id);
entity.setPoseId(monster.pose_id);
this.getScriptMonsterSpawnService()
.onMonsterCreatedListener.forEach(action -> action.onNotify(entity));
@ -410,4 +419,28 @@ public class SceneScriptManager {
public PhTree<SceneBlock> getBlocksIndex() {
return meta.sceneBlockIndex;
}
public void removeMonstersInGroup(SceneGroup group, SceneSuite suite) {
var configSet = suite.sceneMonsters.stream()
.map(m -> m.config_id)
.collect(Collectors.toSet());
var toRemove = getScene().getEntities().values().stream()
.filter(e -> e instanceof EntityMonster)
.filter(e -> e.getGroupId() == group.id)
.filter(e -> configSet.contains(e.getConfigId()))
.toList();
getScene().removeEntities(toRemove, VisionTypeOuterClass.VisionType.VISION_MISS);
}
public void removeGadgetsInGroup(SceneGroup group, SceneSuite suite) {
var configSet = suite.sceneGadgets.stream()
.map(m -> m.config_id)
.collect(Collectors.toSet());
var toRemove = getScene().getEntities().values().stream()
.filter(e -> e instanceof EntityGadget)
.filter(e -> e.getGroupId() == group.id)
.filter(e -> configSet.contains(e.getConfigId()))
.toList();
getScene().removeEntities(toRemove, VisionTypeOuterClass.VisionType.VISION_MISS);
}
}

View File

@ -1,10 +1,11 @@
package emu.grasscutter.scripts;
import emu.grasscutter.game.dungeons.DungeonChallenge;
import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.entity.gadget.GadgetWorktop;
import emu.grasscutter.game.dungeons.challenge.factory.ChallengeFactory;
import emu.grasscutter.scripts.data.SceneGroup;
import emu.grasscutter.scripts.data.SceneRegion;
import emu.grasscutter.server.packet.send.PacketCanUseSkillNotify;
@ -159,48 +160,102 @@ public class ScriptLib {
if (group == null || group.monsters == null) {
return 1;
}
var suiteData = group.getSuiteByIndex(suite);
if(suiteData == null){
return 1;
}
// avoid spawn wrong monster
if(getSceneScriptManager().getScene().getChallenge() != null)
if(!getSceneScriptManager().getScene().getChallenge().inProgress() ||
getSceneScriptManager().getScene().getChallenge().getGroup().id != groupId){
return 0;
}
this.getSceneScriptManager().spawnMonstersInGroup(group, suite);
this.getSceneScriptManager().addGroupSuite(group, suiteData);
return 0;
}
public int GoToGroupSuite(int groupId, int suite) {
logger.debug("[LUA] Call GoToGroupSuite with {},{}",
groupId,suite);
SceneGroup group = getSceneScriptManager().getGroupById(groupId);
if (group == null || group.monsters == null) {
return 1;
}
var suiteData = group.getSuiteByIndex(suite);
if(suiteData == null){
return 1;
}
for(var suiteItem : group.suites){
if(suiteData == suiteItem){
continue;
}
this.getSceneScriptManager().removeGroupSuite(group, suiteItem);
}
this.getSceneScriptManager().addGroupSuite(group, suiteData);
return 0;
}
public int RemoveExtraGroupSuite(int groupId, int suite) {
logger.debug("[LUA] Call RemoveExtraGroupSuite with {},{}",
groupId,suite);
SceneGroup group = getSceneScriptManager().getGroupById(groupId);
if (group == null || group.monsters == null) {
return 1;
}
var suiteData = group.getSuiteByIndex(suite);
if(suiteData == null){
return 1;
}
this.getSceneScriptManager().removeGroupSuite(group, suiteData);
return 0;
}
public int KillExtraGroupSuite(int groupId, int suite) {
logger.debug("[LUA] Call KillExtraGroupSuite with {},{}",
groupId,suite);
SceneGroup group = getSceneScriptManager().getGroupById(groupId);
if (group == null || group.monsters == null) {
return 1;
}
var suiteData = group.getSuiteByIndex(suite);
if(suiteData == null){
return 1;
}
this.getSceneScriptManager().removeGroupSuite(group, suiteData);
return 0;
}
// param3 (probably time limit for timed dungeons)
public int ActiveChallenge(int challengeId, int challengeIndex, int timeLimitOrGroupId, int groupId, int objectiveKills, int param5) {
logger.debug("[LUA] Call ActiveChallenge with {},{},{},{},{},{}",
challengeId,challengeIndex,timeLimitOrGroupId,groupId,objectiveKills,param5);
SceneGroup group = getSceneScriptManager().getGroupById(groupId);
var objective = objectiveKills;
var challenge = ChallengeFactory.getChallenge(
challengeId,
challengeIndex,
timeLimitOrGroupId,
groupId,
objectiveKills,
param5,
getSceneScriptManager().getScene(),
getCurrentGroup().get()
);
if(group == null){
group = getSceneScriptManager().getGroupById(timeLimitOrGroupId);
objective = groupId;
}
if (group == null || group.monsters == null) {
if(challenge == null){
return 1;
}
if(getSceneScriptManager().getScene().getChallenge() != null &&
getSceneScriptManager().getScene().getChallenge().inProgress())
{
return 0;
if(challenge instanceof DungeonChallenge dungeonChallenge){
// set if tower first stage (6-1)
dungeonChallenge.setStage(getSceneScriptManager().getVariables().getOrDefault("stage", -1) == 0);
}
DungeonChallenge challenge = new DungeonChallenge(getSceneScriptManager().getScene(),
group, challengeId, challengeIndex, objective);
// set if tower first stage (6-1)
challenge.setStage(getSceneScriptManager().getVariables().getOrDefault("stage", -1) == 0);
getSceneScriptManager().getScene().setChallenge(challenge);
challenge.start();
return 0;
}
@ -257,7 +312,7 @@ public class ScriptLib {
public int GetRegionEntityCount(LuaTable table) {
logger.debug("[LUA] Call GetRegionEntityCount with {}",
table);
printTable(table));
int regionId = table.get("region_eid").toint();
int entityType = table.get("entity_type").toint();
@ -280,9 +335,8 @@ public class ScriptLib {
// TODO record time
return 0;
}
public int GetGroupMonsterCount(int var1){
logger.debug("[LUA] Call GetGroupMonsterCount with {}",
var1);
public int GetGroupMonsterCount(){
logger.debug("[LUA] Call GetGroupMonsterCount ");
return (int) getSceneScriptManager().getScene().getEntities().values().stream()
.filter(e -> e instanceof EntityMonster &&
@ -328,7 +382,7 @@ public class ScriptLib {
var entity = getSceneScriptManager().getScene().getEntityByConfigId(configId.toint());
if(entity == null){
return 1;
return 0;
}
getSceneScriptManager().getScene().killEntity(entity, 0);
return 0;
@ -398,8 +452,13 @@ public class ScriptLib {
public int GetGadgetStateByConfigId(int groupId, int configId){
logger.debug("[LUA] Call GetGadgetStateByConfigId with {},{}",
groupId, configId);
if(groupId == 0){
groupId = getCurrentGroup().get().id;
}
final int realGroupId = groupId;
var gadget = getSceneScriptManager().getScene().getEntities().values().stream()
.filter(g -> g instanceof EntityGadget entityGadget && entityGadget.getGroupId() == groupId)
.filter(g -> g instanceof EntityGadget entityGadget && entityGadget.getGroupId() == realGroupId)
.filter(g -> g.getConfigId() == configId)
.findFirst();
if(gadget.isEmpty()){
@ -409,8 +468,8 @@ public class ScriptLib {
}
public int MarkPlayerAction(int var1, int var2, int var3, int var4){
logger.debug("[LUA] Call MarkPlayerAction with {},{}",
var1, var2);
logger.debug("[LUA] Call MarkPlayerAction with {},{},{},{}",
var1, var2,var3,var4);
return 0;
}

View File

@ -10,4 +10,5 @@ public class SceneGadget extends SceneObject{
public int state;
public int point_type;
public SceneBossChest boss_chest;
public int interact_id;
}

View File

@ -73,7 +73,7 @@ public class SceneGroup {
return bindings;
}
public SceneGroup load(int sceneId){
public synchronized SceneGroup load(int sceneId){
if(loaded){
return this;
}
@ -118,6 +118,7 @@ public class SceneGroup {
garbages = new SceneGarbage();
if (garbagesTable.checktable().get("gadgets") != LuaValue.NIL) {
garbages.gadgets = ScriptLoader.getSerializer().toList(SceneGadget.class, garbagesTable.checktable().get("gadgets").checktable());
garbages.gadgets.forEach(m -> m.group = this);
}
}

View File

@ -7,4 +7,6 @@ import lombok.ToString;
@Setter
public class SceneMonster extends SceneObject{
public int monster_id;
public int pose_id;
public int drop_id;
}

View File

@ -1,13 +1,13 @@
package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.dungeons.DungeonChallenge;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.ChallengeDataNotifyOuterClass.ChallengeDataNotify;
public class PacketChallengeDataNotify extends BasePacket {
public PacketChallengeDataNotify(DungeonChallenge challenge, int index, int value) {
public PacketChallengeDataNotify(WorldChallenge challenge, int index, int value) {
super(PacketOpcodes.ChallengeDataNotify);
ChallengeDataNotify proto = ChallengeDataNotify.newBuilder()

View File

@ -1,21 +1,20 @@
package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.dungeons.DungeonChallenge;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.DungeonChallengeBeginNotifyOuterClass.DungeonChallengeBeginNotify;
public class PacketDungeonChallengeBeginNotify extends BasePacket {
public PacketDungeonChallengeBeginNotify(DungeonChallenge challenge) {
public PacketDungeonChallengeBeginNotify(WorldChallenge challenge) {
super(PacketOpcodes.DungeonChallengeBeginNotify, true);
DungeonChallengeBeginNotify proto = DungeonChallengeBeginNotify.newBuilder()
.setChallengeId(challenge.getChallengeId())
.setChallengeIndex(challenge.getChallengeIndex())
.setGroupId(challenge.getGroup().id)
.addParamList(challenge.getObjective())
.addParamList(challenge.getTimeLimit())
.addAllParamList(challenge.getParamList())
.build();
this.setData(proto);

View File

@ -1,13 +1,13 @@
package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.dungeons.DungeonChallenge;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.DungeonChallengeFinishNotifyOuterClass.DungeonChallengeFinishNotify;
public class PacketDungeonChallengeFinishNotify extends BasePacket {
public PacketDungeonChallengeFinishNotify(DungeonChallenge challenge) {
public PacketDungeonChallengeFinishNotify(WorldChallenge challenge) {
super(PacketOpcodes.DungeonChallengeFinishNotify, true);
DungeonChallengeFinishNotify proto = DungeonChallengeFinishNotify.newBuilder()

View File

@ -1,6 +1,6 @@
package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.dungeons.DungeonChallenge;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.DungeonSettleNotifyOuterClass.DungeonSettleNotify;
@ -9,7 +9,7 @@ import emu.grasscutter.net.proto.TowerLevelEndNotifyOuterClass.TowerLevelEndNoti
public class PacketDungeonSettleNotify extends BasePacket {
public PacketDungeonSettleNotify(DungeonChallenge challenge) {
public PacketDungeonSettleNotify(WorldChallenge challenge) {
super(PacketOpcodes.DungeonSettleNotify);
DungeonSettleNotify proto = DungeonSettleNotify.newBuilder()
@ -22,10 +22,10 @@ public class PacketDungeonSettleNotify extends BasePacket {
this.setData(proto);
}
public PacketDungeonSettleNotify(DungeonChallenge challenge,
boolean canJump,
boolean hasNextLevel,
int nextFloorId
public PacketDungeonSettleNotify(WorldChallenge challenge,
boolean canJump,
boolean hasNextLevel,
int nextFloorId
) {
super(PacketOpcodes.DungeonSettleNotify);

View File

@ -1,17 +1,23 @@
package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.props.LifeState;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.LifeStateChangeNotifyOuterClass.LifeStateChangeNotify;
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
import emu.grasscutter.net.proto.ServerBuffOuterClass.ServerBuff;
import java.util.ArrayList;
public class PacketLifeStateChangeNotify extends BasePacket {
public PacketLifeStateChangeNotify(GameEntity target, LifeState lifeState) {
super(PacketOpcodes.LifeStateChangeNotify);
LifeStateChangeNotify proto = LifeStateChangeNotify.newBuilder()
.setEntityId(target.getId())
.setLifeState(lifeState.getValue())
.build();
this.setData(proto);
}
public PacketLifeStateChangeNotify(GameEntity attacker, GameEntity target, LifeState lifeState) {
super(PacketOpcodes.LifeStateChangeNotify);