diff --git a/.editorconfig b/.editorconfig index 02ff606f3..ca2573dc0 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,6 +1,6 @@ [*] charset=utf-8 -end_of_line=crlf +end_of_line=lf insert_final_newline=false indent_style=space indent_size=4 @@ -8,12 +8,17 @@ indent_size=4 [{.babelrc,.stylelintrc,.eslintrc,jest.config,*.json,*.jsb3,*.jsb2,*.bowerrc}] indent_style=space indent_size=2 +end_of_line=lf +trim_trailing_whitespace=true -[{*.ats,*.ts}] +[{*.ats,*.ts,*.js}] indent_style=tab tab_width=4 +end_of_line=lf +trim_trailing_whitespace=true [tslint.json] indent_style=space indent_size=2 - +end_of_line=lf +trim_trailing_whitespace=true diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 000000000..12be953bb --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,192 @@ +/* +👋 Hi! This file was autogenerated by tslint-to-eslint-config. +https://github.com/typescript-eslint/tslint-to-eslint-config + +It represents the closest reasonable ESLint configuration to this +project's original TSLint configuration. + +We recommend eventually switching this configuration to extend from +the recommended rulesets in typescript-eslint. +https://github.com/typescript-eslint/tslint-to-eslint-config/blob/master/docs/FAQs.md + +Happy linting! 💖 +*/ +module.exports = { + "env": { + "es6": true + }, + "extends": [ + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking", + "prettier" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "project": "tsconfig.json", + "sourceType": "module" + }, + "plugins": [ + "eslint-plugin-jsdoc", + "eslint-plugin-prefer-arrow", + "@typescript-eslint" + ], + "root": true, + "rules": { + "@typescript-eslint/adjacent-overload-signatures": "error", + "@typescript-eslint/array-type": "off", + "@typescript-eslint/ban-types": "off", + "@typescript-eslint/consistent-type-assertions": "off", + "@typescript-eslint/consistent-type-definitions": "off", + "@typescript-eslint/dot-notation": "error", + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/explicit-member-accessibility": [ + "error", + { + "accessibility": "no-public" + } + ], + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/indent": [ + "off", + "tab" + ], + "@typescript-eslint/member-ordering": "off", + "@typescript-eslint/naming-convention": [ + "off", + { + "selector": "variable", + "format": [ + "camelCase", + "UPPER_CASE", + "PascalCase" + ], + "leadingUnderscore": "allow", + "trailingUnderscore": "forbid" + } + ], + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-empty-interface": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-misused-new": "error", + "@typescript-eslint/no-namespace": "error", + "@typescript-eslint/no-parameter-properties": "off", + "@typescript-eslint/no-shadow": [ + "off", + { + "hoist": "all" + } + ], + "@typescript-eslint/no-unused-expressions": "error", + "@typescript-eslint/no-use-before-define": "off", + "@typescript-eslint/no-var-requires": "error", + "@typescript-eslint/prefer-for-of": "error", + "@typescript-eslint/prefer-function-type": "error", + "@typescript-eslint/prefer-namespace-keyword": "error", + "@typescript-eslint/triple-slash-reference": [ + "error", + { + "path": "always", + "types": "prefer-import", + "lib": "always" + } + ], + "@typescript-eslint/typedef": "off", + "@typescript-eslint/unified-signatures": "error", + "@typescript-eslint/no-namespace": "off", + "@typescript-eslint/no-base-to-string": "off", + "@typescript-eslint/restrict-template-expressions": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "arrow-parens": [ + "off", + "always" + ], + "brace-style": [ + "error", + "1tbs" + ], + "comma-dangle": "off", + "complexity": "off", + "constructor-super": "error", + "curly": [ + "error", + "multi-line" + ], + "dot-notation": "off", + "eqeqeq": [ + "off", + "always" + ], + "guard-for-in": "off", + "id-denylist": [ + "error", + "any", + "Number", + "number", + "String", + "string", + "Boolean", + "boolean", + "Undefined", + "undefined" + ], + "id-match": "error", + "indent": "off", + "jsdoc/check-alignment": "error", + "jsdoc/check-indentation": "error", + "max-classes-per-file": "off", + "max-len": [ + "error", + { + "code": 120 + } + ], + "new-parens": "error", + "no-bitwise": "off", + "no-caller": "error", + "no-cond-assign": "error", + "no-console": "off", + "no-debugger": "error", + "no-empty": "off", + "no-empty-function": "off", + "no-eval": "error", + "no-fallthrough": "off", + "no-invalid-this": "off", + "no-multiple-empty-lines": "off", + "no-new-wrappers": "error", + "no-shadow": "off", + "no-throw-literal": "error", + "no-trailing-spaces": "off", + "no-undef-init": "error", + "no-underscore-dangle": "off", + "no-unsafe-finally": "error", + "no-unused-expressions": "off", + "no-unused-labels": "error", + "no-use-before-define": "off", + "no-var": "error", + "object-shorthand": "off", + "one-var": [ + "off", + "never" + ], + "prefer-arrow/prefer-arrow-functions": "off", + "prefer-const": [ + "error", + { + "destructuring": "all" + } + ], + "quotes": "off", + "radix": "error", + "spaced-comment": [ + "error", + "always", + { + "markers": [ + "/" + ] + } + ], + "use-isnan": "error", + "valid-typeof": "off" + } +}; diff --git a/.gitignore b/.gitignore index 3b4899caf..0d6f2e546 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,6 @@ screeps.json /dist /node_modules /typings -/.rpt2_cache /tsc-out # Ignore Mathematica notebooks so they don't add 50 billion lines to contribution count @@ -27,78 +26,3 @@ screeps.json # Screeps config file /config.json - -# Numerous always-ignore extensions -*.diff -*.err -*.orig -*.log -*.rej -*.swo -*.swp -*.zip -*.vi - -# Editor folders -.cache -.project -.settings -.tmproj -*.esproj -nbproject -*.sublime-project -*.sublime-workspace -.idea - -# ========================= -# Operating System Files -# ========================= - -# OSX -# ========================= - -.DS_Store -.AppleDouble -.LSOverride - -# Thumbnails -._* - -# Files that might appear on external disk -.Spotlight-V100 -.Trashes - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -# Windows -# ========================= - -# Windows image file caches -Thumbs.db -ehthumbs.db - -# Folder config file -Desktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msm -*.msp - -# Windows shortcuts -*.lnk - -# VStudio files -applicationhost.config -*.suo -*.njsproj -*.sln \ No newline at end of file diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 000000000..e9847d93b --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +nodejs 18.9.1 diff --git a/package.json b/package.json index a51e2a7b0..5d7282d77 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "Overmind", + "name": "overmind", "version": "0.6.0", "description": "Overmind Screeps AI", "author": "Ben Bartlett", @@ -14,7 +14,7 @@ }, "scripts": { "build": "tsc -p .", - "lint": "tslint \"src/**/*.ts\"", + "lint": "eslint \"src/**/*.ts\"", "clean": "rm -rf tsc-out && rm -rf dist", "obfuscate": "tsc && javascript-obfuscator tsc-out/Overmind.js --output src/Overmind_obfuscated.js --compact false --self-defending true --string-array true --string-array-threshold 1 --string-array-encoding true && javascript-obfuscator tsc-out/assimilation/Assimilator.js --output src/assimilation/Assimilator_obfuscated.js --compact false --self-defending true --string-array true --string-array-threshold 1 --string-array-encoding true", "compile": "rollup -c", @@ -23,26 +23,32 @@ "push-screepsplus": "rollup -c --environment DEST:screepsplus", "test": "npm run clean && npm run build" }, + "type": "module", "engines": { - "node": "10.x" + "node": ">= 18.9.1" }, "devDependencies": { - "@types/node": "^10.17.18", + "@rollup/plugin-commonjs": "^25.0.3", + "@rollup/plugin-node-resolve": "^15.1.0", + "@types/columnify": "^1.5.1", "@types/lodash": "3.10.2", - "@types/screeps": "^3.1.0", - "@rollup/plugin-commonjs": "^11.0.2", - "@rollup/plugin-node-resolve": "^7.1.1", - "rollup": "2.6.0", - "rollup-plugin-progress": "1.1.1", - "rollup-plugin-screeps": "1.0.0", - "rollup-plugin-typescript2": "0.27.0", - "tslint": "^5.20.0", - "typedoc": "^0.14.2", - "typescript": "2.9.2" + "@types/screeps": "^3.3.3", + "@typescript-eslint/eslint-plugin": "^6.3.0", + "@typescript-eslint/parser": "^6.3.0", + "eslint": "^8.46.0", + "eslint-config-prettier": "^8.10.0", + "eslint-plugin-jsdoc": "^46.4.5", + "eslint-plugin-prefer-arrow": "^1.2.3", + "rollup": "^3.27.1", + "rollup-plugin-progress": "^1.1.2", + "rollup-plugin-screeps": "^1.0.1", + "rollup-plugin-typescript2": "^0.35.0", + "typedoc": "^0.24.8", + "typescript": "^5.1.6" }, "dependencies": { "@tensorflow/tfjs": "^1.2.11", - "columnify": "1.5.4", + "columnify": "^1.6.0", "onnxjs": "^0.1.6", "source-map": "0.7.3" } diff --git a/rollup.config.js b/rollup.config.js index 5ebeaa6ed..295e1278d 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -5,14 +5,17 @@ import commonjs from '@rollup/plugin-commonjs'; import progress from "rollup-plugin-progress"; import typescript from "rollup-plugin-typescript2"; import screeps from "rollup-plugin-screeps"; +import { readFileSync } from 'fs'; let cfg; const dest = process.env.DEST; if (!dest) { console.log('\x1b[46m%s\x1b[0m \x1b[36m%s\x1b[0m', 'Compiling Overmind...', '(deploy destination: none)'); -} else if ((cfg = require("./screeps")[dest]) == null) { - throw new Error("Invalid upload destination"); } else { + cfg = JSON.parse(readFileSync("./screeps.json"))[dest]; + if (!cfg) { + throw new Error("Invalid upload destination"); + } console.log('\x1b[46m%s\x1b[0m \x1b[36m%s\x1b[0m', 'Compiling Overmind...', `(deploy destination: ${dest})`); console.log(`Pushing at time: ${new Date()})`); } @@ -28,13 +31,7 @@ export default { plugins: [ progress({clearLine: true}), resolve(), - commonjs({ - namedExports: { - 'src/Overmind_obfuscated': ['_Overmind'], - 'screeps-profiler': ['profiler'], - 'columnify': ['columnify'] - } - }), + commonjs(), typescript({tsconfig: "./tsconfig.json"}), screeps({config: cfg, dryRun: cfg == null}) ], @@ -70,4 +67,4 @@ export default { '//\n' }, -} \ No newline at end of file +} diff --git a/src/Colony.ts b/src/Colony.ts index 4b503ee91..afd1227f3 100644 --- a/src/Colony.ts +++ b/src/Colony.ts @@ -62,7 +62,7 @@ export interface BunkerData { export interface ColonyMemory { defcon: { - level: number, + level: DEFCON, tick: number, }; expansionData: ColonyExpansionData; @@ -70,6 +70,22 @@ export interface ColonyMemory { outposts: { [roomName: string]: OutpostData }; suspend?: boolean; debug?: boolean; + persistent?: boolean; + evolutionChamber?: { + activeReaction: never; + reactionQueue: never; + status: never; + statusTick: never; + }; + roomPlanner?: { + lastGenerated: number; + }; + roadPlanner?: { + roadCoordsPacked: { [roomName: string]: string }; + }; + barrierPlanner?: { + barrierCoordsPacked: string; + } } // Outpost that is currently not being maintained @@ -100,14 +116,6 @@ const getDefaultColonyMemory: () => ColonyMemory = () => ({ outposts : {}, }); -export interface Assets { - energy: number; - power: number; - ops: number; - - [resourceType: string]: number; -} - /** * Colonies are the highest-level object other than the global Overmind. A colony groups together all rooms, structures, * creeps, utilities, etc. which are run from a single owned room. @@ -127,7 +135,7 @@ export class Colony { // abandonedOutposts: AbandonedOutpost[]; // Outposts that are not currently maintained, not used for now rooms: Room[]; // All rooms including the primary room pos: RoomPosition; - assets: Assets; + assets: StoreContents; // Physical colony structures and roomObjects controller: StructureController; // These are all duplicated from room properties spawns: StructureSpawn[]; // | @@ -144,6 +152,7 @@ export class Colony { observer: StructureObserver | undefined; // | tombstones: Tombstone[]; // | Tombstones in all colony rooms drops: { [resourceType: string]: Resource[] }; // | Dropped resources in all colony rooms + ruins: Ruin[]; // | Ruins in all colony rooms sources: Source[]; // | Sources in all colony rooms extractors: StructureExtractor[]; // | All extractors in owned and remote rooms flags: Flag[]; // | Flags assigned to the colony @@ -167,8 +176,8 @@ export class Colony { // praiseSite: PraiseSite | undefined; // Operational state level: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8; // Level of the colony's main room - stage: number; // The stage of the colony "lifecycle" - defcon: number; // + stage: ColonyStage; // The stage of the colony "lifecycle" + defcon: DEFCON; // state: { bootstrapping?: boolean; // Whether colony is bootstrapping or recovering from crash isIncubating?: boolean; // If the colony is incubating @@ -232,7 +241,9 @@ export class Colony { } }); // Register colony globally to allow 'W1N1' and 'w1n1' to refer to Overmind.colonies.W1N1 + // @ts-expect-error global getter for Colonies global[this.name] = this; + // @ts-expect-error global getter for Colonies global[this.name.toLowerCase()] = this; // Build the colony this.build(roomName, outposts); @@ -343,6 +354,7 @@ export class Colony { this.constructionSites = _.flatten(_.map(this.rooms, room => room.constructionSites)); this.tombstones = _.flatten(_.map(this.rooms, room => room.tombstones)); this.drops = _.merge(_.map(this.rooms, room => room.drops)); + this.ruins = _.merge(_.map(this.rooms, room => room.ruins)); this.repairables = _.flatten(_.map(this.rooms, room => room.repairables)); this.rechargeables = _.flatten(_.map(this.rooms, room => room.rechargeables)); // Register assets @@ -388,6 +400,7 @@ export class Colony { $.set(this, 'constructionSites', () => _.flatten(_.map(this.rooms, room => room.constructionSites)), 10); $.set(this, 'tombstones', () => _.flatten(_.map(this.rooms, room => room.tombstones)), 5); this.drops = _.merge(_.map(this.rooms, room => room.drops)); + $.set(this, 'ruins', () => _.flatten(_.map(this.rooms, room => room.ruins)), 5); // Register assets this.assets = this.computeAssets(); } @@ -398,10 +411,11 @@ export class Colony { private refreshRoomObjects(): void { $.refresh(this, 'controller', 'extensions', 'links', 'towers', 'powerSpawn', 'nuker', 'observer', 'spawns', 'storage', 'terminal', 'factory', 'labs', 'sources', 'extractors', 'constructionSites', 'repairables', - 'rechargeables'); + 'rechargeables', 'ruins'); $.set(this, 'constructionSites', () => _.flatten(_.map(this.rooms, room => room.constructionSites)), 10); $.set(this, 'tombstones', () => _.flatten(_.map(this.rooms, room => room.tombstones)), 5); this.drops = _.merge(_.map(this.rooms, room => room.drops)); + $.set(this, 'ruins', () => _.flatten(_.map(this.rooms, room => room.ruins)), 5); // Re-compute assets this.assets = this.computeAssets(); } @@ -625,13 +639,13 @@ export class Colony { * Summarizes the total of all resources in colony store structures, labs, and some creeps. Will always return * 0 for an asset that it has none of (not undefined) */ - private computeAssets(verbose = false): Assets { + private computeAssets(verbose = false): StoreContents { // Include storage structures, lab contents, and manager carry const assetStructures = _.compact([this.storage, this.terminal, this.factory, ...this.labs]); const assetCreeps = [...this.getCreepsByRole(Roles.queen), ...this.getCreepsByRole(Roles.manager)]; const assetStores = _.map([...assetStructures, ...assetCreeps], thing => thing!.store); - const allAssets = mergeSum([...assetStores, ALL_ZERO_ASSETS]) as Assets; + const allAssets = mergeSum(...assetStores, ALL_ZERO_ASSETS); if (verbose) log.debug(`${this.room.print} assets: ` + JSON.stringify(allAssets)); return allAssets; diff --git a/src/Overseer.ts b/src/Overseer.ts index 68664c3b3..f04680a80 100644 --- a/src/Overseer.ts +++ b/src/Overseer.ts @@ -90,13 +90,17 @@ export class Overseer implements IOverseer { try { callback(); } catch (e) { - if (identifier) { - e.name = `Caught unhandled exception at ${'' + callback} (identifier: ${identifier}): \n` - + e.name + '\n' + e.stack; + if (e instanceof Error) { + if (identifier) { + e.name = `Caught unhandled exception at ${'' + callback} (identifier: ${identifier}): \n` + + e.name + '\n' + e.stack; + } else { + e.name = `Caught unhandled exception at ${'' + callback}: \n` + e.name + '\n' + e.stack; + } + Overmind.exceptions.push(e); } else { - e.name = `Caught unhandled exception at ${'' + callback}: \n` + e.name + '\n' + e.stack; + log.error(`Got a non-Error exception`, e); } - Overmind.exceptions.push(e); } } else { callback(); @@ -180,11 +184,22 @@ export class Overseer implements IOverseer { } } } + + // Pick up all nontrivial ruin resources + for (const ruin of colony.ruins) { + if (ruin.store.getUsedCapacity() > LogisticsNetwork.settings.droppedEnergyThreshold + || ruin.store.getUsedCapacity() > ruin.store.energy) { + if (colony.bunker && ruin.pos.isEqualTo(colony.bunker.anchor)) continue; + colony.logisticsNetwork.requestOutput(ruin, {resourceType: 'all'}); + } + } + // Place a logistics request directive for every tombstone with non-empty store that isn't on a container for (const tombstone of colony.tombstones) { - if (_.sum(tombstone.store) > LogisticsNetwork.settings.droppedEnergyThreshold - || _.sum(tombstone.store) > tombstone.store.energy) { - if (colony.bunker && tombstone.pos.isEqualTo(colony.bunker.anchor)) continue; + if (tombstone.store.getUsedCapacity() > LogisticsNetwork.settings.droppedEnergyThreshold + || tombstone.store.getUsedCapacity() > tombstone.store.energy) { + if (colony.bunker && tombstone.pos.isEqualTo(colony.bunker.anchor) + || tombstone.pos.lookForStructure(STRUCTURE_CONTAINER)) continue; colony.logisticsNetwork.requestOutput(tombstone, {resourceType: 'all'}); } } @@ -314,7 +329,7 @@ export class Overseer implements IOverseer { // Place nuke response directive if there is a nuke present in colony room if (colony.room && colony.level >= DirectiveNukeResponse.requiredRCL) { for (const nuke of colony.room.find(FIND_NUKES)) { - DirectiveNukeResponse.createIfNotPresent(nuke.pos, 'pos'); + DirectiveNukeResponse.createIfNotPresent(colony.controller.pos, 'room'); } } } @@ -356,6 +371,7 @@ export class Overseer implements IOverseer { } private computePossibleOutposts(colony: Colony, depth = 3): string[] { + const colonyRoomStatus = Game.map.getRoomStatus(colony.room.name).status; return _.filter(Cartographer.findRoomsInRange(colony.room.name, depth), roomName => { if (Cartographer.roomType(roomName) != ROOMTYPE_CONTROLLER) { return false; @@ -378,7 +394,7 @@ export class Overseer implements IOverseer { } const neighboringRooms = _.values(Game.map.describeExits(roomName)) as string[]; const isReachableFromColony = _.any(neighboringRooms, r => colony.roomNames.includes(r)); - return isReachableFromColony && Game.map.isRoomAvailable(roomName); + return isReachableFromColony && Game.map.getRoomStatus(roomName).status === colonyRoomStatus; }); } diff --git a/src/algorithms/utf15.ts b/src/algorithms/utf15.ts index 7278c179d..1a161f9f0 100644 --- a/src/algorithms/utf15.ts +++ b/src/algorithms/utf15.ts @@ -1,7 +1,7 @@ +// @ts-nocheck // This module has been adapted from Mototroller's utf15 library: // https://github.com/screepers/utf15 - /* tslint:disable:no-bitwise prefer-for-of variable-name no-unused-expression */ import {profile} from '../profiler/decorator'; diff --git a/src/assimilation/initializer.ts b/src/assimilation/initializer.ts index 0e93d9daa..439d40227 100644 --- a/src/assimilation/initializer.ts +++ b/src/assimilation/initializer.ts @@ -1,3 +1,4 @@ import _Assimilator from './Assimilator_obfuscated'; +// @ts-expect-error obfuscated global.Assimilator = new _Assimilator(); diff --git a/src/caching/GlobalCache.ts b/src/caching/GlobalCache.ts index 7b37f006c..554ea03ef 100644 --- a/src/caching/GlobalCache.ts +++ b/src/caching/GlobalCache.ts @@ -31,6 +31,7 @@ export class $ { // $ = cash = cache... get it? :D return _cache.structures[cacheKey] as T[]; } + // eslint-disable-next-line static number(saver: { ref: string }, key: string, callback: () => number, timeout = SHORT_CACHE_TIMEOUT): number { const cacheKey = saver.ref + '#' + key; if (_cache.numbers[cacheKey] == undefined || Game.time > _cache.expiration[cacheKey]) { @@ -66,7 +67,7 @@ export class $ { // $ = cash = cache... get it? :D _cache.lists[cacheKey] = callback(); _cache.expiration[cacheKey] = getCacheExpiration(timeout, Math.ceil(timeout / 10)); } - return _cache.lists[cacheKey]; + return _cache.lists[cacheKey] as T[]; } /** @@ -98,9 +99,9 @@ export class $ { // $ = cash = cache... get it? :D } static set(thing: T, key: K, - callback: () => (T[K] & (undefined | HasID | HasID[])), + callback: () => (T[K] & (undefined | _HasId | _HasId[])), timeout = CACHE_TIMEOUT): void { - const cacheKey = thing.ref + '$' + key; + const cacheKey = thing.ref + '$' + key; if (!_cache.things[cacheKey] || Game.time > _cache.expiration[cacheKey]) { // Recache if new entry or entry is expired _cache.things[cacheKey] = callback(); @@ -109,39 +110,41 @@ export class $ { // $ = cash = cache... get it? :D // Refresh structure list by ID if not already done on current tick if ((_cache.accessed[cacheKey] || 0) < Game.time) { if (_.isArray(_cache.things[cacheKey])) { - _cache.things[cacheKey] = _.compact(_.map(_cache.things[cacheKey] as HasID[], - s => Game.getObjectById(s.id))) as HasID[]; + _cache.things[cacheKey] = _.compact(_.map(_cache.things[cacheKey] as _HasId[], + s => Game.getObjectById(s.id))) as _HasId[]; } else { - _cache.things[cacheKey] = Game.getObjectById((_cache.things[cacheKey]).id) as HasID; + _cache.things[cacheKey] = Game.getObjectById((<_HasId>_cache.things[cacheKey]).id) as _HasId; } _cache.accessed[cacheKey] = Game.time; } } - thing[key] = _cache.things[cacheKey] as T[K] & (undefined | HasID | HasID[]); + thing[key] = _cache.things[cacheKey] as T[K] & (undefined | _HasId | _HasId[]); } - static refresh, K extends string>(thing: T, ...keys: K[]): void { + static refresh, K extends string>(thing: T, ...keys: K[]): void { _.forEach(keys, function(key) { if (thing[key]) { if (_.isArray(thing[key])) { - thing[key] = _.compact(_.map(thing[key] as HasID[], s => Game.getObjectById(s.id))) as T[K]; + thing[key] = _.compact(_.map(thing[key] as _HasId[], s => Game.getObjectById(s.id))) as T[K]; } else { - thing[key] = Game.getObjectById((thing[key]).id) as T[K]; + thing[key] = Game.getObjectById((<_HasId>thing[key]).id) as T[K]; } } }); } - static refreshObject, + static refreshObject, K extends string>(thing: T, ...keys: K[]): void { _.forEach(keys, function(key) { if (_.isObject(thing[key])) { for (const prop in thing[key]) { if (_.isArray(thing[key][prop])) { - thing[key][prop] = _.compact(_.map(thing[key][prop] as HasID[], - s => Game.getObjectById(s.id))) as HasID[]; + // @ts-expect-error + thing[key][prop] = <_HasId[]>_.compact(_.map(thing[key][prop] as _HasId[], + s => Game.getObjectById(s.id))); } else { - thing[key][prop] = Game.getObjectById((thing[key][prop]).id) as undefined | HasID; + // @ts-expect-error + thing[key][prop] = <_HasId>Game.getObjectById((<_HasId>thing[key][prop]).id); } } } diff --git a/src/console/Console.ts b/src/console/Console.ts index 004ee7e8a..8b68a2c88 100644 --- a/src/console/Console.ts +++ b/src/console/Console.ts @@ -13,6 +13,51 @@ import {log} from './log'; type RecursiveObject = { [key: string]: number | RecursiveObject }; +declare global { + var help: string; + var info: () => string; + var notifications: () => string; + var debug: (obj: any) => string; + var stopDebug: (obj: any) => string; + var setMode: (mode: operationMode) => string; + var setSignature: (signature: string | undefined) => string | undefined; + var timeit: (cb: () => any, repeat?: number) => string; + var profileOverlord: (overlord: string | Overlord, ticks?: number | undefined) => string; + var finishProfilingOverlord: (overlord: string | Overlord) => string; + var setLogLevel: (value: number) => void; + var suspendColony: (roomName: string) => string; + var unsuspendColony: (roomName: string) => string; + var listSuspendedColonies: () => string; + var openRoomPlanner: (roomName: string) => string; + var closeRoomPlanner: (roomName: string) => string; + var cancelRoomPlanner: (roomName: string) => string; + var listActiveRoomPlanners: () => string; + var destroyErrantStructures: (roomName: string) => string; + var destroyAllHostileStructures: (roomName: string) => string; + var destroyAllBarriers: (roomName: string) => string; + var listConstructionSites: () => string; + var removeUnbuiltConstructionSites: () => string; + var listDirectives: () => string; + var listPersistentDirectives: () => string; + var removeAllLogisticsDirectives: () => string; + var removeFlagsByColor: (color: ColorConstant, secondaryColor: ColorConstant) => string; + var removeErrantFlags: () => string; + var deepCleanMemory: () => string; + var startRemoteDebugSession: () => string; + var endRemoteDebugSession: () => string; + var profileMemory: () => string; + var cancelMarketOrders: () => string; + var setRoomUpgradeRate: (roomName: string, rate: number) => string; + var getEmpireMineralDistribution: () => string; + var listPortals: () => string; + var evaluateOutpostEfficiencies: () => string; + var evaluatePotentialOutpostEfficiencies: () => string; +} + +interface MemoryDebug { + debug?: boolean; +} + /** * OvermindConsole registers a number of global methods for direct use in the Screeps console */ @@ -187,12 +232,12 @@ export class OvermindConsole { // Debugging methods =============================================================================================== - static debug(thing: { name?: string, ref?: string, memory: any }): string { + static debug(thing: { name?: string, ref?: string, memory: MemoryDebug }): string { thing.memory.debug = true; return `Enabled debugging for ${thing.name || thing.ref || '(no name or ref)'}.`; } - static stopDebug(thing: { name?: string, ref?: string, memory: any }): string { + static stopDebug(thing: { name?: string, ref?: string, memory: MemoryDebug }): string { delete thing.memory.debug; return `Disabled debugging for ${thing.name || thing.ref || '(no name or ref)'}.`; } @@ -210,8 +255,8 @@ export class OvermindConsole { static print(...args: any[]): string { let message = ''; for (const arg of args) { - let cache: any = []; - const msg = JSON.stringify(arg, function(key, value) { + let cache: any[] = []; + const msg = JSON.stringify(arg, function(key, value: any): any { if (typeof value === 'object' && value !== null) { if (cache.indexOf(value) !== -1) { // Duplicate reference found @@ -228,6 +273,7 @@ export class OvermindConsole { } return value; }, '\t'); + // @ts-expect-error Clear out the cache cache = null; message += '\n' + msg; } @@ -235,12 +281,12 @@ export class OvermindConsole { } static timeit(callback: () => any, repeat = 1): string { - let start, used, i: number; - start = Game.cpu.getUsed(); + const start = Game.cpu.getUsed(); + let i: number; for (i = 0; i < repeat; i++) { callback(); } - used = Game.cpu.getUsed() - start; + const used = Game.cpu.getUsed() - start; return `CPU used: ${used}. Repetitions: ${repeat} (${used / repeat} each).`; } @@ -256,7 +302,7 @@ export class OvermindConsole { } } - static finishProfilingOverlord(overlord: Overlord | string, ticks?: number): string { + static finishProfilingOverlord(overlord: Overlord | string): string { const overlordInstance = typeof overlord == 'string' ? Overmind.overlords[overlord] : overlord as Overlord | undefined; if (!overlordInstance) { @@ -358,7 +404,7 @@ export class OvermindConsole { (colony: Colony) => colony.roomPlanner.active); const names: string[] = _.map(coloniesWithActiveRoomPlanners, colony => colony.room.print); if (names.length > 0) { - console.log('Colonies with active room planners: ' + names); + console.log('Colonies with active room planners: ' + names.toString()); return ''; } else { return `No colonies with active room planners`; @@ -442,7 +488,7 @@ export class OvermindConsole { // Structure management ============================================================================================ static destroyErrantStructures(roomName: string): string { - const colony = Overmind.colonies[roomName] as Colony; + const colony = Overmind.colonies[roomName]; if (!colony) return `${roomName} is not a valid colony!`; const room = colony.room; const allStructures = room.find(FIND_STRUCTURES); @@ -493,7 +539,7 @@ export class OvermindConsole { return msg; } - // Colony Management ================================================================================================= + // Colony Management =============================================================================================== static setRoomUpgradeRate(roomName: string, rate: number): string { const colony: Colony = Overmind.colonies[roomName]; @@ -515,10 +561,9 @@ export class OvermindConsole { const colonies = getAllColonies(); const allPortals = colonies.map(colony => RoomIntel.findPortalsInRange(colony.name, rangeFromColonies)); let ret = `Empire Portal Census \n`; - for (const colonyId in allPortals) { - const portals = allPortals[colonyId]; + for (const [colonyId, portals] of Object.entries(allPortals)) { if (_.keys(portals).length > 0) { - ret += `Colony ${colonies[colonyId].print}: \n`; + ret += `Colony ${Overmind.colonies[colonyId].print}: \n`; } for (const portalRoomName of _.keys(portals)) { const samplePortal = _.first(portals[portalRoomName]); // don't need to list all 8 in a room @@ -530,71 +575,55 @@ export class OvermindConsole { } static evaluateOutpostEfficiencies(): string { - const colonies = getAllColonies(); - const outpostEfficiencies: {[roomName: string]: number} = {}; - let avgEnergyPerCPU = 0; - - colonies.forEach(colony => { - if (colony.bunker) { - colony.outposts.forEach(outpost => { - const res = ExpansionEvaluator.computeTheoreticalMiningEfficiency(colony.bunker!.anchor, outpost.name); - if (typeof res === 'boolean') { - log.error(`Failed on outpost ${outpost.print}`); - } else { - outpostEfficiencies[outpost.name] = res; - avgEnergyPerCPU += res; - } - }); - } - }); + const outpostsPerColony = <[Colony, string[]][]>getAllColonies().filter(c => c.bunker) + .map(c => [c, c.outposts.map(r => r.name)]); - avgEnergyPerCPU = avgEnergyPerCPU/Object.keys(outpostEfficiencies).length; - let ret = `Suspect Outposts +25% below avg efficiency of ${avgEnergyPerCPU}: \n`; + return OvermindConsole.reportOutpostEfficiency(outpostsPerColony, (avg, colonyAvg) => avg < colonyAvg * 0.75); + } - for (const outpost in outpostEfficiencies) { - if (outpostEfficiencies[outpost] < avgEnergyPerCPU*0.75) { - ret += `${outpost} ${outpostEfficiencies[outpost]} \n`; + static evaluatePotentialOutpostEfficiencies(): string { + const outpostsPerColony = <[Colony, string[]][]>getAllColonies().filter(c => c.bunker) + .map(c => { + const outpostNames = c.outposts.map(room => room.name); + return [c, Cartographer.findRoomsInRange(c.name, 2).filter(r => !outpostNames.includes(r))]; } - } + ); - return ret; + return OvermindConsole.reportOutpostEfficiency(outpostsPerColony, (avg, colonyAvg) => avg > colonyAvg * 1.25 || avg > 20); } - static evaluatePotentialOutpostEfficiencies(): string { - const colonies = getAllColonies(); - const outpostEfficiencies: {[roomName: string]: number} = {}; - let avgEnergyPerCPU = 0; - - colonies.forEach(colony => { - if (colony.bunker) { - Cartographer.findRoomsInRange(colony.name, 2).forEach(outpost => { - if (!colony.outposts.map(room => room.name).includes(outpost)) { - const res = ExpansionEvaluator.computeTheoreticalMiningEfficiency(colony.bunker!.anchor, outpost); - if (typeof res === 'boolean') { - log.error(`Failed on outpost ${outpost}`); - } else { - outpostEfficiencies[outpost] = res; - avgEnergyPerCPU += res; - } - } - }); - } - }); + static reportOutpostEfficiency(outpostsPerColony: [Colony, string[]][], selectionCallback: (avg: number, colonyAvg: number) => boolean): string { + let msg = `Estimated outpost efficiency:\n`; + for (const [colony, outposts] of outpostsPerColony) { + let avgEnergyPerCPU = 0; + const outpostAvgEnergyPerCPU = []; + + msg += ` • Colony at ${colony.room.name}:\n` + for (const outpost of outposts) { + const d = ExpansionEvaluator.computeTheoreticalMiningEfficiency(colony.bunker!.anchor, outpost); - avgEnergyPerCPU = avgEnergyPerCPU/Object.keys(outpostEfficiencies).length; - let ret = `Possible new outposts above avg efficiency of ${avgEnergyPerCPU}: \n`; + msg += `\t - ${d.room} ${`(${d.type})`.padLeft(6)}: ${(d.energyPerSource * d.sources / ENERGY_REGEN_TIME).toFixed(2)} energy/source, Net income: ${d.netIncome.toFixed(2)}, Net energy/CPU: ${(d.netIncome / d.cpuCost).toFixed(2)}\n`; + msg += `\t Creep costs: ${d.creepEnergyCost.toFixed(2)} energy/tick, spawn time: ${d.spawnTimeCost.toFixed(2)}, CPU: ${d.cpuCost.toFixed(2)} cycles/tick\n`; + if (d.unreachableSources || d.unreachableController) { + const { unreachableSources: s, unreachableController: c } = d; + msg += `\t ${color("Unreachable:", "yellow")} ${s ? `sources: ${s}` : ""}${s && c ? ', ' : ''}${c ? `controller: ${c}` : ""}\n`; + } - for (const outpost in outpostEfficiencies) { - // 20E/cpu is a good guideline for an efficient room - if (outpostEfficiencies[outpost] > avgEnergyPerCPU*1.25 || outpostEfficiencies[outpost] > 20) { - ret += `${outpost} ${outpostEfficiencies[outpost]} \n`; + outpostAvgEnergyPerCPU.push(d.avgEnergyPerCPU); + avgEnergyPerCPU += d.avgEnergyPerCPU; } - } - return ret; - } + const bestOutposts = outpostAvgEnergyPerCPU.map((avg, idx) => { + // 20E/cpu is a good guideline for an efficient room + if (selectionCallback(avg, avgEnergyPerCPU)) return idx + 1; + return undefined; + }).filter(avg => avg); + msg += `\n Outposts with above average efficiency of ${avgEnergyPerCPU.toFixed(2)}: ${bestOutposts.join(", ")}\n`; + } + return msg; + } // Memory management =============================================================================================== @@ -604,13 +633,14 @@ export class OvermindConsole { for (const colName in Memory.colonies) { for (const key in Memory.colonies[colName]) { if (!protectedColonyKeys.includes(key)) { - delete (Memory.colonies[colName])[key]; + // @ts-expect-error direct property access + delete Memory.colonies[colName][key]; } } } // Suicide any creeps which have no memory for (const i in Game.creeps) { - if (Game.creeps[i].memory == {}) { + if (_.isEmpty(Game.creeps[i].memory)) { Game.creeps[i].suicide(); } } @@ -618,8 +648,8 @@ export class OvermindConsole { delete Memory.screepsProfiler; // Remove overlords memory from flags for (const i in Memory.flags) { - if ((Memory.flags[i]).overlords) { - delete (Memory.flags[i]).overlords; + if (Memory.flags[i].overlords) { + delete Memory.flags[i].overlords; } } // Clean creep memory @@ -654,7 +684,7 @@ export class OvermindConsole { return JSON.stringify(sizes, undefined, '\t'); } - static cancelMarketOrders(filter?: (order: Order) => any): string { + static cancelMarketOrders(filter?: (order: Order) => boolean): string { const ordersToCancel = !!filter ? _.filter(Game.market.orders, order => filter(order)) : Game.market.orders; _.forEach(_.values(ordersToCancel), (order: Order) => Game.market.cancelOrder(order.id)); return `Canceled ${_.values(ordersToCancel).length} orders.`; diff --git a/src/console/globals.ts b/src/console/globals.ts index 8b5c15210..83cb22f2c 100644 --- a/src/console/globals.ts +++ b/src/console/globals.ts @@ -1,14 +1,10 @@ -declare const __VERSION__: string; global.__VERSION__ = '0.5.2'; -declare function deref(ref: string): RoomObject | null; - global.deref = function(ref: string): RoomObject | null { // dereference any object from identifier - return Game.getObjectById(ref) || Game.flags[ref] || Game.creeps[ref] || Game.spawns[ref] || null; + return Game.getObjectById(ref) as any as RoomObject + || Game.flags[ref] || Game.creeps[ref] || Game.spawns[ref] || null; }; -declare function derefRoomPosition(protoPos: ProtoPos): RoomPosition; - global.derefRoomPosition = function(protoPos: ProtoPos): RoomPosition { return new RoomPosition(protoPos.x, protoPos.y, protoPos.roomName); }; diff --git a/src/console/nukeManifest.ts b/src/console/nukeManifest.ts index 4f4d8cd73..715d63a0e 100644 --- a/src/console/nukeManifest.ts +++ b/src/console/nukeManifest.ts @@ -53,11 +53,9 @@ const launchPos = [ ]; export function verifyLaunchManifest() { - for (const i in launchFrom) { - const from = launchFrom[i]; + for (const [i, from] of launchFrom.entries()) { const to = launchTo[i]; - const [x, y] = launchPos[i]; - const nuker: StructureNuker = Overmind.colonies[from].commandCenter.nuker; + const nuker = Overmind.colonies[from].commandCenter?.nuker; if (Game.map.getRoomLinearDistance(from, to) > NUKE_RANGE) { log.info(`${from} to ${to} is out of range!`); @@ -71,13 +69,12 @@ export function verifyLaunchManifest() { } export function doomsdayLaunch() { - for (const i in launchFrom) { - const from = launchFrom[i]; + for (const [i, from] of launchFrom.entries()) { const to = launchTo[i]; const [x, y] = launchPos[i]; - const nuker: StructureNuker = Overmind.colonies[from].commandCenter.nuker; + const nuker = Overmind.colonies[from].commandCenter?.nuker; - if (nuker.cooldown == 0) { + if (nuker && nuker.cooldown == 0) { const pos = new RoomPosition(x, y, to); const ret = nuker.launchNuke(pos); log.alert(`[NUCLEAR LAUNCH] Launching nuke from ${from} to ${pos.print}! Result: ${ret}`); diff --git a/src/declarations/index.d.ts b/src/declarations/index.d.ts index 51b024a8a..60ea047d3 100644 --- a/src/declarations/index.d.ts +++ b/src/declarations/index.d.ts @@ -1,41 +1,51 @@ declare const require: (module: string) => any; -declare var global: any; -declare const MARKET_FEE: 300; // missing in the typed-screeps declarations -global.MARKET_FEE = MARKET_FEE; +declare const MARKET_FEE = 300; // missing in the typed-screeps declarations -declare const NO_ACTION: 1; -declare type NO_ACTION = NO_ACTION; -global.NO_ACTION = NO_ACTION; +declare type NO_ACTION = 1; +declare var NO_ACTION: NO_ACTION = 1; type TickPhase = 'assimilating' | 'build' | 'refresh' | 'init' | 'run' | 'postRun'; declare var PHASE: TickPhase; declare var LATEST_BUILD_TICK: number; declare var LATEST_GLOBAL_RESET_TICK: number; declare var LATEST_GLOBAL_RESET_DATE: Date; +declare var GLOBAL_AGE: number; -declare namespace NodeJS { - interface Global { +declare var __VERSION__: string; - age?: number; +declare function print(...args: any[]): string; +declare function deref(ref: string): RoomObject | null; +declare function derefRoomPosition(protoPos: ProtoPos): RoomPosition; +declare function gc(quick?: boolean): void; - _cache: IGlobalCache; +declare var _cache: IGlobalCache; - __VERSION__: string; +declare var PERMACACHE: { [key: string]: any }; - Overmind: IOvermind; +declare var __DEFAULT_OVERMIND_SIGNATURE__: string; - Assimilator: IAssimilator; +declare var remoteDebugger: import('debug/remoteDebugger').RemoteDebugger; - print(...args: any[]): string; +declare var Overmind: IOvermind; - deref(ref: string): RoomObject | null; +declare var Memory: Memory; +declare var Assimilator: IAssimilator; - derefRoomPosition(protoPos: ProtoPos): RoomPosition; +declare var Cartographer: import('utilities/Cartographer').Cartographer; +declare var Pathing: import('movement/Pathing').Pathing; +declare var RoomIntel: typeof import('intel/RoomIntel').RoomIntel; +declare var CombatIntel: typeof import('intel/CombatIntel').CombatIntel; +declare var GoalFinder: import('targeting/GoalFinder').GoalFinder; +declare var Abathur: import('resources/Abathur').Abathur; - gc(quick?: boolean): void; - } -} +declare var MatrixCache: import('matrix/MatrixLib').MatrixCache; +declare var MatrixLib: import('matrix/MatrixLib').MatrixLib; + +declare var PackratTests: import('utilities/packrat').PackratTests; + +declare var CombatCreepSetup: typeof import('creepSetups/CombatCreepSetup').CombatCreepSetup; +declare var DefaultCombatCreepSetups: {[type: string]: any }; // import('creepSetups/CombatCreepSetup').CombatCreepSetup } type Full = { [P in keyof T]-?: T[P]; @@ -45,7 +55,6 @@ type Omit = Pick>; // declare module 'screeps-profiler'; // I stopped using the typings for this because it was fucking up the Game typings -declare module 'columnify'; // If TS2451 gets thrown, change "declare let Game: Game;" to "declare var Game: Game;" // in typed-screeps index.d.ts file. (See issue #61 until the package is updated) @@ -63,7 +72,7 @@ interface IGlobalCache { lists: { [key: string]: any[] }; costMatrices: { [key: string]: CostMatrix }; roomPositions: { [key: string]: RoomPosition | undefined }; - things: { [key: string]: undefined | HasID | HasID[] }; + things: { [key: string]: undefined | _HasId | _HasId[] }; // objects: { [key: string]: Object }; } @@ -114,19 +123,19 @@ interface IAssimilator { interface IOvermind { shouldBuild: boolean; expiration: number; - cache: ICache; // is actually GameCache - overseer: IOverseer; // is actually Overseer - directives: { [flagName: string]: any }; // is actually { [flagName: string]: Directive } - zerg: { [creepName: string]: any }; // is actually { [creepName: string]: Zerg } - powerZerg: { [creepName: string]: any }; // is actually { [creepName: string]: PowerZerg } - colonies: { [roomName: string]: any }; // is actually { [roomName: string]: Colony } - overlords: { [ref: string]: any }; // is actually { [ref: string]: Overlord } - spawnGroups: { [ref: string]: any }; // is actually { [ref: string]: SpawnGroup } + cache: import('caching/GameCache').GameCache; + overseer: import('Overseer').Overseer; + directives: { [flagName: string]: import('directives/Directive').Directive }; + zerg: { [creepName: string]: import('zerg/Zerg').Zerg }; + powerZerg: { [creepName: string]: import('zerg/PowerZerg').PowerZerg }; + colonies: { [roomName: string]: import('Colony').Colony }; + overlords: { [ref: string]: import('overlords/Overlord').Overlord }; + spawnGroups: { [ref: string]: import('logistics/SpawnGroup').SpawnGroup }; colonyMap: { [roomName: string]: string }; memory: IOvermindMemory; terminalNetwork: ITerminalNetwork; // is actually TerminalNetwork tradeNetwork: ITradeNetwork; // is actually TradeNetwork - expansionPlanner: IExpansionPlanner; + expansionPlanner: import('strategy/ExpansionPlanner').ExpansionPlanner; exceptions: Error[]; build(): void; @@ -243,14 +252,6 @@ interface ITradeNetwork { run(): void; } -declare var Overmind: IOvermind; - -declare var _cache: IGlobalCache; - -declare var PERMACACHE: { [key: string]: any }; - -declare function print(...args: any[]): void; - interface Coord { x: number; y: number; @@ -291,18 +292,10 @@ interface ProtoPos { roomName: string; } -interface HasPos { - pos: RoomPosition; -} - interface HasRef { ref: string; } -interface HasID { - id: Id; -} - type AnyStoreStructure = StructureContainer | StructureExtension @@ -331,7 +324,7 @@ type TransferrableStoreStructure = | StructureTerminal | StructureTower; -// interface StoreLike { -// [resourceType: string]: number -// } +type StoreContentsArray = [resourceType: ResourceConstant, amount: number][]; +type StoreContents = { [resourceType in ResourceConstant]: number }; +type DropContents = { [resourceType in ResourceConstant]: Resource[] | undefined }; diff --git a/src/declarations/memory.d.ts b/src/declarations/memory.d.ts index e695b3118..5f5d1049c 100644 --- a/src/declarations/memory.d.ts +++ b/src/declarations/memory.d.ts @@ -16,13 +16,13 @@ interface RawMemory { interface Memory { tick: number; build: number; - assimilator: any; + assimilator: { users?: any }; Overmind: {}; profiler: any; overseer: any; segmenter: any; roomIntel: any; - colonies: { [name: string]: any }; + colonies: { [name: string]: import("Colony").ColonyMemory }; creeps: { [name: string]: CreepMemory; }; powerCreeps: {[name: string]: PowerCreepMemory}; flags: { [name: string]: FlagMemory; }; @@ -30,7 +30,17 @@ interface Memory { spawns: { [name: string]: SpawnMemory; }; pathing: PathingMemory; constructionSites: { [id: string]: number }; - stats: any; + stats: { + persistent:{ + lastGlobalReset?: number; + lastMemoryReset?: number; + globalAge?: number; + avgCPU?: number; + empireAge?: number; + build?: number; + }; + "cpu.heapStatistics"?: HeapStatistics; + }; // suspend?: number; resetBucket?: boolean; @@ -177,6 +187,7 @@ interface FlagMemory { [MEM.EXPIRATION]: number; incomplete?: boolean; }; + overlords?: any; debug?: boolean; amount?: number; persistent?: boolean; diff --git a/src/declarations/prototypes.d.ts b/src/declarations/prototypes.d.ts index b9dc5da62..06dc8fd44 100644 --- a/src/declarations/prototypes.d.ts +++ b/src/declarations/prototypes.d.ts @@ -7,7 +7,13 @@ interface Creep { inRampart: boolean; approxMoveSpeed: number; bodypartCounts: { [bodypart in BodyPartConstant]: number }; - isHuman: true; + isPlayer: true; + + // private + _boosts: ResourceConstant[]; + _boostCounts: { [boostType: string]: number }; + _inRampart: boolean; + _moveSpeed: number; } interface PowerCreep { @@ -74,7 +80,7 @@ interface Room { threatLevel: number; instantaneousThreatLevel: 0 | 0.5 | 1; - fleeDefaults: HasPos[]; + fleeDefaults: _HasRoomPosition[]; structures: Structure[]; hostileStructures: Structure[]; @@ -131,6 +137,26 @@ interface Room { // _priorityMatrices: { [priority: number]: CostMatrix }; // _skMatrix: CostMatrix; _kitingMatrix: CostMatrix; + + // private caching + _creeps: Creep[]; + _hostiles: Creep[]; + _friendlies: Creep[]; + _invaders: Creep[]; + _sourceKeepers: Creep[]; + _dangerousHostiles: Creep[]; + _playerHostiles: Creep[]; + _dangerousPlayerHostiles: Creep[]; + _fleeDefaults: _HasRoomPosition[]; + _allStructures: Structure[]; + _hostileStructures: Structure[]; + _flags: Flag[]; + _constructionSites: ConstructionSite[]; + _allConstructionSites: ConstructionSite[]; + _hostileConstructionSites: ConstructionSite[]; + _tombstones: Tombstone[]; + _ruins: Ruin[]; + _drops: { [resourceType: string]: Resource[] }; } interface RoomObject { @@ -166,8 +192,6 @@ interface RoomPosition { getOffsetPos(dx: number, dy: number): RoomPosition; - lookFor(structureType: T): Array; - lookForStructure(structureType: StructureConstant): Structure | undefined; isWalkable(ignoreCreeps?: boolean): boolean; @@ -178,8 +202,9 @@ interface RoomPosition { getMultiRoomRangeTo(pos: RoomPosition): number; - findClosestByLimitedRange(objects: T[] | RoomPosition[], rangeLimit: number, - opts?: { filter: any | string; }): T | undefined; + findClosestByLimitedRange(this: RoomPosition, + objects: T[], rangeLimit: number, + opts?: { filter: any | string; }): T | undefined; findClosestByMultiRoomRange(objects: T[]): T | undefined; @@ -187,6 +212,8 @@ interface RoomPosition { } interface RoomVisual { + roads: Point[]; + box(x: number, y: number, w: number, h: number, style?: LineStyle): RoomVisual; infoBox(info: string[], x: number, y: number, opts?: { [option: string]: any }): RoomVisual; @@ -220,12 +247,21 @@ interface Structure { isWalkable: boolean; } -interface StructureContainer { +interface StoreBase { + contents: StoreContentsArray; +} + +interface _StoreLike { energy: number; isFull: boolean; isEmpty: boolean; } +interface StructureContainer extends _StoreLike {} +interface StructureExtension extends _StoreLike {} +interface StructureLink extends _StoreLike {} +interface StructureStorage extends _StoreLike {} + interface StructureController { reservedByMe: boolean; signedByMe: boolean; @@ -234,46 +270,16 @@ interface StructureController { needsReserving(reserveBuffer: number): boolean; } -interface StructureExtension { - isFull: boolean; - isEmpty: boolean; -} - -interface StructureLink { - isFull: boolean; - isEmpty: boolean; - storeCapacity: number; -} - -interface StructureStorage { - energy: number; - isFull: boolean; - isEmpty: boolean; -} - -interface StoreBase { - contents: [ResourceConstant, number][]; -} - -interface StructureSpawn { - isFull: boolean; - isEmpty: boolean; - +interface StructureSpawn extends _StoreLike { cost(bodyArray: string[]): number; } -interface StructureTerminal { - energy: any; - isFull: boolean; - isEmpty: boolean; +interface StructureTerminal extends _StoreLike { isReady: boolean; hasReceived: boolean; } -interface StructureTower { - isFull: boolean; - isEmpty: boolean; - +interface StructureTower extends _StoreLike { // run(): void; // // attackNearestEnemy(): number; diff --git a/src/declarations/typeGuards.ts b/src/declarations/typeGuards.ts index dac03e94b..1de41509e 100644 --- a/src/declarations/typeGuards.ts +++ b/src/declarations/typeGuards.ts @@ -49,8 +49,8 @@ export function isResource(obj: RoomObject): obj is Resource { return (obj).amount != undefined; } -export function hasPos(obj: HasPos | RoomPosition): obj is HasPos { - return (obj).pos != undefined; +export function hasPos(obj: _HasRoomPosition | RoomPosition): obj is _HasRoomPosition { + return (<_HasRoomPosition>obj).pos != undefined; } export function isCreep(obj: RoomObject): obj is Creep { diff --git a/src/deprecated/TerminalNetwork.ts b/src/deprecated/TerminalNetwork.ts index a60f360b1..54142a8c8 100644 --- a/src/deprecated/TerminalNetwork.ts +++ b/src/deprecated/TerminalNetwork.ts @@ -117,7 +117,7 @@ export class TerminalNetwork /*implements ITerminalNetwork*/ { this.exceptionTerminals = {}; // populated in init() this.assets = {}; // populated in init() this.notifications = []; - this.averageFullness = _.sum(this.terminals, t => _.sum(t.store) / t.storeCapacity) / this.terminals.length; + this.averageFullness = _.sum(this.terminals, t => t.store.getUsedCapacity() / t.store.getCapacity()) / this.terminals.length; } refresh(): void { @@ -131,13 +131,13 @@ export class TerminalNetwork /*implements ITerminalNetwork*/ { this.exceptionTerminals = {}; // populated in init() this.assets = {}; // populated in init() this.notifications = []; - this.averageFullness = _.sum(this.terminals, t => _.sum(t.store) / t.storeCapacity) / this.terminals.length; + this.averageFullness = _.sum(this.terminals, t => t.store.getUsedCapacity() / t.store.getCapacity()) / this.terminals.length; } /* Summarizes the total of all resources currently in a colony store structure */ private getAllAssets(): { [resourceType: string]: number } { - return mergeSum(_.map(this.terminals, terminal => - (colonyOf(terminal) != undefined ? colonyOf(terminal).assets : {}))); + const p = _.map(this.terminals, terminal => (!!colonyOf(terminal) ? colonyOf(terminal).assets : {})); + return mergeSum(...p); } private logTransfer(resourceType: ResourceConstant, amount: number, origin: string, destination: string) { @@ -174,10 +174,10 @@ export class TerminalNetwork /*implements ITerminalNetwork*/ { private remainingRoomCapacity(room: Room): number { let remainingCapacity = 0; if (room.storage) { - remainingCapacity += room.storage.storeCapacity - _.sum(room.storage.store); + remainingCapacity += room.storage.store.getFreeCapacity(); } if (room.terminal) { - remainingCapacity += room.terminal.storeCapacity - _.sum(room.terminal.store); + remainingCapacity += room.terminal.store.getFreeCapacity(); } return remainingCapacity; } @@ -362,7 +362,7 @@ export class TerminalNetwork /*implements ITerminalNetwork*/ { sendAmount = Math.floor(Math.max(sendAmount, 0)); const sendCost = Game.market.calcTransactionCost(sendAmount, sender.room.name, receiver.room.name); sendAmount = Math.min(sendAmount, (sender.store[resourceType] || 0) - sendCost - 10, - (receiver.storeCapacity - _.sum(receiver.store))); + (receiver.store.getFreeCapacity())); if (sendAmount < TERMINAL_MIN_SEND) { if (verbose) log.debug(` Size too small`); @@ -437,7 +437,7 @@ export class TerminalNetwork /*implements ITerminalNetwork*/ { // Terminal output state - push resources away from this colony if (state.type == 'out' || state.type == 'in/out') { if (terminal.cooldown == 0 && amount > targetAmount + tolerance) { - const receiver = minBy(this.terminals, t => _.sum(t.store)); + const receiver = minBy(this.terminals, t => t.store.getUsedCapacity()); if (receiver) { let sendAmount: number; if (resource == RESOURCE_ENERGY) { @@ -446,7 +446,7 @@ export class TerminalNetwork /*implements ITerminalNetwork*/ { } else { sendAmount = minMax(amount - targetAmount, TERMINAL_MIN_SEND, maxSendSize); } - if (receiver.storeCapacity - _.sum(receiver.store) > sendAmount) { + if (receiver.store.getFreeCapacity() >= sendAmount) { this.transfer(terminal, receiver, resource, sendAmount, 'exception state out'); return; } diff --git a/src/directives/Directive.ts b/src/directives/Directive.ts index e10118cbb..06bd445f9 100644 --- a/src/directives/Directive.ts +++ b/src/directives/Directive.ts @@ -102,6 +102,7 @@ export abstract class Directive { } // Register directive on Overmind + // @ts-expect-error global shenanigans global[this.name] = this; Overmind.overseer.registerDirective(this); Overmind.directives[this.name] = this; @@ -136,12 +137,10 @@ export abstract class Directive { for (const overlordName in this.overlords) { msg += tab + `${overlordName}:\n`; const olInfo: { [left: string]: string } = {}; - const overlord = this.overlords[overlordName] as any; + const overlord = this.overlords[overlordName]; olInfo[tab + tab + 'Creep usage:'] = JSON.stringify(overlord.creepUsageReport); - olInfo[tab + tab + 'Zerg:'] = _.mapValues(overlord._zerg, - zergOfRole => _.map(zergOfRole, (zerg: any) => zerg.print)); - olInfo[tab + tab + 'CombatZerg:'] = _.mapValues(overlord._combatZerg, - zergOfRole => _.map(zergOfRole, (zerg: any) => zerg.print)); + olInfo[tab + tab + 'Zerg:'] = overlord.getAllZerg().map(z => z.print).join(', '); + olInfo[tab + tab + 'CombatZerg:'] = overlord.getAllCombatZerg().map(z => z.print).join(', '); msg += toColumns(olInfo).join('\n'); } msg += 'Memory:\n' + print(this.memory); @@ -194,11 +193,10 @@ export abstract class Directive { const ret = Pathing.findPath(this.colony.pos, this.pos, {maxOps: DIRECTIVE_PATH_TIMEOUT}); const terrainCache: { [room: string]: RoomTerrain } = {}; const terrainWeighted = _.sum(ret.path, pos => { - let terrain: RoomTerrain; if (!terrainCache[pos.roomName]) { terrainCache[pos.roomName] = Game.map.getRoomTerrain(pos.roomName); } - terrain = terrainCache[pos.roomName]; + const terrain = terrainCache[pos.roomName]; return terrain.get(pos.x, pos.y) == TERRAIN_MASK_SWAMP ? 5 : 1; }); this.memory[MEM.DISTANCE] = { @@ -315,6 +313,7 @@ export abstract class Directive { remove(force = false): OK | undefined { if (!this.memory.persistent || force) { delete Overmind.directives[this.name]; + // @ts-expect-error global shenanigans delete global[this]; Overmind.overseer.removeDirective(this); if (this.colony) { @@ -370,12 +369,12 @@ export abstract class Directive { return true; // usually we want to do something if directive isn't present; so this minimizes bad results } if (typeof posOrRoomName === 'string') { - const roomName = posOrRoomName as string; - const directivesInRoom = Overmind.overseer.getDirectivesInRoom(roomName) as Directive[]; + const roomName = posOrRoomName; + const directivesInRoom = Overmind.overseer.getDirectivesInRoom(roomName); return _.filter(directivesInRoom, directive => this.filter(directive.flag)).length > 0; } else { - const pos = posOrRoomName as RoomPosition; - const directivesInRoom = Overmind.overseer.getDirectivesInRoom(pos.roomName) as Directive[]; + const pos = posOrRoomName; + const directivesInRoom = Overmind.overseer.getDirectivesInRoom(pos.roomName); return _.filter(directivesInRoom, directive => this.filter(directive.flag) && equalXYR(pos, directive.pos)).length > 0; } @@ -456,7 +455,7 @@ export abstract class Directive { * Map a list of flags to directive using the filter of the subclassed directive */ static findInRoom(roomName: string): Directive[] { - const directivesInRoom = Overmind.overseer.getDirectivesInRoom(roomName) as Directive[]; + const directivesInRoom = Overmind.overseer.getDirectivesInRoom(roomName); return _.filter(directivesInRoom, directive => this.filter(directive.flag)); } @@ -464,7 +463,7 @@ export abstract class Directive { * Map a list of flags to directive using the filter of the subclassed directive */ static findInColony(colony: Colony): Directive[] { - const directivesInColony = Overmind.overseer.getDirectivesForColony(colony) as Directive[]; + const directivesInColony = Overmind.overseer.getDirectivesForColony(colony); return _.filter(directivesInColony, directive => this.filter(directive.flag)); } diff --git a/src/directives/colony/poisonRoom.ts b/src/directives/colony/poisonRoom.ts index 316cbe4da..c7a2323d2 100644 --- a/src/directives/colony/poisonRoom.ts +++ b/src/directives/colony/poisonRoom.ts @@ -138,7 +138,7 @@ export class DirectivePoisonRoom extends Directive { } // Don't lock off the last position unless there's a creep with energy to build the site const enoughEnergyToBuildFinalWall = _.any(this.overlords.roomPoisoner.roomPoisoners, - creep => creep.carry.energy >= BUILD_POWER); + creep => creep.store.energy >= BUILD_POWER); if (this.blockPositions.length == 1 && !enoughEnergyToBuildFinalWall) { return; } diff --git a/src/directives/resource/haul.ts b/src/directives/resource/haul.ts index 250503190..9d2cbbbcf 100644 --- a/src/directives/resource/haul.ts +++ b/src/directives/resource/haul.ts @@ -25,8 +25,8 @@ export class DirectiveHaul extends Directive { static color = COLOR_YELLOW; static secondaryColor = COLOR_BLUE; - private _store: StoreDefinition; - private _drops: { [resourceType: string]: Resource[] }; + private _store: StoreContents; + private _drops: DropContents; private _finishAtTime: number; memory: DirectiveHaulMemory; @@ -43,13 +43,13 @@ export class DirectiveHaul extends Directive { return Overmind.cache.targets[this.ref]; } - get drops(): { [resourceType: string]: Resource[] } { + get drops(): DropContents { if (!this.pos.isVisible) { - return {}; + return {}; } if (!this._drops) { const drops = (this.pos.lookFor(LOOK_RESOURCES) || []) as Resource[]; - this._drops = _.groupBy(drops, drop => drop.resourceType); + this._drops = _.groupBy(drops, drop => drop.resourceType); } return this._drops; } @@ -59,37 +59,36 @@ export class DirectiveHaul extends Directive { } get storeStructure(): StructureStorage | StructureTerminal | StructureNuker | StructureContainer | Ruin | undefined { - // TODO remove me console.log(`Looking for store struct in ${this.pos.roomName} - // with ${this.pos.lookForStructure(STRUCTURE_CONTAINER)}`); if (this.pos.isVisible) { return this.pos.lookForStructure(STRUCTURE_STORAGE) || this.pos.lookForStructure(STRUCTURE_TERMINAL) || this.pos.lookForStructure(STRUCTURE_NUKER) || this.pos.lookForStructure(STRUCTURE_CONTAINER) || - this.pos.lookFor(LOOK_RUINS).filter(ruin => _.sum(ruin.store) > 0)[0]; + this.pos.lookFor(LOOK_RUINS).filter(ruin => ruin.store.getUsedCapacity() > 0)[0] || + this.pos.lookFor(LOOK_TOMBSTONES).filter(tombstone => tombstone.store.getUsedCapacity() > 0)[0]; } return undefined; } - get store(): { [resource: string]: number } { + get store(): StoreContents { if (!this._store) { // Merge the "storage" of drops with the store of structure - let store: { [resourceType: string]: number } = {}; + let store = {}; if (this.storeStructure) { store = this.storeStructure.store; } else { - store = {energy: 0}; + store = {energy: 0}; } // Merge with drops - for (const resourceType of _.keys(this.drops)) { - const totalResourceAmount = _.sum(this.drops[resourceType], drop => drop.amount); + for (const resourceType of (_.keys(this.drops) as ResourceConstant[])) { + const totalResourceAmount = _.sum(this.drops[resourceType] as Resource[], drop => drop.amount); if (store[resourceType]) { store[resourceType] += totalResourceAmount; } else { store[resourceType] = totalResourceAmount; } } - this._store = store as StoreDefinition; + this._store = store; } // log.alert(`Haul directive ${this.print} has store of ${JSON.stringify(this._store)}`); return this._store; diff --git a/src/directives/situational/bootstrap.ts b/src/directives/situational/bootstrap.ts index 4748e9dd9..18b57150c 100644 --- a/src/directives/situational/bootstrap.ts +++ b/src/directives/situational/bootstrap.ts @@ -55,10 +55,10 @@ export class DirectiveBootstrap extends Directive { return; // wait a little while at higher levels before stopping bootstrapping } log.alert(`Colony ${this.room.print} has recovered from crash; removing bootstrap directive.`); - // Suicide any fillers so they don't get in the way + // Reassign any filler to the work force const overlord = this.overlords.bootstrap as BootstrappingOverlord; for (const filler of overlord.fillers) { - filler.suicide(); + filler.reassign(this.colony.overlords.logistics, Roles.transport); } // Remove the directive this.remove(); diff --git a/src/directives/situational/stronghold.ts b/src/directives/situational/stronghold.ts index 050cdfd50..8da402cf1 100644 --- a/src/directives/situational/stronghold.ts +++ b/src/directives/situational/stronghold.ts @@ -88,10 +88,10 @@ export class DirectiveStronghold extends Directive { const ruins = this.room.ruins; if (containers) { returns = returns.concat(containers.filter(container => - container.pos.getRangeTo(this.pos) < 5 && _.sum(container.store) > 0)); + container.pos.getRangeTo(this.pos) < 5 && container.store.getUsedCapacity() > 0)); } if (ruins) { - returns = returns.concat(ruins.filter(ruin => ruin.pos.getRangeTo(this.pos) <= 3 && _.sum(ruin.store) > 0)); + returns = returns.concat(ruins.filter(ruin => ruin.pos.getRangeTo(this.pos) <= 3 && ruin.store.getUsedCapacity() > 0)); } return returns; } diff --git a/src/directives/targeting/modularDismantle.ts b/src/directives/targeting/modularDismantle.ts index 87a01f5a5..d158bf161 100644 --- a/src/directives/targeting/modularDismantle.ts +++ b/src/directives/targeting/modularDismantle.ts @@ -7,7 +7,7 @@ import {Directive} from '../Directive'; interface DirectiveModularDismantleMemory extends FlagMemory { - targetId?: string; + targetId?: Id>; numberSpots?: number; attackInsteadOfDismantle?: boolean; onlyKillRampart?: boolean; diff --git a/src/hiveClusters/commandCenter.ts b/src/hiveClusters/commandCenter.ts index 950345f78..2e5eb0c5b 100644 --- a/src/hiveClusters/commandCenter.ts +++ b/src/hiveClusters/commandCenter.ts @@ -238,13 +238,13 @@ export class CommandCenter extends HiveCluster { y = titleCoords.y + 0.25; if (this.storage) { Visualizer.text('Storage', {x: boxX, y: y, roomName: this.room.name}); - Visualizer.barGraph(_.sum(this.storage.store) / this.storage.storeCapacity, + Visualizer.barGraph(this.storage.store.getUsedCapacity() / this.storage.store.getCapacity(), {x: boxX + 4, y: y, roomName: this.room.name}, 5); y += 1; } if (this.terminal) { Visualizer.text('Terminal', {x: boxX, y: y, roomName: this.room.name}); - Visualizer.barGraph(_.sum(this.terminal.store) / this.terminal.storeCapacity, + Visualizer.barGraph(this.terminal.store.getUsedCapacity() / this.terminal.store.getCapacity(), {x: boxX + 4, y: y, roomName: this.room.name}, 5); y += 1; } diff --git a/src/hiveClusters/evolutionChamber.ts b/src/hiveClusters/evolutionChamber.ts index 92294430b..df8816eb7 100644 --- a/src/hiveClusters/evolutionChamber.ts +++ b/src/hiveClusters/evolutionChamber.ts @@ -485,7 +485,7 @@ export class EvolutionChamber extends HiveCluster { } const amountNeeded = this.neededBoosts[boost] + amountUnavailable; - if (amountNeeded > this.colony.assets[boost]) { + if (amountNeeded > this.colony.assets[boost]) { this.debug(`Requesting boost from terminal network: ${this.neededBoosts[boost]} ${boost}`); this.terminalNetwork.requestResource(this.colony, boost, amountNeeded); } else { diff --git a/src/hiveClusters/hatchery.ts b/src/hiveClusters/hatchery.ts index d7a1098d0..95c273cfe 100644 --- a/src/hiveClusters/hatchery.ts +++ b/src/hiveClusters/hatchery.ts @@ -131,8 +131,7 @@ export class Hatchery extends HiveCluster { } spawnMoarOverlords() { - if (this.colony.layout == 'bunker' && (this.colony.storage || this.colony.terminal) - && this.colony.assets[RESOURCE_ENERGY] > 10000) { + if (BunkerQueenOverlord.canFunction(this.colony)) { this.overlord = new BunkerQueenOverlord(this); // use bunker queen if has storage and enough energy } else { this.overlord = new QueenOverlord(this); @@ -221,7 +220,7 @@ export class Hatchery extends HiveCluster { } if (this.battery) { const threshold = this.colony.stage == ColonyStage.Larva ? 0.75 : 0.5; - if (this.battery.energy < threshold * this.battery.storeCapacity) { + if (this.battery.store.getUsedCapacity() < threshold * this.battery.store.getCapacity()) { this.colony.logisticsNetwork.requestInput(this.battery, {multiplier: 1.5}); } // get rid of any minerals in the container if present diff --git a/src/hiveClusters/sporeCrawler.ts b/src/hiveClusters/sporeCrawler.ts index 731abde72..f6fba05d0 100644 --- a/src/hiveClusters/sporeCrawler.ts +++ b/src/hiveClusters/sporeCrawler.ts @@ -15,6 +15,7 @@ import {HiveCluster} from './_HiveCluster'; export class SporeCrawler extends HiveCluster { towers: StructureTower[]; + memory: undefined; static settings = { requestThreshold : 500, @@ -36,15 +37,11 @@ export class SporeCrawler extends HiveCluster { } - get memory(): undefined { - return undefined; - } - private registerEnergyRequests() { // Request energy from transporters if below request threshold for (const tower of this.towers) { - if (tower.energy < SporeCrawler.settings.requestThreshold) { - const multiplier = tower.energy < SporeCrawler.settings.criticalEnergyThreshold ? 2 : 1; + if (tower.store[RESOURCE_ENERGY] < SporeCrawler.settings.requestThreshold) { + const multiplier = tower.store[RESOURCE_ENERGY] < SporeCrawler.settings.criticalEnergyThreshold ? 2 : 1; const dAmountdt = this.room.hostiles.length > 0 ? 10 : 0; this.colony.logisticsNetwork.requestInput(tower, {multiplier: multiplier, dAmountdt: dAmountdt}); } diff --git a/src/hiveClusters/upgradeSite.ts b/src/hiveClusters/upgradeSite.ts index d390c9ab8..c7e70c891 100644 --- a/src/hiveClusters/upgradeSite.ts +++ b/src/hiveClusters/upgradeSite.ts @@ -124,7 +124,7 @@ export class UpgradeSite extends HiveCluster { } const inThreshold = this.colony.stage > ColonyStage.Larva ? 0.5 : 0.75; if (this.battery) { - if (this.battery.energy < inThreshold * this.battery.storeCapacity) { + if (this.battery.energy < inThreshold * this.battery.store.getCapacity()) { const energyPerTick = UPGRADE_CONTROLLER_POWER * this.upgradePowerNeeded; this.colony.logisticsNetwork.requestInput(this.battery, {dAmountdt: energyPerTick}); } diff --git a/src/logistics/Energetics.ts b/src/logistics/Energetics.ts index fb1b46cae..1fe192f24 100644 --- a/src/logistics/Energetics.ts +++ b/src/logistics/Energetics.ts @@ -32,8 +32,8 @@ export class Energetics { static lowPowerMode(colony: Colony): boolean { if (colony.stage == ColonyStage.Adult) { - if (_.sum(colony.storage!.store) > this.settings.storage.total.cap && - colony.terminal && _.sum(colony.terminal.store) > this.settings.terminal.total.cap) { + if (colony.storage!.store.getUsedCapacity() > this.settings.storage.total.cap && + colony.terminal && colony.terminal.store.getUsedCapacity() > this.settings.terminal.total.cap) { return true; } } diff --git a/src/logistics/LogisticsNetwork.ts b/src/logistics/LogisticsNetwork.ts index 60e82677c..c3e2ec331 100644 --- a/src/logistics/LogisticsNetwork.ts +++ b/src/logistics/LogisticsNetwork.ts @@ -29,7 +29,7 @@ export type LogisticsTarget = | Resource; export const ALL_RESOURCE_TYPE_ERROR = - `Improper logistics request: 'all' can only be used for store structure or tombstone!`; + `Improper logistics request: 'all' can only be used for store structure, tombstone, or ruin!`; export type BufferTarget = StructureStorage | StructureTerminal; @@ -81,7 +81,7 @@ export class LogisticsNetwork { // private logisticPositions: { [roomName: string]: RoomPosition[] }; private cache: { nextAvailability: { [transporterName: string]: [number, RoomPosition] }, - predictedTransporterCarry: { [transporterName: string]: { [resourceType: string]: number } }, + predictedTransporterCarry: { [transporterName: string]: StoreContents }, resourceChangeRate: { [requestID: string]: { [transporterName: string]: number } }, }; static settings = { @@ -169,7 +169,7 @@ export class LogisticsNetwork { dAmountdt : 0, }); if (opts.resourceType == 'all' && !isResource(target)) { - if (_.sum(target.store) == target.store.energy) { + if (target.store.getUsedCapacity() == target.store.energy) { opts.resourceType = RESOURCE_ENERGY; // convert "all" requests to energy if that's all they have } } @@ -206,48 +206,12 @@ export class LogisticsNetwork { } private getInputAmount(target: LogisticsTarget, resourceType: ResourceConstant): number { - // if (target instanceof DirectivePickup) { - // return target.storeCapacity - _.sum(target.store); - // } else if (isResource(target) || isTombstone(target) || isRuin(target)) { log.error(`Improper logistics request: should not request input for resource or tombstone!`); return 0; } - // @ts-ignore return target.store.getFreeCapacity(resourceType) || 0; - - // else if (isStoreStructure(target)) { - // return target.storeCapacity - _.sum(target.store); - // } else if (isEnergyStructure(target) && resourceType == RESOURCE_ENERGY) { - // return target.energyCapacity - target.energy; - // } - // // else if (target instanceof Zerg) { - // // return target.carryCapacity - _.sum(target.carry); - // // } - // else { - // if (target instanceof StructureLab) { - // if (resourceType == target.mineralType) { - // return target.mineralCapacity - target.mineralAmount; - // } else if (resourceType == RESOURCE_ENERGY) { - // return target.energyCapacity - target.energy; - // } - // } else if (target instanceof StructureNuker) { - // if (resourceType == RESOURCE_GHODIUM) { - // return target.ghodiumCapacity - target.ghodium; - // } else if (resourceType == RESOURCE_ENERGY) { - // return target.energyCapacity - target.energy; - // } - // } else if (target instanceof StructurePowerSpawn) { - // if (resourceType == RESOURCE_POWER) { - // return target.powerCapacity - target.power; - // } else if (resourceType == RESOURCE_ENERGY) { - // return target.energyCapacity - target.energy; - // } - // } - // } - // log.warning('Could not determine input amount!'); - // return 0; } private getOutputAmount(target: LogisticsTarget, resourceType: ResourceConstant | 'all'): number { @@ -256,52 +220,15 @@ export class LogisticsNetwork { log.error(ALL_RESOURCE_TYPE_ERROR); return 0; } else { - // @ts-ignore - return target.store.getUsedCapacity(); + return target.store.getUsedCapacity() || 0; } } else { if (isResource(target)) { return target.amount; } else { - // @ts-ignore - return target.store.getUsedCapacity(resourceType); + return target.store.getUsedCapacity(resourceType) || 0; } - - // Legacy code - // else if (isTombstone(target)) { - // return target.store[resourceType] || 0; - // } else if (isStoreStructure(target)) { - // return target.store[resourceType] || 0; - // } else if (isEnergyStructure(target) && resourceType == RESOURCE_ENERGY) { - // return target.energy; - // } - // // else if (target instanceof Zerg) { - // // return target.carry[resourceType]!; - // // } - // else { - // if (target instanceof StructureLab) { - // if (resourceType == target.mineralType) { - // return target.mineralAmount; - // } else if (resourceType == RESOURCE_ENERGY) { - // return target.energy; - // } - // } else if (target instanceof StructureNuker) { - // if (resourceType == RESOURCE_GHODIUM) { - // return target.ghodium; - // } else if (resourceType == RESOURCE_ENERGY) { - // return target.energy; - // } - // } else if (target instanceof StructurePowerSpawn) { - // if (resourceType == RESOURCE_POWER) { - // return target.power; - // } else if (resourceType == RESOURCE_ENERGY) { - // return target.energy; - // } - // } - // } } - // log.warning('Could not determine output amount!'); - // return 0; } // Transporter availability and predictive functions =============================================================== @@ -361,25 +288,25 @@ export class LogisticsNetwork { * Returns the predicted state of the transporter's carry after completing its current task */ private computePredictedTransporterCarry(transporter: Zerg, - nextAvailability?: [number, RoomPosition]): { [resourceType: string]: number } { + nextAvailability?: [number, RoomPosition]): StoreContents { if (transporter.task && transporter.task.target) { const requestID = this.targetToRequest[transporter.task.target.ref]; if (requestID) { const request = this.requests[requestID]; if (request) { - const carry = transporter.carry as { [resourceType: string]: number }; - const remainingCapacity = transporter.carryCapacity - _.sum(carry); + const carry = transporter.store; + const remainingCapacity = carry.getFreeCapacity() const resourceAmount = -1 * this.predictedRequestAmount(transporter, request, nextAvailability); // ^ need to multiply amount by -1 since transporter is doing complement of what request needs if (request.resourceType == 'all') { if (isResource(request.target)) { log.error(ALL_RESOURCE_TYPE_ERROR); - return {energy: 0} as StoreDefinition; + return {energy: 0}; } for (const [resourceType, storeAmt] of request.target.store.contents) { - const resourceFraction = storeAmt / _.sum(request.target.store); + const resourceFraction = storeAmt / (request.target.store.getUsedCapacity(resourceType) || storeAmt); if (carry[resourceType]) { - carry[resourceType]! += resourceAmount * resourceFraction; + carry[resourceType] += resourceAmount * resourceFraction; carry[resourceType] = minMax(carry[resourceType]!, 0, remainingCapacity); } else { carry[resourceType] = minMax(resourceAmount, 0, remainingCapacity); @@ -393,17 +320,17 @@ export class LogisticsNetwork { carry[request.resourceType] = minMax(resourceAmount, 0, remainingCapacity); } } - return carry as StoreDefinition; + return carry; } } } - return transporter.carry; + return transporter.store; } /** * Returns the predicted state of the transporter's carry after completing its task */ - private predictedTransporterCarry(transporter: Zerg): { [resourceType: string]: number } { + private predictedTransporterCarry(transporter: Zerg): StoreContents { if (!this.cache.predictedTransporterCarry[transporter.name]) { this.cache.predictedTransporterCarry[transporter.name] = this.computePredictedTransporterCarry(transporter); } @@ -444,7 +371,7 @@ export class LogisticsNetwork { predictedAmount = minMax(predictedAmount, 0, request.target.store.getCapacity(request.resourceType)); } const resourceInflux = _.sum(_.map(otherTargetingTransporters, - other => (other.carry[request.resourceType] || 0))); + other => (other.store[request.resourceType] || 0))); predictedAmount = Math.max(predictedAmount - resourceInflux, 0); return predictedAmount; } else { // output state, resources withdrawn from target @@ -459,7 +386,7 @@ export class LogisticsNetwork { predictedAmount = minMax(predictedAmount, -1 * request.target.store.getCapacity(request.resourceType), 0); } const resourceOutflux = _.sum(_.map(otherTargetingTransporters, - other => other.carryCapacity - _.sum(other.carry))); + other => other.store.getCapacity() - other.store.getUsedCapacity())); predictedAmount = Math.min(predictedAmount + resourceOutflux, 0); return predictedAmount; } @@ -478,13 +405,13 @@ export class LogisticsNetwork { const [ticksUntilFree, newPos] = this.nextAvailability(transporter); const choices: { dQ: number, dt: number, targetRef: string }[] = []; const amount = this.predictedRequestAmount(transporter, request, [ticksUntilFree, newPos]); - let carry: { [resourceType: string]: number }; + let carry: StoreContents; if (!transporter.task || transporter.task.target != request.target) { // If you are not targeting the requestor, use predicted carry after completing current task carry = this.predictedTransporterCarry(transporter); } else { // If you are targeting the requestor, use current carry for computations - carry = transporter.carry; + carry = transporter.store; } if (amount > 0) { // requestInput instance, needs refilling if (request.resourceType == 'all') { @@ -501,12 +428,12 @@ export class LogisticsNetwork { dt : dt_direct, targetRef: request.target.ref }); - if ((carry[request.resourceType] || 0) > amount || _.sum(carry) == transporter.carryCapacity) { + if ((carry[request.resourceType] || 0) > amount || _.sum(carry) == transporter.store.getCapacity()) { return choices; // Return early if you already have enough resources to go direct or are already full } // Change in resources if transporter picks up resources from a buffer first for (const buffer of this.buffers) { - const dQ_buffer = Math.min(amount, transporter.carryCapacity, buffer.store[request.resourceType] || 0); + const dQ_buffer = Math.min(amount, transporter.store.getCapacity(), buffer.store[request.resourceType] || 0); const dt_buffer = newPos.getMultiRoomRangeTo(buffer.pos) * LogisticsNetwork.settings.rangeToPathHeuristic + (Pathing.distance(buffer.pos, request.target.pos) || Infinity) + ticksUntilFree; choices.push({ @@ -517,7 +444,7 @@ export class LogisticsNetwork { } } else if (amount < 0) { // requestOutput instance, needs pickup // Change in resources if transporter goes straight to the output - const remainingCarryCapacity = transporter.carryCapacity - _.sum(carry); + const remainingCarryCapacity = transporter.store.getCapacity() - _.sum(carry); const dQ_direct = Math.min(Math.abs(amount), remainingCarryCapacity); const dt_direct = newPos.getMultiRoomRangeTo(request.target.pos) * LogisticsNetwork.settings.rangeToPathHeuristic + ticksUntilFree; @@ -526,13 +453,13 @@ export class LogisticsNetwork { dt : dt_direct, targetRef: request.target.ref }); - if (remainingCarryCapacity >= Math.abs(amount) || remainingCarryCapacity == transporter.carryCapacity) { + if (remainingCarryCapacity >= Math.abs(amount) || remainingCarryCapacity == transporter.store.getCapacity()) { return choices; // Return early you have sufficient free space or are empty } // Change in resources if transporter drops off resources at a buffer first for (const buffer of this.buffers) { - const dQ_buffer = Math.min(Math.abs(amount), transporter.carryCapacity, - buffer.storeCapacity - _.sum(buffer.store)); + const dQ_buffer = Math.min(Math.abs(amount), transporter.store.getCapacity(), + buffer.store.getFreeCapacity()); const dt_buffer = newPos.getMultiRoomRangeTo(buffer.pos) * LogisticsNetwork.settings.rangeToPathHeuristic + (Pathing.distance(buffer.pos, request.target.pos) || Infinity) + ticksUntilFree; choices.push({ @@ -615,15 +542,15 @@ export class LogisticsNetwork { const transporterStr = transporter.name + ' ' + transporter.pos; const request = this._matching![transporter.name]!; const requestStr = request.target.ref + ' ' + request.target.pos.print; - console.log(`${transporterStr.padRight(30)} : ${requestStr}`); + console.log(`${transporterStr.padRight(35)} : ${requestStr}`); } for (const transporter of unmatchedTransporters) { const transporterStr = transporter.name + ' ' + transporter.pos; - console.log(`${transporterStr.padRight(30)} : ${''}`); + console.log(`${transporterStr.padRight(35)} : ${''}`); } for (const request of unmatchedRequests) { const requestStr = request.target.ref + ' ' + request.target.pos; - console.log(`${''.padRight(30)} : ${requestStr}`); + console.log(`${''.padRight(35)} : ${requestStr}`); } console.log(); } @@ -651,7 +578,7 @@ export class LogisticsNetwork { } else { if (request.resourceType == 'all') { if (!isResource(request.target)) { - amount = _.sum(request.target.store); + amount = request.target.store.getUsedCapacity() || 0; } else { amount = -0.001; } diff --git a/src/logistics/RoadLogistics.ts b/src/logistics/RoadLogistics.ts index aca4bc1fb..af9980f03 100644 --- a/src/logistics/RoadLogistics.ts +++ b/src/logistics/RoadLogistics.ts @@ -47,7 +47,7 @@ export class RoadLogistics { return this.repairableRoads(room).length > 0; } else { // If worker is not already assigned, repair if critical roads or repaving energy >= carry capacity - return this.criticalRoads(room).length > 0 || this.energyToRepave(room.name) >= worker.carryCapacity; + return this.criticalRoads(room).length > 0 || this.energyToRepave(room.name) >= worker.store.getCapacity(); } } else { return false; @@ -163,10 +163,10 @@ export class RoadLogistics { } } - buildPavingManifest(worker: Zerg, room: Room): Task | null { - let energy = worker.carry.energy; + buildPavingManifest(worker: Zerg, room: Room): Task | null { + let energy = worker.store.energy; const targetRefs: { [ref: string]: boolean } = {}; - const tasks: Task[] = []; + const tasks: Task[] = []; let target: StructureRoad | undefined; let previousPos: RoomPosition | undefined; while (true) { diff --git a/src/logistics/TerminalNetwork_v2.ts b/src/logistics/TerminalNetwork_v2.ts index f04e92eed..fe6fff76f 100644 --- a/src/logistics/TerminalNetwork_v2.ts +++ b/src/logistics/TerminalNetwork_v2.ts @@ -302,7 +302,7 @@ export class TerminalNetworkV2 implements ITerminalNetwork { private passiveRequestors: { [resourceType: string]: Colony[] }; private activeRequestors: { [resourceType: string]: Colony[] }; - private assets: { [resourceType: string]: number }; + private assets: StoreContents; private notifications: string[]; private memory: TerminalNetworkMemory; @@ -342,7 +342,7 @@ export class TerminalNetworkV2 implements ITerminalNetwork { this.passiveRequestors = {}; // _.clone(EMPTY_COLONY_TIER); this.activeRequestors = {}; // _.clone(EMPTY_COLONY_TIER); - this.assets = {}; // populated when getAssets() is called in init() + this.assets = {}; // populated when getAssets() is called in init() this.terminalOverload = {}; this.notifications = []; @@ -367,9 +367,9 @@ export class TerminalNetworkV2 implements ITerminalNetwork { } } - getAssets(): { [resourceType: string]: number } { + getAssets(): StoreContents { if (_.isEmpty(this.assets)) { - this.assets = mergeSum(_.map(this.colonies, colony => colony.assets)); + this.assets = mergeSum(..._.map(this.colonies, colony => colony.assets)); } return this.assets; } @@ -422,8 +422,8 @@ export class TerminalNetworkV2 implements ITerminalNetwork { private getRemainingSpace(colony: Colony, includeFactoryCapacity = false): number { let totalAssets = _.sum(colony.assets); // Overfilled storage gets counted as just 100% full - if (colony.storage && _.sum(colony.storage.store) > STORAGE_CAPACITY) { - totalAssets -= (_.sum(colony.storage.store) - STORAGE_CAPACITY); + if (colony.storage && colony.storage.store.getUsedCapacity() > STORAGE_CAPACITY) { + totalAssets -= (colony.storage.store.getUsedCapacity() - STORAGE_CAPACITY); } const roomCapacity = (colony.terminal ? TERMINAL_CAPACITY : 0) + @@ -1205,7 +1205,7 @@ export class TerminalNetworkV2 implements ITerminalNetwork { `${colony.print} actively requesting ----------------------------------------------------\n` + `${bullet}${activeRequestors[colony.name] || '(None)'}\n`; } else { - const resource = resourceOrColony || undefined; + const resource = resourceOrColony || undefined; if (resource) { info += `Active providers for ${resource} -----------------------------------------------------\n` + `${bullet}${_.map(this.activeProviders[resource], col => diff --git a/src/logistics/TransportRequestGroup.ts b/src/logistics/TransportRequestGroup.ts index e246a552a..59889bd25 100644 --- a/src/logistics/TransportRequestGroup.ts +++ b/src/logistics/TransportRequestGroup.ts @@ -155,71 +155,11 @@ export class TransportRequestGroup { // } private getInputAmount(target: TransportRequestTarget, resourceType: ResourceConstant): number { - // @ts-ignore return target.store.getFreeCapacity(resourceType) || 0; - - // Legacy code from before the structure.store refactor - // if (isStoreStructure(target)) { - // return target.storeCapacity - _.sum(target.store); - // } else if (isEnergyStructure(target) && resourceType == RESOURCE_ENERGY) { - // return target.energyCapacity - target.energy; - // } else { - // if (target instanceof StructureLab) { - // if (resourceType == target.mineralType) { - // return target.mineralCapacity - target.mineralAmount; - // } else if (resourceType == RESOURCE_ENERGY) { - // return target.energyCapacity - target.energy; - // } - // } else if (target instanceof StructureNuker) { - // if (resourceType == RESOURCE_GHODIUM) { - // return target.ghodiumCapacity - target.ghodium; - // } else if (resourceType == RESOURCE_ENERGY) { - // return target.energyCapacity - target.energy; - // } - // } else if (target instanceof StructurePowerSpawn) { - // if (resourceType == RESOURCE_POWER) { - // return target.powerCapacity - target.power; - // } else if (resourceType == RESOURCE_ENERGY) { - // return target.energyCapacity - target.energy; - // } - // } - // } - // log.warning('Could not determine requestor amount!'); - // return 0; } private getOutputAmount(target: TransportRequestTarget, resourceType: ResourceConstant): number { - // @ts-ignore return target.store.getUsedCapacity(resourceType) || 0; - - // Legacy code from before the structure.store refactor - // if (isStoreStructure(target)) { - // return target.store[resourceType]!; - // } else if (isEnergyStructure(target) && resourceType == RESOURCE_ENERGY) { - // return target.energy; - // } else { - // if (target instanceof StructureLab) { - // if (resourceType == target.mineralType) { - // return target.mineralAmount; - // } else if (resourceType == RESOURCE_ENERGY) { - // return target.energy; - // } - // } else if (target instanceof StructureNuker) { - // if (resourceType == RESOURCE_GHODIUM) { - // return target.ghodium; - // } else if (resourceType == RESOURCE_ENERGY) { - // return target.energy; - // } - // } else if (target instanceof StructurePowerSpawn) { - // if (resourceType == RESOURCE_POWER) { - // return target.power; - // } else if (resourceType == RESOURCE_ENERGY) { - // return target.energy; - // } - // } - // } - // log.warning('Could not determine provider amount!'); - // return 0; } /** @@ -233,7 +173,9 @@ export class TransportRequestGroup { } for (const request of this.supply[priority]) { if (ignoreEnergy && request.resourceType == RESOURCE_ENERGY) continue; - console.log(` targetID: ${request.target.ref} amount: ${request.amount} ` + + console.log(` target: ${request.target.structureType}@${request.target.pos.print} ` + + `(${request.target.ref}) `+ + `amount: ${request.amount} ` + `resourceType: ${request.resourceType}`); } } @@ -244,7 +186,9 @@ export class TransportRequestGroup { } for (const request of this.withdraw[priority]) { if (ignoreEnergy && request.resourceType == RESOURCE_ENERGY) continue; - console.log(` targetID: ${request.target.ref} amount: ${request.amount} ` + + console.log(` target: ${request.target.structureType}@${request.target.pos.print} ` + + `(${request.target.ref}) `+ + `amount: ${request.amount} ` + `resourceType: ${request.resourceType}`); } } diff --git a/src/main.ts b/src/main.ts index 158135107..6a9280d78 100644 --- a/src/main.ts +++ b/src/main.ts @@ -56,8 +56,10 @@ function main(): void { // Instantiation operations: build or refresh the game state ------------------------------------------------------- if (!Overmind || Overmind.shouldBuild || Game.time >= Overmind.expiration) { PHASE = 'build'; + // @ts-expect-error global shenanigans delete global.Overmind; // Explicitly delete the old Overmind object - Mem.garbageCollect(true); // Run quick garbage collection + Mem.garbageCollect(true); // Run quick garbage collection + // @ts-expect-error obfuscated global.Overmind = new _Overmind(); // Instantiate the Overmind object Overmind.build(); // Build phase: instantiate all game components LATEST_BUILD_TICK = Game.time; // Record this tick as having a build reset @@ -86,7 +88,9 @@ function main(): void { function main_RL(): void { Mem.clean(); + // @ts-expect-error global shenanigans delete global.Overmind; + // @ts-expect-error obfuscated global.Overmind = new _Overmind(); ActionParser.run(); @@ -107,6 +111,7 @@ function onGlobalReset(): void { Assimilator.updateValidChecksumLedger(); } // Make a new Overmind object + // @ts-expect-error obfuscated global.Overmind = new _Overmind(); // Make a remote debugger global.remoteDebugger = new RemoteDebugger(); diff --git a/src/matrix/MatrixLib.ts b/src/matrix/MatrixLib.ts index c6445a36e..c12124d39 100644 --- a/src/matrix/MatrixLib.ts +++ b/src/matrix/MatrixLib.ts @@ -52,7 +52,7 @@ export interface VolatileMatrixOptions { PERMACACHE.terrainMatrices = PERMACACHE.terrainMatrices || {}; -const MatrixCache: { +export const MatrixCache: { [hash: string]: { matrix: CostMatrix; generated: number; @@ -401,7 +401,7 @@ export class MatrixLib { /** * Blocks all specified positions, setting their cost to 0xff */ - static block(matrix: CostMatrix, positions: (RoomPosition | HasPos)[]): CostMatrix { + static block(matrix: CostMatrix, positions: (RoomPosition | _HasRoomPosition)[]): CostMatrix { let pos: RoomPosition; for (let i = 0; i < positions.length; i++) { pos = normalizePos(positions[i]); @@ -413,7 +413,7 @@ export class MatrixLib { /** * Sets the cost of all positions to a value if walls are not present and if the value is above the current value */ - static softBlock(matrix: CostMatrix, positions: (RoomPosition | HasPos)[], + static softBlock(matrix: CostMatrix, positions: (RoomPosition | _HasRoomPosition)[], roomName: string, cost: number): CostMatrix { let pos: RoomPosition; const terrain = Game.map.getRoomTerrain(roomName); @@ -473,7 +473,7 @@ export class MatrixLib { * 0 2 2 2 2 2 0 * 0 0 0 0 0 0 0 */ - static addPyramidPotential(matrix: CostMatrix, pos: RoomPosition | HasPos, range: number, maxCost: number, + static addPyramidPotential(matrix: CostMatrix, pos: RoomPosition | _HasRoomPosition, range: number, maxCost: number, includeTerrain = true, // don't use includeTerrain with explicitTerrainCosts! terrainCosts: TerrainCosts = {plainCost: 1, swampCost: 5}): CostMatrix { @@ -520,7 +520,7 @@ export class MatrixLib { * Adds a square potential with a specified center and range. If includeTerrainCosts=true (by default) then if the * cost for a square is zero, the terrain cost of the tile is added using default costs of {plain: 1, swamp: 5}. */ - static addSquarePotential(matrix: CostMatrix, pos: RoomPosition | HasPos, range: number, addCost: number, + static addSquarePotential(matrix: CostMatrix, pos: RoomPosition | _HasRoomPosition, range: number, addCost: number, includeTerrain = true, // don't use includeTerrain with explicitTerrainCosts! terrainCosts: TerrainCosts = {plainCost: 1, swampCost: 5}): CostMatrix { @@ -588,7 +588,7 @@ export class MatrixLib { * -> Do not run additional passes of applyMovingMaxPool after doing this! * -> This method assumes that you have already added explicit terrian costs. */ - static blockAfterMaxPooling(matrix: CostMatrix, positions: (RoomPosition | HasPos)[], + static blockAfterMaxPooling(matrix: CostMatrix, positions: (RoomPosition | _HasRoomPosition)[], width: number, height: number): CostMatrix { let pos: RoomPosition; let x, y, dx, dy: number; @@ -619,7 +619,7 @@ export class MatrixLib { * 0 9 0 0 0 9 5 0 9 9 5 1 | 0 9 0 0 9 9 1 1 9 9 5 1 * 0 0 0 1 0 0 0 1 0 0 1 1 | 0 0 0 1 0 0 1 1 0 0 1 1 */ - static setToMaxCostAfterMaxPooling(matrix: CostMatrix, positions: (RoomPosition | HasPos)[], + static setToMaxCostAfterMaxPooling(matrix: CostMatrix, positions: (RoomPosition | _HasRoomPosition)[], width: number, height: number, cost: number): CostMatrix { let pos: RoomPosition; let x, y, dx, dy: number; @@ -655,7 +655,7 @@ export class MatrixLib { * 0 9 0 0 0 9 5 0 9 9 5 1 | 0 9 0 0 9 9 1 1 9 9 6 1 * 0 0 0 1 0 0 0 1 0 0 1 1 | 0 0 0 1 0 0 1 1 0 0 1 1 */ - static addCostAfterMaxPooling(matrix: CostMatrix, positions: (RoomPosition | HasPos)[], + static addCostAfterMaxPooling(matrix: CostMatrix, positions: (RoomPosition | _HasRoomPosition)[], width: number, height: number, cost: number): CostMatrix { const addMatrix = new PathFinder.CostMatrix(); MatrixLib.setToMaxCostAfterMaxPooling(addMatrix, positions, width, height, cost); @@ -668,7 +668,7 @@ export class MatrixLib { * to the existing cost of the tile. If the cost for a square is zero, the terrain cost of the tile is added * using implicit costs of {plain: 1, swamp: 5} */ - static setInRange(matrix: CostMatrix, pos: RoomPosition | HasPos, range: number, cost: number, + static setInRange(matrix: CostMatrix, pos: RoomPosition | _HasRoomPosition, range: number, cost: number, addDefaultTerrainCosts = false): CostMatrix { pos = normalizePos(pos); diff --git a/src/memory/Memory.ts b/src/memory/Memory.ts index 240070227..fd7cb5c5c 100644 --- a/src/memory/Memory.ts +++ b/src/memory/Memory.ts @@ -32,7 +32,7 @@ export function getAutonomyLevel(): number { } } -let lastMemory: any; +let lastMemory: Memory; let lastTime: number = 0; const MAX_BUCKET = 10000; @@ -93,14 +93,14 @@ export class Mem { */ static load() { if (lastTime && lastMemory && Game.time == lastTime + 1) { + // @ts-expect-error global shenanigans delete global.Memory; global.Memory = lastMemory; RawMemory._parsed = lastMemory; } else { - // noinspection BadExpressionStatementJS - /* tslint:disable:no-unused-expression */ + // eslint-disable-next-line Memory.rooms; // forces parsing - /* tslint:enable:no-unused-expression */ + // eslint-disable-next-line lastMemory = RawMemory._parsed; Memory.stats.persistent.lastMemoryReset = Game.time; } @@ -183,7 +183,7 @@ export class Mem { spawns : {}, pathing : {distances: {}}, constructionSites : {}, - stats : {}, + stats : {persistent:{}}, playerCreepTracker: {}, settings : { signature : DEFAULT_OVERMIND_SIGNATURE, @@ -249,6 +249,7 @@ export class Mem { Memory.build--; // don't count this reset as a build Game.cpu.halt(); } else if (Game.cpu.bucket < BUCKET_CLEAR_CACHE) { + // @ts-expect-error global shenanigans delete global._cache; this.initGlobalMemory(); } @@ -260,6 +261,7 @@ export class Mem { for (const name in Memory.creeps) { if (!Game.creeps[name]) { delete Memory.creeps[name]; + // @ts-expect-error global shenanigans delete global[name]; } } @@ -270,6 +272,7 @@ export class Mem { for (const name in Memory.flags) { if (!Game.flags[name]) { delete Memory.flags[name]; + // @ts-expect-error global shenanigans delete global[name]; } } @@ -283,6 +286,7 @@ export class Mem { // Delete only if "persistent" is not set - use case: praise rooms if (!Memory.colonies[name].persistent) { delete Memory.colonies[name]; + // @ts-expect-error global shenanigans delete global[name]; } } diff --git a/src/movement/Movement.ts b/src/movement/Movement.ts index 1bd36306e..c1c72a614 100644 --- a/src/movement/Movement.ts +++ b/src/movement/Movement.ts @@ -127,7 +127,7 @@ export class Movement { /** * Move a creep to a destination */ - static goTo(creep: AnyZerg, destination: HasPos | RoomPosition, opts: MoveOptions = {}): number { + static goTo(creep: AnyZerg, destination: _HasRoomPosition | RoomPosition, opts: MoveOptions = {}): number { if (creep.blockMovement && !opts.force) { return ERR_BUSY; @@ -238,6 +238,7 @@ export class Movement { } } + let shouldRepath = false; const state = this.deserializeState(moveData, destination); // // verify creep is in the location it thinks it should be in @@ -267,7 +268,7 @@ export class Movement { } if (state.stuckCount >= opts.stuckValue && Math.random() > .5) { pathOpts.blockCreeps = true; - delete moveData.path; + shouldRepath = true; } // delete path cache if destination is different @@ -276,14 +277,14 @@ export class Movement { moveData.path += state.destination.getDirectionTo(destination); state.destination = destination; } else { - delete moveData.path; + shouldRepath = true; } } // randomly repath with specified probability if (opts.repathChance && Math.random() < opts.repathChance) { - delete moveData.path; + shouldRepath = true; } // TODO: repath if you are not on expected next position @@ -291,7 +292,7 @@ export class Movement { // pathfinding let newPath = false; - if (!moveData.path || moveData.path.length == 0) { + if (shouldRepath || !moveData.path || moveData.path.length == 0) { newPath = true; if (isStandardZerg(creep) && creep.spawning) { return ERR_BUSY; @@ -462,8 +463,10 @@ export class Movement { } else { if (isPowerZerg(creep)) { return MovePriorities.powerCreep; + } else if (creep.memory.role in MovePriorities) { + return MovePriorities[creep.memory.role]; } else { - return MovePriorities[creep.memory.role] || MovePriorities.default; + return MovePriorities.default; } } } @@ -568,7 +571,6 @@ export class Movement { } - // TODO: this is bugged somewhere /** * Recursively moves creeps out of the way of a position to make room for something, such as a spawning creep. * If suicide is specified and there is no series of move commands that can move a block of creeps out of the way, @@ -752,7 +754,7 @@ export class Movement { /** * Moves a pair of creeps; the follower will always attempt to be in the last position of the leader */ - static pairwiseMove(leader: AnyZerg, follower: AnyZerg, target: HasPos | RoomPosition, + static pairwiseMove(leader: AnyZerg, follower: AnyZerg, target: _HasRoomPosition | RoomPosition, opts = {} as MoveOptions, allowedRange = 1): number | undefined { let outcome; if (leader.room != follower.room) { @@ -792,7 +794,7 @@ export class Movement { /** * Moves a swarm to a destination, accounting for group pathfinding */ - static swarmMove(swarm: Swarm, destination: HasPos | RoomPosition, opts: SwarmMoveOptions = {}): number { + static swarmMove(swarm: Swarm, destination: _HasRoomPosition | RoomPosition, opts: SwarmMoveOptions = {}): number { if (swarm.fatigue > 0) { Movement.circle(swarm.anchor, 'aqua', .3); @@ -827,6 +829,7 @@ export class Movement { return NO_ACTION; } + let shouldRepath = false; const state = this.deserializeState(moveData, destination); // check if swarm is stuck @@ -851,21 +854,21 @@ export class Movement { } if (state.stuckCount >= opts.stuckValue && Math.random() > .5) { opts.blockCreeps = true; - delete moveData.path; + shouldRepath = true; } // delete path cache if destination is different if (!destination.isEqualTo(state.destination)) { - delete moveData.path; + shouldRepath = true; } if (opts.repathChance && Math.random() < opts.repathChance) { // randomly repath with some probability - delete moveData.path; + shouldRepath = true; } // pathfinding let newPath = false; - if (!moveData.path) { + if (shouldRepath || !moveData.path) { newPath = true; state.destination = destination; const cpu = Game.cpu.getUsed(); @@ -1194,7 +1197,7 @@ export class Movement { /** * Moving routine for guards or sourceReapers in a room with NPC invaders */ - static invasionMove(creep: Zerg, destination: RoomPosition | HasPos, opts: MoveOptions = {}): number { + static invasionMove(creep: Zerg, destination: RoomPosition | _HasRoomPosition, opts: MoveOptions = {}): number { _.defaults(opts, getDefaultMoveOptions()); const dest = normalizePos(destination); if (creep.pos.getRangeTo(dest) > 8) { @@ -1213,7 +1216,7 @@ export class Movement { /** * Kite around enemies in a single room, repathing every tick. More expensive than flee(). */ - static kite(creep: AnyZerg, avoidGoals: (RoomPosition | HasPos)[], options: MoveOptions = {}): number | undefined { + static kite(creep: AnyZerg, avoidGoals: (RoomPosition | _HasRoomPosition)[], options: MoveOptions = {}): number | undefined { _.defaults(options, { fleeRange : 5, terrainCosts: isPowerZerg(creep) ? {plainCost: 1, swampCost: 1} : getTerrainCosts((creep.creep)), @@ -1227,7 +1230,7 @@ export class Movement { /** * Flee from avoid goals in the room while not re-pathing every tick like kite() does. */ - static flee(creep: AnyZerg, avoidGoals: (RoomPosition | HasPos)[], + static flee(creep: AnyZerg, avoidGoals: (RoomPosition | _HasRoomPosition)[], dropEnergy = false, opts: MoveOptions = {}): number | undefined { if (avoidGoals.length == 0) { @@ -1282,6 +1285,7 @@ export class Movement { moveData.fleeWait = 2; + let shouldRepath = false; // Invalidate path if needed if (moveData.path) { if (moveData.path.length > 0) { @@ -1290,16 +1294,16 @@ export class Movement { if (!pos.isEdge) { const newClosest = pos.findClosestByRange(avoidGoals); if (newClosest && normalizePos(newClosest).getRangeTo(pos) < rangeToClosest) { - delete moveData.path; + shouldRepath = true; } } } else { - delete moveData.path; + shouldRepath = true; } } // Re-calculate path if needed - if (!moveData.path || !moveData.destination) { + if (shouldRepath || !moveData.path || !moveData.destination) { const ret = Pathing.findFleePath(creep.pos, avoidGoals, opts.pathOpts || {}); if (ret.path.length == 0) { return NO_ACTION; diff --git a/src/movement/Pathing.ts b/src/movement/Pathing.ts index 38dc67ec6..af0cad98d 100644 --- a/src/movement/Pathing.ts +++ b/src/movement/Pathing.ts @@ -488,7 +488,7 @@ export class Pathing { /** * Get a kiting path within a room */ - static findKitingPath(creepPos: RoomPosition, fleeFrom: (RoomPosition | HasPos)[], + static findKitingPath(creepPos: RoomPosition, fleeFrom: (RoomPosition | _HasRoomPosition)[], opts: PathOptions = {}): PathFinderPath { _.defaults(opts, { fleeRange : 5, @@ -511,7 +511,7 @@ export class Pathing { /** * Get a flee path possibly leaving the room; generally called further in advance of kitingPath */ - static findFleePath(creepPos: RoomPosition, fleeFrom: (RoomPosition | HasPos)[], + static findFleePath(creepPos: RoomPosition, fleeFrom: (RoomPosition | _HasRoomPosition)[], opts: PathOptions = {}): PathFinderPath { _.defaults(opts, { terrainCosts: {plainCost: 1, swampCost: 5}, @@ -1139,7 +1139,7 @@ export class Pathing { * Whether another object in the same room can be reached from the current position. * This method is very expensive and kind of stupid, so use it sparingly! */ - static isReachable(startPos: RoomPosition, endPos: RoomPosition, obstacles: (RoomPosition | HasPos)[], + static isReachable(startPos: RoomPosition, endPos: RoomPosition, obstacles: (RoomPosition | _HasRoomPosition)[], options: PathOptions = {}): boolean { _.defaults(options, { blockCreeps: false, @@ -1182,7 +1182,7 @@ export class Pathing { /** * Like isReachable(), but returns the first position which should be cleared to find a path to destination */ - static findBlockingPos(startPos: RoomPosition, endPos: RoomPosition, obstacles: (RoomPosition | HasPos)[], + static findBlockingPos(startPos: RoomPosition, endPos: RoomPosition, obstacles: (RoomPosition | _HasRoomPosition)[], options: PathOptions = {}): RoomPosition | undefined { _.defaults(options, { blockCreeps: false, @@ -1190,10 +1190,21 @@ export class Pathing { maxOps : 2000, ensurePath : false, }); - if (startPos.roomName != endPos.roomName) { - log.error(`findBlockingPos() should only be used within a single room!`); - return undefined; + if (startPos.roomName !== endPos.roomName) { + // Start and end aren't in the same room. + const pathToEnd = this.findPath(startPos, endPos, { + range: options.range!, + roadCost: "auto", + terrainCosts: { plainCost: 1, swampCost: 5 }, + }); + + let newStartPos = pathToEnd.path.find(step => step.roomName === endPos.roomName); + if (!newStartPos) + return undefined; + + startPos = newStartPos; } + const matrix = new PathFinder.CostMatrix(); _.forEach(obstacles, obstacle => { if (hasPos(obstacle)) { @@ -1210,11 +1221,14 @@ export class Pathing { maxRooms : 1, roomCallback: callback, }); + for (const pos of ret.path) { if (matrix.get(pos.x, pos.y) > 100) { return pos; } } + + return undefined; } /** diff --git a/src/movement/helpers.ts b/src/movement/helpers.ts index 85fa6b36d..b5a184baf 100644 --- a/src/movement/helpers.ts +++ b/src/movement/helpers.ts @@ -1,7 +1,7 @@ /** * Returns destination.pos if destination has a position, or destination if destination is a RoomPosition */ -export function normalizePos(destination: HasPos | RoomPosition): RoomPosition { +export function normalizePos(destination: _HasRoomPosition | RoomPosition): RoomPosition { return (destination).pos || destination; } diff --git a/src/overlords/CombatOverlord.ts b/src/overlords/CombatOverlord.ts index 6d839982c..eaa31ce25 100644 --- a/src/overlords/CombatOverlord.ts +++ b/src/overlords/CombatOverlord.ts @@ -43,7 +43,7 @@ export abstract class CombatOverlord extends Overlord { autoRun(roleCreeps: CombatZerg[], creepHandler: (creep: CombatZerg) => void) { for (const creep of roleCreeps) { if (creep.spawning) { - return; + continue; } if (creep.hasValidTask) { creep.run(); diff --git a/src/overlords/Overlord.ts b/src/overlords/Overlord.ts index ff8a7f3a4..e41eee587 100644 --- a/src/overlords/Overlord.ts +++ b/src/overlords/Overlord.ts @@ -315,7 +315,7 @@ export abstract class Overlord { for (const creep of this._creeps[role] || []) { if (!zergNames[creep.name]) { if (Overmind.zerg[creep.name] && (Overmind.zerg[creep.name]).isCombatZerg) { - this._combatZerg[role].push(Overmind.zerg[creep.name]); + this._combatZerg[role].push(Overmind.zerg[creep.name]); } else { this._combatZerg[role].push(new CombatZerg(creep, notifyWhenAttacked)); } @@ -543,7 +543,7 @@ export abstract class Overlord { const [moveBoosts, nonMoveBoosts] = _.partition(neededBoostResources, resource => Abathur.isMoveBoost(resource)); - for (const boost of [...moveBoosts, nonMoveBoosts]) { // try to get move boosts first if they're available + for (const boost of [...moveBoosts, ...nonMoveBoosts]) { // try to get move boosts first if they're available const boostLab = _.find(evolutionChamber.boostingLabs, lab => lab.mineralType == boost); if (boostLab) { zerg.task = Tasks.getBoosted(boostLab, boost); diff --git a/src/overlords/colonization/pioneer.ts b/src/overlords/colonization/pioneer.ts index d08bd3641..3b77378c5 100644 --- a/src/overlords/colonization/pioneer.ts +++ b/src/overlords/colonization/pioneer.ts @@ -57,7 +57,7 @@ export class PioneerOverlord extends Overlord { } } // Build and recharge - if (pioneer.carry.energy == 0) { + if (pioneer.store.energy == 0) { pioneer.task = Tasks.recharge(); } else if (this.room && this.room.controller && (this.room.controller.ticksToDowngrade < (0.1 * CONTROLLER_DOWNGRADE[this.room.controller.level]) diff --git a/src/overlords/colonization/roomPoisoner.ts b/src/overlords/colonization/roomPoisoner.ts index ddee7c6a0..69fb24e22 100644 --- a/src/overlords/colonization/roomPoisoner.ts +++ b/src/overlords/colonization/roomPoisoner.ts @@ -45,7 +45,7 @@ export class RoomPoisonerOverlord extends Overlord { private handleRoomPoisoner(posioner: Zerg): void { // Recharge if needed - if (posioner.carry.energy < BUILD_POWER) { + if (posioner.store.energy < BUILD_POWER) { posioner.task = Tasks.recharge(); return; } diff --git a/src/overlords/core/manager.ts b/src/overlords/core/manager.ts index d38345a96..69e7804ea 100644 --- a/src/overlords/core/manager.ts +++ b/src/overlords/core/manager.ts @@ -136,7 +136,7 @@ export class CommandCenterOverlord extends Overlord { if (manager.store.getUsedCapacity() == 0) { return false; } else { - manager.debug(`Unloading carry: ${JSON.stringify(manager.carry)}`); + manager.debug(`Unloading carry: ${JSON.stringify(manager.store)}`); manager.task = Tasks.transferAll(this.commandCenter.storage); // placeholder solution return true; } @@ -150,7 +150,7 @@ export class CommandCenterOverlord extends Overlord { manager.debug('supplyActions'); const request = this.commandCenter.transportRequests.getPrioritizedClosestRequest(manager.pos, 'supply'); if (request) { - const amount = Math.min(request.amount, manager.carryCapacity); + const amount = Math.min(request.amount, manager.store.getCapacity()); const resource = request.resourceType; // If we have enough to fulfill the request, we're done if (manager.store[request.resourceType] >= amount) { @@ -254,7 +254,7 @@ export class CommandCenterOverlord extends Overlord { } const transferAmount = Math.min(terminal.store[resource] - target, storage.store.getFreeCapacity(resource), - manager.carryCapacity); + manager.store.getCapacity()); manager.task = Tasks.chain([Tasks.withdraw(terminal, resource, transferAmount), Tasks.transfer(storage, resource, transferAmount)]); // manager.debug(`Assigned task ${print(manager.task)}`) @@ -269,7 +269,7 @@ export class CommandCenterOverlord extends Overlord { } const transferAmount = Math.min(target - terminal.store[resource], storage.store[resource], - manager.carryCapacity); + manager.store.getCapacity()); manager.task = Tasks.chain([Tasks.withdraw(storage, resource, transferAmount), Tasks.transfer(terminal, resource, transferAmount)]); // manager.debug(`Assigned task ${print(manager.task)}`) @@ -543,7 +543,7 @@ export class CommandCenterOverlord extends Overlord { manager.debug('idleActions'); if (this.mode == 'bunker' && this.managerRepairTarget && manager.getActiveBodyparts(WORK) > 0) { // Repair ramparts when idle - if (manager.carry.energy > 0) { + if (manager.store.energy > 0) { manager.repair(this.managerRepairTarget); } else { const storage = this.commandCenter.storage; diff --git a/src/overlords/core/queen.ts b/src/overlords/core/queen.ts index b5147ebcf..dae1265de 100644 --- a/src/overlords/core/queen.ts +++ b/src/overlords/core/queen.ts @@ -67,13 +67,13 @@ export class QueenOverlord extends Overlord { // Can energy be moved from the link to the battery? if (this.hatchery.battery && !this.hatchery.battery.isFull && !this.hatchery.link.isEmpty) { // Move energy to battery as needed - if (queen.carry.energy < queen.carryCapacity) { + if (queen.store.energy < queen.store.getCapacity()) { queen.task = Tasks.withdraw(this.hatchery.link); } else { queen.task = Tasks.transfer(this.hatchery.battery); } } else { - if (queen.carry.energy < queen.carryCapacity) { // make sure you're recharged + if (queen.store.energy < queen.store.getCapacity()) { // make sure you're recharged if (!this.hatchery.link.isEmpty) { queen.task = Tasks.withdraw(this.hatchery.link); } else if (this.hatchery.battery && !this.hatchery.battery.isEmpty) { @@ -82,14 +82,14 @@ export class QueenOverlord extends Overlord { } } } else { - if (this.hatchery.battery && queen.carry.energy < queen.carryCapacity) { + if (this.hatchery.battery && queen.store.energy < queen.store.getCapacity()) { queen.task = Tasks.withdraw(this.hatchery.battery); } } } private handleQueen(queen: Zerg): void { - if (queen.carry.energy > 0) { + if (queen.store.energy > 0) { this.supplyActions(queen); } else { this.rechargeActions(queen); diff --git a/src/overlords/core/queen_bunker.ts b/src/overlords/core/queen_bunker.ts index f08c695db..ea4098047 100644 --- a/src/overlords/core/queen_bunker.ts +++ b/src/overlords/core/queen_bunker.ts @@ -53,6 +53,13 @@ export class BunkerQueenOverlord extends Overlord { private numActiveQueens: number; assignments: { [queenName: string]: { [id: string]: boolean } }; + static canFunction(colony: Colony): boolean { + return (colony.layout === "bunker" + && insideBunkerBounds(colony.spawns[0].pos, colony) + && (!!colony.storage || !!colony.terminal) + && colony.assets[RESOURCE_ENERGY] > 10000); + } + constructor(hatchery: Hatchery, priority = OverlordPriority.core.queen) { super(hatchery, 'supply', priority); this.queenSetup = Setups.queens.default; @@ -117,11 +124,11 @@ export class BunkerQueenOverlord extends Overlord { } // Builds a series of tasks to empty unnecessary carry contents, withdraw required resources, and supply structures - private buildSupplyTaskManifest(queen: Zerg): Task | null { - let tasks: Task[] = []; + private buildSupplyTaskManifest(queen: Zerg): Task | null { + let tasks: Task[] = []; // Step 1: empty all contents (this shouldn't be necessary since queen is normally empty at this point) let queenPos = queen.pos; - if (_.sum(queen.carry) > 0) { + if (queen.store.getUsedCapacity() > 0) { const transferTarget = this.colony.terminal || this.colony.storage || this.batteries[0]; if (transferTarget) { tasks.push(Tasks.transferAll(transferTarget)); @@ -132,8 +139,8 @@ export class BunkerQueenOverlord extends Overlord { } } // Step 2: figure out what you need to supply for and calculate the needed resources - const queenCarry = {} as { [resourceType: string]: number }; - const allStore = mergeSum(_.map(this.storeStructures, s => s.store)); + const queenCarry = {}; + const allStore = mergeSum(..._.map(this.storeStructures, s => s.store)); const supplyRequests: TransportRequest[] = []; for (const priority in this.colony.transportRequests.supply) { @@ -143,10 +150,10 @@ export class BunkerQueenOverlord extends Overlord { } } } - const supplyTasks: Task[] = []; + const supplyTasks: Task[] = []; for (const request of supplyRequests) { // stop when carry will be full - const remainingAmount = queen.carryCapacity - _.sum(queenCarry); + const remainingAmount = queen.store.getCapacity() - _.sum(queenCarry); if (remainingAmount == 0) break; // figure out how much you can withdraw let amount = Math.min(request.amount, remainingAmount); @@ -161,7 +168,7 @@ export class BunkerQueenOverlord extends Overlord { supplyTasks.push(Tasks.transfer(request.target, request.resourceType, amount)); } // Step 3: make withdraw tasks to get the needed resources - const withdrawTasks: Task[] = []; + const withdrawTasks: Task[] = []; const neededResources = _.keys(queenCarry) as ResourceConstant[]; const targets: AnyStoreStructure[] = _.filter(this.storeStructures, s => _.all(neededResources, resource => (s.store[resource] || 0) >= (queenCarry[resource] || 0))); @@ -201,8 +208,8 @@ export class BunkerQueenOverlord extends Overlord { } if (!withdrawTarget && withdrawTasks.length == 0) { - log.warning(`Could not find adequate withdraw structure for ${queen.print}! (neededResources: - ${neededResources}, queenCarry: ${JSON.stringify(queenCarry)})`); + log.warning(`Could not find adequate withdraw structure for ${queen.print}! ` + + `(neededResources: ${neededResources}, queenCarry: ${JSON.stringify(queenCarry)})`); return null; } // Step 4: put all the tasks in the correct order, set nextPos for each, and chain them together @@ -211,11 +218,11 @@ export class BunkerQueenOverlord extends Overlord { } // Builds a series of tasks to withdraw required resources from targets - private buildWithdrawTaskManifest(queen: Zerg): Task | null { - const tasks: Task[] = []; + private buildWithdrawTaskManifest(queen: Zerg): Task | null { + const tasks: Task[] = []; const transferTarget = this.colony.terminal || this.colony.storage || this.batteries[0]; // Step 1: empty all contents (this shouldn't be necessary since queen is normally empty at this point) - if (_.sum(queen.carry) > 0) { + if (queen.store.getUsedCapacity() > 0) { if (transferTarget) { tasks.push(Tasks.transferAll(transferTarget)); } else { @@ -237,7 +244,7 @@ export class BunkerQueenOverlord extends Overlord { } for (const request of withdrawRequests) { // stop when carry will be full - const remainingAmount = queen.carryCapacity - _.sum(queenCarry); + const remainingAmount = queen.store.getCapacity() - _.sum(queenCarry); if (remainingAmount == 0) break; // figure out how much you can withdraw const amount = Math.min(request.amount, remainingAmount); @@ -313,7 +320,7 @@ export class BunkerQueenOverlord extends Overlord { // Do we need safemodes? else if (this.colony.level > 5 && this.colony.controller.safeModeAvailable < 3 && this.colony.terminal && this.colony.terminal.store[RESOURCE_GHODIUM] >= 1000 && - queen.carryCapacity >= 1000) { + queen.store.getCapacity() >= 1000) { // Only use 1 queen to avoid adding 2 safemodes if (queen.name == _.first(_.sortBy(this.queens, q => q.name)).name) { queen.task = Tasks.chain([ @@ -321,7 +328,7 @@ export class BunkerQueenOverlord extends Overlord { Tasks.withdraw(this.colony.terminal, RESOURCE_GHODIUM, 1000), Tasks.generateSafeMode(this.colony.controller) ]); - log.alert(`${this.colony.print} has ${this.colony.controller.safeModeAvailable} safemodes avaliable, ` + + log.alert(`${this.colony.print} has ${this.colony.controller.safeModeAvailable} safemodes available, ` + `generating a new one`); } } diff --git a/src/overlords/core/transporter.ts b/src/overlords/core/transporter.ts index 059aada78..f87c4df29 100644 --- a/src/overlords/core/transporter.ts +++ b/src/overlords/core/transporter.ts @@ -1,7 +1,7 @@ import {Colony} from '../../Colony'; import {log} from '../../console/log'; import {Roles, Setups} from '../../creepSetups/setups'; -import {isResource, isTombstone} from '../../declarations/typeGuards'; +import {isResource, isRuin, isTombstone} from '../../declarations/typeGuards'; import {ALL_RESOURCE_TYPE_ERROR, BufferTarget, LogisticsRequest} from '../../logistics/LogisticsNetwork'; import {Pathing} from '../../movement/Pathing'; import {OverlordPriority} from '../../priorities/priorities_overlords'; @@ -86,7 +86,7 @@ export class TransportOverlord extends Overlord { const amount = this.colony.logisticsNetwork.predictedRequestAmount(transporter, request); // Target is requesting input if (amount > 0) { - if (isResource(request.target) || isTombstone(request.target)) { + if (isResource(request.target) || isTombstone(request.target) || isRuin(request.target)) { log.warning(`Improper logistics request: should not request input for resource or tombstone!`); return; } else if (request.resourceType == 'all') { @@ -99,7 +99,7 @@ export class TransportOverlord extends Overlord { // If we need to go to a buffer first to get more stuff const buffer = deref(bestChoice.targetRef) as BufferTarget; const withdrawAmount = Math.min(buffer.store[request.resourceType] || 0, - transporter.carryCapacity - _.sum(transporter.carry), amount); + transporter.store.getFreeCapacity(request.resourceType), amount); task = task.fork(Tasks.withdraw(buffer, request.resourceType, withdrawAmount)); if (transporter.hasMineralsInCarry && request.resourceType == RESOURCE_ENERGY) { task = task.fork(Tasks.transferAll(buffer)); @@ -135,7 +135,7 @@ export class TransportOverlord extends Overlord { this.colony.logisticsNetwork.invalidateCache(transporter, request); } else { // If nothing to do, put everything in a store structure - if (_.sum(transporter.carry) > 0) { + if (transporter.store.getUsedCapacity() > 0) { if (transporter.hasMineralsInCarry) { const target = this.colony.terminal || this.colony.storage; if (target) { diff --git a/src/overlords/core/upgrader.ts b/src/overlords/core/upgrader.ts index 5891c3d0c..6bc052fe8 100644 --- a/src/overlords/core/upgrader.ts +++ b/src/overlords/core/upgrader.ts @@ -51,7 +51,7 @@ export class UpgradingOverlord extends Overlord { } private handleUpgrader(upgrader: Zerg): void { - if (upgrader.carry.energy > 0) { + if (upgrader.store.energy > 0) { // Repair link if (this.upgradeSite.link && this.upgradeSite.link.hits < this.upgradeSite.link.hitsMax) { upgrader.task = Tasks.repair(this.upgradeSite.link); diff --git a/src/overlords/core/worker.ts b/src/overlords/core/worker.ts index d900eaa16..e6d60f477 100644 --- a/src/overlords/core/worker.ts +++ b/src/overlords/core/worker.ts @@ -13,6 +13,8 @@ import {Visualizer} from '../../visuals/Visualizer'; import {Zerg} from '../../zerg/Zerg'; import {Overlord} from '../Overlord'; +type hitsCallbackType = (structure: StructureWall | StructureRampart) => number; + /** * Spawns general-purpose workers, which maintain a colony, performing actions such as building, repairing, fortifying, * paving, and upgrading, when needed @@ -29,6 +31,7 @@ export class WorkerOverlord extends Overlord { constructionSites: ConstructionSite[]; nukeDefenseRamparts: StructureRampart[]; nukeDefenseHitsRemaining: { [id: string]: number }; + nukeDefenseHitsNeeded: { [id: string]: number }; useBoostedRepair?: boolean; static settings = { @@ -106,6 +109,7 @@ export class WorkerOverlord extends Overlord { // Nuke defense ramparts needing fortification this.nukeDefenseRamparts = []; this.nukeDefenseHitsRemaining = {}; + this.nukeDefenseHitsNeeded = {}; if (this.room.find(FIND_NUKES).length > 0) { for (const rampart of this.colony.room.ramparts) { const neededHits = this.neededRampartHits(rampart); @@ -133,8 +137,11 @@ export class WorkerOverlord extends Overlord { this.workers = this.zerg(Roles.worker); } - private neededRampartHits(rampart: StructureRampart): number { - let neededHits = WorkerOverlord.settings.barrierHits[this.colony.level]; + private neededNukeHits(rampart: StructureWall|StructureRampart): number { + if (this.nukeDefenseHitsNeeded[rampart.id] !== undefined) { + return this.nukeDefenseHitsNeeded[rampart.id] + } + let neededHits = 0; for (const nuke of rampart.pos.lookFor(LOOK_NUKES)) { neededHits += 10e6; } @@ -143,6 +150,13 @@ export class WorkerOverlord extends Overlord { neededHits += 5e6; } } + this.nukeDefenseHitsNeeded[rampart.id] = neededHits; + return neededHits; + } + + private neededRampartHits(rampart: StructureRampart): number { + let neededHits = WorkerOverlord.settings.barrierHits[this.colony.level]; + neededHits =+ this.neededNukeHits(rampart); return neededHits; } @@ -264,19 +278,26 @@ export class WorkerOverlord extends Overlord { } } - private fortifyActions(worker: Zerg, fortifyStructures = this.fortifyBarriers): boolean { + private findLowBarriers(fortifyStructures = this.fortifyBarriers, + hitsCallback: hitsCallbackType = (structure) => structure.hits, + numBarriersToConsider = 5 + ): (StructureWall | StructureRampart)[] { let lowBarriers: (StructureWall | StructureRampart)[]; - const highestBarrierHits = _.max(_.map(fortifyStructures, structure => structure.hits)); + const highestBarrierHits = _.max(_.map(fortifyStructures, structure => hitsCallback(structure))); if (highestBarrierHits > WorkerOverlord.settings.hitTolerance) { // At high barrier HP, fortify only structures that are within a threshold of the lowest - const lowestBarrierHits = _.min(_.map(fortifyStructures, structure => structure.hits)); - lowBarriers = _.filter(fortifyStructures, structure => structure.hits <= lowestBarrierHits + + const lowestBarrierHits = _.min(_.map(fortifyStructures, structure => hitsCallback(structure))); + lowBarriers = _.filter(fortifyStructures, structure => hitsCallback(structure) <= lowestBarrierHits + WorkerOverlord.settings.hitTolerance); } else { // Otherwise fortify the lowest N structures - const numBarriersToConsider = 5; // Choose the closest barrier of the N barriers with lowest hits lowBarriers = _.take(fortifyStructures, numBarriersToConsider); } + return lowBarriers + } + + private fortifyActions(worker: Zerg, fortifyStructures = this.fortifyBarriers): boolean { + const lowBarriers = this.findLowBarriers(fortifyStructures); const target = worker.pos.findClosestByMultiRoomRange(lowBarriers); if (target) { worker.task = Tasks.fortify(target); @@ -287,7 +308,17 @@ export class WorkerOverlord extends Overlord { } private nukeFortifyActions(worker: Zerg, fortifyStructures = this.nukeDefenseRamparts): boolean { - const target = minBy(fortifyStructures, rampart => { + var self = this; + const adaptedHits = _.reduce(fortifyStructures, function(obj,structure: StructureWall|StructureRampart) { + obj[structure.id] = structure.hits - self.neededNukeHits(structure); + return obj; + }, {} as {[key: string]: number}); + + const lowBarriers = this.findLowBarriers() + const minBarrier = lowBarriers[lowBarriers.length - 1].hits + const urgent = _.filter(fortifyStructures, structure => adaptedHits[structure.id] < minBarrier) + + const target = minBy(urgent, rampart => { const structuresUnderRampart = rampart.pos.lookFor(LOOK_STRUCTURES); return _.min(_.map(structuresUnderRampart, structure => { const priority = _.findIndex(FortifyPriorities, sType => sType == structure.structureType); @@ -298,11 +329,12 @@ export class WorkerOverlord extends Overlord { } })); }); + if (target) { worker.task = Tasks.fortify(target); return true; } else { - return false; + return this.fortifyActions(worker, fortifyStructures); } } @@ -317,7 +349,7 @@ export class WorkerOverlord extends Overlord { } private handleWorker(worker: Zerg) { - if (worker.carry.energy > 0) { + if (worker.store.energy > 0) { // TODO Add high priority to block controller with ramparts/walls in case of downgrade attack // FIXME workers get stalled at controller in case of downgrade attack // Upgrade controller if close to downgrade or if getting controller attacked/was downgraded diff --git a/src/overlords/mining/extractor.ts b/src/overlords/mining/extractor.ts index 9f756c203..35acc5f1f 100644 --- a/src/overlords/mining/extractor.ts +++ b/src/overlords/mining/extractor.ts @@ -38,7 +38,7 @@ export class ExtractorOverlord extends Overlord { // If mineral is ready to be mined, make a container private shouldHaveContainer() { - return this.mineral && (this.mineral.mineralAmount > 0 || this.mineral.ticksToRegeneration < 2000); + return this.mineral && (this.mineral.mineralAmount > 0 || (this.mineral.ticksToRegeneration || 0) < 2000); } private populateStructures() { diff --git a/src/overlords/mining/miner.ts b/src/overlords/mining/miner.ts index 12605db98..c2831fd09 100644 --- a/src/overlords/mining/miner.ts +++ b/src/overlords/mining/miner.ts @@ -1,5 +1,5 @@ import {$} from '../../caching/GlobalCache'; -import {ColonyStage} from '../../Colony'; +import {Colony, ColonyStage} from '../../Colony'; import {log} from '../../console/log'; import {bodyCost, CreepSetup} from '../../creepSetups/CreepSetup'; import {Roles, Setups} from '../../creepSetups/setups'; @@ -77,10 +77,10 @@ export class MiningOverlord extends Overlord { // Check if dismantling is needed if (this.memory.dismantleNeeded || Game.time > (this.memory[DISMANTLE_CHECK] || 0)) { if (this.room) { - this.dismantlePositions = this.getDismantlePositions(); - if (this.dismantlePositions.length > 0) { + const positions = this.getDismantlePositions(); + if (positions.length > 0) { this.memory.dismantleNeeded = true; - this.dismantlePositions = this.getDismantlePositions(); + this.dismantlePositions = positions; } else { this.memory[DISMANTLE_CHECK] = getCacheExpiration(DISMANTLE_CHECK_FREQUENCY, DISMANTLE_CHECK_FREQUENCY / 5); @@ -187,7 +187,7 @@ export class MiningOverlord extends Overlord { private getDismantlePositions(): RoomPosition[] { const dismantleStructures: Structure[] = []; if (this.room) { - const targets = _.compact([...this.room.sources, this.room.controller]) as RoomObject[]; + const targets = _.compact([this.source, this.secondSource, this.container, this.link]) as RoomObject[]; for (const target of targets) { // Add blocking structures const blockingStructure = this.findBlockingStructure(target); @@ -241,26 +241,28 @@ export class MiningOverlord extends Overlord { $.refresh(this, 'source', 'container', 'link', 'constructionSite'); } + static calculateContainerPos(source: RoomPosition, dropoffLocation?: RoomPosition): RoomPosition { + // log.debug(`Computing container position for mining overlord at ${this.pos.print}...`); + if (dropoffLocation) { + const path = Pathing.findShortestPath(source, dropoffLocation).path; + const pos = _.find(path, pos => pos.getRangeTo(source) == 1); + if (pos) return pos; + } + log.warning(`Last resort container position calculation for ${source.print}!`); + return _.first(source.availableNeighbors(true)); + } /** * Calculate where the container output will be built for this site */ private calculateContainerPos(): RoomPosition { - // log.debug(`Computing container position for mining overlord at ${this.pos.print}...`); - let originPos: RoomPosition | undefined; + let dropoff: RoomPosition | undefined; if (this.colony.storage) { - originPos = this.colony.storage.pos; + dropoff = this.colony.storage.pos; } else if (this.colony.roomPlanner.storagePos) { - originPos = this.colony.roomPlanner.storagePos; - } - if (originPos) { - const path = Pathing.findShortestPath(this.pos, originPos).path; - const pos = _.find(path, pos => pos.getRangeTo(this) == 1); - if (pos) return pos; + dropoff = this.colony.roomPlanner.storagePos; } - // Shouldn't ever get here - log.warning(`Last resort container position calculation for ${this.print}!`); - return _.first(this.pos.availableNeighbors(true)); + return MiningOverlord.calculateContainerPos(this.pos, dropoff); } /** @@ -306,7 +308,7 @@ export class MiningOverlord extends Overlord { if (this.container) { const transportCapacity = 200 * this.colony.level; const threshold = this.colony.stage > ColonyStage.Larva ? 0.8 : 0.5; - if (_.sum(this.container.store) > threshold * transportCapacity) { + if (this.container.store.getUsedCapacity() > threshold * transportCapacity) { this.colony.logisticsNetwork.requestOutput(this.container, { resourceType: 'all', dAmountdt : this.energyPerTick @@ -316,7 +318,7 @@ export class MiningOverlord extends Overlord { if (this.link) { // If the link will be full with next deposit from the miner const minerCapacity = 150; - if (this.link.energy + minerCapacity > this.link.energyCapacity) { + if (this.link.store.getUsedCapacity(RESOURCE_ENERGY) + minerCapacity > this.link.store.getCapacity(RESOURCE_ENERGY)) { this.colony.linkNetwork.requestTransmit(this.link); } } @@ -340,10 +342,10 @@ export class MiningOverlord extends Overlord { // Container mining if (this.container) { if (this.container.hits < this.container.hitsMax - && miner.carry.energy >= Math.min(miner.carryCapacity, REPAIR_POWER * miner.getActiveBodyparts(WORK))) { + && miner.store.energy >= Math.min(miner.store.getCapacity(), REPAIR_POWER * miner.getActiveBodyparts(WORK))) { return miner.goRepair(this.container); } else { - if (_.sum(miner.carry) < miner.carryCapacity) { + if (miner.store.getUsedCapacity(RESOURCE_ENERGY) < miner.store.getCapacity()) { return miner.goHarvest(this.source!); } else { return miner.goTransfer(this.container); @@ -353,7 +355,7 @@ export class MiningOverlord extends Overlord { // Build output site if (this.constructionSite) { - if (miner.carry.energy >= Math.min(miner.carryCapacity, BUILD_POWER * miner.getActiveBodyparts(WORK))) { + if (miner.store.energy >= Math.min(miner.store.getCapacity(), BUILD_POWER * miner.getActiveBodyparts(WORK))) { return miner.goBuild(this.constructionSite); } else { return miner.goHarvest(this.source!); @@ -363,7 +365,7 @@ export class MiningOverlord extends Overlord { // Drop mining if (this.allowDropMining) { miner.goHarvest(this.source!); - if (miner.carry.energy > 0.8 * miner.carryCapacity) { // try to drop on top of largest drop if full + if (miner.store.energy > 0.8 * miner.store.getCapacity()) { // try to drop on top of largest drop if full const biggestDrop = maxBy(miner.pos.findInRange(miner.room.droppedEnergy, 1), drop => drop.amount); if (biggestDrop) { miner.goDrop(biggestDrop.pos, RESOURCE_ENERGY); @@ -408,7 +410,7 @@ export class MiningOverlord extends Overlord { if (res == ERR_NOT_IN_RANGE) { // approach mining site if (this.goToMiningSite(miner)) return; } - if (miner.carry.energy > 0.9 * miner.carryCapacity) { + if (miner.store.energy > 0.9 * miner.store.getCapacity()) { miner.transfer(this.link, RESOURCE_ENERGY); } // If for whatever reason there's no reciever link, you can get stuck in a bootstrapping loop, so @@ -442,7 +444,7 @@ export class MiningOverlord extends Overlord { // Container mining if (this.container) { if (this.container.hits < this.container.hitsMax - && miner.carry.energy >= Math.min(miner.carryCapacity, REPAIR_POWER * miner.getActiveBodyparts(WORK))) { + && miner.store.energy >= Math.min(miner.store.getCapacity(), REPAIR_POWER * miner.getActiveBodyparts(WORK))) { return miner.repair(this.container); } else { return this.harvestOrSleep(miner, source); @@ -451,7 +453,7 @@ export class MiningOverlord extends Overlord { // Build output site if (this.constructionSite) { // standard miners won't have both a container and a construction site - if (miner.carry.energy >= Math.min(miner.carryCapacity, BUILD_POWER * miner.getActiveBodyparts(WORK))) { + if (miner.store.energy >= Math.min(miner.store.getCapacity(), BUILD_POWER * miner.getActiveBodyparts(WORK))) { return miner.build(this.constructionSite); } else { return this.harvestOrSleep(miner, source); @@ -461,13 +463,13 @@ export class MiningOverlord extends Overlord { // Drop mining if (this.allowDropMining) { this.harvestOrSleep(miner, source); - if (miner.carry.energy > 0.8 * miner.carryCapacity) { // move over the drop when you're close to full + if (miner.store.energy > 0.8 * miner.store.getCapacity()) { // move over the drop when you're close to full const biggestDrop = maxBy(miner.pos.findInRange(miner.room.droppedEnergy, 1), drop => drop.amount); if (biggestDrop) { miner.goTo(biggestDrop); } } - if (miner.carry.energy == miner.carryCapacity) { // drop when you are full + if (miner.store.energy == miner.store.getCapacity()) { // drop when you are full miner.drop(RESOURCE_ENERGY); } return; @@ -490,7 +492,7 @@ export class MiningOverlord extends Overlord { // Container mining if (this.container) { if (this.container.hits < this.container.hitsMax - && miner.carry.energy >= Math.min(miner.carryCapacity, REPAIR_POWER * miner.getActiveBodyparts(WORK))) { + && miner.store.energy >= Math.min(miner.store.getCapacity(), REPAIR_POWER * miner.getActiveBodyparts(WORK))) { return miner.repair(this.container); } else { return this.harvestOrSleep(miner, source); @@ -499,7 +501,7 @@ export class MiningOverlord extends Overlord { // Build output site if (this.constructionSite) { // standard miners won't have both a container and a construction site - if (miner.carry.energy >= Math.min(miner.carryCapacity, BUILD_POWER * miner.getActiveBodyparts(WORK))) { + if (miner.store.energy >= Math.min(miner.store.getCapacity(), BUILD_POWER * miner.getActiveBodyparts(WORK))) { return miner.build(this.constructionSite); } else { return this.harvestOrSleep(miner, source); @@ -509,13 +511,13 @@ export class MiningOverlord extends Overlord { // Drop mining if (this.allowDropMining) { this.harvestOrSleep(miner, source); - if (miner.carry.energy > 0.8 * miner.carryCapacity) { // move over the drop when you're close to full + if (miner.store.energy > 0.8 * miner.store.getCapacity()) { // move over the drop when you're close to full const biggestDrop = maxBy(miner.pos.findInRange(miner.room.droppedEnergy, 1), drop => drop.amount); if (biggestDrop) { miner.goTo(biggestDrop); } } - if (miner.carry.energy == miner.carryCapacity) { // drop when you are full + if (miner.store.energy == miner.store.getCapacity()) { // drop when you are full miner.drop(RESOURCE_ENERGY); } return; @@ -537,7 +539,7 @@ export class MiningOverlord extends Overlord { } else { miner.harvest(this.secondSource!); } - if (miner.carry.energy > 0.9 * miner.carryCapacity) { + if (miner.store.energy > 0.9 * miner.store.getCapacity()) { miner.transfer(this.link, RESOURCE_ENERGY); } return; @@ -548,7 +550,7 @@ export class MiningOverlord extends Overlord { // Container mining if (this.container) { if (this.container.hits < this.container.hitsMax - && miner.carry.energy >= Math.min(miner.carryCapacity, REPAIR_POWER * miner.getActiveBodyparts(WORK))) { + && miner.store.energy >= Math.min(miner.store.getCapacity(), REPAIR_POWER * miner.getActiveBodyparts(WORK))) { return miner.repair(this.container); } else if (this.source && this.source.energy > 0) { return miner.harvest(this.source!); @@ -559,7 +561,7 @@ export class MiningOverlord extends Overlord { // Build output site if (this.constructionSite) { - if (miner.carry.energy >= Math.min(miner.carryCapacity, BUILD_POWER * miner.getActiveBodyparts(WORK))) { + if (miner.store.energy >= Math.min(miner.store.getCapacity(), BUILD_POWER * miner.getActiveBodyparts(WORK))) { return miner.build(this.constructionSite); } else { return miner.harvest(this.source!); @@ -569,13 +571,13 @@ export class MiningOverlord extends Overlord { // Drop mining if (this.allowDropMining) { miner.harvest(this.source!); - if (miner.carry.energy > 0.8 * miner.carryCapacity) { // move over the drop when you're close to full + if (miner.store.energy > 0.8 * miner.store.getCapacity()) { // move over the drop when you're close to full const biggestDrop = maxBy(miner.pos.findInRange(miner.room.droppedEnergy, 1), drop => drop.amount); if (biggestDrop) { miner.goTo(biggestDrop); } } - if (miner.carry.energy == miner.carryCapacity) { // drop when you are full + if (miner.store.energy == miner.store.getCapacity()) { // drop when you are full miner.drop(RESOURCE_ENERGY); } return; diff --git a/src/overlords/powerMining/PowerHauler.ts b/src/overlords/powerMining/PowerHauler.ts index f9800e5c4..bea5921f6 100644 --- a/src/overlords/powerMining/PowerHauler.ts +++ b/src/overlords/powerMining/PowerHauler.ts @@ -45,7 +45,7 @@ export class PowerHaulingOverlord extends Overlord { } private handleHauler(hauler: Zerg) { - if (_.sum(hauler.carry) == 0) { + if (hauler.store.getUsedCapacity() === 0) { if (this.directive.memory.state >= 4) { // FIXME: Maybe ditch this and put it as a separate on-finishing method to reassign hauler.say('💀 RIP 💀', true); @@ -96,22 +96,22 @@ export class PowerHaulingOverlord extends Overlord { } else { // Travel to colony room and deposit resources if (hauler.inSameRoomAs(this.colony)) { - for (const [resourceType, amount] of hauler.carry.contents) { + for (const [resourceType, amount] of hauler.store.contents) { if (amount == 0) continue; if (resourceType == RESOURCE_ENERGY) { // prefer to put energy in storage - if (this.colony.storage && _.sum(this.colony.storage.store) < STORAGE_CAPACITY) { + if (this.colony.storage && this.colony.storage.store.getUsedCapacity() < STORAGE_CAPACITY) { hauler.task = Tasks.transfer(this.colony.storage, resourceType); return; - } else if (this.colony.terminal && _.sum(this.colony.terminal.store) < TERMINAL_CAPACITY) { + } else if (this.colony.terminal && this.colony.terminal.store.getUsedCapacity() < TERMINAL_CAPACITY) { hauler.task = Tasks.transfer(this.colony.terminal, resourceType); return; } } else { // prefer to put minerals in terminal - this.directive.memory.totalCollected += hauler.carry.power || 0; - if (this.colony.terminal && _.sum(this.colony.terminal.store) < TERMINAL_CAPACITY) { + this.directive.memory.totalCollected += hauler.store.power || 0; + if (this.colony.terminal && this.colony.terminal.store.getUsedCapacity() < TERMINAL_CAPACITY) { hauler.task = Tasks.transfer(this.colony.terminal, resourceType); return; - } else if (this.colony.storage && _.sum(this.colony.storage.store) < STORAGE_CAPACITY) { + } else if (this.colony.storage && this.colony.storage.store.getUsedCapacity() < STORAGE_CAPACITY) { hauler.task = Tasks.transfer(this.colony.storage, resourceType); return; } @@ -126,7 +126,7 @@ export class PowerHaulingOverlord extends Overlord { } checkIfStillCarryingPower() { - return _.find(this.haulers, hauler => hauler.carry.power != undefined && hauler.carry.power > 0); + return _.find(this.haulers, hauler => hauler.store.power != undefined && hauler.store.power > 0); } run() { diff --git a/src/overlords/scouting/randomWalker.ts b/src/overlords/scouting/randomWalker.ts index 367255489..609fa1cee 100644 --- a/src/overlords/scouting/randomWalker.ts +++ b/src/overlords/scouting/randomWalker.ts @@ -32,17 +32,35 @@ export class RandomWalkerScoutOverlord extends Overlord { // scout.goTo(enemyConstructionSites[0].pos); // return; // } + // Check if room might be connected to newbie/respawn zone const indestructibleWalls = _.filter(scout.room.walls, wall => wall.hits == undefined); if (indestructibleWalls.length > 0) { // go back to origin colony if you find a room near newbie zone scout.task = Tasks.goToRoom(this.colony.room.name); // todo: make this more precise - } else { - // Pick a new room - const neighboringRooms = _.values(Game.map.describeExits(scout.pos.roomName)) as string[]; - const roomName = _.sample(neighboringRooms); - if (Game.map.isRoomAvailable(roomName)) { - scout.task = Tasks.goToRoom(roomName); + return; + } + + const roomStatus = Game.map.getRoomStatus(scout.room.name); + + let neighboringRooms = _.values(Game.map.describeExits(scout.pos.roomName)); + neighboringRooms = _.shuffle(neighboringRooms); + + // Pick a new random room from the neighboring rooms, making sure they have compatible room status + let neighboringRoom; + while ((neighboringRoom = neighboringRooms.shift())) { + + const neighborStatus = Game.map.getRoomStatus(scout.room.name); + if (roomStatus.status !== neighborStatus.status) { + continue; } + + scout.task = Tasks.goToRoom(neighboringRoom); + break; + } + + // Just move back to the colony and start over + if (!scout.task) { + scout.task = Tasks.goToRoom(this.colony.room.name); } } diff --git a/src/overlords/situational/bootstrap.ts b/src/overlords/situational/bootstrap.ts index d51d759e0..72f2d56fa 100644 --- a/src/overlords/situational/bootstrap.ts +++ b/src/overlords/situational/bootstrap.ts @@ -134,7 +134,7 @@ export class BootstrappingOverlord extends Overlord { } private handleFiller(filler: Zerg) { - if (filler.carry.energy > 0) { + if (filler.store.energy > 0) { this.supplyActions(filler); } else { this.rechargeActions(filler); diff --git a/src/overlords/situational/dismantler.ts b/src/overlords/situational/dismantler.ts index 293764b85..7520def4d 100644 --- a/src/overlords/situational/dismantler.ts +++ b/src/overlords/situational/dismantler.ts @@ -68,7 +68,7 @@ export class DismantleOverlord extends Overlord { } else { if (!this.target) { if (this.directive.memory.targetId) { - this.target = Game.getObjectById(this.directive.memory.targetId.toString()) || undefined; + this.target = Game.getObjectById(this.directive.memory.targetId) || undefined; } this.target = this.target || this.directive.getTarget(); if (!this.target) { diff --git a/src/overlords/situational/hauler.ts b/src/overlords/situational/hauler.ts index 21e2c9e44..8ec57be08 100644 --- a/src/overlords/situational/hauler.ts +++ b/src/overlords/situational/hauler.ts @@ -28,7 +28,7 @@ export class HaulingOverlord extends Overlord { } init() { - if (!this.colony.storage || _.sum(this.colony.storage.store) > Energetics.settings.storage.total.cap) { + if (!this.colony.storage || this.colony.storage.store.getUsedCapacity() > Energetics.settings.storage.total.cap) { return; } // Spawn a number of haulers sufficient to move all resources within a lifetime, up to a max @@ -36,19 +36,23 @@ export class HaulingOverlord extends Overlord { // Calculate total needed amount of hauling power as (resource amount * trip distance) const tripDistance = 2 * (Pathing.distance((this.colony.storage || this.colony).pos, this.directive.pos) || 0); const haulingPowerNeeded = Math.min(this.directive.totalResources, - this.colony.storage.storeCapacity - - _.sum(this.colony.storage.store)) * tripDistance; + this.colony.storage.store.getCapacity() + - this.colony.storage.store.getUsedCapacity()) * tripDistance; // Calculate amount of hauling each hauler provides in a lifetime const haulerCarryParts = Setups.transporters.early.getBodyPotential(CARRY, this.colony); const haulingPowerPerLifetime = CREEP_LIFE_TIME * haulerCarryParts * CARRY_CAPACITY; // Calculate number of haulers const numHaulers = Math.min(Math.ceil(haulingPowerNeeded / haulingPowerPerLifetime), MAX_HAULERS); // Request the haulers - this.wishlist(numHaulers, Setups.transporters.early); + if (this.haulers.length === 0) { + this.wishlist(numHaulers, Setups.transporters.early, { priority: OverlordPriority.collectionUrgent.haul }); + } else { + this.wishlist(numHaulers, Setups.transporters.early); + } } private handleHauler(hauler: Zerg) { - if (_.sum(hauler.carry) == 0) { + if (hauler.store.getUsedCapacity() == 0) { // Travel to directive and collect resources if (hauler.inSameRoomAs(this.directive)) { // Pick up drops first @@ -64,7 +68,7 @@ export class HaulingOverlord extends Overlord { if (this.directive.storeStructure) { const store = this.directive.store!; let totalDrawn = 0; // Fill to full - for (const resourceType in store) { + for (const resourceType of Object.keys(store)) { if (store[resourceType] > 0) { if (hauler.task) { hauler.task = Tasks.withdraw(this.directive.storeStructure, resourceType).fork(hauler.task); @@ -72,7 +76,7 @@ export class HaulingOverlord extends Overlord { hauler.task = Tasks.withdraw(this.directive.storeStructure, resourceType); } totalDrawn += store[resourceType]; - if (totalDrawn >= hauler.carryCapacity) { + if (totalDrawn >= hauler.store.getCapacity()) { return; } } @@ -93,22 +97,22 @@ export class HaulingOverlord extends Overlord { // Travel to colony room and deposit resources if (hauler.inSameRoomAs(this.colony)) { // Put energy in storage and minerals in terminal if there is one - for (const [resourceType, amount] of hauler.carry.contents) { + for (const [resourceType, amount] of hauler.store.contents) { if (amount == 0) continue; if (resourceType == RESOURCE_ENERGY) { // prefer to put energy in storage - if (this.colony.storage && _.sum(this.colony.storage.store) < STORAGE_CAPACITY) { + if (this.colony.storage && this.colony.storage.store.getUsedCapacity() < STORAGE_CAPACITY) { hauler.task = Tasks.transfer(this.colony.storage, resourceType); return; - } else if (this.colony.terminal && _.sum(this.colony.terminal.store) < TERMINAL_CAPACITY) { + } else if (this.colony.terminal && this.colony.terminal.store.getUsedCapacity() < TERMINAL_CAPACITY) { hauler.task = Tasks.transfer(this.colony.terminal, resourceType); return; } } else { // prefer to put minerals in terminal if (this.colony.terminal && this.colony.terminal.my - && _.sum(this.colony.terminal.store) < TERMINAL_CAPACITY) { + && this.colony.terminal.store.getUsedCapacity() < TERMINAL_CAPACITY) { hauler.task = Tasks.transfer(this.colony.terminal, resourceType); return; - } else if (this.colony.storage && _.sum(this.colony.storage.store) < STORAGE_CAPACITY) { + } else if (this.colony.storage && this.colony.storage.store.getUsedCapacity() < STORAGE_CAPACITY) { hauler.task = Tasks.transfer(this.colony.storage, resourceType); return; } @@ -130,7 +134,7 @@ export class HaulingOverlord extends Overlord { hauler.run(); } // TODO: fix the way this is done - if (this.directive.memory.totalResources == 0 && this.haulers.filter(hauler => _.sum(hauler.carry) > 0).length == 0) { + if (this.directive.memory.totalResources == 0 && this.haulers.filter(hauler => hauler.store.getUsedCapacity() > 0).length == 0) { this.directive.remove(); } } diff --git a/src/overlords/situational/remoteUpgrader.ts b/src/overlords/situational/remoteUpgrader.ts index cb1d8c57d..232ca7c42 100644 --- a/src/overlords/situational/remoteUpgrader.ts +++ b/src/overlords/situational/remoteUpgrader.ts @@ -120,7 +120,7 @@ export class RemoteUpgradingOverlord extends Overlord { return; } // You're in the room, upgrade if you have energy - if (upgrader.carry.energy > 0) { + if (upgrader.store.energy > 0) { upgrader.task = Tasks.upgrade(this.upgradeSite.controller); return; } @@ -135,8 +135,8 @@ export class RemoteUpgradingOverlord extends Overlord { } // Recharge from transporter? const nearbyCarriers = _.filter(this.carriers, carrier => upgrader.pos.getRangeTo(carrier) <= 5); - const nearbyCarriersWaitingToUnload = _.filter(nearbyCarriers, carrier => carrier.carry.energy > 0); - const lowestEnergyCarrier = minBy(nearbyCarriersWaitingToUnload, carrier => carrier.carry.energy); + const nearbyCarriersWaitingToUnload = _.filter(nearbyCarriers, carrier => carrier.store.energy > 0); + const lowestEnergyCarrier = minBy(nearbyCarriersWaitingToUnload, carrier => carrier.store.energy); if (lowestEnergyCarrier) { upgrader.goTo(lowestEnergyCarrier); return; @@ -153,10 +153,10 @@ export class RemoteUpgradingOverlord extends Overlord { } // Get energy from the parent colony if you need it - if (carrier.carry.energy == 0) { + if (carrier.store.energy == 0) { // If you are in the child room and there are valuable resources in a storage/terminal that isn't mine, // then take those back before you go home - if (carrier.room == this.childColony.room && carrier.carry.getFreeCapacity() > 0) { + if (carrier.room == this.childColony.room && carrier.store.getFreeCapacity() > 0) { const storeStructuresNotMy = _.filter(_.compact([this.childColony.room.storage, this.childColony.room.terminal]), @@ -166,7 +166,7 @@ export class RemoteUpgradingOverlord extends Overlord { structure => structure.store.getUsedCapacity(resource) > 0); if (withdrawTarget) { const amount = Math.min(withdrawTarget.store.getUsedCapacity(resource), - carrier.carry.getFreeCapacity()); + carrier.store.getFreeCapacity()); carrier.task = Tasks.withdraw(withdrawTarget, resource, amount); return; } @@ -179,12 +179,12 @@ export class RemoteUpgradingOverlord extends Overlord { } const target = _.find(_.compact([this.parentColony.storage, this.parentColony.terminal]), - s => s!.store[RESOURCE_ENERGY] >= carrier.carryCapacity); + s => s!.store[RESOURCE_ENERGY] >= carrier.store.getCapacity()); if (!target) { log.warning(`${this.print}: no energy withdraw target for ${carrier.print}!`); return; } - if (carrier.carry.getUsedCapacity() > carrier.carry.getUsedCapacity(RESOURCE_ENERGY)) { + if (carrier.store.getUsedCapacity() > carrier.store.getUsedCapacity(RESOURCE_ENERGY)) { carrier.task = Tasks.transferAll(target); } else { carrier.task = Tasks.withdraw(target); @@ -202,9 +202,9 @@ export class RemoteUpgradingOverlord extends Overlord { // otherwise put in storage if you can const depositPos = this.upgradeSite.batteryPos || this.upgradeSite.pos; const carriersWaitingToUnload = _.filter(this.carriers, carrier => - carrier.carry.energy > 0 && carrier.pos.inRangeToPos(depositPos, 5)); + carrier.store.energy > 0 && carrier.pos.inRangeToPos(depositPos, 5)); const firstCarrierInQueue = minBy(carriersWaitingToUnload, carrier => - carrier.carry.energy + (carrier.ticksToLive || Infinity) / 10000); + carrier.store.energy + (carrier.ticksToLive || Infinity) / 10000); // Put in storage if you can if (this.childColony.storage && firstCarrierInQueue && firstCarrierInQueue != carrier) { diff --git a/src/overlords/~template/templateOverlord.ts b/src/overlords/~template/templateOverlord.ts index 1194674ed..555fe781c 100644 --- a/src/overlords/~template/templateOverlord.ts +++ b/src/overlords/~template/templateOverlord.ts @@ -143,7 +143,7 @@ export class TemplateOverlord extends Overlord { return; } // You're in the room, upgrade if you have energy - if (upgrader.carry.energy > 0) { + if (upgrader.store.energy > 0) { upgrader.task = Tasks.upgrade(this.colony.controller); return; } @@ -190,7 +190,7 @@ export class TemplateOverlord extends Overlord { private handleWorker(worker: Zerg): void { // Get energy if needed - if (worker.carry.energy == 0) { + if (worker.store.energy == 0) { worker.task = Tasks.recharge(); return; } diff --git a/src/prototypes/Creep.ts b/src/prototypes/Creep.ts index 744e7f860..62f2e9e51 100644 --- a/src/prototypes/Creep.ts +++ b/src/prototypes/Creep.ts @@ -3,9 +3,9 @@ // Boosting logic ------------------------------------------------------------------------------------------------------ Object.defineProperty(Creep.prototype, 'boosts', { - get() { + get(this: Creep) { if (!this._boosts) { - this._boosts = _.compact(_.unique(_.map(this.body as BodyPartDefinition[], bodyPart => bodyPart.boost))); + this._boosts = _.compact(_.unique(_.map(this.body, bodyPart => bodyPart.boost))); } return this._boosts; }, @@ -13,9 +13,9 @@ Object.defineProperty(Creep.prototype, 'boosts', { }); Object.defineProperty(Creep.prototype, 'boostCounts', { - get() { + get(this: Creep) { if (!this._boostCounts) { - this._boostCounts = _.countBy(this.body as BodyPartDefinition[], bodyPart => bodyPart.boost); + this._boostCounts = _.countBy(this.body, bodyPart => bodyPart.boost); } return this._boostCounts; }, @@ -23,16 +23,16 @@ Object.defineProperty(Creep.prototype, 'boostCounts', { }); Object.defineProperty(Creep.prototype, 'approxMoveSpeed', { - get() { + get(this: Creep) { if (this._moveSpeed == undefined) { - const movePower = _.sum(this.body, (part: BodyPartDefinition) => { + const movePower = _.sum(this.body, (part) => { if (part.type == MOVE && part.boost) { - return BOOSTS.move[<'ZO' | 'ZHO2' | 'XZHO2'>part.boost].fatigue; + return BOOSTS.move[part.boost].fatigue; } else { return 0; } }); - const nonMoveParts = _.sum(this.body, (part: BodyPartDefinition) => part.type != MOVE ? 1 : 0); + const nonMoveParts = _.sum(this.body, (part) => part.type != MOVE ? 1 : 0); this._moveSpeed = Math.max(movePower / nonMoveParts, 1); // if nonMoveParts == 0, this will be Infinity -> 1 } return this._moveSpeed; @@ -41,7 +41,7 @@ Object.defineProperty(Creep.prototype, 'approxMoveSpeed', { }); Object.defineProperty(Creep.prototype, 'inRampart', { - get() { + get(this: Creep) { return !!this.pos.lookForStructure(STRUCTURE_RAMPART); // this assumes hostile creeps can't stand in my ramparts }, configurable: true, @@ -50,7 +50,7 @@ Object.defineProperty(Creep.prototype, 'inRampart', { // Permanently cached properties PERMACACHE.bodypartCounts = PERMACACHE.bodypartCounts || {}; Object.defineProperty(Creep.prototype, 'bodypartCounts', { - get() { + get(this: Creep) { if (PERMACACHE.bodypartCounts[this.id] === undefined) { PERMACACHE.bodypartCounts[this.id] = _.countBy(this.body, (part: BodyPartDefinition) => part.type); _.defaults(PERMACACHE.bodypartCounts[this.id], { @@ -71,7 +71,7 @@ Object.defineProperty(Creep.prototype, 'bodypartCounts', { PERMACACHE.isPlayer = PERMACACHE.isPlayer || {}; Object.defineProperty(Creep.prototype, 'isPlayer', { - get() { + get(this: Creep) { if (PERMACACHE.isPlayer[this.id] === undefined) { PERMACACHE.isPlayer[this.id] = this.owner.username != 'Invader' && this.owner.username != 'Source Keeper' && diff --git a/src/prototypes/Game.ts b/src/prototypes/Game.ts index 6f78ba175..14348b674 100644 --- a/src/prototypes/Game.ts +++ b/src/prototypes/Game.ts @@ -1,7 +1,7 @@ // Modifications to Game-level functions const _marketDeal = Game.market.deal; -Game.market.deal = function(orderId: string, amount: number, targetRoomName?: string): ScreepsReturnCode { +Game.market.deal = function(this: Market, orderId: string, amount: number, targetRoomName?: string): ScreepsReturnCode { const response = _marketDeal(orderId, amount, targetRoomName); if (response == OK) { if (targetRoomName && Game.rooms[targetRoomName] && Game.rooms[targetRoomName].terminal diff --git a/src/prototypes/Miscellaneous.ts b/src/prototypes/Miscellaneous.ts index a797dc742..bc6f968e7 100644 --- a/src/prototypes/Miscellaneous.ts +++ b/src/prototypes/Miscellaneous.ts @@ -1,16 +1,16 @@ -String.prototype.padRight = function(length: number, char = ' '): string { +String.prototype.padRight = function(this: string, length: number, char = ' '): string { return this + char.repeat(Math.max(length - this.length, 0)); }; -String.prototype.padLeft = function(length: number, char = ' '): string { +String.prototype.padLeft = function(this: string, length: number, char = ' '): string { return char.repeat(Math.max(length - this.length, 0)) + this; }; -Number.prototype.toPercent = function(decimals = 0): string { +Number.prototype.toPercent = function(this: number, decimals = 0): string { return (this * 100).toFixed(decimals) + '%'; }; -Number.prototype.truncate = function(decimals: number): number { +Number.prototype.truncate = function(this: number, decimals: number): number { const re = new RegExp('(\\d+\\.\\d{' + decimals + '})(\\d)'), m = this.toString().match(re); return m ? parseFloat(m[1]) : this.valueOf(); @@ -18,7 +18,7 @@ Number.prototype.truncate = function(decimals: number): number { PERMACACHE.structureWalkability = PERMACACHE.structureWalkability || {}; Object.defineProperty(ConstructionSite.prototype, 'isWalkable', { - get() { + get(this: ConstructionSite) { if (PERMACACHE.structureWalkability[this.id] === undefined) { PERMACACHE.structureWalkability[this.id] = this.structureType == STRUCTURE_ROAD || this.structureType == STRUCTURE_CONTAINER || diff --git a/src/prototypes/PowerCreep.ts b/src/prototypes/PowerCreep.ts index 51419cdff..68def4cd5 100644 --- a/src/prototypes/PowerCreep.ts +++ b/src/prototypes/PowerCreep.ts @@ -1,5 +1,5 @@ Object.defineProperty(PowerCreep.prototype, 'inRampart', { - get() { + get(this: PowerCreep) { return !!this.pos.lookForStructure(STRUCTURE_RAMPART); // this assumes hostile creeps can't stand in my ramparts }, configurable: true, diff --git a/src/prototypes/Room.ts b/src/prototypes/Room.ts index 8c4342a40..378e99738 100644 --- a/src/prototypes/Room.ts +++ b/src/prototypes/Room.ts @@ -5,7 +5,7 @@ import {MY_USERNAME} from '../~settings'; // Logging ============================================================================================================= Object.defineProperty(Room.prototype, 'print', { - get() { + get(this: Room) { return '' + this.name + ''; }, configurable: true, @@ -14,14 +14,14 @@ Object.defineProperty(Room.prototype, 'print', { // Room properties ===================================================================================================== Object.defineProperty(Room.prototype, 'my', { - get() { + get(this: Room) { return this.controller && this.controller.my; }, configurable: true, }); Object.defineProperty(Room.prototype, 'isColony', { - get() { + get(this: Room) { return Overmind.colonies[this.name] != undefined; }, configurable: true, @@ -29,28 +29,28 @@ Object.defineProperty(Room.prototype, 'isColony', { Object.defineProperty(Room.prototype, 'isOutpost', { - get() { + get(this: Room) { return Overmind.colonyMap[this.name] != undefined; }, configurable: true, }); Object.defineProperty(Room.prototype, 'owner', { - get() { + get(this: Room) { return this.controller && this.controller.owner ? this.controller.owner.username : undefined; }, configurable: true, }); Object.defineProperty(Room.prototype, 'reservedByMe', { - get() { + get(this: Room) { return this.controller && this.controller.reservation && this.controller.reservation.username == MY_USERNAME; }, configurable: true, }); Object.defineProperty(Room.prototype, 'signedByMe', { - get() { + get(this: Room) { return this.controller && this.controller.sign && this.controller.sign.text == Memory.settings.signature; }, configurable: true, @@ -60,7 +60,7 @@ Object.defineProperty(Room.prototype, 'signedByMe', { // Creeps physically in the room Object.defineProperty(Room.prototype, 'creeps', { - get() { + get(this: Room) { if (!this._creeps) { this._creeps = this.find(FIND_MY_CREEPS); } @@ -72,7 +72,7 @@ Object.defineProperty(Room.prototype, 'creeps', { // Room properties: hostiles =========================================================================================== Object.defineProperty(Room.prototype, 'hostiles', { - get() { + get(this: Room) { if (!this._hostiles) { this._hostiles = this.find(FIND_HOSTILE_CREEPS, {filter: (creep: Creep) => !isAlly(creep.owner.username)}); } @@ -82,7 +82,7 @@ Object.defineProperty(Room.prototype, 'hostiles', { }); Object.defineProperty(Room.prototype, 'friendlies', { - get() { + get(this: Room) { if (!this._friendlies) { this._friendlies = this.find(FIND_HOSTILE_CREEPS, {filter: (creep: Creep) => isAlly(creep.owner.username)}); } @@ -92,7 +92,7 @@ Object.defineProperty(Room.prototype, 'friendlies', { }); Object.defineProperty(Room.prototype, 'invaders', { - get() { + get(this: Room) { if (!this._invaders) { this._invaders = _.filter(this.hostiles, (creep: Creep) => creep.owner.username == 'Invader'); } @@ -102,7 +102,7 @@ Object.defineProperty(Room.prototype, 'invaders', { }); Object.defineProperty(Room.prototype, 'sourceKeepers', { - get() { + get(this: Room) { if (!this._sourceKeepers) { this._sourceKeepers = _.filter(this.hostiles, (creep: Creep) => creep.owner.username == 'Source Keeper'); } @@ -112,9 +112,9 @@ Object.defineProperty(Room.prototype, 'sourceKeepers', { }); Object.defineProperty(Room.prototype, 'playerHostiles', { - get() { + get(this: Room) { if (!this._playerHostiles) { - this._playerHostiles = _.filter(this.hostiles, (creep: Creep) => creep.isHuman); + this._playerHostiles = _.filter(this.hostiles, (creep: Creep) => creep.isPlayer); } return this._playerHostiles; }, @@ -122,7 +122,7 @@ Object.defineProperty(Room.prototype, 'playerHostiles', { }); Object.defineProperty(Room.prototype, 'dangerousHostiles', { - get() { + get(this: Room) { if (!this._dangerousHostiles) { if (this.my) { this._dangerousHostiles = _.filter(this.hostiles, @@ -141,7 +141,7 @@ Object.defineProperty(Room.prototype, 'dangerousHostiles', { }); Object.defineProperty(Room.prototype, 'dangerousPlayerHostiles', { - get() { + get(this: Room) { if (!this._dangerousPlayerHostiles) { if (this.my) { this._dangerousPlayerHostiles = _.filter(this.playerHostiles, @@ -160,7 +160,7 @@ Object.defineProperty(Room.prototype, 'dangerousPlayerHostiles', { }); Object.defineProperty(Room.prototype, 'fleeDefaults', { - get() { + get(this: Room) { if (!this._fleeDefaults) { this._fleeDefaults = [ ...this.dangerousHostiles, @@ -174,7 +174,7 @@ Object.defineProperty(Room.prototype, 'fleeDefaults', { // Hostile structures currently in the room Object.defineProperty(Room.prototype, 'structures', { - get() { + get(this: Room) { if (!this._allStructures) { this._allStructures = this.find(FIND_STRUCTURES); } @@ -186,7 +186,7 @@ Object.defineProperty(Room.prototype, 'structures', { // Hostile structures currently in the room Object.defineProperty(Room.prototype, 'hostileStructures', { - get() { + get(this: Room) { if (!this._hostileStructures) { this._hostileStructures = this.find(FIND_HOSTILE_STRUCTURES, { filter: (s: Structure) => (s.hitsMax) && !isAlly(_.get(s, ['owner', 'username'])) @@ -201,7 +201,7 @@ Object.defineProperty(Room.prototype, 'hostileStructures', { // Flags physically in this room Object.defineProperty(Room.prototype, 'flags', { - get() { + get(this: Room) { if (!this._flags) { this._flags = this.find(FIND_FLAGS); } @@ -213,7 +213,7 @@ Object.defineProperty(Room.prototype, 'flags', { // Room properties: structures ========================================================================================= Object.defineProperty(Room.prototype, 'constructionSites', { - get() { + get(this: Room) { if (!this._constructionSites) { this._constructionSites = this.find(FIND_MY_CONSTRUCTION_SITES); } @@ -223,7 +223,7 @@ Object.defineProperty(Room.prototype, 'constructionSites', { }); Object.defineProperty(Room.prototype, 'allConstructionSites', { - get() { + get(this: Room) { if (!this._allConstructionSites) { this._allConstructionSites = this.find(FIND_CONSTRUCTION_SITES); } @@ -233,7 +233,7 @@ Object.defineProperty(Room.prototype, 'allConstructionSites', { }); Object.defineProperty(Room.prototype, 'hostileConstructionSites', { - get() { + get(this: Room) { if (!this._hostileConstructionSites) { this._hostileConstructionSites = this.find(FIND_HOSTILE_CONSTRUCTION_SITES); } @@ -243,7 +243,7 @@ Object.defineProperty(Room.prototype, 'hostileConstructionSites', { }); Object.defineProperty(Room.prototype, 'tombstones', { - get() { + get(this: Room) { if (!this._tombstones) { this._tombstones = this.find(FIND_TOMBSTONES); } @@ -253,7 +253,7 @@ Object.defineProperty(Room.prototype, 'tombstones', { }); Object.defineProperty(Room.prototype, 'ruins', { - get() { + get(this: Room) { if (!this._ruins) { this._ruins = this.find(FIND_RUINS); } @@ -263,7 +263,7 @@ Object.defineProperty(Room.prototype, 'ruins', { }); Object.defineProperty(Room.prototype, 'drops', { - get() { + get(this: Room) { if (!this._drops) { this._drops = _.groupBy(this.find(FIND_DROPPED_RESOURCES), (r: Resource) => r.resourceType); } @@ -273,14 +273,14 @@ Object.defineProperty(Room.prototype, 'drops', { }); Object.defineProperty(Room.prototype, 'droppedEnergy', { - get() { + get(this: Room) { return this.drops[RESOURCE_ENERGY] || []; }, configurable: true, }); Object.defineProperty(Room.prototype, 'droppedPower', { - get() { + get(this: Room) { return this.drops[RESOURCE_POWER] || []; }, configurable: true, diff --git a/src/prototypes/RoomObject.ts b/src/prototypes/RoomObject.ts index b63375f81..51639d48b 100644 --- a/src/prototypes/RoomObject.ts +++ b/src/prototypes/RoomObject.ts @@ -1,20 +1,20 @@ // RoomObject prototypes Object.defineProperty(RoomObject.prototype, 'ref', { // reference object; see globals.deref (which includes Creep) - get : function() { + get: function(this: _HasId & { name:string }) { return this.id || this.name || ''; }, configurable: true, }); Object.defineProperty(RoomObject.prototype, 'targetedBy', { // List of creep names with tasks targeting this object - get : function() { + get: function(this: RoomObject) { return Overmind.cache.targets[this.ref] || []; }, configurable: true, }); -RoomObject.prototype.serialize = function(): ProtoRoomObject { +RoomObject.prototype.serialize = function(this: RoomObject): ProtoRoomObject { const pos: ProtoPos = { x : this.pos.x, y : this.pos.y, diff --git a/src/prototypes/RoomPosition.ts b/src/prototypes/RoomPosition.ts index 0ef0b24ee..b57c93b59 100644 --- a/src/prototypes/RoomPosition.ts +++ b/src/prototypes/RoomPosition.ts @@ -2,7 +2,7 @@ import {Cartographer} from '../utilities/Cartographer'; import {minBy, mod} from '../utilities/utils'; Object.defineProperty(RoomPosition.prototype, 'print', { - get() { + get(this: RoomPosition) { return '[' + this.roomName + ', ' + this.x + ', ' + this.y + ']'; }, @@ -10,25 +10,25 @@ Object.defineProperty(RoomPosition.prototype, 'print', { }); Object.defineProperty(RoomPosition.prototype, 'printPlain', { - get() { + get(this: RoomPosition) { return `[${this.roomName}, ${this.x}, ${this.y}]`; }, configurable: true, }); Object.defineProperty(RoomPosition.prototype, 'room', { - get : function() { + get(this: RoomPosition) { return Game.rooms[this.roomName]; }, configurable: true, }); -RoomPosition.prototype.toCoord = function(): Coord { +RoomPosition.prototype.toCoord = function(this: RoomPosition): Coord { return {x: this.x, y: this.y}; }; Object.defineProperty(RoomPosition.prototype, 'readableName', { // identifier for the pos, used in caching - get : function() { + get: function(this: RoomPosition) { return this.roomName + ':' + this.x + ':' + this.y; }, configurable: true, @@ -41,11 +41,11 @@ Object.defineProperty(RoomPosition.prototype, 'readableName', { // identifier fo // configurable: true, // }); -RoomPosition.prototype.lookForStructure = function(structureType: StructureConstant): Structure | undefined { +RoomPosition.prototype.lookForStructure = function(this: RoomPosition, structureType: StructureConstant): Structure | undefined { return _.find(this.lookFor(LOOK_STRUCTURES), s => s.structureType === structureType); }; -RoomPosition.prototype.getOffsetPos = function(dx: number, dy: number): RoomPosition { +RoomPosition.prototype.getOffsetPos = function(this: RoomPosition, dx: number, dy: number): RoomPosition { let roomName = this.roomName; let x = this.x + dx; if (x < 0 || x > 49) { @@ -67,28 +67,28 @@ RoomPosition.prototype.getOffsetPos = function(dx: number, dy: number): RoomPosi // } Object.defineProperty(RoomPosition.prototype, 'isEdge', { // if the position is at the edge of a room - get : function() { + get: function(this: RoomPosition) { return this.x === 0 || this.x === 49 || this.y === 0 || this.y === 49; }, configurable: true, }); Object.defineProperty(RoomPosition.prototype, 'isVisible', { // if the position is in a defined room - get : function() { + get: function(this: RoomPosition) { return Game.rooms[this.roomName] != undefined; }, configurable: true, }); Object.defineProperty(RoomPosition.prototype, 'rangeToEdge', { // range to the nearest room edge - get : function() { + get: function(this: RoomPosition) { return _.min([this.x, 49 - this.x, this.y, 49 - this.y]); }, configurable: true, }); Object.defineProperty(RoomPosition.prototype, 'roomCoords', { - get : function() { + get: function(this: RoomPosition) { const parsed = /^[WE]([0-9]+)[NS]([0-9]+)$/.exec(this.roomName); let x = parseInt(parsed![1], 10); let y = parseInt(parsed![2], 10); @@ -100,7 +100,7 @@ Object.defineProperty(RoomPosition.prototype, 'roomCoords', { }); Object.defineProperty(RoomPosition.prototype, 'neighbors', { - get : function() { + get: function(this: RoomPosition) { const adjPos: RoomPosition[] = []; for (const dx of [-1, 0, 1]) { for (const dy of [-1, 0, 1]) { @@ -118,23 +118,24 @@ Object.defineProperty(RoomPosition.prototype, 'neighbors', { configurable: true, }); -RoomPosition.prototype.inRangeToPos = function(pos: RoomPosition, range: number): boolean { +RoomPosition.prototype.inRangeToPos = function(this: RoomPosition, pos: RoomPosition, range: number): boolean { return this.roomName === pos.roomName && ((pos.x - this.x) < 0 ? (this.x - pos.x) : (pos.x - this.x)) <= range && ((pos.y - this.y) < 0 ? (this.y - pos.y) : (pos.y - this.y)) <= range; }; -RoomPosition.prototype.inRangeToXY = function(x: number, y: number, range: number) { +RoomPosition.prototype.inRangeToXY = function(this: RoomPosition, x: number, y: number, range: number) { return ((x - this.x) < 0 ? (this.x - x) : (x - this.x)) <= range && ((y - this.y) < 0 ? (this.y - y) : (y - this.y)) <= range; }; -RoomPosition.prototype.getRangeToXY = function(x: number, y: number) { +RoomPosition.prototype.getRangeToXY = function(this: RoomPosition, x: number, y: number) { return Math.max((x - this.x) < 0 ? (this.x - x) : (x - this.x), ((y - this.y) < 0 ? (this.y - y) : (y - this.y))); }; -RoomPosition.prototype.getPositionsInRange = function(range: number, - includeWalls = false, includeEdges = false): RoomPosition[] { +RoomPosition.prototype.getPositionsInRange = function(this: RoomPosition, + range: number, + includeWalls = false, includeEdges = false): RoomPosition[] { const terrain = Game.map.getRoomTerrain(this.roomName); const adjPos: RoomPosition[] = []; @@ -154,8 +155,9 @@ RoomPosition.prototype.getPositionsInRange = function(range: number, return adjPos; }; -RoomPosition.prototype.getPositionsAtRange = function(range: number, - includeWalls = false, includeEdges = false): RoomPosition[] { +RoomPosition.prototype.getPositionsAtRange = function(this: RoomPosition, + range: number, + includeWalls = false, includeEdges = false): RoomPosition[] { const terrain = Game.map.getRoomTerrain(this.roomName); const adjPos: RoomPosition[] = []; const [xmin, xmax] = includeEdges ? [0, 49] : [1, 48]; @@ -177,7 +179,7 @@ RoomPosition.prototype.getPositionsAtRange = function(range: number, return adjPos; }; -RoomPosition.prototype.isWalkable = function(ignoreCreeps = false): boolean { +RoomPosition.prototype.isWalkable = function(this: RoomPosition, ignoreCreeps = false): boolean { // Is terrain passable? if (Game.map.getRoomTerrain(this.roomName).get(this.x, this.y) == TERRAIN_MASK_WALL) return false; if (this.isVisible) { @@ -189,11 +191,11 @@ RoomPosition.prototype.isWalkable = function(ignoreCreeps = false): boolean { return true; }; -RoomPosition.prototype.availableNeighbors = function(ignoreCreeps = false): RoomPosition[] { +RoomPosition.prototype.availableNeighbors = function(this: RoomPosition, ignoreCreeps = false): RoomPosition[] { return _.filter(this.neighbors, pos => pos.isWalkable(ignoreCreeps)); }; -RoomPosition.prototype.getPositionAtDirection = function(direction: DirectionConstant, range = 1): RoomPosition { +RoomPosition.prototype.getPositionAtDirection = function(this: RoomPosition, direction: DirectionConstant, range = 1): RoomPosition { let dx = 0; let dy = 0; switch (direction) { @@ -249,7 +251,7 @@ RoomPosition.prototype.getPositionAtDirection = function(direction: DirectionCon // }); // Get an estimate for the distance to another room position in a possibly different room -RoomPosition.prototype.getMultiRoomRangeTo = function(pos: RoomPosition): number { +RoomPosition.prototype.getMultiRoomRangeTo = function(this: RoomPosition, pos: RoomPosition): number { if (this.roomName == pos.roomName) { return this.getRangeTo(pos); } else { @@ -261,26 +263,27 @@ RoomPosition.prototype.getMultiRoomRangeTo = function(pos: RoomPosition): number } }; -RoomPosition.prototype.findClosestByLimitedRange = function (objects: T[] | RoomPosition[], rangeLimit: number, - opts?: { filter: any | string; }): T | undefined { - const objectsInRange = this.findInRange(objects, rangeLimit, opts); - return this.findClosestByRange(objectsInRange, opts); +RoomPosition.prototype.findClosestByLimitedRange = function (this: RoomPosition, + objects: T[], rangeLimit: number, + opts?: { filter: any | string; }): T | undefined { + const objectsInRange = this.findInRange(objects, rangeLimit, opts); + return this.findClosestByRange(objectsInRange, opts) || undefined; }; -RoomPosition.prototype.findClosestByMultiRoomRange = function (objects: T[]): +RoomPosition.prototype.findClosestByMultiRoomRange = function (this: RoomPosition, objects: T[]): T | undefined { return minBy(objects, (obj: T) => this.getMultiRoomRangeTo(obj.pos)); }; // This should only be used within a single room -RoomPosition.prototype.findClosestByRangeThenPath = function (objects: T[]): T { +RoomPosition.prototype.findClosestByRangeThenPath = function (this: RoomPosition, objects: T[]): T | undefined { const distances = _.map(objects, obj => this.getRangeTo(obj)); const minDistance = _.min(distances); if (minDistance > 4) { - return this.findClosestByRange(objects); + return this.findClosestByRange(objects) || undefined; } else { const closestObjects = _.filter(objects, obj => this.getRangeTo(obj) == minDistance); - return this.findClosestByPath(closestObjects); // don't clutter up pathing.distance cached values + return this.findClosestByPath(closestObjects) || undefined; // don't clutter up pathing.distance cached values } }; diff --git a/src/prototypes/RoomStructures.ts b/src/prototypes/RoomStructures.ts index 647783c3e..8ea577cb7 100644 --- a/src/prototypes/RoomStructures.ts +++ b/src/prototypes/RoomStructures.ts @@ -3,6 +3,18 @@ import {getCacheExpiration, onPublicServer} from '../utilities/utils'; +declare global { + interface Room { + _storageUnits: StorageUnit[]; + _sources: Source[]; + _mineral: Mineral | undefined; + _repairables: (Structure | null)[]; + _rechargeables: rechargeObjectType[]; + _walkableRamparts: (StructureRampart | null)[]; + _barriers: (StructureWall | StructureRampart)[] | undefined; + } +} + const roomStructureIDs: { [roomName: string]: { [structureType: string]: string[] } } = {}; const roomStructuresExpiration: { [roomName: string]: number } = {}; @@ -36,7 +48,7 @@ Room.prototype._refreshStructureCache = function() { multipleList.forEach(function(type) { Object.defineProperty(Room.prototype, type + 's', { - get : function() { + get: function() { if (this['_' + type + 's']) { return this['_' + type + 's']; } else { @@ -55,7 +67,7 @@ multipleList.forEach(function(type) { singleList.forEach(function(type) { Object.defineProperty(Room.prototype, type, { - get : function() { + get: function() { if (this['_' + type]) { return this['_' + type]; } else { @@ -73,9 +85,10 @@ singleList.forEach(function(type) { Object.defineProperty(Room.prototype, 'storageUnits', { - get() { + get(this: Room) { if (!this._storageUnits) { - this._storageUnits = _.compact([this.storage, this.terminal]).concat(this.containers); + const special: (StorageUnit | undefined)[] = [this.storage, this.terminal]; + this._storageUnits = _.compact(special).concat(this.containers); } return this._storageUnits; }, @@ -83,7 +96,7 @@ Object.defineProperty(Room.prototype, 'storageUnits', { }); Object.defineProperty(Room.prototype, 'sources', { - get() { + get(this: Room) { if (!this._sources) { this._sources = this.find(FIND_SOURCES); } @@ -93,7 +106,7 @@ Object.defineProperty(Room.prototype, 'sources', { }); Object.defineProperty(Room.prototype, 'mineral', { - get() { + get(this: Room) { if (!this._mineral) { this._mineral = this.find(FIND_MINERALS)[0]; } @@ -103,25 +116,26 @@ Object.defineProperty(Room.prototype, 'mineral', { }); Object.defineProperty(Room.prototype, 'repairables', { - get() { + get(this: Room) { if (!this._repairables) { this._refreshStructureCache(); if (roomStructureIDs[this.name].repairables) { return this._repairables = _.compact(_.map(roomStructureIDs[this.name].repairables, - Game.getObjectById)); + o => Game.getObjectById(o))); } else { let repairables: Structure[] = []; for (const structureType of singleList) { - if (this[structureType]) { - repairables.push(this[structureType]); - } + const o = this[structureType]; + if (!o) continue; + repairables.push(o); } for (const structureType of multipleList) { if (structureType != STRUCTURE_WALL && structureType != STRUCTURE_RAMPART && structureType != STRUCTURE_ROAD && !notRepairable.includes(structureType)) { - repairables = repairables.concat(this[structureType + 's']); + const obj = <{[p: string]: Structure[]}>this; + repairables = repairables.concat(obj[structureType + 's']); } } roomStructureIDs[this.name].repairables = _.map(repairables, s => s.id); @@ -136,12 +150,12 @@ Object.defineProperty(Room.prototype, 'repairables', { // TODO: this is expensive and easy to over-use. Perhaps remove this. Object.defineProperty(Room.prototype, 'walkableRamparts', { - get() { + get(this: Room) { if (!this._walkableRamparts) { this._refreshStructureCache(); if (roomStructureIDs[this.name].walkableRamparts) { return this._walkableRamparts = _.compact(_.map(roomStructureIDs[this.name].walkableRamparts, - Game.getObjectById)); + (o) => Game.getObjectById(o))); } else { const walkableRamparts = _.filter(this.ramparts, (r: StructureRampart) => r.pos.isWalkable(true)); @@ -155,7 +169,7 @@ Object.defineProperty(Room.prototype, 'walkableRamparts', { }); Object.defineProperty(Room.prototype, 'rechargeables', { - get() { + get(this: Room) { if (!this._rechargeables) { this._rechargeables = [...this.storageUnits, ...this.droppedEnergy, @@ -168,9 +182,9 @@ Object.defineProperty(Room.prototype, 'rechargeables', { }); Object.defineProperty(Room.prototype, 'barriers', { - get() { + get(this: Room) { if (!this._barriers) { - this._barriers = [].concat(this.ramparts, this.constructedWalls); + this._barriers = (<(StructureRampart | StructureWall)[]>[]).concat(this.ramparts, this.constructedWalls); } return this._barriers; }, @@ -178,7 +192,7 @@ Object.defineProperty(Room.prototype, 'barriers', { }); Object.defineProperty(Room.prototype, 'walls', { - get() { + get(this: Room) { return this.constructedWalls; }, configurable: true, diff --git a/src/prototypes/RoomVisual.ts b/src/prototypes/RoomVisual.ts index 54748090d..e21226aca 100644 --- a/src/prototypes/RoomVisual.ts +++ b/src/prototypes/RoomVisual.ts @@ -1,4 +1,7 @@ -RoomVisual.prototype.infoBox = function(info: string[], x: number, y: number, opts = {}): RoomVisual { +type Point = [number, number]; +type Points = Point[] | RoomPosition[]; + +RoomVisual.prototype.infoBox = function(this: RoomVisual, info: string[], x: number, y: number, opts = {}): RoomVisual { _.defaults(opts, { color : colors.infoBoxGood, textstyle: false, @@ -13,7 +16,7 @@ RoomVisual.prototype.infoBox = function(info: string[], x: number, y: number, op } fontstring += opts.textsize + ' ' + opts.textfont; - let pointer = [ + let pointer: Points = [ [.9, -0.25], [.9, 0.25], [0.3, 0.0], @@ -61,7 +64,7 @@ RoomVisual.prototype.infoBox = function(info: string[], x: number, y: number, op return this; }; -RoomVisual.prototype.multitext = function(textLines: string[], x: number, y: number, opts = {}): RoomVisual { +RoomVisual.prototype.multitext = function(this: RoomVisual, textLines: string[], x: number, y: number, opts = {}): RoomVisual { _.defaults(opts, { color : colors.infoBoxGood, textstyle: false, @@ -100,7 +103,7 @@ RoomVisual.prototype.multitext = function(textLines: string[], x: number, y: num return this; }; -RoomVisual.prototype.box = function(x: number, y: number, w: number, h: number, style?: LineStyle): RoomVisual { +RoomVisual.prototype.box = function(this: RoomVisual, x: number, y: number, w: number, h: number, style?: LineStyle): RoomVisual { return this.line(x, y, x + w, y, style) .line(x + w, y, x + w, y + h, style) .line(x + w, y + h, x, y + h, style) @@ -127,7 +130,7 @@ const colors = { const speechSize = 0.5; const speechFont = 'Times New Roman'; -RoomVisual.prototype.structure = function(x: number, y: number, type: string, opts = {}): RoomVisual { +RoomVisual.prototype.structure = function(this: RoomVisual, x: number, y: number, type: string, opts = {}): RoomVisual { _.defaults(opts, {opacity: 0.5}); switch (type) { case STRUCTURE_EXTENSION: @@ -176,13 +179,13 @@ RoomVisual.prototype.structure = function(x: number, y: number, type: string, op case STRUCTURE_LINK: { // let osize = 0.3; // let isize = 0.2; - let outer = [ + let outer: Points = [ [0.0, -0.5], [0.4, 0.0], [0.0, 0.5], [-0.4, 0.0] ]; - let inner = [ + let inner: Points = [ [0.0, -0.3], [0.25, 0.0], [0.0, 0.3], @@ -200,13 +203,12 @@ RoomVisual.prototype.structure = function(x: number, y: number, type: string, op }); this.poly(inner, { fill : colors.gray, - stroke : false, opacity: opts.opacity }); break; } case STRUCTURE_TERMINAL: { - let outer = [ + let outer: Points = [ [0.0, -0.8], [0.55, -0.55], [0.8, 0.0], @@ -216,7 +218,7 @@ RoomVisual.prototype.structure = function(x: number, y: number, type: string, op [-0.8, 0.0], [-0.55, -0.55], ]; - let inner = [ + let inner: Points = [ [0.0, -0.65], [0.45, -0.45], [0.65, 0.0], @@ -238,7 +240,6 @@ RoomVisual.prototype.structure = function(x: number, y: number, type: string, op }); this.poly(inner, { fill : colors.light, - stroke : false, opacity: opts.opacity }); this.rect(x - 0.45, y - 0.45, 0.9, 0.9, { @@ -264,11 +265,10 @@ RoomVisual.prototype.structure = function(x: number, y: number, type: string, op }); this.rect(x - 0.45, y + 0.3, 0.9, 0.25, { fill : colors.dark, - stroke : false, opacity: opts.opacity }); { - let box = [ + let box: Points = [ [-0.45, 0.3], [-0.45, 0.55], [0.45, 0.55], @@ -305,7 +305,6 @@ RoomVisual.prototype.structure = function(x: number, y: number, type: string, op this.circle(x, y, { radius : 0.175, fill : colors.road, - stroke : false, opacity: opts.opacity }); if (!this.roads) this.roads = []; @@ -367,7 +366,7 @@ RoomVisual.prototype.structure = function(x: number, y: number, type: string, op }); break; case STRUCTURE_NUKER: - let outline = [ + let outline: Points = [ [0, -1], [-0.47, 0.2], [-0.5, 0.5], @@ -382,7 +381,7 @@ RoomVisual.prototype.structure = function(x: number, y: number, type: string, op fill : colors.dark, opacity : opts.opacity }); - let inline = [ + let inline: Points = [ [0, -.80], [-0.40, 0.2], [0.40, 0.2], @@ -430,7 +429,7 @@ const dirs = [ [-1, -1] ]; -RoomVisual.prototype.connectRoads = function(opts = {}): RoomVisual | void { +RoomVisual.prototype.connectRoads = function(this: RoomVisual, opts = {}): RoomVisual | void { _.defaults(opts, {opacity: 0.5}); const color = opts.color || colors.road || 'white'; if (!this.roads) return; @@ -456,7 +455,7 @@ RoomVisual.prototype.connectRoads = function(opts = {}): RoomVisual | void { }; -RoomVisual.prototype.speech = function(text: string, x: number, y: number, opts = {}): RoomVisual { +RoomVisual.prototype.speech = function(this: RoomVisual, text: string, x: number, y: number, opts = {}): RoomVisual { const background = !!opts.background ? opts.background : colors.speechBackground; const textcolor = !!opts.textcolor ? opts.textcolor : colors.speechText; // noinspection PointlessBooleanExpressionJS @@ -471,7 +470,7 @@ RoomVisual.prototype.speech = function(text: string, x: number, y: number, opts } fontstring += textsize + ' ' + textfont; - let pointer = [ + let pointer: Points = [ [-0.2, -0.8], [0.2, -0.8], [0, -0.3] @@ -498,7 +497,7 @@ RoomVisual.prototype.speech = function(text: string, x: number, y: number, opts }; -RoomVisual.prototype.animatedPosition = function(x: number, y: number, opts = {}): RoomVisual { +RoomVisual.prototype.animatedPosition = function(this: RoomVisual, x: number, y: number, opts = {}): RoomVisual { const color = !!opts.color ? opts.color : 'blue'; const opacity = !!opts.opacity ? opts.opacity : 0.5; @@ -513,7 +512,7 @@ RoomVisual.prototype.animatedPosition = function(x: number, y: number, opts = {} const sizeMod = Math.abs(Game.time % frames - frames / 2) / 10; radius += radius * sizeMod; - const points = [ + const points: Points = [ rotate(0, -radius, s, c, x, y), rotate(radius, 0, s, c, x, y), rotate(0, radius, s, c, x, y), @@ -526,14 +525,14 @@ RoomVisual.prototype.animatedPosition = function(x: number, y: number, opts = {} return this; }; -function rotate(x: number, y: number, s: number, c: number, px: number, py: number): { x: number, y: number } { +function rotate(x: number, y: number, s: number, c: number, px: number, py: number): Point { const xDelta = x * c - y * s; const yDelta = x * s + y * c; - return {x: px + xDelta, y: py + yDelta}; + return [px + xDelta, py + yDelta]; } -function relPoly(x: number, y: number, poly: number[][]): number[][] { +function relPoly(x: number, y: number, poly: Point[]): Point[] { return poly.map(p => { p[0] += x; p[1] += y; @@ -541,7 +540,7 @@ function relPoly(x: number, y: number, poly: number[][]): number[][] { }); } -RoomVisual.prototype.test = function(): RoomVisual { +RoomVisual.prototype.test = function(this: RoomVisual): RoomVisual { const demopos = [19, 24]; this.clear(); this.structure(demopos[0] + 0, demopos[1] + 0, STRUCTURE_LAB); @@ -622,7 +621,7 @@ const ResourceColors: { [color: string]: [string, string] } = { }; -RoomVisual.prototype.resource = function(type, x, y, size = 0.25, opacity = 1) { +RoomVisual.prototype.resource = function(this: RoomVisual, type, x, y, size = 0.25, opacity = 1) { if (type == RESOURCE_ENERGY || type == RESOURCE_POWER) { this._fluid(type, x, y, size, opacity); } else if (([RESOURCE_CATALYST, RESOURCE_HYDROGEN, RESOURCE_OXYGEN, RESOURCE_LEMERGIUM, RESOURCE_UTRIUM, @@ -637,7 +636,7 @@ RoomVisual.prototype.resource = function(type, x, y, size = 0.25, opacity = 1) { return OK; }; -RoomVisual.prototype._fluid = function(type, x, y, size = 0.25, opacity = 1) { +RoomVisual.prototype._fluid = function(this: RoomVisual, type, x, y, size = 0.25, opacity = 1) { this.circle(x, y, { radius : size, fill : ResourceColors[type][0], @@ -652,7 +651,7 @@ RoomVisual.prototype._fluid = function(type, x, y, size = 0.25, opacity = 1) { }); }; -RoomVisual.prototype._mineral = function(type, x, y, size = 0.25, opacity = 1) { +RoomVisual.prototype._mineral = function(this: RoomVisual, type, x, y, size = 0.25, opacity = 1) { this.circle(x, y, { radius : size, fill : ResourceColors[type][0], @@ -672,7 +671,7 @@ RoomVisual.prototype._mineral = function(type, x, y, size = 0.25, opacity = 1) { }); }; -RoomVisual.prototype._compound = function(type, x, y, size = 0.25, opacity = 1) { +RoomVisual.prototype._compound = function(this: RoomVisual, type, x, y, size = 0.25, opacity = 1) { const label = type.replace('2', '₂'); this.text(label, x, y, { diff --git a/src/prototypes/Structures.ts b/src/prototypes/Structures.ts index 522aff36f..ab12641ba 100644 --- a/src/prototypes/Structures.ts +++ b/src/prototypes/Structures.ts @@ -45,172 +45,98 @@ OwnedStructure.prototype._isActive = OwnedStructure.prototype.isActive; // return this._isActiveValue; // }; -// Container prototypes ================================================================================================ +// Storage prototypes ================================================================================================ + +const StorageLikeStructures = [ + StructureContainer, + StructureExtension, + StructureLink, + StructureStorage, + StructureTerminal, + StructureSpawn, + Tombstone, + Ruin, +]; + +for (const structure of StorageLikeStructures) { + if (!structure.prototype.hasOwnProperty('energy')) { + Object.defineProperty(structure.prototype, 'energy', { + get(this: typeof structure.prototype) { + return this.store.getUsedCapacity(RESOURCE_ENERGY); + }, + configurable: true, + }); + } -Object.defineProperty(StructureContainer.prototype, 'energy', { - get() { - return this.store[RESOURCE_ENERGY]; - }, - configurable: true, -}); + Object.defineProperty(structure.prototype, 'isFull', { // if this container-like object is full + get(this: typeof structure.prototype) { + return this.store.getFreeCapacity() === 0; + }, + configurable: true, + }); + + Object.defineProperty(structure.prototype, 'isEmpty', { // if this container-like object is empty + get(this: StructureContainer) { + return this.store.getUsedCapacity() === 0; + }, + configurable: true, + }); +} -Object.defineProperty(StructureContainer.prototype, 'isFull', { // if this container-like object is full - get() { - return _.sum(this.store) >= this.storeCapacity; - }, - configurable: true, -}); -Object.defineProperty(StructureContainer.prototype, 'isEmpty', { // if this container-like object is empty - get() { - return _.sum(this.store) == 0; - }, - configurable: true, -}); +// Link prototypes ===================================================================================================== // Controller prototypes =============================================================================================== Object.defineProperty(StructureController.prototype, 'reservedByMe', { - get : function() { + get(this: StructureController) { return this.reservation && this.reservation.username == MY_USERNAME; }, configurable: true, }); Object.defineProperty(StructureController.prototype, 'signedByMe', { - get : function() { + get(this: StructureController) { return this.sign && this.sign.username == MY_USERNAME && Game.time - this.sign.time < 250000; }, configurable: true, }); Object.defineProperty(StructureController.prototype, 'signedByScreeps', { - get : function() { + get(this: StructureController) { return this.sign && this.sign.username == 'Screeps'; }, configurable: true, }); -StructureController.prototype.needsReserving = function(reserveBuffer: number): boolean { +StructureController.prototype.needsReserving = function(this: StructureController, reserveBuffer: number): boolean { return !this.reservation || (this.reservedByMe && this.reservation.ticksToEnd < reserveBuffer); }; // Extension prototypes ================================================================================================ -Object.defineProperty(StructureExtension.prototype, 'isFull', { // if this container-like object is full - get() { - return this.energy >= this.energyCapacity; - }, - configurable: true, -}); - -Object.defineProperty(StructureExtension.prototype, 'isEmpty', { // if this container-like object is empty - get() { - return this.energy == 0; - }, - configurable: true, -}); - // Link prototypes ===================================================================================================== -Object.defineProperty(StructureLink.prototype, 'isFull', { // if this container-like object is full - get() { - return this.energy >= this.energyCapacity; - }, - configurable: true, -}); - -Object.defineProperty(StructureLink.prototype, 'isEmpty', { // if this container-like object is empty - get() { - return this.energy == 0; - }, - configurable: true, -}); - -Object.defineProperty(StructureLink.prototype, 'storeCapacity', { // forwards-backwards compatibility - get() { - return this.energyCapacity; - }, - configurable: true, -}); - - // Nuker prototypes ==================================================================================================== // PowerSpawn prototypes =============================================================================================== // Spawn prototypes ==================================================================================================== -Object.defineProperty(StructureSpawn.prototype, 'isFull', { // if this container-like object is full - get() { - return this.energy >= this.energyCapacity; - }, - configurable: true, -}); - -Object.defineProperty(StructureSpawn.prototype, 'isEmpty', { // if this container-like object is empty - get() { - return this.energy == 0; - }, - configurable: true, -}); - // Storage prototypes ================================================================================================== + declare const Store: any; // Store prototype isn't included in typed-screeps yet Object.defineProperty(Store.prototype, 'contents', { - get() { - return Object.entries(this); + get(this: StoreDefinition) { + return Object.entries(this); }, configurable: true, }); // Storage prototypes ================================================================================================== -Object.defineProperty(StructureStorage.prototype, 'energy', { - get() { - return this.store[RESOURCE_ENERGY]; - }, - configurable: true, -}); - -Object.defineProperty(StructureStorage.prototype, 'isFull', { // if this container-like object is full - get() { - return _.sum(this.store) >= this.storeCapacity; - }, - configurable: true, -}); - -Object.defineProperty(StructureStorage.prototype, 'isEmpty', { // if this container-like object is empty - get() { - return _.sum(this.store) == 0; - }, - configurable: true, -}); - - // Terminal prototypes ================================================================================================= -Object.defineProperty(StructureTerminal.prototype, 'energy', { - get() { - return this.store[RESOURCE_ENERGY]; - }, - configurable: true, -}); - -Object.defineProperty(StructureTerminal.prototype, 'isFull', { // if this container-like object is full - get() { - return _.sum(this.store) >= this.storeCapacity; - }, - configurable: true, -}); - -Object.defineProperty(StructureTerminal.prototype, 'isEmpty', { // if this container-like object is empty - get() { - return _.sum(this.store) == 0; - }, - configurable: true, -}); - Object.defineProperty(StructureTerminal.prototype, 'isReady', { // the terminal is ready to send or deal get() { return this.cooldown == 0 && !this._notReady; @@ -237,28 +163,3 @@ StructureTerminal.prototype.send = function(resourceType: ResourceConstant, amou } return response; }; - -// Tower prototypes - -Object.defineProperty(StructureTower.prototype, 'isFull', { // if this container-like object is full - get() { - return this.energy >= this.energyCapacity; - }, - configurable: true, -}); - -Object.defineProperty(StructureTower.prototype, 'isEmpty', { // if this container-like object is empty - get() { - return this.energy == 0; - }, - configurable: true, -}); - -// Tombstone prototypes ================================================================================================ -Object.defineProperty(Tombstone.prototype, 'energy', { - get() { - return this.store[RESOURCE_ENERGY]; - }, - configurable: true, -}); - diff --git a/src/reinforcementLearning/actionParser.ts b/src/reinforcementLearning/actionParser.ts index d6cbc407d..9767f5f5a 100644 --- a/src/reinforcementLearning/actionParser.ts +++ b/src/reinforcementLearning/actionParser.ts @@ -41,14 +41,14 @@ export class ActionParser { const command: string = action[0]; const predicate: any = action[1]; - const targ: RoomObject | null = typeof predicate == 'string' ? Game.getObjectById(predicate) : null; + const targ: _HasId | null = typeof predicate == 'string' ? Game.getObjectById(predicate) : null; switch (command) { case 'move': actor.move(predicate); break; case 'goTo': - if (targ) actor.goTo(targ); + if (targ) actor.goTo(targ as any as RoomObject); break; case 'attack': if (targ) actor.attack(targ); diff --git a/src/resources/Abathur.ts b/src/resources/Abathur.ts index 1e058f32d..eeb074485 100644 --- a/src/resources/Abathur.ts +++ b/src/resources/Abathur.ts @@ -72,7 +72,7 @@ export const REACTION_PRIORITIES = [ BOOST_TIERS.upgrade.T3, ]; -export const priorityStockAmounts: { [key: string]: number } = { +export const priorityStockAmounts = { XGHO2: 1000, // (-70 % dmg taken) XLHO2: 1000, // (+300 % heal) XZHO2: 1000, // (+300 % fat decr - speed) @@ -94,7 +94,7 @@ export const priorityStockAmounts: { [key: string]: number } = { G : 2000, // For nukes and common compounds }; -export const wantedStockAmounts: { [key: string]: number } = { +export const wantedStockAmounts = { UH : 3000, // (+100 % attack) KO : 3000, // (+100 % ranged attack) XGHO2: 10000, // (-70 % dmg taken) @@ -351,7 +351,7 @@ export class Abathur { // Compute the reaction queue for the highest priority item that you should be and can be making const stocksToCheck = [priorityStockAmounts, wantedStockAmounts]; for (const stocks of stocksToCheck) { - for (const resourceType in stocks) { + for (const resourceType of Object.keys(stocks)) { const amountOwned = colony.assets[resourceType]; const amountNeeded = stocks[resourceType]; if (amountOwned < amountNeeded) { // if there is a shortage of this resource @@ -433,8 +433,8 @@ export class Abathur { const requiredBasicMinerals = Abathur.getRequiredBasicMinerals(reactionQueue); if (verbose) console.log(`Required basic minerals: ${JSON.stringify(requiredBasicMinerals)}`); if (verbose) console.log(`assets: ${JSON.stringify(colony.assets)}`); - const missingBasicMinerals: { [resourceType: string]: number } = {}; - for (const mineralType in requiredBasicMinerals) { + const missingBasicMinerals = {}; + for (const mineralType of Object.keys(requiredBasicMinerals)) { const amountMissing = requiredBasicMinerals[mineralType] - colony.assets[mineralType]; if (amountMissing > 0) { missingBasicMinerals[mineralType] = amountMissing; diff --git a/src/resources/map_resources.ts b/src/resources/map_resources.ts index c8d82a453..91e33589e 100644 --- a/src/resources/map_resources.ts +++ b/src/resources/map_resources.ts @@ -354,4 +354,4 @@ export const DEPOSITS_ALL: ResourceConstant[] = [ RESOURCE_MIST, ]; -export const ALL_ZERO_ASSETS: { [resource: string]: number } = _.zipObject(RESOURCES_ALL, _.map(RESOURCES_ALL, i => 0)); +export const ALL_ZERO_ASSETS: StoreContents = _.zipObject(RESOURCES_ALL, _.map(RESOURCES_ALL, i => 0)); diff --git a/src/roomPlanner/RoomPlanner.ts b/src/roomPlanner/RoomPlanner.ts index f5f0e649a..e440827fe 100644 --- a/src/roomPlanner/RoomPlanner.ts +++ b/src/roomPlanner/RoomPlanner.ts @@ -593,7 +593,7 @@ export class RoomPlanner { // Recall the appropriate map this.recallMap(); - if (!this.map || this.map == {}) { // in case a map hasn't been generated yet + if (!this.map || _.isEmpty(this.map)) { // in case a map hasn't been generated yet log.info(this.colony.name + ' does not have a room plan yet! Unable to demolish errant structures.'); } @@ -653,7 +653,7 @@ export class RoomPlanner { log.info(`${this.colony.name}: waiting until storage is built to remove terminal`); return; } else if (this.colony.terminal && - _.sum(this.colony.terminal.store) - this.colony.terminal.energy > 1000) { + this.colony.terminal.store.getUsedCapacity() - this.colony.terminal.energy > 1000) { log.info(`${this.colony.name}: waiting on resources to evacuate before removing terminal`); return; } else if (this.colony.storage && @@ -718,7 +718,7 @@ export class RoomPlanner { let count = RoomPlanner.settings.maxSitesPerColony - this.colony.constructionSites.length; // Recall the appropriate map this.recallMap(); - if (!this.map || this.map == {}) { // in case a map hasn't been generated yet + if (!this.map || _.isEmpty(this.map)) { // in case a map hasn't been generated yet log.info(this.colony.name + ' does not have a room plan yet! Unable to build missing structures.'); } // Build missing structures from room plan diff --git a/src/stats/stats.ts b/src/stats/stats.ts index 7695896a7..0c9e7a834 100644 --- a/src/stats/stats.ts +++ b/src/stats/stats.ts @@ -17,6 +17,7 @@ export class Stats { ]; for (const key in Memory.stats) { if (!protectedKeys.includes(key)) { + // @ts-expect-error global shenaningans delete Memory.stats[key]; } } @@ -49,7 +50,8 @@ export class Stats { static run() { if (Game.time % LOG_STATS_INTERVAL == 0) { // Record IVM heap statistics - Memory.stats['cpu.heapStatistics'] = (Game.cpu).getHeapStatistics(); + if (Game.cpu.getHeapStatistics) + Memory.stats['cpu.heapStatistics'] = Game.cpu.getHeapStatistics(); // Log GCL this.log('gcl.progress', Game.gcl.progress); this.log('gcl.progressTotal', Game.gcl.progressTotal); diff --git a/src/strategy/ExpansionEvaluator.ts b/src/strategy/ExpansionEvaluator.ts index d584639d8..2830f9ce4 100644 --- a/src/strategy/ExpansionEvaluator.ts +++ b/src/strategy/ExpansionEvaluator.ts @@ -14,6 +14,8 @@ import { ROOMTYPE_SOURCEKEEPER } from '../utilities/Cartographer'; import set = Reflect.set; +import { OvermindConsole } from 'console/Console'; +import { MiningOverlord } from 'overlords/mining/miner'; export const EXPANSION_EVALUATION_FREQ = 500; export const MIN_EXPANSION_DISTANCE = 2; @@ -23,6 +25,20 @@ export interface ColonyExpansionData { expiration: number; } +export interface ExpansionEvaluatorRoomEfficiency { + room: string; + type: string; + dropoffLocation: RoomPosition; + sources: number; + unreachableSources: number; + unreachableController: boolean; + energyPerSource: number; + creepEnergyCost: number; + spawnTimeCost: number; + cpuCost: number; + netIncome: number; + avgEnergyPerCPU: number; +}; @profile export class ExpansionEvaluator { @@ -68,89 +84,101 @@ export class ExpansionEvaluator { * @param room * @param verbose */ - static computeTheoreticalMiningEfficiency(dropoffLocation: RoomPosition, room: string, verbose = false) - : boolean|number { - const roomName = room; - const roomType = Cartographer.roomType(roomName); - let cpuCost = 0; - let creepEnergyCost = 0; - let spawnTimeCost = 0; + static computeTheoreticalMiningEfficiency(dropoffLocation: RoomPosition, room: string) { + const type = Cartographer.roomType(room); const upkeepEnergyCost = 0; // todo later can factor in road damage from all creeps moving - const sourcePositions = RoomIntel.getSourceInfo(roomName); + const data : ExpansionEvaluatorRoomEfficiency = { + room: room, + type: type, + dropoffLocation: dropoffLocation, + sources: 0, + unreachableSources: 0, + unreachableController: false, + energyPerSource: 0, + creepEnergyCost: 0, + spawnTimeCost: 0, + cpuCost: 0, + netIncome: 0, + avgEnergyPerCPU: 0, + }; + + const sourcePositions = RoomIntel.getSourceInfo(room); if (sourcePositions == undefined) { - if (verbose) log.info(`No memory of outpost room: ${roomName}. Aborting score calculation!`); - return false; + log.info(`No memory of outpost room: ${room}. Aborting score calculation!`); + return data; } + // Compute Path length // TODO have it track how many swamp/plain/tunnel const sourcePathLengths: {[sourcePos: string]: number} = {}; for (const source of sourcePositions) { - if (!source.containerPos) { - log.info(`Can't find container position for source ${source} during efficiency calc`); - return false; - } + const containerPos = MiningOverlord.calculateContainerPos(source.pos, dropoffLocation); + // TODO Need to factor in where roads would go - const path = Pathing.findShortestPath(dropoffLocation, source.containerPos, - {ignoreStructures: true, allowHostile: true}); + const path = Pathing.findShortestPath(dropoffLocation, containerPos, + {ignoreStructures: true, allowHostile: true, blockCreeps: false, avoidSK: false}); if (path.incomplete) { - log.error(`Couldn't find path to source ${source} for mining efficiency calc`); - return false; + log.error(`Couldn't find path to source ${source.pos.print} for mining efficiency calc`); + data.unreachableSources++; + continue; } sourcePathLengths[source.pos.print] = path.path.length; } // Compute Energy Supply - const energyPerSource = roomType == ROOMTYPE_CONTROLLER ? SOURCE_ENERGY_CAPACITY : SOURCE_ENERGY_KEEPER_CAPACITY; + data.energyPerSource = type == ROOMTYPE_CONTROLLER ? SOURCE_ENERGY_CAPACITY : SOURCE_ENERGY_KEEPER_CAPACITY; // Compute miner upkeep for (const source of sourcePositions) { - const setup = roomType == ROOMTYPE_CONTROLLER ? Setups.drones.miners.standard.generateMaxedBody() + const setup = type == ROOMTYPE_CONTROLLER ? Setups.drones.miners.standard.generateMaxedBody() : Setups.drones.miners.sourceKeeper.generateMaxedBody(); const effectiveCreepUptime = (CREEP_LIFE_TIME - sourcePathLengths[source.pos.print]); - creepEnergyCost += bodyCost(setup)/effectiveCreepUptime; - spawnTimeCost += setup.length*CREEP_SPAWN_TIME/effectiveCreepUptime; + data.creepEnergyCost += bodyCost(setup)/effectiveCreepUptime; + data.spawnTimeCost += setup.length*CREEP_SPAWN_TIME/effectiveCreepUptime; // Always harvesting, sometimes replacement is moving - cpuCost += 0.2 + 0.2*(1-effectiveCreepUptime/CREEP_LIFE_TIME); + data.cpuCost += 0.2 + 0.2*(1-effectiveCreepUptime/CREEP_LIFE_TIME); } // Compute reserver/skReaper upkeep - if (roomType == ROOMTYPE_CONTROLLER) { - const controller = RoomIntel.getControllerInfo(roomName); + if (type == ROOMTYPE_CONTROLLER) { + const controller = RoomIntel.getControllerInfo(room); if (!controller) { - log.error(`Expansion Efficiency Calc: Can't find controller for room ${roomName}`); - return false; + log.error(`Expansion Efficiency Calc: Can't find controller for room ${room}`); + data.unreachableController = true; } else { const setup = Setups.infestors.reserve.generateMaxedBody(); const controllerPath = Pathing.findShortestPath(dropoffLocation, controller.pos, {ignoreStructures: true, allowHostile: true}); if (controllerPath.incomplete) { log.error(`Couldn't find path to controller ${controller} for mining efficiency calc`); - return false; + data.unreachableController = true; + } else { + const claimPower = _.filter(setup, (part: BodyPartConstant) => part == CLAIM).length; + const effectiveLifetimeReservationGeneration = (CREEP_CLAIM_LIFE_TIME - controllerPath.path.length) * claimPower; + data.creepEnergyCost += bodyCost(setup)/effectiveLifetimeReservationGeneration; + data.spawnTimeCost += setup.length*CREEP_SPAWN_TIME/effectiveLifetimeReservationGeneration; + data.cpuCost += 0.2 * CREEP_CLAIM_LIFE_TIME / effectiveLifetimeReservationGeneration; } - const claimPower = _.filter(setup, (part: BodyPartConstant) => part == CLAIM).length; - const effectiveLifetimeReservationGeneration = (CREEP_CLAIM_LIFE_TIME - controllerPath.path.length) * claimPower; - creepEnergyCost += bodyCost(setup)/effectiveLifetimeReservationGeneration; - spawnTimeCost += setup.length*CREEP_SPAWN_TIME/effectiveLifetimeReservationGeneration; - cpuCost += 0.2 * CREEP_CLAIM_LIFE_TIME / effectiveLifetimeReservationGeneration; } - } else if (roomType == ROOMTYPE_SOURCEKEEPER) { + } else if (type == ROOMTYPE_SOURCEKEEPER) { // Handle SK const setup = CombatSetups.zerglings.sourceKeeper.generateMaxedBody(); - const skPath = Pathing.findPathToRoom(dropoffLocation, roomName, + const skPath = Pathing.findPathToRoom(dropoffLocation, room, {ignoreStructures: true, allowHostile: true}); if (skPath.incomplete) { - log.error(`Couldn't find path to sk room ${roomName} for mining efficiency calc`); - return false; + log.error(`Couldn't find path to sk room ${room} for mining efficiency calc`); + data.unreachableController = true; + } else { + const effectiveCreepUptime = (CREEP_LIFE_TIME - skPath.path.length); + data.creepEnergyCost += bodyCost(setup)/effectiveCreepUptime; + data.spawnTimeCost += setup.length*CREEP_SPAWN_TIME/effectiveCreepUptime; + // Increased cost, always moving, frequent attack/move+heal intents, and during overlap 2nd creep moving to room + data.cpuCost += 0.2 + 0.15 + 0.2*(1-effectiveCreepUptime/CREEP_LIFE_TIME); + // TODO examine for accuracy Increased cost, frequent attack/move+heal intents } - const effectiveCreepUptime = (CREEP_LIFE_TIME - skPath.path.length); - creepEnergyCost += bodyCost(setup)/effectiveCreepUptime; - spawnTimeCost += setup.length*CREEP_SPAWN_TIME/effectiveCreepUptime; - // Increased cost, always moving, frequent attack/move+heal intents, and during overlap 2nd creep moving to room - cpuCost += 0.2 + 0.15 + 0.2*(1-effectiveCreepUptime/CREEP_LIFE_TIME); - // TODO examine for accuracy Increased cost, frequent attack/move+heal intents } // Compute transporter upkeep @@ -160,20 +188,18 @@ export class ExpansionEvaluator { const transporterCarryParts = _.filter(setup, (part: BodyPartConstant) => part == CARRY).length; const effectiveEnergyTransportedPerTick = transporterCarryParts * CARRY_CAPACITY / (2 * sourcePathLengths[source.pos.print]); // round trip - const transportersPerSource = energyPerSource/ENERGY_REGEN_TIME/effectiveEnergyTransportedPerTick; + const transportersPerSource = data.energyPerSource/ENERGY_REGEN_TIME/effectiveEnergyTransportedPerTick; - creepEnergyCost += bodyCost(setup)*transportersPerSource/CREEP_LIFE_TIME; - spawnTimeCost += setup.length*CREEP_SPAWN_TIME*transportersPerSource/CREEP_LIFE_TIME; - cpuCost += 0.2 * transportersPerSource; + data.creepEnergyCost += bodyCost(setup)*transportersPerSource/CREEP_LIFE_TIME; + data.spawnTimeCost += setup.length*CREEP_SPAWN_TIME*transportersPerSource/CREEP_LIFE_TIME; + data.cpuCost += 0.2 * transportersPerSource; } - const netIncome = (energyPerSource*sourcePositions.length/ENERGY_REGEN_TIME)-creepEnergyCost; + data.sources = sourcePositions.length; + data.netIncome = (data.energyPerSource * sourcePositions.length / ENERGY_REGEN_TIME) - data.creepEnergyCost; + data.avgEnergyPerCPU = (data.netIncome / data.cpuCost || 0); - let msg = `(Potential) Outpost ${room} type ${roomType} evaluated for colony at ${dropoffLocation.roomName} with per tick results \n`; - msg += `Income: ${energyPerSource*sourcePositions.length/ENERGY_REGEN_TIME} Net Income: ${netIncome} Net Energy per CPU: ${netIncome/cpuCost}\n`; - msg += `Creep Costs: Energy ${creepEnergyCost}, Spawn Time ${spawnTimeCost}, and CPU ${cpuCost} \n`; - log.alert(msg); - return netIncome/cpuCost; + return data; } // Compute the total score for a room diff --git a/src/strategy/ExpansionPlanner.ts b/src/strategy/ExpansionPlanner.ts index 2b0ea4cc4..afa5daeec 100644 --- a/src/strategy/ExpansionPlanner.ts +++ b/src/strategy/ExpansionPlanner.ts @@ -122,8 +122,9 @@ export class ExpansionPlanner implements IExpansionPlanner { score += CATALYST_BONUS; } } + // Update best choices - if (score > bestScore && Game.map.isRoomAvailable(roomName)) { + if (score > bestScore && Game.map.getRoomStatus(roomName).status === Game.map.getRoomStatus(colony.room.name).status) { bestScore = score; bestRoom = roomName; } diff --git a/src/tasks/Task.ts b/src/tasks/Task.ts index 17bf699f9..57aa77c87 100644 --- a/src/tasks/Task.ts +++ b/src/tasks/Task.ts @@ -17,7 +17,24 @@ import {profile} from '../profiler/decorator'; import {Zerg} from '../zerg/Zerg'; import {initializeTask} from './initializer'; -type targetType = { ref: string, pos: ProtoPos }; // overwrite this variable to specify more precise typing +interface AbstractTaskTarget { + ref: string; // Target id or name + _pos: ProtoPos; // Target position's coordinates in case vision is lost +} + +type ConcreteTaskTarget = HasRef | _HasRoomPosition | RoomPosition; + +function isAbstractTarget(target: any): target is AbstractTaskTarget { + return target && (target)._pos !== undefined; +} + +function isRoomRefTarget(target: any): target is HasRef & _HasRoomPosition { + return target && (target).ref !== undefined; +} + +function isRoomObjectTarget(target: any): target is RoomObject { + return target && (target).pos !== undefined; +} /** * An abstract class for encapsulating creep actions. This generalizes the concept of "do action X to thing Y until @@ -26,18 +43,15 @@ type targetType = { ref: string, pos: ProtoPos }; // overwrite this variable to * to continue. */ @profile -export abstract class Task { +export abstract class Task { static taskName: string; name: string; // Name of the task type, e.g. 'upgrade' - _creep: { // Data for the creep the task is assigned to" + _creep: { // Data for the creep the task is assigned to name: string; // Name of the creep }; - _target: { // Data for the target the task is directed to: - ref: string; // Target id or name - _pos: ProtoPos; // Target position's coordinates in case vision is lost - }; + _target: AbstractTaskTarget;// Data for the target the task is directed to: _parent: ProtoTask | null; // The parent of this task, if any. Task is changed to parent upon completion. tick: number; // When the task was set settings: TaskSettings; // Settings for a given type of task; shouldn't be modified on an instance-basis @@ -46,17 +60,33 @@ export abstract class Task { private _targetPos: RoomPosition; - constructor(taskName: string, target: targetType, options = {} as TaskOptions) { + constructor(taskName: string, target: TargetType | AbstractTaskTarget, options = {} as TaskOptions) { // Parameters for the task this.name = taskName; this._creep = { name: '', }; - if (target) { // Handles edge cases like when you're done building something and target disappears + // Handles edge cases like when you're done building something and target disappears + if (target instanceof RoomPosition) { + this._target = { + ref: '', + _pos: { x: target.x, y: target.y, roomName: target.room!.name }, + } + } else if (isAbstractTarget(target)) { + this._target = { + ref : target.ref, + _pos: target._pos, + }; + } else if (isRoomRefTarget(target)) { this._target = { ref : target.ref, _pos: target.pos, }; + } else if (isRoomObjectTarget(target)) { + this._target = { + ref: '', + _pos: target.pos, + } } else { this._target = { ref : '', @@ -67,6 +97,7 @@ export abstract class Task { } }; } + // log.debug(`creating task ${this.constructor.name}, ${taskName}, target: ${target}, ${print(this._target)}`); this._parent = null; this.settings = { targetRange: 1, // range at which you can perform action @@ -129,8 +160,8 @@ export abstract class Task { /** * Dereferences the Task's target */ - get target(): RoomObject | null { - return deref(this._target.ref); + get target(): TargetType { + return deref(this._target.ref) as TargetType; } /** @@ -140,7 +171,7 @@ export abstract class Task { // refresh if you have visibility of the target if (!this._targetPos) { if (this.target) { - this._target._pos = this.target.pos; + this._target._pos = (this.target as _HasRoomPosition).pos; } this._targetPos = derefRoomPosition(this._target._pos); } @@ -150,14 +181,14 @@ export abstract class Task { /** * Get the Task's parent */ - get parent(): Task | null { + get parent(): Task | null { return (this._parent ? initializeTask(this._parent) : null); } /** * Set the Task's parent */ - set parent(parentTask: Task | null) { + set parent(parentTask: Task | null) { this._parent = parentTask ? parentTask.proto : null; // If the task is already assigned to a creep, update their memory if (this.creep) { @@ -168,8 +199,8 @@ export abstract class Task { /** * Return a list of [this, this.parent, this.parent.parent, ...] as tasks */ - get manifest(): Task[] { - const manifest: Task[] = [this]; + get manifest(): Task[] { + const manifest: Task[] = [this]; let parent = this.parent; while (parent) { manifest.push(parent); @@ -207,7 +238,7 @@ export abstract class Task { /** * Fork the task, assigning a new task to the creep with this task as its parent */ - fork(newTask: Task): Task { + fork(newTask: Task): Task { newTask.parent = this; if (this.creep) { this.creep.task = newTask; diff --git a/src/tasks/Tasks.ts b/src/tasks/Tasks.ts index d9469d594..3fd11620e 100644 --- a/src/tasks/Tasks.ts +++ b/src/tasks/Tasks.ts @@ -32,7 +32,7 @@ import {Task} from './Task'; @profile export class Tasks { - static chain(tasks: Task[], setNextPos = true): Task | null { + static chain(tasks: Task[], setNextPos = true): Task | null { if (tasks.length == 0) { // log.error(`Tasks.chain was passed an empty array of tasks!`); return null; @@ -118,7 +118,7 @@ export class Tasks { } static recharge(minEnergy = 0, options = {} as TaskOptions): TaskRecharge { - return new TaskRecharge(null, minEnergy, options); + return new TaskRecharge(minEnergy, options); } static repair(target: repairTargetType, options = {} as TaskOptions): TaskRepair { diff --git a/src/tasks/initializer.ts b/src/tasks/initializer.ts index 27df18904..fa87a44b1 100644 --- a/src/tasks/initializer.ts +++ b/src/tasks/initializer.ts @@ -33,7 +33,7 @@ import {Task} from './Task'; /** * The task initializer maps serialized prototasks to Task instances */ -export function initializeTask(protoTask: ProtoTask): Task { +export function initializeTask(protoTask: ProtoTask): Task { // Retrieve name and target data from the ProtoTask const taskName = protoTask.name; const target = deref(protoTask._target.ref); @@ -91,7 +91,7 @@ export function initializeTask(protoTask: ProtoTask): Task { task = new TaskRangedAttack(target as rangedAttackTargetType); break; case rechargeTaskName: - task = new TaskRecharge(null); + task = new TaskRecharge(); break; case repairTaskName: task = new TaskRepair(target as repairTargetType); diff --git a/src/tasks/instances/attack.ts b/src/tasks/instances/attack.ts index c5f11ea0f..57076cda7 100644 --- a/src/tasks/instances/attack.ts +++ b/src/tasks/instances/attack.ts @@ -13,11 +13,9 @@ export type attackTargetType = Creep | Structure; export const attackTaskName = 'attack'; @profile -export class TaskAttack extends Task { - target: attackTargetType; - +export class TaskAttack extends Task { constructor(target: attackTargetType, options = {} as TaskOptions) { - super(attackTaskName, target, options); + super(attackTaskName, { ref: target.ref, _pos: target.pos }, options); // Settings this.settings.targetRange = 3; } diff --git a/src/tasks/instances/build.ts b/src/tasks/instances/build.ts index 3155e6250..7abcc9f18 100644 --- a/src/tasks/instances/build.ts +++ b/src/tasks/instances/build.ts @@ -5,9 +5,7 @@ export type buildTargetType = ConstructionSite; export const buildTaskName = 'build'; @profile -export class TaskBuild extends Task { - target: buildTargetType; - +export class TaskBuild extends Task { constructor(target: buildTargetType, options = {} as TaskOptions) { super(buildTaskName, target, options); // Settings @@ -16,7 +14,7 @@ export class TaskBuild extends Task { } isValidTask() { - return this.creep.carry.energy > 0; + return this.creep.store.energy > 0; } isValidTarget() { diff --git a/src/tasks/instances/claim.ts b/src/tasks/instances/claim.ts index d131a82f1..0555fc367 100644 --- a/src/tasks/instances/claim.ts +++ b/src/tasks/instances/claim.ts @@ -5,9 +5,7 @@ export type claimTargetType = StructureController; export const claimTaskName = 'claim'; @profile -export class TaskClaim extends Task { - target: claimTargetType; - +export class TaskClaim extends Task { constructor(target: claimTargetType, options = {} as TaskOptions) { super(claimTaskName, target, options); // Settings diff --git a/src/tasks/instances/dismantle.ts b/src/tasks/instances/dismantle.ts index a78b5389d..48fd9ab14 100644 --- a/src/tasks/instances/dismantle.ts +++ b/src/tasks/instances/dismantle.ts @@ -6,8 +6,7 @@ export type dismantleTargetType = Structure; export const dismantleTaskName = 'dismantle'; @profile -export class TaskDismantle extends Task { - target: dismantleTargetType; +export class TaskDismantle extends Task { constructor(target: dismantleTargetType, options = {} as TaskOptions) { super(dismantleTaskName, target, options); diff --git a/src/tasks/instances/drop.ts b/src/tasks/instances/drop.ts index 1079e2d76..07e05508c 100644 --- a/src/tasks/instances/drop.ts +++ b/src/tasks/instances/drop.ts @@ -1,14 +1,13 @@ import {profile} from '../../profiler/decorator'; import {Task} from '../Task'; -export type dropTargetType = { pos: RoomPosition } | RoomPosition; +export type dropTargetType = HasRef & _HasRoomPosition | RoomPosition; export const dropTaskName = 'drop'; @profile -export class TaskDrop extends Task { +export class TaskDrop extends Task { static taskName = 'drop'; - target: null; data: { resourceType: ResourceConstant amount: number | undefined @@ -19,9 +18,9 @@ export class TaskDrop extends Task { amount?: number, options = {} as TaskOptions) { if (target instanceof RoomPosition) { - super(TaskDrop.taskName, {ref: '', pos: target}, options); + super(TaskDrop.taskName, {ref: '', _pos: target}, options); } else { - super(TaskDrop.taskName, {ref: '', pos: target.pos}, options); + super(TaskDrop.taskName, {ref: target.ref, _pos: target.pos}, options); } // Settings this.settings.targetRange = 0; @@ -33,7 +32,7 @@ export class TaskDrop extends Task { isValidTask() { const amount = this.data.amount || 1; - const resourcesInCarry = this.creep.carry[this.data.resourceType] || 0; + const resourcesInCarry = this.creep.store[this.data.resourceType] || 0; return resourcesInCarry >= amount; } diff --git a/src/tasks/instances/fortify.ts b/src/tasks/instances/fortify.ts index 3d57919a8..ed6e81130 100644 --- a/src/tasks/instances/fortify.ts +++ b/src/tasks/instances/fortify.ts @@ -6,8 +6,7 @@ export type fortifyTargetType = StructureWall | StructureRampart; export const fortifyTaskName = 'fortify'; @profile -export class TaskFortify extends Task { - target: fortifyTargetType; +export class TaskFortify extends Task { data: { hitsMax: number | undefined; }; @@ -22,7 +21,7 @@ export class TaskFortify extends Task { } isValidTask() { - return (this.creep.carry.energy > 0); // Times out once creep is out of energy + return (this.creep.store.energy > 0); // Times out once creep is out of energy } isValidTarget() { diff --git a/src/tasks/instances/generateSafeMode.ts b/src/tasks/instances/generateSafeMode.ts index 29ea7fb16..fb53cd802 100644 --- a/src/tasks/instances/generateSafeMode.ts +++ b/src/tasks/instances/generateSafeMode.ts @@ -5,15 +5,13 @@ export type generateSafeModeTargetType = StructureController; export const generateSafeModeTaskName = 'generateSafeMode'; @profile -export class TaskGenerateSafeMode extends Task { - target: generateSafeModeTargetType; - +export class TaskGenerateSafeMode extends Task { constructor(target: generateSafeModeTargetType, options = {} as TaskOptions) { super(generateSafeModeTaskName, target, options); } isValidTask() { - return (this.creep.carry[RESOURCE_GHODIUM] >= 1000); + return (this.creep.store[RESOURCE_GHODIUM] >= 1000); } isValidTarget() { diff --git a/src/tasks/instances/getBoosted.ts b/src/tasks/instances/getBoosted.ts index 7fb6aa23f..a7f4e1d5f 100644 --- a/src/tasks/instances/getBoosted.ts +++ b/src/tasks/instances/getBoosted.ts @@ -9,10 +9,7 @@ export const getBoostedTaskName = 'getBoosted'; export const MIN_LIFETIME_FOR_BOOST = 0.85; @profile -export class TaskGetBoosted extends Task { - - target: getBoostedTargetType; - +export class TaskGetBoosted extends Task { data: { resourceType: ResourceConstant; amount: number | undefined; diff --git a/src/tasks/instances/getRenewed.ts b/src/tasks/instances/getRenewed.ts index 2479f8e8f..d773dde4b 100644 --- a/src/tasks/instances/getRenewed.ts +++ b/src/tasks/instances/getRenewed.ts @@ -5,9 +5,7 @@ export type getRenewedTargetType = StructureSpawn; export const getRenewedTaskName = 'getRenewed'; @profile -export class TaskGetRenewed extends Task { - target: getRenewedTargetType; - +export class TaskGetRenewed extends Task { constructor(target: getRenewedTargetType, options = {} as TaskOptions) { super(getRenewedTaskName, target, options); } diff --git a/src/tasks/instances/goTo.ts b/src/tasks/instances/goTo.ts index f6f9f02ff..9faeb5c73 100644 --- a/src/tasks/instances/goTo.ts +++ b/src/tasks/instances/goTo.ts @@ -2,13 +2,11 @@ import {hasPos} from '../../declarations/typeGuards'; import {profile} from '../../profiler/decorator'; import {Task} from '../Task'; -export type goToTargetType = { pos: RoomPosition } | RoomPosition; +export type goToTargetType = _HasRoomPosition | RoomPosition; export const goToTaskName = 'goTo'; @profile -export class TaskGoTo extends Task { - target: null; - +export class TaskGoTo extends Task { constructor(target: goToTargetType, options = {} as TaskOptions) { if (hasPos(target)) { super(goToTaskName, {ref: '', pos: target.pos}, options); diff --git a/src/tasks/instances/goToRoom.ts b/src/tasks/instances/goToRoom.ts index 55ac505d3..d320a249e 100644 --- a/src/tasks/instances/goToRoom.ts +++ b/src/tasks/instances/goToRoom.ts @@ -6,12 +6,10 @@ export type goToRoomTargetType = string; export const goToRoomTaskName = 'goToRoom'; @profile -export class TaskGoToRoom extends Task { - - target: null; +export class TaskGoToRoom extends Task { constructor(roomName: goToRoomTargetType, options = {} as TaskOptions) { - super(goToRoomTaskName, {ref: '', pos: new RoomPosition(25, 25, roomName)}, options); + super(goToRoomTaskName, {ref: '', _pos: new RoomPosition(25, 25, roomName)}, options); // Settings this.settings.targetRange = 23; // Target is almost always controller flag, so range of 2 is acceptable } diff --git a/src/tasks/instances/harvest.ts b/src/tasks/instances/harvest.ts index ca94de5af..cc84ad41e 100644 --- a/src/tasks/instances/harvest.ts +++ b/src/tasks/instances/harvest.ts @@ -6,15 +6,13 @@ export type harvestTargetType = Source | Mineral; export const harvestTaskName = 'harvest'; @profile -export class TaskHarvest extends Task { - target: harvestTargetType; - +export class TaskHarvest extends Task { constructor(target: harvestTargetType, options = {} as TaskOptions) { super(harvestTaskName, target, options); } isValidTask() { - return _.sum(this.creep.carry) < this.creep.carryCapacity; + return this.creep.store.getUsedCapacity() < this.creep.store.getCapacity(); } isValidTarget() { diff --git a/src/tasks/instances/heal.ts b/src/tasks/instances/heal.ts index feadd6659..c373f8bd9 100644 --- a/src/tasks/instances/heal.ts +++ b/src/tasks/instances/heal.ts @@ -5,16 +5,13 @@ export type healTargetType = Creep; export const healTaskName = 'heal'; @profile -export class TaskHeal extends Task { - target: healTargetType; - +export class TaskHeal extends Task { constructor(target: healTargetType, options = {} as TaskOptions) { super(healTaskName, target, options); // Settings this.settings.targetRange = 3; } - isValidTask() { return (this.creep.getActiveBodyparts(HEAL) > 0); } diff --git a/src/tasks/instances/invalid.ts b/src/tasks/instances/invalid.ts index 1dd58c901..c930a02d8 100644 --- a/src/tasks/instances/invalid.ts +++ b/src/tasks/instances/invalid.ts @@ -13,12 +13,10 @@ const invalidTarget = { }; @profile -export class TaskInvalid extends Task { - target: any; +export class TaskInvalid extends Task { constructor() { super('INVALID', invalidTarget); - } isValidTask() { diff --git a/src/tasks/instances/meleeAttack.ts b/src/tasks/instances/meleeAttack.ts index 0146d75da..88a58adce 100644 --- a/src/tasks/instances/meleeAttack.ts +++ b/src/tasks/instances/meleeAttack.ts @@ -5,9 +5,7 @@ export type meleeAttackTargetType = Creep | Structure; export const meleeAttackTaskName = 'meleeAttack'; @profile -export class TaskMeleeAttack extends Task { - target: meleeAttackTargetType; - +export class TaskMeleeAttack extends Task { constructor(target: meleeAttackTargetType, options = {} as TaskOptions) { super(meleeAttackTaskName, target, options); // Settings diff --git a/src/tasks/instances/pickup.ts b/src/tasks/instances/pickup.ts index 186922376..34744849a 100644 --- a/src/tasks/instances/pickup.ts +++ b/src/tasks/instances/pickup.ts @@ -5,16 +5,14 @@ export type pickupTargetType = Resource; export const pickupTaskName = 'pickup'; @profile -export class TaskPickup extends Task { - target: pickupTargetType; - +export class TaskPickup extends Task { constructor(target: pickupTargetType, options = {} as TaskOptions) { super('pickup', target, options); this.settings.oneShot = true; } isValidTask() { - return _.sum(this.creep.carry) < this.creep.carryCapacity; + return this.creep.store.getUsedCapacity() < this.creep.store.getCapacity(); } isValidTarget() { diff --git a/src/tasks/instances/rangedAttack.ts b/src/tasks/instances/rangedAttack.ts index 25c4fac39..69b3cc1ec 100644 --- a/src/tasks/instances/rangedAttack.ts +++ b/src/tasks/instances/rangedAttack.ts @@ -5,9 +5,7 @@ export type rangedAttackTargetType = Creep | Structure; export const rangedAttackTaskName = 'rangedAttack'; @profile -export class TaskRangedAttack extends Task { - target: rangedAttackTargetType; - +export class TaskRangedAttack extends Task { constructor(target: rangedAttackTargetType, options = {} as TaskOptions) { super(rangedAttackTaskName, target, options); // Settings diff --git a/src/tasks/instances/recharge.ts b/src/tasks/instances/recharge.ts index 7b2dbaa54..4fecf3846 100644 --- a/src/tasks/instances/recharge.ts +++ b/src/tasks/instances/recharge.ts @@ -14,15 +14,13 @@ export const rechargeTaskName = 'recharge'; // This is a "dispenser task" which is not itself a valid task, but dispenses a task when assigned to a creep. @profile -export class TaskRecharge extends Task { - target: rechargeTargetType; - +export class TaskRecharge extends Task { data: { minEnergy: number; }; - constructor(target: rechargeTargetType, minEnergy = 0, options = {} as TaskOptions) { - super(rechargeTaskName, {ref: '', pos: {x: -1, y: -1, roomName: ''}}, options); + constructor(minEnergy = 0, options = {} as TaskOptions) { + super(rechargeTaskName, null, options); this.data.minEnergy = minEnergy; } @@ -36,12 +34,11 @@ export class TaskRecharge extends Task { return false; } const otherTargeters = _.filter(_.map(obj.targetedBy, name => Overmind.zerg[name]), - zerg => !!zerg && zerg.memory._task - && (zerg.memory._task.name == withdrawTaskName - || zerg.memory._task.name == pickupTaskName)); - const resourceOutflux = _.sum(_.map(otherTargeters, - other => other.carryCapacity - _.sum(other.carry))); - amount = minMax(amount - resourceOutflux, 0, creep.carryCapacity); + zerg => !!zerg && zerg.task + && (zerg.task.name == withdrawTaskName + || zerg.task.name == pickupTaskName)); + const resourceOutflux = _.sum(_.map(otherTargeters, other => other.store.getFreeCapacity())); + amount = minMax(amount - resourceOutflux, 0, creep.store.getCapacity()); const effectiveAmount = amount / (creep.pos.getMultiRoomRangeTo(obj.pos) + 1); if (effectiveAmount <= 0) { return false; diff --git a/src/tasks/instances/repair.ts b/src/tasks/instances/repair.ts index 7a04bb9ac..d928556d8 100644 --- a/src/tasks/instances/repair.ts +++ b/src/tasks/instances/repair.ts @@ -5,9 +5,7 @@ export type repairTargetType = Structure; export const repairTaskName = 'repair'; @profile -export class TaskRepair extends Task { - target: repairTargetType; - +export class TaskRepair extends Task { constructor(target: repairTargetType, options = {} as TaskOptions) { super(repairTaskName, target, options); // Settings @@ -16,7 +14,7 @@ export class TaskRepair extends Task { } isValidTask() { - return this.creep.carry.energy > 0; + return this.creep.store.energy > 0; } isValidTarget() { diff --git a/src/tasks/instances/reserve.ts b/src/tasks/instances/reserve.ts index 176016287..22c58e769 100644 --- a/src/tasks/instances/reserve.ts +++ b/src/tasks/instances/reserve.ts @@ -6,9 +6,7 @@ export type reserveTargetType = StructureController; export const reserveTaskName = 'colony'; @profile -export class TaskReserve extends Task { - target: reserveTargetType; - +export class TaskReserve extends Task { constructor(target: reserveTargetType, options = {} as TaskOptions) { super(reserveTaskName, target, options); } diff --git a/src/tasks/instances/signController.ts b/src/tasks/instances/signController.ts index f0f01af22..0d36a8dc9 100644 --- a/src/tasks/instances/signController.ts +++ b/src/tasks/instances/signController.ts @@ -5,9 +5,7 @@ export type signControllerTargetType = StructureController; export const signControllerTaskName = 'signController'; @profile -export class TaskSignController extends Task { - target: signControllerTargetType; - +export class TaskSignController extends Task { constructor(target: signControllerTargetType, options = {} as TaskOptions) { super(signControllerTaskName, target, options); } diff --git a/src/tasks/instances/transfer.ts b/src/tasks/instances/transfer.ts index 98cc77b87..cb2ddecaf 100644 --- a/src/tasks/instances/transfer.ts +++ b/src/tasks/instances/transfer.ts @@ -9,9 +9,7 @@ export type transferTargetType = export const transferTaskName = 'transfer'; @profile -export class TaskTransfer extends Task { - - target: transferTargetType; +export class TaskTransfer extends Task { data: { resourceType: ResourceConstant amount: number | undefined @@ -28,7 +26,7 @@ export class TaskTransfer extends Task { isValidTask() { const amount = this.data.amount || 1; - const resourcesInCarry = this.creep.carry[this.data.resourceType] || 0; + const resourcesInCarry = this.creep.store[this.data.resourceType] || 0; return resourcesInCarry >= amount; } diff --git a/src/tasks/instances/transferAll.ts b/src/tasks/instances/transferAll.ts index 6d0d495c7..a564fed1b 100644 --- a/src/tasks/instances/transferAll.ts +++ b/src/tasks/instances/transferAll.ts @@ -7,9 +7,7 @@ export type transferAllTargetType = StructureStorage | StructureTerminal | Struc export const transferAllTaskName = 'transferAll'; @profile -export class TaskTransferAll extends Task { - - target: transferAllTargetType; +export class TaskTransferAll extends Task { data: { skipEnergy?: boolean; }; @@ -20,7 +18,7 @@ export class TaskTransferAll extends Task { } isValidTask() { - for (const [resourceType, amount] of this.creep.carry.contents) { + for (const [resourceType, amount] of Object.entries(this.creep.store)) { if (this.data.skipEnergy && resourceType == RESOURCE_ENERGY) { continue; } @@ -32,11 +30,11 @@ export class TaskTransferAll extends Task { } isValidTarget() { - return _.sum(this.target.store) < this.target.storeCapacity; + return this.target.store.getUsedCapacity() < this.target.store.getCapacity(); } work() { - for (const [resourceType, amount] of this.creep.carry.contents) { + for (const [resourceType, amount] of this.creep.store.contents) { if (this.data.skipEnergy && resourceType == RESOURCE_ENERGY) { continue; } diff --git a/src/tasks/instances/upgrade.ts b/src/tasks/instances/upgrade.ts index 166b6cdd6..0ca14a302 100644 --- a/src/tasks/instances/upgrade.ts +++ b/src/tasks/instances/upgrade.ts @@ -6,9 +6,7 @@ export const upgradeTaskName = 'upgrade'; @profile -export class TaskUpgrade extends Task { - target: upgradeTargetType; - +export class TaskUpgrade extends Task { constructor(target: upgradeTargetType, options = {} as TaskOptions) { super(upgradeTaskName, target, options); // Settings @@ -17,7 +15,7 @@ export class TaskUpgrade extends Task { } isValidTask() { - return (this.creep.carry.energy > 0); + return (this.creep.store.energy > 0); } isValidTarget() { diff --git a/src/tasks/instances/withdraw.ts b/src/tasks/instances/withdraw.ts index 26a2e5418..d0e546fae 100644 --- a/src/tasks/instances/withdraw.ts +++ b/src/tasks/instances/withdraw.ts @@ -1,6 +1,4 @@ /* Withdraw a resource from a target */ - -import {isRuin, isTombstone,} from '../../declarations/typeGuards'; import {profile} from '../../profiler/decorator'; import {Task} from '../Task'; @@ -9,9 +7,7 @@ export type withdrawTargetType = AnyStoreStructure export const withdrawTaskName = 'withdraw'; @profile -export class TaskWithdraw extends Task { - - target: withdrawTargetType; +export class TaskWithdraw extends Task { data: { resourceType: ResourceConstant, amount: number | undefined, @@ -28,26 +24,12 @@ export class TaskWithdraw extends Task { isValidTask() { const amount = this.data.amount || 1; - return (_.sum(this.creep.carry) <= this.creep.carryCapacity - amount); + return (this.creep.store.getUsedCapacity() <= this.creep.store.getCapacity() - amount); } isValidTarget() { const amount = this.data.amount || 1; - // @ts-ignore - return this.target.store.getUsedCapacity(this.data.resourceType) >= amount; - // const target = this.target; - // if (isTombstone(target) || isRuin(target) || isStoreStructure(target)) { - // return (target.store[this.data.resourceType] || 0) >= amount; - // } else if (isEnergyStructure(target) && this.data.resourceType == RESOURCE_ENERGY) { - // return target.energy >= amount; - // } else { - // if (target instanceof StructureLab) { - // return this.data.resourceType == target.mineralType && target.mineralAmount >= amount; - // } else if (target instanceof StructurePowerSpawn) { - // return this.data.resourceType == RESOURCE_POWER && target.power >= amount; - // } - // } - // return false; + return (this.target.store.getUsedCapacity(this.data.resourceType) || 0) >= amount; } work() { diff --git a/src/tasks/instances/withdrawAll.ts b/src/tasks/instances/withdrawAll.ts index 36b2acb09..997af58ef 100644 --- a/src/tasks/instances/withdrawAll.ts +++ b/src/tasks/instances/withdrawAll.ts @@ -8,20 +8,17 @@ export type withdrawAllTargetType = AnyStoreStructure; export const withdrawAllTaskName = 'withdrawAll'; @profile -export class TaskWithdrawAll extends Task { - - target: withdrawAllTargetType; - +export class TaskWithdrawAll extends Task { constructor(target: withdrawAllTargetType, options = {} as TaskOptions) { super(withdrawAllTaskName, target, options); } isValidTask() { - return (_.sum(this.creep.carry) < this.creep.carryCapacity); + return (this.creep.store.getUsedCapacity() < this.creep.store.getCapacity()); } isValidTarget() { - return _.sum(this.target.store) > 0; + return (this.target.store.getUsedCapacity() || 0) > 0; } work() { diff --git a/src/utilities/Cartographer.ts b/src/utilities/Cartographer.ts index 352e1dcb1..4d017872c 100644 --- a/src/utilities/Cartographer.ts +++ b/src/utilities/Cartographer.ts @@ -58,7 +58,7 @@ export class Cartographer { } else { visited[roomName] = Math.min(depth, visited[roomName]); } - const neighbors = _.values(Game.map.describeExits(roomName)) as string[]; + const neighbors = _.values(Game.map.describeExits(roomName)); if (depth < maxDepth) { for (const neighbor of neighbors) { // Visit the neighbor if not already done or if this would be a more direct route diff --git a/src/utilities/packrat.ts b/src/utilities/packrat.ts index da6a5a7c0..9619a5129 100644 --- a/src/utilities/packrat.ts +++ b/src/utilities/packrat.ts @@ -335,6 +335,22 @@ export function unpackPosList(chars: string): RoomPosition[] { return posList; } +declare global { + var packId: any; + var unpackId: any; + var packIdList: any; + var unpackIdList: any; + var packCoord: any; + var unpackCoord: any; + var unpackCoordAsPos: any; + var packCoordList: any; + var unpackCoordList: any; + var unpackCoordListAsPosList: any; + var packPos: any; + var unpackPos: any; + var packPosList: any; + var unpackPosList: any; +} // Useful to register these functions on global diff --git a/src/utilities/utils.ts b/src/utilities/utils.ts index 1763ab8e2..c28e0ac9a 100644 --- a/src/utilities/utils.ts +++ b/src/utilities/utils.ts @@ -54,7 +54,7 @@ export function minMax(value: number, min: number, max: number): number { return Math.max(Math.min(value, max), min); } -export function hasMinerals(store: { [resourceType: string]: number }): boolean { +export function hasMinerals(store: StoreDefinition): boolean { for (const resourceType in store) { if (resourceType != RESOURCE_ENERGY && (store[resourceType] || 0) > 0) { return true; @@ -166,14 +166,11 @@ export function toColumns(obj: { [key: string]: string }, opts = {} as ToColumnO /** * Merges a list of store-like objects, summing overlapping keys. Useful for calculating assets from multiple sources */ -export function mergeSum(objects: { [key: string]: number | undefined }[]): { [key: string]: number } { - const ret: { [key: string]: number } = {}; - for (const object of objects) { - for (const key in object) { - const amount = object[key] || 0; - if (!ret[key]) { - ret[key] = 0; - } +export function mergeSum(...stores: StoreContents[]): StoreContents { + const ret = {}; + for (const store of stores) { + for (const [key, amount] of <[ResourceConstant, number][]>Object.entries(store)) { + if (!ret[key]) ret[key] = 0; ret[key] += amount; } } diff --git a/src/versionMigration/migrator.ts b/src/versionMigration/migrator.ts index 081916c12..0bc084cb4 100644 --- a/src/versionMigration/migrator.ts +++ b/src/versionMigration/migrator.ts @@ -71,7 +71,7 @@ export class VersionMigration { static get memory(): VersionMigratorMemory { return Mem.wrap(Memory.Overmind, 'versionMigrator', () => ({ versions: {} - })); + })) as VersionMigratorMemory; } /* @@ -314,6 +314,7 @@ export class VersionMigration { for (const name in Memory.colonies) { for (const key in Memory.colonies[name]) { if (key.includes('miningSite@')) { + // @ts-expect-error migrated delete Memory.colonies[name][key]; } } @@ -356,10 +357,10 @@ export class VersionMigration { // Remove all orders for (const colonyName in Memory.colonies) { if (Memory.colonies[colonyName].evolutionChamber) { - delete Memory.colonies[colonyName].evolutionChamber.activeReaction; - delete Memory.colonies[colonyName].evolutionChamber.reactionQueue; - delete Memory.colonies[colonyName].evolutionChamber.status; - delete Memory.colonies[colonyName].evolutionChamber.statusTick; + delete Memory.colonies[colonyName].evolutionChamber?.activeReaction; + delete Memory.colonies[colonyName].evolutionChamber?.reactionQueue; + delete Memory.colonies[colonyName].evolutionChamber?.status; + delete Memory.colonies[colonyName].evolutionChamber?.statusTick; } } this.memory.versions['053to06X_part3'] = true; @@ -391,7 +392,7 @@ export class VersionMigration { for (const name in Memory.colonies) { if (Memory.colonies[name] && Memory.colonies[name].roomPlanner) { const rpmem = Memory.colonies[name].roomPlanner; - if (rpmem.lastGenerated && rpmem.lastGenerated < oldestTick) { + if (rpmem && rpmem.lastGenerated && rpmem.lastGenerated < oldestTick) { oldestTick = rpmem.lastGenerated; } } @@ -413,8 +414,10 @@ export class VersionMigration { delete Memory.zoneRooms; Memory.roomIntel = {}; // reset this - delete Memory.stats.persistent.terminalNetwork.transfers; - delete Memory.stats.persistent.terminalNetwork.costs; + // @ts-expect-error migrated + delete Memory.stats.persistent.terminalNetwork?.transfers; + // @ts-expect-error migrated + delete Memory.stats.persistent.terminalNetwork?.costs; const mem = Memory as any; @@ -438,8 +441,9 @@ export class VersionMigration { for (const name in Memory.colonies) { const colmem = Memory.colonies[name]; + // @ts-expect-error migrated delete colmem.abathur; // outdated - + // @ts-expect-error migrated delete colmem.expansionData; // bugged log.alert(`Migrating room planner memories...`); @@ -449,6 +453,7 @@ export class VersionMigration { if (colmem.roomPlanner) { for (const key in colmem.roomPlanner) { if (!validRoomPlannerMemKeys.includes(key)) { + // @ts-expect-error migrated delete colmem.roomPlanner[key]; } } @@ -457,14 +462,16 @@ export class VersionMigration { // Migrate road planner to new format log.alert(`Migrating road planner memories...`); if (colmem.roadPlanner) { - if (colmem.roadPlanner.roadLookup) { - const roadLookup = colmem.roadPlanner.roadLookup; + // @ts-expect-error migration + const roadLookup = colmem.roadPlanner.roadLookup; + if (roadLookup) { const roadCoordsPacked: { [roomName: string]: string } = {}; for (const roomName in roadLookup) { const roadCoords = _.map(_.keys(roadLookup[roomName]), coordName => derefCoords(coordName)); roadCoordsPacked[roomName] = packCoordList(roadCoords); } colmem.roadPlanner.roadCoordsPacked = roadCoordsPacked; + // @ts-expect-error migration delete colmem.roadPlanner.roadLookup; } } @@ -472,10 +479,12 @@ export class VersionMigration { // Migrate barrier planner to new format log.alert(`Migrating barrier planner memories...`); if (colmem.barrierPlanner) { - if (colmem.barrierPlanner.barrierLookup) { - const barrierLookup = colmem.barrierPlanner.barrierLookup; + // @ts-expect-error migration + const barrierLookup = colmem.barrierPlanner.barrierLookup; + if (barrierLookup) { const barrierCoords = _.map(_.keys(barrierLookup), coordName => derefCoords(coordName)); colmem.barrierPlanner.barrierCoordsPacked = packCoordList(barrierCoords); + // @ts-expect-error migration delete colmem.barrierPlanner.barrierLookup; } } diff --git a/src/visuals/Visualizer.ts b/src/visuals/Visualizer.ts index e1a45bd0c..23391bfd5 100644 --- a/src/visuals/Visualizer.ts +++ b/src/visuals/Visualizer.ts @@ -315,7 +315,7 @@ export class Visualizer { static drawGraphs(): void { this.text(`CPU`, {x: 1, y: 7}); - this.barGraph(Memory.stats.persistent.avgCPU / Game.cpu.limit, {x: 2.75, y: 7}); + this.barGraph((Memory.stats.persistent.avgCPU ?? 0) / Game.cpu.limit, {x: 2.75, y: 7}); this.text(`BKT`, {x: 1, y: 8}); this.barGraph(Game.cpu.bucket / 10000, {x: 2.75, y: 8}); this.text(`GCL`, {x: 1, y: 9}); diff --git a/src/zerg/AnyZerg.ts b/src/zerg/AnyZerg.ts index f8bd6b254..9458045ba 100644 --- a/src/zerg/AnyZerg.ts +++ b/src/zerg/AnyZerg.ts @@ -80,9 +80,7 @@ export abstract class AnyZerg { isAnyZerg: true; creep: AnyCreep; // The creep that this wrapper class will control // body: BodyPartDefinition[]; // These properties are all wrapped from this.creep.* to this.* - carry: StoreDefinition; // | store: StoreDefinition; // | - carryCapacity: number; // | effects: RoomObjectEffect[]; // fatigue: number; // | hits: number; // | @@ -108,9 +106,7 @@ export abstract class AnyZerg { // Copy over creep references this.creep = creep; // this.body = creep.body; - this.carry = creep.carry; this.store = creep.store; - this.carryCapacity = creep.carryCapacity; // this.fatigue = creep.fatigue; this.effects = creep.effects; this.hits = creep.hits; @@ -137,6 +133,7 @@ export abstract class AnyZerg { this.blockMovement = false; // Register global references // Overmind.zerg[this.name] = this; + // @ts-expect-error Global getter for Zergs global[this.name] = this; // Handle attack notification when at lifetime - 1 if (!notifyWhenAttacked && (this.ticksToLive || 0) >= this.lifetime - (NEW_OVERMIND_INTERVAL + 1)) { @@ -155,9 +152,7 @@ export abstract class AnyZerg { this.pos = creep.pos; this.nextPos = creep.pos; // this.body = creep.body; - this.carry = creep.carry; this.store = creep.store; - this.carryCapacity = creep.carryCapacity; // this.fatigue = creep.fatigue; this.hits = creep.hits; this.memory = creep.memory; @@ -171,7 +166,7 @@ export abstract class AnyZerg { // this._task = null; } else { log.debug(`Deleting ${this.print} from global`); - // delete Overmind.zerg[this.name]; + // @ts-expect-error Global getter for Zergs delete global[this.name]; } } @@ -209,7 +204,7 @@ export abstract class AnyZerg { } move(direction: DirectionConstant, force = false) { - if (!this.blockMovement && !force) { + if (!this.blockMovement || force) { const result = this.creep.move(direction); if (result == OK) { if (!this.actionLog.move) this.actionLog.move = true; @@ -240,7 +235,8 @@ export abstract class AnyZerg { return this.creep.suicide(); } - transfer(target: AnyCreep | AnyZerg | Structure, resourceType: ResourceConstant = RESOURCE_ENERGY, amount?: number) { + transfer(target: AnyCreep | AnyZerg | Structure, + resourceType: ResourceConstant = RESOURCE_ENERGY, amount?: number) { let result: ScreepsReturnCode; if (isAnyZerg(target)) { result = this.creep.transfer(target.creep, resourceType, amount); @@ -251,7 +247,7 @@ export abstract class AnyZerg { return result; } - transferAll(target: AnyCreep | AnyZerg | Structure, amount?: number) { + transferAll(target: AnyCreep | AnyZerg | Structure) { for (const [resourceType, amount] of this.creep.store.contents) { if (amount > 0) { return this.transfer(target, resourceType); @@ -284,7 +280,7 @@ export abstract class AnyZerg { // Carry methods get hasMineralsInCarry(): boolean { - for (const [resourceType, amount] of this.carry.contents) { + for (const [resourceType, amount] of this.store.contents) { if (resourceType != RESOURCE_ENERGY && amount > 0) { return true; } @@ -309,7 +305,7 @@ export abstract class AnyZerg { */ get colony(): Colony | null { if (this.memory[MEM.COLONY] != null) { - return Overmind.colonies[this.memory[MEM.COLONY] as string]; + return Overmind.colonies[this.memory[MEM.COLONY]]; } else { return null; } @@ -332,7 +328,7 @@ export abstract class AnyZerg { // Movement and location ------------------------------------------------------------------------------------------- - goTo(destination: RoomPosition | HasPos, options: MoveOptions = {}) { + goTo(destination: RoomPosition | _HasRoomPosition, options: MoveOptions = {}) { return Movement.goTo(this, destination, options); } @@ -340,7 +336,7 @@ export abstract class AnyZerg { return Movement.goToRoom(this, roomName, options); } - inSameRoomAs(target: HasPos): boolean { + inSameRoomAs(target: _HasRoomPosition): boolean { return this.pos.roomName == target.pos.roomName; } @@ -353,14 +349,15 @@ export abstract class AnyZerg { } get isMoving(): boolean { - const moveData = this.memory._go as MoveData | undefined; + const moveData = this.memory._go; return (!!moveData && !!moveData.path && moveData.path.length > 1) || this.actionLog[MOVE]; } /** * Kite around hostiles in the room */ - kite(avoidGoals: (RoomPosition | HasPos)[] = this.room.hostiles, options: MoveOptions = {}): number | undefined { + kite(avoidGoals: (RoomPosition | _HasRoomPosition)[] = this.room.hostiles, + options: MoveOptions = {}): number | undefined { _.defaults(options, { fleeRange: 5 }); @@ -368,7 +365,7 @@ export abstract class AnyZerg { } private defaultFleeGoals() { - let fleeGoals: (RoomPosition | HasPos)[] = []; + let fleeGoals: (RoomPosition | _HasRoomPosition)[] = []; fleeGoals = fleeGoals.concat(this.room.hostiles) .concat(_.filter(this.room.keeperLairs, lair => (lair.ticksToSpawn || Infinity) < 10)); return fleeGoals; @@ -377,7 +374,7 @@ export abstract class AnyZerg { /** * Flee from hostiles in the room, while not repathing every tick // TODO: take a look at this */ - flee(avoidGoals: (RoomPosition | HasPos)[] = this.room.fleeDefaults, + flee(avoidGoals: (RoomPosition | _HasRoomPosition)[] = this.room.fleeDefaults, fleeOptions: FleeOptions = {}, moveOptions: MoveOptions = {}): boolean { if (avoidGoals.length == 0 || this.room.dangerousHostiles.find( @@ -389,7 +386,7 @@ export abstract class AnyZerg { const fleeing = Movement.flee(this, avoidGoals, fleeOptions.dropEnergy, moveOptions) != undefined; if (fleeing) { // Drop energy if needed - if (fleeOptions.dropEnergy && this.carry.energy > 0) { + if (fleeOptions.dropEnergy && this.store.energy > 0) { const nearbyContainers = this.pos.findInRange(this.room.storageUnits, 1); if (nearbyContainers.length > 0) { this.transfer(_.first(nearbyContainers), RESOURCE_ENERGY); @@ -420,7 +417,7 @@ export abstract class AnyZerg { if (this.memory.avoidDanger) { if (this.memory.avoidDanger.timer > 0) { this.goToRoom(this.memory.avoidDanger.fallback); - if (opts.dropEnergy && this.carry.energy > 0) { + if (opts.dropEnergy && this.store.energy > 0) { this.drop(RESOURCE_ENERGY); // transfer energy to container check is only run on first danger tick } this.memory.avoidDanger.timer--; @@ -449,9 +446,8 @@ export abstract class AnyZerg { // Like 99.999% of the time this will be the case if (this.colony && Game.map.getRoomLinearDistance(this.room.name, this.colony.name) <= maxLinearRange) { fallback = this.colony.name; - } - // But this could happen if the creep was working remotely through a portal - else { + } else { + // But this could happen if the creep was working remotely through a portal const nearbyColonies = _.filter(getAllColonies(), colony => Game.map.getRoomLinearDistance(this.room.name, colony.name) <= maxLinearRange); const closestColony = minBy(nearbyColonies, colony => { @@ -475,7 +471,7 @@ export abstract class AnyZerg { fallback: fallback, }; - if (opts.dropEnergy && this.carry.energy > 0) { + if (opts.dropEnergy && this.store.energy > 0) { const containersInRange = this.pos.findInRange(this.room.containers, 1); const adjacentContainer = _.first(containersInRange); if (adjacentContainer) { @@ -516,7 +512,7 @@ export abstract class AnyZerg { /** * Moves off of an exit tile */ - moveOffExit(towardPos?: RoomPosition, avoidSwamp = true): ScreepsReturnCode { + moveOffExit(towardPos?: RoomPosition, avoidSwamp = true): ScreepsReturnCode | NO_ACTION { return Movement.moveOffExit(this, towardPos, avoidSwamp); } diff --git a/src/zerg/NeuralZerg.ts b/src/zerg/NeuralZerg.ts index 13fbaea98..350ee4b74 100644 --- a/src/zerg/NeuralZerg.ts +++ b/src/zerg/NeuralZerg.ts @@ -41,7 +41,7 @@ export class NeuralZerg extends CombatZerg { return Movement.combatMove(this, [], avoid); } - maneuver(approachTargs: HasPos[], avoidTargs: HasPos[]) { + maneuver(approachTargs: _HasRoomPosition[], avoidTargs: _HasRoomPosition[]) { const approach = _.map(approachTargs, targ => ({pos: targ.pos, range: APPROACH_RANGE})); const avoid = _.map(avoidTargs, targ => ({pos: targ.pos, range: AVOID_RANGE})); return Movement.combatMove(this, approach, avoid); diff --git a/src/zerg/PowerZerg.ts b/src/zerg/PowerZerg.ts index 19d8436d5..cb8c954cb 100644 --- a/src/zerg/PowerZerg.ts +++ b/src/zerg/PowerZerg.ts @@ -55,9 +55,7 @@ export abstract class PowerZerg extends AnyZerg { this.creep = powerCreep; this.pos = powerCreep.pos; this.nextPos = powerCreep.pos; - this.carry = powerCreep.carry; this.store = powerCreep.store; - this.carryCapacity = powerCreep.carryCapacity; this.hits = powerCreep.hits; this.memory = powerCreep.memory; this.room = powerCreep.room as Room; // not actually as Room @@ -81,6 +79,7 @@ export abstract class PowerZerg extends AnyZerg { } else { log.debug(`Deleting ${this.print} from global`); delete Overmind.powerZerg[this.name]; + // @ts-expect-error global getter for PowerZergs delete global[this.name]; } } diff --git a/src/zerg/Swarm.ts b/src/zerg/Swarm.ts index 0ebe4b2f7..1d6ac1826 100644 --- a/src/zerg/Swarm.ts +++ b/src/zerg/Swarm.ts @@ -337,7 +337,7 @@ export class Swarm implements ProtoSwarm { // Range finding methods =========================================================================================== - minRangeTo(obj: RoomPosition | HasPos): number { + minRangeTo(obj: RoomPosition | _HasRoomPosition): number { if (hasPos(obj)) { return _.min(_.map(this.creeps, creep => creep.pos.roomName === obj.pos.roomName ? creep.pos.getRangeToXY(obj.pos.x, obj.pos.y) : Infinity)); @@ -347,7 +347,7 @@ export class Swarm implements ProtoSwarm { } } - maxRangeTo(obj: RoomPosition | HasPos): number { + maxRangeTo(obj: RoomPosition | _HasRoomPosition): number { if (hasPos(obj)) { return _.max(_.map(this.creeps, creep => creep.pos.roomName === obj.pos.roomName ? creep.pos.getRangeToXY(obj.pos.x, obj.pos.y) : Infinity)); @@ -357,7 +357,7 @@ export class Swarm implements ProtoSwarm { } } - findInMinRange(targets: HasPos[], range: number): HasPos[] { + findInMinRange(targets: _HasRoomPosition[], range: number): _HasRoomPosition[] { const initialRange = range + Math.max(this.width, this.height) - 1; const targetsInRange = _.filter(targets, t => this.anchor.inRangeToXY(t.pos.x, t.pos.y, initialRange)); return _.filter(targetsInRange, t => this.minRangeTo(t) <= range); @@ -366,7 +366,7 @@ export class Swarm implements ProtoSwarm { /** * Compute the "average" direction to a target */ - getDirectionTo(obj: RoomPosition | HasPos): DirectionConstant { + getDirectionTo(obj: RoomPosition | _HasRoomPosition): DirectionConstant { const pos = normalizePos(obj); const directions = _.map(this.creeps, creep => creep.pos.getDirectionTo(obj)); // TODO @@ -519,7 +519,7 @@ export class Swarm implements ProtoSwarm { return allMoved ? OK : ERR_NOT_ALL_OK; } - goTo(destination: RoomPosition | HasPos, options: SwarmMoveOptions = {}): number { + goTo(destination: RoomPosition | _HasRoomPosition, options: SwarmMoveOptions = {}): number { // if (DEBUG) { // options.displayCostMatrix = true; // } @@ -588,7 +588,7 @@ export class Swarm implements ProtoSwarm { } private getBestOrientation(room: Room, includeStructures = true, includeCreeps = false): TOP | RIGHT | BOTTOM | LEFT { - const targets: HasPos[] = []; + const targets: _HasRoomPosition[] = []; if (includeStructures) { const structureTargets = this.findInMinRange(room.hostileStructures, 1); for (const structure of structureTargets) { @@ -823,7 +823,7 @@ export class Swarm implements ProtoSwarm { /** * Groups enemies into proto-swarms based on proximity to each other */ - static findEnemySwarms(room: Room, anchor?: HasPos, maxClumpSize = 3): ProtoSwarm[] { + static findEnemySwarms(room: Room, anchor?: _HasRoomPosition, maxClumpSize = 3): ProtoSwarm[] { const enemySwarms: ProtoSwarm[] = []; const origin = anchor || _.first(room.spawns) || room.controller || {pos: new RoomPosition(25, 25, room.name)}; let attackers = _.sortBy(room.dangerousHostiles, creep => origin.pos.getRangeTo(creep)); diff --git a/src/zerg/Zerg.ts b/src/zerg/Zerg.ts index 3a242bb01..7b15ac667 100644 --- a/src/zerg/Zerg.ts +++ b/src/zerg/Zerg.ts @@ -56,9 +56,7 @@ export class Zerg extends AnyZerg { isStandardZerg: true; creep: Creep; // The creep that this wrapper class will control body: BodyPartDefinition[]; // These properties are all wrapped from this.creep.* to this.* - carry: StoreDefinition; // | - store: StoreDefinition; // | - carryCapacity: number; // | + store: StoreDefinition; // | fatigue: number; // | hits: number; // | hitsMax: number; // | @@ -78,7 +76,7 @@ export class Zerg extends AnyZerg { blockMovement: boolean; // Whether the zerg is allowed to move or not // Cached properties - private _task: Task | null; + private _task: Task | null; private _neededBoosts: { [boostResource: string]: number } | undefined; constructor(creep: Creep, notifyWhenAttacked = true) { @@ -557,7 +555,7 @@ export class Zerg extends AnyZerg { /** * Wrapper for _task */ - get task(): Task | null { + get task(): Task | null { if (!this._task) { this._task = this.memory.task ? initializeTask(this.memory.task) : null; } @@ -567,7 +565,7 @@ export class Zerg extends AnyZerg { /** * Assign the creep a task with the setter, replacing creep.assign(Task) */ - set task(task: Task | null) { + set task(task: Task | null) { // Unregister target from old task if applicable const oldProtoTask = this.memory.task; if (oldProtoTask) { diff --git a/tslint.json b/tslint.json deleted file mode 100644 index 0e2eb347b..000000000 --- a/tslint.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "extends": [ - "tslint:recommended" - ], - "linterOptions": { - "exclude": [ - "src/deprecated/*.ts", - "node_modules/**/*" - ] - }, - "rules": { - "align": false, - "array-type": false, - "arrow-parens": false, - "ban-types": false, - "curly": [ - true, - "ignore-same-line" - ], - "forin": false, - "indent": [ - true, - "tabs", - 4 - ], - "interface-name": false, - "interface-over-type-literal": false, - "jsdoc-format": [ - true, - "check-multiline-start" - ], - "object-literal-sort-keys": false, - "one-line": [ - true, - "check-catch", - "check-finally", - // "check-else", // putting else on a different line is sometimes useful - "check-open-brace", - "check-whitespace" - ], - "one-variable-per-declaration": false, - "only-arrow-functions": false, - "max-classes-per-file": false, - "max-line-length": [ - true, - 120 - ], - "member-access": [ - true, - "no-public" - ], - "member-ordering": [ - false - ], - "no-angle-bracket-type-assertion": false, - "no-consecutive-blank-lines": false, - "no-console": [ - false - ], - "no-empty": false, - "no-empty-interface": false, - "no-namespace": [ - true, - "allow-declarations" - ], - "no-shadowed-variable": false, - "no-trailing-whitespace": false, - "object-literal-shorthand": false, - "prefer-const": [ - true, - { - "destructuring": "all" - } - ], - "quotemark": [ - true, - "single" - // I recognize the irony here - ], - "trailing-comma": false, - "triple-equals": false, - "variable-name": [ - true, - "ban-keywords", - "check-format", - "allow-pascal-case", - "allow-leading-underscore" - ], - "whitespace": false - } -}