CONSTRUCTION · DESTRUCTIVE STRUCTURE CHANGE

How to Destroy a Misplaced Extension Without Hitting the Wrong Structure

Use a one-time request locked to Structure ID, room, X, Y, STRUCTURE_EXTENSION and an exact confirmation phrase, verify ownership through Game.structures, stop when hostile Creeps are present, disable before destroy(), save the result, and verify later disappearance.

Verification statusChinese source article: Reviewed in full · Official docs: Checked — Structure.destroy(), Game.structures, Game.getObjectById(), hostile-room restriction and return codes · Destructive scope: Only STRUCTURE_EXTENSION is allowed; no Spawn, Storage, Terminal, Nuker or generic Structure deletion

VERIFICATION

Evidence and test status

Chinese source article
Reviewed in full
Official docs
Checked — Structure.destroy(), Game.structures, Game.getObjectById(), hostile-room restriction and return codes
Destructive scope
Only STRUCTURE_EXTENSION is allowed; no Spawn, Storage, Terminal, Nuker or generic Structure deletion
Execution boundary
OK schedules destruction; object absence and coordinate state require later observation
JavaScript syntax
Passed
Offline destruction review
Passed — request, confirmation, identity, ownership, type, room, coordinates, hostiles and ready states
Screeps Console test
Pending
Live Extension removal, hostile-room busy state, capacity impact and coordinate verification test
Pending
Last verified
July 26, 2026

Quick answer

Inspect the completed Structure read-only, then create one request containing its exact ID, expected room, X, Y, STRUCTURE_EXTENSION and the confirmation phrase DESTROY_EXTENSION. Recover the object, verify it appears in Game.structures, require every identity field to match, stop if hostile Creeps are present, set request.enabled = false, call structure.destroy() once, save the return code, and verify the ID and coordinate on a later tick.

Use the method for the correct object stage

ObjectCorrect removal method
Unfinished ConstructionSitesite.remove()
Completed Structurestructure.destroy()

Do not infer the object stage from the intended structure type. Inspect the actual object first.

Inspect the tile before creating a request

function inspectStructuresAt(roomName, x, y) {
  const room = Game.rooms[roomName];
  if (!room) {
    return [];
  }

  return room.lookForAt(
    LOOK_STRUCTURES,
    x,
    y
  ).map(structure => ({
    id: structure.id,
    owned: Boolean(Game.structures[structure.id]),
    structureType: structure.structureType,
    roomName: structure.pos.roomName,
    x: structure.pos.x,
    y: structure.pos.y,
    hits: structure.hits,
    hitsMax: structure.hitsMax
  }));
}

Ramparts and other Structures can share a tile. Do not select the first array entry and assume it is the intended Extension.

Lock ID, room, coordinates, type, and confirmation

Memory.destroyStructureRequest = {
  enabled: true,
  structureId: 'replace-with-extension-id',
  roomName: 'W1N1',
  x: 20,
  y: 20,
  expectedType: STRUCTURE_EXTENSION,
  confirmation: 'DESTROY_EXTENSION'
};

If the Structure was rebuilt, moved in a blueprint revision, or copied from another room, at least one identity check should fail.

Allow only Extension destruction

const ALLOWED_DESTROY_TYPES = new Set([
  STRUCTURE_EXTENSION
]);

Supporting another Structure type requires its own dependency and resource migration review. This example intentionally cannot destroy Spawn, Storage, Terminal, Lab, Factory, Power Spawn or Nuker.

Build a testable destruction plan

