CREEP LIFECYCLE · RENEWAL

How to Use renewCreep() Safely in Screeps

Calculate the TTL and Energy gained per renewal, reject CLAIM Creeps, require explicit Boost removal approval, coordinate Spawn time, move adjacent, stop at a target TTL, and handle every documented result.

Verification statusChinese source article: Reviewed in full · Official docs: Checked — renewCreep(), ticksToLive, body costs and game-loop timing · Formula check: TTL floor(600 / body size); Energy ceil(creep cost / 2.5 / body size)

VERIFICATION

Evidence and test status

Chinese source article
Reviewed in full
Official docs
Checked — renewCreep(), ticksToLive, body costs and game-loop timing
Formula check
TTL floor(600 / body size); Energy ceil(creep cost / 2.5 / body size)
Safety boundary
Renewal removes all Boosts and rejects Creeps with CLAIM parts
Source correction
Persistent renewing state keeps the mission active until targetTtl
JavaScript syntax
Passed
Offline decision review
Passed — object, TTL, CLAIM, Boost, range, Spawn, Energy and target-TTL branches
Screeps Console test
Pending
Live renewal and Boost-removal test
Pending
Last verified
July 25, 2026

Quick answer

StructureSpawn.renewCreep(creep) increases a regular Creep's remaining lifetime when the Creep is adjacent to the Spawn. The Spawn must not be spawning another Creep, the target cannot contain a CLAIM part, the Spawn must have enough of its own Energy, and every Boost on the target will be removed. Do not renew from a TTL threshold alone: calculate the step, confirm Boost loss, coordinate Spawn priority, and stop at an explicit target TTL.

Official TTL and Energy formulas

One successful renewal call uses the official formulas:

added TTL = floor(600 / body size)

Energy cost = ceil(creep cost / 2.5 / body size)

The body size is the total number of body parts, including damaged parts. The Creep cost is the original sum of the official BODYPART_COST values for every part type.

function getBodyCost(body) {
  return body.reduce((total, part) => {
    const cost = BODYPART_COST[part.type];

    if (!Number.isFinite(cost)) {
      throw new TypeError(
        'Unknown body part: ' + String(part.type)
      );
    }

    return total + cost;
  }, 0);
}

function getRenewStep(body) {
  if (!Array.isArray(body) || body.length === 0) {
    return {
      valid: false,
      reason: 'body-invalid'
    };
  }

  const bodyCost = getBodyCost(body);

  return {
    valid: true,
    reason: 'ready',
    bodySize: body.length,
    bodyCost,
    addedTicks: Math.floor(600 / body.length),
    energyCost: Math.ceil(
      bodyCost / 2.5 / body.length
    )
  };
}

A larger body gains fewer ticks per call. Expensive body parts also increase the Energy cost. These formulas describe one execution, not whether renewing is strategically efficient.

Renewal removes every Boost

The official API explicitly states that renewing removes all Boosts from the target. Inspect the body before allowing the action:

function getBoostedParts(creep) {
  return creep.body.filter(part =>
    typeof part.boost === 'string'
  );
}

This guide uses a player-defined safety flag:

allowBoostRemoval === true

The flag is not an official argument. It prevents an automatic TTL rule from silently stripping a boosted fighter, miner, or upgrader. When Boosts must remain, create a replacement instead.

Creeps with CLAIM cannot be renewed

function hasClaimPart(creep) {
  return creep.body.some(part =>
    part.type === CLAIM
  );
}

Check the complete body, not only active parts. The API rejects any target with a CLAIM part. Claimer lifetime should be managed through replacement production rather than renewal.

Choose a threshold and target TTL

A renewal threshold determines when the Creep starts returning to the Spawn. A target TTL determines when renewal stops:

const renewThreshold = 300;
const targetTtl = 1200;

These numbers are examples, not official recommendations. A useful threshold depends on:

  • travel time back to the Spawn;
  • body spawn time and the replacement lead time;
  • current Spawn queue pressure;
  • whether the Creep can leave its work position;
  • how long it should remain after renewal;
  • whether another Creep is already taking over.

