Rewrite how scenes load entities to support spawning the correct rogue monsters

This commit is contained in:
Melledy
2023-10-25 08:53:16 -07:00
parent fb902c2b8a
commit c38cf693ba
9 changed files with 240 additions and 107 deletions

View File

@@ -37,6 +37,7 @@ public class GameData {
@Getter private static Int2ObjectMap<RogueAreaExcel> rogueAreaExcelMap = new Int2ObjectOpenHashMap<>(); @Getter private static Int2ObjectMap<RogueAreaExcel> rogueAreaExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<RogueManagerExcel> rogueManagerExcelMap = new Int2ObjectOpenHashMap<>(); @Getter private static Int2ObjectMap<RogueManagerExcel> rogueManagerExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<RogueRoomExcel> rogueRoomExcelMap = new Int2ObjectOpenHashMap<>(); @Getter private static Int2ObjectMap<RogueRoomExcel> rogueRoomExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<RogueMonsterExcel> rogueMonsterExcelMap = new Int2ObjectOpenHashMap<>();
private static Int2ObjectMap<AvatarPromotionExcel> avatarPromotionExcelMap = new Int2ObjectOpenHashMap<>(); private static Int2ObjectMap<AvatarPromotionExcel> avatarPromotionExcelMap = new Int2ObjectOpenHashMap<>();
private static Int2ObjectMap<AvatarSkillTreeExcel> avatarSkillTreeExcelMap = new Int2ObjectOpenHashMap<>(); private static Int2ObjectMap<AvatarSkillTreeExcel> avatarSkillTreeExcelMap = new Int2ObjectOpenHashMap<>();

View File

@@ -0,0 +1,20 @@
package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import lombok.Getter;
@Getter
@ResourceType(name = {"RogueMonster.json"})
public class RogueMonsterExcel extends GameResource {
private int RogueMonsterID;
private int NpcMonsterID;
private int EventID;
@Override
public int getId() {
return RogueMonsterID;
}
}

View File

@@ -1,10 +1,8 @@
package emu.lunarcore.data.excel; package emu.lunarcore.data.excel;
import java.util.Map;
import emu.lunarcore.data.GameResource; import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType; import emu.lunarcore.data.ResourceType;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import lombok.Getter; import lombok.Getter;
@Getter @Getter
@@ -14,11 +12,15 @@ public class RogueRoomExcel extends GameResource {
private int RogueRoomType; private int RogueRoomType;
private int MapEntrance; private int MapEntrance;
private int GroupID; private int GroupID;
private Map<Integer, Integer> GroupWithContent; private Int2IntOpenHashMap GroupWithContent;
@Override @Override
public int getId() { public int getId() {
return RogueRoomID; return RogueRoomID;
} }
public int getGroupContent(int id) {
return GroupWithContent.get(id);
}
} }

View File

@@ -1,22 +1,30 @@
package emu.lunarcore.game.enums; package emu.lunarcore.game.enums;
import emu.lunarcore.game.rogue.RogueEntityLoader;
import emu.lunarcore.game.scene.SceneEntityLoader;
import lombok.Getter; import lombok.Getter;
@Getter @Getter
public enum PlaneType { public enum PlaneType {
Unknown (0), Unknown (0),
Town (1), Town (1),
Maze (2), Maze (2),
Train (3), Train (3),
Challenge (4), Challenge (4),
Rogue (5), Rogue (5, new RogueEntityLoader()),
Raid (6), Raid (6),
AetherDivide (7), AetherDivide (7),
TrialActivity (8); TrialActivity (8);
private final int val; private final int val;
private final SceneEntityLoader sceneEntityLoader;
private PlaneType(int value) { private PlaneType(int value) {
this(value, new SceneEntityLoader());
}
private PlaneType(int value, SceneEntityLoader entityLoader) {
this.val = value; this.val = value;
this.sceneEntityLoader = entityLoader;
} }
} }

View File

