Dependencies
moon add Kaida-Amethyst/screeps{
...
"preferred-target": "js"
}import { getObjectsByPrototype, createConstructionSite } from "game/utils";
import {
ConstructionSite,
Creep,
Flag,
Source,
StructureContainer,
StructureExtension,
StructureRampart,
StructureRoad,
StructureSpawn,
Structure,
StructureTower,
StructureWall,
} from "game/prototypes";
import { loop as moonbitLoop } from "./moonbit-main.mjs";
const prototypeTable = {
ConstructionSite,
Creep,
Flag,
Source,
StructureContainer,
StructureExtension,
StructureRampart,
StructureRoad,
Structure,
StructureSpawn,
StructureTower,
StructureWall,
};
globalThis.__moonbit_screeps_host = globalThis.__moonbit_screeps_host ?? {};
globalThis.__moonbit_screeps_host.getObjectsByPrototypeName = (name) => {
const prototype = prototypeTable[name];
if (!prototype) {
throw new Error(`Unknown Screeps prototype: ${name}`);
}
return getObjectsByPrototype(prototype);
};
globalThis.__moonbit_screeps_host.createConstructionSiteByPrototypeName = (
pos,
name,
) => {
const prototype = prototypeTable[name];
if (!prototype) {
throw new Error(`Unknown Screeps prototype: ${name}`);
}
return createConstructionSite(pos, prototype);
};
export function loop() {
moonbitLoop();
}moon buildcp _build/js/debug/build/main/main.js /path/to/ScreepsArena/moonbit-main.mjs
cp main.mjs /path/to/ScreepsArena/main.mjsimport {
"Kaida-Amethyst/screeps",
}
options(
"is-main": true,
link: { "js": { "exports": [ "main_loop:loop" ] } },
)///|
pub fn main_loop() -> Unit {
println(@screeps.get_ticks())
}
///|
fn main {
}moon buildcp _build/js/debug/build/main/main.js /path/to/ScreepsArena/moonbit-main.mjs
cp main.mjs /path/to/ScreepsArena/main.mjsimport { getTicks } from "game/utils";
export function loop() {
console.log("Current tick:", getTicks());
}import { getObjectsByPrototype } from "game/utils";
import { Creep, Flag } from "game/prototypes";
export function loop() {
var creeps = getObjectsByPrototype(Creep);
var flags = getObjectsByPrototype(Flag);
creeps[0].moveTo(flags[0]);
}///|
pub fn main_loop() -> Unit {
guard @screeps.my_creeps() is [creep, ..] else { return }
guard @screeps.flags() is [flag, ..] else { return }
creep.move_to(flag) |> ignore
}
///|
fn main {
}import { getObjectsByPrototype } from "game/utils";
import { Creep } from "game/prototypes";
import { ERR_NOT_IN_RANGE } from "game/constants";
export function loop() {
var myCreep = getObjectsByPrototype(Creep).find((creep) => creep.my);
var enemyCreep = getObjectsByPrototype(Creep).find((creep) => !creep.my);
if (myCreep.attack(enemyCreep) == ERR_NOT_IN_RANGE) {
myCreep.moveTo(enemyCreep);
}
}///|
pub fn main_loop() -> Unit {
guard @screeps.my_creeps() is [my_creep, ..] else { return }
guard @screeps.enemy_creeps() is [enemy_creep, ..] else { return }
my_creep.attack_or_move(enemy_creep)
}
///|
fn main {
}import { getObjectsByPrototype } from "game/utils";
import { Creep } from "game/prototypes";
import { ERR_NOT_IN_RANGE, ATTACK, RANGED_ATTACK, HEAL } from "game/constants";
export function loop() {
var myCreeps = getObjectsByPrototype(Creep).filter((creep) => creep.my);
var enemyCreep = getObjectsByPrototype(Creep).find((creep) => !creep.my);
for (var creep of myCreeps) {
if (creep.body.some((bodyPart) => bodyPart.type == ATTACK)) {
if (creep.attack(enemyCreep) == ERR_NOT_IN_RANGE) {
creep.moveTo(enemyCreep);
}
}
if (creep.body.some((bodyPart) => bodyPart.type == RANGED_ATTACK)) {
if (creep.rangedAttack(enemyCreep) == ERR_NOT_IN_RANGE) {
creep.moveTo(enemyCreep);
}
}
if (creep.body.some((bodyPart) => bodyPart.type == HEAL)) {
var myDamagedCreeps = myCreeps.filter((i) => i.hits < i.hitsMax);
if (myDamagedCreeps.length > 0) {
if (creep.heal(myDamagedCreeps[0]) == ERR_NOT_IN_RANGE) {
creep.moveTo(myDamagedCreeps[0]);
}
}
}
}
}///|
pub fn main_loop() -> Unit {
let my_creeps = @screeps.my_creeps()
guard @screeps.enemy_creeps() is [enemy_creep, ..] else { return }
let damaged_creeps = my_creeps.filter(creep => creep.hp() < creep.max_hp())
let melee_creeps = my_creeps.filter(creep => {
creep.has_body_part(@screeps.Attack)
})
let healers = my_creeps.filter(creep => creep.has_body_part(@screeps.Heal))
let ranged_creeps = my_creeps.filter(creep => {
creep.has_body_part(@screeps.RangedAttack)
})
for creep in melee_creeps {
creep.attack_or_move(enemy_creep)
}
for creep in ranged_creeps {
creep.ranged_attack_or_move(enemy_creep)
}
if damaged_creeps is [damaged_creep, ..] {
for creep in healers {
creep.heal_or_move(damaged_creep)
}
}
}
///|
fn main {
}import { prototypes, utils, constants } from "game";
export function loop() {
const tower = utils.getObjectsByPrototype(prototypes.StructureTower)[0];
if (tower.store[constants.RESOURCE_ENERGY] < 10) {
var myCreep = utils
.getObjectsByPrototype(prototypes.Creep)
.find((creep) => creep.my);
if (myCreep.store[constants.RESOURCE_ENERGY] == 0) {
var container = utils.getObjectsByPrototype(prototypes.StructureContainer)[0];
myCreep.withdraw(container, constants.RESOURCE_ENERGY);
} else {
myCreep.transfer(tower, constants.RESOURCE_ENERGY);
}
} else {
var target = utils
.getObjectsByPrototype(prototypes.Creep)
.find((creep) => !creep.my);
tower.attack(target);
}
}///|
pub fn main_loop() -> Unit {
guard @screeps.my_towers() is [tower, ..] else { return }
guard @screeps.my_creeps() is [my_creep, ..] else { return }
guard @screeps.containers() is [container, ..] else { return }
guard @screeps.enemy_creeps() is [target, ..] else { return }
if tower.store().energy() >= 10 {
tower.attack(target) |> ignore
return
}
match my_creep.store().energy() {
0 => my_creep.withdraw_energy_or_move(container)
_ => my_creep.transfer_energy_or_move(tower)
}
}
///|
fn main {
}import { getObjectsByPrototype } from "game/utils";
import { Creep, Flag } from "game/prototypes";
export function loop() {
var creeps = getObjectsByPrototype(Creep).filter((i) => i.my);
var flags = getObjectsByPrototype(Flag);
for (var creep of creeps) {
var flag = creep.findClosestByPath(flags);
creep.moveTo(flag);
}
}///|
pub fn main_loop() -> Unit {
let flags = @screeps.flags()
for creep in @screeps.my_creeps() {
guard creep.find_closest(flags) is Some(flag) else { continue }
creep.move_to(flag) |> ignore
}
}
///|
fn main {
}creep.find_closest(flags, by=Range)import { getObjectsByPrototype } from "game/utils";
import { Creep, Flag, StructureSpawn } from "game/prototypes";
import { MOVE } from "game/constants";
var creep1, creep2;
export function loop() {
var mySpawn = getObjectsByPrototype(StructureSpawn)[0];
var flags = getObjectsByPrototype(Flag);
if (!creep1) {
creep1 = mySpawn.spawnCreep([MOVE]).object;
} else {
creep1.moveTo(flags[0]);
if (!creep2) {
creep2 = mySpawn.spawnCreep([MOVE]).object;
} else {
creep2.moveTo(flags[1]);
}
}
}///|
pub fn main_loop() -> Unit {
guard @screeps.my_spawns() is [my_spawn, ..] else { return }
let flags = @screeps.flags()
let creeps = @screeps.my_creeps()
let num_of_flags = flags.length()
let num_of_creeps = creeps.length()
if num_of_creeps < num_of_flags {
my_spawn.spawn_creep([Move]) |> ignore
}
for i in 0..<min(num_of_creeps, num_of_flags) {
creeps[i].move_to(flags[i]) |> ignore
}
}
///|
fn min(a : Int, b : Int) -> Int {
match a < b {
true => a
false => b
}
}
///|
fn main {
}import { prototypes, utils, constants } from "game";
export function loop() {
var creep = utils.getObjectsByPrototype(prototypes.Creep).find((i) => i.my);
var source = utils.getObjectsByPrototype(prototypes.Source)[0];
var spawn = utils
.getObjectsByPrototype(prototypes.StructureSpawn)
.find((i) => i.my);
if (creep.store.getFreeCapacity(constants.RESOURCE_ENERGY)) {
if (creep.harvest(source) == constants.ERR_NOT_IN_RANGE) {
creep.moveTo(source);
}
} else {
if (creep.transfer(spawn, constants.RESOURCE_ENERGY) == constants.ERR_NOT_IN_RANGE) {
creep.moveTo(spawn);
}
}
}///|
pub fn main_loop() -> Unit {
guard @screeps.my_creeps() is [creep, ..] else { return }
guard @screeps.sources() is [source, ..] else { return }
guard @screeps.my_spawns() is [spawn, ..] else { return }
match creep.store().free_energy() {
0 => creep.transfer_energy_or_move(spawn)
_ => creep.harvest_or_move(source)
}
}
///|
fn main {
}import { prototypes, utils } from "game";
import { RESOURCE_ENERGY, ERR_NOT_IN_RANGE } from "game/constants";
export function loop() {
const creep = utils.getObjectsByPrototype(prototypes.Creep).find((i) => i.my);
if (!creep.store[RESOURCE_ENERGY]) {
const container = utils.findClosestByPath(
creep,
utils.getObjectsByPrototype(prototypes.StructureContainer),
);
if (creep.withdraw(container, RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
creep.moveTo(container);
}
} else {
const constructionSite = utils
.getObjectsByPrototype(prototypes.ConstructionSite)
.find((i) => i.my);
if (!constructionSite) {
utils.createConstructionSite(50, 55, prototypes.StructureTower);
} else {
if (creep.build(constructionSite) == ERR_NOT_IN_RANGE) {
creep.moveTo(constructionSite);
}
}
}
}///|
pub fn main_loop() -> Unit {
guard @screeps.my_creeps() is [creep, ..] else { return }
let containers = @screeps.containers()
if creep.store().energy() == 0 {
if creep.find_closest(containers) is Some(container) {
creep.withdraw_energy_or_move(container) |> ignore
}
return
}
match @screeps.my_construction_sites() {
[construction_site, ..] => creep.build_or_move(construction_site)
_ => @screeps.create_construction_site(50, 55, Tower) |> ignore
}
}
///|
fn main {
}///|
using @screeps {
type BodyPartKind,
type MyCreep,
type EnemyCreep,
type Source,
type MySpawn,
}
///|
let worker_body : Array[BodyPartKind] = [Work, Carry, Move]
///|
let light_fighter_body : Array[BodyPartKind] = [Attack, Move]
///|
let heavy_fighter_body : Array[BodyPartKind] = [Attack, Attack, Move, Move]
///|
fn is_worker(creep : MyCreep) -> Bool {
creep.store().capacity(@screeps.Energy) > 0
}
///|
fn workers(creeps : Array[MyCreep]) -> Array[MyCreep] {
creeps.filter(is_worker)
}
///|
fn fighters(creeps : Array[MyCreep]) -> Array[MyCreep] {
creeps.filter(creep => !is_worker(creep))
}
///|
fn worker_action(
creep : MyCreep,
sources : Array[Source],
spawn : MySpawn,
) -> Unit {
if creep.store().free_energy() > 0 {
guard creep.find_closest(sources, by=Range) is Some(source) else { return }
creep.harvest_or_move(source)
} else {
creep.transfer_energy_or_move(spawn)
}
}
///|
fn fighter_action(creep : MyCreep, enemies : Array[EnemyCreep]) -> Unit {
guard creep.find_closest(enemies, by=Range) is Some(enemy) else { return }
creep.attack_or_move(enemy)
}
///|
fn spawn_worker_if_possible(spawn : MySpawn) -> Unit {
ignore(spawn.spawn_creep(worker_body))
}
///|
fn spawn_fighter_if_possible(spawn : MySpawn) -> Unit {
let heavy_result = spawn.spawn_creep(heavy_fighter_body)
match heavy_result {
Spawned(_) => ()
SpawnFailed(SpawnNotEnoughEnergy) =>
spawn.spawn_creep(light_fighter_body) |> ignore
SpawnFailed(_) => ()
}
}
///|
enum SpawnRole {
Worker
Fighter
}
///|
fn next_spawn_role(my_creeps : Array[MyCreep]) -> SpawnRole {
let worker_count = workers(my_creeps).length()
let fighter_count = fighters(my_creeps).length()
if worker_count == 0 {
Worker
} else if fighter_count == 0 {
Fighter
} else if worker_count < 2 {
Worker
} else {
Fighter
}
}
///|
fn spawn_action(spawn : MySpawn, my_creeps : Array[MyCreep]) -> Unit {
match next_spawn_role(my_creeps) {
Worker => spawn_worker_if_possible(spawn)
Fighter => spawn_fighter_if_possible(spawn)
}
}
///|
fn fallback_sources() -> Array[Source] {
let live_sources = @screeps.active_sources()
match live_sources.is_empty() {
true => @screeps.sources()
false => live_sources
}
}
///|
pub fn main_loop() -> Unit {
guard @screeps.my_spawns() is [spawn, ..] else { return }
let my_creeps = @screeps.my_creeps()
let enemies = @screeps.enemy_creeps()
let sources = fallback_sources()
spawn_action(spawn, my_creeps)
workers(my_creeps).each(creep => worker_action(creep, sources, spawn))
fighters(my_creeps).each(creep => fighter_action(creep, enemies))
}
///|
fn main {
}pub(open) trait GameObjectLike {
as_game_object(Self) -> GameObject
id(Self) -> String
x(Self) -> Int
y(Self) -> Int
exists(Self) -> Bool
}pub(open) trait HasHits {
hits(Self) -> Int
hits_max(Self) -> Int
}pub struct BodyPart {
// private fields
}pub struct ConstructionSite {
// private fields
}impl BuildTarget for ConstructionSiteimpl GameObjectLike for ConstructionSiteimpl MoveTarget for ConstructionSiteimpl MyLike for ConstructionSitefn[T : GameObjectLike] ConstructionSite::find_closest(self : ConstructionSite, candidates : Array[T], by? : ClosestMetric) -> T?fn[T : GameObjectLike] ConstructionSite::find_in_range(self : ConstructionSite, candidates : Array[T], range : Int) -> Array[T]pub(all) enum CreateConstructionSiteResult {
Created(ConstructionSite)
CreateFailed(CreateConstructionSiteFailure)
}pub struct Creep {
// private fields
}impl AttackTarget for Creepimpl GameObjectLike for Creepimpl HealTarget for Creepimpl MoveTarget for Creepimpl RangedAttackTarget for Creepimpl TowerAttackTarget for Creepimpl TransferTarget for Creepfn[T : GameObjectLike] Creep::find_closest(self : Creep, candidates : Array[T], by? : ClosestMetric) -> T?fn[T : GameObjectLike] Creep::find_in_range(self : Creep, candidates : Array[T], range : Int) -> Array[T]pub struct EnemyCreep {
// private fields
}impl AttackTarget for EnemyCreepimpl GameObjectLike for EnemyCreepimpl HasHits for EnemyCreepimpl HasStore for EnemyCreepimpl HealTarget for EnemyCreepimpl MoveTarget for EnemyCreepimpl MyLike for EnemyCreepimpl RangedAttackTarget for EnemyCreepimpl TowerAttackTarget for EnemyCreepimpl TransferTarget for EnemyCreepfn[T : GameObjectLike] EnemyCreep::find_closest(self : EnemyCreep, candidates : Array[T], by? : ClosestMetric) -> T?fn[T : GameObjectLike] EnemyCreep::find_in_range(self : EnemyCreep, candidates : Array[T], range : Int) -> Array[T]pub struct EnemySpawn {
// private fields
}impl AttackTarget for EnemySpawnimpl GameObjectLike for EnemySpawnimpl HasHits for EnemySpawnimpl HasStore for EnemySpawnimpl MoveTarget for EnemySpawnimpl MyLike for EnemySpawnimpl RangedAttackTarget for EnemySpawnimpl TowerAttackTarget for EnemySpawnimpl TransferTarget for EnemySpawnimpl WithdrawTarget for EnemySpawnfn[T : GameObjectLike] EnemySpawn::find_closest(self : EnemySpawn, candidates : Array[T], by? : ClosestMetric) -> T?fn[T : GameObjectLike] EnemySpawn::find_in_range(self : EnemySpawn, candidates : Array[T], range : Int) -> Array[T]pub struct EnemyTower {
// private fields
}impl AttackTarget for EnemyTowerimpl GameObjectLike for EnemyTowerimpl HasHits for EnemyTowerimpl HasStore for EnemyTowerimpl MoveTarget for EnemyTowerimpl MyLike for EnemyTowerimpl RangedAttackTarget for EnemyTowerimpl TowerAttackTarget for EnemyTowerimpl TransferTarget for EnemyTowerimpl WithdrawTarget for EnemyTowerfn[T : GameObjectLike] EnemyTower::find_closest(self : EnemyTower, candidates : Array[T], by? : ClosestMetric) -> T?fn[T : GameObjectLike] EnemyTower::find_in_range(self : EnemyTower, candidates : Array[T], range : Int) -> Array[T]pub struct Flag {
// private fields
}impl GameObjectLike for Flagimpl MoveTarget for Flagfn[T : GameObjectLike] Flag::find_closest(self : Flag, candidates : Array[T], by? : ClosestMetric) -> T?fn[T : GameObjectLike] Flag::find_in_range(self : Flag, candidates : Array[T], range : Int) -> Array[T]pub struct GameObject {
// private fields
}impl GameObjectLike for GameObjectimpl MoveTarget for GameObjectfn[T : GameObjectLike] GameObject::find_closest(self : GameObject, candidates : Array[T], by? : ClosestMetric) -> T?fn[T : GameObjectLike] GameObject::find_in_range(self : GameObject, candidates : Array[T], range : Int) -> Array[T]pub struct MyCreep {
// private fields
}impl AttackTarget for MyCreepimpl GameObjectLike for MyCreepimpl HealTarget for MyCreepimpl MoveTarget for MyCreepimpl RangedAttackTarget for MyCreepimpl TowerAttackTarget for MyCreepimpl TransferTarget for MyCreepfn[T : GameObjectLike] MyCreep::find_closest(self : MyCreep, candidates : Array[T], by? : ClosestMetric) -> T?fn[T : GameObjectLike] MyCreep::find_in_range(self : MyCreep, candidates : Array[T], range : Int) -> Array[T]fn[T : MoveTarget + GameObjectLike] MyCreep::move_if_not_in_range(self : MyCreep, target : T, result : ActionResult) -> Unitfn[T : RangedAttackTarget + GameObjectLike] MyCreep::ranged_attack(self : MyCreep, target : T) -> ActionResultfn[T : RangedAttackTarget + GameObjectLike] MyCreep::ranged_attack_or_move(self : MyCreep, target : T) -> Unitfn[T : TransferTarget + GameObjectLike] MyCreep::transfer(self : MyCreep, target : T, resource : ResourceKind) -> ActionResultfn[T : TransferTarget + GameObjectLike] MyCreep::transfer_energy(self : MyCreep, target : T) -> ActionResultfn[T : TransferTarget + GameObjectLike] MyCreep::transfer_energy_or_move(self : MyCreep, target : T) -> Unitfn[T : TransferTarget + GameObjectLike] MyCreep::transfer_or_move(self : MyCreep, target : T, resource : ResourceKind) -> Unitfn[T : WithdrawTarget + GameObjectLike] MyCreep::withdraw(self : MyCreep, target : T, resource : ResourceKind) -> ActionResultfn[T : WithdrawTarget + GameObjectLike] MyCreep::withdraw_energy(self : MyCreep, target : T) -> ActionResultfn[T : WithdrawTarget + GameObjectLike] MyCreep::withdraw_energy_or_move(self : MyCreep, target : T) -> Unitfn[T : WithdrawTarget + GameObjectLike] MyCreep::withdraw_or_move(self : MyCreep, target : T, resource : ResourceKind) -> Unitpub struct MySpawn {
// private fields
}impl AttackTarget for MySpawnimpl GameObjectLike for MySpawnimpl MoveTarget for MySpawnimpl RangedAttackTarget for MySpawnimpl TowerAttackTarget for MySpawnimpl TransferTarget for MySpawnimpl WithdrawTarget for MySpawnfn[T : GameObjectLike] MySpawn::find_closest(self : MySpawn, candidates : Array[T], by? : ClosestMetric) -> T?fn[T : GameObjectLike] MySpawn::find_in_range(self : MySpawn, candidates : Array[T], range : Int) -> Array[T]pub struct MyTower {
// private fields
}impl AttackTarget for MyTowerimpl GameObjectLike for MyTowerimpl MoveTarget for MyTowerimpl RangedAttackTarget for MyTowerimpl TowerAttackTarget for MyTowerimpl TransferTarget for MyTowerimpl WithdrawTarget for MyTowerfn[T : TowerAttackTarget + GameObjectLike] MyTower::attack(self : MyTower, target : T) -> ActionResultfn[T : GameObjectLike] MyTower::find_closest(self : MyTower, candidates : Array[T], by? : ClosestMetric) -> T?fn[T : GameObjectLike] MyTower::find_in_range(self : MyTower, candidates : Array[T], range : Int) -> Array[T]pub struct Source {
// private fields
}impl GameObjectLike for Sourceimpl HarvestTarget for Sourceimpl MoveTarget for Sourcefn[T : GameObjectLike] Source::find_closest(self : Source, candidates : Array[T], by? : ClosestMetric) -> T?fn[T : GameObjectLike] Source::find_in_range(self : Source, candidates : Array[T], range : Int) -> Array[T]pub struct Spawning {
// private fields
}pub struct Store {
// private fields
}pub struct Structure {
// private fields
}impl AttackTarget for Structureimpl GameObjectLike for Structureimpl MoveTarget for Structureimpl RangedAttackTarget for Structureimpl TowerAttackTarget for Structurefn[T : GameObjectLike] Structure::find_closest(self : Structure, candidates : Array[T], by? : ClosestMetric) -> T?fn[T : GameObjectLike] Structure::find_in_range(self : Structure, candidates : Array[T], range : Int) -> Array[T]pub struct StructureContainer {
// private fields
}impl AttackTarget for StructureContainerimpl GameObjectLike for StructureContainerimpl HasHits for StructureContainerimpl HasStore for StructureContainerimpl MoveTarget for StructureContainerimpl MyLike for StructureContainerimpl RangedAttackTarget for StructureContainerimpl TowerAttackTarget for StructureContainerimpl TransferTarget for StructureContainerimpl WithdrawTarget for StructureContainerfn[T : GameObjectLike] StructureContainer::find_closest(self : StructureContainer, candidates : Array[T], by? : ClosestMetric) -> T?fn[T : GameObjectLike] StructureContainer::find_in_range(self : StructureContainer, candidates : Array[T], range : Int) -> Array[T]pub struct StructureSpawn {
// private fields
}impl AttackTarget for StructureSpawnimpl GameObjectLike for StructureSpawnimpl HasHits for StructureSpawnimpl HasStore for StructureSpawnimpl MoveTarget for StructureSpawnimpl MyLike for StructureSpawnimpl RangedAttackTarget for StructureSpawnimpl TowerAttackTarget for StructureSpawnimpl TransferTarget for StructureSpawnimpl WithdrawTarget for StructureSpawnfn[T : GameObjectLike] StructureSpawn::find_closest(self : StructureSpawn, candidates : Array[T], by? : ClosestMetric) -> T?fn[T : GameObjectLike] StructureSpawn::find_in_range(self : StructureSpawn, candidates : Array[T], range : Int) -> Array[T]pub struct StructureTower {
// private fields
}impl AttackTarget for StructureTowerimpl GameObjectLike for StructureTowerimpl HasHits for StructureTowerimpl HasStore for StructureTowerimpl MoveTarget for StructureTowerimpl MyLike for StructureTowerimpl RangedAttackTarget for StructureTowerimpl TowerAttackTarget for StructureTowerimpl TransferTarget for StructureTowerimpl WithdrawTarget for StructureTowerfn[T : GameObjectLike] StructureTower::find_closest(self : StructureTower, candidates : Array[T], by? : ClosestMetric) -> T?fn[T : GameObjectLike] StructureTower::find_in_range(self : StructureTower, candidates : Array[T], range : Int) -> Array[T]fn[From : GameObjectLike, T : GameObjectLike] find_closest(from : From, candidates : Array[T], by? : ClosestMetric) -> T?fn[From : GameObjectLike, T : GameObjectLike] find_in_range(from : From, candidates : Array[T], range : Int) -> Array[T]Dependencies