Handle debuff techniques better

Using a single target debuff technique on a group of monsters should only debuff the monster wave(s) from the attacked monster
This commit is contained in:
Melledy
2023-12-15 08:11:16 -08:00
parent 3b2633f78f
commit 246b5e0340
14 changed files with 129 additions and 134 deletions

View File

@@ -62,7 +62,7 @@ public class SummonUnitInfo {
for (var task : this.OnTriggerEnter) { for (var task : this.OnTriggerEnter) {
if (task.getType().contains("AddMazeBuff")) { if (task.getType().contains("AddMazeBuff")) {
// TODO get duration from params if buff duration is dynamic // TODO get duration from params if buff duration is dynamic
var actionAddBuff = new MazeSkillAddBuff(task.getID(), 15); var actionAddBuff = new MazeSkillAddBuff(task.getID(), 5);
actionAddBuff.setSendBuffPacket(true); actionAddBuff.setSendBuffPacket(true);
actions.add(actionAddBuff); actions.add(actionAddBuff);

View File

@@ -130,10 +130,6 @@ public class Battle {
return this.turnSnapshotList; return this.turnSnapshotList;
} }
public int getMonsterWaveCount() {
return this.getWaves().size();
}
public void setCustomLevel(int level) { public void setCustomLevel(int level) {
for (var wave : this.getWaves()) { for (var wave : this.getWaves()) {
wave.setCustomLevel(level); wave.setCustomLevel(level);

View File

@@ -2,7 +2,6 @@ package emu.lunarcore.game.battle;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set;
import emu.lunarcore.GameConstants; import emu.lunarcore.GameConstants;
import emu.lunarcore.data.GameData; import emu.lunarcore.data.GameData;
@@ -24,6 +23,7 @@ import emu.lunarcore.server.packet.send.PacketReEnterLastElementStageScRsp;
import emu.lunarcore.server.packet.send.PacketSceneCastSkillScRsp; import emu.lunarcore.server.packet.send.PacketSceneCastSkillScRsp;
import emu.lunarcore.server.packet.send.PacketStartCocoonStageScRsp; import emu.lunarcore.server.packet.send.PacketStartCocoonStageScRsp;
import emu.lunarcore.server.packet.send.PacketSyncLineupNotify; import emu.lunarcore.server.packet.send.PacketSyncLineupNotify;
import it.unimi.dsi.fastutil.ints.IntSet;
public class BattleService extends BaseGameService { public class BattleService extends BaseGameService {
@@ -31,15 +31,18 @@ public class BattleService extends BaseGameService {
super(server); super(server);
} }
public void startBattle(Player player, int casterId, int attackedGroupId, MazeSkill castedSkill, Set<Integer> targets) { public void startBattle(Player player, int casterId, int attackedGroupId, MazeSkill castedSkill, IntSet hitTargets, IntSet assistMonsters) {
// Setup variables // Setup variables
List<GameEntity> targetEntities = new ArrayList<>(); List<GameEntity> targetEntities = new ArrayList<>();
boolean isPlayerCaster = player.getScene().getAvatarEntityIds().contains(casterId); GameAvatar castingAvatar = null;
// Check if attacker is the player or not // Check if attacker is the player or not
if (isPlayerCaster) { if (player.getScene().getAvatarEntityIds().contains(casterId)) {
// Player is the attacker // Get casting avatar
for (int entityId : targets) { castingAvatar = player.getCurrentLeaderAvatar();
// Player is the attacker, add hit targets to the battle
for (int entityId : hitTargets) {
GameEntity entity = player.getScene().getEntities().get(entityId); GameEntity entity = player.getScene().getEntities().get(entityId);
if (entity != null) { if (entity != null) {
@@ -53,15 +56,6 @@ public class BattleService extends BaseGameService {
if (entity != null) { if (entity != null) {
targetEntities.add(entity); targetEntities.add(entity);
} }
// Add any assisting monsters from target list
for (int entityId : targets) {
entity = player.getScene().getEntities().get(entityId);
if (entity != null) {
targetEntities.add(entity);
}
}
} }
// Skip if no attacked entities detected // Skip if no attacked entities detected
@@ -83,6 +77,8 @@ public class BattleService extends BaseGameService {
} else if (entity instanceof EntityProp prop) { } else if (entity instanceof EntityProp prop) {
it.remove(); it.remove();
player.getScene().destroyProp(prop); player.getScene().destroyProp(prop);
} else {
it.remove();
} }
} }
@@ -95,8 +91,22 @@ public class BattleService extends BaseGameService {
return; return;
} }
// Add any assisting monsters from monster assist list
for (int entityId : assistMonsters) {
GameEntity entity = player.getScene().getEntities().get(entityId);
if (entity != null && entity instanceof EntityMonster monster) {
monsters.add(monster);
}
}
// Start battle // Start battle
if (monsters.size() > 0) { if (monsters.size() > 0) {
// Maze skill attack event
if (castedSkill != null && castingAvatar != null) {
castedSkill.onAttack(castingAvatar, targetEntities);
}
// Create battle and add npc monsters to it // Create battle and add npc monsters to it
Battle battle = new Battle(player, player.getLineupManager().getCurrentLineup(), monsters); Battle battle = new Battle(player, player.getLineupManager().getCurrentLineup(), monsters);
@@ -107,23 +117,15 @@ public class BattleService extends BaseGameService {
} }
// Add buffs to battle // Add buffs to battle
if (isPlayerCaster) { if (castingAvatar != null) {
GameAvatar avatar = player.getCurrentLeaderAvatar(); // Add elemental weakness debuff to enemies
MazeBuff buff = battle.addBuff(castingAvatar.getExcel().getDamageType().getEnterBattleBuff(), battle.getLineup().getLeader());
if (avatar != null) { if (buff != null) {
// Maze skill attack event buff.addTargetIndex(battle.getLineup().getLeader());
if (castedSkill != null) { buff.addDynamicValue("SkillIndex", castedSkill.getIndex());
castedSkill.onAttack(avatar, battle);
}
// Add elemental weakness buff to enemies
MazeBuff buff = battle.addBuff(avatar.getExcel().getDamageType().getEnterBattleBuff(), battle.getLineup().getLeader());
if (buff != null) {
buff.addTargetIndex(battle.getLineup().getLeader());
buff.addDynamicValue("SkillIndex", castedSkill.getIndex());
}
} }
} else { } else {
// Ambush buff (for monsters) // Ambush debuff (from monsters)
battle.addBuff(GameConstants.BATTLE_AMBUSH_BUFF_ID, -1, 1); battle.addBuff(GameConstants.BATTLE_AMBUSH_BUFF_ID, -1, 1);
} }

View File

@@ -5,7 +5,6 @@ import java.util.List;
import emu.lunarcore.data.excel.AvatarExcel; import emu.lunarcore.data.excel.AvatarExcel;
import emu.lunarcore.game.avatar.GameAvatar; import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.battle.Battle;
import emu.lunarcore.game.scene.entity.GameEntity; import emu.lunarcore.game.scene.entity.GameEntity;
import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo; import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo;
import lombok.Getter; import lombok.Getter;
@@ -28,7 +27,9 @@ public class MazeSkill {
this.attackActions = new ArrayList<>(); this.attackActions = new ArrayList<>();
} }
// Triggered when player casts a skill /**
* Triggered when player casts a skill
*/
public void onCast(GameAvatar caster, MotionInfo castPosition) { public void onCast(GameAvatar caster, MotionInfo castPosition) {
if (this.getCastActions().size() == 0) return; if (this.getCastActions().size() == 0) return;
@@ -37,21 +38,25 @@ public class MazeSkill {
} }
} }
// Triggered when player attacks an enemy /**
public void onAttack(GameAvatar caster, Battle battle) { * Triggered when player casts a skill and it hits entities
*/
public void onCastHit(GameAvatar caster, List<? extends GameEntity> entities) {
if (this.getAttackActions().size() == 0) return; if (this.getAttackActions().size() == 0) return;
for (var action : this.getAttackActions()) { for (var action : this.getAttackActions()) {
action.onAttack(caster, battle); action.onCastHit(caster, entities);
} }
} }
// Triggered when player attacks an enemy /**
public void onAttack(GameAvatar caster, List<? extends GameEntity> entities) { * Triggered when player attacks an enemy
*/
public void onAttack(GameAvatar caster, List<? extends GameEntity> targets) {
if (this.getAttackActions().size() == 0) return; if (this.getAttackActions().size() == 0) return;
for (var action : this.getAttackActions()) { for (var action : this.getAttackActions()) {
action.onAttack(caster, entities); action.onAttack(caster, targets);
} }
} }
} }

View File

@@ -3,16 +3,21 @@ package emu.lunarcore.game.battle.skills;
import java.util.List; import java.util.List;
import emu.lunarcore.game.avatar.GameAvatar; import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.battle.Battle;
import emu.lunarcore.game.scene.entity.GameEntity; import emu.lunarcore.game.scene.entity.GameEntity;
import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo; import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo;
public abstract class MazeSkillAction { public abstract class MazeSkillAction {
public abstract void onCast(GameAvatar caster, MotionInfo castPosition); public void onCast(GameAvatar caster, MotionInfo castPosition) {
public abstract void onAttack(GameAvatar caster, Battle battle); }
public abstract void onAttack(GameAvatar caster, List<? extends GameEntity> entities); public void onCastHit(GameAvatar caster, List<? extends GameEntity> entities) {
}
public void onAttack(GameAvatar caster, List<? extends GameEntity> targets) {
}
} }

View File

@@ -3,7 +3,7 @@ package emu.lunarcore.game.battle.skills;
import java.util.List; import java.util.List;
import emu.lunarcore.game.avatar.GameAvatar; import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.battle.Battle; import emu.lunarcore.game.scene.SceneBuff;
import emu.lunarcore.game.scene.entity.EntityMonster; import emu.lunarcore.game.scene.entity.EntityMonster;
import emu.lunarcore.game.scene.entity.GameEntity; import emu.lunarcore.game.scene.entity.GameEntity;
import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo; import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo;
@@ -30,17 +30,18 @@ public class MazeSkillAddBuff extends MazeSkillAction {
} }
@Override @Override
public void onAttack(GameAvatar caster, Battle battle) { public void onAttack(GameAvatar caster, List<? extends GameEntity> targets) {
// Get amount of monster waves in battle // Add debuff to monsters
int waveCount = battle.getMonsterWaveCount(); for (GameEntity target : targets) {
// Add buff for each wave id if (target instanceof EntityMonster monster) {
for (int i = 0; i < waveCount; i++) { // Set as temp buff
battle.addBuff(buffId, battle.getLineup().getLeader(), 1 << i); monster.setTempBuff(new SceneBuff(caster.getAvatarId(), buffId));
}
} }
} }
@Override @Override
public void onAttack(GameAvatar caster, List<? extends GameEntity> entities) { public void onCastHit(GameAvatar caster, List<? extends GameEntity> entities) {
for (GameEntity entity : entities) { for (GameEntity entity : entities) {
if (entity instanceof EntityMonster monster) { if (entity instanceof EntityMonster monster) {
// Add buff to monster // Add buff to monster

View File

@@ -3,27 +3,15 @@ package emu.lunarcore.game.battle.skills;
import java.util.List; import java.util.List;
import emu.lunarcore.game.avatar.GameAvatar; import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.battle.Battle;
import emu.lunarcore.game.scene.entity.EntityProp; import emu.lunarcore.game.scene.entity.EntityProp;
import emu.lunarcore.game.scene.entity.GameEntity; import emu.lunarcore.game.scene.entity.GameEntity;
import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo;
import lombok.Getter; import lombok.Getter;
@Getter @Getter
public class MazeSkillHitProp extends MazeSkillAction { public class MazeSkillHitProp extends MazeSkillAction {
@Override @Override
public void onCast(GameAvatar caster, MotionInfo castPosition) { public void onCastHit(GameAvatar caster, List<? extends GameEntity> entities) {
// Skip
}
@Override
public void onAttack(GameAvatar caster, Battle battle) {
// Skip
}
@Override
public void onAttack(GameAvatar caster, List<? extends GameEntity> entities) {
for (GameEntity entity : entities) { for (GameEntity entity : entities) {
if (entity instanceof EntityProp prop) { if (entity instanceof EntityProp prop) {
caster.getScene().destroyProp(prop); caster.getScene().destroyProp(prop);

View File

@@ -1,10 +1,6 @@
package emu.lunarcore.game.battle.skills; package emu.lunarcore.game.battle.skills;
import java.util.List;
import emu.lunarcore.game.avatar.GameAvatar; import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.battle.Battle;
import emu.lunarcore.game.scene.entity.GameEntity;
import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo; import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo;
public class MazeSkillModifyHP extends MazeSkillAction { public class MazeSkillModifyHP extends MazeSkillAction {
@@ -19,14 +15,4 @@ public class MazeSkillModifyHP extends MazeSkillAction {
caster.getOwner().getCurrentLineup().heal(this.amount, false); caster.getOwner().getCurrentLineup().heal(this.amount, false);
} }
@Override
public void onAttack(GameAvatar caster, Battle battle) {
}
@Override
public void onAttack(GameAvatar caster, List<? extends GameEntity> entities) {
}
} }

View File

@@ -3,7 +3,6 @@ package emu.lunarcore.game.battle.skills;
import java.util.List; import java.util.List;
import emu.lunarcore.game.avatar.GameAvatar; import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.battle.Battle;
import emu.lunarcore.game.scene.entity.GameEntity; import emu.lunarcore.game.scene.entity.GameEntity;
import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo; import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo;
@@ -23,12 +22,7 @@ public class MazeSkillModifySP extends MazeSkillAction {
} }
@Override @Override
public void onAttack(GameAvatar caster, Battle battle) { public void onCastHit(GameAvatar caster, List<? extends GameEntity> entities) {
}
@Override
public void onAttack(GameAvatar caster, List<? extends GameEntity> entities) {
} }

View File

@@ -1,11 +1,7 @@
package emu.lunarcore.game.battle.skills; package emu.lunarcore.game.battle.skills;
import java.util.List;
import emu.lunarcore.data.excel.SummonUnitExcel; import emu.lunarcore.data.excel.SummonUnitExcel;
import emu.lunarcore.game.avatar.GameAvatar; import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.battle.Battle;
import emu.lunarcore.game.scene.entity.GameEntity;
import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo; import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo;
import emu.lunarcore.util.Position; import emu.lunarcore.util.Position;
import lombok.Getter; import lombok.Getter;
@@ -25,14 +21,4 @@ public class MazeSkillSummonUnit extends MazeSkillAction {
caster.getScene().summonUnit(caster, excel, new Position(castPosition.getPos()), new Position(castPosition.getRot()), duration); caster.getScene().summonUnit(caster, excel, new Position(castPosition.getPos()), new Position(castPosition.getRot()), duration);
} }
@Override
public void onAttack(GameAvatar caster, Battle battle) {
// Skip
}
@Override
public void onAttack(GameAvatar caster, List<? extends GameEntity> entities) {
// Skip
}
} }

View File

@@ -261,7 +261,7 @@ public class Scene implements Tickable {
// Handle task actions // Handle task actions
for (var action : trigger.getActions()) { for (var action : trigger.getActions()) {
action.onAttack(summonUnit.getCaster(), targets); action.onCastHit(summonUnit.getCaster(), targets);
} }
// Send packet // Send packet

View File

@@ -8,17 +8,28 @@ public class SceneBuff {
private int casterAvatarId; // Owner avatar id private int casterAvatarId; // Owner avatar id
private int buffId; private int buffId;
private int buffLevel; private int buffLevel;
private int duration; private float duration;
private long createTime; private long createTime;
private long expiry; private long expiry;
public SceneBuff(int casterAvatarId, int buffId, int seconds) { public SceneBuff(int buffId) {
this.casterAvatarId = casterAvatarId;
this.buffId = buffId; this.buffId = buffId;
this.buffLevel = 1; this.buffLevel = 1;
this.createTime = System.currentTimeMillis(); this.createTime = System.currentTimeMillis();
this.duration = -1;
}
public SceneBuff(int casterAvatarId, int buffId) {
this(buffId);
this.casterAvatarId = casterAvatarId;
this.expiry = Long.MAX_VALUE;
}
public SceneBuff(int casterAvatarId, int buffId, int seconds) {
this(buffId);
this.casterAvatarId = casterAvatarId;
this.duration = seconds * 1000; this.duration = seconds * 1000;
this.expiry = this.createTime + duration; this.expiry = this.createTime + (long) duration;
} }
public boolean isExpired(long timestamp) { public boolean isExpired(long timestamp) {
@@ -33,7 +44,7 @@ public class SceneBuff {
.setLevel(this.getBuffLevel()) .setLevel(this.getBuffLevel())
.setBaseAvatarId(this.getCasterAvatarId()) .setBaseAvatarId(this.getCasterAvatarId())
.setAddTimeMs(this.getCreateTime()) .setAddTimeMs(this.getCreateTime())
.setLifeTime(this.getDuration() / 10) .setLifeTime(this.getDuration())
.setCount(1); .setCount(1);
return proto; return proto;

View File

@@ -32,6 +32,8 @@ public class EntityMonster implements GameEntity, Tickable {
private final Position rot; private final Position rot;
private Int2ObjectMap<SceneBuff> buffs; private Int2ObjectMap<SceneBuff> buffs;
@Setter private SceneBuff tempBuff;
private int farmElementId; private int farmElementId;
@Setter private int customStageId; @Setter private int customStageId;
@Setter private int customLevel; @Setter private int customLevel;
@@ -72,22 +74,36 @@ public class EntityMonster implements GameEntity, Tickable {
} }
public synchronized void applyBuffs(Battle battle, int waveIndex) { public synchronized void applyBuffs(Battle battle, int waveIndex) {
if (this.buffs == null) return; if (this.buffs != null) {
for (var entry : this.buffs.int2ObjectEntrySet()) {
// Check expiry for buff
if (entry.getValue().isExpired(battle.getTimestamp())) {
continue;
}
for (var entry : this.buffs.int2ObjectEntrySet()) { // Add buff to battle
// Check expiry for buff this.applyBuff(battle, entry.getValue(), waveIndex);
if (entry.getValue().isExpired(battle.getTimestamp())) {
continue;
}
// Get owner index
int ownerIndex = battle.getLineup().indexOf(entry.getValue().getCasterAvatarId());
// Add buff to battle if owner exists
if (ownerIndex != -1) {
battle.addBuff(entry.getIntKey(), ownerIndex, 1 << waveIndex);
} }
} }
if (this.getTempBuff() != null) {
this.applyBuff(battle, this.getTempBuff(), waveIndex);
this.tempBuff = null;
}
}
private boolean applyBuff(Battle battle, SceneBuff buff, int waveIndex) {
// Get index of owner in lineup
int ownerIndex = battle.getLineup().indexOf(buff.getCasterAvatarId());
// Add buff to battle if owner exists
if (ownerIndex != -1) {
battle.addBuff(buff.getBuffId(), ownerIndex, 1 << waveIndex);
return true;
}
// Failure
return false;
} }
@Override @Override

View File

@@ -1,8 +1,5 @@
package emu.lunarcore.server.packet.recv; package emu.lunarcore.server.packet.recv;
import java.util.LinkedHashSet;
import java.util.Set;
import emu.lunarcore.game.avatar.GameAvatar; import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.battle.skills.MazeSkill; import emu.lunarcore.game.battle.skills.MazeSkill;
import emu.lunarcore.game.player.Player; import emu.lunarcore.game.player.Player;
@@ -13,6 +10,8 @@ import emu.lunarcore.server.packet.Opcodes;
import emu.lunarcore.server.packet.PacketHandler; import emu.lunarcore.server.packet.PacketHandler;
import emu.lunarcore.server.packet.send.PacketSceneCastSkillMpUpdateScNotify; import emu.lunarcore.server.packet.send.PacketSceneCastSkillMpUpdateScNotify;
import emu.lunarcore.server.packet.send.PacketSceneCastSkillScRsp; import emu.lunarcore.server.packet.send.PacketSceneCastSkillScRsp;
import it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
@Opcodes(CmdId.SceneCastSkillCsReq) @Opcodes(CmdId.SceneCastSkillCsReq)
public class HandlerSceneCastSkillCsReq extends PacketHandler { public class HandlerSceneCastSkillCsReq extends PacketHandler {
@@ -52,13 +51,19 @@ public class HandlerSceneCastSkillCsReq extends PacketHandler {
} }
if (req.hasHitTargetIdList()) { if (req.hasHitTargetIdList()) {
// Create target list // Parse targets efficiently (skips integer boxing)
Set<Integer> targets = new LinkedHashSet<>(); IntSet hitTargets = new IntLinkedOpenHashSet();
req.getHitTargetIdList().forEach(targets::add); for (int i = 0; i < req.getHitTargetIdList().length(); i++) {
req.getAssistMonsterIdList().forEach(targets::add); hitTargets.add(req.getHitTargetIdList().get(i));
}
IntSet assistMonsters = new IntLinkedOpenHashSet();
for (int i = 0; i < req.getAssistMonsterIdList().length(); i++) {
assistMonsters.add(req.getAssistMonsterIdList().get(i));
}
// Start battle // Start battle
session.getServer().getBattleService().startBattle(player, req.getCasterId(), req.getAttackedGroupId(), skill, targets); session.getServer().getBattleService().startBattle(player, req.getCasterId(), req.getAttackedGroupId(), skill, hitTargets, assistMonsters);
} else { } else {
// We had no targets for some reason // We had no targets for some reason
session.send(new PacketSceneCastSkillScRsp(req.getAttackedGroupId())); session.send(new PacketSceneCastSkillScRsp(req.getAttackedGroupId()));