Use this guide when
Use this page when one role needs a body that can scale with room Energy. The body planner should answer one question: what legal body can this Spawn afford under the current policy? It should not also decide role quotas, replacement timing, Spawn priority, or whether the room is in an emergency.
Use the spawnCreep() return-code guide when a finished body already exists but the Spawn rejects it.
Define minimum, target, and emergency plans
| Plan | Purpose | Energy rule |
|---|---|---|
| Minimum | Smallest body that can perform the role | Never return a body below this capability |
| Target | Normal body for an established room | Wait when current workers make waiting safe |
| Emergency | Reduced body used to restore essential income | Scale from current energyAvailable |
A dynamic body is not automatically an emergency body. Normal replacements may wait for the target budget, while a room with no income Creep may need the smallest useful body immediately.
Current Energy and capacity answer different questions
room.energyAvailable is the Energy currently loaded in Spawns and Extensions. room.energyCapacityAvailable is the maximum those structures can hold. Capacity does not prove that the target body is affordable now.
function chooseBodyBudget(room, policy) {
if (!room || !policy) {
return { status: 'input-missing', budget: 0 };
}
const targetBudget = Math.min(
room.energyCapacityAvailable,
policy.maximumEnergy
);
if (policy.emergency === true) {
return {
status: 'emergency-budget',
budget: room.energyAvailable,
targetBudget
};
}
if (room.energyAvailable < policy.minimumEnergy) {
return {
status: 'minimum-not-affordable',
budget: room.energyAvailable,
targetBudget
};
}
return {
status: room.energyAvailable >= targetBudget
? 'target-affordable'
: 'wait-or-scale',
budget: Math.min(room.energyAvailable, targetBudget),
targetBudget
};
}
The returned status leaves the wait-or-spawn decision visible. A body function should not silently treat every partial refill as an emergency.
Validate the body parts and derive cost
const VALID_BODY_PARTS = new Set([
MOVE,
WORK,
CARRY,
ATTACK,
RANGED_ATTACK,
HEAL,
TOUGH,
CLAIM
]);
function getBodyCost(body) {
if (
!Array.isArray(body)
|| body.length < 1
|| body.some(part => !VALID_BODY_PARTS.has(part))
) {
throw new TypeError('Invalid Creep body');
}
return body.reduce(
(total, part) => total + BODYPART_COST[part],
0
);
}
Derive costs from BODYPART_COST. Hard-coded totals become easy to break when templates change.
Scale from a minimum body with complete repeat units
function buildScaledBody(input) {
const {
energyBudget,
minimumBody,
repeatUnit,
maximumParts = 50,
maximumEnergy = Infinity
} = input;
if (
!Number.isFinite(energyBudget)
|| energyBudget < 0
|| !Array.isArray(minimumBody)
|| minimumBody.length < 1
|| !Array.isArray(repeatUnit)
|| repeatUnit.length < 1
|| !Number.isInteger(maximumParts)
|| maximumParts < 1
|| maximumParts > 50
|| (
maximumEnergy !== Infinity
&& !Number.isFinite(maximumEnergy)
)
|| maximumEnergy < 0
) {
return {
status: 'invalid-input',
body: []
};
}
const minimumCost = getBodyCost(minimumBody);
const unitCost = getBodyCost(repeatUnit);
const budget = Math.min(
energyBudget,
maximumEnergy
);
if (
minimumBody.length > maximumParts
|| minimumCost > budget
) {
return {
status: 'minimum-not-affordable',
body: [],
minimumCost,
budget
};
}
const body = [...minimumBody];
let bodyCost = minimumCost;
let unitsAdded = 0;
while (
body.length + repeatUnit.length <= maximumParts
&& bodyCost + unitCost <= budget
) {
body.push(...repeatUnit);
bodyCost += unitCost;
unitsAdded += 1;
}
return {
status: bodyCost === budget
? 'budget-filled'
: 'body-ready',
body,
bodyCost,
budget,
unitsAdded,
unusedEnergy: budget - bodyCost,
spawnTime: body.length * CREEP_SPAWN_TIME
};
}
Only complete repeat units are appended. Leftover Energy is normal; spending every last unit is less important than preserving the role's body ratio and part ordering.
Keep the template specific to one role
function planWorkerBody(room, emergency) {
const budgetResult = chooseBodyBudget(room, {
emergency,
minimumEnergy: 200,
maximumEnergy: 1200
});
if (budgetResult.status === 'minimum-not-affordable') {
return {
status: budgetResult.status,
body: []
};
}
const plan = buildScaledBody({
energyBudget: budgetResult.budget,
minimumBody: [WORK, CARRY, MOVE],
repeatUnit: [WORK, CARRY, MOVE],
maximumParts: 18,
maximumEnergy: 1200
});
return {
...plan,
budgetStatus: budgetResult.status,
targetBudget: budgetResult.targetBudget
};
}
This is a general worker example, not a universal body. A fixed miner, road hauler, upgrader, reserver, healer, and attacker need different minimum capabilities, repeat units, movement ratios, caps, and part order.
Make the wait decision outside the builder
Spawn a reduced body when losing more time is more dangerous than losing efficiency. Wait for the target body when the existing workforce can keep the room stable until the refill completes. Record the reason as policy data such as income-collapse, replacement-safe-to-wait, or target-affordable; do not infer emergency status merely from low Energy.
Use the emergency recovery guide for room-collapse detection and duplicate Spawn prevention.
Validate and submit spawning separately
function submitBodyPlan(spawn, name, plan, memory) {
if (!spawn || !plan || plan.body.length < 1) {
return { status: 'plan-not-ready' };
}
const dryRunResult = spawn.spawnCreep(
plan.body,
name,
{ memory, dryRun: true }
);
if (dryRunResult !== OK) {
return {
status: 'dry-run-failed',
dryRunResult,
bodyCost: plan.bodyCost
};
}
const spawnResult = spawn.spawnCreep(
plan.body,
name,
{ memory }
);
return {
status: spawnResult === OK
? 'spawn-submitted'
: 'spawn-failed-after-dry-run',
dryRunResult,
spawnResult,
bodyCost: plan.bodyCost,
spawnTime: plan.spawnTime
};
}
OK means the Spawn accepted the request. The Creep still requires body.length × CREEP_SPAWN_TIME ticks to finish, so replacement lead time belongs in the scheduler rather than the body builder.
Body order and MOVE ratio remain policy
Two bodies with the same parts and cost can lose abilities in a different order when damaged. Repeating a unit is easy to inspect, but it is not automatically optimal. Also check how loaded non-MOVE parts generate fatigue on roads, plains, and swamps. Use the movement-ratio guide for that calculation.
Verify the plan and the Spawn on different ticks
Log the budget status, body array, cost, unused Energy, and expected spawn time before submission. On later ticks, inspect spawn.spawning and confirm the resulting Creep Memory. This revision contains static analysis and syntax checks only; live replacement and emergency-spawn observations remain pending.