Quick answer: Use lab.boostCreep(creep, bodyPartsCount) when the target Creep is adjacent to the owned Lab, the Lab mineral can boost eligible parts on that Creep, and the Lab has enough mineral and Energy. Omit bodyPartsCount to boost all eligible parts; provide it to limit how many matching parts are boosted.
Return codes: OK means the operation was scheduled successfully. ERR_NOT_FOUND means the Lab mineral cannot boost any matching Creep body part; ERR_NOT_ENOUGH_RESOURCES means mineral or Energy is short; ERR_INVALID_TARGET means the target is not a valid Creep; ERR_NOT_IN_RANGE means it is too far away. Ownership and RCL failures return ERR_NOT_OWNER and ERR_RCL_NOT_ENOUGH.
Verify afterward: Do not turn OK into a body-change claim. Preserve the exact Creep ID and expected body-part indexes, then compare the resulting part.boost values and Lab Store deltas after the tick is processed.
Use this guide when
Use this workflow when a Lab should boost a known Creep and you need to prove which body entries changed. Two boundaries matter more than the usual “mineral + Energy + range” checklist: the current engine does not require an eligible body entry to have hits > 0, and an accepted request does not by itself prove every requested part was actually boosted.
Separate the API contract from stricter project policy
| Boundary | What the current engine does |
|---|---|
| Lab ownership | The Lab that calls boostCreep() must be yours. |
| Target ownership | The API validates a Creep target, range, spawning state and eligibility; target ownership is not the engine's ownership precondition. |
| Eligible part damage | Eligibility is based on body type, boost mapping and whether the entry is already boosted. It does not filter on part.hits > 0. |
| Requested part count | If bodyPartsCount exceeds eligible entries, the call can return ERR_NOT_FOUND. |
| Resource precheck | The API requires enough mineral and Energy to process at least one eligible part; the processor then continues while resources remain. |
For your own empire, requiring creep.my === true is usually a good safety policy. It is still project policy, not the same thing as the method's ownership check.
Count engine-eligible entries without a hits filter
function getBoostBodyType(mineralType) {
if (typeof mineralType !== 'string') return null;
for (const bodyType of BODYPARTS_ALL) {
if (BOOSTS[bodyType]?.[mineralType]) {
return bodyType;
}
}
return null;
}
function getEligibleBoostParts(creep, bodyType, mineralType) {
return creep.body
.map((part, index) => ({ part, index }))
.filter(({ part }) =>
part.type === bodyType
&& !part.boost
&& BOOSTS[part.type]?.[mineralType]
);
}
Do not add part.hits > 0 to this engine-eligibility count. A destroyed WORK, MOVE, TOUGH, or other matching body entry can still be selected by the current boost processor. “Active body part” is a different concept used by APIs such as getActiveBodyparts(); it is not the boost eligibility rule.
Predict the body indexes the processor will visit
function getExpectedBoostIndexes(
eligible,
bodyType,
count
) {
const ordered = bodyType === TOUGH
? [...eligible]
: [...eligible].reverse();
return ordered
.slice(0, count)
.map(item => item.index);
}
The current processor handles TOUGH from left to right and other body types from right to left. This matters when bodyPartsCount is smaller than the eligible set or when damaged body entries are mixed with intact ones.
Choose between API acceptance and an all-or-nothing project budget
The method's acceptance check and a production scheduler's desired guarantee are not identical. If your scheduler wants exactly N parts or nothing, preflight the full budget yourself:
function getStrictBoostBudget(bodyPartsCount) {
if (
!Number.isInteger(bodyPartsCount)
|| bodyPartsCount <= 0
) {
return null;
}
return {
mineral: LAB_BOOST_MINERAL * bodyPartsCount,
energy: LAB_BOOST_ENERGY * bodyPartsCount
};
}
This full-N requirement is a project safeguard. The current API validation only needs enough mineral and Energy for at least one part before accepting the intent. During processing, each boosted entry consumes one LAB_BOOST_MINERAL and one LAB_BOOST_ENERGY unit bundle until the requested count, eligible set, or available resources stop the loop.
Build a strict boost plan before changing state
function buildStrictBoostPlan(lab, creep, bodyPartsCount) {
if (!lab || !creep || creep.spawning === true) {
return { ready: false, reason: 'object-unavailable' };
}
const mineralType = lab.mineralType;
const bodyType = getBoostBodyType(mineralType);
if (!mineralType || !bodyType) {
return { ready: false, reason: 'boost-mineral-invalid' };
}
const eligible = getEligibleBoostParts(
creep,
bodyType,
mineralType
);
const count = bodyPartsCount == null
? eligible.length
: bodyPartsCount;
if (
!Number.isInteger(count)
|| count <= 0
|| count > eligible.length
) {
return {
ready: false,
reason: 'part-count-invalid',
eligibleCount: eligible.length
};
}
const budget = getStrictBoostBudget(count);
if (
lab.store.getUsedCapacity(mineralType) < budget.mineral
|| lab.store.getUsedCapacity(RESOURCE_ENERGY) < budget.energy
) {
return {
ready: false,
reason: 'strict-budget-insufficient',
mineralType,
bodyType,
...budget
};
}
if (!lab.pos.isNearTo(creep)) {
return { ready: false, reason: 'creep-out-of-range' };
}
return {
ready: true,
reason: 'ready',
mineralType,
bodyType,
partCount: count,
expectedIndexes: getExpectedBoostIndexes(
eligible,
bodyType,
count
),
...budget
};
}
If you intentionally allow partial boosting, do not use this strict budget gate. Instead, model “accepted but fewer parts changed” as an explicit outcome.
Snapshot Creep identity, body state, and Lab resources before one call
function submitBoostRequest(request) {
const lab = Game.getObjectById(request.labId);
const creep = Game.getObjectById(request.creepId);
if (
!lab
|| lab.structureType !== STRUCTURE_LAB
|| lab.my !== true
|| lab.isActive() !== true
|| !creep
|| creep.spawning === true
) {
return { status: 'object-unavailable' };
}
// Owning the target Creep is a project safety policy here.
if (creep.my !== true) {
return { status: 'target-not-owned-by-policy' };
}
const plan = buildStrictBoostPlan(
lab,
creep,
request.bodyPartsCount
);
if (!plan.ready) {
return { status: plan.reason };
}
request.enabled = false;
request.snapshot = {
labId: lab.id,
creepId: creep.id,
creepName: creep.name,
bodyLength: creep.body.length,
mineralType: plan.mineralType,
bodyType: plan.bodyType,
partCount: plan.partCount,
expectedIndexes: plan.expectedIndexes,
boostsBefore: creep.body.map(part => part.boost ?? null),
mineralBefore:
lab.store.getUsedCapacity(plan.mineralType),
energyBefore:
lab.store.getUsedCapacity(RESOURCE_ENERGY),
exclusiveWindow: request.exclusiveWindow === true
};
const result = lab.boostCreep(creep, plan.partCount);
request.result = result;
request.resultAt = Game.time;
request.status = result === OK
? 'accepted-pending-boost'
: 'failed-review-required';
return { status: request.status, result };
}
Using the Creep ID as well as the name avoids silently verifying a replacement Creep that reused the same name after the original died.
Verify exact body indexes and Lab Store deltas
function verifyBoost(request) {
const snapshot = request?.snapshot;
if (!snapshot || request.result !== OK) {
return { status: 'submission-not-accepted' };
}
if (snapshot.exclusiveWindow !== true) {
return { status: 'not-exclusive' };
}
const lab = Game.getObjectById(snapshot.labId);
const creep = Game.getObjectById(snapshot.creepId);
if (!lab || !creep) {
return { status: 'object-unavailable' };
}
if (
creep.name !== snapshot.creepName
|| creep.body.length !== snapshot.bodyLength
) {
return { status: 'body-identity-mismatch' };
}
const changedIndexes = creep.body
.map((part, index) => ({
index,
before: snapshot.boostsBefore[index],
after: part.boost ?? null
}))
.filter(item => item.before !== item.after)
.map(item => item.index);
const mineralDelta =
lab.store.getUsedCapacity(snapshot.mineralType)
- snapshot.mineralBefore;
const energyDelta =
lab.store.getUsedCapacity(RESOURCE_ENERGY)
- snapshot.energyBefore;
const expectedMineralDelta =
-LAB_BOOST_MINERAL * snapshot.partCount;
const expectedEnergyDelta =
-LAB_BOOST_ENERGY * snapshot.partCount;
const exactIndexes =
changedIndexes.length === snapshot.expectedIndexes.length
&& changedIndexes.every((index, position) =>
index === snapshot.expectedIndexes[position]
);
if (
exactIndexes
&& mineralDelta === expectedMineralDelta
&& energyDelta === expectedEnergyDelta
) {
return {
status: 'verified-exact-boost',
changedIndexes,
expectedIndexes: snapshot.expectedIndexes,
mineralDelta,
energyDelta
};
}
return {
status: changedIndexes.length > 0
? 'partial-or-ambiguous-boost'
: 'pending-no-body-delta',
changedIndexes,
expectedIndexes: snapshot.expectedIndexes,
mineralDelta,
energyDelta
};
}
In the strict workflow, OK plus a later partial body change is evidence that the all-or-nothing assumptions were violated by another actor, resource change, or unsupported timing assumption. In a deliberately permissive workflow, partial change can simply be the expected processor outcome when the Lab lacked full resources for the requested count.
Do not collapse partial change into success
| Status | Meaning |
|---|---|
accepted-pending-boost | The method returned OK; body and Store evidence is still pending. |
body-identity-mismatch | The observed Creep is not the same body snapshot the request targeted. |
partial-or-ambiguous-boost | Some body entries changed, but not the exact indexes and resource signature expected by the strict plan. |
verified-exact-boost | Exact targeted indexes changed and the Lab lost exactly the matching mineral and Energy budget. |
Preserve the current return-code boundary
Documented results include ERR_NOT_OWNER, ERR_NOT_FOUND, ERR_NOT_ENOUGH_RESOURCES, ERR_INVALID_TARGET, ERR_NOT_IN_RANGE, and ERR_RCL_NOT_ENOUGH. In particular, ERR_NOT_FOUND covers a Lab mineral that cannot boost any eligible part on the target, including an excessive bodyPartsCount request against the eligible set.
Keep Lab loading, queues, and combat policy separate
This page owns one boost request and its evidence. Lab mineral loading, boost queues, boost selection strategy, body design, combat deployment, and unboosting should be separate systems. The verification layer should remain useful even when those policies change.