A threshold that is too high wastes work and Spawn time. One that is too low may let the Creep die before it arrives. A target TTL avoids repeatedly calling until ERR_FULL.

Run read-only checks first

const spawn = Game.spawns.Spawn1;
const creep = Game.creeps.Worker1;

console.log(JSON.stringify({
  spawnFound: Boolean(spawn),
  creepFound: Boolean(creep),
  spawnOwned: spawn?.my ?? null,
  creepOwned: creep?.my ?? null,
  spawnBusy: Boolean(spawn?.spawning),
  creepSpawning: creep?.spawning ?? null,
  ticksToLive: creep?.ticksToLive ?? null,
  bodySize: creep?.body.length ?? null,
  hasClaim: creep
    ? creep.body.some(part => part.type === CLAIM)
    : null,
  boostedParts: creep
    ? getBoostedParts(creep).length
    : null,
  nearSpawn: spawn && creep
    ? creep.pos.isNearTo(spawn)
    : null,
  spawnEnergy: spawn
    ? spawn.store.getUsedCapacity(RESOURCE_ENERGY)
    : null
}));

This command does not move, renew, or change Memory. Use it to confirm the selected objects and the destructive Boost consequence before enabling a mission.

Separate the decision from the action

function evaluateRenewRequest(input) {
  const {
    creepExists,
    creepOwned,
    creepSpawning,
    ticksToLive,
    renewThreshold,
    targetTtl,
    renewMissionActive,
    hasClaimPart,
    boostedPartCount,
    allowBoostRemoval,
    isNearSpawn,
    spawnExists,
    spawnOwned,
    spawnActive,
    spawnBusy,
    spawnEnergy,
    energyCost
  } = input;

  if (!creepExists || !spawnExists) {
    return {
      ready: false,
      action: 'wait',
      reason: 'object-missing'
    };
  }

  if (!creepOwned || !spawnOwned) {
    return {
      ready: false,
      action: 'stop',
      reason: 'ownership-invalid'
    };
  }

  if (!spawnActive) {
    return {
      ready: false,
      action: 'wait',
      reason: 'spawn-inactive'
    };
  }

  if (creepSpawning) {
    return {
      ready: false,
      action: 'wait',
      reason: 'creep-spawning'
    };
  }

  if (
    !Number.isFinite(ticksToLive)
    || !Number.isFinite(renewThreshold)
    || !Number.isFinite(targetTtl)
    || renewThreshold < 0
    || targetTtl <= renewThreshold
  ) {
    return {
      ready: false,
      action: 'stop',
      reason: 'ttl-policy-invalid'
    };
  }

  if (ticksToLive >= targetTtl) {
    return {
      ready: false,
      action: 'work',
      reason: 'target-ttl-reached'
    };
  }

  if (
    renewMissionActive !== true
    && ticksToLive > renewThreshold
  ) {
    return {
      ready: false,
      action: 'work',
      reason: 'ttl-sufficient'
    };
  }

  if (hasClaimPart) {
    return {
      ready: false,
      action: 'replace',
      reason: 'claim-part-present'
    };
  }

  if (
    boostedPartCount > 0
    && allowBoostRemoval !== true
  ) {
    return {
      ready: false,
      action: 'replace',
      reason: 'boost-removal-not-confirmed'
    };
  }

  if (!isNearSpawn) {
    return {
      ready: false,
      action: 'move',
      reason: 'move-to-spawn'
    };
  }

  if (spawnBusy) {
    return {
      ready: false,
      action: 'wait',
      reason: 'spawn-busy'
    };
  }

  if (
    !Number.isFinite(spawnEnergy)
    || !Number.isFinite(energyCost)
    || spawnEnergy < energyCost
  ) {
    return {
      ready: false,
      action: 'wait',
      reason: 'spawn-energy-not-enough'
    };
  }

  return {
    ready: true,
    action: 'renew',
    reason: 'ready'
  };
}

The pure decision can be tested with ordinary objects. It does not call the server API, remove Boosts, or reserve the Spawn.

Complete renewal mission

State impact: this script writes creep.memory.renewing, may move Worker1 toward Spawn1, and may submit repeated renewal actions until the configured target TTL is reached. A successful renewal consumes Spawn Energy, occupies the Spawn action, increases TTL after tick processing, and removes all Boosts.

