Quick answer
Recover one owned active Lab and one owned Creep in the same room, read the Lab mineral, find the supported body type through BOOSTS, count unboosted eligible parts, limit the requested count, require LAB_BOOST_MINERAL and LAB_BOOST_ENERGY per part, enforce range 1, disable the one-time request before lab.boostCreep(), then compare body-part boost fields and Lab Store afterward.
Map the Lab mineral through BOOSTS
function getBoostBodyType(mineralType) {
if (typeof mineralType !== 'string') {
return null;
}
for (const bodyType of BODYPARTS_ALL) {
if (BOOSTS[bodyType]?.[mineralType]) {
return bodyType;
}
}
return null;
}
Do not infer the body type from the mineral name. Use the current official BOOSTS mapping.
Count eligible unboosted parts
function getEligibleBoostParts(creep, bodyType) {
return creep.body
.map((part, index) => ({ part, index }))
.filter(({ part }) =>
part.type === bodyType
&& part.hits > 0
&& !part.boost
);
}
Damaged but active parts remain eligible when hits > 0. Already boosted parts must not be counted again.
Understand body-part application order
function getExpectedBoostIndexes(
eligible,
bodyType,
count
) {
const ordered = bodyType === TOUGH
? [...eligible]
: [...eligible].reverse();
return ordered
.slice(0, count)
.map(item => item.index);
}
This prediction helps verification when bodyPartsCount is lower than the total eligible count. The API's part-order rule matters for carefully ordered combat bodies.
Calculate mineral and Energy budgets
function calculateBoostBudget(partCount) {
if (!Number.isInteger(partCount) || partCount <= 0) {
return null;
}
return {
mineral: partCount * LAB_BOOST_MINERAL,
energy: partCount * LAB_BOOST_ENERGY
};
}
The Lab must already contain the boost mineral and enough Energy. Resource hauling is a separate workflow.
Build a testable boost plan
function evaluateBoost(input) {
const { lab, creep, requestedParts } = input;
if (!lab || !creep) {
return { ready: false, reason: 'object-missing' };
}
if (lab.room.name !== creep.room.name) {
return { ready: false, reason: 'different-rooms' };
}
const mineralType = lab.mineralType;
const bodyType = getBoostBodyType(mineralType);
if (!mineralType || !bodyType) {
return { ready: false, reason: 'boost-mineral-invalid' };
}
const eligible = getEligibleBoostParts(creep, bodyType);
const count = requestedParts == null
? eligible.length
: requestedParts;
if (
!Number.isInteger(count)
|| count <= 0
|| count > eligible.length
) {
return {
ready: false,
reason: 'part-count-invalid',
eligibleCount: eligible.length
};
}
const budget = calculateBoostBudget(count);
if (
lab.store.getUsedCapacity(mineralType)
< budget.mineral
|| lab.store.getUsedCapacity(RESOURCE_ENERGY)
< budget.energy
) {
return {
ready: false,
reason: 'resources-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
};
}
Complete one-time boost example
State impact: this code may schedule one real boost and writes a request snapshot. It never retries automatically.
function getOwnedActiveLab(id) {
const lab = typeof id === 'string'
? Game.getObjectById(id)
: null;
return lab
&& lab.structureType === STRUCTURE_LAB
&& lab.my === true
&& lab.isActive() === true
? lab
: null;
}
module.exports.loop = function () {
const request = Memory.labBoostRequest;
if (!request || request.enabled !== true) {
return;
}
const lab = getOwnedActiveLab(request.labId);
const creep = typeof request.creepName === 'string'
? Game.creeps[request.creepName]
: null;
const ownedCreep = creep?.my === true
&& creep.spawning !== true
? creep
: null;
const plan = evaluateBoost({
lab,
creep: ownedCreep,
requestedParts: request.bodyPartsCount
});
request.lastCheckedAt = Game.time;
request.lastStatus = plan.reason;
if (!plan.ready) {
request.preview = {
eligibleCount: plan.eligibleCount ?? null,
mineral: plan.mineral ?? null,
energy: plan.energy ?? null
};
return;
}
request.enabled = false;
request.submittedAt = Game.time;
request.snapshot = {
labId: lab.id,
creepName: ownedCreep.name,
mineralType: plan.mineralType,
bodyType: plan.bodyType,
partCount: plan.partCount,
expectedIndexes: plan.expectedIndexes,
mineralBefore:
lab.store.getUsedCapacity(plan.mineralType),
energyBefore:
lab.store.getUsedCapacity(RESOURCE_ENERGY),
boostsBefore: ownedCreep.body.map(part =>
part.boost ?? null
)
};
const result = lab.boostCreep(
ownedCreep,
plan.partCount
);
request.result = result;
request.resultAt = Game.time;
request.status = result === OK
? 'accepted-pending-verification'
: 'failed-review-required';
};
Verify body and Store changes
function verifyBoost(request) {
const snapshot = request?.snapshot;
const creep = snapshot
? Game.creeps[snapshot.creepName]
: null;
if (!snapshot || !creep) {
return { verified: false, reason: 'snapshot-or-creep-missing' };
}
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);
return {
verified:
request.result === OK
&& changedIndexes.length === snapshot.partCount,
changedIndexes,
expectedIndexes: snapshot.expectedIndexes
};
}
The Creep may die or leave visibility before verification. Keep the request snapshot and treat that as an unavailable outcome, not automatic success.
Handle return codes
| Code | Meaning |
|---|---|
OK | Boost scheduled |
ERR_NOT_ENOUGH_RESOURCES | Mineral or Energy insufficient |
ERR_INVALID_TARGET | Creep cannot be boosted by this Lab mineral |
ERR_NOT_IN_RANGE | Creep is not adjacent |
ERR_INVALID_ARGS | Requested body-part count is invalid |
ERR_RCL_NOT_ENOUGH | Lab is inactive at the current RCL |
Debugging checklist
- Recover one owned active Lab and one owned Creep.
- Require the same room and range 1.
- Map the mineral through
BOOSTS. - Count only active unboosted eligible parts.
- Validate the requested count.
- Calculate per-part mineral and Energy.
- Predict part order for limited boosts.
- Disable before the call.
- Save body and Store snapshots.
- Verify exact changed indexes later.
Scope and next steps
This guide does not implement boost queues, Lab mineral loading, body design, combat deployment, unboosting, or multi-Lab assignment. Continue with Factory commodity production.
Frequently asked questions
Can already boosted parts be boosted again?
No. Count only unboosted eligible parts.
Can bodyPartsCount exceed eligible parts?
No. Reject that request before calling.
Why predict indexes?
Limited boosts follow a documented body-order rule and can affect combat body behavior.
Should a failed boost retry every tick?
No. Require a new reviewed request.