Recoil Game Tutorial
More actions
Making A Basic Recoil Game
This guide walks you through creating a minimal but complete game with the Recoil engine. By the end you will have a working game archive that loads in the engine, with a playable Commander unit, a simple weapon, and a basic Lua gadget.
No prior Spring or Recoil experience is assumed, though basic familiarity with Lua will help when you reach the scripting sections.
1. What is Recoil?
Recoil is an open-source, cross-platform real-time strategy game engine descended from Spring (which itself descended from Total Annihilation). The engine handles low-level simulation, networking, and rendering. Your game is a Lua + asset package that rides on top of the engine: you control the rules, the units, the UI, and everything else.
Out of the box the engine provides:
- Physics simulation — projectile trajectories, terrain collision, unit movement and pathfinding
- Resource system — metal and energy by default, fully customisable
- Networking — authoritative server, deterministic lock-step simulation, automatic replay recording
- Lua scripting — every mechanic is overridable via Lua gadgets and widgets
- VFS — a virtual file system that merges your game archive, maps, and basecontent into one namespace
Key terminology
| Term | Meaning |
|---|---|
| Game | Your content archive (units, Lua scripts, textures, …) loaded by the engine. |
| Map | A separate terrain archive (.smf height data + textures). Distributed independently of your game. |
| Gadget | A Lua script that runs game logic (synced or unsynced). Lives in luarules/gadgets/.
|
| Widget | A Lua script that runs UI logic. Lives in luaui/widgets/.
|
| Wupget | Informal collective term for widgets and gadgets. |
| Callin | A function in your Lua code that the engine calls on events (e.g. gadget:UnitCreated).
|
| Callout | A function the engine exposes to Lua (e.g. Spring.GetUnitPosition).
|
| VFS | Virtual File System — unified read-only view of the game archive, map archive, and basecontent. |
| Basecontent | An archive always loaded by the engine that provides default wupget handlers, water textures, and other bare necessities. |
| .sdd | A game folder whose name ends in .sdd. Treated as an uncompressed archive during development.
|
| .sdz / .sd7 | Production game archives (zip and 7-zip respectively). Faster to distribute and load than .sdd. |
| Elmo | The internal distance unit. Roughly 8 height units of map height. Used in unit defs for speed, range, etc. |
2. Prerequisites
Before starting you need:
- The Recoil engine binary. Download a pre-built release from GitHub Releases or build from source following the build documentation. The main executable is
springon Linux orspring.exeon Windows. - A map archive (
.smf/.sd7) placed in themaps/subfolder of your Recoil data directory. Any map from an existing Recoil game works for early testing. - A text editor. Any editor works. For the best experience, configure the Recoil:Lua Language Server with Recoil type stubs so you get autocomplete on all
Spring.*functions.
Your Recoil data directory typically looks like:
recoil-data/ ├── spring ← engine binary (Linux) or spring.exe (Windows) ├── spring_headless ← headless binary (no rendering; useful for automated testing) ├── spring_dedicated ← dedicated server binary ├── games/ ← your game archives / development folders go here └── maps/ ← map archives go here
3. Virtual File System
The engine loads content from several sources, merged into one Virtual File System (VFS):
- Game archive — your
.sddfolder or.sdz/.sd7file. Files here have the highest priority. - Map archive — the terrain and map-specific data. Maps can override some of your game's properties via
MapOptions.lua. - Basecontent — always loaded by the engine. Provides the default wupget handler framework, stock water textures, and other bare essentials.
Game archives can declare dependencies on other archives in modinfo.lua.
The engine merges all archives into the VFS, with your game taking priority over its dependencies.
This lets you build on top of an existing game without copying its entire asset tree.
See Recoil:VFS Basics for the full list of VFS modes available to Lua scripts.
4. Game Archive Structure
During development, use an .sdd folder. Create it inside your games/ directory:
games/
└── mygame.sdd/
├── modinfo.lua ← required: identifies your game to the engine
├── gamedata/ ← def pre/post processing, game options, rules
│ ├── modrules.lua
│ ├── unitdefs_post.lua
│ └── ModOptions.lua
├── units/ ← unit definition files (one per unit by convention)
│ └── commander.lua
├── weapons/ ← weapon definition files
│ └── laser.lua
├── features/ ← feature (prop/wreck/tree) definitions
│ └── trees.lua
├── luarules/
│ └── gadgets/ ← synced and unsynced game logic
│ └── game_end.lua
├── luaui/
│ └── widgets/ ← UI scripts
│ └── resources_display.lua
├── objects3d/ ← unit models (.dae / .obj / .s3o)
└── unittextures/ ← unit icon textures (unitname.png)
For production, compress the contents into a .sdz or .sd7 archive.
modinfo.lua must end up at the archive root — not inside a subfolder.
5. modinfo.lua
modinfo.lua is the most important file in your archive. Without it the engine will not recognise it as a game.
-- games/mygame.sdd/modinfo.lua
local modinfo = {
-- Displayed in lobbies and the engine title bar
name = "My Recoil Game",
-- Short unique identifier; no spaces, keep it stable across versions
shortName = "MRG",
description = "A minimal Recoil game for learning purposes.",
version = "0.1.0",
-- Leave empty for base games (not mods layered on top of another game)
mutator = "",
-- Other archive names this game depends on; basecontent is always loaded
depend = {},
-- 1 = game/mod (visible in lobbies), 0 = hidden
modtype = 1,
-- Faction names available in start scripts and team configuration
sides = { "MyFaction" },
-- Per-side starting unit def name
startscreens = {
MyFaction = "commander",
},
}
return modinfo
ModOptions.lua
To expose configurable options in lobbies (sliders, checkboxes, dropdowns), add gamedata/ModOptions.lua:
-- gamedata/ModOptions.lua
return {
{
key = "startmetal",
name = "Starting Metal",
desc = "Metal each player starts with.",
type = "number",
def = 1000,
min = 0,
max = 10000,
step = 100,
},
{
key = "commanderdie",
name = "Commander Death Ends Game",
desc = "Lose when your Commander is destroyed.",
type = "bool",
def = true,
},
}
Read options at runtime in gadgets with Spring.GetModOptions().
6. Unit Definitions
Unit defs are Lua files in units/ that return a table of unit type definitions.
The convention is one unit per file, where the filename matches the unit def name.
-- units/commander.lua
return {
commander = {
-- ── Display ──────────────────────────────────────────────────
name = "Commander",
description = "The starting unit.",
buildPic = "commander.png", -- icon shown in unit HUD
-- ── Model ────────────────────────────────────────────────────
objectName = "commander.dae", -- path inside objects3d/
-- ── Health ───────────────────────────────────────────────────
health = 3000,
armorType = "commander", -- armour class name
-- ── Footprint ────────────────────────────────────────────────
footprintX = 2, -- width in map squares (8 elmos each)
footprintZ = 2,
collisionVolumeScales = "30 50 30",
collisionVolumeType = "ellipsoid",
-- ── Movement ─────────────────────────────────────────────────
moveDef = {
family = "tank",
maxSlope = 36, -- steepest traversable slope in degrees
turnRate = 1024,
},
maxVelocity = 42, -- elmos/second
maxAcc = 0.5, -- elmos/second²
maxDec = 1.5,
-- ── Construction ─────────────────────────────────────────────
workerTime = 200, -- build power provided when assisting
buildCostMetal = 0, -- Commanders are spawned free by the engine
buildCostEnergy = 0,
buildTime = 1,
-- ── Economy ──────────────────────────────────────────────────
metalMake = 1, -- passive metal income per second
energyMake = 20, -- passive energy income per second
-- ── Weapons ──────────────────────────────────────────────────
weapons = {
{ def = "COMMANDER_LASER" },
},
-- ── Misc ─────────────────────────────────────────────────────
commander = true, -- marks this as the starting Commander unit
sightDistance = 660,
},
}
Notable unit def keys
| Key | Type | Notes |
|---|---|---|
name |
string | Display name shown in the UI. Not the same as the def name (the Lua table key). |
health |
number | Maximum hit points. |
armorType |
string | Armour class name. Controls how much damage weapons of each type deal. |
moveDef |
table | Inline movement class. family can be tank, kbot, hover, ship, boat, amphibious, submarine, or air.
|
maxVelocity |
number | Top speed in elmos/second. |
buildCostMetal |
number | Metal cost. Constructor must have enough free income to begin building. |
buildTime |
number | Build time in seconds at 1 build power. Actual time = buildTime / workerTime.
|
workerTime |
number | Build power this unit contributes when constructing or assisting. |
weapons |
table | Up to 3 entries; each a table with a def key naming a WeaponDef.
|
commander |
bool | Marks this unit as a Commander for lobby/engine purposes. |
extractsMetal |
number | > 0 means this unit extracts metal when placed over a metal spot. |
customParams |
table | Arbitrary string/number key-value pairs preserved by the engine for gadget use. |
For the full list of unit def keys and their effects see the Recoil Lua API documentation.
Unit types overview
Recoil games traditionally group units into several broad categories. Understanding these helps when designing your roster:
- Ground vehicles (family
tank)- Typically heavy armour, strong weapons, poor slope climbing. Sluggish turning, struggles on hills. Best for open terrain and armour-versus-armour engagements.
- K-bots / bipeds (family
kbot)- Lighter and cheaper than vehicles. Excellent slope climbing — can go where tanks cannot. Smaller profile, so they slip through narrow gaps in terrain or walls.
- Aircraft (family
air)- Ignores terrain completely. Fastest units. Roles: bombers, gunships, scouts, air superiority fighters, torpedo bombers, transports. Countered by anti-air (AA) units and structures. Expensive and fragile compared to ground units.
- Ships (family
ship/boat)- Powerful weapons and heavy armour, but confined to water. Long effective range. Very expensive. Vulnerable to submarines and aircraft with torpedoes.
- Hovercraft (family
hover)- Amphibious — traverse both land and water. Weaker than dedicated land vehicles and ships, but unique in their flexibility. Poor slope climbing.
- Amphibious units (family
amphibious)- Can operate both underwater and on the surface. Slow. Excellent for surprise attacks and on maps with mixed terrain.
- Structures / towers
- Fixed-position buildings. Includes factories, resource extractors, power generators, defence turrets, radar towers, and storage. Cannot move.
Def pre- and post-processing
The engine reads gamedata/defs.lua (provided by basecontent) to orchestrate def loading.
It runs gamedata/unitdefs_pre.lua first (populates a Shared table visible to all def files), then loads every units/*.lua, then runs gamedata/unitdefs_post.lua.
-- gamedata/unitdefs_post.lua
-- Normalise key casing so post-processing code is consistent
for _, unitDef in pairs(UnitDefs) do
lowerkeys(unitDef) -- lowerkeys() is provided by basecontent
end
-- Example: give every unit 10% bonus health
for _, unitDef in pairs(UnitDefs) do
if unitDef.health then
unitDef.health = unitDef.health * 1.1
end
end
lowerkeys() converts all keys to lowercase in-place. If your def files are consistent in their casing you don't need it, but it prevents subtle bugs when multiple contributors write defs in different styles.7. Weapon Definitions
Weapon defs follow the same pattern as unit defs — Lua files in weapons/ returning tables.
-- weapons/laser.lua
return {
COMMANDER_LASER = {
name = "Commander Laser",
description = "Short-range direct-fire laser.",
-- Weapon physics model
-- Options: Cannon, LaserCannon, MissileLauncher, AircraftBomb,
-- BeamLaser, LightningCannon, Flame, TorpedoLauncher, ...
weaponType = "LaserCannon",
-- Only engage targets in these categories
onlyTargetCategory = "SURFACE",
-- ── Visuals ──────────────────────────────────────────────────
rgbColor = "1 0.2 0.2", -- beam colour (R G B, 0-1 each)
rgbColor2 = "1 0.9 0.9", -- colour at the far end of the beam
beamtime = 1, -- visual beam length in elmos
thickness = 3,
corethickness = 0.3,
laserflaresize = 8,
-- ── Ballistics ───────────────────────────────────────────────
projectilespeed = 800, -- elmos/second
range = 350, -- elmos
reloadtime = 1.5, -- seconds between shots
burst = 1, -- shots per fire command
accuracy = 0, -- spread in degrees (0 = perfect accuracy)
-- ── Damage ───────────────────────────────────────────────────
damage = {
default = 50, -- applies to armour types not explicitly listed
},
areaOfEffect = 8, -- splash radius in elmos (0 = single target)
impulse = 0, -- knockback force
firestarter = 0, -- chance to ignite (0 = no fire)
},
}
Like unit defs, you can post-process weapon defs in gamedata/weapondefs_post.lua.
WeaponDefs inside wupgets is 0-indexed (unlike UnitDefs and FeatureDefs which are 1-indexed). Beware of for i = 1, #WeaponDefs do — this will skip the first weapon def.8. Feature Definitions
Features are non-commandable objects on the map: trees, rocks, wreckage, and any other static props. They can block pathfinding, provide reclaim value, and leave behind other features when destroyed.
-- features/trees.lua
return {
tree = {
description = "A tree.",
object = "tree.dae", -- model in objects3d/
damage = 50, -- HP before the feature is destroyed
metal = 0, -- metal gained when reclaimed
energy = 10, -- energy gained when reclaimed
blocking = true, -- blocks ground unit pathfinding
reclaimable = true,
deathFeature = "", -- feature name to spawn on death (blank = nothing)
},
}
9. Game Rules (modrules.lua)
gamedata/modrules.lua configures engine-level behaviour not driven by unit or weapon defs.
-- gamedata/modrules.lua
return {
system = {
-- Starting resources (also overridable via ModOptions or start script)
startMetal = 1000,
startEnergy = 1000,
-- Maximum units per team (0 = unlimited)
maxUnits = 1000,
-- 0 = game continues on Commander death
-- 1 = team loses when Commander dies
-- 2 = lineage mode (Commander transferred to surviving ally)
gameMode = 1,
-- Repair speed cap (% max health per second; 0 = unlimited)
repairSpeed = 0,
-- Whether resources are recovered when resurrecting a unit
resurrectHealth = false,
-- Metal recovered when reclaiming a living unit (1 = full cost)
reclaimUnitMethod = 1,
-- Whether destroyed units leave wreck features
featureDeathResurrect = true,
},
}
10. Lua Scripting
Lua is the scripting language for everything above the engine's C++ layer. The engine provides several isolated Lua environments:
| Environment | Entry point | Purpose |
|---|---|---|
| LuaRules (synced) | luarules/main.lua |
Authoritative game logic. All clients run the same code to stay in sync. |
| LuaRules (unsynced) | luarules/draw.lua |
Effects and UI driven by game logic but not part of the simulation. |
| LuaUI | luaui/main.lua |
Player-facing UI: HUD, minimap overlays, build menus, etc. |
| LuaGaia | luagaia/main.lua |
Logic for the neutral Gaia team (wrecks, map features, world events). |
| LuaIntro | luaintro/main.lua |
Loading screen and pre-game briefings. |
| LuaMenu | luamenu/main.lua |
Main menu (pre-game). Requires a menu archive. |
Basecontent provides default main.lua entry points and the wupget handler boilerplate so you can start writing gadgets and widgets without building a handler yourself.
Synced vs unsynced
- Synced code participates in the game simulation. All clients run exactly the same instructions in exactly the same order — this is what keeps the game deterministic. Only synced code can affect units, resources, or any other simulation state.
- Unsynced code runs on each client independently. It can read simulation state for display purposes, but cannot modify it. UI, rendering, and sound belong here.
Your first gadget
A gadget is a Lua file loaded by the gadget handler from luarules/gadgets/.
-- luarules/gadgets/game_end.lua
-- Ends the game when a team's Commander is destroyed.
local gadget = gadget ---@type Gadget
function gadget:GetInfo()
return {
name = "Game End Conditions",
desc = "Ends the game on Commander death.",
author = "YourName",
date = "2025",
license = "GNU GPL, v2 or later",
layer = 0,
enabled = true,
}
end
-- Only run the logic in synced mode
if not gadgetHandler:IsSyncedCode() then return end
-- Track alive Commanders by team
local commanderByTeam = {}
function gadget:UnitFinished(unitID, unitDefID, teamID)
local def = UnitDefs[unitDefID]
if def and def.isCommander then
commanderByTeam[teamID] = unitID
end
end
function gadget:UnitDestroyed(unitID, unitDefID, teamID)
if commanderByTeam[teamID] == unitID then
commanderByTeam[teamID] = nil
local allyTeamID = Spring.GetTeamAllyTeamID(teamID)
local hasCommander = false
for _, ally in ipairs(Spring.GetTeamList(allyTeamID)) do
if commanderByTeam[ally] then
hasCommander = true
break
end
end
if not hasCommander then
Spring.GameOver({ allyTeamID })
end
end
end
Your first widget
A widget is a Lua file loaded by the widget handler from luaui/widgets/.
-- luaui/widgets/resources_display.lua
-- Draws current metal and energy in the corner of the screen.
local widget = widget ---@type Widget
function widget:GetInfo()
return {
name = "Resources Display",
desc = "Shows current resource totals.",
author = "YourName",
date = "2025",
license = "GNU GPL, v2 or later",
layer = 0,
enabled = true,
}
end
function widget:DrawScreen()
local metal, _, metalIncome = Spring.GetTeamResources(Spring.GetMyTeamID(), "metal")
local energy, _, energyIncome = Spring.GetTeamResources(Spring.GetMyTeamID(), "energy")
if not metal then return end
gl.Color(1, 1, 1, 1)
gl.Text(string.format("Metal: %.0f (+%.1f/s)", metal, metalIncome), 10, 60, 14, "s")
gl.Text(string.format("Energy: %.0f (+%.1f/s)", energy, energyIncome), 10, 40, 14, "s")
end
Callins and callouts
Callins are functions in your code the engine invokes at the right moment: UnitCreated, UnitDestroyed, GameOver, DrawScreen, Update, and many more.
You can list all available callins at runtime with Script.GetCallInList().
Callouts are Spring.* functions you call into the engine: Spring.GetTeamResources, Spring.GiveOrderToUnit, Spring.Echo, etc.
The full reference is at beyond-all-reason.github.io/spring/ldoc/.
Communicating between environments
The Lua environments are isolated from each other by design. Common bridges:
| Method | Direction | Notes |
|---|---|---|
WG / GG table |
Within one environment | Widgets share the global WG table; gadgets share GG.
|
SendToUnsynced / RecvFromSynced |
Synced → Unsynced | Pass data from game logic to rendering or UI without exposing it as a callout. |
Spring.SendLuaRulesMsg / gadget:RecvLuaMsg |
LuaUI → LuaRules | UI triggers a game action beyond normal unit orders. |
Spring.SendLuaUIMsg / widget:RecvLuaMsg |
LuaUI ↔ all LuaUIs | Broadcast UI state to other players' widgets. |
Rules params (Spring.SetRulesParam) |
LuaRules → all | Expose synced values as read-only key/value pairs readable by any environment. |
See Recoil:Wupget Communication for detailed examples and best practices.
Creating user interfaces
There are two main approaches to UI in Recoil:
- OpenGL drawing — use
gl.*callouts directly (as in the widget example above) or a higher-level framework like FlowUI or Chili. - RmlUi — Recoil has built-in support for an HTML/CSS-style reactive UI framework. Recommended for complex interfaces. See Recoil:RmlUI Starter Guide.
11. Running Your Game
The engine is launched with a start script — a plain text file (conventionally .sdf) describing who is playing, which game, and which map.
Minimal start script
[GAME]
{
GameType = My Recoil Game; -- must match modinfo.lua name or shortName
MapName = my_map.smf; -- the .smf file inside the map archive
IsHost = 1;
MyPlayerName = Dev;
[PLAYER0]
{
Name = Dev;
Password = ;
Spectator = 0;
Team = 0;
}
[TEAM0]
{
TeamLeader = 0;
AllyTeam = 0;
RgbColor = 0.1 0.4 1.0;
Side = MyFaction; -- must match a side declared in modinfo.lua
StartPosX = 4000;
StartPosZ = 4000;
}
[ALLYTEAM0]
{
NumAllies = 0;
}
}
Save as game.sdf in your Recoil data directory and launch:
./spring game.sdf # Linux spring.exe game.sdf # Windows
Headless mode
For automated testing or AI-vs-AI matches without graphics:
./spring_headless game.sdf
Widgets still run in headless mode, making it useful for data-gathering scripts and CI testing.
Hot-reloading Lua scripts
While a game is running you can reload Lua environments without restarting:
/luarules reload ← reload LuaRules gadgets /luaui reload ← reload LuaUI widgets
This dramatically speeds up iteration. Keep /cheat toggled on during development so these commands are available.
12. Useful Debug Commands
In-game chat commands prefixed with / control engine and game state.
Enable cheats first with /cheat:
| Command | Effect |
|---|---|
/cheat |
Toggle cheats on/off. Required for most debug commands. |
/give [unitname] |
Spawn a unit at the cursor position. |
/destroy |
Destroy all selected units immediately. |
/invincible |
Toggle invincibility for selected units. |
/godmode |
Toggle god mode — all units obey all players. |
/speed [x] |
Set game speed multiplier (e.g. /speed 5).
|
/luarules reload |
Reload all LuaRules gadgets. |
/luaui reload |
Reload all LuaUI widgets. |
/editdefs |
Enter def-editing mode. Lets you change unit defs live via Lua assignment (development only, desyncs in multiplayer). |
Spring.Echo("msg") |
Print a message to the in-game console from any Lua environment. |
13. Resources and Economy
Recoil's economy follows the TA tradition: resources are a flow rate, not a stockpile of discrete units.
Metal
Metal is the primary construction resource, gathered by:
- Metal extractors (mexes) — units with
extractsMetal > 0placed over metal spots defined in the map. - Metal converters / makers — units that convert energy into metal at an exchange rate.
- Reclaiming — construction units can reclaim wreckage, trees, and living units/structures to recover metal.
Metal spots are defined in the map archive (as an SMF metal intensity map or a Lua table) and read with Spring.GetMetalMapSize() and related callouts.
Energy
Energy is produced by:
- Solar collectors — cheap, stable, small output.
- Geothermal plants — placed on geothermal vents for high, free output.
- Fusion reactors — high output, high cost, large explosion on death.
- Wind generators — cheap, output depends on map wind setting.
Most level-2 weapons and some movement types consume energy to fire or operate. An energy deficit stalls construction and disables energy-hungry weapons.
Storage
By default teams have a small resource cap. Build storage structures to increase it.
metalStorage and energyStorage are unit def keys you set on storage buildings.
14. Maps and Map Integration
Maps are separate archives placed in maps/.
They are not bundled with your game — players download them independently.
The map defines:
- Height terrain data (the
.smffile) - Sky, water, atmosphere settings
- Metal spot positions
- Feature placement (trees, rocks)
- Optional
mapinfo.luawith gravity, max wind, and other overrides
Reading map data from Lua
-- Inside a gadget (synced):
local mapX, mapZ = Game.mapSizeX, Game.mapSizeZ
local metalX, metalZ = Spring.GetMetalMapSize()
-- Place a unit at the start position of team 0:
local x, y, z = Spring.GetTeamStartPosition(0)
Spring.CreateUnit("commander", x, y, z, "n", 0)
Restricting map compatibility
Check map properties at startup and call Spring.GameOver if requirements are not met:
function gadget:GamePreload()
local mapOptions = Spring.GetMapOptions()
if mapOptions.myRequiredKey ~= "expectedValue" then
Spring.Echo("This map is not compatible with MyGame!")
Spring.GameOver({})
end
end
15. Packaging and Distribution
When ready to distribute, compress your .sdd folder contents:
# Linux — create .sdz (zip) cd games/mygame.sdd zip -r ../mygame-0.1.0.sdz . # Linux — create .sd7 (7-zip; better compression) cd games/mygame.sdd 7z a ../mygame-0.1.0.sd7 .
Rapid
Rapid is the standard package distribution system for Recoil games. It allows lobby clients such as BAR Lobby or Chobby to download and update games automatically. See the Rapid documentation for how to host and tag releases.
16. Next Steps
With a working minimal game, the usual next steps are:
- More unit types
- Add builders, resource extractors, power generators, turrets, and mobile combat units following the same unit def pattern. Give each its own file in
units/.
- MoveClasses
- Define movement types in
gamedata/movedefs.luato control how units traverse terrain: hover, boat, amphibious, kbot, tank, aircraft. Each family has different slope tolerance and water behaviour.
- Armour classes
- Define armour types in
gamedata/armorclasses.luaand reference them in unit defs. Then use thedamagetable in weapon defs to give different damage values against different armour classes.
- Resource extractors and converters
- A unit with
extractsMetal > 0placed over a metal spot extracts metal. Add an energy converter unit to turn metal into energy and vice versa. Add storage buildings withmetalStorage/energyStorage.
- Tech levels and factories
- A factory unit has a
buildOptionslist of unit def names it can produce. Level-2 factories can be unlocked by a prerequisite gadget once a level-2 lab is built.
- AI support
- The Skirmish AI interface lets players add bot opponents without any gadget work. For custom AI behaviour, write a [Lua AI] inside your game's Lua scripting.
- Full UI
- The basecontent widget handler gives you a functional HUD for free. For a polished interface, use RmlUI for HTML/CSS-style reactive widgets, or raw
gl.*OpenGL callouts for maximum control.
- Map creation
- Creating your own map requires MapConv (height and texture compilation) and a height editor such as SpringMapEdit. See the Recoil map documentation for the full pipeline.
- Multiplayer and dedicated servers
- Replays are recorded automatically when
RecordDemo=1is set in the start script. For large-scale multiplayer, use the dedicated binary (spring_dedicated) with an autohost such as SPADS.
17. Further Reading
On this wiki
- Recoil:VFS Basics — full documentation of the Virtual File System, modes, and load order
- Recoil:Unit Types Basics — deep dive into unit defs, post-processing, and the wupget-facing UnitDefs table
- Recoil:Widgets and Gadgets — the addon (wupget) system in full detail
- Recoil:Wupget Communication — passing data between Lua environments
- Recoil:RmlUI Starter Guide — reactive HTML/CSS-style UI for your game
- Recoil:Headless and Dedicated — server binaries and automated testing
- Recoil:Migrating From Spring — breaking changes from Spring 105 to Recoil
- Spring_guide! — the original Spring player guide (most strategy concepts still apply)
External resources
- Recoil Lua API reference — complete callout and callin documentation
- RecoilEngine on GitHub — source, releases, and issue tracker
- Basecontent source — default wupget handlers, tree defs, and other baseline content
- Spring Simple Game Tutorial — older but still broadly applicable (Spring and Recoil are highly compatible)
- SpringRTS wiki — extensive documentation covering unit defs, weapon types, movement types, and more that applies directly to Recoil