thedivergentai/godot-genre-moba
Expert blueprint for MOBA games including lane logic (minion wave spawning every 30s), tower aggro priority (hero attacking ally over minion over hero), click-to-move controls (RTS-style raycasting), hero ability systems (QWER cooldowns, mana cost), fog of war (SubViewport projections), and authoritative networking (server validates damage). Use for competitive 5v5 or arena games. Trigger keywords: MOBA, lane_manager, minion_waves, tower_aggro, click_to_move, ability_cooldowns, fog_of_war, comeback_mechanics.
npx skills add https://github.com/thedivergentai/GD-Agentic-Skills --skill godot-genre-moba
Expert blueprint for MOBAs emphasizing competitive balance and strategic depth.
multiplayer.is_server().TRANSFER_MODE_RELIABLE for continuous movement; strictly use UNRELIABLE or UNRELIABLE_ORDERED for position/velocity to prevent network congestion.MultiplayerSynchronizer and implement Interp/Client-Side Prediction for visual smoothness.get_next_path_position() calls across multiple frames.NavigationAgent paths inside _process(); strictly use _physics_process() to interact with the navigation server and avoidance systems.RenderingServer or crashing the engine.path_search_max_polygons too low in large maps; agents will stop or walk incorrectly if the limit is reached before the destination.Area2D for high-performance Fog of War LOS; strictly use nodeless physics queries (intersect_ray) to bypass node overhead.Resource scripts for data separation and memory efficiency.duplicate(true) on shared ability Resources; modifying a buff on a shared resource will affect all heroes globally.StringName (&"stunned") for pointer-speed comparisons.Vector2i and TileMapLayer to prevent precision jitter.WorkerThreadPool to maintain 60+ FPS.Callable bindings for decoupled architecture.is_equal_approx() for range, cooldown, and mana validations.| Goal | Load first | Skip / defer |
|------|------------|--------------|
| Solo lane prototype (1 hero, local waves, no peers) | tower_priority_aggro.gd, weighted_target_selector.gd, skill_shot_indicator.gd, hero_state_machine.gd | server_minion_sync, full fog grid, prediction |
| Authoritative 5v5 (dedicated/listen server) | server_minion_sync.gd, synced_ability_controller.gd, fog_visibility_check.gd + fog_grid_mask.gd, minion_worker_pathfinder.gd | Client-trusted damage, per-minion MultiplayerSynchronizer |
| Peer skill | godot-multiplayer-networking, godot-navigation-pathfinding, godot-ability-system | Inventing a second networking stack inside this genre skill |
_physics_process pathfinding. Prefer over per-minion full A* every frame.var_to_bytes match replay frames (not JSON).leash_radius.| Phase | Skills | Purpose |
|-------|--------|---------|
| 1. Control | rts-controls | Right-click to move, A-move, Stop |
| 2. AI | godot-navigation-pathfinding | Minion waves, Tower aggro logic |
| 3. Combat | godot-ability-system, godot-rpg-stats | QWER abilities, cooldowns, scaling |
| 4. Network | godot-multiplayer-networking | Authority, lag compensation, prediction |
| 5. Map | godot-3d-world-building | Lanes, Jungle, River, Bases |
| 6. Balance | godot-monte-carlo-balancer | Hero/asymmetry matrix (not sole AFK→pro) |
Do not invent inline lane_manager / minion_ai samples.
> MANDATORY reads: server_minion_sync.gd for batched wave state; minion_worker_pathfinder.gd when agent count needs WorkerThreadPool; weighted_target_selector.gd for march→combat target picks. Spawn cadence stays data/timer-driven on the server; clients render from sync arrays.
Priority: Hero attacking Ally > unit attacking Ally Hero > closest minion > closest hero.
> MANDATORY read: tower_priority_aggro.gd. Compose with weighted_target_selector.gd for group ranks.
> MANDATORY read: fog_visibility_check.gd for nodeless LoS; paint results into fog_grid_mask.gd. Never use Area2D overlap as the fog oracle.
Implementation pattern for "QWER" targeting:
Raycasting from camera to terrain.
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("move"):
var result = raycast_from_mouse()
if result:
nav_agent.target_position = result.position
Defining "Fireball" or "Hook" without unique scripts for everything.
# ability_data.gd
class_name Ability extends Resource
@export var cooldown: float
@export var mana_cost: float
@export var damage: float
@export var effect_scene: PackedScene
avoidance_enabled for minions so they flow around each other like water, rather than stacking.SubViewport with a fog texture. Paint "holes" in the texture where allies are. Project this texture onto the terrain shader.Professional implementation of match playback, network smoothing, and advanced jungle AI.
For high-performance match recording, use var_to_bytes() to serialize state dictionaries into a compressed binary format. Avoid JSON for replays to minimize disk I/O and file size.
class_name ReplayManager extends Node
var frame_history: Array[PackedByteArray] = []
func record_frame(state: Dictionary) -> void:
# Efficiently convert data to bytes
frame_history.append(var_to_bytes(state))
func save_replay(match_id: String) -> void:
var file := FileAccess.open("user://replays/" + match_id + ".dat", FileAccess.WRITE)
if file:
file.store_var(frame_history) # Stores the whole array as a variant
file.close()
func play_frame(frame_index: int) -> Dictionary:
return bytes_to_var(frame_history[frame_index])
Use Godot 4.x's built-in physics interpolation to mask network jitter. Combined with MultiplayerSynchronizer, this provides smooth hero movement even at low tick rates (15-20Hz).
class_name HeroNetSync extends CharacterBody3D
func _ready() -> void:
# Enable native engine interpolation for visual smoothness
physics_interpolation_mode = Node.PHYSICS_INTERPOLATION_MODE_ON
if is_multiplayer_authority():
setup_synchronizer()
func setup_synchronizer() -> void:
var sync := $MultiplayerSynchronizer
var config := SceneReplicationConfig.new()
# Sync position/rotation via unreliable ordered packets
config.add_property(NodePath(".:global_position"))
sync.replication_config = config
Implement a state machine for jungle monsters that monitors distance from their spawn point. If a hero draws them too far, they enter a "Leashing" state, becoming invulnerable and returning home.
class_name JungleCreep extends CharacterBody3D
@export var leash_radius: float = 12.0
@onready var spawn_pos := global_position
func _physics_process(_delta: float) -> void:
var dist_from_home := global_position.distance_to(spawn_pos)
match state:
State.CHASING:
if dist_from_home > leash_radius:
state = State.LEASHING
State.LEASHING:
# Move back to spawn_pos using NavigationAgent3D
nav_agent.target_position = spawn_pos
if global_position.distance_to(spawn_pos) < 1.0:
state = State.IDLE
health = max_health # Reset health on return
Expert Tip: Always use NavigationServer3D.map_get_iteration_id() to ensure the navigation map is fully synced before allowing AI to pathfind after spawning.
> MANDATORY for depth beyond decision trees and script catalog: moba-meta-systems-deep.md. Do NOT Load on first-pass wiring — use bundled scripts/ first.
> Progressive disclosure: open Official Documentation links only when researching a specific API;
> load Related Skills when routing work to a peer domain — do not preload the whole lattice.
Resource data with duplicate(true) so buffs never mutate shared templates.Take thedivergentai/godot-genre-moba from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.