diff --git a/src/main/java/emu/lunarcore/data/config/FloorInfo.java b/src/main/java/emu/lunarcore/data/config/FloorInfo.java index 7b043db..28ab84b 100644 --- a/src/main/java/emu/lunarcore/data/config/FloorInfo.java +++ b/src/main/java/emu/lunarcore/data/config/FloorInfo.java @@ -6,6 +6,7 @@ import java.util.List; import com.google.gson.annotations.SerializedName; import emu.lunarcore.game.enums.PropState; +import emu.lunarcore.game.scene.triggers.TriggerOpenTreasureWhenMonsterDie; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; @@ -61,6 +62,15 @@ public class FloorInfo { // Force prop to be in the unlocked state prop.setState(PropState.CheckPointEnable); + } else if (prop.getInitLevelGraph() != null) { + String json = prop.getInitLevelGraph(); + + // Hacky way to setup prop triggers + if (json.contains("Maze_GroupProp_OpenTreasure_WhenMonsterDie")) { + prop.setTrigger(new TriggerOpenTreasureWhenMonsterDie(group.getId())); + } + + prop.setInitLevelGraph(null); } } } diff --git a/src/main/java/emu/lunarcore/data/config/PropInfo.java b/src/main/java/emu/lunarcore/data/config/PropInfo.java index f0b2826..0a7cf44 100644 --- a/src/main/java/emu/lunarcore/data/config/PropInfo.java +++ b/src/main/java/emu/lunarcore/data/config/PropInfo.java @@ -1,6 +1,7 @@ package emu.lunarcore.data.config; import emu.lunarcore.game.enums.PropState; +import emu.lunarcore.game.scene.triggers.PropTrigger; import lombok.Getter; import lombok.Setter; @@ -15,7 +16,10 @@ public class PropInfo extends ObjectInfo { private int EventID; private int CocoonID; private int FarmElementID; + private int ChestID; - @Setter - private PropState State = PropState.Closed; + @Setter private String InitLevelGraph; + @Setter private PropState State = PropState.Closed; + + @Setter private transient PropTrigger trigger; } diff --git a/src/main/java/emu/lunarcore/game/avatar/GameAvatar.java b/src/main/java/emu/lunarcore/game/avatar/GameAvatar.java index ea8524c..2c4419d 100644 --- a/src/main/java/emu/lunarcore/game/avatar/GameAvatar.java +++ b/src/main/java/emu/lunarcore/game/avatar/GameAvatar.java @@ -16,6 +16,7 @@ import emu.lunarcore.data.excel.AvatarExcel; import emu.lunarcore.game.inventory.GameItem; import emu.lunarcore.game.inventory.ItemMainType; import emu.lunarcore.game.player.Player; +import emu.lunarcore.game.scene.Scene; import emu.lunarcore.game.scene.entity.GameEntity; import emu.lunarcore.proto.AvatarOuterClass.Avatar; import emu.lunarcore.proto.AvatarSkillTreeOuterClass.AvatarSkillTree; @@ -86,6 +87,11 @@ public class GameAvatar implements GameEntity { this.setHeroPath(path); } + @Override + public Scene getScene() { + return this.getOwner().getScene(); + } + public void setExcel(AvatarExcel excel) { if (this.excel == null) { this.excel = excel; diff --git a/src/main/java/emu/lunarcore/game/scene/Scene.java b/src/main/java/emu/lunarcore/game/scene/Scene.java index fbb7863..e07e0ae 100644 --- a/src/main/java/emu/lunarcore/game/scene/Scene.java +++ b/src/main/java/emu/lunarcore/game/scene/Scene.java @@ -14,6 +14,8 @@ import emu.lunarcore.game.enums.PropState; import emu.lunarcore.game.enums.PropType; import emu.lunarcore.game.player.PlayerLineup; import emu.lunarcore.game.scene.entity.*; +import emu.lunarcore.game.scene.triggers.PropTrigger; +import emu.lunarcore.game.scene.triggers.PropTriggerType; import emu.lunarcore.game.player.Player; import emu.lunarcore.proto.SceneEntityGroupInfoOuterClass.SceneEntityGroupInfo; import emu.lunarcore.proto.SceneInfoOuterClass.SceneInfo; @@ -24,6 +26,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; import lombok.Getter; @Getter @@ -36,6 +39,7 @@ public class Scene { private int entryId; private int lastEntityId = 0; + private boolean loaded = false; // Avatar entites private IntSet avatarEntityIds; @@ -45,6 +49,7 @@ public class Scene { private Int2ObjectMap entities; // Cache + private List triggers; private List healingSprings; public Scene(Player player, MazePlaneExcel excel, int floorId) { @@ -57,7 +62,8 @@ public class Scene { this.avatarEntityIds = new IntOpenHashSet(); this.avatars = new Int2ObjectOpenHashMap<>(); this.entities = new Int2ObjectOpenHashMap<>(); - this.healingSprings = new ArrayList<>(); + this.healingSprings = new ObjectArrayList<>(); + this.triggers = new ObjectArrayList<>(); PlayerLineup lineup = getPlayer().getCurrentLineup(); @@ -90,7 +96,7 @@ public class Scene { if (npcMonsterExcel == null) continue; // Create monster with excels - EntityMonster monster = new EntityMonster(npcMonsterExcel, monsterInfo.clonePos()); + EntityMonster monster = new EntityMonster(this, npcMonsterExcel, monsterInfo.clonePos()); monster.getRot().setY((int) (monsterInfo.getRotY() * 1000f)); monster.setInstId(monsterInfo.getID()); monster.setEventId(monsterInfo.getEventID()); @@ -112,7 +118,7 @@ public class Scene { } // Create prop from prop info - EntityProp prop = new EntityProp(propExcel, propInfo.clonePos()); + EntityProp prop = new EntityProp(this, propExcel, propInfo.clonePos()); prop.setState(propInfo.getState()); prop.getRot().set( (int) (propInfo.getRotX() * 1000f), @@ -127,10 +133,15 @@ public class Scene { // Open simulated universe prop.setState(PropState.Open); } else if (prop.getExcel().getPropType() == PropType.PROP_SPRING) { - // Teleport anchors cache + // Cache teleport anchors this.getHealingSprings().add(prop); } + // Add trigger + if (propInfo.getTrigger() != null) { + this.getTriggers().add(propInfo.getTrigger()); + } + // Add to monsters this.addEntity(prop); } @@ -155,7 +166,7 @@ public class Scene { if (haseDuplicateNpcId) continue; // Create npc from npc info - EntityNpc npc = new EntityNpc(npcInfo.getNPCID(), npcInfo.clonePos()); + EntityNpc npc = new EntityNpc(this, npcInfo.getNPCID(), npcInfo.clonePos()); npc.getRot().setY((int) (npcInfo.getRotY() * 1000f)); npc.setInstId(npcInfo.getID()); npc.setGroupId(group.getId()); @@ -165,6 +176,9 @@ public class Scene { } } } + + // Done + this.loaded = true; } public void setEntryId(int entryId) { @@ -175,8 +189,8 @@ public class Scene { return ++lastEntityId; } - public GameEntity getEntityById(int id) { - return this.entities.get(id); + public synchronized GameEntity getEntityById(int id) { + return this.getEntities().get(id); } public void syncLineup() { @@ -237,15 +251,24 @@ public class Scene { player.sendPacket(new PacketActivateFarmElementScRsp(entityId, worldLevel)); return true; } + + // TODO + public void fireTrigger(PropTriggerType type, int param) { + for (PropTrigger trigger : this.getTriggers()) { + if (trigger.shouldRun(param)) { + trigger.run(this); + } + } + } public synchronized void addEntity(GameEntity entity) { // Dont add if monster id already exists if (entity.getEntityId() != 0) return; // Set entity id and add monster to entity map entity.setEntityId(this.getNextEntityId()); - this.entities.put(entity.getEntityId(), entity); + this.getEntities().put(entity.getEntityId(), entity); // Entity add callback - entity.onAdd(this); + entity.onAdd(); } public synchronized void removeEntity(GameEntity entity) { @@ -253,17 +276,17 @@ public class Scene { } public synchronized void removeEntity(int entityId) { - GameEntity entity = this.entities.remove(entityId); + GameEntity entity = this.getEntities().remove(entityId); if (entity != null) { // Entity remove callback - entity.onRemove(this); + entity.onRemove(); // Send packet player.sendPacket(new PacketSceneGroupRefreshScNotify(null, entity)); } } - public SceneInfo toProto() { + public synchronized SceneInfo toProto() { // Proto var proto = SceneInfo.newInstance() .setWorldId(this.getExcel().getWorldID()) @@ -293,7 +316,7 @@ public class Scene { groups.put(0, playerGroup); // Add rest of the entities to groups - for (var entity : entities.values()) { + for (var entity : getEntities().values()) { var group = groups.computeIfAbsent(entity.getGroupId(), i -> SceneEntityGroupInfo.newInstance().setGroupId(i)); group.addEntityList(entity.toSceneEntityProto()); } diff --git a/src/main/java/emu/lunarcore/game/scene/entity/EntityMonster.java b/src/main/java/emu/lunarcore/game/scene/entity/EntityMonster.java index 83e803b..f4d4650 100644 --- a/src/main/java/emu/lunarcore/game/scene/entity/EntityMonster.java +++ b/src/main/java/emu/lunarcore/game/scene/entity/EntityMonster.java @@ -2,6 +2,8 @@ package emu.lunarcore.game.scene.entity; import emu.lunarcore.data.excel.NpcMonsterExcel; import emu.lunarcore.data.excel.StageExcel; +import emu.lunarcore.game.scene.Scene; +import emu.lunarcore.game.scene.triggers.PropTriggerType; import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo; import emu.lunarcore.proto.SceneEntityInfoOuterClass.SceneEntityInfo; import emu.lunarcore.proto.SceneNpcMonsterInfoOuterClass.SceneNpcMonsterInfo; @@ -19,11 +21,13 @@ public class EntityMonster implements GameEntity { @Setter private int eventId; @Setter private int overrideStageId; - private NpcMonsterExcel excel; - private Position pos; - private Position rot; + private final Scene scene; + private final NpcMonsterExcel excel; + private final Position pos; + private final Position rot; - public EntityMonster(NpcMonsterExcel excel, Position pos) { + public EntityMonster(Scene scene, NpcMonsterExcel excel, Position pos) { + this.scene = scene; this.excel = excel; this.pos = pos; this.rot = new Position(); @@ -36,6 +40,12 @@ public class EntityMonster implements GameEntity { return this.overrideStageId; } } + + @Override + public void onRemove() { + // Try to fire any triggers + getScene().fireTrigger(PropTriggerType.MONSTER_DIE, this.getGroupId()); + } @Override public SceneEntityInfo toSceneEntityProto() { diff --git a/src/main/java/emu/lunarcore/game/scene/entity/EntityNpc.java b/src/main/java/emu/lunarcore/game/scene/entity/EntityNpc.java index 9bd19d7..c2ead93 100644 --- a/src/main/java/emu/lunarcore/game/scene/entity/EntityNpc.java +++ b/src/main/java/emu/lunarcore/game/scene/entity/EntityNpc.java @@ -1,5 +1,6 @@ package emu.lunarcore.game.scene.entity; +import emu.lunarcore.game.scene.Scene; import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo; import emu.lunarcore.proto.SceneEntityInfoOuterClass.SceneEntityInfo; import emu.lunarcore.proto.SceneNpcInfoOuterClass.SceneNpcInfo; @@ -14,10 +15,12 @@ public class EntityNpc implements GameEntity { @Setter private int instId; @Setter private int npcId; - private Position pos; - private Position rot; + private final Scene scene; + private final Position pos; + private final Position rot; - public EntityNpc(int npcId, Position pos) { + public EntityNpc(Scene scene, int npcId, Position pos) { + this.scene = scene; this.npcId = npcId; this.pos = pos; this.rot = new Position(); diff --git a/src/main/java/emu/lunarcore/game/scene/entity/EntityProp.java b/src/main/java/emu/lunarcore/game/scene/entity/EntityProp.java index b504273..cdbc861 100644 --- a/src/main/java/emu/lunarcore/game/scene/entity/EntityProp.java +++ b/src/main/java/emu/lunarcore/game/scene/entity/EntityProp.java @@ -6,6 +6,7 @@ import emu.lunarcore.game.scene.Scene; import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo; import emu.lunarcore.proto.SceneEntityInfoOuterClass.SceneEntityInfo; import emu.lunarcore.proto.ScenePropInfoOuterClass.ScenePropInfo; +import emu.lunarcore.server.packet.send.PacketSceneGroupRefreshScNotify; import emu.lunarcore.util.Position; import lombok.Getter; import lombok.Setter; @@ -15,13 +16,15 @@ public class EntityProp implements GameEntity { @Setter private int entityId; @Setter private int groupId; @Setter private int instId; - @Setter private PropState state; + private PropState state; - private PropExcel excel; - private Position pos; - private Position rot; + private final Scene scene; + private final PropExcel excel; + private final Position pos; + private final Position rot; - public EntityProp(PropExcel excel, Position pos) { + public EntityProp(Scene scene, PropExcel excel, Position pos) { + this.scene = scene; this.excel = excel; this.pos = pos; this.rot = new Position(); @@ -32,8 +35,17 @@ public class EntityProp implements GameEntity { return excel.getId(); } + public void setState(PropState state) { + // Set state + this.state = state; + // Sync state update to client + if (this.getScene().isLoaded()) { + this.getScene().getPlayer().sendPacket(new PacketSceneGroupRefreshScNotify(this, null)); + } + } + @Override - public void onRemove(Scene scene) { + public void onRemove() { if (excel.isRecoverMp()) { scene.getPlayer().getLineupManager().addMp(2); } else if (excel.isRecoverHp()) { diff --git a/src/main/java/emu/lunarcore/game/scene/entity/GameEntity.java b/src/main/java/emu/lunarcore/game/scene/entity/GameEntity.java index 1acf4ab..a23732f 100644 --- a/src/main/java/emu/lunarcore/game/scene/entity/GameEntity.java +++ b/src/main/java/emu/lunarcore/game/scene/entity/GameEntity.java @@ -4,10 +4,12 @@ import emu.lunarcore.game.scene.Scene; import emu.lunarcore.proto.SceneEntityInfoOuterClass.SceneEntityInfo; public interface GameEntity { - + public int getEntityId(); public void setEntityId(int id); + + public Scene getScene(); public default int getGroupId() { return 0; @@ -17,11 +19,11 @@ public interface GameEntity { return 0; } - public default void onAdd(Scene scene) { + public default void onAdd() { } - public default void onRemove(Scene scene) { + public default void onRemove() { } diff --git a/src/main/java/emu/lunarcore/game/scene/triggers/PropTrigger.java b/src/main/java/emu/lunarcore/game/scene/triggers/PropTrigger.java new file mode 100644 index 0000000..48967b4 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/scene/triggers/PropTrigger.java @@ -0,0 +1,13 @@ +package emu.lunarcore.game.scene.triggers; + +import emu.lunarcore.game.scene.Scene; + +public abstract class PropTrigger { + + public abstract PropTriggerType getType(); + + public abstract boolean shouldRun(int param); + + public abstract void run(Scene scene); + +} \ No newline at end of file diff --git a/src/main/java/emu/lunarcore/game/scene/triggers/PropTriggerType.java b/src/main/java/emu/lunarcore/game/scene/triggers/PropTriggerType.java new file mode 100644 index 0000000..3514a59 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/scene/triggers/PropTriggerType.java @@ -0,0 +1,5 @@ +package emu.lunarcore.game.scene.triggers; + +public enum PropTriggerType { + MONSTER_DIE; +} diff --git a/src/main/java/emu/lunarcore/game/scene/triggers/TriggerOpenTreasureWhenMonsterDie.java b/src/main/java/emu/lunarcore/game/scene/triggers/TriggerOpenTreasureWhenMonsterDie.java new file mode 100644 index 0000000..6bb4a30 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/scene/triggers/TriggerOpenTreasureWhenMonsterDie.java @@ -0,0 +1,44 @@ +package emu.lunarcore.game.scene.triggers; + +import emu.lunarcore.game.enums.PropState; +import emu.lunarcore.game.enums.PropType; +import emu.lunarcore.game.scene.Scene; +import emu.lunarcore.game.scene.entity.EntityProp; +import emu.lunarcore.game.scene.entity.GameEntity; +import lombok.Getter; + +@Getter +public class TriggerOpenTreasureWhenMonsterDie extends PropTrigger { + private int groupId; + + public TriggerOpenTreasureWhenMonsterDie(int groupId) { + this.groupId = groupId; + } + + @Override + public PropTriggerType getType() { + return PropTriggerType.MONSTER_DIE; + } + + @Override + public boolean shouldRun(int param) { + return this.groupId == param; + } + + @Override + public void run(Scene scene) { + synchronized (scene) { + for (GameEntity entity : scene.getEntities().values()) { + if (entity.getGroupId() != this.groupId) { + continue; + } + + if (entity instanceof EntityProp prop) { + if (prop.getExcel().getPropType() == PropType.PROP_TREASURE_CHEST) { + prop.setState(PropState.ChestClosed); + } + } + } + } + } +}