From 8aecc9dbce4d331a24c6d25dc6a7610a4a813ba1 Mon Sep 17 00:00:00 2001 From: liang799 <71926348+liang799@users.noreply.github.com> Date: Wed, 22 Apr 2026 17:49:41 +0800 Subject: [PATCH 1/2] refactor: replace marker battle events with typed dispatch --- .../sc2002/turnbased/report/ActionEvent.java | 63 +++++------- .../sc2002/turnbased/report/BattleEvent.java | 15 +++ .../turnbased/report/CombatantSummary.java | 47 ++++----- .../turnbased/report/NarrationEvent.java | 13 ++- .../turnbased/report/RoundStartEvent.java | 9 +- .../turnbased/report/RoundSummaryEvent.java | 44 ++++----- .../turnbased/report/SkippedTurnEvent.java | 27 +++--- .../report/StatusEffectReportEvent.java | 5 + .../turnbased/ui/BattleConsoleFormatter.java | 50 ++++++---- .../gui/playback/BattleDialogueFormatter.java | 95 ++++++++++++------- .../ui/gui/view/ArenaSceneModel.java | 66 +++++++++---- .../turnbased/domain/EnemyCombatantTest.java | 4 + 12 files changed, 253 insertions(+), 185 deletions(-) diff --git a/src/main/java/sc2002/turnbased/report/ActionEvent.java b/src/main/java/sc2002/turnbased/report/ActionEvent.java index 19f123c..8f10f3c 100644 --- a/src/main/java/sc2002/turnbased/report/ActionEvent.java +++ b/src/main/java/sc2002/turnbased/report/ActionEvent.java @@ -7,46 +7,24 @@ import sc2002.turnbased.domain.Combatant; import sc2002.turnbased.domain.CombatantId; -public class ActionEvent implements BattleEvent { - private final CombatantId actorId; - private final String actorName; - private final String actionName; - private final CombatantId targetId; - private final String targetName; - private final int hpBefore; - private final int hpAfter; - private final int attackerAttack; - private final int targetDefense; - private final int damage; - private final boolean targetEliminated; - private final List statusEffectNotes; - - public ActionEvent( - CombatantId actorId, - String actorName, - String actionName, - CombatantId targetId, - String targetName, - int hpBefore, - int hpAfter, - int attackerAttack, - int targetDefense, - int damage, - boolean targetEliminated, - List statusEffectNotes - ) { - this.actorId = Objects.requireNonNull(actorId, "actorId"); - this.actorName = actorName; - this.actionName = actionName; - this.targetId = Objects.requireNonNull(targetId, "targetId"); - this.targetName = targetName; - this.hpBefore = hpBefore; - this.hpAfter = hpAfter; - this.attackerAttack = attackerAttack; - this.targetDefense = targetDefense; - this.damage = damage; - this.targetEliminated = targetEliminated; - this.statusEffectNotes = List.copyOf(Objects.requireNonNull(statusEffectNotes, "statusEffectNotes")); +public record ActionEvent( + CombatantId actorId, + String actorName, + String actionName, + CombatantId targetId, + String targetName, + int hpBefore, + int hpAfter, + int attackerAttack, + int targetDefense, + int damage, + boolean targetEliminated, + List statusEffectNotes +) implements BattleEvent { + public ActionEvent { + actorId = Objects.requireNonNull(actorId, "actorId"); + targetId = Objects.requireNonNull(targetId, "targetId"); + statusEffectNotes = List.copyOf(Objects.requireNonNull(statusEffectNotes, "statusEffectNotes")); } public ActionEvent(Combatant actor, String actionName, Combatant target, AttackResolution attackResolution) { @@ -66,6 +44,11 @@ public ActionEvent(Combatant actor, String actionName, Combatant target, AttackR ); } + @Override + public T visit(Visitor visitor) { + return visitor.onAction(this); + } + public CombatantId getActorId() { return actorId; } diff --git a/src/main/java/sc2002/turnbased/report/BattleEvent.java b/src/main/java/sc2002/turnbased/report/BattleEvent.java index 1bebd59..5279a03 100644 --- a/src/main/java/sc2002/turnbased/report/BattleEvent.java +++ b/src/main/java/sc2002/turnbased/report/BattleEvent.java @@ -1,4 +1,19 @@ package sc2002.turnbased.report; public interface BattleEvent { + T visit(Visitor visitor); + + interface Visitor { + T onAction(ActionEvent actionEvent); + + T onNarration(NarrationEvent narrationEvent); + + T onRoundStart(RoundStartEvent roundStartEvent); + + T onRoundSummary(RoundSummaryEvent roundSummaryEvent); + + T onSkippedTurn(SkippedTurnEvent skippedTurnEvent); + + T onStatusEffectReport(StatusEffectReportEvent statusEffectReportEvent); + } } diff --git a/src/main/java/sc2002/turnbased/report/CombatantSummary.java b/src/main/java/sc2002/turnbased/report/CombatantSummary.java index 4f36d00..4601818 100644 --- a/src/main/java/sc2002/turnbased/report/CombatantSummary.java +++ b/src/main/java/sc2002/turnbased/report/CombatantSummary.java @@ -1,39 +1,24 @@ package sc2002.turnbased.report; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; +import java.util.Objects; import sc2002.turnbased.domain.CombatantId; -public class CombatantSummary { - private final CombatantId combatantId; - private final String name; - private final int currentHp; - private final int maxHp; - private final int currentAttack; - private final int baseAttack; - private final boolean alive; - private final List activeStatuses; - - public CombatantSummary( - CombatantId combatantId, - String name, - int currentHp, - int maxHp, - int currentAttack, - int baseAttack, - boolean alive, - List activeStatuses - ) { - this.combatantId = combatantId; - this.name = name; - this.currentHp = currentHp; - this.maxHp = maxHp; - this.currentAttack = currentAttack; - this.baseAttack = baseAttack; - this.alive = alive; - this.activeStatuses = new ArrayList<>(activeStatuses); +public record CombatantSummary( + CombatantId combatantId, + String name, + int currentHp, + int maxHp, + int currentAttack, + int baseAttack, + boolean alive, + List activeStatuses +) { + public CombatantSummary { + combatantId = Objects.requireNonNull(combatantId, "combatantId"); + name = Objects.requireNonNull(name, "name"); + activeStatuses = List.copyOf(Objects.requireNonNull(activeStatuses, "activeStatuses")); } public CombatantId getCombatantId() { @@ -65,6 +50,6 @@ public boolean isAlive() { } public List getActiveStatuses() { - return Collections.unmodifiableList(activeStatuses); + return activeStatuses; } } diff --git a/src/main/java/sc2002/turnbased/report/NarrationEvent.java b/src/main/java/sc2002/turnbased/report/NarrationEvent.java index f178b80..d744066 100644 --- a/src/main/java/sc2002/turnbased/report/NarrationEvent.java +++ b/src/main/java/sc2002/turnbased/report/NarrationEvent.java @@ -1,10 +1,15 @@ package sc2002.turnbased.report; -public class NarrationEvent implements BattleEvent { - private final String text; +import java.util.Objects; - public NarrationEvent(String text) { - this.text = text; +public record NarrationEvent(String text) implements BattleEvent { + public NarrationEvent { + text = Objects.requireNonNull(text, "text"); + } + + @Override + public T visit(Visitor visitor) { + return visitor.onNarration(this); } public String getText() { diff --git a/src/main/java/sc2002/turnbased/report/RoundStartEvent.java b/src/main/java/sc2002/turnbased/report/RoundStartEvent.java index f9837ad..f673ec3 100644 --- a/src/main/java/sc2002/turnbased/report/RoundStartEvent.java +++ b/src/main/java/sc2002/turnbased/report/RoundStartEvent.java @@ -1,10 +1,9 @@ package sc2002.turnbased.report; -public class RoundStartEvent implements BattleEvent { - private final int roundNumber; - - public RoundStartEvent(int roundNumber) { - this.roundNumber = roundNumber; +public record RoundStartEvent(int roundNumber) implements BattleEvent { + @Override + public T visit(Visitor visitor) { + return visitor.onRoundStart(this); } public int getRoundNumber() { diff --git a/src/main/java/sc2002/turnbased/report/RoundSummaryEvent.java b/src/main/java/sc2002/turnbased/report/RoundSummaryEvent.java index 81dbf08..aabc40a 100644 --- a/src/main/java/sc2002/turnbased/report/RoundSummaryEvent.java +++ b/src/main/java/sc2002/turnbased/report/RoundSummaryEvent.java @@ -1,31 +1,31 @@ package sc2002.turnbased.report; -import java.util.ArrayList; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import sc2002.turnbased.domain.ItemType; -public class RoundSummaryEvent implements BattleEvent { - private final int roundNumber; - private final CombatantSummary playerSummary; - private final List enemySummaries; - private final Map inventorySnapshot; - private final int specialSkillCooldown; - - public RoundSummaryEvent( - int roundNumber, - CombatantSummary playerSummary, - List enemySummaries, - Map inventorySnapshot, - int specialSkillCooldown - ) { - this.roundNumber = roundNumber; - this.playerSummary = playerSummary; - this.enemySummaries = new ArrayList<>(enemySummaries); - this.inventorySnapshot = inventorySnapshot; - this.specialSkillCooldown = specialSkillCooldown; +public record RoundSummaryEvent( + int roundNumber, + CombatantSummary playerSummary, + List enemySummaries, + Map inventorySnapshot, + int specialSkillCooldown +) implements BattleEvent { + public RoundSummaryEvent { + playerSummary = Objects.requireNonNull(playerSummary, "playerSummary"); + enemySummaries = List.copyOf(Objects.requireNonNull(enemySummaries, "enemySummaries")); + inventorySnapshot = Collections.unmodifiableMap(new LinkedHashMap<>( + Objects.requireNonNull(inventorySnapshot, "inventorySnapshot") + )); + } + + @Override + public T visit(Visitor visitor) { + return visitor.onRoundSummary(this); } public int getRoundNumber() { @@ -37,11 +37,11 @@ public CombatantSummary getPlayerSummary() { } public List getEnemySummaries() { - return Collections.unmodifiableList(enemySummaries); + return enemySummaries; } public Map getInventorySnapshot() { - return Collections.unmodifiableMap(inventorySnapshot); + return inventorySnapshot; } public int getSpecialSkillCooldown() { diff --git a/src/main/java/sc2002/turnbased/report/SkippedTurnEvent.java b/src/main/java/sc2002/turnbased/report/SkippedTurnEvent.java index a37660b..77f1404 100644 --- a/src/main/java/sc2002/turnbased/report/SkippedTurnEvent.java +++ b/src/main/java/sc2002/turnbased/report/SkippedTurnEvent.java @@ -7,17 +7,17 @@ import sc2002.turnbased.domain.CombatantId; import sc2002.turnbased.domain.status.CombatantStatusOutcome; -public class SkippedTurnEvent implements BattleEvent { - private final CombatantId combatantId; - private final String combatantName; - private final String reason; - private final List statusEffectNotes; - - public SkippedTurnEvent(CombatantId combatantId, String combatantName, String reason, List statusEffectNotes) { - this.combatantId = Objects.requireNonNull(combatantId, "combatantId"); - this.combatantName = combatantName; - this.reason = reason; - this.statusEffectNotes = List.copyOf(Objects.requireNonNull(statusEffectNotes, "statusEffectNotes")); +public record SkippedTurnEvent( + CombatantId combatantId, + String combatantName, + String reason, + List statusEffectNotes +) implements BattleEvent { + public SkippedTurnEvent { + combatantId = Objects.requireNonNull(combatantId, "combatantId"); + combatantName = Objects.requireNonNull(combatantName, "combatantName"); + reason = Objects.requireNonNull(reason, "reason"); + statusEffectNotes = List.copyOf(Objects.requireNonNull(statusEffectNotes, "statusEffectNotes")); } public static SkippedTurnEvent fromStatusEffectOutcomes( @@ -34,6 +34,11 @@ public static SkippedTurnEvent fromStatusEffectOutcomes( ); } + @Override + public T visit(Visitor visitor) { + return visitor.onSkippedTurn(this); + } + public CombatantId getCombatantId() { return combatantId; } diff --git a/src/main/java/sc2002/turnbased/report/StatusEffectReportEvent.java b/src/main/java/sc2002/turnbased/report/StatusEffectReportEvent.java index 1250a5e..37ec2ab 100644 --- a/src/main/java/sc2002/turnbased/report/StatusEffectReportEvent.java +++ b/src/main/java/sc2002/turnbased/report/StatusEffectReportEvent.java @@ -10,6 +10,11 @@ public record StatusEffectReportEvent(List statusEffectNotes) implements statusEffectNotes = List.copyOf(Objects.requireNonNull(statusEffectNotes, "statusEffectNotes")); } + @Override + public T visit(Visitor visitor) { + return visitor.onStatusEffectReport(this); + } + public static StatusEffectReportEvent fromStatusEffectOutcomes(List statusEffectOutcomes) { return new StatusEffectReportEvent(StatusEffectReportMapper.toNotes(statusEffectOutcomes)); } diff --git a/src/main/java/sc2002/turnbased/ui/BattleConsoleFormatter.java b/src/main/java/sc2002/turnbased/ui/BattleConsoleFormatter.java index fada48b..3f2752e 100644 --- a/src/main/java/sc2002/turnbased/ui/BattleConsoleFormatter.java +++ b/src/main/java/sc2002/turnbased/ui/BattleConsoleFormatter.java @@ -18,31 +18,43 @@ public class BattleConsoleFormatter { public List format(List events) { List lines = new ArrayList<>(); for (BattleEvent event : events) { - if (event instanceof RoundStartEvent roundStartEvent) { - lines.add("Round " + roundStartEvent.getRoundNumber()); - continue; + lines.addAll(formatEvent(event)); + } + return lines; + } + + private List formatEvent(BattleEvent event) { + return event.visit(new BattleEvent.Visitor<>() { + @Override + public List onAction(ActionEvent actionEvent) { + return List.of(formatAction(actionEvent)); } - if (event instanceof ActionEvent actionEvent) { - lines.add(formatAction(actionEvent)); - continue; + + @Override + public List onNarration(NarrationEvent narrationEvent) { + return List.of(narrationEvent.getText()); } - if (event instanceof NarrationEvent narrationEvent) { - lines.add(narrationEvent.getText()); - continue; + + @Override + public List onRoundStart(RoundStartEvent roundStartEvent) { + return List.of("Round " + roundStartEvent.getRoundNumber()); } - if (event instanceof SkippedTurnEvent skippedTurnEvent) { - lines.add(formatSkippedTurn(skippedTurnEvent)); - continue; + + @Override + public List onRoundSummary(RoundSummaryEvent roundSummaryEvent) { + return formatRoundSummary(roundSummaryEvent); } - if (event instanceof StatusEffectReportEvent statusEffectReportEvent) { - lines.addAll(formatStatusEffectNotes(statusEffectReportEvent.statusEffectNotes())); - continue; + + @Override + public List onSkippedTurn(SkippedTurnEvent skippedTurnEvent) { + return List.of(formatSkippedTurn(skippedTurnEvent)); } - if (event instanceof RoundSummaryEvent roundSummaryEvent) { - lines.addAll(formatRoundSummary(roundSummaryEvent)); + + @Override + public List onStatusEffectReport(StatusEffectReportEvent statusEffectReportEvent) { + return formatStatusEffectNotes(statusEffectReportEvent.statusEffectNotes()); } - } - return lines; + }); } private String formatAction(ActionEvent actionEvent) { diff --git a/src/main/java/sc2002/turnbased/ui/gui/playback/BattleDialogueFormatter.java b/src/main/java/sc2002/turnbased/ui/gui/playback/BattleDialogueFormatter.java index 9f92997..a546c0b 100644 --- a/src/main/java/sc2002/turnbased/ui/gui/playback/BattleDialogueFormatter.java +++ b/src/main/java/sc2002/turnbased/ui/gui/playback/BattleDialogueFormatter.java @@ -10,42 +10,71 @@ public final class BattleDialogueFormatter { public String format(BattleEvent event) { - if (event instanceof ActionEvent actionEvent) { - return actionMessage(actionEvent); - } - if (event instanceof NarrationEvent narrationEvent) { - return cleanBattleText(narrationEvent.getText()); - } - if (event instanceof SkippedTurnEvent skippedTurnEvent) { - return skippedTurnMessage(skippedTurnEvent); - } - if (event instanceof StatusEffectReportEvent statusEffectReportEvent) { - return statusEffectMessage(statusEffectReportEvent); - } - if (event instanceof RoundStartEvent roundStartEvent) { - return "Round " + roundStartEvent.getRoundNumber() + " started!"; - } - if (event instanceof RoundSummaryEvent roundSummaryEvent) { - return "Round " + roundSummaryEvent.getRoundNumber() + " is over."; - } - return "The battle continues."; + return event.visit(new BattleEvent.Visitor<>() { + @Override + public String onAction(ActionEvent actionEvent) { + return actionMessage(actionEvent); + } + + @Override + public String onNarration(NarrationEvent narrationEvent) { + return cleanBattleText(narrationEvent.getText()); + } + + @Override + public String onRoundStart(RoundStartEvent roundStartEvent) { + return "Round " + roundStartEvent.getRoundNumber() + " started!"; + } + + @Override + public String onRoundSummary(RoundSummaryEvent roundSummaryEvent) { + return "Round " + roundSummaryEvent.getRoundNumber() + " is over."; + } + + @Override + public String onSkippedTurn(SkippedTurnEvent skippedTurnEvent) { + return skippedTurnMessage(skippedTurnEvent); + } + + @Override + public String onStatusEffectReport(StatusEffectReportEvent statusEffectReportEvent) { + return statusEffectMessage(statusEffectReportEvent); + } + }); } public int playbackDelayMillis(BattleEvent event) { - int baseDelay = 1_000; - if (event instanceof ActionEvent) { - baseDelay = 1_350; - } else if (event instanceof NarrationEvent) { - baseDelay = 1_250; - } else if (event instanceof SkippedTurnEvent) { - baseDelay = 1_150; - } else if (event instanceof StatusEffectReportEvent) { - baseDelay = 1_100; - } else if (event instanceof RoundStartEvent) { - baseDelay = 850; - } else if (event instanceof RoundSummaryEvent) { - baseDelay = 900; - } + int baseDelay = event.visit(new BattleEvent.Visitor<>() { + @Override + public Integer onAction(ActionEvent actionEvent) { + return 1_350; + } + + @Override + public Integer onNarration(NarrationEvent narrationEvent) { + return 1_250; + } + + @Override + public Integer onRoundStart(RoundStartEvent roundStartEvent) { + return 850; + } + + @Override + public Integer onRoundSummary(RoundSummaryEvent roundSummaryEvent) { + return 900; + } + + @Override + public Integer onSkippedTurn(SkippedTurnEvent skippedTurnEvent) { + return 1_150; + } + + @Override + public Integer onStatusEffectReport(StatusEffectReportEvent statusEffectReportEvent) { + return 1_100; + } + }); int typewriterDelay = format(event).length() * 12 + 520; return Math.max(baseDelay, Math.min(2_000, typewriterDelay)); } diff --git a/src/main/java/sc2002/turnbased/ui/gui/view/ArenaSceneModel.java b/src/main/java/sc2002/turnbased/ui/gui/view/ArenaSceneModel.java index a097aa3..9d566dd 100644 --- a/src/main/java/sc2002/turnbased/ui/gui/view/ArenaSceneModel.java +++ b/src/main/java/sc2002/turnbased/ui/gui/view/ArenaSceneModel.java @@ -152,28 +152,54 @@ void applyBattleEvent(BattleEvent event, int arenaWidth, int arenaHeight) { void applyBattleEvent(BattleEvent event, int arenaWidth, int arenaHeight, long now) { Objects.requireNonNull(event, "event"); - if (event instanceof ActionEvent actionEvent) { - applyActionEvent(actionEvent, now); - } else if (event instanceof RoundStartEvent roundStartEvent) { - roundNumber = roundStartEvent.getRoundNumber(); - banner = "Round " + roundNumber; - } else if (event instanceof RoundSummaryEvent roundSummaryEvent) { - applyRoundSummary(roundSummaryEvent, arenaWidth, arenaHeight); - } else if (event instanceof NarrationEvent narrationEvent) { - banner = narrationEvent.getText(); - if (banner.startsWith("Victory") || banner.startsWith("Defeat")) { - acceptingPlayerTurn = false; - battleActive = false; + event.visit(new BattleEvent.Visitor<>() { + @Override + public Void onAction(ActionEvent actionEvent) { + applyActionEvent(actionEvent, now); + return null; } - } else if (event instanceof SkippedTurnEvent skippedTurnEvent) { - FighterSpriteDto skipped = sprites.get(skippedTurnEvent.getCombatantId()); - if (skipped != null) { - skipped.pulseUntil = now + ACTION_ANIMATION_NANOS; + + @Override + public Void onNarration(NarrationEvent narrationEvent) { + banner = narrationEvent.getText(); + if (banner.startsWith("Victory") || banner.startsWith("Defeat")) { + acceptingPlayerTurn = false; + battleActive = false; + } + return null; } - banner = skippedTurnEvent.getCombatantName() + " cannot act: " + skippedTurnEvent.getReason(); - } else if (event instanceof StatusEffectReportEvent statusEvent && !statusEvent.statusEffectNotes().isEmpty()) { - banner = String.join(" | ", statusEvent.statusEffectNotes()); - } + + @Override + public Void onRoundStart(RoundStartEvent roundStartEvent) { + roundNumber = roundStartEvent.getRoundNumber(); + banner = "Round " + roundNumber; + return null; + } + + @Override + public Void onRoundSummary(RoundSummaryEvent roundSummaryEvent) { + applyRoundSummary(roundSummaryEvent, arenaWidth, arenaHeight); + return null; + } + + @Override + public Void onSkippedTurn(SkippedTurnEvent skippedTurnEvent) { + FighterSpriteDto skipped = sprites.get(skippedTurnEvent.getCombatantId()); + if (skipped != null) { + skipped.pulseUntil = now + ACTION_ANIMATION_NANOS; + } + banner = skippedTurnEvent.getCombatantName() + " cannot act: " + skippedTurnEvent.getReason(); + return null; + } + + @Override + public Void onStatusEffectReport(StatusEffectReportEvent statusEvent) { + if (!statusEvent.statusEffectNotes().isEmpty()) { + banner = String.join(" | ", statusEvent.statusEffectNotes()); + } + return null; + } + }); chooseDefaultTarget(); layoutEnemies(arenaWidth, arenaHeight); } diff --git a/src/test/java/sc2002/turnbased/domain/EnemyCombatantTest.java b/src/test/java/sc2002/turnbased/domain/EnemyCombatantTest.java index bcb17c4..54533aa 100644 --- a/src/test/java/sc2002/turnbased/domain/EnemyCombatantTest.java +++ b/src/test/java/sc2002/turnbased/domain/EnemyCombatantTest.java @@ -81,5 +81,9 @@ public List execute(ActionExecutionContext context, Combatant actor } private static final class TestBattleEvent implements BattleEvent { + @Override + public T visit(Visitor visitor) { + throw new UnsupportedOperationException("Test event should not be visited"); + } } } From 5e1acbf60f3cc15c53a17b06ea7d05f361caa716 Mon Sep 17 00:00:00 2001 From: liang799 <71926348+liang799@users.noreply.github.com> Date: Wed, 22 Apr 2026 17:56:13 +0800 Subject: [PATCH 2/2] fix: validate action events and cache playback formatting --- .../sc2002/turnbased/report/ActionEvent.java | 41 +++++++++++----- .../gui/playback/BattleDialogueFormatter.java | 48 +++++++------------ 2 files changed, 46 insertions(+), 43 deletions(-) diff --git a/src/main/java/sc2002/turnbased/report/ActionEvent.java b/src/main/java/sc2002/turnbased/report/ActionEvent.java index 8f10f3c..ca6bc54 100644 --- a/src/main/java/sc2002/turnbased/report/ActionEvent.java +++ b/src/main/java/sc2002/turnbased/report/ActionEvent.java @@ -23,27 +23,46 @@ public record ActionEvent( ) implements BattleEvent { public ActionEvent { actorId = Objects.requireNonNull(actorId, "actorId"); + actorName = Objects.requireNonNull(actorName, "actorName"); + actionName = Objects.requireNonNull(actionName, "actionName"); targetId = Objects.requireNonNull(targetId, "targetId"); + targetName = Objects.requireNonNull(targetName, "targetName"); statusEffectNotes = List.copyOf(Objects.requireNonNull(statusEffectNotes, "statusEffectNotes")); } public ActionEvent(Combatant actor, String actionName, Combatant target, AttackResolution attackResolution) { this( - actor.combatantId(), - actor.getName(), + requireActor(actor).combatantId(), + requireCombatantName(actor, "actor"), actionName, - target.combatantId(), - target.getName(), - attackResolution.hpBefore(), - attackResolution.hpAfter(), - attackResolution.attackUsed(), - attackResolution.targetDefense(), - attackResolution.damage(), - attackResolution.targetEliminated(), - StatusEffectReportMapper.toNotes(attackResolution.statusEffectOutcomes()) + requireTarget(target).combatantId(), + requireCombatantName(target, "target"), + requireAttackResolution(attackResolution).hpBefore(), + requireAttackResolution(attackResolution).hpAfter(), + requireAttackResolution(attackResolution).attackUsed(), + requireAttackResolution(attackResolution).targetDefense(), + requireAttackResolution(attackResolution).damage(), + requireAttackResolution(attackResolution).targetEliminated(), + StatusEffectReportMapper.toNotes(requireAttackResolution(attackResolution).statusEffectOutcomes()) ); } + private static Combatant requireActor(Combatant actor) { + return Objects.requireNonNull(actor, "actor"); + } + + private static Combatant requireTarget(Combatant target) { + return Objects.requireNonNull(target, "target"); + } + + private static AttackResolution requireAttackResolution(AttackResolution attackResolution) { + return Objects.requireNonNull(attackResolution, "attackResolution"); + } + + private static String requireCombatantName(Combatant combatant, String role) { + return Objects.requireNonNull(Objects.requireNonNull(combatant, role).getName(), role + ".name"); + } + @Override public T visit(Visitor visitor) { return visitor.onAction(this); diff --git a/src/main/java/sc2002/turnbased/ui/gui/playback/BattleDialogueFormatter.java b/src/main/java/sc2002/turnbased/ui/gui/playback/BattleDialogueFormatter.java index a546c0b..f8580ca 100644 --- a/src/main/java/sc2002/turnbased/ui/gui/playback/BattleDialogueFormatter.java +++ b/src/main/java/sc2002/turnbased/ui/gui/playback/BattleDialogueFormatter.java @@ -44,38 +44,22 @@ public String onStatusEffectReport(StatusEffectReportEvent statusEffectReportEve } public int playbackDelayMillis(BattleEvent event) { - int baseDelay = event.visit(new BattleEvent.Visitor<>() { - @Override - public Integer onAction(ActionEvent actionEvent) { - return 1_350; - } - - @Override - public Integer onNarration(NarrationEvent narrationEvent) { - return 1_250; - } - - @Override - public Integer onRoundStart(RoundStartEvent roundStartEvent) { - return 850; - } - - @Override - public Integer onRoundSummary(RoundSummaryEvent roundSummaryEvent) { - return 900; - } - - @Override - public Integer onSkippedTurn(SkippedTurnEvent skippedTurnEvent) { - return 1_150; - } - - @Override - public Integer onStatusEffectReport(StatusEffectReportEvent statusEffectReportEvent) { - return 1_100; - } - }); - int typewriterDelay = format(event).length() * 12 + 520; + String formatted = format(event); + int typewriterDelay = formatted.length() * 12 + 520; + int baseDelay = 1_000; + if (event instanceof ActionEvent) { + baseDelay = 1_350; + } else if (event instanceof NarrationEvent) { + baseDelay = 1_250; + } else if (event instanceof RoundStartEvent) { + baseDelay = 850; + } else if (event instanceof RoundSummaryEvent) { + baseDelay = 900; + } else if (event instanceof SkippedTurnEvent) { + baseDelay = 1_150; + } else if (event instanceof StatusEffectReportEvent) { + baseDelay = 1_100; + } return Math.max(baseDelay, Math.min(2_000, typewriterDelay)); }