Repository navigation
Tutorial Enemy Integration Debug Commands and Forms
Team 1 · Issue #436 · Related tutorial integration #365 · Last reviewed: 19 October 2026
This work connects the Votive Attendant to the tutorial battle, adds two commands for reproducing enemy states during testing, and supports visual form changes for the Riven Knight and Tomb Guardian.
- The tutorial battle uses the
votive_attendantconfiguration, atlas and dedicatedTutorialEnemyAI. -
setenemyhpchanges the health of an enemy in a zero-based battle slot. -
forceintentreplaces one already-displayed upcoming intent; the next selection returns to the normal AI. - The Riven Knight's form follows the AI's actual enrage state.
- The Tomb Guardian holds its taunting form while its own Taunt is active.
- A missing form-specific animation falls back to the normal animation for the same action.
Li provided the tutorial enemy's AI decision pattern. This ticket integrates that AI into the battle rather than claiming authorship of its original design. The final form artwork and its atlas checks are documented separately in Enemy Action and Form Animations and its test plan.
classDiagram
direction LR
class BattleScreen {
+createBattle()
+registerCommands()
}
class BattleController {
+getEnemyInSlot(slot)
+forceNextEnemyIntent(slot, intent)
+getAliveTaunterTargetIds()
}
class SetEnemyHealthCommand {
+action(args) boolean
}
class ForceIntentCommand {
+action(args) boolean
}
class EnemyFactory {
+create(config) Entity
}
class EnemyBehaviourComponent {
+setAI(ai)
+rollIntent() EnemyIntent
+forceCurrentIntent(intent)
}
class EnemyAI {
<<interface>>
+decide(context) EnemyIntent
}
class TutorialEnemyAI {
+decide(context) EnemyIntent
}
class EnrageLowHealthAI {
+decide(context) EnemyIntent
+isEnraged() boolean
}
class CombatStatsComponent {
+setHealth(health)
+applyStatusEffect(type, value, duration)
}
class EnemyAnimationController {
-currentForm String
+setForm(form)
}
class AnimationRenderComponent {
+hasAnimation(name) boolean
+startAnimation(name)
}
BattleScreen --> BattleController : manages battle
BattleScreen --> EnemyFactory : creates enemies
BattleScreen --> SetEnemyHealthCommand : registers
BattleScreen --> ForceIntentCommand : registers
SetEnemyHealthCommand --> BattleController : finds enemy slot
ForceIntentCommand --> BattleController : overrides intent
EnemyFactory --> EnemyBehaviourComponent : attaches
EnemyFactory --> EnemyAnimationController : attaches
EnemyBehaviourComponent --> EnemyAI : requests decision
TutorialEnemyAI ..|> EnemyAI
EnrageLowHealthAI ..|> EnemyAI
BattleController --> CombatStatsComponent : reads statuses
EnemyAnimationController --> AnimationRenderComponent : plays animation
The commands and form system reuse the existing battle components. They do not create a replacement AI framework or a second battle state.
For a tutorial battle, BattleScreen selects votive_attendant instead of a normal encounter line-up. EnemyFactory creates it from configs/enemies.json and its atlas. BattleScreen then assigns TutorialEnemyAI to the enemy's EnemyBehaviourComponent.
The tutorial AI follows a fixed attack → defend → buff cycle. Its attack is designed to leave a living player with at least one health. EnemyBehaviourComponent also caps the resolved tutorial attack after combat modifiers, so Vulnerable cannot unexpectedly make that attack lethal. The buff increases the enemy's base attack for later actions.
sequenceDiagram
participant Screen as BattleScreen
participant Factory as EnemyFactory
participant Behaviour as EnemyBehaviourComponent
participant AI as TutorialEnemyAI
Screen->>Factory: Create "votive_attendant"
Factory-->>Screen: Enemy entity with art and components
Screen->>Behaviour: setAI(new TutorialEnemyAI())
loop Each enemy intent selection
Behaviour->>AI: decide(battle context)
AI-->>Behaviour: Attack, Defend or Buff
Behaviour-->>Screen: Telegraph selected intent
end
The rendered tutorial and the remaining acceptance criteria of #365 still require joint checking with Team 2. See also Tutorial Battle Setup and Enemy Integration and the Tutorial Acceptance Test Plan.
Both commands are registered in BattleScreen and are available through the battle debug terminal. Slots count from 0, left to right.
For example, setenemyhp 0 40 sets the first enemy's current health to 40. CombatStatsComponent.setHealth() emits the existing health-update event, allowing the visible bar to refresh. The command rejects missing or non-numeric arguments, a negative health value, and an out-of-range slot. It is useful for reaching a health-dependent phase without repeatedly playing cards; the AI evaluates that health when it next selects an intent.
Supported names are attack, defend, silence, damage_on_card_play and taunt. For example, forceintent 0 silence changes the first enemy's already-displayed upcoming action to Silence. The override applies to that action only; the following intent is selected by its normal AI. The command rejects missing arguments, non-numeric or invalid slots, unknown names, and dead enemies. The controller accepts the override only during the player turn.
sequenceDiagram
actor Tester
participant Terminal
participant Command as ForceIntentCommand
participant Battle as BattleController
participant Behaviour as EnemyBehaviourComponent
participant AI as EnemyAI
Tester->>Terminal: forceintent 0 silence
Terminal->>Command: action(slot=0, intent=silence)
Command->>Battle: forceNextEnemyIntent(0, silence)
Battle->>Behaviour: forceCurrentIntent(silence)
Behaviour-->>Tester: Display Silence as upcoming intent
Tester->>Battle: End player turn
Battle->>Behaviour: Execute forced intent
Note over Behaviour,AI: Override is not a permanent AI rule
Battle->>Behaviour: Select following intent
Behaviour->>AI: decide(context)
AI-->>Behaviour: Normal AI decision
EnemyAnimationController holds a currentForm, normally normal. On enemyFormChanged, it tries <action>_<form> first: for example, idle_enraged. If that animation is unavailable, it plays the normal animation for the same action. A form change does not interrupt an attack or cast already in progress; the new stance is shown when the enemy returns to idle.
EnrageLowHealthAI can become enraged when health falls below its threshold or when player memory shows the player has not been defending. After the AI selects an intent, EnemyBehaviourComponent compares the AI's actual isEnraged() state with the previous visual state. It emits enemyFormChanged only when that state changes. The appearance therefore follows the AI's real state rather than inferring enrage from health alone.
Taunt is stored on the player under a source-specific key, TAUNT:<enemy ID>. BattleController listens for status application and removal. A Tomb Guardian enters the taunt form only while that Guardian's own Taunt is present and it is alive. Expiration or defeat removes the restriction and restores the normal form. Another enemy's Taunt must not keep this Guardian in its taunting form.
sequenceDiagram
participant Guardian as Tomb Guardian
participant PlayerStats as Player CombatStats
participant Battle as BattleController
participant Animation as EnemyAnimationController
Guardian->>PlayerStats: Apply TAUNT:<Guardian ID>
PlayerStats-->>Battle: statusEffectApplied
Battle-->>Animation: enemyFormChanged("taunt")
Animation->>Animation: Play idle_taunt if present
Note over Animation: Missing form action falls back to normal action
alt Taunt expires
PlayerStats-->>Battle: statusEffectRemoved
Battle-->>Animation: enemyFormChanged("normal")
else Guardian is defeated
Guardian-->>Battle: enemyDefeated
Battle->>PlayerStats: Remove its TAUNT:<Guardian ID>
Battle-->>Animation: enemyFormChanged("normal")
end
| Form | Expected atlas region names |
|---|---|
| Riven Knight enrage |
idle_enraged, attack_enraged, hurt_enraged
|
| Tomb Guardian taunt |
idle_taunt, hurt_taunt
|
The regions are optional when an animation is registered. Missing form-specific art must not crash the battle. The art-side naming and atlas checks are covered by #438's animation page.
JUnit tests verify command validation, AI decisions, form events, status ownership and animation fallback. A rendered game is still needed to judge health-bar updates, poses, timing and the full tutorial flow.
| ID | Test case | Expected result | Verification |
|---|---|---|---|
| T1 | Run setenemyhp 0 40
|
Enemy health becomes 40 and a health-update event is emitted | Automated: BattleDebugCommandsTest — passed |
| T2 | Give setenemyhp an invalid slot, missing argument, non-numeric value or negative health |
Command is rejected without changing health | Automated: BattleDebugCommandsTest — passed |
| T3 | Run forceintent 0 silence and advance the battle |
Silence is used for the upcoming action only; the following intent comes from normal AI selection | Automated: BattleDebugCommandsTest — passed |
| T4 | Try supported and invalid forceintent names |
Supported names map correctly; invalid input does not replace the current intent | Automated: BattleDebugCommandsTest — passed |
| T5 | Trigger Riven Knight enrage through health and memory conditions | Visual form follows AI state; a change event is emitted when that state changes | Automated: EnrageLowHealthAITest and EnemyBehaviourComponentTest — passed |
| T6 | Apply and expire a Tomb Guardian's own Taunt; defeat a taunter | Only its own active Taunt holds the form; expiration or defeat clears it | Automated: BattleControllerTest — passed |
| T7 | Request an unavailable form-specific action | The matching normal action plays without a crash | Automated: EnemyAnimationControllerTest and EnemyFactoryTest — passed |
| T8 | Create the Votive Attendant and exercise its tutorial actions | Correct art and AI are assigned; its cycle, buff and non-lethal attack rules work | Automated: EnemyFactoryTest, TutorialEnemyAITest and EnemyBehaviourComponentTest — passed |
| T9 | Use both commands in a rendered battle, including a multi-enemy battle | Health bar and intent display visibly update for the selected slot | Manual in-game check — pending |
| T10 | Play the tutorial battle and observe elite form changes | Tutorial progression, sprite alignment, timing and form transitions look correct | Manual in-game check — pending |
| T11 | Check the remaining acceptance criteria of #365 with Team 2 | Tutorial integration is jointly confirmed | Joint check — pending |
On the local version reviewed on 19 October 2026, seven relevant test classes completed with 174 tests, 0 failures and 0 errors (BUILD SUCCESSFUL) using JDK 21. These classes include pre-existing regression tests; 174 is not the number of tests newly added for #436. This was a targeted run, not a full-project test or a coverage measurement.
From source/, run the same targeted classes with:
.\gradlew.bat :core:test --tests "com.csse3200.game.ui.terminal.BattleDebugCommandsTest" --tests "com.csse3200.game.components.combat.BattleControllerTest" --tests "com.csse3200.game.components.enemy.EnemyBehaviourComponentTest" --tests "com.csse3200.game.components.enemy.EnemyAnimationControllerTest" --tests "com.csse3200.game.components.enemy.EnemyAI.EnrageLowHealthAITest" --tests "com.csse3200.game.components.enemy.EnemyAI.TutorialEnemyAITest" --tests "com.csse3200.game.entities.factories.EnemyFactoryTest"Run the complete core suite separately with ./gradlew core:test (or .\gradlew.bat :core:test on Windows). The project's Gradle version requires a compatible JDK; Java 8 is too old.
- Start the desktop game (
.\gradlew.bat :desktop:runon Windows) and enter the tutorial battle. Confirm that the opponent is the Votive Attendant rather than the regular Bone Crawler. - Observe several enemy turns. Confirm that it attacks, defends and buffs in its intended cycle, and that an ordinary tutorial attack cannot reduce a living player below one health.
- In a suitable battle, open the debug terminal with
F1and runsetenemyhp 0 40. Check the correct enemy's visible health bar. Try an invalid slot and a non-numeric health value; neither should crash or change health. - During the player turn, run
forceintent 0 silenceor another supported name. Check the displayed intent, end the turn, and confirm that the enemy performs it once. Its following intent should return to AI selection. - Observe the Riven Knight entering and leaving enrage, including the memory-triggered case. Its visible form should match the actual AI state.
- Observe the Tomb Guardian casting Taunt. Its form should remain active only while its own Taunt is on the player and should end on expiration or defeat.
- Check sprite position, visible bounds, status visuals and fallback poses. Component tests cannot establish whether these look correct in the rendered game.
Record screenshots or short clips and the observed result before changing T9–T11 from pending to passed.
The targeted automated tests passed. The complete core test suite, the required coverage gate, rendered-game checks, and the remaining #365 joint acceptance items are not claimed as complete here. Add their actual results and evidence after they are performed.
-
source/core/src/main/com/csse3200/game/screens/BattleScreen.java— tutorial line-up, tutorial AI assignment and command registration. -
source/core/src/main/com/csse3200/game/ui/terminal/commands/SetEnemyHealthCommand.java— health command and argument checks. -
source/core/src/main/com/csse3200/game/ui/terminal/commands/ForceIntentCommand.java— intent-name mapping and argument checks. -
source/core/src/main/com/csse3200/game/components/combat/BattleController.java— slot lookup, one-use intent override and Taunt-form coordination. -
source/core/src/main/com/csse3200/game/components/enemy/EnemyBehaviourComponent.java— intent selection, enrage-form event and tutorial attack resolution. -
source/core/src/main/com/csse3200/game/components/enemy/EnemyAnimationController.java— current form and animation fallback. -
source/core/src/main/com/csse3200/game/entities/factories/EnemyFactory.java— registration of available form animations. -
source/core/assets/configs/enemies.json— Votive Attendant configuration.
Entities and Components
Input Handling
Game Screens and Areas
Encounters
Tutorials and Contextual Help (Team 2)
- Feature Overview
- Tutorial Entry and Fresh Run Transition
- Tutorial Battle Setup and Enemy Integration
- Battle Tutorial Guidance UI
- Battle Tutorial Steps and Progression
- Contextual Event Help and Tutorial Content
- Tutorial Acceptance Test Plan
- Card Keyword Tooltips
- Map Introduction
- Tutorial Status Teaching
- Tutorial Victory Screen