From dt-brigid
Enemy AI for 3D MMO platformers — patrol loops, server-controlled entities, BackgroundService AI, state machines, aggro, boss patterns, and multiplayer authority
npx claudepluginhub dreamteam-hq/brigid --plugin dt-brigidThis skill uses the workspace's default tool permissions.
CrystalMagica pattern: `EnemyControllerService` inherits from `BackgroundService` and runs all patrol/combat logic server-side. The service broadcasts `CharacterAction` messages to every connected client via the existing action relay. Clients never run AI logic — they only render what the server tells them. The server is the single source of truth for enemy position, state, and behavior.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Checks Next.js compilation errors using a running Turbopack dev server after code edits. Fixes actionable issues before reporting complete. Replaces `next build`.
CrystalMagica pattern: EnemyControllerService inherits from BackgroundService and runs all patrol/combat logic server-side. The service broadcasts CharacterAction messages to every connected client via the existing action relay. Clients never run AI logic — they only render what the server tells them. The server is the single source of truth for enemy position, state, and behavior.
Key points:
BackgroundService per enemy type or zone manages a pool of entitiesSimplest behavior pattern — a repeating movement cycle:
MoveBegin(Right) — enemy starts moving rightStop — enemy haltsMoveBegin(Left) — enemy reversesPosition is tracked server-side and updated with each action broadcast. This flat loop is extensible to waypoint systems by replacing left/right with a waypoint queue and direction vectors.
Server-side finite state machine governs all enemy behavior:
Idle → Patrol → Chase → Attack → Return → Idle
Transition triggers:
The current state is broadcast to clients so they can play the correct animation. State transitions are logged for debugging.
The server maintains a threat table per enemy instance:
Bosses use a phase-based FSM layered on top of the standard state machine:
Example structure:
All AI decisions are made server-side. Clients are pure renderers for enemy entities:
CharacterAction relay as players — no separate system neededCrystalMagica uses 3D physics and 3D navigation — CharacterBody3D, Vector3, 3D collision layers. The sidescroller feel (2.5D) is a gameplay constraint, not an engine constraint. NavigationServer3D is the correct pathfinding system.
Godot's built-in server-side pathfinding for 3D environments:
NavigationServer3D for real pathfinding without rendering overheadGodot 4.6 API (not deprecated 4.0 patterns):
NavigationServer3D.map_create() / NavigationServer3D.map_set_active() for runtime nav map managementNavigationServer3D.agent_create() with NavigationAgent3D for avoidanceNavigation node — use NavigationRegion3D exclusivelyCrystalMagica is a sidescroller rendered in 3D space. Enemy AI must respect this constraint:
Vector3 with Z ignored, or project to Vector2). A full 3D sphere check would incorrectly include enemies/players on different Z layers (e.g., background lanes).CharacterBody3D.Velocity.Y), not 2D. Apply gravity as a negative Y force each tick.ChangeLane action), not free 3D movement.The EnemyControllerService (inherits BackgroundService) is the concrete implementation of all patterns above:
MoveBegin / Stop CharacterAction messages.EntitySpawn / EntityDespawn relay messages and instantiate/free scene nodes accordingly.Vector3 AuthoritativePosition per enemy. Clients never write to this — they interpolate their rendering position toward the broadcast value.CharacterAction relay pipeline as players. No enemy-specific protocol needed.EnemyControllerService per zone. The zone service lifecycle matches zone load/unload, keeping memory bounded.