diff --git a/src/main/java/emu/lunarcore/command/commands/SpawnCommand.java b/src/main/java/emu/lunarcore/command/commands/SpawnCommand.java index bb03c3c..c25b845 100644 --- a/src/main/java/emu/lunarcore/command/commands/SpawnCommand.java +++ b/src/main/java/emu/lunarcore/command/commands/SpawnCommand.java @@ -73,10 +73,10 @@ public class SpawnCommand implements CommandHandler { EntityMonster monster = new EntityMonster(target.getScene(), monsterExcel, groupInfo, monsterInfo); monster.getPos().set(pos); monster.setEventId(monsterInfo.getEventID()); - monster.setOverrideStageId(stage); + monster.setCustomStageId(stage); if (args.getLevel() > 0) { - monster.setOverrideLevel(Math.min(args.getLevel(), 100)); + monster.setCustomLevel(Math.min(args.getLevel(), 100)); } target.getScene().addEntity(monster, true); diff --git a/src/main/java/emu/lunarcore/data/excel/StageExcel.java b/src/main/java/emu/lunarcore/data/excel/StageExcel.java index e50c48b..94fd8d7 100644 --- a/src/main/java/emu/lunarcore/data/excel/StageExcel.java +++ b/src/main/java/emu/lunarcore/data/excel/StageExcel.java @@ -34,8 +34,13 @@ public class StageExcel extends GameResource { public void onLoad() { // Cache monster list this.monsterWaves = new ArrayList<>(); + for (StageMonsterWave wave : MonsterList) { - this.monsterWaves.add(wave.toList()); + var monsterIds = wave.toList(); + + if (!monsterIds.isEmpty()) { + this.monsterWaves.add(monsterIds); + } } } diff --git a/src/main/java/emu/lunarcore/game/battle/Battle.java b/src/main/java/emu/lunarcore/game/battle/Battle.java index d74333e..9edcbc5 100644 --- a/src/main/java/emu/lunarcore/game/battle/Battle.java +++ b/src/main/java/emu/lunarcore/game/battle/Battle.java @@ -4,19 +4,17 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import emu.lunarcore.GameConstants; import emu.lunarcore.data.GameData; import emu.lunarcore.data.excel.MazeBuffExcel; import emu.lunarcore.data.excel.StageExcel; import emu.lunarcore.game.avatar.GameAvatar; -import emu.lunarcore.game.enums.StageType; import emu.lunarcore.game.inventory.GameItem; import emu.lunarcore.game.player.Player; import emu.lunarcore.game.player.lineup.PlayerLineup; import emu.lunarcore.game.scene.entity.EntityMonster; import emu.lunarcore.proto.ClientTurnSnapshotOuterClass.ClientTurnSnapshot; import emu.lunarcore.proto.SceneBattleInfoOuterClass.SceneBattleInfo; -import emu.lunarcore.proto.SceneMonsterOuterClass.SceneMonster; -import emu.lunarcore.proto.SceneMonsterWaveOuterClass.SceneMonsterWave; import emu.lunarcore.util.Utils; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; @@ -30,14 +28,14 @@ public class Battle { private final PlayerLineup lineup; private final List npcMonsters; private final List buffs; - private final List stages; + private final List waves; private final List drops; private final long timestamp; + private StageExcel stage; // Main battle stage private IntList turnSnapshotList; // TODO maybe turn it into a map? @Setter private int staminaCost; - @Setter private int levelOverride; @Setter private int roundsLimit; // Used for calculating cocoon/farm element drops @@ -51,19 +49,78 @@ public class Battle { this.lineup = lineup; this.npcMonsters = new ArrayList<>(); this.buffs = new ArrayList<>(); - this.stages = new ArrayList<>(); + this.waves = new ArrayList<>(); this.drops = new ArrayList<>(); this.timestamp = System.currentTimeMillis(); } public Battle(Player player, PlayerLineup lineup, StageExcel stage) { this(player, lineup); - this.stages.add(stage); + this.stage = stage; + this.loadStage(stage); } - public Battle(Player player, PlayerLineup lineup, Collection stages) { + public Battle(Player player, PlayerLineup lineup, List stages) { this(player, lineup); - this.stages.addAll(stages); + this.stage = stages.get(0); + + for (StageExcel stage : stages) { + this.loadStage(stage); + } + } + + public Battle(Player player, PlayerLineup lineup, Collection npcMonsters) { + this(player, lineup); + + // Parse npc monster + for (EntityMonster npcMonster : npcMonsters) { + // Add monster + this.npcMonsters.add(npcMonster); + + // Check farm element + if (npcMonster.getFarmElementId() != 0) { + this.setMappingInfoId(npcMonster.getFarmElementId()); + this.setWorldLevel(npcMonster.getWorldLevel()); + this.setStaminaCost(GameConstants.FARM_ELEMENT_STAMINA_COST); + } + + // Get stage + StageExcel stage = GameData.getStageExcelMap().get(npcMonster.getStageId()); + if (stage == null) continue; + + // Set main battle stage if we havent already + if (this.stage == null) { + this.stage = stage; + } + + // Create monster waves from stage + this.loadStage(stage, npcMonster); + } + } + + private void loadStage(StageExcel stage) { + this.loadStage(stage, null); + } + + private void loadStage(StageExcel stage, EntityMonster npcMonster) { + // Build monster waves + for (IntList stageMonsterWave : stage.getMonsterWaves()) { + // Create battle wave + var wave = new BattleMonsterWave(stage); + wave.getMonsters().addAll(stageMonsterWave); + + // Handle npc monster + if (npcMonster != null) { + // Set wave custom level + wave.setCustomLevel(npcMonster.getCustomLevel()); + + // Handle monster buffs + npcMonster.applyBuffs(this, this.getWaves().size()); + } + + // Finally add wave to battle + this.getWaves().add(wave); + } } public IntList getTurnSnapshotList() { @@ -73,38 +130,14 @@ public class Battle { return this.turnSnapshotList; } - public StageType getStageType() { - StageExcel stage = this.getFirstStage(); - if (stage != null) { - return stage.getStageType(); - } - return StageType.Unknown; - } - - public StageExcel getFirstStage() { - if (this.getStages().size() > 0) { - return this.getStages().get(0); - } else { - return null; - } - } - - public int getStageId() { - if (this.getStages().size() > 0) { - return this.getStages().get(0).getId(); - } else { - return 0; - } - } - public int getMonsterWaveCount() { - int count = 0; - - for (StageExcel stage : stages) { - count += stage.getMonsterWaves().size(); + return this.getWaves().size(); + } + + public void setCustomLevel(int level) { + for (var wave : this.getWaves()) { + wave.setCustomLevel(level); } - - return count; } // Battle buffs @@ -140,34 +173,14 @@ public class Battle { // Build battle info var proto = SceneBattleInfo.newInstance() .setBattleId(this.getId()) + .setStageId(this.getStage().getId()) .setRoundsLimit(this.getRoundsLimit()) .setLogicRandomSeed(Utils.randomRange(1, Short.MAX_VALUE)) .setWorldLevel(player.getWorldLevel()); - // Add monster waves from stages - for (StageExcel stage : stages) { - // Build monster waves - for (IntList sceneMonsterWave : stage.getMonsterWaves()) { - var wave = SceneMonsterWave.newInstance() - .setWaveId(1) // Probably not named correctly - .setStageId(stage.getId()); - - if (this.levelOverride > 0) { - wave.getMutableWaveParam().setLevel(this.levelOverride); - } - - for (int monsterId : sceneMonsterWave) { - var monster = SceneMonster.newInstance().setMonsterId(monsterId); - wave.addMonsterList(monster); - } - - proto.addMonsterWaveList(wave); - } - - // Set stage for the battle - if (proto.getStageId() == 0) { - proto.setStageId(stage.getId()); - } + // Add monster waves + for (var wave : this.getWaves()) { + proto.addMonsterWaveList(wave.toProto()); } // Avatars diff --git a/src/main/java/emu/lunarcore/game/battle/BattleMonsterWave.java b/src/main/java/emu/lunarcore/game/battle/BattleMonsterWave.java new file mode 100644 index 0000000..9bc310c --- /dev/null +++ b/src/main/java/emu/lunarcore/game/battle/BattleMonsterWave.java @@ -0,0 +1,40 @@ +package emu.lunarcore.game.battle; + +import emu.lunarcore.data.excel.StageExcel; +import emu.lunarcore.proto.SceneMonsterOuterClass.SceneMonster; +import emu.lunarcore.proto.SceneMonsterWaveOuterClass.SceneMonsterWave; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import lombok.Getter; +import lombok.Setter; + +@Getter +public class BattleMonsterWave { + private final StageExcel stage; + private IntList monsters; + + @Setter + private int customLevel; + + public BattleMonsterWave(StageExcel stage) { + this.stage = stage; + this.monsters = new IntArrayList(); + } + + public SceneMonsterWave toProto() { + var proto = SceneMonsterWave.newInstance() + .setWaveId(1) // Probably not named correctly + .setStageId(stage.getId()); + + if (this.customLevel > 0) { + proto.getMutableWaveParam().setLevel(this.customLevel); + } + + for (int monsterId : monsters) { + var monster = SceneMonster.newInstance().setMonsterId(monsterId); + proto.addMonsterList(monster); + } + + return proto; + } +} diff --git a/src/main/java/emu/lunarcore/game/battle/BattleService.java b/src/main/java/emu/lunarcore/game/battle/BattleService.java index 6715a08..073ad1d 100644 --- a/src/main/java/emu/lunarcore/game/battle/BattleService.java +++ b/src/main/java/emu/lunarcore/game/battle/BattleService.java @@ -97,48 +97,15 @@ public class BattleService extends BaseGameService { // Start battle if (monsters.size() > 0) { - // Get stages from monsters - List stages = new ArrayList<>(); + // Create battle and add npc monsters to it + Battle battle = new Battle(player, player.getLineupManager().getCurrentLineup(), monsters); - for (var monster : monsters) { - StageExcel stage = GameData.getStageExcelMap().get(monster.getStageId()); - - if (stage != null) { - stages.add(stage); - } - } - - if (stages.size() == 0) { - // An error has occurred while trying to get stage data + // Make sure we have at least one stage for the battle + if (battle.getStage() == null) { player.sendPacket(new PacketSceneCastSkillScRsp()); return; } - // Create battle and add npc monsters to it - Battle battle = new Battle(player, player.getLineupManager().getCurrentLineup(), stages); - - // Add npc monsters - for (var monster : monsters) { - // Add npc monster - battle.getNpcMonsters().add(monster); - - // Check farm element - if (monster.getFarmElementId() != 0) { - battle.setMappingInfoId(monster.getFarmElementId()); - battle.setWorldLevel(monster.getWorldLevel()); - battle.setStaminaCost(GameConstants.FARM_ELEMENT_STAMINA_COST); - } - - // Handle monster buffs - // TODO handle multiple waves properly - monster.applyBuffs(battle); - - // Override level - if (monster.getOverrideLevel() > 0) { - battle.setLevelOverride(monster.getOverrideLevel()); - } - } - // Add buffs to battle if (isPlayerCaster) { GameAvatar avatar = player.getCurrentLeaderAvatar(); @@ -270,7 +237,7 @@ public class BattleService extends BaseGameService { case BATTLE_END_QUIT -> { updateStatus = false; // Only teleport back to anchor if stage is a random fight - if (battle.getStageType().getVal() <= StageType.Maze.getVal()) { + if (battle.getStage().getStageType().getVal() <= StageType.Maze.getVal()) { teleportToAnchor = true; } } diff --git a/src/main/java/emu/lunarcore/game/challenge/ChallengeEntityLoader.java b/src/main/java/emu/lunarcore/game/challenge/ChallengeEntityLoader.java index 0c94c92..0644141 100644 --- a/src/main/java/emu/lunarcore/game/challenge/ChallengeEntityLoader.java +++ b/src/main/java/emu/lunarcore/game/challenge/ChallengeEntityLoader.java @@ -53,7 +53,7 @@ public class ChallengeEntityLoader extends SceneEntityLoader { // Create monster from group monster info EntityMonster monster = new EntityMonster(scene, npcMonsterExcel, group, monsterInfo); monster.setEventId(challengeMonsterInfo.getEventId()); - monster.setOverrideStageId(challengeMonsterInfo.getEventId()); + monster.setCustomStageId(challengeMonsterInfo.getEventId()); return monster; } diff --git a/src/main/java/emu/lunarcore/game/rogue/RogueEntityLoader.java b/src/main/java/emu/lunarcore/game/rogue/RogueEntityLoader.java index dacebed..c4cdbbe 100644 --- a/src/main/java/emu/lunarcore/game/rogue/RogueEntityLoader.java +++ b/src/main/java/emu/lunarcore/game/rogue/RogueEntityLoader.java @@ -57,7 +57,7 @@ public class RogueEntityLoader extends SceneEntityLoader { // Actually create the monster now EntityMonster monster = new EntityMonster(scene, npcMonster, group, monsterInfo); monster.setEventId(rogueMonster.getEventID()); - monster.setOverrideStageId(rogueMonster.getEventID()); + monster.setCustomStageId(rogueMonster.getEventID()); return monster; } diff --git a/src/main/java/emu/lunarcore/game/rogue/RogueInstance.java b/src/main/java/emu/lunarcore/game/rogue/RogueInstance.java index d237422..3e5325d 100644 --- a/src/main/java/emu/lunarcore/game/rogue/RogueInstance.java +++ b/src/main/java/emu/lunarcore/game/rogue/RogueInstance.java @@ -332,7 +332,7 @@ public class RogueInstance { // Set monster level for battle RogueMapExcel mapExcel = GameData.getRogueMapExcel(this.getExcel().getMapId(), this.getCurrentSiteId()); if (mapExcel != null && mapExcel.getLevelList() != null && mapExcel.getLevelList().length >= 1) { - battle.setLevelOverride(mapExcel.getLevelList()[0]); + battle.setCustomLevel(mapExcel.getLevelList()[0]); } } 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 1dbbe8f..04a4032 100644 --- a/src/main/java/emu/lunarcore/game/scene/entity/EntityMonster.java +++ b/src/main/java/emu/lunarcore/game/scene/entity/EntityMonster.java @@ -33,8 +33,8 @@ public class EntityMonster implements GameEntity, Tickable { private Int2ObjectMap buffs; private int farmElementId; - @Setter private int overrideStageId; - @Setter private int overrideLevel; + @Setter private int customStageId; + @Setter private int customLevel; public EntityMonster(Scene scene, NpcMonsterExcel excel, GroupInfo group, MonsterInfo monsterInfo) { this.scene = scene; @@ -51,10 +51,10 @@ public class EntityMonster implements GameEntity, Tickable { } public int getStageId() { - if (this.overrideStageId == 0) { + if (this.customStageId == 0) { return (this.getEventId() * 10) + worldLevel; } else { - return this.overrideStageId; + return this.customStageId; } } @@ -71,7 +71,7 @@ public class EntityMonster implements GameEntity, Tickable { return buff; } - public synchronized void applyBuffs(Battle battle) { + public synchronized void applyBuffs(Battle battle, int waveIndex) { if (this.buffs == null) return; for (var entry : this.buffs.int2ObjectEntrySet()) { @@ -80,18 +80,12 @@ public class EntityMonster implements GameEntity, Tickable { continue; } - // Dont add duplicate buffs - if (battle.hasBuff(entry.getIntKey())) { - continue; - } - // Get owner index int ownerIndex = battle.getLineup().indexOf(entry.getValue().getCasterAvatarId()); // Add buff to battle if owner exists if (ownerIndex != -1) { - // TODO handle multiple waves properly - battle.addBuff(entry.getIntKey(), ownerIndex, 1); + battle.addBuff(entry.getIntKey(), ownerIndex, 1 << waveIndex); } } } diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketReEnterLastElementStageScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketReEnterLastElementStageScRsp.java index 2533868..33b518d 100644 --- a/src/main/java/emu/lunarcore/server/packet/send/PacketReEnterLastElementStageScRsp.java +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketReEnterLastElementStageScRsp.java @@ -20,7 +20,7 @@ public class PacketReEnterLastElementStageScRsp extends BasePacket { super(CmdId.ReEnterLastElementStageScRsp); var data = ReEnterLastElementStageScRsp.newInstance() - .setStageId(battle.getStageId()) + .setStageId(battle.getStage().getId()) .setBattleInfo(battle.toProto()); this.setData(data);