Files
oracle-of-secrets/Docs/Features/CastleAmbush_Plan.md

611 lines
18 KiB
Markdown

# Castle Ambush & Guard Capture System - Implementation Plan
**Status:** 🚧 Planning Phase
**Created:** October 3, 2025
**Target:** Future Update
**Related Files:** `Core/capture.asm`, `Sprites/Enemies/custom_guard.asm`, `Sprites/overlord_ref.asm`
---
## Overview
The **Castle Ambush System** will create a dynamic encounter where Link is detected by guards in the castle, captured, and warped to a dungeon. This combines:
1. **Probe Detection System** - Guards detect Link entering restricted areas
2. **Guard Capture Mechanics** - Guards surround and capture Link
3. **Warp System** - Link is transported to a dungeon entrance
4. **Overlord Management** - Multi-screen guard coordination
---
## Current State Analysis
### Existing Components
#### ✅ Core/capture.asm
**Status:** Implemented but untested
```asm
Oracle_CaptureAndWarp:
{
STA.w $010E ; Set the target entrance ID
LDA.b #$05 ; Game Mode 05: (hole/whirlpool transition)
STA.b $10 ; Set the game mode
STZ.b $2F ; Clear Link's action state
STZ.b $5D ; Clear Link's state
LDA.b #$02 : STA.b $71 ; Set transition flag
RTL
}
```
**Purpose:** Warps Link to a specific dungeon entrance (like WallMaster)
**Issues to Address:**
- [ ] Test entrance ID values (need to determine correct dungeon entrance)
- [ ] Verify game mode $05 works for this use case
- [ ] Add screen fade/transition effect
- [ ] Play capture sound effect
- [ ] Store pre-capture location for potential escape sequence
#### 🚧 Sprites/Enemies/custom_guard.asm
**Status:** Prototype with duplicate code
**Contains:**
1. `Oracle_CaptureAndWarp` (DUPLICATE - already in Core/capture.asm)
2. `Hooked_Guard_Main` - Modified guard behavior
**Issues:**
- [ ] Remove duplicate `Oracle_CaptureAndWarp` function
- [ ] Complete `Hooked_Guard_Main` implementation
- [ ] Test guard capture trigger conditions
- [ ] Integrate with vanilla guard sprites (ID $41, $42, $43)
#### 📚 Sprites/overlord_ref.asm
**Status:** Reference material (now in experimental/)
**Purpose:** Documents overlord path patterns for crumbling tiles
**Relevance:** Can be adapted for guard patrol paths
---
## System Architecture
### Phase 1: Detection (Probe System)
```
┌─────────────────────────────────────────────────┐
│ Link enters castle restricted area │
│ SRAM flag: $7EF??? = Castle infiltration active │
└──────────────────┬──────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ Guard spawns probe sprites every 32 frames │
│ Probe checks: │
│ - Link within 16px radius │
│ - Same floor layer ($0F20) │
│ - Not invisible/bunny │
└──────────────────┬──────────────────────────────┘
┌────────┴────────┐
│ Probe Hit? │
└────────┬────────┘
┌────────────┼────────────┐
│ YES │ NO
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Trigger │ │ Continue │
│ Alert State │ │ Patrol │
└──────┬───────┘ └──────────────┘
┌──────────────────────────────────────┐
│ Play alert sound ($1D) │
│ Set SprTimerD = $B0 (176 frames) │
│ Spawn reinforcement guards │
└──────────────────────────────────────┘
```
### Phase 2: Pursuit & Capture
```
┌─────────────────────────────────────────────────┐
│ Alert state active (SprTimerD > 0) │
└──────────────────┬──────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ Guards converge on Link's position │
│ - Use Guard_ChaseLinkOnOneAxis │
│ - Spawn additional guards from off-screen │
│ - Maximum 4 guards active │
└──────────────────┬──────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ Check capture conditions (every frame) │
│ - Link is surrounded (guards on 3+ sides) │
│ - Link is not moving (speed = 0) │
│ - Link has taken damage from guard │
└──────────────────┬──────────────────────────────┘
┌────────┴────────┐
│ Captured? │
└────────┬────────┘
┌────────────┼────────────┐
│ YES │ NO
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Initiate │ │ Continue │
│ Capture │ │ Chase │
└──────┬───────┘ └──────────────┘
┌──────────────────────────────────────┐
│ Phase 3: Warp Sequence │
└──────────────────────────────────────┘
```
### Phase 3: Warp Sequence
```
┌─────────────────────────────────────────────────┐
│ Freeze Link (disable input) │
│ Play capture animation │
│ - Link's sprite changes to "captured" pose │
│ - Guards move to surround positions │
│ - Screen shake effect (3 frames) │
└──────────────────┬──────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ Fade out screen ($0012 = $01) │
│ Wait 32 frames │
└──────────────────┬──────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ Call Oracle_CaptureAndWarp │
│ - A register = Dungeon entrance ID │
│ - Sets game mode to $05 (transition) │
│ - Clears Link state │
└──────────────────┬──────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ Link spawns in dungeon cell │
│ - Set SRAM flag: $7EF??? = Captured │
│ - Play jingle ($06) │
│ - Trigger escape sequence │
└──────────────────────────────────────────────────┘
```
---
## File Consolidation Plan
### Step 1: Organize Core Utilities
**Keep in `Core/capture.asm`:**
```asm
; Core warp functionality
Oracle_CaptureAndWarp:
{
; (existing implementation)
}
; NEW: Enhanced version with effects
Oracle_CaptureAndWarp_Enhanced:
{
PHP
; Store entrance ID
STA.w $010E
; Store capture flag in SRAM
LDA.b #$01
STA.l $7EF3D8 ; Custom flag: Has been captured
; Play capture sound
LDA.b #$1D ; Alert/Capture sound
STA.w $012E
; Fade out screen
LDA.b #$01
STA.w $0012 ; Request fade
; Set up timer for transition
LDA.b #$40 ; Wait 64 frames
STA.b $00
.wait_fade
LDA.b $00 : BNE .wait_fade
; Execute warp
LDA.w $010E ; Get entrance ID back
STA.w $010E
LDA.b #$05 ; Game Mode 05
STA.b $10
STZ.b $2F
STZ.b $5D
LDA.b #$02
STA.b $71
PLP
RTL
}
```
### Step 2: Create Unified Guard Sprite
**New file: `Sprites/Enemies/castle_guard.asm`**
This will replace `Sprites/Enemies/custom_guard.asm` with a complete implementation:
```asm
; =========================================================
; Castle Guard - Ambush & Capture Variant
; =========================================================
!SPRID = Sprite_CastleGuard ; Use new sprite ID or override vanilla
!NbrTiles = 02
!Health = 08
!Damage = 04
; ... other properties ...
%Set_Sprite_Properties(Sprite_CastleGuard_Prep, Sprite_CastleGuard_Long)
; States
!STATE_PATROL = 0
!STATE_ALERT = 1
!STATE_CHASE = 2
!STATE_CAPTURE = 3
Sprite_CastleGuard_Long:
{
PHB : PHK : PLB
JSR Sprite_CastleGuard_Draw
JSL Sprite_DrawShadow
JSL Sprite_CheckActive : BCC .inactive
JSR Sprite_CastleGuard_Main
.inactive
PLB
RTL
}
Sprite_CastleGuard_Prep:
{
PHB : PHK : PLB
; Check if castle ambush is active
LDA.l $7EF3D7 : BEQ .no_ambush ; Custom flag: Castle ambush active
; Enable enhanced AI
LDA.b #$01 : STA.w $0E80, X ; Custom flag for this guard
.no_ambush
; Standard health based on sword level
LDA.l $7EF359 : TAY
LDA.w .health, Y : STA.w SprHealth, X
; Enable parrying
LDA.b #$80 : STA.w SprDefl, X
PLB
RTL
.health
db $04, $06, $08, $0A
}
Sprite_CastleGuard_Main:
{
; State machine
LDA.w SprAction, X
JSL JumpTableLocal
dw CastleGuard_Patrol
dw CastleGuard_Alert
dw CastleGuard_Chase
dw CastleGuard_Capture
}
CastleGuard_Patrol:
{
; Check if castle ambush should activate
LDA.l $7EF3D7 : BEQ .normal_patrol
; Check distance to Link
JSL GetDistance8bit_Long : CMP.b #$80 : BCS .no_probe
; Spawn probe for detection
LDA.w SprTimerA, X : BNE .no_probe
LDA.b #$20 : STA.w SprTimerA, X ; Spawn every 32 frames
JSL Sprite_SpawnProbeAlways_long
.no_probe
; Check if probe triggered alert
LDA.w SprTimerD, X : BEQ .normal_patrol
; Probe detected Link!
LDA.b #!STATE_ALERT : STA.w SprAction, X
; Play alert sound
LDA.b #$1D : STA.w $012E
; Spawn reinforcements
JSR CastleGuard_SpawnReinforcements
RTS
.normal_patrol
; Standard patrol behavior
JSR Guard_StandardPatrol
RTS
}
CastleGuard_Alert:
{
; Transition to chase after alert plays
LDA.w SprTimerD, X : CMP.b #$A0 : BCS .stay_alert
LDA.b #!STATE_CHASE : STA.w SprAction, X
.stay_alert
; Face Link
JSL Sprite_DirectionToFacePlayer : TYA : STA.w SprMiscC, X
; Draw with alert animation
LDA.b #$08 : STA.w SprGfx, X
RTS
}
CastleGuard_Chase:
{
; Move toward Link
LDA.b #$0C : JSL Sprite_ApplySpeedTowardsPlayer
JSL Guard_ChaseLinkOnOneAxis
JSL Sprite_Move
JSL Sprite_BounceFromTileCollision
; Check if Link is surrounded
JSR CastleGuard_CheckSurrounded : BCC .not_surrounded
; Capture Link!
LDA.b #!STATE_CAPTURE : STA.w SprAction, X
LDA.b #$60 : STA.w SprTimerC, X ; Capture animation duration
; Freeze Link
LDA.b #$17 : STA.b $5D ; Link state: captured
STZ.b $67 ; Stop Link's movement
RTS
.not_surrounded
; Continue chase
JSL Guard_ParrySwordAttacks
JSL Sprite_CheckDamageFromPlayer
RTS
}
CastleGuard_Capture:
{
; Animate capture sequence
LDA.w SprTimerC, X : BNE .animating
; Capture complete - warp Link
LDA.b #$42 ; Entrance ID for dungeon cell
JSL Oracle_CaptureAndWarp_Enhanced
RTS
.animating
; Guards surround Link
; ... capture animation logic ...
RTS
}
CastleGuard_CheckSurrounded:
{
; Count guards within range in each direction
; Return carry set if Link is surrounded
; (3+ guards within 32px, covering different quadrants)
; ... implementation ...
CLC ; Not surrounded
RTS
}
CastleGuard_SpawnReinforcements:
{
; Spawn additional castle guards off-screen
LDY.b #$00
LDX.b #$0F
.spawn_loop
LDA.w $0DD0, X : BEQ .found_slot
DEX : BPL .spawn_loop
RTS ; No free slots
.found_slot
; Spawn guard sprite
LDA.b #Sprite_CastleGuard : STA.w $0E20, X
LDA.b #$09 : STA.w $0DD0, X ; Active state
; Position off-screen based on Link's position
; ... positioning logic ...
; Set to chase state immediately
LDA.b #!STATE_CHASE : STA.w SprAction, X
INY
CPY.b #$03 : BCC .spawn_loop ; Spawn up to 3 reinforcements
RTS
}
; ... drawing routine ...
```
### Step 3: Integrate with Existing Systems
**Modify `Sprites/all_sprites.asm`:**
```asm
org $318000
%log_start("castle_guard", !LOG_SPRITES)
incsrc "Sprites/Enemies/castle_guard.asm"
%log_end("castle_guard", !LOG_SPRITES)
```
**Add SRAM Flags to `Core/sram.asm`:**
```asm
; Castle Ambush System
$7EF3D7 = CastleAmbushActive ; 01 = ambush scenario active
$7EF3D8 = HasBeenCaptured ; 01 = player has been captured before
$7EF3D9 = CaptureCount ; Number of times captured
```
**Add Constants to `Core/symbols.asm`:**
```asm
; Castle Ambush
CastleAmbushActive = $7EF3D7
HasBeenCaptured = $7EF3D8
CaptureCount = $7EF3D9
```
---
## Testing Plan
### Test Case 1: Detection
- [ ] Enter castle area with ambush flag set
- [ ] Walk near guard
- [ ] Verify probe spawns every 32 frames
- [ ] Walk into probe's path
- [ ] Verify alert sound plays
- [ ] Verify guard enters alert state
### Test Case 2: Chase
- [ ] Continue from Test Case 1
- [ ] Verify guard chases Link
- [ ] Verify reinforcements spawn
- [ ] Verify multiple guards coordinate
- [ ] Verify guards use parrying
### Test Case 3: Capture
- [ ] Let guards surround Link
- [ ] Verify capture check works
- [ ] Verify Link is frozen
- [ ] Verify capture animation plays
- [ ] Verify screen fades out
### Test Case 4: Warp
- [ ] Continue from Test Case 3
- [ ] Verify Link warps to dungeon
- [ ] Verify SRAM flag is set
- [ ] Verify Link spawns in correct room
- [ ] Verify capture count increments
### Test Case 5: Escape
- [ ] Escape from dungeon cell
- [ ] Return to castle
- [ ] Verify guards remember previous capture
- [ ] Verify harder difficulty on subsequent captures
---
## Implementation Phases
### Phase A: Core Functionality (Week 1)
- [ ] Clean up `Core/capture.asm`
- [ ] Add `Oracle_CaptureAndWarp_Enhanced`
- [ ] Test basic warp functionality
- [ ] Determine correct entrance ID for dungeon cell
### Phase B: Guard AI (Week 2)
- [ ] Create `Sprites/Enemies/castle_guard.asm`
- [ ] Implement probe detection
- [ ] Implement state machine
- [ ] Test patrol → alert → chase transitions
### Phase C: Capture Mechanics (Week 3)
- [ ] Implement surround check
- [ ] Implement capture animation
- [ ] Test capture trigger conditions
- [ ] Add sound effects
### Phase D: Integration (Week 4)
- [ ] Add SRAM flags
- [ ] Integrate with quest system
- [ ] Create dungeon escape sequence
- [ ] Test full cycle
### Phase E: Polish (Week 5)
- [ ] Add dialogue/cutscenes
- [ ] Add visual effects
- [ ] Balance difficulty
- [ ] Add achievements/tracking
---
## Entrance IDs Reference
Need to determine correct entrance for dungeon cell:
```asm
; Common dungeon entrances
$00 = Hyrule Castle (main entrance)
$04 = Hyrule Castle (throne room)
$0E = Hyrule Castle (dark passage)
$20 = Eastern Palace
$42 = Dark Palace
$?? = Custom dungeon cell (TBD)
```
**Action Required:** Find or create appropriate dungeon cell entrance
---
## Files to Create/Modify
### Create:
- [ ] `Sprites/Enemies/castle_guard.asm` - Main guard implementation
- [ ] `Docs/Features/CastleAmbush.md` - System documentation
- [ ] `Docs/Sprites/Enemies/CastleGuard.md` - Sprite documentation
### Modify:
- [ ] `Core/capture.asm` - Add enhanced version
- [ ] `Core/sram.asm` - Add SRAM flags
- [ ] `Core/symbols.asm` - Add constants
- [ ] `Sprites/all_sprites.asm` - Include castle_guard.asm
### Delete/Consolidate:
- [ ] `Sprites/Enemies/custom_guard.asm` - Consolidate into castle_guard.asm
- [ ] Remove duplicate `Oracle_CaptureAndWarp` from custom_guard.asm
---
## Questions to Resolve
1. **Entrance ID:** Which dungeon entrance should be used for the cell?
2. **Quest Integration:** When should castle ambush activate?
- After certain quest milestone?
- When Link enters specific castle area?
- Triggered by dialogue/cutscene?
3. **Difficulty Scaling:** Should capture difficulty increase after first capture?
4. **Escape Sequence:** How should the escape play out?
- Find key item?
- Stealth section?
- Fight way out?
5. **Sprite Slot:** New sprite ID or override vanilla guard ($41/$42/$43)?
---
## See Also
- `Docs/Sprites/ProbeSprites.md` - Probe detection system
- `Docs/Sprites/Enemies/Darknut.md` - Similar guard-type enemy
- `Docs/Guides/SpriteCreationGuide.md` - Sprite creation reference
- `Core/capture.asm` - Core warp functionality
- `Sprites/experimental/probe.asm` - Probe system reference