function getBodyCost(body) {
  return body.reduce((total, part) => {
    const cost = BODYPART_COST[part.type];

    if (!Number.isFinite(cost)) {
      throw new TypeError(
        'Unknown body part: ' + String(part.type)
      );
    }

    return total + cost;
  }, 0);
}

function getRenewStep(body) {
  if (!Array.isArray(body) || body.length === 0) {
    return null;
  }

  const bodyCost = getBodyCost(body);

  return {
    bodySize: body.length,
    bodyCost,
    addedTicks: Math.floor(600 / body.length),
    energyCost: Math.ceil(
      bodyCost / 2.5 / body.length
    )
  };
}

function runRenewMission(input) {
  const {
    spawn,
    creep,
    renewThreshold,
    targetTtl,
    allowBoostRemoval
  } = input;

  if (!spawn || !creep) {
    return {
      status: 'object-missing'
    };
  }

  if (spawn.my !== true || creep.my !== true) {
    return {
      status: 'ownership-invalid'
    };
  }

  if (!spawn.isActive()) {
    return {
      status: 'spawn-inactive'
    };
  }

  if (creep.spawning) {
    return {
      status: 'creep-spawning'
    };
  }

  if (
    !Number.isFinite(renewThreshold)
    || !Number.isFinite(targetTtl)
    || renewThreshold < 0
    || targetTtl <= renewThreshold
  ) {
    return {
      status: 'ttl-policy-invalid'
    };
  }

  const step = getRenewStep(creep.body);

  if (!step) {
    return {
      status: 'body-invalid'
    };
  }

  if (creep.body.some(part => part.type === CLAIM)) {
    return {
      status: 'claim-creep-must-be-replaced'
    };
  }

  const boostedPartCount = creep.body.filter(part =>
    typeof part.boost === 'string'
  ).length;

  if (
    boostedPartCount > 0
    && allowBoostRemoval !== true
  ) {
    return {
      status: 'boost-removal-not-confirmed',
      boostedPartCount
    };
  }

  if (!Number.isFinite(creep.ticksToLive)) {
    return {
      status: 'ttl-unavailable'
    };
  }

  if (creep.ticksToLive >= targetTtl) {
    creep.memory.renewing = false;

    return {
      status: 'target-ttl-reached',
      ticksToLive: creep.ticksToLive
    };
  }

  if (
    creep.memory.renewing !== true
    && creep.ticksToLive > renewThreshold
  ) {
    return {
      status: 'ttl-sufficient',
      ticksToLive: creep.ticksToLive
    };
  }

  if (creep.ticksToLive <= renewThreshold) {
    creep.memory.renewing = true;
  }

  if (!creep.pos.isNearTo(spawn)) {
    const moveResult = creep.moveTo(spawn, {
      range: 1,
      reusePath: 10
    });

    return {
      status: 'moving-to-spawn',
      moveResult,
      step
    };
  }

  if (spawn.spawning) {
    return {
      status: 'spawn-busy',
      step
    };
  }

  const spawnEnergy = spawn.store.getUsedCapacity(
    RESOURCE_ENERGY
  );

  if (
    !Number.isFinite(spawnEnergy)
    || spawnEnergy < step.energyCost
  ) {
    return {
      status: 'spawn-energy-not-enough',
      spawnEnergy,
      step
    };
  }

  const ticksToLiveBefore = creep.ticksToLive;
  const result = spawn.renewCreep(creep);

  return {
    status: result === OK
      ? 'renew-submitted'
      : 'renew-failed',
    result,
    step,
    ticksToLiveBefore
  };
}

module.exports.loop = function () {
  const spawn = Game.spawns.Spawn1;
  const creep = Game.creeps.Worker1;

  const outcome = runRenewMission({
    spawn,
    creep,
    renewThreshold: 300,
    targetTtl: 1200,
    allowBoostRemoval: false
  });

  if (outcome.status === 'renew-failed') {
    console.log(JSON.stringify({
      type: 'renew-creep-failed',
      spawnName: spawn?.name ?? null,
      creepName: creep?.name ?? null,
      ...outcome
    }));
  }
};

