Rework battle system

This commit is contained in:
Melledy
2023-12-14 09:46:57 -08:00
parent d9785289dd
commit 912ebfe8cf
10 changed files with 139 additions and 120 deletions

View File

@@ -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);

View File

@@ -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);
}
}
}

View File

@@ -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<EntityMonster> npcMonsters;
private final List<MazeBuff> buffs;
private final List<StageExcel> stages;
private final List<BattleMonsterWave> waves;
private final List<GameItem> 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<StageExcel> stages) {
public Battle(Player player, PlayerLineup lineup, List<StageExcel> 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<EntityMonster> 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

View File

@@ -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;
}
}

View File

@@ -97,48 +97,15 @@ public class BattleService extends BaseGameService {
// Start battle
if (monsters.size() > 0) {
// Get stages from monsters
List<StageExcel> 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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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]);
}
}

View File

@@ -33,8 +33,8 @@ public class EntityMonster implements GameEntity, Tickable {
private Int2ObjectMap<SceneBuff> 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);
}
}
}

View File

@@ -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);