Skip to content

Boss encounters: vampire-hunter arena fights#22

Open
partouf wants to merge 22 commits into
masterfrom
boss-encounters
Open

Boss encounters: vampire-hunter arena fights#22
partouf wants to merge 22 commits into
masterfrom
boss-encounters

Conversation

@partouf

@partouf partouf commented Jul 14, 2026

Copy link
Copy Markdown
Member

Adds boss encounters (GitHub issue #16). Every level ends in a boss arena where a vampire hunter fights the player with telegraphed ranged attacks; defeating it unlocks the Recruit Ghoul ability and opens the exit.

What's in it

Arena

  • A fixed 12×12 boss arena is appended as an extra band on top of the full-size city (total height = (level+1)*12 city + 12 arena) — the city is never shortened. Enclosed layout with a central entrance/exit gap; humans and items stay in the city.
  • Exit stays sealed until the boss is defeated.

The boss (Boss : Human)

  • Reuses a human prefab body but swaps in a custom brain — no dedicated prefab needed.
  • Large blood pool, self-heals, and hunts the player (mostly steps toward you, holds each spot ~0.8–2s), confined to the arena band.
  • Plants and faces you to shoot — never fires mid-step.
  • Defeated by draining it in melee; on death it opens the exit and grants Recruit Ghoul (once).

Area-denial attack (BossAttackController + BossProjectile)

  • Aims a line at the player, flashes a red telegraph (~0.9s), then fires an arrow down it (3 damage).
  • Purely ranged — a melee blind spot means point-blank players are never hit, so closing to drain the boss is safe from arrows.

Intro cutscene (SceneManager.BossIntroRoutine)

  • On entering the arena: player input locks, camera pans up, the boss walks in from the exit, delivers a line (BossDialogUI, advance with Confirm), then the fight begins and the camera returns.

Also

  • Fixed melee whiffing on moving targets (grid-based, movement-aware target lookup).
  • Replaced deprecated FindObjectOfType/FindObjectsOfType calls.
  • Docs updated in docs/mechanics.md.

Notes

  • Needs Unity to compile/playtest (the out-of-Unity mock build is stale/pre-existing-broken and the boss code uses Unity-only APIs). Playtested interactively during development.
  • No existing scene/prefab files were modified — all new UI/visuals are code-generated at runtime.

🤖 Generated with Claude Code

partouf and others added 22 commits July 13, 2026 23:38
Every level now ends in a fixed 12x12 boss arena (top chunk band, marked
with a new 'B' BossSpawn tile). The boss is a vampire hunter that reuses a
human prefab body but swaps in a ranged, keep-distance brain: it self-heals,
holds ~3 tiles away, and fires telegraphed volleys of arrows. A red warning
overlay flashes on the target line, then a projectile deals heavy damage on
hit.

The boss is defeated by draining it (GetResistance keeps the player on the
melee-drain path until the pool empties). Defeat seals-then-opens the level
exit and unlocks the Recruit Ghoul ability.

- Boss/BossAttackController/BossProjectile: boss brain, telegraph, projectiles
- Human: GetResistance/LoseBlood made virtual; AI extracted into Brain()
- ConstructionTypes/ChunkPresets/Map: BossSpawn tile + forced arena template
- LevelConstruction/SceneManager: spawn boss, lock exit, reward on defeat
- MapExit: respect sealed exit

Arena is kept fully passable: MapTest probes the map edges at mid-height,
which lands on the arena's bottom row at level 1, so edge walls there would
hang generation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The boss AI moved toward the player from level load, so it walked down out
of the top arena band and met the player mid-city (~8 tiles in) instead of
guarding the end of the level.

Now the boss stays idle at its spawn until the player steps into the arena
band (tracked via ArenaMinY, set to the top 12-row band), then engages. Its
movement is clamped so it never descends below the arena, and it holds fire
until the fight has begun.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The arena was overwriting the top 12-row city band, so the playable city
shrank by half (level 1 became 12 city rows instead of 24). Now the arena is
an ADDITIVE extra band: total height = (level+1)*12 city + 12 arena. Level 1
is 24 city rows + 12 arena = 36 total, exit above everything.

- LevelConstruction: cityHeight vs total height; humans/items/difficulty keyed
  to cityHeight so the sealed arena stays clean and difficulty is unchanged
- ChunkPresets: arena is now enclosed (walls + central entrance/exit gap);
  safe because the mid-height traversability probe now lands in the city
- boss ArenaMinY = cityHeight

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The boss could fire while walking away to keep its distance, and shots came
out regardless of which way it faced. Now the attack only begins when the
boss is engaged and standing still; at that point it turns to face the player
(sets facing to the shot direction) and holds position for the whole
telegraph + shot via a new IsAttacking flag that suppresses movement. Once
the shot is loosed it resumes repositioning.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The boss was only clamped at the arena's bottom edge, so when it chased the
player upward it walked straight through the centre exit gap and off the top
of the map. Added ArenaMaxY (top row of the arena band) and clamp upward
movement to it, matching the existing bottom clamp.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The keep-distance AI made the boss back away from the player, and with the
arena clamps it just parked itself in the top exit gap. Replaced it with a
random wander within the arena band: mostly random 4-directional steps, with
an occasional step toward the player when it has drifted far away so it does
not corner itself. It never deliberately retreats.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pure random wander felt aimless. The boss now holds each spot for a
randomized dwell (0.8-2.0s) then takes a single step; most steps hunt the
player on the dominant axis so it presses the attack, with an occasional
random step to stay unpredictable. Combined with the aim-and-plant shooting,
it reads as actively trying to corner and kill the player.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two bugs made attacks miss a target that was mid-step even when it visually
overlapped the player:
- the player bailed out entirely (`if (sheep.isMoving) return;`) when the
  target was moving
- target detection was a zero-length raycast at the tile centre, which whiffs
  while a target's collider is sliding between tiles

Replaced combat target detection with a movement-aware grid lookup
(GetAttackTargetAt): a target counts if the tile ahead matches either the tile
it is stepping from or to, so melee connects anywhere along its step. The
lookup also covers the boss, which isn't in the humans list. Added
MovingObject.GetDestinationPosition to expose the step's destination tile.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Unity 6 deprecated these. Use FindObjectsByType(FindObjectsSortMode.None)
where order is irrelevant (destroying all AbilityUI instances) and
FindFirstObjectByType for the single-instance lookups (Canvas, AbilityIconSet).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
At 6 damage an arrow took 60% of the player's starting 10 bloodfill and two
hits was a game over before feeding. 3 keeps it threatening but survivable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The from-or-to occupancy check counted a mover's origin tile as occupied for
the whole step, so you could still hit the boss on the tile it had almost
fully left (e.g. both moving up, you stepping into where it just stood). Now a
mover occupies the single tile its interpolated position rounds to: melee
connects once it is at least halfway onto a tile and stops on a tile it has
mostly vacated. Removed the now-unused GetDestinationPosition accessor.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When the player enters the arena, play a scripted intro before the fight:
- lock player input (GameInput.Locked) while leaving Confirm live for dialogue
- pan the camera up to frame the arena (SceneManager owns the camera during
  the cutscene; normal follow resumes after)
- the boss is no longer pre-placed; it spawns at the exit and walks in
  (Boss.InIntro) to just above the arena centre
- show the boss's line in a code-generated dialogue box (BossDialogUI),
  advanced with Confirm
- Boss.BeginFight() then engages the normal hunt brain + attacks, camera pans
  back to the player, input unlocks

SpawnBoss refactored to SpawnBossAt (returns the boss) + SpawnBossForIntro.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The area-denial volley could hit a point-blank player. Now it has a melee
blind spot: tiles within MeleeRange (1) of the boss are not telegraphed, and
the arrow only arms past that distance, so it flies harmlessly over a player
standing in the boss's melee range. Closing to drain the boss is now safe
from arrows.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
IntroTargetY now uses the recorded 'B' tile position instead of a computed
mid-arena value, so the arena template controls where the boss ends up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Introduce BossDefinition (name, dialogue, arena template, and combat/attack
tuning) and BossRoster, which picks a random boss for the level from a list
(currently a level-1 roster: Vampire Hunter, Zealous Priest, Old Stalker).

The chosen definition drives everything: its arena template feeds map
generation (ChunkTemplates.BuildArena from supplied rows), its stats configure
the Boss body and BossAttackController, and its lines drive the intro dialogue.
Add or tweak encounters by editing data, no logic changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A church's holy aura (HolyAura on ChurchH) at the arena entrance could burn
the player to death during the boss intro, because the player is frozen and
can't step out of it. Burn and ReceivePunch now no-op while GameInput.Locked,
so no aura, sun, or attack can kill a player who has no way to react. Normal
damage resumes as soon as control returns.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The boss used a random human body every run. BossDefinition now has a
BodyPrefab name, resolved against BloodPrefabs by GameObject name (falling
back to random if unset/not found), so each roster entry gets a consistent
look (Vampire Hunter=Human1, Zealous Priest=Human2, Old Stalker=Human3).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Boss bodies now resolve against a dedicated inspector-assigned BossPrefabs
array first, then fall back to the civilian BloodPrefabs, then random. Keeps
boss art out of the civilian pool while still working before BossPrefabs is
populated (the current Human1/2/3 names resolve via the BloodPrefabs fallback).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Human5 was moved into the dedicated BossPrefabs array, so the roster now
references it for all three level-1 bosses (they share the one boss body until
more boss prefabs are added).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant