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
| Object | Correct removal method |
|---|---|
Unfinished ConstructionSite | site.remove() |
Completed Structure | structure.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
| Code | Meaning | Review |
|---|---|---|
OK | Destruction scheduled | ID and coordinate later |
ERR_NOT_OWNER | Structure is not yours | ID and Game.structures |
ERR_BUSY | Hostile Creep present | Current 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.