Wizard vs zombie horde arena brawler. The last living wizard holds back endless waves — every hit from a wizard zombie corrupts your spells, making them more powerful but pushing you closer to transformation.
Design & studio: ibrews/spellrot — game concept, design docs, 49-agent Claude Code studio
Engine: Unreal Engine 5.7
Base: Third Person Template + ECABridge MCP plugin
- Open
ThirdPersonClass.uprojectin UE 5.7 and hit Play (Lvl_ThirdPerson). Enemies (BP_Enemy) spawn fromBP_WaveSpawneractors and chase you. Each enemy that reaches you applies 100 damage which raisesCorruptionLevelby 0.25. After 4 hits you ragdoll and the level restarts after 3s. - Walk into a
BP_KOTHZone(yellow cube). On overlap it callsApplyCleanseon you, resettingCorruptionLevelto 0. Walk out to deactivate. - Left-click to fire a line trace. If it hits
BP_Enemy,ApplyDamageruns the enemy'sReceiveAnyDamagewhich casts back to you and subtracts 0.1 fromCorruptionLevel, then ragdoll-and-recovers the enemy. - Inspect the
WBP_HUDwidget — it has aCorruptionTextfield and aCurrentCorruptionfloat variable. The Tick event readsCurrentCorruptionand writes the formatted string toCorruptionText. To wire up display in PIE, add aCreateWidget(WBP_HUD)→AddToViewportchain inBP_PlatformingGameModeBeginPlay (currently empty — TODO). - Run ECABridge (
ECABridge.GenerateClientConfig ClaudeCodein the editor console) then open Claude Code in this directory to drive the editor via MCP.
- Unreal Engine 5.7 required (Epic Games Launcher)
- Open
ThirdPersonClass.uproject— accept any "missing modules" prompt - For ECABridge (AI-assisted development):
Then in terminal:
# In UE editor console: ECABridge.GenerateClientConfig ClaudeCodeclaude mcp list # should show unreal-ecabridge
| Mechanic | File | Notes |
|---|---|---|
| Corruption accumulation | BP_PlatformingCharacter |
ReceiveAnyDamage → CorruptionLevel += 0.25; branch on >= 1.0 → ragdoll + delay 3s + OpenLevel("/Game/ThirdPerson/Lvl_ThirdPerson") |
| Cleanse zone reset | BP_KOTHZone |
BeginOverlap casts to BP_PlatformingCharacter, sets IsActive=true, calls ApplyCleanse on player. EndOverlap clears IsActive. |
| Fireball | BP_PlatformingCharacter |
IA_Fire (Started) → LineTraceByChannel → branch on hit → cast to BP_Enemy → ApplyDamage(100) |
| Enemy damage relay | BP_Enemy |
ReceiveAnyDamage → cast to player → CorruptionLevel -= 0.1 → RagdollAndRecover (2s ragdoll then restore) |
| Trail color shift | BP_PlatformingCharacter |
BeginPlay activates Trail_L/Trail_R Niagara; Tick lerps User.Color parameter from base to magenta as corruption rises |
| Wave spawning | BP_WaveSpawner |
Timer calls SpawnWave every SpawnInterval seconds, spawns BP_Enemy at spawner transform. 3 spawner instances in level. |
| HUD widget | WBP_HUD |
Static ScoreText + dynamic CorruptionText bound to CurrentCorruption member float via Tick. Wiring to gameplay still TODO. |
- HUD spawn: Add
CreateWidget(WBP_HUD) → AddToViewporttoBP_PlatformingGameModeBeginPlay.CreateWidgetis a K2 macro and cannot be added through ECABridge'sadd_blueprint_function_node— needs manual editor work. - HUD ↔ player wiring: When
CorruptionLevelchanges on player, push value toHUDWidget.CurrentCorruption. RequiresHUDWidgetreference variable on player + setter calls. - Wave scaling: Increment
WaveCounteachSpawnWaveand use it to spawn additional enemies.lisp_to_blueprintkeeps dropping the actor class onBeginDeferredActorSpawnFromClasswhen nested in branches — needs manual surgery. - Enemy color tint: Visual distinction for fast/tanky variants.
- Score display on death: Kills + time survived on game-over screen.
See the design repo for the full pattern catalog: docs/engine-reference/unreal/ecabridge-patterns.md
lisp_to_blueprintcross-BP variable set:(set CorruptionLevel ...)inside a(cast)body does NOT properly target the cast result — it tries to resolve on the casting BP. Workaround: define a custom event on the target BP that does the set, then call it from the casting BP usingadd_blueprint_function_nodewithtarget_classpointing to the target's_Cclass.(call FunctionName)inside(cast): Breaks the cast's Object pin (becomes wildcard) and the compile fails. Use surgicaladd_blueprint_function_node+connect_blueprint_nodesinstead.(print float)in lisp: Auto-resolves toPrintStringexpecting a string, fails with float-to-string compile error. Workaround: drop the print or pre-convert.CreateWidgetnot addable via function_node: It's aK2Node_CreateWidgetmacro, not a regular function. ECABridge has no surface for it yet — must add in editor manually.- Always reload from git after a broken
lisp_to_blueprintrebuild:delete_loaded_assetunloads the asset and orphans the in-memory state.git checkout+scan_paths_synchronous(force_rescan=True)restores cleanly.