@@ -58,6 +58,8 @@ public class RogueData {
public RogueRoomData getCurrentRoom() { public RogueRoomData getCurrentRoom() {
return this.rooms.get(this.getCurrentSiteId()); return this.rooms.get(this.getCurrentSiteId());
} }
// Serialization
public RogueCurrentInfo toProto() { public RogueCurrentInfo toProto() {
var proto = RogueCurrentInfo.newInstance() var proto = RogueCurrentInfo.newInstance()

View File

@@ -0,0 +1,40 @@
package emu.lunarcore.game.rogue;
import emu.lunarcore.data.GameData;
import emu.lunarcore.data.config.GroupInfo;
import emu.lunarcore.data.config.MonsterInfo;
import emu.lunarcore.data.excel.NpcMonsterExcel;
import emu.lunarcore.data.excel.RogueMonsterExcel;
import emu.lunarcore.game.scene.Scene;
import emu.lunarcore.game.scene.SceneEntityLoader;
import emu.lunarcore.game.scene.entity.EntityMonster;
public class RogueEntityLoader extends SceneEntityLoader {
public EntityMonster loadMonster(Scene scene, GroupInfo group, MonsterInfo monsterInfo) {
// Make sure player is in a rogue instance
RogueData rogue = scene.getPlayer().getRogueData();
if (rogue == null) return null;
// Get rogue group content
int content = rogue.getCurrentRoom().getExcel().getGroupContent(group.getId());
if (content <= 0) return null;
// Get rogue monster excel and npc monster excel
RogueMonsterExcel rogueMonster = GameData.getRogueMonsterExcelMap().get((content * 10) + 1);
if (rogueMonster == null) return null;
NpcMonsterExcel npcMonster = GameData.getNpcMonsterExcelMap().get(rogueMonster.getNpcMonsterID());
if (npcMonster == null) return null;
// Actually create the monster now
EntityMonster monster = new EntityMonster(scene, npcMonster, monsterInfo.getPos());
monster.getRot().set(monsterInfo.getRot());
monster.setGroupId(group.getId());
monster.setInstId(monsterInfo.getID());
monster.setEventId(rogueMonster.getEventID());
monster.setOverrideStageId(rogueMonster.getEventID());
return monster;
}
}

View File

@@ -30,6 +30,12 @@ public class RogueManager extends BasePlayerManager {
} }
public void startRogue(int areaId, RepeatedInt avatarIdList) { public void startRogue(int areaId, RepeatedInt avatarIdList) {
// Make sure player already isnt in a rogue instance
if (getPlayer().getRogueData() != null) {
getPlayer().sendPacket(new PacketStartRogueScRsp());
return;
}
// Get excel // Get excel
var excel = GameData.getRogueAreaExcelMap().get(areaId); var excel = GameData.getRogueAreaExcelMap().get(areaId);
if (excel == null || !excel.isValid()) { if (excel == null || !excel.isValid()) {
@@ -72,13 +78,13 @@ public class RogueManager extends BasePlayerManager {
getPlayer().sendPacket(new PacketStartRogueScRsp()); getPlayer().sendPacket(new PacketStartRogueScRsp());
return; return;
} }
// Load scene groups
RogueRoomExcel roomExcel = data.getCurrentRoom().getExcel();
for (var entry : roomExcel.getGroupWithContent().entrySet()) {
getPlayer().getScene().loadGroup(entry.getKey());
}
// Set rogue data
getPlayer().setRogueData(data);
// Get room excel
RogueRoomExcel roomExcel = data.getCurrentRoom().getExcel();
// Move player to rogue start position // Move player to rogue start position
AnchorInfo anchor = getPlayer().getScene().getFloorInfo().getAnchorInfo(roomExcel.getGroupID(), 1); AnchorInfo anchor = getPlayer().getScene().getFloorInfo().getAnchorInfo(roomExcel.getGroupID(), 1);
if (anchor != null) { if (anchor != null) {
@@ -86,8 +92,12 @@ public class RogueManager extends BasePlayerManager {
getPlayer().getRot().set(anchor.getRot()); getPlayer().getRot().set(anchor.getRot());
} }
// Set rogue data and send packet // Load scene groups. THIS NEEDS TO BE LAST
getPlayer().setRogueData(data); for (int key : roomExcel.getGroupWithContent().keySet()) {
getPlayer().getScene().loadGroup(key);
}
// Done
getPlayer().sendPacket(new PacketStartRogueScRsp(getPlayer())); getPlayer().sendPacket(new PacketStartRogueScRsp(getPlayer()));
} }

View File

@@ -7,12 +7,8 @@ import emu.lunarcore.data.GameData;
import emu.lunarcore.data.config.*; import emu.lunarcore.data.config.*;
import emu.lunarcore.data.config.GroupInfo.GroupLoadSide; import emu.lunarcore.data.config.GroupInfo.GroupLoadSide;
import emu.lunarcore.data.excel.MazePlaneExcel; import emu.lunarcore.data.excel.MazePlaneExcel;
import emu.lunarcore.data.excel.NpcMonsterExcel;
import emu.lunarcore.data.excel.PropExcel;
import emu.lunarcore.game.avatar.GameAvatar; import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.enums.PlaneType; import emu.lunarcore.game.enums.PlaneType;
import emu.lunarcore.game.enums.PropState;
import emu.lunarcore.game.enums.PropType;
import emu.lunarcore.game.player.PlayerLineup; import emu.lunarcore.game.player.PlayerLineup;
import emu.lunarcore.game.scene.entity.*; import emu.lunarcore.game.scene.entity.*;
import emu.lunarcore.game.scene.triggers.PropTrigger; import emu.lunarcore.game.scene.triggers.PropTrigger;
@@ -23,6 +19,7 @@ import emu.lunarcore.proto.SceneGroupStateOuterClass.SceneGroupState;
import emu.lunarcore.proto.SceneInfoOuterClass.SceneInfo; import emu.lunarcore.proto.SceneInfoOuterClass.SceneInfo;
import emu.lunarcore.server.packet.send.PacketActivateFarmElementScRsp; import emu.lunarcore.server.packet.send.PacketActivateFarmElementScRsp;
import emu.lunarcore.server.packet.send.PacketSceneGroupRefreshScNotify; import emu.lunarcore.server.packet.send.PacketSceneGroupRefreshScNotify;
import it.unimi.dsi.fastutil.ints.*; import it.unimi.dsi.fastutil.ints.*;
import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import lombok.Getter; import lombok.Getter;
@@ -32,6 +29,7 @@ public class Scene {
private final Player player; private final Player player;
private final MazePlaneExcel excel; private final MazePlaneExcel excel;
private final FloorInfo floorInfo; private final FloorInfo floorInfo;
private final SceneEntityLoader entityLoader;
private final int planeId; private final int planeId;
private final int floorId; private final int floorId;
private int entryId; private int entryId;
@@ -50,7 +48,7 @@ public class Scene {
// Cache // Cache
private List<PropTrigger> triggers; private List<PropTrigger> triggers;
private List<EntityProp> healingSprings; private List<EntityProp> healingSprings;
public Scene(Player player, MazePlaneExcel excel, int floorId) { public Scene(Player player, MazePlaneExcel excel, int floorId) {
this.player = player; this.player = player;
this.excel = excel; this.excel = excel;
@@ -65,7 +63,11 @@ public class Scene {
this.healingSprings = new ObjectArrayList<>(); this.healingSprings = new ObjectArrayList<>();
this.triggers = new ObjectArrayList<>(); this.triggers = new ObjectArrayList<>();
// Use singleton to avoid allocating memory for a new entity loader everytime we create a scene
this.entityLoader = getExcel().getPlaneType().getSceneEntityLoader();
// Add avatar entities
PlayerLineup lineup = getPlayer().getCurrentLineup(); PlayerLineup lineup = getPlayer().getCurrentLineup();
for (int avatarId : lineup.getAvatars()) { for (int avatarId : lineup.getAvatars()) {
@@ -115,97 +117,36 @@ public class Scene {
// Add monsters // Add monsters
if (group.getMonsterList() != null && group.getMonsterList().size() > 0) { if (group.getMonsterList() != null && group.getMonsterList().size() > 0) {
for (MonsterInfo monsterInfo : group.getMonsterList()) { for (MonsterInfo monsterInfo : group.getMonsterList()) {
// Don't spawn entity if they have the IsDelete flag in group info try {
if (monsterInfo.isIsDelete()) continue; EntityMonster monster = this.getEntityLoader().loadMonster(this, group, monsterInfo);
this.addEntity(monster);
// Get excels from game data } catch (Exception e) {
NpcMonsterExcel npcMonsterExcel = GameData.getNpcMonsterExcelMap().get(monsterInfo.getNPCMonsterID()); // Ignored
if (npcMonsterExcel == null) continue; }
// Create monster with excels
EntityMonster monster = new EntityMonster(this, npcMonsterExcel, monsterInfo.getPos());
monster.getRot().set(monsterInfo.getRot());
monster.setInstId(monsterInfo.getID());
monster.setEventId(monsterInfo.getEventID());
monster.setGroupId(group.getId());
monster.setWorldLevel(this.getPlayer().getWorldLevel());
// Add to monsters
this.addEntity(monster);
} }
} }
// Add props // Add props
if (group.getPropList() != null && group.getPropList().size() > 0) { if (group.getPropList() != null && group.getPropList().size() > 0) {
for (PropInfo propInfo : group.getPropList()) { for (PropInfo propInfo : group.getPropList()) {
// Don't spawn entity if they have the IsDelete flag in group info try {
if (propInfo.isIsDelete()) continue; EntityProp prop = this.getEntityLoader().loadNpc(this, group, propInfo);
this.addEntity(prop);
// Get prop excel } catch (Exception e) {
PropExcel propExcel = GameData.getPropExcelMap().get(propInfo.getPropID()); // Ignored
if (propExcel == null) {
continue;
} }
// Create prop from prop info
EntityProp prop = new EntityProp(this, propExcel, propInfo.getPos());
prop.setState(propInfo.getState());
prop.getRot().set(propInfo.getRot());
prop.setInstId(propInfo.getID());
prop.setGroupId(group.getId());
prop.setPropInfo(propInfo);
// Cache
if (prop.getPropId() == 1003) {
// Hacky fix to open simulated universe
if (propInfo.getMappingInfoID() == 2220) {
// Regular simulated universe is locked behind a mission requirement by default
prop.setState(PropState.Open);
} else {
// Skip tutorial simulated universe
continue;
}
} else if (prop.getExcel().getPropType() == PropType.PROP_SPRING) {
// Cache teleport anchors
this.getHealingSprings().add(prop);
}
// Add trigger
if (propInfo.getTrigger() != null) {
this.getTriggers().add(propInfo.getTrigger());
}
// Add to monsters
this.addEntity(prop);
} }
} }
// Add npcs // Add NPCs
if (group.getNPCList() != null && group.getNPCList().size() > 0) { if (group.getNPCList() != null && group.getNPCList().size() > 0) {
for (NpcInfo npcInfo : group.getNPCList()) { for (NpcInfo npcInfo : group.getNPCList()) {
// Don't spawn entity if they have the IsDelete flag in group info try {
if (npcInfo.isIsDelete() || !GameData.getNpcExcelMap().containsKey(npcInfo.getNPCID())) { EntityNpc npc = this.getEntityLoader().loadNpc(this, group, npcInfo);
continue; this.addEntity(npc);
} catch (Exception e) {
// Ignored
} }
// Dont spawn duplicate NPCs
boolean haseDuplicateNpcId = false;
for (GameEntity entity : this.getEntities().values()) {
if (entity instanceof EntityNpc eNpc && eNpc.getNpcId() == npcInfo.getNPCID()) {
haseDuplicateNpcId = true;
break;
}
}
if (haseDuplicateNpcId) continue;
// Create npc from npc info
EntityNpc npc = new EntityNpc(this, npcInfo.getNPCID(), npcInfo.getPos());
npc.getRot().set(npcInfo.getRot());
npc.setInstId(npcInfo.getID());
npc.setGroupId(group.getId());
// Add to monsters
this.addEntity(npc);
} }
} }
} }
@@ -325,13 +266,18 @@ public class Scene {
} }
public synchronized void addEntity(GameEntity entity, boolean sendPacket) { public synchronized void addEntity(GameEntity entity, boolean sendPacket) {
// Dont add if monster id already exists // Sanity checks - Also dont add entity if it already exists
if (entity.getEntityId() != 0) return; if (entity == null || entity.getEntityId() != 0) {
return;
}
// Set entity id and add monster to entity map // Set entity id and add monster to entity map
entity.setEntityId(this.getNextEntityId()); entity.setEntityId(this.getNextEntityId());
this.getEntities().put(entity.getEntityId(), entity); this.getEntities().put(entity.getEntityId(), entity);
// Entity add callback // Entity add callback
entity.onAdd(); entity.onAdd();
// Send packet // Send packet
if (sendPacket) { if (sendPacket) {
player.sendPacket(new PacketSceneGroupRefreshScNotify(entity, null)); player.sendPacket(new PacketSceneGroupRefreshScNotify(entity, null));
@@ -346,7 +292,8 @@ public class Scene {
GameEntity entity = this.getEntities().remove(entityId); GameEntity entity = this.getEntities().remove(entityId);
if (entity != null) { if (entity != null) {
// Entity remove callback // Reset entity id and run event
entity.setEntityId(0);
entity.onRemove(); entity.onRemove();
// Send packet // Send packet
player.sendPacket(new PacketSceneGroupRefreshScNotify(null, entity)); player.sendPacket(new PacketSceneGroupRefreshScNotify(null, entity));

View File

@@ -0,0 +1,103 @@
package emu.lunarcore.game.scene;
import emu.lunarcore.data.GameData;
import emu.lunarcore.data.config.GroupInfo;
import emu.lunarcore.data.config.MonsterInfo;
import emu.lunarcore.data.config.NpcInfo;
import emu.lunarcore.data.config.PropInfo;
import emu.lunarcore.data.excel.NpcMonsterExcel;
import emu.lunarcore.data.excel.PropExcel;
import emu.lunarcore.game.enums.PropState;
import emu.lunarcore.game.enums.PropType;
import emu.lunarcore.game.scene.entity.EntityMonster;
import emu.lunarcore.game.scene.entity.EntityNpc;
import emu.lunarcore.game.scene.entity.EntityProp;
import emu.lunarcore.game.scene.entity.GameEntity;
public class SceneEntityLoader {
public EntityMonster loadMonster(Scene scene, GroupInfo group, MonsterInfo monsterInfo) {
// Don't spawn entity if they have the IsDelete flag in group info
if (monsterInfo.isIsDelete()) return null;
// Get excels from game data
NpcMonsterExcel npcMonsterExcel = GameData.getNpcMonsterExcelMap().get(monsterInfo.getNPCMonsterID());
if (npcMonsterExcel == null) return null;
// Create monster with excels
EntityMonster monster = new EntityMonster(scene, npcMonsterExcel, monsterInfo.getPos());
monster.getRot().set(monsterInfo.getRot());
monster.setGroupId(group.getId());
monster.setInstId(monsterInfo.getID());
monster.setEventId(monsterInfo.getEventID());
monster.setWorldLevel(scene.getPlayer().getWorldLevel());
return monster;
}
public EntityProp loadNpc(Scene scene, GroupInfo group, PropInfo propInfo) {
// Don't spawn entity if they have the IsDelete flag in group info
if (propInfo.isIsDelete()) return null;
// Get prop excel
PropExcel propExcel = GameData.getPropExcelMap().get(propInfo.getPropID());
if (propExcel == null) {
return null;
}
// Create prop from prop info
EntityProp prop = new EntityProp(scene, propExcel, propInfo.getPos());
prop.setState(propInfo.getState());
prop.getRot().set(propInfo.getRot());
prop.setInstId(propInfo.getID());
prop.setGroupId(group.getId());
prop.setPropInfo(propInfo);
// Cache
if (prop.getPropId() == 1003) {
// Hacky fix to open simulated universe
if (propInfo.getMappingInfoID() == 2220) {
// Regular simulated universe is locked behind a mission requirement by default
prop.setState(PropState.Open);
} else {
// Skip tutorial simulated universe
return null;
}
} else if (prop.getExcel().getPropType() == PropType.PROP_SPRING) {
// Cache teleport anchors
scene.getHealingSprings().add(prop);
}
// Add trigger
if (propInfo.getTrigger() != null) {
scene.getTriggers().add(propInfo.getTrigger());
}
return prop;
}
public EntityNpc loadNpc(Scene scene, GroupInfo group, NpcInfo npcInfo) {
// Don't spawn entity if they have the IsDelete flag in group info
if (npcInfo.isIsDelete() || !GameData.getNpcExcelMap().containsKey(npcInfo.getNPCID())) {
return null;
}
// Dont spawn duplicate NPCs
boolean haseDuplicateNpcId = false;
for (GameEntity entity : scene.getEntities().values()) {
if (entity instanceof EntityNpc eNpc && eNpc.getNpcId() == npcInfo.getNPCID()) {
haseDuplicateNpcId = true;
break;
}
}
if (haseDuplicateNpcId) return null;
// Create npc from npc info
EntityNpc npc = new EntityNpc(scene, npcInfo.getNPCID(), npcInfo.getPos());
npc.getRot().set(npcInfo.getRot());
npc.setInstId(npcInfo.getID());
npc.setGroupId(group.getId());
return npc;
}
}