Files
Screeps/screeps-tests.ts

1437 lines
46 KiB
TypeScript

// This file exists solely to test whether or not the typings actually work.
// After working on your changes, make sure to run `npm run compile` to build
// the declarations before opening this file.
//
// If you open this file and see no red squiggly lines, then you're good!
// Feel free to add more test cases in the form of a sample code.
// TODO: add more test cases.
// Sample inputs
const creep: Creep = Game.creeps.sampleCreep;
const room: Room = Game.rooms.W10S10;
const flag: Flag = Game.flags.Flag1;
const powerCreep: PowerCreep = Game.powerCreeps.samplePowerCreep;
const spawn: StructureSpawn = Game.spawns.Spawn1;
const body: BodyPartConstant[] = [WORK, WORK, CARRY, MOVE];
// Sample inputs for Game.map.findRoute testing
const anotherRoomName: Room = Game.rooms.W10S11;
// Sample memory extensions
interface CreepMemory {
sourceId: Id<Source>;
lastHits: number;
}
// Typescript always uses 'string' as the type of a key inside 'for in' loops.
// In case of objects with a restricted set of properties (e.g. ResourceConstant as key in StoreDefinition)
// the type of the key should be narrowed down in order to prevent casting (key as ResourceConstant).
// This helper function provides strongly typed keys for such objects.
// See discussion (https://github.com/Microsoft/TypeScript/pull/12253) why Object.keys does not return typed keys.
function keys<T extends Record<string, any>>(o: T): Array<keyof T> {
return Object.keys(o) as Array<keyof T>;
}
function resources(o: GenericStore): ResourceConstant[] {
return Object.keys(o) as ResourceConstant[];
}
// Game object Id types
{
const creepId: Id<Creep> = "1" as Id<Creep>;
const creepOne: Creep | null = Game.getObjectById(creepId);
const creepThree: Creep = new Creep(creepId); // Works with typed ID
if (creepOne) {
creepOne.hits;
const recycle = Game.getObjectById(creepOne.id);
}
type StoreStructure = StructureContainer | StructureStorage | StructureLink;
const storeUnionID: Id<StoreStructure> = "1234" as Id<StoreStructure>; // Strict assertion required
const storeIdUnion: StoreStructure["id"] = "1234" as StoreStructure["id"];
const stringID: string = storeUnionID; // Id<T> assignable implicitly to string
const stringID2: string = storeIdUnion; // Id<T> assignable implicitly to string
const storeObject = Game.getObjectById(storeUnionID)!;
const storeObject2 = Game.getObjectById(storeIdUnion)!;
// Object recognized
switch (storeObject.structureType) {
case STRUCTURE_CONTAINER:
storeObject.structureType === "container";
case STRUCTURE_STORAGE:
storeObject.structureType === "storage";
default:
storeObject.structureType === "link";
}
}
// Game.creeps
{
for (const creepName of Object.keys(Game.creeps)) {
Game.creeps[creepName].moveTo(flag);
}
}
// Game.flags
{
creep.moveTo(Game.flags.Flag1);
}
// Game.powerCreeps
{
PowerCreep.create("steve", POWER_CLASS.OPERATOR) === OK;
for (const i of Object.keys(Game.powerCreeps)) {
const powerCreep = Game.powerCreeps[i];
if (powerCreep.ticksToLive === undefined) {
// Not spawned in world; spawn creep
const spawn = Game.getObjectById("powerSpawnID" as Id<StructurePowerSpawn>)!;
powerCreep.spawn(spawn);
} else {
// Generate Ops
if (
powerCreep.powers[PWR_GENERATE_OPS] &&
powerCreep.powers[PWR_GENERATE_OPS].cooldown === 0 &&
(powerCreep.carry.ops || 0) < 10
) {
Game.powerCreeps[i].usePower(PWR_GENERATE_OPS);
} else {
// Boost resource
const targetSource = Game.getObjectById("targetSourceID" as Id<Source>)!;
const sourceEffect = targetSource.effects?.find((effect) => effect.effect === PWR_REGEN_SOURCE && effect.level > 0);
if (!sourceEffect && powerCreep.powers[PWR_REGEN_SOURCE] && powerCreep.powers[PWR_REGEN_SOURCE].cooldown === 0) {
powerCreep.usePower(PWR_REGEN_SOURCE, targetSource);
}
}
}
// AnyCreep type checks
creep.attack(powerCreep);
creep.heal(powerCreep);
creep.rangedAttack(powerCreep);
creep.rangedHeal(powerCreep);
creep.transfer(powerCreep, RESOURCE_ENERGY);
powerCreep.transfer(creep, RESOURCE_ENERGY);
// Upgrading
powerCreep.upgrade(PWR_GENERATE_OPS);
}
const myPowaCreeps = Game.rooms.sim.find(FIND_MY_POWER_CREEPS);
// Constant type checking
POWER_INFO[PWR_GENERATE_OPS].className === POWER_CLASS.OPERATOR;
typeof POWER_INFO[PWR_GENERATE_OPS].level[0] === "number";
}
// Game.spawns
{
for (const i of Object.keys(Game.spawns)) {
Game.spawns[i].createCreep(body);
// Test StructureSpawn.Spawning
const creep: Spawning | null = Game.spawns[i].spawning;
if (creep) {
const name: string = creep.name;
const needTime: number = creep.needTime;
const remainingTime: number = creep.remainingTime;
const creepSpawn: StructureSpawn = creep.spawn;
const cancelStatus: OK | ERR_NOT_OWNER = creep.cancel();
const setDirectionStatus: OK | ERR_NOT_OWNER | ERR_INVALID_ARGS = creep.setDirections([TOP, BOTTOM, LEFT, RIGHT]);
}
const invaderCore = new StructureInvaderCore("" as Id<StructureInvaderCore>);
const invader = invaderCore.spawning;
if (invader) {
const name = invader.name;
}
}
}
// Game.time
{
let time = Game.time;
time += 1;
}
// Game.cpu.getUsed()
{
if (Game.cpu.getUsed() > Game.cpu.tickLimit / 2) {
// Half CPU Usged
}
}
{
for (const name of Object.keys(Game.creeps)) {
const startCpu = Game.cpu.getUsed();
// creep logic goes here
const elapsed = Game.cpu.getUsed() - startCpu;
}
}
// Game.cpu.setShardLimits()
{
Game.cpu.setShardLimits({ shard0: 20, shard1: 10 });
}
// Game.cpu.halt()
{
if (Game.cpu.hasOwnProperty("halt")) {
Game.cpu.halt!();
}
}
// Game.cpu.unlock()
{
if (!Game.cpu.unlocked) {
if (!Game.cpu.unlockedTime) {
const unlock_state = Game.cpu.unlock();
if (unlock_state === OK) {
// Unlimited cosmic power!
}
}
}
}
// Game.getObjectById(id)
{
creep.memory.sourceId = creep.pos.findClosestByRange(FIND_SOURCES)!.id;
const source = Game.getObjectById<Source>(creep.memory.sourceId);
}
// Game.notify(message, [groupInterval])
{
if (creep.hits < creep.memory.lastHits) {
Game.notify(`Creep ${creep.toString()} has been attacked at ${creep.pos.toString()}!`);
}
creep.memory.lastHits = creep.hits;
}
{
if (Game.spawns["Spawn1"].energy === 0) {
Game.notify(
"Spawn1 is out of energy",
180, // group these notifications for 3 hours
);
}
}
// Game.map.describeExits()
{
const exits = Game.map.describeExits("W8N3");
if (exits) {
// tslint:disable-next-line:newline-per-chained-call
keys(exits).map((exitKey) => {
const nextRoom = exits[exitKey];
const exitDir = +exitKey as ExitConstant;
const exitPos = creep.pos.findClosestByRange(exitDir);
return { nextRoom, exitPos };
});
}
}
// Game.map.findExit()
{
if (creep.room.name !== anotherRoomName.name) {
const exitDir = Game.map.findExit(creep.room, anotherRoomName);
if (exitDir !== ERR_NO_PATH && exitDir !== ERR_INVALID_ARGS) {
const exit = creep.pos.findClosestByRange(exitDir);
if (exit !== null) {
creep.moveTo(exit);
}
}
} else {
// go to some place in another room
}
}
{
creep.moveTo(new RoomPosition(25, 25, anotherRoomName.name));
}
// Game.map.findRoute()
{
const route = Game.map.findRoute(creep.room, anotherRoomName);
if (route !== ERR_NO_PATH && route.length > 0) {
const exit = creep.pos.findClosestByRange(route[0].exit);
if (exit !== null) {
creep.moveTo(exit);
}
}
}
{
const route = Game.map.findRoute(creep.room, anotherRoomName, {
routeCallback(roomName, fromRoomName) {
if (roomName === "W10S10") {
// avoid this room
return Infinity;
}
return 1;
},
});
}
{
const from = new RoomPosition(25, 25, "E1N1");
const to = new RoomPosition(25, 25, "E4N1");
// Use `findRoute` to calculate a high-level plan for this path,
// prioritizing highways and owned rooms
const allowedRooms = { [from.roomName]: true };
const route = Game.map.findRoute(from.roomName, to.roomName, {
routeCallback(roomName) {
const parsed = /^[WE]([0-9]+)[NS]([0-9]+)$/.exec(roomName);
if (parsed !== null) {
const isHighway = parseInt(parsed[1], 10) % 10 === 0 || parseInt(parsed[2], 10) % 10 === 0;
const isMyRoom = Game.rooms[roomName] && Game.rooms[roomName].controller && Game.rooms[roomName].controller!.my;
if (isHighway || isMyRoom) {
return 1;
} else {
return 2.5;
}
} else {
return 2.5;
}
},
});
if (route !== ERR_NO_PATH) {
route.forEach((info) => {
allowedRooms[info.room] = true;
});
}
// Invoke PathFinder, allowing access only to rooms from `findRoute`
const ret = PathFinder.search(from, [to], {
roomCallback: (roomName) => {
if (allowedRooms[roomName] === undefined) {
return false;
} else {
return true;
}
},
});
}
// Game.map.getRoomLinearDistance(roomName1, roomName2, [continuous])
{
Game.map.getRoomLinearDistance("W1N1", "W4N2"); // 3
Game.map.getRoomLinearDistance("E65S55", "W65S55", false); // 131
Game.map.getRoomLinearDistance("E65S55", "W65S55", true); // 11
}
// Game.map.getTerrainAt(x, y, roomName)
// Game.map.getTerrainAt(pos)
{
Game.map.getTerrainAt(25, 20, "W10N10");
}
{
Game.map.getTerrainAt(new RoomPosition(25, 20, "W10N10"));
}
// Game.map.getRoomStatus(roomName)
{
const roomStatus = Game.map.getRoomStatus(room.name);
if (roomStatus.status === "normal") {
creep.moveTo(room.getPositionAt(25, 25)!);
}
}
// Game.market
{
// Game.market.calcTransactionCost(amount, roomName1, roomName2)
const cost = Game.market.calcTransactionCost(1000, "W0N0", "W10N5");
// Game.market.cancelOrder(orderId)
for (const id of Object.keys(Game.market.orders)) {
Game.market.cancelOrder(id);
}
// Game.market.changeOrderPrice(orderId, newPrice)
Game.market.changeOrderPrice("57bec1bf77f4d17c4c011960", 9.95);
// Game.market.createOrder({type, resourceType, price, totalAmount, [roomName]})
Game.market.createOrder({ type: ORDER_SELL, resourceType: RESOURCE_GHODIUM, price: 9.95, totalAmount: 10000, roomName: "W1N1" });
Game.market.createOrder({ type: ORDER_SELL, resourceType: RESOURCE_GHODIUM, price: 9.95, totalAmount: 10000 });
// Testing the hardcoded string literal value of the `type` field
{
// error
Game.market.createOrder({
// @ts-expect-error
type: "BUY",
resourceType: RESOURCE_GHODIUM,
price: 9.95,
totalAmount: 10000,
});
// okay
Game.market.createOrder({ type: "buy", resourceType: RESOURCE_GHODIUM, price: 9.95, totalAmount: 10000 });
}
// Game.market.deal(orderId, amount, [yourRoomName])
Game.market.deal("57cd2b12cda69a004ae223a3", 1000, "W1N1");
const amountToBuy = 2000;
const maxTransferEnergyCost = 500;
const orders = Game.market.getAllOrders({ type: ORDER_SELL, resourceType: RESOURCE_GHODIUM });
for (const i of orders) {
if (i.roomName) {
const transferEnergyCost = Game.market.calcTransactionCost(amountToBuy, "W1N1", i.roomName);
if (transferEnergyCost < maxTransferEnergyCost) {
Game.market.deal(i.id, amountToBuy, "W1N1");
break;
}
}
}
// Game.market.extendOrder(orderId, addAmount)
Game.market.extendOrder("57bec1bf77f4d17c4c011960", 10000);
// Game.market.getAllOrders([filter])
Game.market.getAllOrders();
Game.market.getAllOrders({ type: ORDER_SELL, resourceType: RESOURCE_GHODIUM });
const targetRoom = "W1N1";
Game.market.getAllOrders(
(currentOrder) =>
currentOrder.resourceType === RESOURCE_GHODIUM &&
currentOrder.type === ORDER_SELL &&
Game.market.calcTransactionCost(1000, targetRoom, currentOrder.roomName!) < 500,
);
// Game.market.getOrderById(id)
const order = Game.market.getOrderById("55c34a6b5be41a0a6e80c123");
// Subscription tokens
Game.market.getAllOrders({ type: ORDER_SELL, resourceType: SUBSCRIPTION_TOKEN });
Game.market.createOrder({ type: ORDER_BUY, resourceType: SUBSCRIPTION_TOKEN, totalAmount: 10000000, price: 1 });
const priceHistory = Game.market.getHistory(RESOURCE_FIXTURES);
const avgPrice: number = priceHistory[0].avgPrice;
const stddevPrice: number = priceHistory[0].stddevPrice;
const volume: number = priceHistory[0].volume;
// Game.market.getHistory([resourceType])
const energyHistory = Game.market.getHistory(RESOURCE_ENERGY);
const pixelHistory = Game.market.getHistory(PIXEL);
}
// PathFinder
{
const pfCreep = Game.creeps.John;
// tslint:disable-next-line:newline-per-chained-call
const goals = pfCreep.room.find(FIND_SOURCES).map((source) => {
// We can't actually walk on sources-- set `range` to 1
// so we path next to it.
return { pos: source.pos, range: 1 };
});
const ret = PathFinder.search(pfCreep.pos, goals, {
// We need to set the defaults costs higher so that we
// can set the road cost lower in `roomCallback`
plainCost: 2,
swampCost: 10,
roomCallback(roomName) {
const curRoom = Game.rooms[roomName];
// In this example `room` will always exist, but since
// PathFinder supports searches which span multiple rooms
// you should be careful!
if (!curRoom) {
return false;
}
const costs = new PathFinder.CostMatrix();
// tslint:disable-next-line:newline-per-chained-call
curRoom.find(FIND_STRUCTURES).forEach((struct) => {
if (struct.structureType === STRUCTURE_ROAD) {
// Favor roads over plain tiles
costs.set(struct.pos.x, struct.pos.y, 1);
} else if (
struct.structureType !== STRUCTURE_CONTAINER &&
(struct.structureType !== STRUCTURE_RAMPART || !(struct as OwnedStructure).my)
) {
// Can't walk through non-walkable buildings
costs.set(struct.pos.x, struct.pos.y, 0xff);
}
});
// Avoid creeps in the room
// tslint:disable-next-line:newline-per-chained-call
curRoom.find(FIND_CREEPS).forEach((thisCreep) => {
costs.set(thisCreep.pos.x, thisCreep.pos.y, 0xff);
});
return costs;
},
});
const pos = ret.path[0];
pfCreep.move(pfCreep.pos.getDirectionTo(pos));
// CostMatrix Creation
const costs = new PathFinder.CostMatrix();
costs.set(20, 20, 42);
// Serialization
const toStoreInMemory = costs.serialize();
const costsFromMemory = PathFinder.CostMatrix.deserialize([]);
}
// RawMemory
{
// RawMemory.segments
RawMemory.setActiveSegments([0, 3]);
// on the next tick
const segmentZero = RawMemory.segments[0];
RawMemory.segments[3] = '{"foo": "bar", "counter": 15}';
// RawMemory.foreignSegment
RawMemory.setActiveForeignSegment("player");
// on the next tick
const playerSegment = RawMemory.foreignSegment;
// --> {"username": "player", "id": 40, "data": "Hello!"}
// RawMemory.interShardSegment
RawMemory.interShardSegment = JSON.stringify({
creeps: {
Bob: { role: "claimer" },
},
});
// on another shard
const interShardData = JSON.parse(RawMemory.interShardSegment);
if (interShardData.creeps[creep.name]) {
creep.memory = interShardData[creep.name];
delete interShardData.creeps[creep.name]; // tslint:disable-line no-dynamic-delete
}
RawMemory.interShardSegment = JSON.stringify(interShardData);
// RawMemory.get()
const myMemory = JSON.parse(RawMemory.get());
// RawMemory.set(value)
RawMemory.set(JSON.stringify(myMemory));
// RawMemory.setActiveSegments(ids)
RawMemory.setActiveSegments([0, 3]);
// RawMemory.setActiveForeignSegment(username, [id])
RawMemory.setActiveForeignSegment("player");
RawMemory.setActiveForeignSegment("player", 10);
RawMemory.setActiveForeignSegment(null);
// RawMemory.setDefaultPublicSegment(id)
RawMemory.setPublicSegments([5, 20, 21]);
RawMemory.setDefaultPublicSegment(5);
RawMemory.setDefaultPublicSegment(null);
// RawMemory.setPublicSegments(ids)
RawMemory.setPublicSegments([5, 3]);
RawMemory.setPublicSegments([]);
}
// InterShardMemory
{
let localShardData = "";
InterShardMemory.setLocal(localShardData);
localShardData = InterShardMemory.getLocal();
const remoteShardData: string = InterShardMemory.getRemote("shard2") || "";
}
// Find Overloads
{
const creeps = room.find(FIND_HOSTILE_CREEPS);
creeps[0].say(creeps[1].name);
const flags = room.find(FIND_FLAGS);
flags[0].remove();
const spawns = room.find(FIND_HOSTILE_SPAWNS);
spawns[0].spawning;
const sources = room.find(FIND_SOURCES);
sources[0].ticksToRegeneration;
const resources = room.find(FIND_DROPPED_RESOURCES);
resources[0].resourceType;
const sites = room.find(FIND_CONSTRUCTION_SITES);
sites[0].remove();
const extensionsites = room.find(FIND_CONSTRUCTION_SITES, {
filter: (site): site is ConstructionSite<STRUCTURE_EXTENSION> => {
return site.structureType === STRUCTURE_EXTENSION;
},
});
// Should always be true. needs proper testing
extensionsites[0].structureType === STRUCTURE_EXTENSION;
// Should have type (_HasRoomPosition | RoomPosition)[]
const exits = room.find(FIND_EXIT);
const creepsHere = room.lookForAt(LOOK_CREEPS, 10, 10);
creepsHere[0].getActiveBodyparts(ATTACK);
const towers = room.find<StructureTower>(FIND_MY_STRUCTURES, {
filter: (structure) => {
return structure.structureType === STRUCTURE_TOWER;
},
});
towers[0].attack(creeps[0]);
towers[0].attack(creeps[0] as AnyCreep);
towers[0].attack(powerCreep);
towers[0].attack(spawns[0]);
towers[0].heal(powerCreep);
const isTower = (structure: AnyStructure): structure is StructureTower => {
return structure.structureType === STRUCTURE_TOWER;
};
const tower = room.find(FIND_MY_STRUCTURES, {
filter: isTower,
})[0];
tower.attack(creeps[0]);
tower.attack(creeps[0] as AnyCreep);
tower.attack(powerCreep);
tower.attack(spawns[0]);
tower.heal(powerCreep);
// All the params in filter callback should be automatically inferred
room.find(FIND_STRUCTURES, {
filter: (s, idx, array) => {
s; // $ExpectType AnyStructure
idx; // $ExpectType number
array; // $ExpectType AnyStructure[]
return true;
},
});
}
// RoomPosition Finds
{
// Should have type Creep
const hostileCreep = creep.pos.findClosestByRange(FIND_HOSTILE_CREEPS);
if (hostileCreep !== null) {
creep.say(hostileCreep.name);
}
const tower = creep.pos.findClosestByPath<StructureTower>(FIND_HOSTILE_STRUCTURES, {
// ? s should be AnyOwnedStructure in the future
// $ExpectType (structure: AnyStructure) => boolean
filter: (structure) => {
return structure.structureType === STRUCTURE_TOWER;
},
algorithm: "astar",
});
if (tower !== null) {
tower.attack(creep);
tower.attack(powerCreep);
}
// Generic type predicate filter
const isStructureType = <T extends StructureConstant, S extends ConcreteStructure<T>>(structureType: T) => {
return (structure: AnyStructure): structure is S => {
return structure.structureType === (structureType as string);
};
};
const tower2 = creep.pos.findClosestByPath(FIND_HOSTILE_STRUCTURES, {
filter: isStructureType(STRUCTURE_TOWER),
algorithm: "astar",
});
if (tower2 !== null) {
tower2.attack(creep);
tower2.attack(powerCreep);
}
const creepWithEnergy = creep.pos.findClosestByPath(creep.room.find(FIND_CREEPS), { filter: (c) => c.store.energy > 0 });
const creepAbove = creep.pos.findClosestByPath(
creep.room.find(FIND_CREEPS).map((c) => c.pos),
{
// $ExpectType (p: RoomPosition) => boolean
filter: (p) => p.getDirectionTo(creep) === TOP,
},
);
const rampart = creep.pos.findClosestByRange<StructureRampart>(FIND_HOSTILE_STRUCTURES, {
// ? s should be AnyOwnedStructure in the future
// $ExpectType (structure: AnyStructure) => boolean
filter: (structure) => {
return structure.structureType === STRUCTURE_RAMPART;
},
});
if (rampart !== null) {
rampart.isPublic;
}
// Should have type Creep[]
const hostileCreeps = creep.pos.findInRange(FIND_HOSTILE_CREEPS, 10);
hostileCreeps[0].saying;
const labs = creep.pos.findInRange<StructureLab>(FIND_MY_STRUCTURES, 4, {
// ? s should be AnyOwnedStructure in the future
// $ExpectType (structure: AnyStructure) => boolean
filter: (structure) => {
return structure.structureType === STRUCTURE_LAB;
},
});
labs[0].boostCreep(creep);
// Should be able to automatically infer the type of params in the filter function
creep.pos.findClosestByPath(FIND_STRUCTURES, {
// $ExpectType (s: AnyStructure) => boolean
filter: (s) => s.structureType === STRUCTURE_EXTENSION,
});
creep.pos.findClosestByPath([] as AnyStructure[], {
// $ExpectType (s: AnyStructure) => boolean
filter: (s) => s.structureType === STRUCTURE_EXTENSION,
});
creep.pos.findClosestByRange(FIND_STRUCTURES, {
// $ExpectType (s: AnyStructure) => boolean
filter: (s) => s.structureType === STRUCTURE_EXTENSION,
});
creep.pos.findClosestByRange([] as AnyStructure[], {
// $ExpectType (s: AnyStructure) => boolean
filter: (s) => s.structureType === STRUCTURE_EXTENSION,
});
creep.pos.findInRange(FIND_STRUCTURES, 10, {
// $ExpectType (s: AnyStructure) => boolean
filter: (s) => s.structureType === STRUCTURE_EXTENSION,
});
creep.pos.findInRange([] as AnyStructure[], 10, {
// $ExpectType (s: AnyStructure) => boolean
filter: (s) => s.structureType === STRUCTURE_EXTENSION,
});
// All the params in filter callback should be automatically inferred
creep.pos.findClosestByPath(FIND_STRUCTURES, {
filter: (s, idx, array) => {
s; // $ExpectType AnyStructure
idx; // $ExpectType number
array; // $ExpectType AnyStructure[]
return true;
},
});
creep.pos.findClosestByPath([] as AnyStructure[], {
filter: (s, idx, array) => {
s; // $ExpectType AnyStructure
idx; // $ExpectType number
array; // $ExpectType AnyStructure[]
return true;
},
});
// `findInRange, findClosestByRange, findClosestByPath` should be able to accept filter callback's type predicate
// when using FIND_* constants
{
// $ExpectType StructureTower[]
const towers = creep.pos.findInRange(FIND_STRUCTURES, 2, {
filter: isStructureType(STRUCTURE_TOWER),
});
towers[0].attack(creep);
}
{
// $ExpectType StructureTower | null
const tower = creep.pos.findClosestByPath(FIND_STRUCTURES, {
filter: isStructureType(STRUCTURE_TOWER),
});
tower?.attack(creep);
}
{
// $ExpectType StructureTower | null
const tower = creep.pos.findClosestByRange(FIND_STRUCTURES, {
filter: isStructureType(STRUCTURE_TOWER),
});
tower?.attack(creep);
}
// when pass in an array of room objects
{
// $ExpectType StructureTower[]
const towers = creep.pos.findInRange([] as AnyStructure[], 2, {
filter: isStructureType(STRUCTURE_TOWER),
});
towers[0].attack(creep);
}
{
// $ExpectType StructureTower | null
const tower = creep.pos.findClosestByPath([] as AnyStructure[], {
filter: isStructureType(STRUCTURE_TOWER),
});
tower?.attack(creep);
}
{
// $ExpectType StructureTower | null
const tower = creep.pos.findClosestByRange([] as AnyStructure[], {
filter: isStructureType(STRUCTURE_TOWER),
});
tower?.attack(creep);
}
// Lodash's object style filter predicate
// Currently do not support narrowing.
{
// $ExpectType AnyStructure[]
const towers = room.find(FIND_STRUCTURES, {
filter: { structureType: STRUCTURE_TOWER },
});
}
{
// $ExpectType AnyStructure[]
const towers = creep.pos.findInRange(FIND_STRUCTURES, 2, {
filter: { structureType: STRUCTURE_TOWER },
});
// $ExpectType AnyStructure[]
const towers2 = creep.pos.findInRange([] as AnyStructure[], 2, {
filter: { structureType: STRUCTURE_TOWER },
});
}
{
// $ExpectType AnyStructure | null
const tower = creep.pos.findClosestByPath(FIND_STRUCTURES, {
filter: { structureType: STRUCTURE_TOWER },
});
// $ExpectType AnyStructure | null
const towers2 = creep.pos.findClosestByPath([] as AnyStructure[], {
filter: { structureType: STRUCTURE_TOWER },
});
}
{
// $ExpectType AnyStructure | null
const tower = creep.pos.findClosestByRange(FIND_STRUCTURES, {
filter: { structureType: STRUCTURE_TOWER },
});
// $ExpectType AnyStructure | null
const towers2 = creep.pos.findClosestByRange([] as AnyStructure[], {
filter: { structureType: STRUCTURE_TOWER },
});
}
// should throw error if the property is not exist in the object
{
// @ts-expect-error
creep.pos.findInRange(FIND_STRUCTURES, 2, {
filter: { foo: "bar" },
});
}
// should throw error if type of values are not match
{
// @ts-expect-error
creep.pos.findInRange(FIND_STRUCTURES, 2, {
filter: { structureType: "foobar" },
});
// @ts-expect-error
creep.pos.findInRange(FIND_STRUCTURES, 2, {
filter: { structureType: 123 },
});
}
// should support deep property
{
// $ExpectType AnyOwnedStructure | null
const tower1 = creep.pos.findClosestByPath(FIND_MY_STRUCTURES, {
filter: { owner: { username: "foo" } },
});
// @ts-expect-error
const tower2 = creep.pos.findClosestByPath(FIND_MY_STRUCTURES, {
filter: { owner: { yourname: "Mitsuha" } },
});
}
// Lodash's path string style filter predicate
// Currently do not support narrowing, and maybe never will.
{
// $ExpectType AnyStructure[]
const towers = creep.pos.findInRange(FIND_STRUCTURES, 2, {
filter: "my",
});
}
}
// LookAt Finds
{
const matrix = room.lookAtArea(10, 10, 20, 20, false);
for (const y of Object.keys(matrix)) {
const row = matrix[y as unknown as number];
for (const x of Object.keys(row)) {
const pos = new RoomPosition(+x, +y, room.name);
const objects = row[x as unknown as number];
if (objects.length > 0) {
objects.map((o) => o.type);
}
}
}
const nukes = room.lookForAt(LOOK_NUKES, creep.pos);
nukes[0].launchRoomName;
const flags = room.lookForAtArea(LOOK_FLAGS, 10, 10, 20, 20);
const x = flags[10];
const y = x[11];
const entry = y[0];
entry.remove();
const creeps = room.lookForAtArea(LOOK_CREEPS, 10, 10, 20, 20, true);
creeps[0].x;
creeps[0].y;
creeps[0].creep.move(TOP);
// #252
const structuresMatrix = room.lookForAtArea(LOOK_STRUCTURES, 10, 10, 20, 20);
structuresMatrix[15][15].find((s) => s.structureType === STRUCTURE_CONTROLLER);
// #252 with explicit isArray=false
const structuresMatrix2 = room.lookForAtArea(LOOK_STRUCTURES, 10, 10, 20, 20, false);
structuresMatrix2[15][15].find((s) => s.structureType === STRUCTURE_CONTROLLER);
}
// StoreDefinition
{
for (const resourceType of resources(creep.carry)) {
const amount = creep.carry[resourceType];
creep.drop(resourceType, amount);
}
const extension = new StructureExtension("" as Id<StructureExtension>);
const e1: number = extension.store.getUsedCapacity(RESOURCE_ENERGY);
const e2: number = extension.store[RESOURCE_ENERGY];
// Invalid resource type for extension
const eg1: null = extension.store.getUsedCapacity(RESOURCE_GHODIUM);
const eg2: null = extension.store.getFreeCapacity(RESOURCE_GHODIUM);
const eg3: null = extension.store.getCapacity(RESOURCE_GHODIUM);
const eg4: 0 = extension.store.G;
const storage = new StructureStorage("" as Id<StructureStorage>);
const sg1: number = storage.store.getUsedCapacity(RESOURCE_GHODIUM);
const sg2: number = storage.store.getFreeCapacity(RESOURCE_GHODIUM);
const sg3: number = storage.store.getCapacity(RESOURCE_GHODIUM);
}
// Advanced Structure types
{
const owned = Game.getObjectById("blah" as Id<AnyOwnedStructure>)!;
const owner = owned.owner && owned.owner.username;
owned.notifyWhenAttacked(false);
const structs = room.find(FIND_MY_STRUCTURES);
structs.forEach((struct) => {
switch (struct.structureType) {
case STRUCTURE_CONTROLLER:
const usernameOptional: string | undefined = struct.owner && struct.owner.username;
break;
default:
const usernameRequired: string = struct.owner.username;
break;
}
});
const unowned = Game.getObjectById("blah2" as Id<AnyStructure>)!;
const hp = unowned.hits / unowned.hitsMax;
// test discriminated union
switch (unowned.structureType) {
case STRUCTURE_TOWER:
unowned.heal(Game.creeps.myCreep);
break;
case STRUCTURE_CONTAINER:
case STRUCTURE_STORAGE:
case STRUCTURE_TERMINAL:
const energyPercent = unowned.store.energy / unowned.storeCapacity;
break;
case STRUCTURE_WALL:
case STRUCTURE_RAMPART:
const wallHp = unowned.hits / unowned.hitsMax;
break;
}
// test discriminated union using filter functions on find
// $ExpectType AnyStructure
const from = Game.rooms.myRoom.find(FIND_STRUCTURES, {
filter: (s) => (s.structureType === STRUCTURE_CONTAINER || s.structureType === STRUCTURE_STORAGE) && s.store.energy > 0,
})[0];
// $ExpectType AnyOwnedStructure | null
const to = from.pos.findClosestByPath(FIND_MY_STRUCTURES, {
filter: (s) => (s.structureType === STRUCTURE_SPAWN || s.structureType === STRUCTURE_EXTENSION) && s.energy < s.energyCapacity,
});
Game.rooms.myRoom
.find(FIND_MY_STRUCTURES, {
filter: (s) => s.structureType === STRUCTURE_RAMPART,
})
.forEach((r) => r.notifyWhenAttacked(false));
}
{
// Test that you can use signatures
EXTENSION_ENERGY_CAPACITY[Game.rooms.myRoom.controller!.level];
REACTIONS[Object.keys(creep.carry)[0]];
BOOSTS[creep.body[0].type];
}
// Tombstones
{
const tombstone = room.find(FIND_TOMBSTONES)[0];
(tombstone.creep as PowerCreep).spawnCooldownTime;
(tombstone.creep as Creep).my;
tombstone.store.energy;
tombstone.id;
const creep = Game.creeps["dave"];
creep.withdraw(tombstone, RESOURCE_ENERGY);
}
// Ruin
{
const ruin = room.find(FIND_RUINS)[0];
creep.withdraw(ruin, RESOURCE_ENERGY);
powerCreep.withdraw(ruin, RESOURCE_ENERGY);
}
{
if (Game.cpu.hasOwnProperty("getHeapStatistics")) {
const heap = Game.cpu.getHeapStatistics!();
heap.total_heap_size;
}
}
// StructurePortal
{
const portals = room.find<StructurePortal>(FIND_STRUCTURES, { filter: (s) => s.structureType === STRUCTURE_PORTAL });
portals.forEach((p: StructurePortal) => {
const state = p.ticksToDecay === undefined ? "stable" : "unstable";
if (p.destination instanceof RoomPosition) {
Game.notify(`Found ${state} inter-room portal to ${p.destination.toString()}`);
} else {
Game.notify(`Found ${state} inter-shard portal to ${p.destination.shard} ${p.destination.room}`);
}
});
}
// ConstructionSite
{
room.createConstructionSite(10, 10, STRUCTURE_EXTENSION);
room.createConstructionSite(10, 11, STRUCTURE_SPAWN, "mySpawn");
const pos = new RoomPosition(10, 10, room.name);
room.createConstructionSite(pos, STRUCTURE_EXTENSION);
room.createConstructionSite(pos, STRUCTURE_SPAWN, "mySpawn");
pos.createConstructionSite(STRUCTURE_EXTENSION);
pos.createConstructionSite(STRUCTURE_SPAWN, "mySpawn");
}
// StructureLab
{
const lab0 = Game.getObjectById("lab" as Id<StructureLab>);
const lab1 = Game.getObjectById("lab" as Id<StructureLab>);
const lab2 = Game.getObjectById("lab" as Id<StructureLab>);
if (lab0 !== null && lab1 !== null && lab2 !== null) {
if (lab1.mineralAmount >= LAB_REACTION_AMOUNT && lab2.mineralAmount >= LAB_REACTION_AMOUNT && lab0.mineralType === null) {
lab0.runReaction(lab1, lab2);
}
// nevermind, reverse that
lab0.reverseReaction(lab1, lab2);
}
}
// Room event log
{
room.getEventLog();
const eventStr = room.getEventLog(true);
if (eventStr.includes(`build:4`)) {
// Build occured on last tick.
}
const events = room.getEventLog();
const event = events[0];
switch (event.event) {
case EVENT_ATTACK:
const attackType: EventAttackType = event.data.attackType;
break;
case EVENT_BUILD:
const energySpent: number = event.data.energySpent;
break;
case EVENT_POWER:
const power = event.data.power;
break;
}
}
// Room.Terrain
{
const room = Game.rooms[""];
const myTerrain = room.getTerrain();
const otherTerrain = new Room.Terrain("E2S7");
const anotherTerrain = Game.map.getRoomTerrain("W2N5");
const ret = myTerrain.get(5, 5);
if (ret === 0) {
/*plain*/
}
if (ret === TERRAIN_MASK_SWAMP) {
/*swamp*/
}
if (ret === TERRAIN_MASK_WALL) {
/*wall*/
}
const myRawTerrain = myTerrain.getRawBuffer();
const otherRawTerrain = otherTerrain.getRawBuffer(new Int8Array(2500));
const anotherRawTerrain = anotherTerrain.getRawBuffer(new Uint16Array(2500));
for (const rawTerrain of [myRawTerrain, otherRawTerrain, anotherRawTerrain]) {
for (let y = 0; y < 50; y++) {
for (let x = 0; x < 50; x++) {
const code = rawTerrain[y * 50 + x];
if (code === 0) {
/*plain*/
}
if (code & TERRAIN_MASK_SWAMP) {
/*swamp*/
}
if (code & TERRAIN_MASK_WALL) {
/*wall*/
}
}
}
}
}
// Creep.body
function atackPower(creep: Creep) {
return creep.body
.map((part) => {
if (part.type === ATTACK) {
const multiplier = part.boost ? BOOSTS[part.type][part.boost].attack : 1;
return multiplier * ATTACK_POWER;
}
return 0;
})
.reduce((a, b) => a + b);
}
// Factories and Commodities
{
const factory = new StructureFactory("" as Id<StructureFactory>);
if (!factory.level) {
powerCreep.usePower(PWR_OPERATE_FACTORY, factory);
}
creep.transfer(factory, RESOURCE_CELL, 20);
creep.transfer(factory, RESOURCE_OXIDANT, 36);
creep.transfer(factory, RESOURCE_LEMERGIUM_BAR, 16);
creep.transfer(factory, RESOURCE_ENERGY, 8);
factory.produce(RESOURCE_PHLEGM);
factory.produce(RESOURCE_BATTERY);
factory.produce(RESOURCE_ENERGY);
factory.produce(RESOURCE_GHODIUM);
factory.produce(RESOURCE_GHODIUM_MELT);
creep.withdraw(factory, RESOURCE_PHLEGM);
// Energy and ghodium commodities
COMMODITIES[RESOURCE_ENERGY];
COMMODITIES[RESOURCE_GHODIUM];
// Mineral commodities
COMMODITIES[RESOURCE_UTRIUM];
COMMODITIES[RESOURCE_LEMERGIUM];
COMMODITIES[RESOURCE_KEANIUM];
COMMODITIES[RESOURCE_ZYNTHIUM];
COMMODITIES[RESOURCE_OXYGEN];
COMMODITIES[RESOURCE_HYDROGEN];
COMMODITIES[RESOURCE_CATALYST];
// Commodity commodities
COMMODITIES[RESOURCE_UTRIUM_BAR];
COMMODITIES[RESOURCE_LEMERGIUM_BAR];
COMMODITIES[RESOURCE_ZYNTHIUM_BAR];
COMMODITIES[RESOURCE_KEANIUM_BAR];
COMMODITIES[RESOURCE_GHODIUM_MELT];
COMMODITIES[RESOURCE_OXIDANT];
COMMODITIES[RESOURCE_REDUCTANT];
COMMODITIES[RESOURCE_PURIFIER];
COMMODITIES[RESOURCE_BATTERY];
COMMODITIES[RESOURCE_COMPOSITE];
COMMODITIES[RESOURCE_CRYSTAL];
COMMODITIES[RESOURCE_LIQUID];
COMMODITIES[RESOURCE_WIRE];
COMMODITIES[RESOURCE_SWITCH];
COMMODITIES[RESOURCE_TRANSISTOR];
COMMODITIES[RESOURCE_MICROCHIP];
COMMODITIES[RESOURCE_CIRCUIT];
COMMODITIES[RESOURCE_DEVICE];
COMMODITIES[RESOURCE_CELL];
COMMODITIES[RESOURCE_PHLEGM];
COMMODITIES[RESOURCE_TISSUE];
COMMODITIES[RESOURCE_MUSCLE];
COMMODITIES[RESOURCE_ORGANOID];
COMMODITIES[RESOURCE_ORGANISM];
COMMODITIES[RESOURCE_ALLOY];
COMMODITIES[RESOURCE_TUBE];
COMMODITIES[RESOURCE_FIXTURES];
COMMODITIES[RESOURCE_FRAME];
COMMODITIES[RESOURCE_HYDRAULICS];
COMMODITIES[RESOURCE_MACHINE];
COMMODITIES[RESOURCE_CONDENSATE];
COMMODITIES[RESOURCE_CONCENTRATE];
COMMODITIES[RESOURCE_EXTRACT];
COMMODITIES[RESOURCE_SPIRIT];
COMMODITIES[RESOURCE_EMANATION];
COMMODITIES[RESOURCE_ESSENCE];
}
// <strike>Horse armor!</strike>Pixels!
{
const ret: OK | ERR_NOT_ENOUGH_RESOURCES | ERR_FULL = Game.cpu.generatePixel();
}
// Game.map.visual
{
const mapVis = Game.map.visual;
const point1 = new RoomPosition(1, 1, "E1N1");
const point2 = new RoomPosition(1, 1, "E1N8");
const point3 = new RoomPosition(1, 1, "E8N8");
const point4 = new RoomPosition(1, 1, "E1N8");
mapVis.line(point1, point2).circle(point3, { fill: "#f2f2f2" }).poly([point1, point2, point3, point4]).rect(point3, 50, 50);
const size: number = mapVis.getSize();
const visData = mapVis.export();
mapVis.clear();
mapVis.import(visData);
}
// Store
{
// Store definitions of structures are well typed with their capable resources
{
const energyOnlyStores = [
new StructureSpawn("" as Id<StructureSpawn>).store,
new StructureExtension("" as Id<StructureExtension>).store,
new StructureTower("" as Id<StructureTower>).store,
new StructureLink("" as Id<StructureLink>).store,
];
for (const store of energyOnlyStores) {
// should be number for capabale resources
const shouldBeNumber = store.getCapacity(RESOURCE_ENERGY); // $ExpectType number
const shouldBeNumber2 = store.getFreeCapacity(RESOURCE_ENERGY); // $ExpectType number
const shouldBeNumber3 = store.getUsedCapacity(RESOURCE_ENERGY); // $ExpectType number
// should be null for non-capabale resources
const shouldBeNull1 = store.getCapacity(RESOURCE_HYDROGEN); // $ExpectType null
const shouldBeNull2 = store.getFreeCapacity(RESOURCE_HYDROGEN); // $ExpectType null
const shouldBeNull3 = store.getUsedCapacity(RESOURCE_HYDROGEN); // $ExpectType null
const shouldBeZero = store[RESOURCE_HYDROGEN]; // $ExpectType 0
}
const nukerStore = new StructureNuker("" as Id<StructureNuker>).store;
// should be number for capabale resources
const shouldBeNumber = nukerStore.getCapacity(RESOURCE_ENERGY); // $ExpectType number
const shouldBeNumber2 = nukerStore.getFreeCapacity(RESOURCE_ENERGY); // $ExpectType number
const shouldBeNumber3 = nukerStore.getUsedCapacity(RESOURCE_ENERGY); // $ExpectType number
const shouldBeNumber4 = nukerStore.getCapacity(RESOURCE_GHODIUM); // $ExpectType number
const shouldBeNumber5 = nukerStore.getFreeCapacity(RESOURCE_GHODIUM); // $ExpectType number
const shouldBeNumber6 = nukerStore.getUsedCapacity(RESOURCE_GHODIUM); // $ExpectType number
// should be null for non-capabale resources
const shouldBeNull1 = nukerStore.getCapacity(RESOURCE_HYDROGEN); // $ExpectType null
const shouldBeNull2 = nukerStore.getFreeCapacity(RESOURCE_HYDROGEN); // $ExpectType null
const shouldBeNull3 = nukerStore.getUsedCapacity(RESOURCE_HYDROGEN); // $ExpectType null
}
// Unlimited store have no notion of capacity and free capacity
{
const ruinStore = new Ruin("" as Id<Ruin>).store;
for (const resourceType of RESOURCES_ALL) {
const shouldBeNull1 = ruinStore.getCapacity(resourceType); // $ExpectType null
const shouldBeNull2 = ruinStore.getFreeCapacity(resourceType); // $ExpectType null
const shouldNotBeNull = ruinStore.getUsedCapacity(resourceType); // $ExpectType number
const shouldNotBeNull2 = ruinStore[resourceType]; // $ExpectType number
}
const tombstoneStore = new Tombstone("" as Id<Tombstone>).store;
for (const resourceType of RESOURCES_ALL) {
const shouldBeNull1 = tombstoneStore.getCapacity(resourceType); // $ExpectType null
const shouldBeNull2 = tombstoneStore.getFreeCapacity(resourceType); // $ExpectType null
const shouldNotBeNull = tombstoneStore.getUsedCapacity(resourceType); // $ExpectType number
const shouldNotBeNull2 = tombstoneStore[resourceType]; // $ExpectType number
}
}
// test return type of `get*Capacity()` with no resourceType specified
{
// should be null for structures that only accept certain resource types
{
const spawnStore = new StructureSpawn("" as Id<StructureSpawn>).store;
const shouldBeNull1 = spawnStore.getCapacity(); // $ExpectType null
const shouldBeNull2 = spawnStore.getFreeCapacity(); // $ExpectType null
const shouldBeNull3 = spawnStore.getUsedCapacity(); // $ExpectType null
}
{
const nukeStore = new StructureNuker("" as Id<StructureNuker>).store;
const shouldBeNull1 = nukeStore.getCapacity(); // $ExpectType null
const shouldBeNull2 = nukeStore.getFreeCapacity(); // $ExpectType null
const shouldBeNull3 = nukeStore.getUsedCapacity(); // $ExpectType null
}
{
const labStore = new StructureLab("" as Id<StructureLab>).store;
const shouldBeNull1 = labStore.getCapacity(); // $ExpectType null
const shouldBeNull2 = labStore.getFreeCapacity(); // $ExpectType null
const shouldBeNull3 = labStore.getUsedCapacity(); // $ExpectType null
}
// should be number for structures that accept all resource types
{
const containerStore = new StructureContainer("" as Id<StructureContainer>).store;
const shouldBeNumber1 = containerStore.getCapacity(); // $ExpectType number
const shouldBeNumber2 = containerStore.getFreeCapacity(); // $ExpectType number
const shouldBeNumber3 = containerStore.getUsedCapacity(); // $ExpectType number
}
{
const storageStore = new StructureStorage("" as Id<StructureStorage>).store;
const shouldBeNumber1 = storageStore.getCapacity(); // $ExpectType number
const shouldBeNumber2 = storageStore.getFreeCapacity(); // $ExpectType number
const shouldBeNumber3 = storageStore.getUsedCapacity(); // $ExpectType number
}
{
const terminalStore = new StructureTerminal("" as Id<StructureTerminal>).store;
const shouldBeNumber1 = terminalStore.getCapacity(); // $ExpectType number
const shouldBeNumber2 = terminalStore.getFreeCapacity(); // $ExpectType number
const shouldBeNumber3 = terminalStore.getUsedCapacity(); // $ExpectType number
}
{
const factoryStore = new StructureFactory("" as Id<StructureFactory>).store;
const shouldBeNumber1 = factoryStore.getCapacity(); // $ExpectType number
const shouldBeNumber2 = factoryStore.getFreeCapacity(); // $ExpectType number
const shouldBeNumber3 = factoryStore.getUsedCapacity(); // $ExpectType number
}
}
}
// Room Object
{
// `RoomObject.effects` can be undefined
{
const source = new Source("" as Id<Source>);
// @ts-expect-error
source.effects.find((effect) => effect.effect === PWR_REGEN_SOURCE);
// no error with optional chaining operator
source.effects?.find((effect) => effect.effect === PWR_REGEN_SOURCE);
}
}
// Id
{
/// @ts-expect-error
const roomId = "" as Id<Room>;
const creep = Game.getObjectById("" as Id<Creep>);
/// @ts-expect-error
const foo = Game.getObjectById<StructureTower>("" as Id<Creep>);
}