function evaluateDestroyRequest(input) {
  const request = input.request;

  if (!request || request.enabled !== true) {
    return { ready: false, reason: 'disabled' };
  }

  if (
    typeof request.structureId !== 'string'
    || typeof request.roomName !== 'string'
    || !Number.isInteger(request.x)
    || !Number.isInteger(request.y)
    || request.x < 0
    || request.x > 49
    || request.y < 0
    || request.y > 49
    || !ALLOWED_DESTROY_TYPES.has(
      request.expectedType
    )
    || request.confirmation !== 'DESTROY_EXTENSION'
  ) {
    return { ready: false, reason: 'invalid-request' };
  }

  if (!input.structure) {
    return { ready: false, reason: 'structure-missing' };
  }

  if (!input.owned) {
    return { ready: false, reason: 'not-owner' };
  }

  if (
    input.structure.structureType
    !== request.expectedType
  ) {
    return { ready: false, reason: 'type-mismatch' };
  }

  if (
    input.structure.pos.roomName
    !== request.roomName
  ) {
    return { ready: false, reason: 'room-mismatch' };
  }

  if (
    input.structure.pos.x !== request.x
    || input.structure.pos.y !== request.y
  ) {
    return { ready: false, reason: 'position-mismatch' };
  }

  if (
    !Number.isInteger(input.hostileCount)
    || input.hostileCount > 0
  ) {
    return { ready: false, reason: 'hostiles-present' };
  }

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

Complete destruction request handler

function handleDestroyExtensionRequest() {
  const request = Memory.destroyStructureRequest;
  if (!request || request.enabled !== true) {
    return { status: 'disabled' };
  }

  const structure = typeof request.structureId === 'string'
    ? Game.getObjectById(request.structureId)
    : null;
  const owned = Boolean(
    structure
    && Game.structures[structure.id]
  );
  const hostileCount = structure?.room
    ? structure.room.find(
        FIND_HOSTILE_CREEPS
      ).length
    : 0;
  const plan = evaluateDestroyRequest({
    request,
    structure,
    owned,
    hostileCount
  });

  request.checkedAt = Game.time;
  request.status = plan.reason;

  if (!plan.ready) {
    request.enabled = false;
    return { status: plan.reason };
  }

  request.enabled = false;
  request.status = 'submitted';
  request.submittedAt = Game.time;
  request.before = {
    structureId: structure.id,
    roomName: structure.pos.roomName,
    x: structure.pos.x,
    y: structure.pos.y,
    structureType: structure.structureType,
    hostileCount,
    energyCapacityAvailable:
      structure.room.energyCapacityAvailable
  };

  const result = structure.destroy();

  request.result = result;
  request.resultAt = Game.time;
  request.status = result === OK
    ? 'accepted'
    : 'failed-review-required';

  return {
    status: request.status,
    result,
    before: request.before
  };
}
module.exports.loop = function () {
  const outcome = handleDestroyExtensionRequest();

  if (
    outcome.status === 'accepted'
    || outcome.status === 'failed-review-required'
  ) {
    console.log(JSON.stringify({
      type: 'destroy-extension-request-result',
      ...outcome
    }));
  }
};

Stop when hostile Creeps are present

The official method can return ERR_BUSY while hostile Creeps are in the room. The preflight records this state clearly, but the method result remains authoritative because room state can change between inspection and resolution.

function countVisibleHostiles(room) {
  return room
    ? room.find(FIND_HOSTILE_CREEPS).length
    : 0;
}

Disable before the destructive call

request.enabled = false;
const result = structure.destroy();

A failed request must be reviewed and explicitly recreated or re-enabled. It should not fire automatically after hostiles leave or another Structure is rebuilt at the coordinate.

Verify the next tick

function verifyDestroyedExtension(request) {
  const object = Game.getObjectById(
    request.structureId
  );
  const room = Game.rooms[request.roomName];

  if (object) {
    return {
      verified: false,
      reason: 'object-still-visible'
    };
  }

  if (!room) {
    return {
      verified: false,
      reason: 'room-not-visible'
    };
  }

  const matchingExtension = room.lookForAt(
    LOOK_STRUCTURES,
    request.x,
    request.y
  ).find(structure =>
    structure.structureType
      === STRUCTURE_EXTENSION
  ) || null;

  return matchingExtension
    ? {
        verified: false,
        reason: 'extension-at-coordinate',
        structureId: matchingExtension.id
      }
    : { verified: true };
}

ID absence and coordinate inspection together distinguish the intended object from a newly built replacement.

Review room dependencies first

An Extension contributes to room.energyCapacityAvailable and can affect Spawn body plans, logistics paths, Rampart coverage and room blueprints. This guide records capacity before the call but does not promise resource refunds or automatic rebuilding.

Handle return codes

CodeMeaningReview
OKDestruction scheduledID and coordinate later
ERR_NOT_OWNERStructure is not yoursID and Game.structures
ERR_BUSYHostile Creep presentCurrent room hostiles

Debugging checklist

  • Distinguish Construction Site from Structure.
  • Inspect the tile read-only first.
  • Use the exact Structure ID.
  • Require room, X and Y matches.
  • Allow only STRUCTURE_EXTENSION.
  • Require DESTROY_EXTENSION.
  • Verify ownership through Game.structures.
  • Stop when hostiles are present.
  • Disable before the call.
  • Save the official return code.
  • Verify ID and coordinate later.

Scope and next steps

This guide does not destroy critical Structures, remove Construction Sites, migrate resources, redesign the room, calculate refunds or rebuild the Extension. Return to the English article library for construction and room-planning topics.

Frequently asked questions

Why not use structure.my alone?

The example cross-checks the recovered ID against Game.structures, which contains your Structures, and then validates the full request identity.

Why disable a request rejected because of hostiles?

Executing automatically after combat ends may no longer reflect current layout intent. Require a fresh review.

Does destroy() return construction resources?

This guide makes no refund claim. Plan dependencies and resource consequences separately before submitting the destructive request.

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.