Quick answer
Recover the configured owned active Power Spawn, keep processing behind an explicit enabled switch, determine the planned Power amount from the base rate and active PWR_OPERATE_POWER effect, require enough RESOURCE_POWER and plannedPower * POWER_SPAWN_ENERGY_RATIO Energy, protect a configurable room Energy reserve, save Game.gpl.progress and Store snapshots, call processPower(), and verify later deltas instead of treating OK as completed processing.
Calculate the base processing ratio
Without an active speed effect, one normal call processes the base amount. Use the official Energy ratio constant for the resource budget:
function getPowerProcessingEnergy(plannedPower) {
if (
!Number.isInteger(plannedPower)
|| plannedPower <= 0
) {
return null;
}
return plannedPower * POWER_SPAWN_ENERGY_RATIO;
}
Keeping the ratio in one constant-backed calculation prevents an old copied number from drifting away from the game data.
Read PWR_OPERATE_POWER safely
function getOperatePowerEffect(powerSpawn) {
const effects = Array.isArray(powerSpawn?.effects)
? powerSpawn.effects
: [];
return effects.find(effect =>
effect.effect === PWR_OPERATE_POWER
) || null;
}
function getPlannedPowerAmount(powerSpawn) {
const effect = getOperatePowerEffect(powerSpawn);
if (!effect || !Number.isInteger(effect.level)) {
return 1;
}
const values = POWER_INFO[PWR_OPERATE_POWER]?.effect;
const extra = Array.isArray(values)
? values[effect.level - 1]
: null;
return Number.isFinite(extra)
? 1 + extra
: 1;
}
The base amount remains one, and the current effect contributes additional processing capacity. If the effect or current POWER_INFO shape is unavailable, the function falls back to the base plan and still trusts the official return code.
Use an explicit long-running switch
Power processing is different from an irreversible market purchase. It is usually a repeated production task, so an explicit persistent switch is appropriate:
Memory.powerProcessing = {
enabled: true,
powerSpawnId: 'replace-with-power-spawn-id',
energyReserve: 100000
};
The switch does not bypass checks. Every tick still refreshes the structure, effect, Power, local Energy, room stock and reserve. Set enabled to false to stop further calls.
Protect a room Energy reserve
Checking only the Power Spawn's local Energy answers whether the next call can execute, but not whether the room should spend that Energy. This simple budget counts Storage, Terminal and Power Spawn Energy:
function getRoomEnergyStock(room, powerSpawn) {
const storageEnergy = room.storage
? room.storage.store.getUsedCapacity(
RESOURCE_ENERGY
)
: 0;
const terminalEnergy = room.terminal
? room.terminal.store.getUsedCapacity(
RESOURCE_ENERGY
)
: 0;
const localEnergy = powerSpawn.store.getUsedCapacity(
RESOURCE_ENERGY
);
return storageEnergy + terminalEnergy + localEnergy;
}
room.powerSpawn is not a standard Room property. Recover the structure by ID and pass it explicitly. The reserve protects other room needs such as spawning, defense, upgrading, Terminal transfers, Labs and Factory production.
Build a testable processing plan
function evaluatePowerProcessing(input) {
if (input.enabled !== true) {
return { ready: false, reason: 'disabled' };
}
if (
!Number.isInteger(input.plannedPower)
|| input.plannedPower <= 0
) {
return { ready: false, reason: 'invalid-plan' };
}
const energyRequired = getPowerProcessingEnergy(
input.plannedPower
);
if (!Number.isFinite(energyRequired)) {
return { ready: false, reason: 'invalid-energy-plan' };
}
if (input.powerAvailable < input.plannedPower) {
return {
ready: false,
reason: 'power-shortage',
powerRequired: input.plannedPower
};
}
if (input.localEnergy < energyRequired) {
return {
ready: false,
reason: 'power-spawn-energy-shortage',
energyRequired
};
}
const reserve = Number.isFinite(input.energyReserve)
? Math.max(0, input.energyReserve)
: 0;
if (input.roomEnergyStock - energyRequired < reserve) {
return {
ready: false,
reason: 'room-energy-reserve',
energyRequired,
reserve
};
}
return {
ready: true,
reason: 'ready',
plannedPower: input.plannedPower,
energyRequired,
roomEnergyAfter:
input.roomEnergyStock - energyRequired
};
}
The reserve is a site strategy. The official API does not choose whether the room can economically afford the processing.
Complete controlled processing example
function getOwnedActivePowerSpawn(id) {
const structure = typeof id === 'string'
? Game.getObjectById(id)
: null;
if (
!structure
|| structure.structureType !== STRUCTURE_POWER_SPAWN
|| structure.my !== true
|| structure.isActive() !== true
) {
return null;
}
return structure;
}
function runPowerProcessing(config) {
if (!config || config.enabled !== true) {
return { status: 'disabled' };
}
const powerSpawn = getOwnedActivePowerSpawn(
config.powerSpawnId
);
if (!powerSpawn) {
return { status: 'power-spawn-unavailable' };
}
const plannedPower = getPlannedPowerAmount(
powerSpawn
);
const powerAvailable = powerSpawn.store.getUsedCapacity(
RESOURCE_POWER
);
const localEnergy = powerSpawn.store.getUsedCapacity(
RESOURCE_ENERGY
);
const roomEnergyStock = getRoomEnergyStock(
powerSpawn.room,
powerSpawn
);
const plan = evaluatePowerProcessing({
enabled: config.enabled,
plannedPower,
powerAvailable,
localEnergy,
roomEnergyStock,
energyReserve: config.energyReserve
});
config.lastCheckedAt = Game.time;
config.lastStatus = plan.reason;
if (!plan.ready) {
config.lastPlan = plan;
return { status: plan.reason, plan };
}
config.lastBefore = {
gameTick: Game.time,
gplProgress: Game.gpl.progress,
power: powerAvailable,
energy: localEnergy,
roomEnergyStock,
plannedPower: plan.plannedPower,
plannedEnergy: plan.energyRequired
};
const result = powerSpawn.processPower();
config.lastResult = result;
config.lastResultAt = Game.time;
config.lastStatus = result === OK
? 'processing-scheduled'
: 'processing-rejected';
return {
status: config.lastStatus,
result,
plan,
powerSpawnId: powerSpawn.id
};
}
module.exports.loop = function () {
Memory.powerProcessing ??= {
enabled: false,
powerSpawnId: null,
energyReserve: 100000
};
const outcome = runPowerProcessing(
Memory.powerProcessing
);
if (
outcome.status === 'processing-rejected'
|| Game.time % 100 === 0
) {
console.log(JSON.stringify({
type: 'power-processing-status',
...outcome
}));
}
};
The default is disabled. Configure the real Power Spawn ID and reserve, review the plan, and enable it explicitly.
Verify GPL and Store deltas
On a later tick, compare the stored before snapshot with current Game.gpl.progress, Power Spawn Power, Power Spawn Energy and active effect. Other Power Spawns can also change account GPL, so a GPL delta alone does not identify which structure caused it.
function inspectPowerProcessing(config) {
const before = config?.lastBefore;
const powerSpawn = getOwnedActivePowerSpawn(
config?.powerSpawnId
);
if (!before || !powerSpawn) {
return null;
}
return {
submittedAt: before.gameTick,
gplProgressBefore: before.gplProgress,
gplProgressNow: Game.gpl.progress,
powerBefore: before.power,
powerNow: powerSpawn.store.getUsedCapacity(
RESOURCE_POWER
),
energyBefore: before.energy,
energyNow: powerSpawn.store.getUsedCapacity(
RESOURCE_ENERGY
),
currentEffect: getOperatePowerEffect(powerSpawn)
};
}
Handle return codes
| Code | Meaning | Review |
|---|---|---|
OK | Processing scheduled | GPL and Store later |
ERR_NOT_OWNER | Power Spawn not yours | ID and ownership |
ERR_NOT_ENOUGH_RESOURCES | Power or Energy missing | Planned amount and ratio |
ERR_INVALID_ARGS | Invalid processing state | Current structure and constants |
ERR_RCL_NOT_ENOUGH | Structure inactive | RCL and isActive() |
Debugging checklist
- Require an explicit enabled switch.
- Recover the Power Spawn by ID.
- Check ownership and activity.
- Read the active
PWR_OPERATE_POWEReffect. - Resolve the effect through current
POWER_INFO. - Calculate Energy with
POWER_SPAWN_ENERGY_RATIO. - Check local Power and Energy.
- Protect the configured room reserve.
- Save GPL and Store before the call.
- Keep the official return code.
- Verify later deltas without overclaiming causality.
Scope and next steps
This guide does not transport Power, schedule a Power Creep, decide GPL strategy, forecast room Energy income, coordinate multiple Power Spawns, or optimize the reserve. Return to the English article library for related resource and logistics guides.
Frequently asked questions
Why is the default switch disabled?
Continuous processing consumes real room Energy. A real structure ID and reviewed reserve must be configured before enabling it.
Why count Storage, Terminal and local Energy?
It provides a simple room-level affordability check instead of testing only whether the Power Spawn can execute one more call.
Can POWER_INFO be hard-coded?
Reading the current constant keeps effect levels coupled to current game data and makes fallback behavior explicit.