The example intentionally renews only one named Creep at one named Spawn. A real queue must decide which renewal or spawn request has priority.

Source correction: a renewal mission must remember that it has started. Checking only whether the current TTL is above the start threshold would stop the mission immediately after the first successful step. The published code uses creep.memory.renewing so the Creep continues until targetTtl, then clears the flag.

Renewal competes with spawning

The Spawn must not be busy creating another Creep. Renewal and replacement production compete for the same structure:

renew the current Creep
OR
spawn a replacement or another required role

Do not let independent modules control the same Spawn without a shared priority decision. Emergency harvesting and defense generally outrank renewing a non-critical convenience role.

When to renew and when to replace

Renewal may fitReplacement may fit
The Creep works close to the Spawn.The Creep is in a remote room.
The existing body still matches the task.A newer body design is needed.
The Spawn has spare time.The Spawn queue requires centralized timing.
No important Boost must be preserved.The Creep has CLAIM or valuable Boosts.
Leaving work briefly is acceptable.A smooth handoff is required.

Renewal is not the default best choice. It trades Spawn time, Energy, travel, and possible Boost loss for continued use of the current body.

Return-code troubleshooting

CodeLikely causeAction
OKThe renewal was scheduled.Read TTL and Spawn Energy on the next tick.
ERR_NOT_OWNERThe Spawn or Creep is not yours.Validate both objects.
ERR_BUSYThe Spawn is creating another Creep.Use one shared Spawn queue.
ERR_NOT_ENOUGH_ENERGYThe Spawn lacks enough Energy.Compare its Store with the calculated step cost.
ERR_INVALID_TARGETThe target is not a Creep or contains CLAIM.Check the object and full body.
ERR_FULLThe target timer cannot be increased further.Stop at a lower target TTL.
ERR_NOT_IN_RANGEThe Creep is not adjacent.Move to range 1 and retry later.
ERR_RCL_NOT_ENOUGHThe Spawn is inactive at the current RCL.Check Controller and isActive().

Verify TTL and Energy on the next tick

OK means the operation was scheduled. The current object properties are not a verified post-action state during the same script execution. Record a before snapshot, then inspect the following tick:

console.log(JSON.stringify({
  tick: Game.time,
  creepName: creep.name,
  ticksToLive: creep.ticksToLive,
  spawnEnergy: spawn.store.getUsedCapacity(
    RESOURCE_ENERGY
  ),
  boostedParts: getBoostedParts(creep).length
}));

This article has not performed a live renewal or Boost-removal test, so those Verification fields remain Pending.

Debugging checklist

  • Confirm the Spawn and Creep names, ownership, and current visibility.
  • Reject a Creep that is still spawning.
  • Calculate one renewal step from the complete body.
  • Reject any body containing CLAIM.
  • Require explicit approval before removing Boosts.
  • Choose both a return threshold and a target TTL.
  • Move to an adjacent square and save the movement result.
  • Check that the Spawn is active and not spawning.
  • Check the selected Spawn's own Energy Store.
  • Save the renewCreep() result and verify the next tick.
  • Coordinate renewal with emergency and replacement spawning.
  • Use the error-code reference for shared constants.

Scope and next steps

This guide does not implement a multi-Creep renewal queue, compare long-term resource efficiency, reserve traffic around the Spawn, reapply Boosts, route remote Creeps home, renew Power Creeps, or schedule several Spawns. Continue with safe Creep recycling.

Frequently asked questions

How many ticks does one renewal add?

floor(600 / body size), if the API accepts the action and the timer is not full.

Does renewal use Extension Energy?

This guide checks the selected Spawn's Store because the official method requires the Spawn to have enough Energy.

Can renewal preserve Boosts?

No. The method removes all Boosts.

Should every low-TTL Creep be renewed?

No. Travel, CLAIM, Boosts, queue pressure, body upgrades, and handoff requirements often favor replacement.

Official documentation

SOURCE AND SCOPE

Review the source, evidence, or next system

This English guide is rewritten for a focused search intent while preserving the technical scope and verification boundaries of its Chinese source. Live-room evidence is claimed only when the verification record says it exists.