Quick answer
creep.pickup(resource) works only on a dropped Resource object. Filter FIND_DROPPED_RESOURCES for RESOURCE_ENERGY, reject zero or invalid amounts, confirm the Creep has active CARRY capacity, and choose a reachable target. If pickup() returns ERR_NOT_IN_RANGE, move within range 1 and retry on a later tick.
pickup() is only for ground Resource objects
| Resource location | API |
|---|---|
Ground Resource | creep.pickup(resource) |
| Container or Storage | creep.withdraw(target, resourceType) |
| Tombstone or Ruin Store | creep.withdraw(target, resourceType) |
| Source | creep.harvest(source) |
Use the Container-withdrawal guide for stored Energy. Passing a ground Resource to withdraw() uses the wrong object type.
Dropped resources decay
The Chinese source article records the official ground-resource decay rule as:
ceil(amount / 1000) per tick
A pile's current amount is a snapshot, not a promise about the amount that will remain when the Creep arrives. Other Creeps can also collect the pile first. Target selection should therefore be repeated or revalidated on later ticks.
Calculate collectible amount
function getCollectibleAmount(
resourceAmount,
freeCapacity
) {
if (
!Number.isFinite(resourceAmount)
|| !Number.isFinite(freeCapacity)
|| resourceAmount <= 0
|| freeCapacity <= 0
) {
return 0;
}
return Math.min(
resourceAmount,
freeCapacity
);
}
pickup() has no amount argument. If the pile contains more than the Creep can carry, a successful action collects only what fits and leaves the rest on the ground.
Choose a useful reachable pile
The source article uses a deliberate strategy: prioritize the amount the Creep can actually collect, then prefer the shorter path, then use the Resource ID as a stable tie-breaker.
function selectDroppedEnergy(creep) {
const free =
creep.store.getFreeCapacity();
if (!Number.isFinite(free) || free <= 0) {
return null;
}
const resources = creep.room.find(
FIND_DROPPED_RESOURCES,
{
filter: resource =>
resource.resourceType ===
RESOURCE_ENERGY
&& Number.isFinite(resource.amount)
&& resource.amount > 0
}
);
const candidates = [];
for (const resource of resources) {
const path = creep.pos.findPathTo(
resource,
{
range: 1,
ignoreCreeps: true
}
);
if (
path.length === 0
&& !creep.pos.inRangeTo(resource, 1)
) {
continue;
}
candidates.push({
resource,
collectible: Math.min(
resource.amount,
free
),
pathLength: path.length
});
}
return candidates.sort((left, right) => {
if (
left.collectible !== right.collectible
) {
return (
right.collectible
- left.collectible
);
}
if (
left.pathLength !== right.pathLength
) {
return (
left.pathLength
- right.pathLength
);
}
return left.resource.id.localeCompare(
right.resource.id
);
})[0]?.resource ?? null;
}
This is a “fill the Creep” strategy. A path-first strategy may be more appropriate for urgent cleanup or danger avoidance. Do not claim that one ranking is always optimal.
Complete focused example
State impact: this script may move Collector1 and collect Energy from one ground Resource. It does not reserve targets for other Creeps and does not deliver the collected Energy.
function selectDroppedEnergy(creep) {
const free =
creep.store.getFreeCapacity();
if (!Number.isFinite(free) || free <= 0) {
return null;
}
const resources = creep.room.find(
FIND_DROPPED_RESOURCES,
{
filter: resource =>
resource.resourceType ===
RESOURCE_ENERGY
&& Number.isFinite(resource.amount)
&& resource.amount > 0
}
);
const candidates = [];
for (const resource of resources) {
const path = creep.pos.findPathTo(
resource,
{
range: 1,
ignoreCreeps: true
}
);
if (
path.length === 0
&& !creep.pos.inRangeTo(resource, 1)
) {
continue;
}
candidates.push({
resource,
collectible: Math.min(
resource.amount,
free
),
pathLength: path.length
});
}
return candidates.sort((left, right) => {
if (
left.collectible !== right.collectible
) {
return (
right.collectible
- left.collectible
);
}
if (
left.pathLength !== right.pathLength
) {
return (
left.pathLength
- right.pathLength
);
}
return left.resource.id.localeCompare(
right.resource.id
);
})[0]?.resource ?? null;
}
module.exports.loop = function () {
const creep = Game.creeps.Collector1;
if (!creep || creep.spawning) {
return;
}
if (creep.getActiveBodyparts(CARRY) <= 0) {
console.log(
'Collector1 has no active CARRY part.'
);
return;
}
const target = selectDroppedEnergy(creep);
if (!target) {
return;
}
const pickupResult = creep.pickup(target);
if (pickupResult === ERR_NOT_IN_RANGE) {
const moveResult = creep.moveTo(target, {
range: 1,
reusePath: 5
});
if (
moveResult !== OK
&& moveResult !== ERR_TIRED
) {
console.log(
'Collector1 moveTo() returned '
+ moveResult
);
}
return;
}
if (pickupResult !== OK) {
console.log(JSON.stringify({
type: 'dropped-energy-pickup-failed',
creepName: creep.name,
resourceId: target.id,
amountSeen: target.amount,
pickupResult
}));
}
};
Why the target can change every tick
- Another Creep may collect it first.
- Decay may reduce or remove the pile.
- The remaining amount may be lower than the earlier snapshot.
- A path that looked open may change.
- The object may no longer be in a visible room.
When a task must remember the target, save only resource.id. On a later tick, call Game.getObjectById(), handle null, verify resourceType and amount, and select a replacement when needed. Review the Memory guide before adding that state.
Return-code troubleshooting
| Result | Likely meaning | Response |
|---|---|---|
OK | The pickup command was accepted. | Inspect Store and Resource on the next tick. |
ERR_NOT_OWNER | The Creep is not yours. | Check the selected Creep. |
ERR_BUSY | The Creep is spawning. | Wait. |
ERR_INVALID_TARGET | The target is not a current valid Resource. | Refresh or reselect it. |
ERR_FULL | The Creep has no free capacity. | Switch to delivery. |
ERR_NOT_IN_RANGE | The target is farther than range 1. | Move closer and retry later. |
Use the English error-code reference to separate movement failures from pickup failures.
Debugging checklist
- Use
FIND_DROPPED_RESOURCES. - Filter specifically for
RESOURCE_ENERGY. - Reject non-finite or non-positive amounts.
- Confirm active CARRY capacity.
- Do not expect
pickup()to accept an amount argument. - Reject unreachable candidates.
- Save
pickupResultandmoveResultseparately. - Expect decay and same-tick competition.
- Save only the target ID when using Memory.
- Add target reservation before assigning many collectors to the same pile.
Scope and next steps
This article does not cover Tombstones, Ruins, Containers, Storage, multi-resource scavenging, multi-Creep reservations, cross-room recovery, danger avoidance, or delivery after collection. Use the delivery guide when the Collector must unload Energy.
Frequently asked questions
Should I use withdraw() for a ground Resource?
No. Use pickup() for a dropped Resource and withdraw() for a compatible Store target.
Does pickup() accept an amount?
No. The target holds the resource type and amount; the Creep takes only what fits.
Why can the target disappear?
It may be collected, decay away, or leave visibility. Revalidate it every tick.
Why prioritize collectible amount before path length?
That ranking tries to fill the Creep. Path-first ranking can be better for urgent cleanup, so the strategy should match the task.