├── .gitignore ├── Gruntfile.js ├── LICENSE ├── default ├── configs │ └── config.default.js ├── main.colony.js ├── main.js ├── main.world.js ├── misc │ ├── globals.Helpers.js │ ├── globals.js │ ├── logger.js │ ├── require.js │ ├── tools.misc.js │ ├── tools.profiler.js │ └── util.minCut.js ├── modules │ ├── module.bodyGenerator.js │ ├── module.cleanup.js │ ├── module.creepSpawning.js │ ├── module.defense.js │ ├── module.diplomacy.js │ ├── module.expansion.js │ ├── module.factoryController.js │ ├── module.highCommand.js │ ├── module.hud.js │ ├── module.labController.js │ ├── module.linkController.js │ ├── module.observerController.js │ ├── module.pathFinder.js │ ├── module.pixelFarm.js │ ├── module.powerManager.js │ ├── module.roomPlanner.js │ ├── module.segmentManager.js │ ├── module.stateManager.js │ ├── module.terminalController.js │ └── module.towerController.js ├── operations │ ├── operation.borderPatrol.js │ ├── operation.guard.js │ ├── operation.harass.js │ ├── operation.remoteDenial.js │ ├── operation.roomDenial.js │ ├── operation.scout.js │ └── operation.stronghold.js ├── prototypes │ ├── prototype.creep.js │ ├── prototype.creepCombat.js │ ├── prototype.powerCreep.js │ ├── prototype.room.js │ ├── prototype.roomObject.js │ └── prototype.roomPosition.js └── roles │ ├── powerRole.operator.js │ ├── role.SKAttacker.js │ ├── role.attacker.js │ ├── role.claimAttacker.js │ ├── role.claimer.js │ ├── role.cleaner.js │ ├── role.commodityMiner.js │ ├── role.defender.js │ ├── role.drone.js │ ├── role.explorer.js │ ├── role.hauler.js │ ├── role.labTech.js │ ├── role.longbow.js │ ├── role.longbowSquad.js │ ├── role.mineralHarvester.js │ ├── role.powerAttacker.js │ ├── role.powerHauler.js │ ├── role.powerHealer.js │ ├── role.powerManager.js │ ├── role.remoteHarvester.js │ ├── role.remoteHauler.js │ ├── role.reserver.js │ ├── role.roadBuilder.js │ ├── role.scout.js │ ├── role.shuttle.js │ ├── role.siegeDuo.js │ ├── role.stationaryHarvester.js │ ├── role.testSquad.js │ └── role.upgrader.js ├── package.json └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### JetBrains template 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 5 | 6 | # User-specific stuff: 7 | /.idea/ 8 | /upload/ 9 | 10 | # Non default config file 11 | /default/config.*.js 12 | !/default/config.default.js 13 | 14 | ## File-based project format: 15 | ## Plugin-specific files: 16 | 17 | # IntelliJ 18 | # mpeltonen/sbt-idea plugin 19 | # JIRA plugin 20 | # Crashlytics plugin (for Android Studio and IntelliJ) 21 | node_modules/ 22 | package-lock.json 23 | /.auth.json 24 | /ScreepsAutocomplete/ 25 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | module.exports = async function (grunt) { 5 | // --force --host=xxx --email=xxx --pass=xxx 6 | let host = grunt.option('host'); 7 | let email = grunt.option('email'); 8 | let pass = grunt.option('pass'); 9 | let token = grunt.option('token'); 10 | let server = grunt.option('server') || "world"; 11 | let port = grunt.option('port') || 21025; 12 | grunt.loadNpmTasks('grunt-screeps'); 13 | 14 | clearDirectory('./upload/') 15 | moveFilesRecursively('./default/', './upload/'); 16 | 17 | if (!token) { 18 | await grunt.initConfig({ 19 | screeps: { 20 | options: { 21 | server: { 22 | host: host, 23 | port: port, 24 | http: true 25 | }, 26 | email: email, 27 | password: pass, 28 | branch: "default", 29 | ptr: false 30 | }, 31 | dist: { 32 | src: ['upload/*.js'] 33 | } 34 | } 35 | }); 36 | } else { 37 | await grunt.initConfig({ 38 | screeps: { 39 | options: { 40 | email: email, 41 | token: token, 42 | branch: "default", 43 | server: server 44 | }, 45 | dist: { 46 | src: ['upload/*.js'] 47 | } 48 | } 49 | }); 50 | } 51 | clearDirectory('./upload/') 52 | }; 53 | 54 | // Function to move files recursively 55 | function moveFilesRecursively(sourceDir, targetDir) { 56 | // Check if source directory exists 57 | if (!fs.existsSync(sourceDir)) { 58 | console.error('Source directory does not exist.'); 59 | return; 60 | } 61 | 62 | // Create target directory if it doesn't exist 63 | if (!fs.existsSync(targetDir)) { 64 | fs.mkdirSync(targetDir, {recursive: true}); 65 | } 66 | 67 | // Read the contents of the directory 68 | const entries = fs.readdirSync(sourceDir, {withFileTypes: true}); 69 | 70 | for (let entry of entries) { 71 | const sourcePath = path.join(sourceDir, entry.name); 72 | const targetPath = path.join(targetDir, entry.name); 73 | 74 | if (entry.isDirectory()) { 75 | // If it's a directory, recursively move its contents 76 | moveFilesRecursively(sourcePath, targetDir); 77 | } else { 78 | // If it's a file, move it 79 | fs.copyFileSync(sourcePath, targetPath); 80 | console.log(`Moved file: ${sourcePath} to ${targetPath}`); 81 | } 82 | } 83 | } 84 | 85 | function clearDirectory(dir) { 86 | if (!fs.existsSync(dir)) return; 87 | 88 | const entries = fs.readdirSync(dir, {withFileTypes: true}); 89 | 90 | for (let entry of entries) { 91 | const entryPath = path.join(dir, entry.name); 92 | if (entry.isDirectory()) { 93 | // Recursively remove directories 94 | clearDirectory(entryPath); 95 | fs.rmdirSync(entryPath); 96 | } else { 97 | // Remove files 98 | fs.unlinkSync(entryPath); 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /default/configs/config.default.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | // BACKUP THIS FILE BETWEEN UPDATES!!!!!!! 6 | 7 | 8 | // General Settings 9 | global.STATUS_COOLDOWN = 180; // Seconds between console status reports 10 | global.GENERATE_PIXELS = false; // Generate pixels when feasible (not in war) 11 | global.PIXEL_FARM = false; // Use this on spawn ins, bot will do nothing but farm pixels and keep the room from decaying 12 | global.SELL_PIXELS = false; // Sell pixels 13 | global.PIXEL_BUFFER = 1000; // Sell any pixels above this amount 14 | global.DESIRED_LOGGING_LEVEL = 4; //Set level 1-5 (5 being most info) 15 | global.TOWER_FIRST = false; // Set to true to have towers built before spawns 16 | global.FORCE_CLAIM = undefined; 17 | 18 | // Diplomacy 19 | global.AVOID_ALLIED_SECTORS = true; // Try not to claim rooms in allied sectors 20 | global.COMBAT_SERVER = false; // If you'd like to declare everyone hostile 21 | global.HOSTILES = []; // Manually set players as hostile 22 | global.MANUAL_FRIENDS = []; // Manually set players as friends (overrides COMBAT_SERVER) 23 | global.NO_DIRECT_ATTACKS = []; // Manually set players that will not be directly attacked but are also no friends 24 | global.RAMPART_ACCESS = false // Allow friends and allies access through ramparts. Having this disabled does save CPU. 25 | global.NAP_ALLIANCE = []; // Do not attack members of this alliance 26 | global.FUNNEL_REQUESTS = false; // Whether to make energy funneling requests 27 | 28 | // Remote Mining 29 | global.REMOTE_MINING = true; // Whether we remote mine or not 30 | global.REMOTE_DISTANCE_MAX = 75; // Max distance score per source 31 | global.SK_MINING = true; // Do we SK mine 32 | global.SK_MINING_LEVEL = 7; // What level do we do this (won't work before 7 atm) 33 | 34 | // Combat Settings 35 | global.OFFENSIVE_OPERATIONS = false; // Offensive Combat, disabling this will disable all offensive operations 36 | global.HARASSMENT_OPERATIONS = false; // Proactive harassers that will target people on the threat list 37 | global.HOLD_SECTOR = true; // Attack rooms in sectors you have rooms 38 | global.ATTACK_LOCALS = false; // Attacks targets within range indiscriminately. Bot will still attack aggressors. 39 | global.NEW_SPAWN_DENIAL = false; // Crush new spawns immediately 40 | global.NCP_HOSTILE = false; // Always attack users of open source bots 41 | global.ATTACK_COOLDOWN = 3000; //Time between attacks on a room 42 | global.AVOID_ATTACKING_ALLIANCES = true; // Check LOAN and avoid attacking people in alliances 43 | global.DEFENSIVE_BUBBLE = 2; // What range are we more aggressive 44 | 45 | // Market Settings 46 | global.BUY_ENERGY = false; // If true it will buy energy when above the buffer 47 | global.BUY_ENERGY_CREDIT_BUFFER = 500000; // Stay above this to buy energy 48 | global.CREDIT_BUFFER = 10000; // Stay above this amount 49 | global.REACTION_AMOUNT = 10000; // Minimum amount we aim for base minerals 50 | global.TERMINAL_ENERGY_BUFFER = 10000; // Keep this much in terminal (Needed for trade) 51 | global.SELL_BOOSTS = false; // If we should sell spare boosts or not 52 | 53 | // Room Build 54 | global.BUNKER_LEVEL = 5; // What level do we start building the bunker 55 | global.BARRIER_TARGET = 7500000; // Hit target for ramparts/walls at rcl8 56 | global.SPECIAL_RAMPARTS = 7; // What level do we build ramparts on important structures/controller/sources/on-ramps 57 | global.PROTECT_STRUCTURES = true; // Rampart significant structures (tied to the above) 58 | global.PROTECT_CONTROLLER = true; // Build ramparts around the controller 59 | global.PROTECT_MINERAL = false; // Build ramparts around the mineral 60 | global.PROTECT_SOURCES = false; // Build ramparts around the source 61 | global.ROAD_LEVEL = 4 // What level to build roads 62 | 63 | // Manual Operations 64 | global.MANUAL_OPERATIONS = []; // Manually set rooms to attack 65 | 66 | // Signing and whatnot 67 | global.SIGN_ROOMS = true; 68 | global.OWNED_ROOM_SIGNS = [ 69 | "SlothBot by Shibdib" 70 | ]; 71 | 72 | global.RESERVE_ROOM_SIGNS = [ 73 | "Protected Territory - Unauthorized Entry Prohibited" 74 | ]; 75 | 76 | global.EXPLORED_ROOM_SIGNS = [ 77 | "Explored by SlothBot" 78 | ]; 79 | 80 | global.ATTACK_ROOM_SIGNS = [ 81 | "SlothBot" 82 | ]; -------------------------------------------------------------------------------- /default/main.colony.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const ObserverControl = require('module.observerController'); 6 | const LabControl = require('module.labController'); 7 | const FactoryControl = require('module.factoryController'); 8 | const DefenseControl = require('module.defense'); 9 | const LinkControl = require('module.linkController'); 10 | const TerminalControl = require('module.terminalController'); 11 | const spawning = require('module.creepSpawning'); 12 | const diplomacy = require('module.diplomacy'); 13 | const profiler = require('tools.profiler'); 14 | 15 | class Colony { 16 | constructor(room) { 17 | const worldStart = Game.cpu.getUsed(); 18 | this.room = room; 19 | 20 | // Handle room creeps 21 | this.creepManager(); 22 | 23 | // Handle creep spawning 24 | this.creepSpawningController(); 25 | 26 | // Defense Controller 27 | this.defenseController(); 28 | 29 | // Lab Controller 30 | this.labController(); 31 | 32 | // Handle links if room level >= 5 33 | if (this.room.level >= 5) this.linkController(); 34 | 35 | // Handle terminal 36 | this.terminalController(); 37 | 38 | // Observer controller for room level >= 8 39 | if (this.room.level >= 8) this.observerController(); 40 | 41 | // Factory controller 42 | if (this.room.factory) this.factoryController(); 43 | 44 | // Store tick tracker and cpu usage data 45 | this.storeCpuData(Game.cpu.getUsed() - worldStart); 46 | } 47 | 48 | creepManager() { 49 | const roomCreeps = shuffle(Object.values(Game.creeps).filter(creep => creep.memory.colony === this.room.name && !creep.memory.military)); 50 | for (const creep of roomCreeps) { 51 | try { 52 | this.minionController(creep); 53 | } catch (e) { 54 | this.handleCreepError(creep, e); 55 | } 56 | } 57 | } 58 | 59 | handleCreepError(creep, e) { 60 | errorCount[creep.name] = (errorCount[creep.name] || 0) + 1; 61 | 62 | if (errorCount[creep.name] >= 10) { 63 | log.e(`${creep.name} encountered repeated errors and has been terminated.`); 64 | log.e(e.stack); 65 | log.e(JSON.stringify(creep.memory)); 66 | //creep.recycleCreep(); 67 | } else if (errorCount[creep.name] === 1) { 68 | log.e(`${creep.name} encountered an error in room ${roomLink(creep.room.name)}`); 69 | log.e(e.stack); 70 | Game.notify(e.stack); 71 | } 72 | } 73 | 74 | linkController() { 75 | new LinkControl().run(this.room); 76 | } 77 | 78 | labController() { 79 | new LabControl(this.room).run(this.room); 80 | } 81 | 82 | terminalController() { 83 | new TerminalControl(this.room).run(); 84 | } 85 | 86 | observerController() { 87 | new ObserverControl().run(this.room); 88 | } 89 | 90 | factoryController() { 91 | new FactoryControl().run(this.room); 92 | } 93 | 94 | defenseController() { 95 | new DefenseControl(this.room).run(); 96 | } 97 | 98 | creepSpawningController() { 99 | spawning.processBuildQueue(this.room); 100 | const spawnFunctions = [ 101 | {name: 'essentialSpawning', f: spawning.essentialCreepQueue}, 102 | {name: 'miscSpawning', f: spawning.miscCreepQueue}, 103 | {name: 'remoteSpawning', f: spawning.remoteCreepQueue} 104 | ]; 105 | 106 | for (const task of spawnFunctions) { 107 | try { 108 | task.f(this.room); 109 | } catch (e) { 110 | log.e(`${task.name} for room ${this.room.name} encountered an error`); 111 | log.e(e.stack); 112 | Game.notify(e.stack); 113 | } 114 | } 115 | } 116 | 117 | storeCpuData(used) { 118 | let cpuUsageArray = ROOM_CPU_ARRAY[this.room.name] || []; 119 | cpuUsageArray.push(used); 120 | 121 | if (cpuUsageArray.length > 25) cpuUsageArray.shift(); 122 | 123 | if (cpuUsageArray.length === 25) { 124 | const avgCpu = average(cpuUsageArray); 125 | let roomCount = MY_ROOMS.length; 126 | // If we're RCL8 and have energy, make this more likely 127 | if (this.room.level === 8 && this.room.energyState) roomCount *= 1.5 128 | const roomCpuTarget = (Game.cpu.limit * 0.95) / roomCount 129 | if (avgCpu > roomCpuTarget) { 130 | let cpuOverCount = this.room.memory.cpuOverage || 0; 131 | this.room.memory.cpuOverage = cpuOverCount + 1; 132 | if (cpuOverCount >= 75 && Game.cpu.bucket < BUCKET_MAX * 0.25) { 133 | this.room.memory.cpuOverage = undefined; 134 | this.room.memory.noRemote = Game.time + (CREEP_LIFE_TIME * 3); 135 | this.suicideRemoteCreeps(); 136 | log.e(`${roomLink(this.room.name)} remote spawning has been disabled to conserve CPU.`, 'ROOM MANAGER:'); 137 | cpuUsageArray = []; 138 | } else if (cpuOverCount >= 25 && Game.cpu.bucket < BUCKET_MAX * 0.25) { 139 | this.room.memory.cpuOverage = undefined; 140 | this.room.memory.remotePenalty = Game.time + (CREEP_LIFE_TIME * 2); 141 | log.e(`${roomLink(this.room.name)} remote spawning has been disabled to conserve CPU.`, 'ROOM MANAGER:'); 142 | cpuUsageArray = []; 143 | } 144 | } else { 145 | if (this.room.memory.cpuOverage) this.room.memory.cpuOverage--; 146 | if (this.room.memory.noRemote || this.room.memory.remotePenalty) this.handleNoRemote(); 147 | } 148 | } 149 | 150 | ROOM_CPU_ARRAY[this.room.name] = cpuUsageArray; 151 | } 152 | 153 | suicideRemoteCreeps() { 154 | Object.values(Game.creeps) 155 | .filter(creep => creep.my && creep.memory.colony === this.room.name && (creep.memory.role.includes('remote') || creep.memory.role.includes('SK'))) 156 | .forEach(creep => creep.suicide()); 157 | } 158 | 159 | handleNoRemote() { 160 | if (this.room.memory.noRemote <= Game.time) { 161 | // If we still have energy.. keep it rocking 162 | if (this.room.energyState > 1) { 163 | log.a(`${roomLink(this.room.name)} is remaining No Remote as it has energy.`, 'ROOM MANAGER:'); 164 | this.room.memory.noRemote = Game.time + (CREEP_LIFE_TIME * 3); 165 | } else { 166 | log.a(`${roomLink(this.room.name)} has re-enabled remote spawning.`, 'ROOM MANAGER:'); 167 | this.room.memory.noRemote = undefined; 168 | this.room.memory.remotePenalty = undefined; 169 | } 170 | } else if (this.room.memory.remotePenalty <= Game.time) { 171 | // If we still have energy.. keep it rocking 172 | if (this.room.energyState > 1) { 173 | this.room.memory.remotePenalty = Game.time + (CREEP_LIFE_TIME * 2); 174 | } else { 175 | this.room.memory.remotePenalty = undefined; 176 | } 177 | } 178 | } 179 | 180 | minionController(minion) { 181 | // Disable notifications if not already disabled 182 | if (!minion.memory.notifyDisabled) { 183 | minion.notifyWhenAttacked(false); 184 | minion.memory.notifyDisabled = true; 185 | } 186 | 187 | if (minion.towTruck()) return; 188 | 189 | // Return if idle 190 | if (minion.idle) return; 191 | 192 | // Track Threat 193 | diplomacy.trackThreat(minion); 194 | 195 | // Handle edge cases (border or nuke flee) 196 | if (minion.memory.fleeNukeTime && minion.fleeNukeRoom()) { 197 | return; 198 | } 199 | 200 | // Border 201 | minion.borderCheck(); 202 | 203 | // Report intel if outside MY_ROOMS 204 | if (!MY_ROOMS.includes(minion.room.name)) { 205 | minion.room.invaderCheck(); 206 | minion.room.cacheRoomIntel(false, minion); 207 | } 208 | 209 | // If no role, the minion should suicide 210 | if (!minion.memory.role) return minion.recycleCreep(); 211 | 212 | // If we're fleeing, continue to do so 213 | if (minion.memory.runCooldown && Game.time < minion.memory.runCooldown) return minion.fleeHome(true); 214 | 215 | // If being recycled do that 216 | if (minion.memory.recycling) return minion.recycleCreep(); 217 | 218 | // Check if the role is cached 219 | let Role; 220 | if (ROLE_CACHE[minion.memory.role]) { 221 | Role = ROLE_CACHE[minion.memory.role]; 222 | } else { 223 | // Load the role and cache it 224 | Role = require('role.' + minion.memory.role); 225 | ROLE_CACHE[minion.memory.role] = Role; 226 | } 227 | 228 | new Role(minion); 229 | } 230 | } 231 | 232 | profiler.registerClass(Colony, 'Colony'); 233 | module.exports = Colony; 234 | 235 | let errorCount = {}; 236 | -------------------------------------------------------------------------------- /default/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | //Setup globals and prototypes 6 | require("require"); 7 | let memWipe, running; 8 | const tools = require("tools.misc"); 9 | const world = require('main.world'); 10 | const segments = require('module.segmentManager'); 11 | const cleanUp = require('module.cleanup'); 12 | const profiler = require('tools.profiler'); 13 | 14 | if (PROFILER_ENABLED) profiler.enable(); 15 | module.exports.loop = function () { 16 | try { 17 | profiler.wrap(function () { 18 | // Memhack Initialization 19 | tryInitSameMemory(); 20 | 21 | // CPU Bucket Cooldown Check 22 | if (!Memory.cpuTracking) Memory.cpuTracking = {}; 23 | const cpuTracking = Memory.cpuTracking; 24 | const currentBucket = Game.cpu.bucket; 25 | 26 | if (cpuTracking.cooldown) { 27 | if (cpuTracking.cooldown + 50 < Game.time || currentBucket > BUCKET_MAX * 0.5) { 28 | delete Memory.cpuTracking.cooldown; 29 | } else { 30 | const countdown = (cpuTracking.cooldown + 50) - Game.time; 31 | log.e(`On CPU Cooldown for ${countdown} more ticks or until the bucket reaches ${BUCKET_MAX * 0.5}. Current Bucket: ${currentBucket}`); 32 | return; 33 | } 34 | } else if (currentBucket < BUCKET_MAX * 0.01) { 35 | const cooldown = Game.time; 36 | let {roomPenalty = 0, bucketIssueCount = 0} = Memory.cpuTracking || {}; 37 | if (bucketIssueCount === 10) { 38 | } else if (bucketIssueCount >= 50) { 39 | log.e('Bucket Issue Count Exceeded - Abandoning Worst Room'); 40 | //abandonWorstRoom(); 41 | roomPenalty = Game.time; 42 | bucketIssueCount = 0; 43 | } 44 | Memory.cpuTracking = { 45 | cooldown, 46 | bucketIssueCount: bucketIssueCount++, 47 | roomPenalty: roomPenalty 48 | }; 49 | log.e('CPU Bucket Too Low - Cooldown Initiated'); 50 | return; 51 | } else if (currentBucket === BUCKET_MAX && Memory.cpuTracking.bucketIssueCount > 0) Memory.cpuTracking.bucketIssueCount--; 52 | 53 | // Store Owned Rooms (Update Every 25 Ticks) 54 | if (!global.MY_ROOMS || !global.MAX_LEVEL || Game.time % 25 === 0) { 55 | const ownedRooms = Object.values(Game.rooms).filter( 56 | (r) => r.controller && r.controller.my 57 | ); 58 | if (ownedRooms.length) { 59 | global.MY_ROOMS = ownedRooms.map((r) => r.name); 60 | global.MAX_LEVEL = Math.max(...ownedRooms.map((r) => r.controller.level)); 61 | global.MIN_LEVEL = Math.min(...ownedRooms.map((r) => r.controller.level)); 62 | 63 | // Clean INTEL Cache 64 | Object.keys(INTEL).forEach((key) => { 65 | if (INTEL[key].owner === MY_USERNAME && !global.MY_ROOMS.includes(key)) { 66 | purgeIntel(key); 67 | } 68 | }); 69 | } 70 | } 71 | 72 | // Initialize Intel Cache 73 | if (!segments.retrieveIntel()) return; 74 | 75 | // Initialize Pathing Cache 76 | if (!segments.retrievePathing()) return; 77 | 78 | // Auto Respawn Logic 79 | if (!running) { 80 | const ownedRoom = Object.values(Game.rooms).find( 81 | (r) => r.controller && r.controller.my 82 | ); 83 | const spawn = _.filter(Game.structures, (s) => s.my && s.structureType === STRUCTURE_SPAWN); 84 | const creeps = _.filter(Game.creeps, (s) => s.my); 85 | 86 | if (ownedRoom && ownedRoom.controller.level === 1 && ((!_.size(spawn) && !_.size(creeps)) || (_.size(spawn) === 1 && !_.size(creeps)))) { 87 | if (!memWipe) { 88 | resetMemory(); 89 | memWipe = true; 90 | } 91 | if (!_.size(spawn)) { 92 | require('module.roomPlanner').buildRoom(ownedRoom); 93 | return; 94 | } 95 | } else if (_.size(spawn)) { 96 | running = true; 97 | } 98 | } 99 | 100 | // Pixel Farming 101 | if (PIXEL_FARM && ['shard0', 'shard1', 'shard2', 'shard3'].includes(Game.shard.name)) { 102 | return require('module.pixelFarm').farm(Game.rooms[Object.keys(Game.rooms)[0]]); 103 | } 104 | 105 | // Pixel Generation (Every 1500 Ticks) 106 | if ( 107 | GENERATE_PIXELS && 108 | (!Memory.lastPixel || Memory.lastPixel + 100 < Game.time) && 109 | currentBucket >= PIXEL_CPU_COST && 110 | !MY_ROOMS.some((r) => INTEL[r].threatLevel) 111 | ) { 112 | log.a('Pixel Generated'); 113 | Game.cpu.generatePixel(); 114 | Memory.lastPixel = Game.time; 115 | return; 116 | } 117 | 118 | // Miscellaneous Tools 119 | try { 120 | tools.CPULimits(); 121 | tools.tickLength(); 122 | tools.cleanMemory(); 123 | tools.status(); 124 | populateLOANlist(); 125 | cleanUp.cleanup(); 126 | } catch (e) { 127 | log.e('Error with a main tool function'); 128 | log.e(`${e} ${e.stack}`); 129 | Game.notify(`${e} ${e.stack}`); 130 | } 131 | 132 | // World 133 | new world(); 134 | 135 | // Save Caches 136 | try { 137 | segments.storeIntel(); 138 | segments.storePathing(); 139 | } catch (e) { 140 | log.e('Error saving caches'); 141 | log.e(`${e} ${e.stack}`); 142 | Game.notify(`${e} ${e.stack}`); 143 | } 144 | }); 145 | } catch (e) { 146 | log.e(`Error Caught - ${e.stack}`) 147 | Game.notify(`Error Caught - ${e.stack}`) 148 | } 149 | }; 150 | 151 | global.resetMemory = function () { 152 | log.e('Memory Wipe Initiated'); 153 | RawMemory.set('{}'); 154 | Memory.creeps = {}; 155 | Memory.rooms = {}; 156 | Memory.flags = {}; 157 | Memory.spawns = {}; 158 | } 159 | 160 | global.lastMemoryTick = undefined; 161 | 162 | function tryInitSameMemory() { 163 | if (lastMemoryTick && global.LastMemory && Game.time == (lastMemoryTick + 1)) { 164 | delete global.Memory 165 | global.Memory = global.LastMemory 166 | RawMemory._parsed = global.LastMemory 167 | } else { 168 | Memory; 169 | global.LastMemory = RawMemory._parsed 170 | } 171 | lastMemoryTick = Game.time 172 | } 173 | 174 | function abandonWorstRoom() { 175 | let worstRoom = _.min(MY_ROOMS, room => Game.rooms[room].controller.level); 176 | if (worstRoom) { 177 | log.a(`Abandoning ${worstRoom}`); 178 | //abandonRoom(Game.rooms[worstRoom]); 179 | } 180 | } -------------------------------------------------------------------------------- /default/main.world.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const colony = require('main.colony'); 6 | const highCommand = require('module.highCommand'); 7 | const segments = require('module.segmentManager'); 8 | const power = require('module.powerManager'); 9 | const spawning = require('module.creepSpawning'); 10 | const ExpansionControl = require('module.expansion'); 11 | const diplomacy = require('module.diplomacy'); 12 | const HudControl = require('module.hud'); 13 | const StateManager = require('module.stateManager'); 14 | const profiler = require('tools.profiler'); 15 | const planner = require('module.roomPlanner'); 16 | let buildingNotifications; 17 | let tickTracker = {}; 18 | 19 | class World { 20 | constructor() { 21 | // General housekeeping 22 | this.houseKeeping(); 23 | 24 | // Manage segments 25 | this.segmentManager(); 26 | 27 | // Manage room states 28 | this.stateManager(); 29 | 30 | // Manage rooms 31 | this.colonyManager(); 32 | 33 | // Manage military creeps 34 | this.militaryCreepManager(); 35 | 36 | // Manage Power Creeps 37 | this.powerCreepManager(); 38 | 39 | // Update HUD 40 | this.hudManager(); 41 | 42 | // Handle room building 43 | this.constructionController(); 44 | 45 | // Global Queue (Every 10 Ticks) 46 | if ((tickTracker['globalQueue'] || 0) + 10 < Game.time) { 47 | this.globalQueue(); 48 | tickTracker['globalQueue'] = Game.time; 49 | } 50 | 51 | // High Command 52 | this.highCommand(); 53 | 54 | // Expansion Manager 55 | if ((tickTracker['expansionManager'] || 0) + 1000 < Game.time) { 56 | this.expansionManager(); 57 | tickTracker['expansionManager'] = Game.time; 58 | } 59 | } 60 | 61 | houseKeeping() { 62 | // Timing 63 | Memory.tickCooldowns = undefined; 64 | 65 | // Silence Alerts (Every 2500 Ticks) 66 | if (Game.time % 2500 === 0) { 67 | buildingNotifications = true; 68 | const structures = Object.values(Game.structures); 69 | structures.forEach((building) => building.notifyWhenAttacked(false)); 70 | } 71 | 72 | // Track mined minerals 73 | if (!_.size(MY_MINERALS) || MY_MINERALS.roomCount !== MY_ROOMS.length || Math.random() > 0.95) { 74 | MY_ROOMS.forEach(function (r) { 75 | if (Game.rooms[r].level >= 6) { 76 | const mineral = Game.rooms[r].mineral; 77 | if (!MY_MINERALS[mineral.mineralType]) { 78 | MY_MINERALS[mineral.mineralType] = true; 79 | } 80 | } 81 | }) 82 | MY_MINERALS.roomCount = MY_ROOMS.length; 83 | } 84 | 85 | // Diplomacy Manager 86 | diplomacy.diplomacyManager(); 87 | } 88 | 89 | hudManager() { 90 | new HudControl().run(); 91 | } 92 | 93 | stateManager() { 94 | new StateManager().run(); 95 | } 96 | 97 | highCommand() { 98 | highCommand.highCommand(); 99 | } 100 | 101 | constructionController() { 102 | planner.buildRoom(); 103 | } 104 | 105 | expansionManager() { 106 | new ExpansionControl().run(); 107 | } 108 | 109 | globalQueue() { 110 | spawning.globalCreepQueue(); 111 | } 112 | 113 | powerCreepManager() { 114 | power.powerControl(); 115 | } 116 | 117 | segmentManager() { 118 | segments.init(); 119 | } 120 | 121 | militaryCreepManager() { 122 | const creeps = shuffle(Object.values(Game.creeps).filter((creep) => 123 | (creep.memory.military || !creep.memory.colony) 124 | )); 125 | 126 | for (const creep of creeps) { 127 | try { 128 | minionController(creep); 129 | } catch (e) { 130 | this.handleCreepError(creep, e); 131 | } 132 | } 133 | } 134 | 135 | colonyManager() { 136 | const rooms = shuffle([...MY_ROOMS]); // Cache rooms to avoid global lookups 137 | 138 | for (const roomName of rooms) { 139 | const room = Game.rooms[roomName]; 140 | if (!room) { 141 | global.MY_ROOMS = global.MY_ROOMS.filter((r) => r !== roomName); 142 | continue; 143 | } 144 | 145 | try { 146 | room.invaderCheck(); 147 | room.cacheRoomIntel(); 148 | const roomLimit = (CPU_TASK_LIMITS['roomLimit'] * 0.9) / MY_ROOMS.length; 149 | new colony(room, roomLimit); 150 | } catch (e) { 151 | log.e(`Colony Module experienced an error in room ${roomLink(roomName)}`); 152 | log.e(e.stack); 153 | Game.notify(e.stack); 154 | } 155 | } 156 | } 157 | 158 | handleCreepError(creep, error) { 159 | if (!errorCount[creep.name]) { 160 | errorCount[creep.name] = 1; 161 | log.e(`${creep.name} encountered an error in room ${roomLink(creep.room.name)}`); 162 | log.e(error); 163 | log.e(error.stack); 164 | Game.notify(`${error}\n${error.stack}`); 165 | } else { 166 | errorCount[creep.name]++; 167 | } 168 | 169 | if (errorCount[creep.name] >= 50) { 170 | log.e(`${creep.name} encountered repeated errors and has been terminated.`); 171 | log.e(error.stack); 172 | //creep.suicide(); 173 | } 174 | } 175 | } 176 | 177 | profiler.registerClass(World, 'World'); 178 | module.exports = World; 179 | 180 | let errorCount = {}; 181 | 182 | function minionController(minion) { 183 | // Disable notifications 184 | if (!minion.memory.notifyDisabled) { 185 | minion.notifyWhenAttacked(false); 186 | minion.memory.notifyDisabled = true; 187 | } 188 | // Handle idle 189 | if (minion.idle) { 190 | return; 191 | } 192 | // Track Threat 193 | diplomacy.trackThreat(minion); 194 | // Handle edge cases 195 | if (minion.memory.fleeNukeTime && minion.fleeNukeRoom()) { 196 | return; 197 | } 198 | // Border 199 | minion.borderCheck(); 200 | // Report intel chance 201 | if (!MY_ROOMS.includes(minion.room.name)) { 202 | minion.room.invaderCheck(); 203 | minion.room.cacheRoomIntel(false, minion); 204 | } 205 | // Run role 206 | if (!minion.memory.role) return minion.suicide(); 207 | 208 | // If being recycled do that 209 | if (minion.memory.recycling) return minion.recycleCreep(); 210 | 211 | // Check if the role is cached 212 | let Role; 213 | if (ROLE_CACHE[minion.memory.role]) { 214 | Role = ROLE_CACHE[minion.memory.role]; 215 | } else { 216 | // Load the role and cache it 217 | Role = require('role.' + minion.memory.role); 218 | ROLE_CACHE[minion.memory.role] = Role; 219 | } 220 | 221 | new Role(minion); 222 | } 223 | -------------------------------------------------------------------------------- /default/misc/logger.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | class Log { 6 | 7 | constructor() { 8 | // Define logging levels with associated names and values 9 | this.LOGGING_LEVEL = { 10 | ALERT : {name: 'ALERT', value: 1}, 11 | ERROR : {name: 'ERROR', value: 2}, 12 | WARN : {name: 'WARN', value: 3}, 13 | INFO : {name: 'INFO', value: 4}, 14 | DEBUG : {name: 'DEBUG', value: 5} 15 | }; 16 | 17 | // Default logging level is WARN unless overridden in Memory 18 | this._desiredLoggingLevel = Memory.loggingLevel || 3; // Default to WARN level 19 | } 20 | 21 | // Getter for DESIRED_LOGGING_LEVEL 22 | get desiredLoggingLevel() { 23 | return this._desiredLoggingLevel; 24 | } 25 | 26 | // Setter for DESIRED_LOGGING_LEVEL 27 | set desiredLoggingLevel(level) { 28 | if (Object.values(this.LOGGING_LEVEL).find(l => l.value === level)) { 29 | this._desiredLoggingLevel = level; 30 | Memory.loggingLevel = level; // Persist logging level in memory 31 | } else { 32 | this.w('Attempted to set an invalid logging level, ignoring.'); 33 | } 34 | } 35 | 36 | /** 37 | * DEBUG level log message. This will only appear when logging level is set to DEBUG. 38 | * Use this to print console output that is useful during debugging. 39 | */ 40 | d(message) { 41 | this.cprint('DEBUG: ' + message, this.LOGGING_LEVEL.DEBUG, '#6e6770'); 42 | } 43 | 44 | /** 45 | * INFO level log message. This will appear when the logging level is set to INFO or DEBUG. 46 | * Use for informational messages. 47 | */ 48 | i(message, custom = undefined) { 49 | if (custom) { 50 | this.cprint(custom + ' ' + message, this.LOGGING_LEVEL.INFO, '#0b5ed7'); 51 | } else { 52 | this.cprint('INFO: ' + message, this.LOGGING_LEVEL.INFO, '#0b5ed7'); 53 | } 54 | } 55 | 56 | /** 57 | * WARN level log message. This will appear when the logging level is set to WARN, INFO, or DEBUG. 58 | * Use for warnings, such as minor issues or things to keep an eye on. 59 | */ 60 | w(message, custom = undefined) { 61 | if (custom) { 62 | this.cprint(custom + ' ' + message, this.LOGGING_LEVEL.WARN, '#f43e6d'); 63 | } else { 64 | this.cprint('WARN: ' + message, this.LOGGING_LEVEL.WARN, '#f43e6d'); 65 | } 66 | } 67 | 68 | /** 69 | * ERROR level log message. This will appear when logging level is set to ERROR, WARN, INFO, or DEBUG. 70 | * Use for issues that need immediate attention. 71 | */ 72 | e(message, custom = undefined) { 73 | if (custom) { 74 | this.cprint(custom + ' ' + message, this.LOGGING_LEVEL.ERROR, '#e59821'); 75 | } else { 76 | this.cprint('ERROR: ' + message, this.LOGGING_LEVEL.ERROR, '#e59821'); 77 | Memory.errorLogs = undefined 78 | } 79 | } 80 | 81 | /** 82 | * ALERT level log message. This will appear at all logging levels. 83 | * Use this for critical messages that should be noticed immediately. 84 | */ 85 | a(message, custom = undefined) { 86 | if (custom) { 87 | this.cprint(custom + ' ' + message, this.LOGGING_LEVEL.ALERT, '#00ff07'); 88 | } else { 89 | this.cprint('ALERT: ' + message, this.LOGGING_LEVEL.ALERT, '#00ff07'); 90 | } 91 | } 92 | 93 | /** 94 | * Core function to print log messages. Checks logging level and prints with color. 95 | */ 96 | cprint(message, logLevel, color = '#ffffff') { 97 | if (logLevel.value <= this.desiredLoggingLevel) { 98 | // For all logs, ensure they print the message without the `%c` format 99 | if (logLevel === this.LOGGING_LEVEL.DEBUG) { 100 | console.debug(message); // Use console.debug for DEBUG logs 101 | } else { 102 | console.log(message); // Use console.log for other logs 103 | } 104 | } 105 | } 106 | } 107 | 108 | module.exports = Log; -------------------------------------------------------------------------------- /default/misc/require.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | require("globals")(); 6 | require("globals.Helpers")(); 7 | require("prototype.creep"); 8 | require("prototype.creepCombat"); 9 | require("prototype.powerCreep"); 10 | require("prototype.roomPosition"); 11 | require("prototype.roomObject"); 12 | require("prototype.room"); 13 | require('module.pathFinder'); 14 | 15 | // Operations 16 | require("operation.scout"); 17 | require("operation.guard"); 18 | require("operation.roomDenial"); 19 | require("operation.borderPatrol"); 20 | require("operation.harass"); 21 | require("operation.remoteDenial"); 22 | require("operation.stronghold"); -------------------------------------------------------------------------------- /default/misc/tools.misc.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | // Get Tick Length 6 | const tickLengthArray = []; 7 | module.exports.tickLength = function () { 8 | if (!Memory.tickInfo) { 9 | Memory.tickInfo = {}; 10 | Memory.lastTick = undefined; 11 | Memory.tickLength = undefined; 12 | } 13 | let d = new Date(); 14 | let seconds = _.round(d.getTime() / 1000, 2); 15 | let lastTick = Memory.tickInfo.lastTick || seconds; 16 | Memory.tickInfo.lastTick = seconds; 17 | let tickLength = seconds - lastTick; 18 | if (tickLengthArray.length < 50) { 19 | tickLengthArray.push(tickLength) 20 | } else { 21 | tickLengthArray.shift(); 22 | tickLengthArray.push(tickLength) 23 | } 24 | Memory.tickInfo.tickLength = _.round(average(tickLengthArray), 3); 25 | } 26 | 27 | // Handle cleaning memory for respawn 28 | let memCleaned; 29 | module.exports.cleanMemory = function () { 30 | if (!memCleaned && !_.filter(Game.rooms, (r) => r.controller && r.controller.owner && r.controller.my && (r.memory.bunkerHub || r.memory.praiseRoom)).length) { 31 | for (let key in Memory) delete Memory[key]; 32 | Memory.spawnIn = Game.time; 33 | } 34 | if (!Memory.spawnIn) Memory.spawnIn = Game.time - 5000; 35 | memCleaned = true; 36 | } 37 | 38 | // Set Task CPU Limits 39 | module.exports.CPULimits = function () { 40 | let totalLimit = Game.cpu.limit; 41 | CPU_TASK_LIMITS['roomLimit'] = adjustedCPULimit(totalLimit * 0.9, Game.cpu.bucket, 2500); 42 | CPU_TASK_LIMITS['military'] = adjustedCPULimit(totalLimit * 0.02, Game.cpu.bucket, 2000); 43 | CPU_TASK_LIMITS['worldTasks'] = adjustedCPULimit(totalLimit * 0.08, Game.cpu.bucket, 2500); 44 | } 45 | 46 | // CPU Limit Tool 47 | function adjustedCPULimit(limit, bucket, target = BUCKET_MAX * 0.8, maxCpuPerTick = Game.cpu.limit * 2) { 48 | var multiplier = 1; 49 | if (bucket < target) { 50 | multiplier = Math.sin(Math.PI * bucket / (2 * target)); 51 | } 52 | if (bucket > target) { 53 | // Thanks @Deign for support with the sine function below 54 | multiplier = 2 + Math.sin((Math.PI * (bucket - BUCKET_MAX)) / (2 * (BUCKET_MAX - target))); 55 | // take care of our 10 CPU folks, to dip into their bucket reserves more... 56 | // help them burn through excess bucket above the target. 57 | if (limit === 10 && multiplier > 1.5) 58 | multiplier += 1; 59 | } 60 | 61 | return clamp(Math.round(limit * 0.2), Math.round(limit * multiplier), maxCpuPerTick); 62 | } 63 | 64 | // Status console with cache expiration and enhanced output formatting 65 | let lastStatus = 0; 66 | module.exports.status = function () { 67 | const currentTime = _.round(new Date().getTime() / 1000, 2); 68 | const timeSinceLastStatus = currentTime - lastStatus; 69 | 70 | // Check if the status cooldown has expired or if we need to refresh the status 71 | if (timeSinceLastStatus >= STATUS_COOLDOWN) { 72 | lastStatus = currentTime; 73 | 74 | log.a('===========================================================================', ' '); 75 | log.a('------------------------------- GLOBAL INFO -------------------------------', ' '); 76 | log.e(`🏆 GCL: ${Game.gcl.level} | Progress: ${(Game.gcl.progress / Game.gcl.progressTotal * 100).toFixed(2)}%`, ' '); 77 | log.e(`💻 CPU Bucket: ${Game.cpu.bucket} | CPU Limit: ${Game.cpu.limit} | Available: ${Game.cpu.tickLimit}`, ' '); 78 | log.e(`👾 Total Creeps: ${_.size(Game.creeps)}`, ' '); 79 | 80 | log.a('------------------------------- ROOM INFO -------------------------------', ' '); 81 | MY_ROOMS.forEach(roomName => { 82 | const room = Game.rooms[roomName]; 83 | if (!room || !room.controller) return; 84 | 85 | const roomCreeps = _.filter(Game.creeps, c => c.memory && c.memory.colony === room.name); 86 | const avgCpu = ROOM_CPU_ARRAY[room.name] ? (_.round(average(ROOM_CPU_ARRAY[room.name])) || 'No Data') : 'No Data'; 87 | const lowPowerText = room.memory.noRemote ? ' 🔋[CPU SAVING]' : ''; 88 | let progress = ((room.controller.progress / room.controller.progressTotal) * 100).toFixed(2) + "%"; 89 | if (room.controller.level === 8) progress = "Max Level"; 90 | const energyInfo = `Energy: ${room.energy} | Income: ${room.energyIncome}`; 91 | const progressBarLength = 20; 92 | const progressRatio = room.controller.progress / room.controller.progressTotal; 93 | const filledLength = Math.floor(progressBarLength * progressRatio); 94 | const emptyLength = progressBarLength - filledLength; 95 | let progressBar = `[${'X'.repeat(filledLength)}${'-'.repeat(emptyLength)}]`; 96 | if (room.controller.level === 8) progressBar = ""; 97 | const resource = room.mineral.mineralType; 98 | const resourceActive = !room.mineral.ticksToRegeneration ? '*' : ' '; 99 | 100 | // Log general info along with the progress bar 101 | log.e(`${roomLink(room.name)}${lowPowerText} | ${resource}${resourceActive} | RCL: ${room.controller.level} | CPU Usage: ${avgCpu} | RCL Progress: ${progress} ${progressBar}`, ' '); 102 | log.e(`${energyInfo} | Creeps: ${_.size(roomCreeps)}`, ' '); 103 | }); 104 | 105 | // OPERATION INFO 106 | displayOperationsInfo(); 107 | 108 | // HARASSMENT INFO 109 | displayHarassmentInfo(); 110 | 111 | // DIPLOMATIC INFO 112 | displayDiplomaticInfo(); 113 | 114 | // Update the last status time 115 | Memory.lastStatus = undefined; 116 | getUptime(); 117 | 118 | log.a('===========================================================================', ' '); 119 | } 120 | 121 | // Helper function to display operation information 122 | function displayOperationsInfo() { 123 | const operations = {...Memory.targetRooms, ...Memory.auxiliaryTargets}; 124 | 125 | if (_.size(operations)) { 126 | log.a('------------------------------ OPERATION INFO -----------------------------', ' '); 127 | 128 | Object.entries(operations).forEach(([key, op]) => { 129 | if (!op) return; 130 | 131 | const { 132 | level = 0, 133 | type, 134 | priority, 135 | assignedRoom, 136 | dDay, 137 | enemyDead, 138 | friendlyDead, 139 | trackedEnemy = [], 140 | trackedFriendly = [] 141 | } = op; 142 | const roomLinkText = roomLink(key); 143 | 144 | let logText = `${_.capitalize(type)} | Level: ${level} | Priority: ${priority} | Room: ${roomLinkText}`; 145 | 146 | if (assignedRoom) { 147 | logText += ` | Assigned Room: ${roomLink(assignedRoom)}`; 148 | } 149 | 150 | if (enemyDead || friendlyDead) { 151 | logText += ` | 💥 Enemy KIA: ${trackedEnemy.length}/${enemyDead} | 🤝 Friendly KIA: ${trackedFriendly.length}/${friendlyDead}`; 152 | } else if (type === 'pending') { 153 | logText += ` | ⏳ Countdown: ${dDay - Game.time} ticks`; 154 | } 155 | 156 | log.e(logText, ' '); 157 | }); 158 | 159 | const scouts = _.filter(operations, t => t && (t.type === 'scout' || t.type === 'attack')); 160 | if (scouts.length) { 161 | log.e(`🔍 Scout Target Count: ${scouts.length}`, ' '); 162 | } 163 | } 164 | } 165 | 166 | // Helper function to display harassment info 167 | function displayHarassmentInfo() { 168 | const activeHarassers = _.filter(Game.creeps, c => c.memory && c.memory.operation === 'harass'); 169 | if (activeHarassers.length) { 170 | log.a('----------------------------- HARASSMENT INFO ----------------------------', ' '); 171 | log.e(`🎯 Harass Targets: ${THREATS.join(", ")}`, ' '); 172 | log.e(`⚔️ Active Harassers: ${activeHarassers.length}`, ' '); 173 | log.e(`📍 Targets: ${_.pluck(activeHarassers, 'memory.destination').join(", ")}`, ' '); 174 | } 175 | } 176 | 177 | // Helper function to display diplomatic info 178 | function displayDiplomaticInfo() { 179 | if (ENEMIES && ENEMIES.length) { 180 | log.a('------------------------------ DIPLOMATIC INFO ----------------------------', ' '); 181 | log.e(`⚔️ Enemies: ${ENEMIES.join(", ")}`, ' '); 182 | } 183 | } 184 | }; 185 | 186 | -------------------------------------------------------------------------------- /default/modules/module.cleanup.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | module.exports.cleanup = function () { 6 | if (Game.time % 100 === 0) { 7 | cleanDistanceCacheByUsage(); 8 | cleanConstructionSites(); 9 | cleanStructureMemory(); 10 | cleanStructures(); 11 | cleanPathingCaches(); 12 | } 13 | if (Game.time % EST_TICKS_PER_DAY === 0) { 14 | // Uncomment to enable: cleanRoomIntel(); 15 | } 16 | if (Game.time % 5 === 0) { 17 | // Cleanup old creep memory 18 | for (let name in Memory.creeps) { 19 | if (!Game.creeps[name]) { 20 | delete Memory.creeps[name]; 21 | } 22 | } 23 | 24 | // Cleanup old flag memory 25 | for (let name in Memory.flags) { 26 | if (!Game.flags[name]) { 27 | delete Memory.flags[name]; 28 | } 29 | } 30 | } 31 | }; 32 | 33 | function cleanDistanceCacheByUsage() { 34 | if (!Memory._distanceCache) return; 35 | 36 | let cache; 37 | try { 38 | cache = JSON.parse(Memory._distanceCache); 39 | } catch (e) { 40 | delete Memory._distanceCache; 41 | return; 42 | } 43 | 44 | if (_.size(cache) < 5000) return; 45 | 46 | const sorted = _.sortBy(cache, "uses"); 47 | const overage = _.size(cache) - 2000 + 250; 48 | log.i(`Cleaning Distance cache (Over max size by ${overage})...`); 49 | Memory._distanceCache = JSON.stringify(_.slice(sorted, overage)); 50 | } 51 | 52 | function cleanConstructionSites() { 53 | for (let id in Game.constructionSites) { 54 | const site = Game.constructionSites[id]; 55 | if ( 56 | Math.random() > 0.5 && 57 | (!site.room || !site.pos.findClosestByRange(FIND_MY_CREEPS)) && 58 | site.structureType !== STRUCTURE_SPAWN && 59 | site.structureType !== STRUCTURE_EXTENSION && 60 | site.structureType !== STRUCTURE_CONTAINER && 61 | site.structureType !== STRUCTURE_ROAD 62 | ) { 63 | site.remove(); 64 | } 65 | } 66 | } 67 | 68 | // Uncomment to use this function when necessary 69 | function cleanRoomIntel() { 70 | if (!INTEL) return; 71 | 72 | const startLength = _.size(INTEL); 73 | for (let roomName in INTEL) { 74 | const intel = INTEL[roomName]; 75 | if ( 76 | intel.cached + 10000 < Game.time || 77 | (intel.cached + 20000 < Game.time && intel.important) || 78 | (findClosestOwnedRoom(roomName, true) > 10 && intel.cached + 5000 < Game.time) 79 | ) { 80 | } 81 | } 82 | 83 | const newLength = _.size(INTEL); 84 | if (startLength > newLength) { 85 | log.d(`CleanUp: Room Cache now has ${newLength} entries.`); 86 | } 87 | } 88 | 89 | function cleanStructureMemory() { 90 | if (Memory.structureMemory) { 91 | delete Memory.structureMemory; 92 | return; 93 | } 94 | 95 | for (let i = 0; i < MY_ROOMS.length; i++) { 96 | const room = Game.rooms[MY_ROOMS[i]]; 97 | if (room && room.memory && room.memory.structureMemory) { 98 | for (let structureId in room.memory.structureMemory) { 99 | if (!Game.getObjectById(structureId)) { 100 | delete room.memory.structureMemory[structureId]; 101 | } 102 | } 103 | } 104 | } 105 | } 106 | 107 | function cleanStructures() { 108 | const structures = _.filter(Game.structures, function (s) { 109 | return ( 110 | s.room.controller && 111 | (!s.room.controller.owner || s.room.controller.owner.username !== MY_USERNAME) && 112 | !s.isActive() 113 | ); 114 | }); 115 | 116 | for (let i = 0; i < structures.length; i++) { 117 | structures[i].destroy(); 118 | } 119 | } 120 | 121 | const cleanPathingCaches = () => { 122 | const now = Game.time; 123 | const routeCache = CACHE.ROUTE_CACHE; 124 | const pathCache = CACHE.PATH_CACHE; 125 | 126 | for (let key in routeCache) { 127 | if (now - routeCache[key].tick > 500) { 128 | delete routeCache[key]; 129 | } 130 | } 131 | for (let key in pathCache) { 132 | if (now - pathCache[key].tick > 200) { 133 | delete pathCache[key]; 134 | } 135 | } 136 | }; -------------------------------------------------------------------------------- /default/modules/module.diplomacy.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | let tempHostiles = {}; 6 | 7 | module.exports.diplomacyManager = function () { 8 | if (!Memory._userList || !(Memory._userList instanceof Object)) Memory._userList = {}; 9 | // Manage friendlies 10 | global.FRIENDLIES = _.union(LOAN_LIST, [MY_USERNAME], ['Shibdib'], MANUAL_FRIENDS).filter((u) => !_.find(tempHostiles, (h) => h.user === u && h.tick > Game.time)); 11 | // Manage threats 12 | threatManager(); 13 | // Diplomacy recap 14 | }; 15 | 16 | function threatManager() { 17 | // Process user standings 18 | for (const name in Memory._userList) { 19 | // Sanity checks 20 | if (!name || name === MY_USERNAME || name === 'undefined') continue; 21 | const user = Memory._userList[name]; 22 | 23 | // These update every 25 ticks 24 | if (user.lastChange + 25 > Game.time) continue; 25 | 26 | let currentRating = user.standing; 27 | if (user.lastAction + 25 < Game.time && user.lastChange + 25 < Game.time) { 28 | currentRating = Math.max(-1000, Math.min(100, currentRating)); 29 | currentRating = (currentRating > 5) ? currentRating - 0.2 : currentRating + 1; 30 | user.lastChange = Game.time; 31 | } 32 | 33 | if (currentRating < -250 || (COMBAT_SERVER && !FRIENDLIES.includes(name))) { 34 | ENEMIES.push(name); 35 | } 36 | if (currentRating < -5 && !FRIENDLIES.includes(name)) THREATS.push(name); 37 | 38 | user.standing = Math.round(currentRating * 100) / 100; 39 | Memory._userList[name] = user; 40 | } 41 | 42 | // Include manual enemies 43 | global.ENEMIES = _.union(ENEMIES, HOSTILES); 44 | global.THREATS = _.union(THREATS, HOSTILES); 45 | } 46 | 47 | module.exports.trackThreat = function (creep) { 48 | const {room, hits, hitsMax, memory} = creep; 49 | 50 | if (!INTEL[creep.room.name]) return creep.room.cacheRoomIntel(); 51 | 52 | // Handle updating rooms with towers 53 | const tower = creep.room.structures.find((s) => !s.my && s.structureType === STRUCTURE_TOWER); 54 | if (tower && !INTEL[creep.room.name].towers) { 55 | creep.room.cacheRoomIntel(true); 56 | purgeBadRoute(creep.room.name); 57 | } 58 | 59 | // Process damage detection 60 | if (hits < memory._lastHits || hitsMax) { 61 | if (!INTEL[room.name]) return room.cacheRoomIntel(); 62 | 63 | INTEL[room.name].lastCombat = Game.time; 64 | INTEL[room.name].pathingPenalty = Game.time; 65 | INTEL[room.name].armedHostile = Game.time; 66 | 67 | const isHostileRoom = INTEL[creep.room.name] && INTEL[creep.room.name].user && !FRIENDLIES.includes(INTEL[creep.room.name].user); 68 | if (isHostileRoom) purgeBadRoute(creep.room.name); 69 | if (isHostileRoom && memory.destination !== room.name) return; 70 | 71 | const nearbyHostiles = _.uniq(_.pluck(_.filter(room.creeps, 72 | function (c) { 73 | return (((c.hasActiveBodyparts(RANGED_ATTACK) && c.pos.inRangeTo(creep, 3)) || (c.hasActiveBodyparts(ATTACK) && c.pos.isNearTo(creep))) && c.owner.username !== MY_USERNAME); 74 | } 75 | ), "owner.username")); 76 | 77 | for (let i = 0; i < nearbyHostiles.length; i++) { 78 | const user = nearbyHostiles[i]; 79 | if (user === MY_USERNAME || user === "Invader" || user === "Source Keeper") continue; 80 | 81 | if (!Memory._userList) Memory._userList = {}; 82 | const cache = Memory._userList; 83 | const userEntry = cache[user] || {}; 84 | let standing = userEntry.standing || 0; 85 | 86 | const multiplier = INTEL[room.name] && INTEL[room.name].user === MY_USERNAME ? 3 : 0.25; 87 | 88 | if (FRIENDLIES.includes(user)) { 89 | standing -= multiplier; 90 | } else { 91 | standing -= 2.5 * multiplier; 92 | } 93 | 94 | standing = Math.max(standing, -1500); 95 | cache[user] = {standing: standing, lastAction: Game.time, lastChange: Game.time}; 96 | Memory._userList = cache; 97 | } 98 | } 99 | 100 | memory._lastHits = hits; 101 | 102 | // Handle trespassing 103 | if (room.hostileCreeps.length && INTEL[room.name] && INTEL[room.name].user === MY_USERNAME) { 104 | const neutrals = _.uniq(_.pluck(room.hostileCreeps, "owner.username")); 105 | 106 | for (let i = 0; i < neutrals.length; i++) { 107 | const user = neutrals[i]; 108 | if ( 109 | user === MY_USERNAME || 110 | user === "Invader" || 111 | user === "Source Keeper" || 112 | FRIENDLIES.includes(user) || 113 | (INTEL[room.name] && INTEL[room.name].isHighway) 114 | ) 115 | continue; 116 | 117 | if (!Memory._userList) Memory._userList = {}; 118 | const cache = Memory._userList; 119 | const userEntry = cache[user] || {}; 120 | const lastAction = userEntry.lastAction || 0; 121 | if (lastAction + 50 > Game.time) continue; 122 | 123 | let standing = userEntry.standing || 0; 124 | standing -= 0.5; 125 | 126 | standing = Math.max(standing, -5004); 127 | cache[user] = {standing: standing, lastAction: Game.time, lastChange: Game.time}; 128 | Memory._userList = cache; 129 | } 130 | } 131 | }; 132 | 133 | -------------------------------------------------------------------------------- /default/modules/module.expansion.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const profiler = require("tools.profiler"); 6 | 7 | class ExpansionControl { 8 | constructor() { 9 | this.claimTarget = Memory.claimTarget || FORCE_CLAIM || {}; 10 | this.worthyRooms = []; 11 | } 12 | 13 | run() { 14 | if (!MY_ROOMS[0] || Object.keys(INTEL).length < 15) return; 15 | 16 | this.findClaimTarget(); 17 | 18 | if (this.claimTarget.room) { 19 | if (!this.checkForActiveClaims(Memory.auxiliaryTargets)) { 20 | this.claimOperation(this.claimTarget); 21 | } 22 | } else { 23 | log.a(`No claim targets found out of ${this.worthyRooms.length} possible rooms.`, 'EXPANSION CONTROL:'); 24 | } 25 | } 26 | 27 | findClaimTarget() { 28 | if (this.claimTarget.room) { 29 | const targetIntel = INTEL[this.claimTarget.room]; 30 | if (!targetIntel || targetIntel.owner || targetIntel.reservation || this.claimTarget.tick + CREEP_LIFE_TIME < Game.time) { 31 | if (FORCE_CLAIM && (!INTEL[Memory.forceClaim] || !INTEL[Memory.forceClaim].owner)) { 32 | this.claimTarget.room = FORCE_CLAIM; 33 | this.claimTarget.tick = Game.time; 34 | Memory.claimTarget = this.claimTarget; 35 | return; 36 | } 37 | log.a(`Refreshing claim target. Old claim target - ${this.claimTarget.room}`, 'EXPANSION CONTROL:'); 38 | Memory.claimTarget = {}; 39 | this.claimTarget = {}; 40 | } else { 41 | return; // already have a valid target, proceed to claim operation 42 | } 43 | } 44 | 45 | if (FORCE_CLAIM && (!INTEL[Memory.forceClaim] || !INTEL[Memory.forceClaim].owner)) { 46 | this.claimTarget = {}; 47 | this.claimTarget.room = FORCE_CLAIM; 48 | this.claimTarget.tick = Game.time; 49 | Memory.claimTarget = this.claimTarget; 50 | return; 51 | } 52 | 53 | this.filterWorthyRooms(); 54 | if (!this.worthyRooms.length) return; 55 | 56 | // Prioritize rooms within the same sector 57 | const sameSectorRooms = this.worthyRooms.filter(room => myRoomInSectorCheck(room.name)); 58 | if (sameSectorRooms.length) { 59 | this.worthyRooms = sameSectorRooms; 60 | } 61 | 62 | this.scoreRooms(); 63 | const max = _.max(this.worthyRooms, 'claimValue'); 64 | this.claimTarget.room = max ? max.name : undefined; 65 | } 66 | 67 | filterWorthyRooms() { 68 | this.worthyRooms = Object.values(INTEL).filter(room => 69 | room.hubCheck && 70 | !room.owner && 71 | room.cached + 10000 > Game.time && 72 | (!room.noClaim || room.noClaim < Game.time) && 73 | !room.obstacles && 74 | (!room.reservation || room.reservation === MY_USERNAME) && 75 | this.checkNeighboringRooms(room.name) && 76 | findClosestOwnedRoom(room.name, true) <= 14 77 | ); 78 | } 79 | 80 | checkNeighboringRooms(roomName) { 81 | const neighboring = Object.values(Game.map.describeExits(roomName)); 82 | for (const neighbor of neighboring) { 83 | const intel = INTEL[neighbor]; 84 | if (!intel) return false; 85 | if (intel.owner) return false; 86 | } 87 | return true; 88 | } 89 | 90 | scoreRooms() { 91 | const friendlyRooms = Object.values(INTEL).filter(r => r.level && FRIENDLIES.includes(r.owner)); 92 | const enemyRooms = Object.values(INTEL).filter(r => r.level && HOSTILES.includes(r.owner)); 93 | 94 | for (const room of this.worthyRooms) { 95 | room.claimValue = this.calculateRoomScore(room, friendlyRooms, enemyRooms); 96 | } 97 | } 98 | 99 | calculateRoomScore(room, friendlyRooms, enemyRooms) { 100 | let score = 10000; 101 | 102 | // Penalize failed claim attempts 103 | if (room.failedClaim) { 104 | if (room.failedClaim >= 5) return undefined; 105 | score -= room.failedClaim * 1000; 106 | } 107 | 108 | // Adjust score based on proximity to friendly rooms 109 | for (const fRoom of friendlyRooms) { 110 | const distance = Game.map.findRoute(room.name, fRoom.name).length; 111 | if (distance <= 2) return undefined; // Too close to allies 112 | score += this.friendlyRoomScoreAdjustment(distance); 113 | if (AVOID_ALLIED_SECTORS && sameSectorCheck(room.name, fRoom.name)) score -= 500; 114 | } 115 | 116 | // Adjust score based on proximity to enemy rooms 117 | for (const eRoom of enemyRooms) { 118 | const distance = Math.min( 119 | Game.map.getRoomLinearDistance(room.name, eRoom.name), 120 | Game.map.findRoute(room.name, eRoom.name).length 121 | ); 122 | if (distance <= 3) score -= 10000 / distance; 123 | else if (distance < 6) score -= 250; 124 | } 125 | 126 | // Score based on remote source access 127 | const neighboring = Object.values(Game.map.describeExits(room.name)); 128 | const sourceCount = neighboring.reduce((sum, r) => { 129 | if (!INTEL[r]) return sum + 1; 130 | return INTEL[r].user ? sum : sum + (INTEL[r].sources || 0); 131 | }, 0); 132 | 133 | if (!sourceCount) return undefined; 134 | score += sourceCount * 250; 135 | 136 | // Penalize swamp terrain 137 | const terrain = Game.map.getRoomTerrain(room.name); 138 | for (let y = 0; y < 50; y++) { 139 | for (let x = 0; x < 50; x++) { 140 | if (terrain.get(x, y) === TERRAIN_MASK_SWAMP) score -= 10; 141 | } 142 | } 143 | 144 | // Score based on minerals 145 | if (!MY_MINERALS[room.mineral]) { 146 | score += this.getMineralBonus(room.mineral); 147 | } else { 148 | score *= 0.5; 149 | } 150 | 151 | // Prioritize rooms in the same sector 152 | if (myRoomInSectorCheck(room.name)) score += 7000; 153 | 154 | return score; 155 | } 156 | 157 | friendlyRoomScoreAdjustment(distance) { 158 | return distance === 3 ? 2000 : distance < 7 ? 100 : distance > 15 ? -Infinity : 1; 159 | } 160 | 161 | getMineralBonus(mineralType) { 162 | const bonusTable = { 163 | [RESOURCE_OXYGEN]: 1500, 164 | [RESOURCE_HYDROGEN]: 1500, 165 | [RESOURCE_LEMERGIUM]: 750, 166 | [RESOURCE_KEANIUM]: 500 167 | }; 168 | return bonusTable[mineralType] || 200; 169 | } 170 | 171 | claimOperation(claimTarget) { 172 | const roomName = claimTarget.room; 173 | const limit = roomStatus(MY_ROOMS[0]) === 'novice' ? 3 : Memory.cpuTracking.roomPenalty && Memory.cpuTracking.roomPenalty + 50000 > Game.time ? Game.gcl.level - 1 : Game.gcl.level; 174 | 175 | if (limit > MY_ROOMS.length && MAX_LEVEL >= 4 && !Memory.auxiliaryTargets[roomName]) { 176 | Memory.claimTarget = {}; 177 | Memory.auxiliaryTargets[roomName] = { 178 | tick: Game.time, 179 | type: 'claim', 180 | priority: 1 181 | }; 182 | log.a(`Claim Mission for ${roomLink(roomName)} initiated.`, 'EXPANSION CONTROL:'); 183 | } else if (!Memory.claimTarget || Memory.claimTarget.room !== roomName) { 184 | log.a(`Next claim target set to ${roomLink(roomName)} once available.`, 'EXPANSION CONTROL:'); 185 | Memory.claimTarget = {room: roomName, tick: Game.time}; 186 | } 187 | } 188 | 189 | checkForActiveClaims(auxiliaryTargets) { 190 | for (let key in auxiliaryTargets) { 191 | if (auxiliaryTargets.hasOwnProperty(key)) { 192 | if (auxiliaryTargets[key] && (auxiliaryTargets[key].type === 'rebuild' || auxiliaryTargets[key].type === 'claim')) { 193 | return true; 194 | } 195 | } 196 | } 197 | return false; 198 | } 199 | } 200 | 201 | profiler.registerClass(ExpansionControl, 'ExpansionControl'); 202 | module.exports = ExpansionControl; -------------------------------------------------------------------------------- /default/modules/module.factoryController.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | const profiler = require("tools.profiler"); 5 | let tickTracker = {}; 6 | let cooldownTracker = {}; 7 | 8 | class FactoryControl { 9 | constructor() { 10 | } 11 | 12 | run(room) { 13 | const factory = room.factory; 14 | if (!factory || room.nukes.length) return; 15 | 16 | const currentTime = Game.time; 17 | const lastRun = tickTracker[room.name] || 0; 18 | const coolDown = cooldownTracker[room.name] || 10; 19 | 20 | if (lastRun + coolDown > currentTime) return; 21 | tickTracker[room.name] = currentTime; 22 | 23 | if (factory.cooldown) { 24 | cooldownTracker[room.name] = factory.cooldown + 1; 25 | return; 26 | } 27 | 28 | const energyStored = room.store(RESOURCE_ENERGY, true); 29 | const factoryLevel = factory.level || (factory.effects && factory.effects.length ? 1 : 0); 30 | 31 | // Stop current production if conditions are met 32 | if (factory.memory.producing && this.shouldStopProduction(room, factory, energyStored)) { 33 | log.a('No longer producing ' + factory.memory.producing + ' in ' + roomLink(room.name), 'FACTORY CONTROL:'); 34 | delete factory.memory.producing; 35 | } 36 | 37 | // Decide on new production if not currently producing 38 | if (!factory.memory.producing) { 39 | this.decideProduction(room, factory, energyStored, factoryLevel); 40 | } 41 | 42 | // Attempt production only if we have a valid target 43 | if (factory.memory.producing && this.isValidProductionTarget(factory.memory.producing, room, factoryLevel)) { 44 | const result = factory.produce(factory.memory.producing); 45 | if (result === OK) { 46 | cooldownTracker[room.name] = COMMODITIES[factory.memory.producing].cooldown + 1; 47 | } else { 48 | cooldownTracker[room.name] = COMMODITIES[factory.memory.producing].cooldown * 0.5; 49 | } 50 | } else if (factory.memory.producing) { 51 | log.a('Clearing invalid production target ' + factory.memory.producing + ' in ' + roomLink(room.name), 'FACTORY CONTROL:'); 52 | delete factory.memory.producing; 53 | } 54 | } 55 | 56 | setProduction(factory, resource, logMessage) { 57 | factory.memory.producing = resource; 58 | log.a(logMessage + ' in ' + roomLink(factory.room.name), 'FACTORY CONTROL:'); 59 | } 60 | 61 | shouldStopProduction(room, factory, energyStored) { 62 | const producing = factory.memory.producing; 63 | const commodity = COMMODITIES[producing]; 64 | const batteryStored = room.store(RESOURCE_BATTERY); 65 | 66 | if (producing === RESOURCE_BATTERY && room.energyState < 2) return true; 67 | else if (producing === RESOURCE_ENERGY) { 68 | return (room.energyState > 1 || batteryStored < 50); 69 | } else if (producing !== RESOURCE_ENERGY && producing !== RESOURCE_BATTERY && !COMPRESSED_COMMODITIES.includes(producing)) { 70 | let productionThreshold = BASE_MINERALS.includes(producing) ? REACTION_AMOUNT : DUMP_AMOUNT * 0.9; 71 | if (room.store(producing) >= productionThreshold) return true; 72 | } else if (COMPRESSED_COMMODITIES.includes(producing) && 73 | Object.keys(commodity.components).some(resource => 74 | resource !== RESOURCE_ENERGY && room.store(resource) < REACTION_AMOUNT * 0.5 75 | )) return true; 76 | else if (commodity.components[RESOURCE_ENERGY] && !room.energyState) return true; 77 | 78 | return false; 79 | } 80 | 81 | decideProduction(room, factory, energyStored, factoryLevel) { 82 | const batteryStored = room.store(RESOURCE_BATTERY); 83 | 84 | // Priority: Convert batteries to energy if energy is low but batteries are available 85 | if (!room.energyState && batteryStored >= 50) { 86 | this.setProduction(factory, RESOURCE_ENERGY, 'Converting Battery to Energy'); 87 | return; 88 | } 89 | 90 | // Produce Battery if there's excess energy and we're low on room 91 | if (room.energyState > 1 && !this.checkStorageSpace(room)) { 92 | this.setProduction(factory, RESOURCE_BATTERY, 'Producing Battery'); 93 | return; 94 | } 95 | 96 | // Try to make stuff 97 | const resources = shuffle([...BASE_MINERALS, ...MANUFACTURED_COMMODITIES]); 98 | for (let resource of resources) { 99 | if (this.isValidProductionTarget(resource, room, factoryLevel) && ![RESOURCE_BATTERY, RESOURCE_ENERGY].includes(resource)) { 100 | this.setProduction(factory, resource, 'Producing ' + resource); 101 | return; 102 | } 103 | } 104 | 105 | // Compress stuff 106 | let compressedResources = shuffle([COMPRESSED_COMMODITIES]); 107 | for (let resource of compressedResources) { 108 | if (this.isValidProductionTarget(resource, room, factoryLevel) && ![RESOURCE_BATTERY, RESOURCE_ENERGY].includes(resource)) { 109 | this.setProduction(factory, resource, 'Producing ' + resource); 110 | return; 111 | } 112 | } 113 | } 114 | 115 | isValidProductionTarget(resource, room, factoryLevel) { 116 | const commodity = COMMODITIES[resource]; 117 | if (!commodity) return false; // Commodity doesn't exist 118 | if (commodity.level && commodity.level !== factoryLevel) return false; // Factory level mismatch 119 | 120 | // Always true for energy and battery as its production is handled separately 121 | if ([RESOURCE_ENERGY, RESOURCE_BATTERY].includes(resource)) return true; 122 | 123 | // Check if we should produce based on current storage levels 124 | let productionThreshold = BASE_MINERALS.includes(resource) ? REACTION_AMOUNT * 0.25 : DUMP_AMOUNT * 0.9; 125 | 126 | if (room.store(resource) >= productionThreshold) return false; // Already have enough 127 | 128 | // Ensure all components are available in sufficient quantity 129 | return Object.keys(commodity.components).every(component => { 130 | let requiredAmount = commodity.components[component]; 131 | // Special handling for energy component if producing energy from batteries 132 | if (resource === RESOURCE_ENERGY && component === RESOURCE_BATTERY) { 133 | requiredAmount = 50; // Assuming 50 batteries are needed to produce energy 134 | } 135 | return room.store(component) >= requiredAmount && 136 | (!COMPRESSED_COMMODITIES.includes(resource) || room.store(component) >= REACTION_AMOUNT * 1.1); 137 | }); 138 | } 139 | 140 | checkStorageSpace(room) { 141 | return !(room.storage && room.storage.store.getFreeCapacity() < STORAGE_CAPACITY * 0.1); 142 | } 143 | } 144 | 145 | profiler.registerClass(FactoryControl, 'FactoryControl'); 146 | module.exports = FactoryControl; -------------------------------------------------------------------------------- /default/modules/module.hud.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const profiler = require("tools.profiler"); 6 | 7 | class HUD { 8 | constructor() { 9 | if (Memory.HUD) this.hudData = Memory.HUD; 10 | else this.hudData = Memory.HUD = { 11 | ...(Memory.HUD || {}), 12 | GCL: {last: Game.gcl.progress, progress: []}, 13 | RCL: {} 14 | }; 15 | } 16 | 17 | run() { 18 | if (!Memory.tickInfo) return; 19 | 20 | for (const room of this.getOwnedRooms()) { 21 | this.updateData(room); 22 | this.renderDashboard(room); 23 | } 24 | } 25 | 26 | getOwnedRooms() { 27 | return Object.values(Game.rooms).filter(r => r.controller && r.controller.my); 28 | } 29 | 30 | updateData(room) { 31 | this.updateGCLData(); 32 | this.updateRCLData(room); 33 | } 34 | 35 | updateGCLData() { 36 | const currentProgress = Game.gcl.progress; 37 | if (currentProgress > this.hudData.GCL.last) { 38 | this.hudData.GCL.progress.push(currentProgress - this.hudData.GCL.last); 39 | if (this.hudData.GCL.progress.length > 25) this.hudData.GCL.progress.shift(); 40 | this.hudData.GCL.last = currentProgress; 41 | } 42 | } 43 | 44 | updateRCLData(room) { 45 | if (!room.controller.progressTotal) return; 46 | const currentProgress = room.controller.progress; 47 | this.hudData.RCL[room.name] = this.hudData.RCL[room.name] || {last: currentProgress, progress: []}; 48 | if (currentProgress > this.hudData.RCL[room.name].last) { 49 | this.hudData.RCL[room.name].progress.push(currentProgress - this.hudData.RCL[room.name].last); 50 | if (this.hudData.RCL[room.name].progress.length > 25) this.hudData.RCL[room.name].progress.shift(); 51 | this.hudData.RCL[room.name].last = currentProgress; 52 | } 53 | } 54 | 55 | average(arr) { 56 | if (!arr) return 0; 57 | return arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : 0; 58 | } 59 | 60 | renderDashboard(room) { 61 | const x = 1; 62 | let y = 1; 63 | 64 | y = this.renderProgressDots(room, x, y, 'GCL', this.getGCLInfo(), '#00B7EB'); 65 | if (room.level < 8) { 66 | y = this.renderProgressDots(room, x, y, 'RCL', this.getRCLInfo(room), '#7D3C98'); 67 | } 68 | y = this.renderStatusIcon(room, x, y); 69 | y = this.renderEnergyInfo(room, x, y); 70 | } 71 | 72 | getGCLInfo() { 73 | const avg = this.average(this.hudData.GCL.progress); 74 | const remaining = (Game.gcl.progressTotal - Game.gcl.progress) / avg * Memory.tickInfo.tickLength; 75 | return { 76 | level: Game.gcl.level, 77 | progress: (Game.gcl.progress / Game.gcl.progressTotal) * 100, 78 | time: this.timeFormat(remaining) 79 | }; 80 | } 81 | 82 | getRCLInfo(room) { 83 | const rclData = this.hudData.RCL[room.name] || {progress: []}; 84 | const avg = this.average(rclData.progress); 85 | const remaining = (room.controller.progressTotal - room.controller.progress) / avg * Memory.tickInfo.tickLength; 86 | return { 87 | level: room.controller.level, 88 | progress: (room.controller.progress / room.controller.progressTotal) * 100, 89 | time: this.timeFormat(remaining), 90 | cpu: this.average(ROOM_CPU_ARRAY[room.name]).toFixed(2) 91 | }; 92 | } 93 | 94 | renderProgressDots(room, x, y, label, info, color) { 95 | const dots = Math.round(info.progress / 10); // 10% per dot 96 | for (let i = 0; i < 10; i++) { 97 | room.visual.circle(x + i, y, { 98 | radius: 0.2, 99 | fill: i < dots ? color : '#333333', 100 | opacity: 1 101 | }); 102 | } 103 | room.visual.text(`${label}: ${info.level} (${info.time})`, x + 10, y, { 104 | color: color, 105 | align: 'left', 106 | font: '0.5 Tahoma', 107 | opacity: 1 108 | }); 109 | return y + 1; 110 | } 111 | 112 | renderStatusIcon(room, x, y) { 113 | if (room.controller.safeMode) { 114 | room.visual.text('⏳', x, y, {color: '#FF4500', align: 'left', font: '0.5 Tahoma'}); 115 | room.visual.text(`${this.timeFormat(room.controller.safeMode * Memory.tickInfo.tickLength)}`, x + 1, y, { 116 | color: '#FF4500', 117 | align: 'left', 118 | font: '0.5 Tahoma' 119 | }); 120 | } else if (INTEL[room.name] && INTEL[room.name].threatLevel) { 121 | room.visual.text('⚔️', x, y, {color: '#FF0000', align: 'left', font: '0.5 Tahoma'}); 122 | room.visual.text(`Level ${INTEL[room.name].threatLevel}`, x + 1, y, { 123 | color: '#FF0000', 124 | align: 'left', 125 | font: '0.5 Tahoma' 126 | }); 127 | } 128 | return y + 1; 129 | } 130 | 131 | renderEnergyInfo(room, x, y) { 132 | const storage = room.storage ? room.storage.store[RESOURCE_ENERGY] : 0; 133 | const terminal = room.terminal ? room.terminal.store[RESOURCE_ENERGY] : 0; 134 | room.visual.text(`⚡ ${storage + terminal}`, x, y, {color: '#FFD700', align: 'left', font: '0.5 Tahoma'}); 135 | return y + 1; 136 | } 137 | 138 | timeFormat(seconds) { 139 | if (seconds === Infinity || seconds < 0) return 'Calculating...'; 140 | const [h, m, s] = [Math.floor(seconds / 3600), Math.floor((seconds % 3600) / 60), Math.floor(seconds % 60)]; 141 | return `${h}h ${m}m ${s}s`.replace(/\b0\w+\s*/g, ''); 142 | } 143 | } 144 | 145 | profiler.registerClass(HUD, 'HUD'); 146 | module.exports = HUD; -------------------------------------------------------------------------------- /default/modules/module.labController.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | const profiler = require("tools.profiler"); 5 | const runNext = {}; 6 | const lastClean = {}; 7 | const goOverCap = {}; 8 | const productionTracker = {}; 9 | 10 | class LabManager { 11 | constructor(room) { 12 | this.primaryLabs = {}; 13 | this.room = room; 14 | this.hub = this.getLabHub(room); 15 | } 16 | 17 | run(room) { 18 | const labs = room.structures.filter(s => s.structureType === STRUCTURE_LAB); 19 | if (!labs.length) return; 20 | if (!runNext[room.name] || runNext[room.name] < Game.time) { 21 | this.manageBoostProduction(room); 22 | this.manageActiveLabs(room); 23 | if (!runNext[room.name] || runNext[room.name] < Game.time) runNext[room.name] = Game.time + 15; 24 | } 25 | } 26 | 27 | manageBoostProduction(room) { 28 | if (room.memory.producingBoost) return; 29 | let hub = this.getLabHub(room); 30 | if (!hub) return; 31 | let secondaryLabs = room.impassibleStructures.filter(lab => 32 | lab.structureType === STRUCTURE_LAB && !this.primaryLabs[room.name].includes(lab.id) 33 | ); 34 | if (secondaryLabs.length < 1 || hub.length < 2) return; 35 | let boost = this.findBoostToProduce(room, secondaryLabs); 36 | if (!boost) return; 37 | 38 | this.setupProduction(hub, boost, room); 39 | } 40 | 41 | manageActiveLabs(room) { 42 | if (!room.memory.producingBoost || !this.hub) return; 43 | 44 | // Sanity check for broken hub 45 | for (const lab of this.hub) { 46 | if (!lab.memory.itemNeeded) return this.stopProduction(room); 47 | } 48 | 49 | // Visual feedback on what's being produced 50 | this.hub[0].say(room.memory.producingBoost); 51 | 52 | let secondaryLabs = room.impassibleStructures.filter(lab => 53 | !lab.cooldown && lab.structureType === STRUCTURE_LAB && 54 | !this.primaryLabs[room.name].includes(lab.id) && 55 | (!lab.memory.paused || lab.memory.neededBoost === room.memory.producingBoost) && 56 | (!lab.memory.neededBoost || lab.memory.neededBoost === room.memory.producingBoost) && 57 | (!lab.mineralType || lab.mineralType === room.memory.producingBoost) 58 | ); 59 | 60 | for (let target of secondaryLabs) { 61 | let result = target.runReaction(this.hub[0], this.hub[1]); 62 | if (result === OK) { 63 | this.shouldStopProduction(room); 64 | const coolDown = Game.time + REACTION_TIME[room.memory.producingBoost] - 1; 65 | if (!runNext[room.name] || runNext[room.name] > coolDown || runNext[room.name] <= Game.time) { 66 | runNext[room.name] = coolDown; 67 | } 68 | if (!productionTracker[this.room.name]) productionTracker[this.room.name] = Game.time; 69 | } else if (result === ERR_NOT_ENOUGH_RESOURCES) { 70 | this.shouldStopProduction(room); 71 | } 72 | } 73 | } 74 | 75 | shouldStopProduction(room) { 76 | if (room.store(room.memory.producingBoost) > this.getProductionCutoff(room.memory.producingBoost)) { 77 | this.stopProduction(room, 'Boost cap reached.'); 78 | } else if (productionTracker[this.room.name] && productionTracker[this.room.name] + CREEP_LIFE_TIME * 3 < Game.time) { 79 | this.stopProduction(room, 'Production time exceeded.'); 80 | } else if (this.hub.some(lab => room.store(lab.memory.itemNeeded) < 50)) { 81 | this.stopProduction(room, 'Not enough resources.'); 82 | } 83 | } 84 | 85 | stopProduction(room, message) { 86 | if (productionTracker[this.room.name]) log.a(`${roomLink(room.name)} is halting production of ${room.memory.producingBoost}. ${message || ''}`); 87 | room.memory.producingBoost = undefined; 88 | this.primaryLabs[room.name] = undefined; 89 | goOverCap[this.room.name] = undefined; 90 | this.hub.forEach(lab => lab.memory = undefined); 91 | } 92 | 93 | findBoostToProduce(room) { 94 | const priority = this.tryPriority(room); 95 | if (priority) return priority; 96 | let boostList = [...new Set([...BASE_COMPOUNDS, ...TIER_3_BOOSTS, ...TIER_2_BOOSTS, ...TIER_1_BOOSTS])]; 97 | for (const boost of shuffle(boostList)) { 98 | let cutOff = this.getProductionCutoff(boost); 99 | if (room.store(boost) >= cutOff) continue; 100 | if (this.checkForInputs(room, boost)) { 101 | return boost; 102 | } 103 | } 104 | goOverCap[room.name] = true; 105 | return null; 106 | } 107 | 108 | tryPriority(room) { 109 | const priority = !HOSTILES.length ? LAB_PEACE_PRIORITY : LAB_WAR_PRIORITY; 110 | for (let boost of priority) { 111 | let cutOff = this.getProductionCutoff(boost); 112 | if (getResourceTotal(boost) >= cutOff) continue; 113 | if (this.checkForInputs(room)) { 114 | return boost; 115 | } else { 116 | const components = BOOST_COMPONENTS[boost]; 117 | if (!components || !components.length) continue; 118 | for (boost of components) { 119 | let cutOff = this.getProductionCutoff(boost); 120 | if (room.store(boost) >= cutOff) continue; 121 | if (this.checkForInputs(room, boost)) { 122 | return boost; 123 | } else { 124 | const components = BOOST_COMPONENTS[boost]; 125 | if (!components || !components.length) continue; 126 | for (boost of components) { 127 | let cutOff = this.getProductionCutoff(boost); 128 | if (room.store(boost) >= cutOff) continue; 129 | if (this.checkForInputs(room, boost)) { 130 | return boost; 131 | } else { 132 | const components = BOOST_COMPONENTS[boost]; 133 | if (!components || !components.length) continue; 134 | for (boost of components) { 135 | let cutOff = this.getProductionCutoff(boost); 136 | if (room.store(boost) >= cutOff) continue; 137 | if (this.checkForInputs(room, boost)) { 138 | return boost; 139 | } 140 | } 141 | } 142 | } 143 | } 144 | } 145 | } 146 | } 147 | } 148 | 149 | getProductionCutoff(boost) { 150 | if (boost === RESOURCE_GHODIUM) { 151 | return (NUKER_GHODIUM_CAPACITY * 2.5) + (SAFE_MODE_COST * 1.5); 152 | } else if (goOverCap[this.room.name]) { 153 | return BOOST_AMOUNT(this.room) * 10; 154 | } else if (LAB_PEACE_PRIORITY.includes(boost) || LAB_WAR_PRIORITY.includes(boost)) { 155 | return BOOST_AMOUNT(this.room) * 2; 156 | } 157 | return BOOST_AMOUNT(this.room); 158 | } 159 | 160 | checkForInputs(room, boost) { 161 | let components = BOOST_COMPONENTS[boost]; 162 | if (!components || components.length === 0) return false; 163 | return components.every(input => room.store(input) >= 50 * room.level); 164 | } 165 | 166 | setupProduction(hub, boost, room) { 167 | hub.forEach((lab, i) => { 168 | lab.memory = { 169 | itemNeeded: BOOST_COMPONENTS[boost][i], 170 | room: room.name 171 | }; 172 | }); 173 | room.memory.producingBoost = boost; 174 | productionTracker[this.room.name] = Game.time; 175 | log.a(roomLink(room.name) + ' queued ' + boost + ' for creation.'); 176 | } 177 | 178 | cleanLabs(labs) { 179 | labs.forEach(lab => { 180 | if (lab.memory.neededBoost) { 181 | if (!lab.memory.requested || lab.memory.requested + 150 < Game.time || !Game.getObjectById(lab.memory.requestor)) { 182 | lab.memory = undefined; 183 | } 184 | } 185 | }); 186 | } 187 | 188 | getLabHub(room) { 189 | if (!this.primaryLabs[room.name]) { 190 | if (!room.memory.labHub) return; 191 | let labHub = new RoomPosition(room.memory.labHub.x, room.memory.labHub.y, room.name); 192 | // Clear a bad hub if we have labs but not at the hub 193 | const labs = room.impassibleStructures.filter((s) => s.my && s.structureType === STRUCTURE_LAB); 194 | const hubLabs = labs.filter(lab => 195 | lab.structureType === STRUCTURE_LAB && 196 | ((lab.pos.x === labHub.x && lab.pos.y === labHub.y) || 197 | (lab.pos.x === labHub.x && lab.pos.y === labHub.y + 1)) 198 | ); 199 | if (labs.length && !hubLabs.length) { 200 | return room.memory.labHub = undefined; 201 | } else if (hubLabs.length) { 202 | this.primaryLabs[room.name] = hubLabs.map(lab => lab.id); 203 | } 204 | } 205 | if (this.primaryLabs[room.name]) return this.primaryLabs[room.name].map(id => Game.getObjectById(id)); 206 | } 207 | } 208 | 209 | profiler.registerClass(LabManager, 'LabManager'); 210 | module.exports = LabManager; -------------------------------------------------------------------------------- /default/modules/module.linkController.js: -------------------------------------------------------------------------------- 1 | const profiler = require("tools.profiler"); 2 | 3 | class LinkControl { 4 | constructor() { 5 | } 6 | 7 | run(room) { 8 | // Get all available links with conditions inlined 9 | let links = room.impassibleStructures.filter(s => 10 | s.structureType === STRUCTURE_LINK && 11 | !s.cooldown && 12 | s.store[RESOURCE_ENERGY] 13 | ).sort(() => Math.random() - 0.5); // Shuffle array 14 | 15 | if (!links.length) return; 16 | 17 | let hubLink = Game.getObjectById(room.memory.hubLink); 18 | let controllerLink = Game.getObjectById(room.memory.controllerLink); 19 | 20 | // Set controller link if not already set 21 | if (!controllerLink || !(controllerLink instanceof StructureLink)) this.setControllerLink(room, links, controllerLink); 22 | 23 | // Ensure hub link is valid or delete from memory if not 24 | if (!hubLink || !(hubLink instanceof StructureLink)) delete room.memory.hubLink; 25 | 26 | // Process links and handle energy transfer 27 | links.forEach(link => this.processLink(link, room, hubLink, controllerLink)); 28 | } 29 | 30 | setControllerLink(room, links, controllerLink) { 31 | controllerLink = links.find(s => s.pos.findInRange(room.structures, 2, 32 | {filter: f => f.structureType === STRUCTURE_CONTROLLER} 33 | )[0]); 34 | if (controllerLink) room.memory.controllerLink = controllerLink.id; 35 | } 36 | 37 | processLink(link, room, hubLink, controllerLink) { 38 | //if (link.id === room.memory.hubLink && link.room.energyAvailable !== link.room.energyCapacityAvailable) return; 39 | 40 | // Controller link only shares if room is under attack 41 | if (link.id === room.memory.controllerLink && !room.memory.dangerousAttack) return; 42 | 43 | const upgrader = room.creeps.find(c => c.memory && c.memory.role === 'upgrader' && c.memory.inPosition); 44 | 45 | // Simplified energy transfer logic 46 | if (upgrader && controllerLink && controllerLink.store[RESOURCE_ENERGY] < LINK_CAPACITY * 0.5) { 47 | link.transferEnergy(controllerLink); 48 | } else { 49 | if (hubLink && !hubLink.room.energyState) { 50 | link.transferEnergy(hubLink); 51 | } else if (hubLink && hubLink.store[RESOURCE_ENERGY] < 400) { 52 | link.transferEnergy(hubLink); 53 | } else if (controllerLink && controllerLink.store[RESOURCE_ENERGY] < LINK_CAPACITY * 0.7) { 54 | link.transferEnergy(controllerLink); 55 | } else if (hubLink && hubLink.store[RESOURCE_ENERGY] < LINK_CAPACITY) { 56 | link.transferEnergy(hubLink); 57 | } 58 | } 59 | } 60 | } 61 | 62 | profiler.registerClass(LinkControl, 'LinkControl'); 63 | module.exports = LinkControl; -------------------------------------------------------------------------------- /default/modules/module.observerController.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | const profiler = require("tools.profiler"); 5 | let observedRooms = {}; 6 | 7 | class ObserverControl { 8 | constructor() { 9 | } 10 | 11 | run(room) { 12 | const observer = room.impassibleStructures.find(s => s.structureType === STRUCTURE_OBSERVER); 13 | if (!observer) return; 14 | 15 | const roomName = room.name; 16 | const currentTime = Game.time; 17 | 18 | // Handle the actual observing 19 | if (observedRooms[roomName]) observer.operationPlanner(Game.rooms[observedRooms[roomName]]); 20 | observedRooms[roomName] = undefined; 21 | 22 | // Handle manual observation 23 | if (this.handleManualObservation(roomName, observer, currentTime)) return; 24 | 25 | // Select target for observation 26 | const targetRoom = this.selectObservationTarget(roomName, currentTime); 27 | 28 | // Observe if a target is selected 29 | if (targetRoom) { 30 | this.observeRoom(observer, roomName, targetRoom, currentTime); 31 | } 32 | } 33 | 34 | handleManualObservation(roomName, observer, currentTime) { 35 | if (Memory.observeRoom && Memory.observeRoom === observedRooms[roomName] && Game.rooms[Memory.observeRoom]) { 36 | if (Memory.observeRoom === observedRooms[roomName]) { 37 | log.a(`${roomName} is done observing ${Memory.observeRoom} and will now observe randomly.`); 38 | Memory.observeRoom = undefined; 39 | observedRooms[roomName] = undefined; 40 | } else { 41 | observedRooms[roomName] = Memory.observeRoom; 42 | return true; 43 | } 44 | } 45 | return false; 46 | } 47 | 48 | selectObservationTarget(roomName, currentTime) { 49 | return this.findStrategicTarget(roomName, currentTime) || this.findRandomTarget(roomName, currentTime); 50 | } 51 | 52 | findStrategicTarget(roomName, currentTime) { 53 | return; 54 | return Object.keys(Memory.targetRooms).find(room => 55 | Memory.targetRooms[room] && (Memory.targetRooms[room].type === 'scout' || 56 | (!INTEL[room].lastObservation || INTEL[room].lastObservation + 50 < currentTime)) && 57 | Game.map.getRoomLinearDistance(roomName, room) <= OBSERVER_RANGE 58 | ); 59 | } 60 | 61 | findRandomTarget(roomName, currentTime) { 62 | // Parse base coordinates 63 | const [, eW, xStr, nS, yStr] = roomName.match(/^([EW])(\d+)([NS])(\d+)$/); 64 | const baseX = (eW === 'W' ? -1 : 1) * (xStr | 0); 65 | const baseY = (nS === 'N' ? -1 : 1) * (yStr | 0); 66 | const RANGE = OBSERVER_RANGE; 67 | const directions = [ 68 | // Ring 1 (distance 1) 69 | [1, 0], [0, 1], [-1, 0], [0, -1], // Cardinals 70 | [1, 1], [-1, 1], [-1, -1], [1, -1], // Diagonals 71 | // Ring 2 (distance 2) 72 | [2, 0], [0, 2], [-2, 0], [0, -2], // Cardinals 73 | [2, 1], [1, 2], [-1, 2], [-2, 1], // Primary diagonals 74 | [2, -1], [1, -2], [-1, -2], [-2, -1], // Secondary diagonals 75 | // Ring 3 (distance 3) 76 | [3, 0], [0, 3], [-3, 0], [0, -3], // Cardinals 77 | [3, 1], [1, 3], [-1, 3], [-3, 1], // Primary diagonals 78 | [3, -1], [1, -3], [-1, -3], [-3, -1], // Secondary diagonals 79 | [2, 2], [-2, 2], [-2, -2], [2, -2], // Corner diagonals 80 | // Ring 4 (distance 4) 81 | [4, 0], [0, 4], [-4, 0], [0, -4], // Cardinals 82 | [4, 1], [1, 4], [-1, 4], [-4, 1], // Primary diagonals 83 | [4, -1], [1, -4], [-1, -4], [-4, -1], // Secondary diagonals 84 | // Ring 5 (distance 5) 85 | [5, 0], [0, 5], [-5, 0], [0, -5], // Cardinals 86 | [5, 1], [1, 5], [-1, 5], [-5, 1], // Primary diagonals 87 | [5, -1], [1, -5], [-1, -5], [-5, -1], // Secondary diagonals 88 | [4, 2], [2, 4], [-2, 4], [-4, 2], // Intermediate diagonals 89 | [4, -2], [2, -4], [-2, -4], [-4, -2], // Intermediate diagonals 90 | [3, 3], [-3, 3], [-3, -3], [3, -3], // Corner diagonals 91 | // Ring 6 (distance 6) 92 | [6, 0], [0, 6], [-6, 0], [0, -6], // Cardinals 93 | [6, 1], [1, 6], [-1, 6], [-6, 1], // Primary diagonals 94 | [6, -1], [1, -6], [-1, -6], [-6, -1], // Secondary diagonals 95 | [5, 2], [2, 5], [-2, 5], [-5, 2], // Intermediate diagonals 96 | [5, -2], [2, -5], [-2, -5], [-5, -2] // Intermediate diagonals 97 | ]; 98 | for (let [dx, dy] of directions) { 99 | if (Math.abs(dx) > RANGE || Math.abs(dy) > RANGE) continue; 100 | if (dx * dx + dy * dy > RANGE * RANGE) continue; 101 | const newX = baseX + dx; 102 | const newY = baseY + dy; 103 | const target = `${newX >= 0 ? 'E' : 'W'}${Math.abs(newX)}${newY >= 0 ? 'S' : 'N'}${Math.abs(newY)}`; 104 | if ((!INTEL[target] || INTEL[target].lastObservation + 50 <= currentTime) && 105 | roomStatus(target) !== 'closed') { 106 | return target; 107 | } 108 | } 109 | return null; 110 | } 111 | 112 | observeRoom(observer, roomName, targetRoom, currentTime) { 113 | observer.observeRoom(targetRoom); 114 | observedRooms[roomName] = targetRoom; 115 | } 116 | } 117 | 118 | profiler.registerClass(ObserverControl, 'ObserverControl'); 119 | module.exports = ObserverControl; -------------------------------------------------------------------------------- /default/modules/module.pixelFarm.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | module.exports.farm = function (room) { 6 | room.cacheRoomIntel(); 7 | // If no drone make one, otherwise run drone 8 | let drone = _.find(room.myCreeps, (c) => c.memory.role === 'drone'); 9 | if (!drone) { 10 | let spawn = _.find(room.impassibleStructures, (s) => s.structureType === STRUCTURE_SPAWN); 11 | if (spawn) spawn.createCreep([WORK, CARRY, MOVE], 'drone' + getRandomInt(1, 99), {role: 'drone', other: {}}); 12 | } else { 13 | require('role.drone').role(drone); 14 | } 15 | // Generate pixels 16 | if (Game.cpu.bucket === BUCKET_MAX) { 17 | log.a('Pixel Generated'); 18 | Game.cpu.generatePixel(); 19 | } 20 | }; -------------------------------------------------------------------------------- /default/modules/module.powerManager.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | /** 6 | * Created by rober on 5/16/2017. 7 | */ 8 | 9 | module.exports.powerControl = function () { 10 | let powerSpawns = _.filter(Game.structures, (s) => s.structureType === STRUCTURE_POWER_SPAWN && s.store[RESOURCE_POWER] && s.store[RESOURCE_ENERGY] >= 50); 11 | if (powerSpawns.length) { 12 | for (let powerSpawn of powerSpawns) { 13 | powerSpawn.processPower(); 14 | } 15 | } 16 | // Handle PC spawning 17 | if (Game.gpl.level) { 18 | let sparePowerLevels = Game.gpl.level - (_.size(Game.powerCreeps) + _.sum(Game.powerCreeps, 'level')); 19 | let myRooms = _.filter(Game.rooms, (r) => r.energyAvailable && r.controller.owner && r.controller.owner.username === MY_USERNAME && r.controller.level >= 7); 20 | let lowestOperator = _.min(Game.powerCreeps, 'level'); 21 | if (sparePowerLevels > 1 && _.size(Game.powerCreeps) < myRooms.length && (!lowestOperator.id || lowestOperator.level >= 11)) { 22 | let name = 'operator_' + _.random(1, 999); 23 | log.a('Created an operator named ' + name); 24 | PowerCreep.create(name, POWER_CLASS.OPERATOR); 25 | } else if (_.size(Game.powerCreeps)) { 26 | let powerCreeps = _.filter(Game.powerCreeps, (c) => c.my); 27 | for (let powerCreep of powerCreeps) { 28 | if (powerCreep.ticksToLive) { 29 | const powerCreepRole = require('powerRole.' + powerCreep.className); 30 | try { 31 | // Handle suicide 32 | if (!powerCreep.level && sparePowerLevels <= 0) { 33 | powerCreep.suicide(); 34 | continue; 35 | } 36 | // If idle sleep 37 | if (powerCreep.idle) continue; 38 | // Handle nuke flee 39 | if (powerCreep.memory.fleeNukeTime && powerCreep.fleeNukeRoom()) return; 40 | powerCreepRole.role(powerCreep); 41 | } catch (e) { 42 | log.e(powerCreepRole.name + ' in room ' + powerCreep.room.name + ' experienced an error'); 43 | log.e(e.stack); 44 | Game.notify(e.stack); 45 | } 46 | } else if (!powerCreep.deleteTime) { 47 | // Handle deleting 48 | if (!powerCreep.level && (sparePowerLevels <= 1 || (lowestOperator.id && lowestOperator.id !== powerCreep.id && lowestOperator.level < 11 && sparePowerLevels <= 11))) { 49 | powerCreep.delete(); 50 | } else if (!powerCreep.spawnCooldownTime || powerCreep.spawnCooldownTime < Date.now()) { 51 | let spawn = _.find(Game.structures, (s) => s.my && s.structureType === STRUCTURE_POWER_SPAWN && s.isActive()); 52 | if (spawn) { 53 | log.a('Spawned an operator in ' + roomLink(spawn.room.name)); 54 | powerCreep.spawn(spawn); 55 | } 56 | } 57 | } 58 | } 59 | } 60 | } 61 | }; 62 | 63 | -------------------------------------------------------------------------------- /default/modules/module.stateManager.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const profiler = require("tools.profiler"); 6 | const LAST_UPDATE = {}; 7 | const ENERGY_TRACKER = {}; 8 | 9 | class StateManager { 10 | constructor() { 11 | this.myRooms = MY_ROOMS; 12 | } 13 | 14 | run() { 15 | // Run every 10 ticks 16 | const lastRun = LAST_UPDATE.tick || 0; 17 | if (lastRun + 10 > Game.time) return; 18 | LAST_UPDATE.tick = Game.time; 19 | 20 | this.myRooms.forEach(room => this.roomTracking(room)); 21 | } 22 | 23 | roomTracking(room) { 24 | room = Game.rooms[room]; 25 | 26 | // Track energy 27 | this.energyTracking(room); 28 | 29 | // Track leveling stats 30 | this.levelingStatTracking(room); 31 | 32 | // Request builders only if certain conditions are met 33 | this.requestBuilders(room); 34 | 35 | // Funnel requests 36 | this.funnelRequest(room); 37 | 38 | // Barrier tracking 39 | this.barrierTracking(room); 40 | 41 | room.memory.stateInformation = undefined; 42 | } 43 | 44 | energyTracking(room) { 45 | // Energy tracking 46 | const lastEnergy = room.memory.lastEnergyAmount || 0; 47 | room.memory.lastEnergyAmount = room.energy; 48 | const energyIncomeArray = ROOM_ENERGY_INCOME_ARRAY[room.name] || []; 49 | energyIncomeArray.push(room.energy - lastEnergy); 50 | 51 | if (energyIncomeArray.length > 50) { 52 | energyIncomeArray.shift(); 53 | } 54 | 55 | ROOM_ENERGY_INCOME_ARRAY[room.name] = energyIncomeArray; 56 | 57 | // Track if the room is filling extensions/spawns fast enough 58 | if (room.energyCapacityAvailable > room.energyAvailable) { 59 | if (ENERGY_TRACKER[room.name]) ENERGY_TRACKER[room.name]++; else ENERGY_TRACKER[room.name] = 1; 60 | } else if (ENERGY_TRACKER[room.name] > 0) ENERGY_TRACKER[room.name]--; 61 | room.memory.needsHaulers = ENERGY_TRACKER[room.name] > 10; 62 | 63 | // Track projected income based off harvester count. 10 for in room and 8 for remote 64 | const harvesters = room.myCreeps.filter((c) => c.memory.role === 'stationaryHarvester').concat(_.filter(Game.creeps, ((c) => c.my && c.memory.colony === room.name && c.memory.role === 'remoteHarvester' && c.memory.other && c.memory.other.haulingRequired))); 65 | const income = Math.floor(harvesters.reduce((sum, creep) => sum + (creep.getActiveBodyparts(WORK) * 0.8), 0)); 66 | const energyUsers = room.myCreeps.filter((c) => ['drone', 'upgrader'].includes(c.memory.role)); 67 | const expense = Math.ceil(energyUsers.reduce((sum, creep) => sum + creep.getActiveBodyparts(WORK), 0)); 68 | const spareIncome = room.energyState > 2 ? 9999999 : room.energyState < 2 ? (income - expense) * 0.5 : income - expense; 69 | room.memory.energyInfo = {income: income, expense: expense, spareIncome: spareIncome}; 70 | room.memory.energyPositive = (average(energyIncomeArray) > 0 && income > expense) || room.energyState > 1; 71 | 72 | if (!room.memory.combatReady && room.energyState > 1) room.memory.combatReady = true; 73 | else if (room.memory.combatReady && !room.energyState) room.memory.combatReady = undefined; 74 | } 75 | 76 | levelingStatTracking(room) { 77 | // Stats tracking 78 | let stats = room.memory.stats || {}; 79 | stats.levelInfo = stats.levelInfo || {}; 80 | stats.levelInfo[room.controller.level] = stats.levelInfo[room.controller.level] || Game.time; 81 | 82 | stats.highestRCL = Math.max(stats.highestRCL || 0, room.controller.level); 83 | 84 | room.memory.stats = stats; 85 | } 86 | 87 | requestBuilders(room) { 88 | const impassibleStructures = room.impassibleStructures || []; 89 | let hasSpawn = false; 90 | let hasTower = false; 91 | 92 | // Iterate once over the impassible structures and check for spawns and towers 93 | for (let structure of impassibleStructures) { 94 | if (structure.structureType === STRUCTURE_SPAWN) { 95 | hasSpawn = true; 96 | } else if (structure.structureType === STRUCTURE_TOWER) { 97 | hasTower = true; 98 | } 99 | 100 | // Early exit if both spawns and towers are found 101 | if (hasSpawn && hasTower) { 102 | break; 103 | } 104 | } 105 | 106 | // Set buildersNeeded flag only if one or both structures are missing 107 | room.memory.buildersNeeded = !(hasSpawn && hasTower) || room.level < room.controller.level - 1; 108 | } 109 | 110 | funnelRequest(room) { 111 | const requests = ALLY_HELP_REQUESTS[MY_USERNAME] ? ALLY_HELP_REQUESTS[MY_USERNAME].requests : {}; 112 | if (room.terminal && room.level < 8 && FUNNEL_REQUESTS) { 113 | let funnelRequests = requests.funnel ? requests.funnel : []; 114 | if (funnelRequests) { 115 | funnelRequests = funnelRequests.filter((r) => r.roomName !== room.name); 116 | const goalType = room.level === 6 ? 1 : room.level === 7 ? 2 : 0; 117 | funnelRequests.push({ 118 | goalType: goalType, 119 | maxAmount: Math.min((room.controller.progressTotal - room.controller.progress) - room.energy, 200000), 120 | timeout: Game.time + CREEP_LIFE_TIME, 121 | roomName: room.name 122 | }); 123 | ALLY_HELP_REQUESTS[MY_USERNAME].requests.funnel = funnelRequests; 124 | } 125 | } else { 126 | let funnelRequests = requests.funnel ? requests.funnel : []; 127 | if (funnelRequests && ALLY_HELP_REQUESTS[MY_USERNAME]) { 128 | funnelRequests = funnelRequests.filter((r) => r.roomName !== room.name); 129 | ALLY_HELP_REQUESTS[MY_USERNAME].requests.funnel = funnelRequests; 130 | } 131 | } 132 | } 133 | 134 | barrierTracking(room) { 135 | const lowestBarrier = _.min(room.structures.filter((s) => [STRUCTURE_WALL, STRUCTURE_RAMPART].includes(s.structureType)), (s) => s.hits); 136 | if (!lowestBarrier) return; 137 | const hostileMulti = HOSTILES.length ? 1 : 0.5; 138 | const targetHits = Math.min((BARRIER_TARGET * (room.level / 8)) * hostileMulti, lowestBarrier.hitsMax); 139 | room.memory.barrierHitsTarget = targetHits; 140 | room.memory.barrierBuilding = lowestBarrier.hits < targetHits; 141 | } 142 | } 143 | 144 | profiler.registerClass(StateManager, 'StateManager'); 145 | module.exports = StateManager; -------------------------------------------------------------------------------- /default/modules/module.towerController.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | let roomRepairTower = {}; 6 | 7 | module.exports.towerControl = function (room) { 8 | // Reset memory flags 9 | room.memory.towerTarget = undefined; 10 | room.memory.dangerousAttack = undefined; 11 | room.memory.spawnDefenders = undefined; 12 | 13 | // Handle repair tower selection and checking energy state 14 | let repairTower = getRepairTower(room); 15 | 16 | if (room.hostileCreeps.length) { 17 | handleHostileCreeps(room); 18 | } else if (repairTower) { 19 | handleRepairTowerActions(room, repairTower); 20 | } 21 | }; 22 | 23 | // Helper function to get the best repair tower 24 | function getRepairTower(room) { 25 | return Game.getObjectById(roomRepairTower[room.name]) || _.max( 26 | _.filter(room.impassibleStructures, s => s.structureType === STRUCTURE_TOWER && s.store[RESOURCE_ENERGY] > TOWER_CAPACITY * 0.5), 27 | 'energy' 28 | ); 29 | } 30 | 31 | // Handle repair tower actions 32 | function handleRepairTowerActions(room, repairTower) { 33 | // Clear bugged towers 34 | if (!(repairTower instanceof StructureTower)) { 35 | return roomRepairTower[room.name] = undefined; 36 | } 37 | // Check if room is in a state where we can repair 38 | if (repairTower && repairTower.store[RESOURCE_ENERGY] > TOWER_CAPACITY * 0.25) { 39 | roomRepairTower[room.name] = repairTower.id; 40 | 41 | let woundedCreep = findWoundedCreep(room); 42 | let degradingStructure = findDegradingStructure(room); 43 | 44 | // Perform healing or repair 45 | if (woundedCreep) { 46 | repairTower.heal(woundedCreep); 47 | } else if (degradingStructure && room.energyState) { 48 | repairTower.repair(degradingStructure); 49 | } 50 | } 51 | } 52 | 53 | // Find the first wounded creep (friendly or powerCreep) 54 | function findWoundedCreep(room) { 55 | return _.find(room.friendlyCreeps, c => c.hits < c.hitsMax) || _.find(room.powerCreeps, c => c.hits < c.hitsMax && _.includes(FRIENDLIES, c.owner.username)); 56 | } 57 | 58 | // Find structures that are degrading and need repair 59 | function findDegradingStructure(room) { 60 | const multi = room.energyState ? 2 : 1; 61 | return _.find(room.structures, s => 62 | (s.structureType === STRUCTURE_ROAD && s.hits < s.hitsMax * (0.25 & multi)) || 63 | (s.structureType === STRUCTURE_CONTAINER && s.hits < s.hitsMax * (0.2 & multi)) || 64 | ((s.structureType === STRUCTURE_RAMPART || s.structureType === STRUCTURE_WALL) && s.hits < 10000) 65 | ); 66 | } 67 | 68 | // Handle hostile creeps in the room 69 | function handleHostileCreeps(room) { 70 | let hostileCreeps = _.sortBy(room.hostileCreeps, function (c) { 71 | return c.hits + calculateHealPower(room, c); 72 | }); // Sort hostile creeps by their hit points for more efficient targeting 73 | let towers = _.shuffle(_.filter(room.impassibleStructures, s => s.structureType === STRUCTURE_TOWER && s.isActive() && s.store[RESOURCE_ENERGY] >= TOWER_ENERGY_COST)); 74 | 75 | // If no towers and no safe mode, trigger defender spawning 76 | if (!towers.length && !room.controller.safeMode) { 77 | room.memory.dangerousAttack = true; 78 | room.memory.spawnDefenders = true; 79 | return; 80 | } 81 | 82 | for (let i = 0; i < hostileCreeps.length; i++) { 83 | if (hostileCreeps[i].pos.checkIfOutOfBounds()) continue; 84 | let attackPower = calculateAttackPower(room, hostileCreeps[i], towers); 85 | let healPower = calculateHealPower(room, hostileCreeps[i]); 86 | 87 | // Check if the enemy creep should be attacked or if defenders should be spawned 88 | room.visual.text(attackPower + ' / ' + healPower, hostileCreeps[i].pos.x, hostileCreeps[i].pos.y, { 89 | align: 'left', 90 | opacity: 0.8 91 | }); 92 | if (attackPower > healPower) { 93 | room.memory.towerTarget = hostileCreeps[i].id; 94 | const targetTank = hostileCreeps[i].hits + healPower; 95 | let damageDone = 0; 96 | const sortedTowers = _.sortBy(towers, function (t) { 97 | return determineDamage(t.pos.getRangeTo(hostileCreeps[i])); 98 | }) 99 | for (const tower of sortedTowers) { 100 | tower.attack(hostileCreeps[i]); 101 | damageDone += determineDamage(tower.pos.getRangeTo(hostileCreeps[i])); 102 | if (damageDone > targetTank) break; 103 | } 104 | break; 105 | } else if (room.energyState) { 106 | combatRepair(room); 107 | } 108 | 109 | // If the hostile creep has enough healing power, spawn defenders 110 | if (healPower * 2 > attackPower) { 111 | room.memory.dangerousAttack = true; 112 | room.memory.spawnDefenders = true; 113 | room.memory.defenseCooldown = Game.time + CREEP_LIFE_TIME; 114 | break; 115 | } 116 | } 117 | } 118 | 119 | function combatRepair(room) { 120 | const enemies = room.creeps.filter((c) => !c.my && (c.hasActiveBodyparts(ATTACK) || c.hasActiveBodyparts(WORK) || c.hasActiveBodyparts(RANGED_ATTACK))); 121 | const damagedCreep = findWoundedCreep(room); 122 | const ramparts = room.structures.filter((s) => s.structureType === STRUCTURE_RAMPART && s.hits < 1000000 && s.pos.findInRange(enemies, 8).length); 123 | const towers = room.structures.filter((s) => s.structureType === STRUCTURE_TOWER && s.store[RESOURCE_ENERGY] > TOWER_ENERGY_COST); 124 | if (damagedCreep) { 125 | towers.forEach((t) => t.heal(damagedCreep)) 126 | } else if (ramparts.length) { 127 | towers.forEach((t) => t.repair(_.min(ramparts, 'hits'))) 128 | } 129 | } 130 | 131 | // Calculate the attack power against a single hostile creep 132 | function calculateAttackPower(room, hostileCreep, towers) { 133 | let attackPower = 0; 134 | 135 | let inMeleeRange = _.filter(room.friendlyCreeps, c => c.pos.isNearTo(hostileCreep) && c.hasActiveBodyparts(ATTACK)); 136 | let inRangedRange = _.filter(room.friendlyCreeps, c => c.pos.getRangeTo(hostileCreep) <= 3 && c.hasActiveBodyparts(RANGED_ATTACK)); 137 | 138 | // Add attack power from friendly creeps 139 | inMeleeRange.forEach(c => attackPower += abilityPower(c.body).attack); 140 | inRangedRange.forEach(c => attackPower += abilityPower(c.body).rangedAttack); 141 | 142 | // Add tower damage 143 | towers.forEach(t => attackPower += determineDamage(hostileCreep.pos.getRangeTo(t))); 144 | 145 | return attackPower; 146 | } 147 | 148 | // Calculate the heal power of a hostile creep 149 | const healPowerCache = {}; 150 | function calculateHealPower(room, hostileCreep) { 151 | const cacheKey = hostileCreep.id + '.' + _.filter(hostileCreep.body, (b) => b.hits).length; 152 | if (healPowerCache[cacheKey]) return healPowerCache[cacheKey]; 153 | let healPower = 0; 154 | 155 | if (!room.controller.safeMode) { 156 | let inRangeMeleeHealers = _.filter(room.hostileCreeps, s => s.pos.isNearTo(hostileCreep) && s.hasActiveBodyparts(HEAL)); 157 | let inRangeRangedHealers = _.filter(room.hostileCreeps, s => s.pos.getRangeTo(hostileCreep) <= 3 && s.hasActiveBodyparts(HEAL)); 158 | 159 | inRangeMeleeHealers.forEach(c => healPower += abilityPower(c.body).heal); 160 | inRangeRangedHealers.forEach(c => healPower += abilityPower(c.body).rangedHeal); 161 | healPower += abilityPower(hostileCreep.body).heal; 162 | } 163 | 164 | healPowerCache[cacheKey] = healPower; 165 | return healPower; 166 | } 167 | 168 | // Computes damage of a tower based on range 169 | function determineDamage(range) { 170 | if (range <= 5) { 171 | return 600; 172 | } else if (range < 20) { 173 | return 600 - 450 * (range - 5) / 15; 174 | } else { 175 | return 150; 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /default/operations/operation.borderPatrol.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | Creep.prototype.borderPatrol = function () { 6 | let sentence = [ICONS.border, 'Border', 'Patrol']; 7 | let word = Game.time % sentence.length; 8 | this.say(sentence[word], true); 9 | 10 | // Combat handling 11 | if (this.handleMilitaryCreep(false, true, false)) return; 12 | 13 | // Healing 14 | if (this.hits < this.hitsMax) { 15 | if (this.hasActiveBodyparts(HEAL)) { 16 | this.findDefensivePosition(); 17 | return this.heal(this); 18 | } else { 19 | return this.fleeHome(); 20 | } 21 | } 22 | 23 | // Determine creep/squad power 24 | let combatPower = abilityPower(this.body).attack + abilityPower(this.body).heal; 25 | if (this.memory.squadMembers) { 26 | for (const member of this.memory.squadMembers) { 27 | if (this.room.creeps[member]) { 28 | combatPower += abilityPower(this.room.creeps[member].body).attack + abilityPower(this.room.creeps[member].body).heal; 29 | } 30 | } 31 | } 32 | 33 | // Movement 34 | if (this.memory.destination) { 35 | // If we can't win, let's move back home 36 | if (INTEL[this.memory.destination] && INTEL[this.memory.destination].hostilePower > combatPower) { 37 | if (!this.memory.squadMembers || this.memory.squadMembers.length < 3) this.memory.needsMoreSquadMembers = true; 38 | return this.fleeHome(true); 39 | } else if (this.room.name !== this.memory.destination) { 40 | this.memory.needsMoreSquadMembers = undefined; 41 | return this.shibMove(new RoomPosition(25, 25, this.memory.destination), {range: 20}); 42 | } else { 43 | if (!this.room.hostileCreeps.length && !this.room.hostileStructures.length) { 44 | if (!this.memory.standingGuard) this.memory.standingGuard = Game.time; 45 | else if (this.memory.standingGuard + 25 < Game.time) { 46 | this.memory.destination = undefined; 47 | } 48 | this.idleFor(5); 49 | return; 50 | } 51 | } 52 | } 53 | 54 | // Awaiting orders 55 | if (!this.memory.destination && !this.memory.awaitingOrders) { 56 | this.memory.needsMoreSquadMembers = undefined; 57 | this.memory.destination = this.memory.colony; 58 | this.memory.awaitingOrders = true; 59 | } 60 | 61 | // Idle handling 62 | if (this.memory.awaitingOrders && !this.memory.destination && this.room.name !== this.memory.colony) { 63 | this.memory.destination = this.memory.colony; 64 | this.shibMove(new RoomPosition(25, 25, this.memory.destination), {range: 24}); 65 | } else { 66 | scanForNearbyThreats(this); 67 | if (this.ticksToLive <= 500 && HOSTILES.length) this.memory.operation = 'harass'; 68 | else if (!scanForNearbyThreats(this) && this.findDefensivePosition()) this.idleFor(5); 69 | } 70 | }; 71 | 72 | function scanForNearbyThreats(creep) { 73 | const adjacentRooms = _.map(Game.map.describeExits(creep.room.name)); 74 | for (let roomName of adjacentRooms) { 75 | let roomIntel = INTEL[roomName]; 76 | if (roomIntel) { 77 | if (roomIntel.towers) continue; 78 | if (roomIntel.threatLevel) { 79 | if (!creep.memory.destination || creep.memory.destination !== roomName) { 80 | creep.memory.destination = roomName; 81 | creep.memory.awaitingOrders = undefined; 82 | creep.say('Threat!', true); 83 | return true; 84 | } 85 | } 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /default/operations/operation.guard.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | Creep.prototype.guardRoom = function () { 6 | let destination = this.memory.destination; 7 | let sentence = ['Security', 'Guard', 'For', destination]; 8 | let word = Game.time % sentence.length; 9 | this.say(sentence[word], true); 10 | 11 | this.attackInRange(); 12 | this.healInRange(); 13 | 14 | // Move to the destination room if not there yet 15 | if (this.room.name !== destination) { 16 | return this.shibMove(new RoomPosition(25, 25, destination), {range: 24}); 17 | } else { 18 | // Check for new mission or update orders if necessary 19 | this.operationManager(); 20 | // Combat handling 21 | if (this.handleMilitaryCreep() || this.findDefensivePosition()) return; 22 | } 23 | }; -------------------------------------------------------------------------------- /default/operations/operation.harass.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | Creep.prototype.harass = function () { 6 | // Combat handling 7 | if (this.canIWin(50) && this.handleMilitaryCreep()) return; 8 | 9 | this.attackInRange(); 10 | 11 | let sentence = ['MURDER', 'MODE', 'ACTIVATED', '--', 'DANGER', '--']; 12 | this.say(sentence[Game.time % sentence.length], true); 13 | 14 | if (this.room.name === this.memory.destination || !this.memory.destination) { 15 | // Handle visited tracking 16 | let visited = this.memory.other.visited || []; 17 | 18 | // Find the next harass target by considering threat level and user activity 19 | let target = _.min( 20 | _.filter(INTEL, (r) => { 21 | return (!visited.includes(r.name) && (!r.owner || !r.towers) && THREATS.includes(r.user) 22 | && (!r.armedHostile || r.armedHostile + CREEP_LIFE_TIME < Game.time) && !r.safemode); 23 | }), 24 | (r) => findClosestOwnedRoom(r.name, true) 25 | ); 26 | 27 | if (target && target.name) { 28 | visited.push(this.room.name); 29 | this.memory.other.visited = visited; 30 | this.memory.destination = target.name; 31 | this.say('RE-TASKED', true); 32 | log.a('Re-tasking harasser ' + this.name + ' to ' + roomLink(target.name) + ' targeting ' + INTEL[target.name].user + ' from ' + roomLink(this.room.name), 'HARASS: '); 33 | } else if (this.memory.other.visited.length) { 34 | this.memory.other.visited = []; 35 | } else { 36 | return this.fleeHome(true); 37 | } 38 | } else { 39 | return this.shibMove(new RoomPosition(25, 25, this.memory.destination), {range: 22}); 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /default/operations/operation.remoteDenial.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const highCommand = require('module.highCommand'); 6 | 7 | Creep.prototype.remoteDenial = function () { 8 | let sentence = ['No', 'Remotes', 'Allowed']; 9 | this.say(sentence[Game.time % sentence.length], true); 10 | 11 | // Combat handling 12 | if (this.handleMilitaryCreep()) return; 13 | 14 | // Healing 15 | if (this.hits < this.hitsMax) { 16 | if (this.hasActiveBodyparts(HEAL)) { 17 | this.findDefensivePosition(); 18 | return this.heal(this); 19 | } else { 20 | return this.fleeHome(); 21 | } 22 | } 23 | 24 | // If already in the target room 25 | if (this.room.name === this.memory.destination || !this.memory.destination) { 26 | highCommand.generateThreat(this); 27 | if (this.memory.other) highCommand.operationSustainability(this.room, this.memory.other.target); 28 | 29 | if ((this.room.hostileCreeps.length || this.room.hostileStructures.length) && this.canIWin(50)) { 30 | if (Memory.targetRooms[this.memory.other.target]) { 31 | if (this.room.hostileCreeps.length) { 32 | Memory.targetRooms[this.memory.other.target].level = 2; 33 | } else { 34 | Memory.targetRooms[this.memory.other.target].level = 1; 35 | } 36 | } 37 | } else { 38 | const remotes = Object.values(Game.map.describeExits(this.memory.other.target)).filter((n) => 39 | (!INTEL[n] || !INTEL[n].user || INTEL[n].user === INTEL[this.memory.other.target].owner) && Object.values(Game.map.describeExits(n)).length > 1); 40 | this.memory.destination = _.sample(remotes); 41 | this.say('RETASKED', true); 42 | } 43 | } else { 44 | return this.shibMove(new RoomPosition(25, 25, this.memory.destination), {range: 22}); 45 | } 46 | }; 47 | -------------------------------------------------------------------------------- /default/operations/operation.roomDenial.js: -------------------------------------------------------------------------------- 1 | const highCommand = require('module.highCommand'); 2 | 3 | Creep.prototype.denyRoom = function () { 4 | // Make sure to display status to inform the user what's happening 5 | const sentence = ['Coming', 'For', 'That', 'Booty', this.memory.destination]; 6 | this.say(sentence[Game.time % sentence.length], true); 7 | 8 | if (this.room.name === this.memory.destination) { 9 | // Track wave if we haven't yet 10 | if (!this.memory.waveTracked) { 11 | if (Memory.targetRooms[this.room.name] && (!Memory.targetRooms[this.room.name].lastWave || Memory.targetRooms[this.room.name].lastWave + 20 < Game.time)) { 12 | this.memory.waveTracked = true; 13 | Memory.targetRooms[this.room.name].lastWave = Game.time; 14 | Memory.targetRooms[this.room.name].waves = Memory.targetRooms[this.room.name].waves ? Memory.targetRooms[this.room.name].waves++ : 1; 15 | } else { 16 | this.memory.waveTracked = true; 17 | } 18 | } 19 | // Track if active defenders spawn or not 20 | if (!this.memory.activeTracked || this.memory.activeTracked + 100 < Game.time) { 21 | if (this.memory.activeTracked) { 22 | const armedHostiles = this.room.creeps.filter((c) => !c.my && (c.hasActiveBodyparts(ATTACK) || !c.hasActiveBodyparts(RANGED_ATTACK))); 23 | if (!armedHostiles.length) INTEL[this.room.name].noActiveDefenders = true; 24 | } else { 25 | this.memory.activeTracked = Game.time; 26 | } 27 | } 28 | // Call operation manager periodically (every 5 ticks) 29 | if (Game.time % 5 === 0) { 30 | this.operationManager(); 31 | } 32 | 33 | // Update sustainability of operation in this room 34 | highCommand.operationSustainability(this.room); 35 | 36 | // Combat handling 37 | if (this.handleMilitaryCreep()) return; 38 | } 39 | 40 | // Handle staging and moving to destination 41 | if (!this.memory.misc || !this.memory.misc.stagingRoom) { 42 | if (!this.memory.misc) this.memory.misc = {}; 43 | this.memory.misc.stagingRoom = INTEL[this.memory.destination].attackDirection ? Game.map.describeExits(this.memory.destination)[INTEL[this.memory.destination].attackDirection] : this.memory.destination; 44 | } 45 | if (this.memory.misc && this.memory.misc.stagingRoom && this.memory.misc.stagingRoom === this.room.name) this.memory.misc.staged = true; 46 | let destination = this.memory.misc && this.memory.misc.stagingRoom && !this.memory.misc.staged ? this.memory.misc.stagingRoom : this.memory.destination; 47 | if (this.room.name !== destination) { 48 | return this.shibMove(new RoomPosition(25, 25, destination), {range: 23}); 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /default/operations/operation.scout.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | Creep.prototype.scoutRoom = function () { 5 | if (!Memory.targetRooms[this.memory.destination]) return this.recycleCreep(); 6 | if (this.room.name !== this.memory.destination) { 7 | return this.shibMove(new RoomPosition(25, 25, this.memory.destination), { 8 | range: 23, 9 | offRoad: true 10 | }); 11 | } 12 | if (!this.hide()) this.idleFor(10); 13 | return operationPlanner(this.room, this); 14 | }; 15 | 16 | Creep.prototype.operationManager = function () { 17 | return forwardObserver(this.room); 18 | }; 19 | 20 | StructureObserver.prototype.operationPlanner = function (room) { 21 | if (!room) return; 22 | room.invaderCheck(); 23 | room.cacheRoomIntel(); 24 | if (Memory.targetRooms[room.name]) return operationPlanner(room); 25 | }; 26 | 27 | function operationPlanner(room, creep = undefined) { 28 | // Handle forward observer 29 | if (Memory.targetRooms[room.name]) { 30 | forwardObserver(room); 31 | } else { 32 | room.cacheRoomIntel(); 33 | } 34 | } 35 | 36 | // Observer tasks 37 | function forwardObserver(room) { 38 | room.cacheRoomIntel(); 39 | const targetRoom = Memory.targetRooms[room.name]; 40 | if (!targetRoom) return false; 41 | 42 | if (room.controller && room.controller.safeMode && !FRIENDLIES.includes(room.controller.owner.username)) { 43 | updateRoomSafemode(room); 44 | return; 45 | } 46 | 47 | updateHostileUsers(room); 48 | 49 | switch (targetRoom.type) { 50 | case 'roomDenial': 51 | handleRoomDenialOperation(room); 52 | break; 53 | case 'scout': 54 | handleScoutOperation(room); 55 | break; 56 | case 'remoteDenial': 57 | handleRemoteDenialOperation(room); 58 | break; 59 | default: 60 | updateRoomLevel(room); 61 | break; 62 | } 63 | } 64 | 65 | function updateRoomSafemode(room) { 66 | const tick = Game.time; 67 | let targetRoom = Memory.targetRooms[room.name] || {}; 68 | targetRoom = { 69 | ...targetRoom, 70 | tick, 71 | type: 'remoteDenial', 72 | dDay: tick + room.controller.safeMode 73 | }; 74 | Memory.targetRooms[room.name] = targetRoom; 75 | } 76 | 77 | function updateHostileUsers(room) { 78 | if (room.hostileCreeps.length) { 79 | let userList = Memory.targetRooms[room.name].userList || []; 80 | let users = _.uniq(_.map(room.hostileCreeps, 'owner.username')); 81 | Memory.targetRooms[room.name].userList = _.union(userList, users); 82 | 83 | let maxStrengthUser = _.max(Memory.targetRooms[room.name].userList, userStrength); 84 | Memory.targetRooms[room.name].maxLevel = userStrength(maxStrengthUser); 85 | } 86 | } 87 | 88 | function handleRoomDenialOperation(room) { 89 | if (!room.controller || !room.controller.owner) { 90 | log.a(`Canceling room denial operation in ${roomLink(room.name)} as it is no longer owned.`, 'HIGH COMMAND: '); 91 | delete Memory.targetRooms[room.name]; 92 | return; 93 | } 94 | 95 | const towers = room.structures.find((s) => s.structureType === STRUCTURE_TOWER); 96 | Memory.targetRooms[room.name].camping = !!towers; 97 | 98 | updateRoomLevel(room); 99 | handleCleanerAndClaimAttacker(room); 100 | } 101 | 102 | function handleScoutOperation(room) { 103 | room.cacheRoomIntel(true) 104 | const towers = room.structures.filter((s) => s.structureType === STRUCTURE_TOWER); 105 | if (INTEL[room.name].sk && towers.length) { 106 | Memory.targetRooms[room.name].type = 'stronghold'; 107 | Memory.targetRooms[room.name].boosts = [HEAL]; 108 | } else if (INTEL[room.name].owner && (!INTEL[room.name].towers || towers.length <= 3)) { 109 | Memory.targetRooms[room.name].type = 'roomDenial'; 110 | if (towers.length) Memory.targetRooms[room.name].boosts = [HEAL]; 111 | log.a(`Room ${roomLink(room.name)} converted to room denial operation.`, 'HIGH COMMAND: '); 112 | } else if (INTEL[room.name].owner && INTEL[room.name].towers > 3) { 113 | Memory.targetRooms[room.name].type = 'remoteDenial'; 114 | log.a(`Room ${roomLink(room.name)} converted to remote denial operation.`, 'HIGH COMMAND: '); 115 | } else { 116 | Memory.targetRooms[room.name].type = 'guard'; 117 | log.a(`Room ${roomLink(room.name)} converted to guard operation.`, 'HIGH COMMAND: '); 118 | } 119 | updateRoomLevel(room); 120 | } 121 | 122 | function handleRemoteDenialOperation(room) { 123 | const armedHostiles = room.hostileCreeps.find((c) => c.hasActiveBodyparts(ATTACK) || c.hasActiveBodyparts(RANGED_ATTACK)); 124 | if (armedHostiles) { 125 | Memory.targetRooms[room.name].level = 2; 126 | } else { 127 | Memory.targetRooms[room.name].level = 1; 128 | } 129 | } 130 | 131 | function handleCleanerAndClaimAttacker(room) { 132 | const targetRoom = Memory.targetRooms[room.name]; 133 | if (!room.hostileCreeps.length) { 134 | // If no hostile creeps, request cleaner and claim attacker if possible 135 | if (room.hostileStructures.length) targetRoom.cleaner = true; 136 | if ((!room.controller.upgradeBlocked || room.controller.upgradeBlocked < CREEP_CLAIM_LIFE_TIME) && 137 | room.controller.pos.countOpenTerrainAround()) { 138 | targetRoom.claimAttacker = true; 139 | } else { 140 | targetRoom.claimAttacker = undefined; 141 | } 142 | } else { 143 | targetRoom.claimAttacker = undefined; 144 | targetRoom.cleaner = undefined; 145 | } 146 | } 147 | 148 | function updateRoomLevel(room) { 149 | const targetRoom = Memory.targetRooms[room.name]; 150 | const towers = room.hostileStructures.filter((s) => s.structureType === STRUCTURE_TOWER && s.store[RESOURCE_ENERGY] >= TOWER_ENERGY_COST); 151 | const armedCreeps = room.hostileCreeps.find((c) => c.hasActiveBodyparts(ATTACK) || c.hasActiveBodyparts(RANGED_ATTACK)); 152 | if (towers.length) { 153 | if (towers.length === 1) { 154 | targetRoom.level = 2; 155 | } else if (towers.length === 2) { 156 | targetRoom.level = 3; 157 | } else { 158 | targetRoom.level = 4; 159 | } 160 | } else if (armedCreeps || (Memory.targetRooms[room.name].type === 'guard' && room.controller && room.controller.safeMode < CREEP_LIFE_TIME)) { 161 | targetRoom.level = 2; 162 | } else if (room.hostileCreeps.length || room.hostileStructures.length) { 163 | targetRoom.level = 1; 164 | } else { 165 | if (Memory.targetRooms[room.name].type === 'guard') targetRoom.builders = FRIENDLIES.includes(room.controller.owner.username); 166 | targetRoom.level = 0; 167 | } 168 | if (!towers.length) targetRoom.boosts = undefined; 169 | } -------------------------------------------------------------------------------- /default/operations/operation.stronghold.js: -------------------------------------------------------------------------------- 1 | Creep.prototype.strongholdAttack = function () { 2 | // Make sure to display status to inform the user what's happening 3 | const sentence = ['Gimme', 'The', 'Loot', this.memory.destination]; 4 | this.say(sentence[Game.time % sentence.length], true); 5 | 6 | // Combat handling 7 | if (this.handleMilitaryCreep()) return; 8 | 9 | // If not in the destination room, move there 10 | if (this.room.name !== this.memory.destination) { 11 | return this.shibMove(new RoomPosition(25, 25, this.memory.destination), {range: 23}); 12 | } else { 13 | const tower = this.room.structures.find((s) => s.structureType === STRUCTURE_TOWER); 14 | if (!tower) { 15 | const container = this.room.structures.find((s) => s.structureType === STRUCTURE_CONTAINER && _.sum(s.store) > s.store[RESOURCE_ENERGY]); 16 | if (container) { 17 | if (container.pos.checkForRampart()) this.memory.target = container.pos.checkForRampart().id; 18 | else { 19 | Memory.targetRooms[this.room.name].loot = true; 20 | Memory.targetRooms[this.room.name].level = 0; 21 | } 22 | } 23 | } 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /default/prototypes/prototype.powerCreep.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | /** 6 | * Set the unit to idle-mode until recall tick 7 | * 8 | * @type {int} 9 | */ 10 | Object.defineProperty(PowerCreep.prototype, "idle", { 11 | configurable: true, 12 | get: function () { 13 | if (this.memory.idle === undefined) return 0; 14 | if (this.memory.idle <= Game.time) { 15 | delete this.idle; 16 | return 0; 17 | } 18 | this.say(_.sample([ICONS.wait23, ICONS.wait21, ICONS.wait19, ICONS.wait17, ICONS.wait13, ICONS.wait11, ICONS.wait7, ICONS.wait10, ICONS.wait3, ICONS.wait1]), true); 19 | if (this.pos.checkForRoad() && this.memory.role !== 'stationaryHarvester' && this.memory.role !== 'upgrader' && this.memory.role !== 'mineralHarvester' && this.memory.role !== 'remoteHarvester') { 20 | this.moveRandom(); 21 | } else if (this.pos.getRangeTo(this.pos.findClosestByRange(FIND_MY_SPAWNS)) === 1) { 22 | this.moveRandom(); 23 | } else { 24 | return this.memory.idle; 25 | } 26 | }, 27 | set: function (val) { 28 | if (!val && this.memory.idle) { 29 | delete(this.memory.idle); 30 | } else { 31 | this.memory.idle = val; 32 | } 33 | } 34 | }); 35 | 36 | Object.defineProperty(PowerCreep.prototype, 'ops', { 37 | get: function () { 38 | if (!this._ops) { 39 | this._ops = this.store[RESOURCE_OPS]; 40 | } 41 | return this._ops; 42 | }, 43 | enumerable: false, 44 | configurable: true 45 | }); 46 | 47 | Object.defineProperty(PowerCreep.prototype, 'isFull', { 48 | get: function () { 49 | if (!this._isFull) { 50 | this._isFull = _.sum(this.store) >= this.store.getCapacity() * 0.95; 51 | } 52 | return this._isFull; 53 | }, 54 | enumerable: false, 55 | configurable: true 56 | }); 57 | 58 | /** 59 | * Go to room hub 60 | * @param destination 61 | * @returns {*|boolean|boolean|void|string} 62 | */ 63 | PowerCreep.prototype.goToHub = function (destination) { 64 | let hub = new RoomPosition(25, 25, destination); 65 | if (this.pos.getRangeTo(hub) <= 15) return this.idleFor(10); 66 | return this.shibMove(hub, {range: 15}) 67 | }; 68 | 69 | /** 70 | * Idle for x ticks 71 | * @param ticks 72 | */ 73 | PowerCreep.prototype.idleFor = function (ticks = 0) { 74 | if (ticks > 0) { 75 | this.idle = Game.time + ticks; 76 | } else { 77 | delete this.idle; 78 | } 79 | }; 80 | 81 | /** 82 | * Move randomly 83 | */ 84 | PowerCreep.prototype.moveRandom = function () { 85 | let start = Math.ceil(Math.random() * 8); 86 | let direction = 0; 87 | for (let i = start; i < start + 8; i++) { 88 | direction = ((i - 1) % 8) + 1; 89 | let pos = this.pos.getAdjacentPosition(direction); 90 | if (!pos || pos.isExit() || pos.checkForWall() || pos.checkForObstacleStructure() || pos.checkForCreep()) { 91 | continue; 92 | } 93 | break; 94 | } 95 | this.move(direction); 96 | }; 97 | 98 | /** 99 | * Handle border checks 100 | * @returns {*|boolean} 101 | */ 102 | PowerCreep.prototype.borderCheck = function () { 103 | let x = this.pos.x; 104 | let y = this.pos.y; 105 | if (x === 0 || y === 0 || x === 49 || y === 49) { 106 | // Handle stuck creeps 107 | if (this.memory.borderCountDown) this.memory.borderCountDown++; else this.memory.borderCountDown = 1; 108 | // Handle path following 109 | if (this.memory.borderCountDown < 5 && this.memory._shibMove && this.memory._shibMove.path && this.memory._shibMove.path.length) { 110 | let pathInfo = this.memory._shibMove; 111 | let origin = normalizePos(this); 112 | pathInfo.path = pathInfo.path.slice(1); 113 | let nextDirection = parseInt(pathInfo.path[0], 10); 114 | pathInfo.newPos = origin.positionAtDirection(nextDirection); 115 | switch (this.move(nextDirection)) { 116 | case OK: 117 | pathInfo.pathPosTime = 0; 118 | pathInfo.lastMoveTick = Game.time; 119 | this.memory._shibMove = pathInfo; 120 | return false; 121 | } 122 | // Handle corners 123 | } else if (x === 0 && y === 0) { 124 | this.move(BOTTOM_RIGHT); 125 | } else if (x === 0 && y === 49) { 126 | this.move(TOP_RIGHT); 127 | } else if (x === 49 && y === 0) { 128 | this.move(BOTTOM_LEFT); 129 | } else if (x === 49 && y === 49) { 130 | this.move(TOP_LEFT); 131 | } 132 | // Handle border movement 133 | let options; 134 | let road = _.find(this.room.structures, (s) => s.structureType === STRUCTURE_ROAD && s.pos.isNearTo(this)); 135 | if (road) { 136 | this.move(this.pos.getDirectionTo(road)); 137 | } else if (x === 49) { 138 | options = [LEFT, TOP_LEFT, BOTTOM_LEFT]; 139 | this.move(_.sample(options)); 140 | } else if (x === 0) { 141 | options = [RIGHT, TOP_RIGHT, BOTTOM_RIGHT]; 142 | this.move(_.sample(options)); 143 | } else if (y === 0) { 144 | options = [BOTTOM, BOTTOM_LEFT, BOTTOM_RIGHT]; 145 | this.move(_.sample(options)); 146 | } else if (y === 49) { 147 | options = [TOP, TOP_LEFT, TOP_RIGHT]; 148 | this.move(_.sample(options)); 149 | } 150 | return true; 151 | } 152 | this.memory.borderCountDown = undefined; 153 | return false; 154 | }; 155 | 156 | /** 157 | * Handle nuke fleeing 158 | * @returns {boolean} 159 | */ 160 | PowerCreep.prototype.fleeNukeRoom = function () { 161 | this.say('NUKE!', true); 162 | if (this.memory.fleeNukeTime <= Game.time) { 163 | this.memory.fleeNukeTime = undefined; 164 | this.memory.fleeNukeRoom = undefined; 165 | return false; 166 | } 167 | if (this.memory.fleeTo && this.room.name !== this.memory.fleeTo) this.shibMove(new RoomPosition(25, 25, this.memory.fleeTo), {range: 23}); else if (this.room.name !== this.memory.fleeTo) this.idleFor(this.memory.fleeNukeTime - Game.time); 168 | if (!this.memory.fleeTo) this.memory.fleeTo = _.sample(_.filter(MY_ROOMS, (r) => !r.nukes.length)).name; 169 | }; -------------------------------------------------------------------------------- /default/prototypes/prototype.roomObject.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | /** 6 | * Provides structure memory. 7 | */ 8 | Object.defineProperty(StructureLab.prototype, 'memory', { 9 | get: function () { 10 | if (this.room.memory._structureMemory === undefined || !this.room.memory._structureMemory) { 11 | this.room.memory._structureMemory = {}; 12 | } 13 | if (this.room.memory._structureMemory[this.id] === undefined || !this.room.memory._structureMemory[this.id]) { 14 | this.room.memory._structureMemory[this.id] = {}; 15 | } 16 | return this.room.memory._structureMemory[this.id]; 17 | }, 18 | set: function (v) { 19 | return _.set(this.room.memory, '_structureMemory.' + this.id, v); 20 | }, 21 | configurable: true, 22 | enumerable: false, 23 | }); 24 | 25 | Object.defineProperty(StructureFactory.prototype, 'memory', { 26 | get: function () { 27 | if (this.room.memory._structureMemory === undefined || !this.room.memory._structureMemory) { 28 | this.room.memory._structureMemory = {}; 29 | } 30 | if (this.room.memory._structureMemory[this.id] === undefined || !this.room.memory._structureMemory[this.id]) { 31 | this.room.memory._structureMemory[this.id] = {}; 32 | } 33 | return this.room.memory._structureMemory[this.id]; 34 | }, 35 | set: function (v) { 36 | return _.set(this.room.memory, '_structureMemory.' + this.id, v); 37 | }, 38 | configurable: true, 39 | enumerable: false, 40 | }); 41 | 42 | Object.defineProperty(StructureTerminal.prototype, 'memory', { 43 | get: function () { 44 | if (this.room.memory._structureMemory === undefined || !this.room.memory._structureMemory) { 45 | this.room.memory._structureMemory = {}; 46 | } 47 | if (this.room.memory._structureMemory[this.id] === undefined || !this.room.memory._structureMemory[this.id]) { 48 | this.room.memory._structureMemory[this.id] = {}; 49 | } 50 | return this.room.memory._structureMemory[this.id]; 51 | }, 52 | set: function (v) { 53 | return _.set(this.room.memory, '_structureMemory.' + this.id, v); 54 | }, 55 | configurable: true, 56 | enumerable: false, 57 | }); 58 | 59 | Object.defineProperty(Source.prototype, 'memory', { 60 | get: function () { 61 | if (this.room.memory._structureMemory === undefined || !this.room.memory._structureMemory) { 62 | this.room.memory._structureMemory = {}; 63 | } 64 | if (this.room.memory._structureMemory[this.id] === undefined || !this.room.memory._structureMemory[this.id]) { 65 | this.room.memory._structureMemory[this.id] = {}; 66 | } 67 | return this.room.memory._structureMemory[this.id]; 68 | }, 69 | set: function (v) { 70 | return _.set(this.room.memory, '_structureMemory.' + this.id, v); 71 | }, 72 | configurable: true, 73 | enumerable: false, 74 | }); 75 | 76 | let isActive = OwnedStructure.prototype.isActive; 77 | /** 78 | * More efficient isActive function for owned structures 79 | * @returns {boolean|*} 80 | */ 81 | OwnedStructure.prototype.isActive = function () { 82 | if (this.room.memory && this.room.memory.stats && this.room.memory.stats.highestRCL && this.room.memory.stats.highestRCL === (this.room.controller.level || 0)) { 83 | return true; 84 | } 85 | return isActive.call(this); 86 | } 87 | 88 | /** 89 | * Structure room visual 90 | * @param what 91 | */ 92 | RoomObject.prototype.say = function (what) { 93 | if (!this.room) return; 94 | this.room.visual.line(this.pos.x, this.pos.y, this.pos.x + 1 - 0.2, this.pos.y - 1, { 95 | color: "#eeeeee", 96 | opacity: 0.9, 97 | width: 0.1 98 | }).circle(this.pos, { 99 | fill: "#aaffaa", 100 | opacity: 0.9 101 | }).text(what, this.pos.x + 1, this.pos.y - 1, { 102 | color: "black", 103 | opacity: 0.9, 104 | align: "left", 105 | font: "bold 0.6 Monospace", 106 | backgroundColor: "black", 107 | backgroundPadding: 0.3 108 | }).text(what, this.pos.x + 1, this.pos.y - 1, { 109 | color: "black", 110 | opacity: 0.9, 111 | align: "left", 112 | font: "bold 0.6 Monospace", 113 | backgroundColor: "#eeeeee", 114 | backgroundPadding: 0.2 115 | }); 116 | }; 117 | 118 | /** 119 | * Structure room visual line to another object 120 | * @param to 121 | * @param color 122 | */ 123 | RoomObject.prototype.lineTo = function (to, color = "#eeeeee") { 124 | this.room.visual.line(this.pos.x, this.pos.y, to.pos.x, to.pos.y, { 125 | color: color, 126 | opacity: 0.9, 127 | width: 0.1 128 | }); 129 | }; -------------------------------------------------------------------------------- /default/roles/role.SKAttacker.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const profiler = require("tools.profiler"); 6 | 7 | class RoleSKAttacker { 8 | constructor(creep) { 9 | this.creep = creep; 10 | this.room = creep.room; 11 | this.performRoleActions(); 12 | } 13 | 14 | performRoleActions() { 15 | if (this.housekeeping()) return; 16 | if (!this.creep.memory.keeper && !this.creep.memory.lair && this.room.name !== this.creep.memory.destination) { 17 | this.travel(); 18 | } else { 19 | this.creep.memory.arrived = true; 20 | this.SKAttackerTasks(); 21 | } 22 | } 23 | 24 | housekeeping() { 25 | // Boosting 26 | if (this.creep.tryToBoost()) return true; 27 | // Handle invader core in sk 28 | if (this.room.hostileStructures.length) { 29 | let core = _.filter(this.room.impassibleStructures, (s) => s.structureType === STRUCTURE_INVADER_CORE)[0]; 30 | if (core) { 31 | this.room.cacheRoomIntel(true); 32 | return this.creep.recycleCreep(); 33 | } 34 | } 35 | } 36 | 37 | travel() { 38 | this.creep.shibMove(new RoomPosition(25, 25, this.creep.memory.destination), {range: 23}); 39 | } 40 | 41 | SKAttackerTasks() { 42 | let sourceKeeper = Game.getObjectById(this.creep.memory.keeper) || this.creep.pos.findClosestByRange(this.room.creeps, 43 | {filter: (c) => c.room.name === this.creep.memory.destination && !FRIENDLIES.includes(c.owner.username)}); 44 | if (sourceKeeper) { 45 | this.creep.healInRange(); 46 | this.creep.memory.lair = undefined; 47 | this.creep.memory.keeper = sourceKeeper.id; 48 | this.creep.rangedAttack(sourceKeeper); 49 | switch (this.creep.attack(sourceKeeper)) { 50 | case ERR_NOT_IN_RANGE: 51 | if (this.creep.hits < this.creep.hitsMax * 0.8 && this.creep.pos.getRangeTo(sourceKeeper) < 12) return; 52 | this.creep.shibMove(sourceKeeper); 53 | break; 54 | case ERR_NO_BODYPART: 55 | break; 56 | case OK: 57 | this.creep.move(this.creep.pos.getDirectionTo(sourceKeeper)); 58 | break; 59 | } 60 | } else { 61 | this.creep.healInRange(); 62 | let lair = Game.getObjectById(this.creep.memory.lair) || _.min(_.filter(this.room.structures, (s) => 63 | s.structureType === STRUCTURE_KEEPER_LAIR && s.room.name === this.creep.memory.destination), 'ticksToSpawn'); 64 | this.creep.memory.keeper = undefined; 65 | this.creep.memory.lair = lair.id; 66 | if (this.creep.hits < this.creep.hitsMax * 0.8 && this.creep.pos.getRangeTo(lair) < 12) return; 67 | if (this.creep.pos.isNearTo(lair)) this.creep.idleFor(lair.ticksToSpawn - 1); else this.creep.shibMove(lair); 68 | } 69 | } 70 | } 71 | 72 | profiler.registerClass(RoleSKAttacker, 'SKAttacker'); 73 | module.exports = RoleSKAttacker; 74 | -------------------------------------------------------------------------------- /default/roles/role.attacker.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const profiler = require("tools.profiler"); 6 | 7 | class RoleAttacker { 8 | constructor(creep) { 9 | this.creep = creep; 10 | this.performRoleActions(); 11 | } 12 | 13 | performRoleActions() { 14 | if (this.creep.memory.operation) { 15 | this.operationSelection(this.creep); 16 | } else if (this.creep.memory.misc && this.creep.memory.misc.guardDog) { 17 | this.guardDog(this.creep); 18 | } else { 19 | this.unassignedTasks(this.creep); 20 | } 21 | } 22 | 23 | operationSelection(creep) { 24 | switch (creep.memory.operation) { 25 | case 'guard': 26 | creep.guardRoom(); 27 | break; 28 | case 'roomDenial': 29 | creep.denyRoom(); 30 | break; 31 | case 'borderPatrol': 32 | creep.borderPatrol(); 33 | break; 34 | } 35 | } 36 | 37 | guardDog(creep) { 38 | creep.say('Woof!', true); 39 | if (creep.canIWin(5) && creep.handleMilitaryCreep()) return; 40 | if (!creep.memory.leader) { 41 | const needsDog = _.find(Game.creeps, (c) => c.my && c.memory.leader && c.memory.squadMembers.length && (!c.memory.dog || !Game.getObjectById(c.memory.dog))); 42 | if (needsDog) { 43 | creep.memory.leader = needsDog.id; 44 | needsDog.memory.dog = creep.id; 45 | } else { 46 | creep.idleFor(5); 47 | } 48 | return; 49 | } 50 | const leader = Game.getObjectById(creep.memory.leader); 51 | if (!leader) return creep.memory.leader = undefined; 52 | if (creep.room.name !== leader.room.name || creep.pos.getRangeTo(leader) > 3) return creep.shibMove(leader, {range: 3}); 53 | } 54 | 55 | unassignedTasks(creep) { 56 | if (!Game.getObjectById(creep.memory.target) && creep.memory.destination && creep.memory.destination !== creep.room.name) { 57 | return creep.shibMove(new RoomPosition(25, 25, creep.memory.destination), {range: 22}); 58 | } 59 | if (!creep.handleMilitaryCreep()) { 60 | if (creep.memory.destination) { 61 | creep.room.cacheRoomIntel(true); 62 | creep.memory.destination = undefined; 63 | } 64 | creep.findDefensivePosition(creep); 65 | } 66 | } 67 | } 68 | 69 | profiler.registerClass(RoleAttacker, 'Attacker'); 70 | module.exports = RoleAttacker; 71 | 72 | -------------------------------------------------------------------------------- /default/roles/role.claimAttacker.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const profiler = require("tools.profiler"); 6 | 7 | class RoleClaimAttacker { 8 | constructor(creep) { 9 | this.creep = creep; 10 | this.room = creep.room; 11 | this.performRoleActions(); 12 | } 13 | 14 | performRoleActions() { 15 | if (this.houseKeeping()) { 16 | 17 | } else if (this.creep.room.controller && (!INTEL[this.room.name] || INTEL[this.room.name].user !== MY_USERNAME) && 18 | (this.creep.room.controller.owner || this.creep.room.controller.reservation)) { 19 | this.attackController(this.creep); 20 | } else if (this.creep.room.controller) { 21 | this.reserveController(this.creep); 22 | } 23 | } 24 | 25 | houseKeeping() { 26 | if (!Memory.targetRooms[this.creep.memory.destination] || (Memory.targetRooms[this.creep.memory.destination] && 27 | !Memory.targetRooms[this.creep.memory.destination].claimAttacker)) { 28 | this.creep.recycleCreep(); 29 | return true; 30 | } else if (this.creep.room.name !== this.creep.memory.destination) { 31 | this.creep.shibMove(new RoomPosition(25, 25, this.creep.memory.destination), {range: 22}); 32 | return true; 33 | } else if (this.creep.room.controller && this.creep.room.controller.upgradeBlocked > this.creep.ticksToLive) { 34 | this.creep.recycleCreep(); 35 | return true; 36 | } 37 | } 38 | 39 | attackController(creep) { 40 | switch (creep.attackController(creep.room.controller)) { 41 | case OK: 42 | if (!creep.memory.signed) { 43 | creep.signController(creep.room.controller, _.sample(ATTACK_ROOM_SIGNS)); 44 | creep.memory.signed = true; 45 | } 46 | break; 47 | case ERR_NOT_IN_RANGE: 48 | creep.shibMove(creep.room.controller, {range: 1}); 49 | break; 50 | } 51 | } 52 | 53 | reserveController(creep) { 54 | switch (creep.reserveController(creep.room.controller)) { 55 | case OK: 56 | if (!creep.memory.signed) { 57 | creep.signController(creep.room.controller, _.sample(ATTACK_ROOM_SIGNS)); 58 | creep.memory.signed = true; 59 | } 60 | break; 61 | case ERR_NOT_IN_RANGE: 62 | creep.shibMove(creep.room.controller, {range: 1}); 63 | break; 64 | } 65 | } 66 | } 67 | 68 | profiler.registerClass(RoleClaimAttacker, 'ClaimAttacker'); 69 | module.exports = RoleClaimAttacker; 70 | 71 | -------------------------------------------------------------------------------- /default/roles/role.claimer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const profiler = require("tools.profiler"); 6 | 7 | class RoleClaimer { 8 | constructor(creep) { 9 | this.creep = creep; 10 | this.room = creep.room; 11 | this.performRoleActions(); 12 | } 13 | 14 | // Placeholder for role-specific actions 15 | performRoleActions() { 16 | if (this.housekeeping()) { 17 | 18 | } else if (this.room.name !== this.creep.memory.destination) { 19 | this.travel(); 20 | } else if (this.creep.memory.operation === 'claimClear') { 21 | this.claimClear(); 22 | } else { 23 | this.claimRoom(); 24 | } 25 | } 26 | 27 | housekeeping() { 28 | // If you lost your claim part... die 29 | if (!this.creep.hasActiveBodyparts(CLAIM)) this.creep.suicide(); 30 | if (Game.gcl.level <= MY_ROOMS.length) { 31 | delete Memory.auxiliaryTargets[this.creep.room.name]; 32 | return this.creep.recycleCreep(); 33 | } 34 | } 35 | 36 | travel() { 37 | this.creep.shibMove(new RoomPosition(25, 25, this.creep.memory.destination), {range: 23}); 38 | } 39 | 40 | claimRoom() { 41 | if (this.creep.room.controller.owner) { 42 | this.cleanRoom(this.room); 43 | return this.creep.recycleCreep(); 44 | } else if (!this.creep.pos.findClosestByPath(_.filter(this.room.structures, (s) => s.structureType === STRUCTURE_CONTROLLER))) { 45 | INTEL[this.room.name].obstacles = true; 46 | Memory.auxiliaryTargets[this.room.name] = undefined; 47 | return this.creep.recycleCreep(); 48 | } else if (!this.creep.memory.signed) { 49 | switch (this.creep.signController(this.room.controller, _.sample(OWNED_ROOM_SIGNS))) { 50 | case ERR_NOT_IN_RANGE: 51 | this.creep.shibMove(this.room.controller); 52 | break; 53 | case OK: 54 | this.creep.memory.signed = true; 55 | } 56 | } else { 57 | switch (this.creep.claimController(this.room.controller)) { 58 | case ERR_NOT_IN_RANGE: 59 | this.creep.shibMove(this.room.controller); 60 | break; 61 | case ERR_BUSY: 62 | break; 63 | case ERR_NOT_FOUND: 64 | break; 65 | case ERR_INVALID_TARGET: 66 | break; 67 | case OK: 68 | Memory.auxiliaryTargets[this.room.name] = undefined; 69 | Memory.targetRooms[this.room.name] = undefined; 70 | MY_ROOMS.push(this.room.name); 71 | } 72 | } 73 | } 74 | 75 | cleanRoom() { 76 | _.filter(this.room.structures, (s) => s.structureType !== STRUCTURE_CONTROLLER && s.structureType !== STRUCTURE_ROAD).forEach((s) => s.destroy()); 77 | _.filter(this.room.constructionSites, (s) => s.owner.username !== MY_USERNAME).forEach((s) => s.remove()); 78 | } 79 | 80 | claimClear() { 81 | if (!this.room.controller.owner) { 82 | switch (this.creep.claimController(this.room.controller)) { 83 | case ERR_NOT_IN_RANGE: 84 | this.creep.shibMove(this.room.controller); 85 | break; 86 | case OK: 87 | this.creep.signController(this.creep.room.controller, 'Cleaning provided by SlothBot'); 88 | INTEL[this.creep.room.name] = undefined; 89 | } 90 | } else { 91 | abandonRoom(this.room); 92 | if (Memory.auxiliaryTargets) delete Memory.auxiliaryTargets[this.room.name]; 93 | this.creep.recycleCreep(); 94 | } 95 | } 96 | } 97 | 98 | profiler.registerClass(RoleClaimer, 'Claimer'); 99 | module.exports = RoleClaimer; 100 | 101 | -------------------------------------------------------------------------------- /default/roles/role.cleaner.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const profiler = require("tools.profiler"); 6 | 7 | class RoleCleaner { 8 | constructor(creep) { 9 | this.creep = creep; 10 | this.room = creep.room; 11 | this.performRoleActions(); 12 | } 13 | 14 | performRoleActions() { 15 | if (this.housekeeping()) return; 16 | if (this.creep.memory.barrierClearing) { 17 | this.barrierCleaning(); 18 | } else if (this.creep.memory.destination && this.room.name !== this.creep.memory.destination) { 19 | this.travel(); 20 | } else { 21 | this.cleanRoom(); 22 | } 23 | } 24 | 25 | housekeeping() { 26 | // Boosting 27 | if (this.creep.tryToBoost()) return true; 28 | this.creep.say('NOM!', true); 29 | } 30 | 31 | barrierCleaning() { 32 | let barrier = Game.getObjectById(this.creep.memory.target); 33 | if (!barrier) return this.creep.memory.target = undefined; 34 | if (this.creep.pos.isNearTo(barrier)) { 35 | if (this.creep.hasActiveBodyparts(WORK)) { 36 | if (this.creep.dismantle(barrier) === OK) this.creep.memory.other.stationary = true; 37 | } 38 | } else this.creep.shibMove(barrier); 39 | } 40 | 41 | travel() { 42 | this.creep.shibMove(new RoomPosition(25, 25, this.creep.memory.destination)); 43 | } 44 | 45 | cleanRoom() { 46 | // If we can't get to the controller or sources, clear a path 47 | const blocked = blockedLocations(this.creep); 48 | if (blocked && blocked.length) { 49 | this.creep.memory.target = blocked[0].structure.id; 50 | } else if (!this.creep.scorchedEarth()) { 51 | this.room.cacheRoomIntel(true); 52 | this.creep.suicide(); 53 | } 54 | } 55 | } 56 | 57 | function blockedLocations(creep) { 58 | if (!creep.memory.notBlocked) { 59 | // Check controller 60 | if (findBestCleaningPath(creep, creep.room.controller).length) return findBestCleaningPath(creep, creep.room.controller); 61 | else INTEL[creep.room.name].claimClear = true; 62 | // Check sources 63 | for (const source of creep.room.sources) { 64 | if (findBestCleaningPath(creep, source).length) return findBestCleaningPath(creep, source); 65 | } 66 | // Check exits 67 | for (const exit of Object.values(Game.map.describeExits(creep.room.name))) { 68 | const targetPos = creep.pos.findClosestByRange(creep.room.findExitTo(exit)) 69 | if (findBestCleaningPath(creep, targetPos).length) return findBestCleaningPath(creep, targetPos); 70 | } 71 | } 72 | creep.memory.notBlocked = true; 73 | } 74 | 75 | function findBestCleaningPath(creep, target) { 76 | const room = creep.room; 77 | if (!room) return {path: null, structures: []}; // Room not visible 78 | const costMatrix = new PathFinder.CostMatrix(); 79 | room.find(FIND_STRUCTURES).forEach(structure => { 80 | if (structure.structureType === STRUCTURE_RAMPART || structure.structureType === STRUCTURE_WALL) { 81 | if (structure.owner && structure.owner.username === MY_USERNAME) { 82 | costMatrix.set(structure.pos.x, structure.pos.y, 1); 83 | } else { 84 | // Calculate the cost based on hits, higher hits = higher cost 85 | let cost = Math.floor(structure.hits / 100000); // Adjust this divisor as needed 86 | // Cap the cost to prevent impassable barriers 87 | cost = Math.min(cost, 255); // 255 is the max cost in a CostMatrix 88 | costMatrix.set(structure.pos.x, structure.pos.y, cost); 89 | } 90 | } 91 | }); 92 | if (!(target instanceof RoomPosition)) target = target.pos; 93 | // Pathfinding options 94 | const pathOptions = { 95 | roomCallback: function (roomName) { 96 | if (roomName === room.name) { 97 | return costMatrix; 98 | } 99 | return false; 100 | }, 101 | plainCost: 1, 102 | swampCost: 2, 103 | maxOps: 2000, 104 | }; 105 | const path = PathFinder.search(creep.pos, {pos: target, range: 1}, pathOptions); 106 | let structuresOnPath = []; 107 | if (path.path.length > 0) { 108 | structuresOnPath = path.path.reduce((acc, pos) => { 109 | const structures = room.lookForAt(LOOK_STRUCTURES, pos.x, pos.y); 110 | structures.forEach(structure => { 111 | if (structure.structureType === STRUCTURE_RAMPART || structure.structureType === STRUCTURE_WALL) { 112 | acc.push({pos: structure.pos, structure: structure}); 113 | } 114 | }); 115 | return acc; 116 | }, []); 117 | } 118 | return structuresOnPath; 119 | } 120 | 121 | profiler.registerClass(RoleCleaner, 'Cleaner'); 122 | module.exports = RoleCleaner; 123 | -------------------------------------------------------------------------------- /default/roles/role.commodityMiner.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const profiler = require("tools.profiler"); 6 | 7 | class RoleCommodityMiner { 8 | constructor(creep) { 9 | this.creep = creep; 10 | this.room = creep.room; 11 | this.performRoleActions(); 12 | } 13 | 14 | // Placeholder for role-specific actions 15 | performRoleActions() { 16 | if (this.housekeeping()) return; 17 | if (!this.creep.memory.deposit && this.room.name !== this.creep.memory.destination && !_.sum(this.creep.store)) { 18 | this.travelToDeposit(); 19 | } else if (this.creep.memory.deposit && !this.creep.isFull && this.creep.ticksToLive > this.creep.memory.tickCutoff) { 20 | this.harvest(); 21 | } else if (_.sum(this.creep.store)) { 22 | this.creep.memory.deposit = undefined; 23 | this.returnResource(); 24 | } else { 25 | this.findDeposit(); 26 | } 27 | } 28 | 29 | housekeeping() { 30 | // Boosting 31 | if (this.creep.tryToBoost()) return true; 32 | 33 | // If unsafe return home 34 | if (this.creep.skSafety()) return true; 35 | 36 | // Set dropoff 37 | this.creep.memory.closestRoom = this.creep.memory.closestRoom || findClosestOwnedRoom(this.room.name, false, 4) || this.creep.memory.colony; 38 | 39 | // Set tick cutoff 40 | if (!this.creep.memory.tickCutoff) this.creep.memory.tickCutoff = (Game.map.getRoomLinearDistance(this.creep.memory.closestRoom, this.creep.memory.destination) + 3) * 50; 41 | 42 | // Old age and work/carry part check 43 | if (this.creep.ticksToLive < this.creep.memory.tickCutoff || !this.creep.hasActiveBodyparts(WORK) || !this.creep.hasActiveBodyparts(CARRY)) { 44 | this.creep.recycleCreep(); 45 | return true; 46 | } 47 | 48 | // Make sure the operation is active and valid destination exists 49 | if (!this.creep.memory.destination || !Memory.auxiliaryTargets[this.creep.memory.destination]) { 50 | this.creep.recycleCreep(); 51 | return true; 52 | } 53 | } 54 | 55 | travelToDeposit() { 56 | const destination = Game.getObjectById(this.creep.memory.deposit) || new RoomPosition(25, 25, this.creep.memory.destination); 57 | return this.creep.shibMove(destination, {range: 1}); 58 | } 59 | 60 | harvest() { 61 | let deposit = Game.getObjectById(this.creep.memory.deposit); 62 | // Clear the deposit if needed 63 | if (!deposit || (!deposit.depositType && !deposit.mineralAmount)) return this.creep.memory.deposit = undefined; 64 | // Refresh the operation 65 | if (Memory.auxiliaryTargets[this.creep.memory.destination]) Memory.auxiliaryTargets[this.creep.memory.destination].tick = Game.time; 66 | switch (this.creep.harvest(deposit)) { 67 | case OK: 68 | this.creep.memory.other.stationary = true; 69 | break; 70 | case ERR_NOT_IN_RANGE: 71 | this.creep.shibMove(deposit); 72 | break; 73 | case ERR_NOT_ENOUGH_RESOURCES: 74 | this.creep.memory.deposit = undefined; 75 | break; 76 | case ERR_TIRED: 77 | if (this.creep.pos.isNearTo(deposit)) this.creep.idleFor(deposit.cooldown); 78 | } 79 | } 80 | 81 | returnResource() { 82 | this.creep.memory.other.stationary = undefined; 83 | this.creep.memory.closestRoom = this.creep.memory.closestRoom || findClosestOwnedRoom(this.room.name, false, 4) || this.creep.memory.colony; 84 | if (this.room.name !== this.creep.memory.closestRoom) { 85 | return this.creep.shibMove(new RoomPosition(25, 25, this.creep.memory.closestRoom), {range: 23}); 86 | } else { 87 | let deliver = this.room.terminal || this.room.storage; 88 | if (deliver) { 89 | for (let resourceType in this.creep.store) { 90 | switch (this.creep.transfer(deliver, resourceType)) { 91 | case OK: 92 | this.creep.memory.hauling = undefined; 93 | break; 94 | case ERR_NOT_IN_RANGE: 95 | this.creep.shibMove(deliver); 96 | break; 97 | } 98 | } 99 | } 100 | } 101 | } 102 | 103 | findDeposit() { 104 | //Find Deposit 105 | let deposit = _.find(this.room.deposits, (d) => (d.depositType || d.mineralAmount)); 106 | // If no deposits check for a mineral 107 | if (!deposit && this.room.mineral && !this.room.controller) { 108 | deposit = this.room.mineral; 109 | if (deposit) { 110 | return this.creep.memory.deposit = deposit.id; 111 | } 112 | } else if (!deposit) { 113 | INTEL[this.creep.memory.destination].commodity = undefined; 114 | Memory.auxiliaryTargets[this.creep.memory.destination] = undefined; 115 | this.creep.recycleCreep(); 116 | return this.creep.memory.deposit = undefined; 117 | } else { 118 | // Choose a random deposit 119 | return this.creep.memory.deposit = deposit.id; 120 | } 121 | } 122 | } 123 | 124 | profiler.registerClass(RoleCommodityMiner, 'CommodityMiner'); 125 | module.exports = RoleCommodityMiner; 126 | -------------------------------------------------------------------------------- /default/roles/role.defender.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const profiler = require("tools.profiler"); 6 | 7 | class RoleDefender { 8 | constructor(creep) { 9 | this.creep = creep; 10 | this.room = creep.room; 11 | this.performRoleActions(); 12 | } 13 | 14 | performRoleActions() { 15 | if (this.housekeeping()) return; 16 | this.defenseActions(); 17 | } 18 | 19 | housekeeping() { 20 | // Boosting 21 | if (this.creep.tryToBoost()) return true; 22 | } 23 | 24 | defenseActions() { 25 | if (!INTEL[this.room.name].threatLevel && this.creep.ticksToLive <= 100) return this.creep.recycleCreep(); 26 | if (!this.creep.handleMilitaryCreep() && this.creep.findDefensivePosition(this.creep)) this.creep.idleFor(5); 27 | } 28 | } 29 | 30 | profiler.registerClass(RoleDefender, 'Defender'); 31 | module.exports = RoleDefender; 32 | -------------------------------------------------------------------------------- /default/roles/role.explorer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const profiler = require("tools.profiler"); 6 | 7 | class RoleExplorer { 8 | constructor(creep) { 9 | this.creep = creep; 10 | this.room = creep.room; 11 | this.performRoleActions(); 12 | } 13 | 14 | performRoleActions() { 15 | this.housekeeping(); 16 | if (!this.creep.memory.destination) { 17 | this.findDestination(); 18 | } else if (this.room.name === this.creep.memory.destination) { 19 | this.exploreRoom(); 20 | } else { 21 | this.travel(); 22 | } 23 | } 24 | 25 | housekeeping() { 26 | this.creep.say(ICONS.eye, true); 27 | } 28 | 29 | findDestination() { 30 | let portal = Game.getObjectById(this.creep.memory.portal) || this.creep.pos.findClosestByRange(_.filter(this.creep.room.structures, (s) => s.structureType === STRUCTURE_PORTAL && !s.destination.shard)); 31 | if (!this.creep.memory.usedPortal && portal && (this.creep.memory.other.portalJump || Math.random() > 0.01)) { 32 | if (!this.creep.memory.other.portalJump) { 33 | let roomName; 34 | if (portal.destination.shard) roomName = portal.destination.room.name; else roomName = portal.destination.roomName; 35 | this.creep.memory.other.portalJump = roomName; 36 | if (!this.creep.memory.portal) log.a(this.creep.name + ' has found a portal in ' + roomLink(this.room.name) + ' and is taking it.') 37 | this.creep.memory.portal = portal.id; 38 | } else if (this.creep.memory.other.portalJump === this.creep.room.name) { 39 | return this.creep.memory.usedPortal = true; 40 | } 41 | return this.creep.shibMove(portal, {range: 0}); 42 | } else { 43 | const exits = Game.map.describeExits(this.room.name); 44 | const rooms = Object.keys(exits).map((direction) => { 45 | return {name: exits[direction], direction: parseInt(direction, 10)}; 46 | }); 47 | let adjacent = _.filter(rooms, (r) => !this.room.lookForAt(LOOK_STRUCTURES, this.room.find(r.direction)[0])[0] && roomStatus(r.name) !== 'closed' && pathableExit(this.creep, this.room.find(r.direction)[0])); 48 | // Filter out the last room if we have options 49 | if (this.creep.memory.lastRoom && adjacent.length > 1) adjacent = _.filter(adjacent, (a) => a.name !== this.creep.memory.lastRoom); 50 | // If there's unexplored prioritize else pick random 51 | let target = _.find(adjacent, (r) => !INTEL[r.name]) || _.sample(adjacent); 52 | if (target && target.name) this.creep.memory.destination = target.name; else this.creep.idleFor(6); 53 | } 54 | } 55 | 56 | exploreRoom() { 57 | if (SIGN_ROOMS && this.creep.memory.lastRoom !== this.room.name) return this.signRooms(); 58 | this.creep.memory.destination = undefined; 59 | this.creep.memory.lastRoom = this.room.name; 60 | } 61 | 62 | travel() { 63 | this.creep.shibMove(new RoomPosition(25, 25, this.creep.memory.destination), {range: 22}); 64 | } 65 | 66 | signRooms() { 67 | if (this.room.controller && (!this.room.controller.sign || this.room.controller.sign.username !== MY_USERNAME) && this.creep.pos.findClosestByPath(this.room.controller)) { 68 | switch (this.creep.signController(this.room.controller, _.sample(EXPLORED_ROOM_SIGNS) + ` - ` + Game.time)) { 69 | case ERR_NOT_IN_RANGE: 70 | if (!this.creep.memory.signAttempt) this.creep.memory.signAttempt = Game.time; 71 | else if (this.creep.memory.signAttempt + 50 < Game.time) { 72 | this.creep.memory.signAttempt = undefined; 73 | return this.creep.memory.lastRoom = this.room.name; 74 | } 75 | return this.creep.shibMove(this.room.controller); 76 | } 77 | } 78 | this.creep.memory.lastRoom = this.room.name; 79 | } 80 | } 81 | 82 | function pathableExit(creep, exitPosition) { 83 | const roomCallback = (roomName) => { 84 | const room = Game.rooms[roomName]; 85 | const costMatrix = new PathFinder.CostMatrix(); 86 | if (room) { 87 | const structures = room.impassibleStructures; 88 | structures.forEach((structure) => costMatrix.set(structure.pos.x, structure.pos.y, 256)); 89 | } 90 | return costMatrix; 91 | }; 92 | const search = PathFinder.search(creep.pos, exitPosition, { 93 | maxRooms: 0, 94 | roomCallback: roomCallback, 95 | }); 96 | return search.incomplete !== true && search.path.length > 3; 97 | } 98 | 99 | profiler.registerClass(RoleExplorer, 'Explorer'); 100 | module.exports = RoleExplorer; -------------------------------------------------------------------------------- /default/roles/role.hauler.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const profiler = require("tools.profiler"); 6 | 7 | class RoleHauler { 8 | constructor(creep) { 9 | this.creep = creep; 10 | this.performRoleActions(); 11 | } 12 | 13 | performRoleActions() { 14 | this.housekeeping() 15 | if (_.sum(this.creep.store) > this.creep.store.getCapacity() * 0.2) { 16 | this.deliverResource(); 17 | } else { 18 | this.findResource(); 19 | } 20 | } 21 | 22 | housekeeping() { 23 | this.creep.say(ICONS.haul, true); 24 | this.creep.opportunisticFill(); 25 | } 26 | 27 | deliverResource() { 28 | if (!this.creep.haulerDelivery() && _.sum(this.creep.store)) return; 29 | } 30 | 31 | findResource() { 32 | if (!this.creep.memory.energyDestination) this.creep.memory._shibMove = undefined; 33 | if (this.creep.memory.energyDestination || this.creep.locateEnergy()) { 34 | this.creep.withdrawResource() 35 | } else { 36 | this.creep.idleFor(5); 37 | } 38 | } 39 | } 40 | 41 | profiler.registerClass(RoleHauler, 'Hauler'); 42 | module.exports = RoleHauler; 43 | -------------------------------------------------------------------------------- /default/roles/role.longbow.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const profiler = require("tools.profiler"); 6 | 7 | class RoleLongbow { 8 | constructor(creep) { 9 | this.creep = creep; 10 | this.room = creep.room; 11 | this.performRoleActions(); 12 | } 13 | 14 | performRoleActions() { 15 | if (this.housekeeping()) return; 16 | if (this.creep.memory.operation) { 17 | this.operationManagement(); 18 | } else if (this.creep.memory.destination) { 19 | this.destinationManagement(); 20 | } 21 | } 22 | 23 | housekeeping() { 24 | // Boosting 25 | if (this.creep.tryToBoost()) return true; 26 | // Blinky mode 27 | this.creep.healInRange(this.room.hostileCreeps.length || this.room.hostileStructures.length); 28 | // Check and set partner if conditions warrant 29 | if (this.creep.formSquad()) return true; 30 | } 31 | 32 | operationManagement() { 33 | switch (this.creep.memory.operation) { 34 | case 'borderPatrol': 35 | this.creep.borderPatrol(); 36 | break; 37 | case 'guard': 38 | this.creep.guardRoom(); 39 | break; 40 | case 'roomDenial': 41 | this.creep.denyRoom(); 42 | break; 43 | case 'harass': 44 | this.creep.harass(); 45 | break; 46 | case 'remoteDenial': 47 | this.creep.remoteDenial(); 48 | break; 49 | } 50 | } 51 | 52 | destinationManagement() { 53 | // Combat handling 54 | if (this.creep.handleMilitaryCreep()) return; 55 | 56 | // Healing 57 | if (this.creep.hits < this.creep.hitsMax) { 58 | if (this.creep.hasActiveBodyparts(HEAL)) { 59 | this.creep.findDefensivePosition(); 60 | return this.creep.heal(this.creep); 61 | } else { 62 | return this.creep.fleeHome(); 63 | } 64 | } 65 | 66 | if (this.room.name !== this.creep.memory.destination) { 67 | return this.creep.shibMove(new RoomPosition(25, 25, this.creep.memory.destination), {range: 22}); 68 | } else { 69 | if (this.creep.findDefensivePosition()) this.creep.idleFor(5); 70 | } 71 | } 72 | } 73 | 74 | profiler.registerClass(RoleLongbow, 'Longbow'); 75 | module.exports = RoleLongbow; 76 | -------------------------------------------------------------------------------- /default/roles/role.mineralHarvester.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const profiler = require("tools.profiler"); 6 | 7 | class RoleMineralHarvester { 8 | constructor(creep) { 9 | this.creep = creep; 10 | this.room = creep.room; 11 | this.performRoleActions(); 12 | } 13 | 14 | performRoleActions() { 15 | if (this.housekeeping()) return; 16 | if (!this.creep.memory.extractor) { 17 | this.setExtractor(); 18 | } else { 19 | this.extractResource(); 20 | } 21 | } 22 | 23 | housekeeping() { 24 | // Boosting 25 | if (this.creep.tryToBoost()) return true; 26 | // Check if mineral depleted 27 | if (this.creep.memory.other.assignedMineral && Game.getObjectById(this.creep.memory.other.assignedMineral).mineralAmount === 0) { 28 | log.a(this.room.name + ' supply of ' + Game.getObjectById(this.creep.memory.other.assignedMineral).mineralType + ' has been depleted.'); 29 | return this.creep.recycleCreep(); 30 | } 31 | } 32 | 33 | setExtractor() { 34 | let extractor = this.room.structures.filter((s) => s.structureType === STRUCTURE_EXTRACTOR)[0]; 35 | if (extractor) { 36 | this.creep.memory.extractor = extractor.id; 37 | } else { 38 | this.creep.recycleCreep(); 39 | } 40 | } 41 | 42 | extractResource() { 43 | if (!this.creep.memory.onContainer) { 44 | let container = Game.getObjectById(this.room.memory.extractorContainer); 45 | if (container) { 46 | if (this.creep.pos.getRangeTo(container)) return this.creep.shibMove(container, {range: 0}); else this.creep.memory.onContainer = true; 47 | } else { 48 | this.creep.memory.onContainer = true; 49 | } 50 | } else if (Math.random() > 0.9) this.creep.memory.onContainer = undefined; 51 | let extractor = Game.getObjectById(this.creep.memory.extractor); 52 | if (!extractor) return this.creep.recycleCreep(); 53 | if (Game.getObjectById(this.room.memory.extractorContainer) && _.sum(Game.getObjectById(this.room.memory.extractorContainer).store) === 2000 54 | && !this.creep.pos.getRangeTo(Game.getObjectById(this.room.memory.extractorContainer))) return this.creep.idleFor(25); 55 | if (extractor.cooldown && extractor.pos.getRangeTo(this.creep) < 2) { 56 | this.creep.idleFor(extractor.cooldown - 1) 57 | } else { 58 | let mineral = Game.getObjectById(this.creep.memory.other.assignedMineral); 59 | switch (this.creep.harvest(mineral)) { 60 | case OK: 61 | this.creep.memory.other.stationary = true; 62 | break; 63 | case ERR_NOT_IN_RANGE: 64 | this.creep.shibMove(mineral); 65 | break; 66 | case ERR_NOT_FOUND: 67 | mineral.pos.createConstructionSite(STRUCTURE_EXTRACTOR); 68 | break; 69 | } 70 | } 71 | } 72 | } 73 | 74 | profiler.registerClass(RoleMineralHarvester, 'MineralHarvester'); 75 | module.exports = RoleMineralHarvester; 76 | -------------------------------------------------------------------------------- /default/roles/role.powerAttacker.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const profiler = require("tools.profiler"); 6 | 7 | class RolePowerAttacker { 8 | constructor(creep) { 9 | this.creep = creep; 10 | this.room = creep.room; 11 | this.performRoleActions(); 12 | } 13 | 14 | performRoleActions() { 15 | if (this.housekeeping()) return; 16 | if (this.room.name !== this.creep.memory.destination) { 17 | this.travel(); 18 | } else { 19 | this.extractResource(); 20 | } 21 | } 22 | 23 | housekeeping() { 24 | if (!Memory.auxiliaryTargets[this.creep.memory.destination]) return this.creep.recycleCreep(); 25 | } 26 | 27 | travel() { 28 | this.creep.shibMove(new RoomPosition(25, 25, this.creep.memory.destination), {range: 23}); 29 | } 30 | 31 | extractResource() { 32 | if (!this.creep.hasActiveBodyparts(ATTACK) || this.creep.hits < this.creep.hitsMax * 0.25) return; 33 | // Handle military 34 | let armedHostile = _.find(this.creep.room.hostileCreeps, (c) => c.hasActiveBodyparts(ATTACK) || c.hasActiveBodyparts(RANGED_ATTACK)); 35 | if (armedHostile) { 36 | this.creep.handleMilitaryCreep(); 37 | } else if (this.creep.memory.powerBank) { 38 | let powerBank = Game.getObjectById(this.creep.memory.powerBank); 39 | if (!powerBank) { 40 | return Memory.auxiliaryTargets[this.creep.memory.destination] = undefined; 41 | } 42 | if (!Memory.auxiliaryTargets[this.creep.memory.destination].space) Memory.auxiliaryTargets[this.creep.memory.destination].space = powerBank.pos.countOpenTerrainAround(); 43 | if (powerBank.hits < 350000) Memory.auxiliaryTargets[this.creep.memory.destination].hauler = powerBank.power / 1250; 44 | if (!powerBank) { 45 | Memory.auxiliaryTargets[this.creep.memory.destination].complete = true; 46 | } 47 | switch (this.creep.attack(powerBank)) { 48 | case OK: 49 | this.creep.memory.other.stationary = true; 50 | break; 51 | case ERR_NOT_IN_RANGE: 52 | this.creep.shibMove(powerBank); 53 | break; 54 | } 55 | } else { 56 | let powerBank = _.find(this.room.impassibleStructures, (s) => s.structureType === STRUCTURE_POWER_BANK); 57 | if (powerBank) { 58 | this.creep.memory.powerBank = powerBank.id; 59 | } else { 60 | Memory.auxiliaryTargets[this.creep.memory.destination] = undefined; 61 | } 62 | } 63 | } 64 | } 65 | 66 | profiler.registerClass(RolePowerAttacker, 'PowerAttacker'); 67 | module.exports = RolePowerAttacker; 68 | -------------------------------------------------------------------------------- /default/roles/role.powerHauler.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const profiler = require("tools.profiler"); 6 | 7 | class RolePowerHauler { 8 | constructor(creep) { 9 | this.creep = creep; 10 | this.room = creep.room; 11 | this.performRoleActions(); 12 | } 13 | 14 | performRoleActions() { 15 | if (this.housekeeping()) return; 16 | else if (this.creep.memory.misc && this.creep.memory.misc.deliveryRoom) { 17 | this.roomDelivery(); 18 | } else if (_.sum(this.creep.store)) { 19 | this.deliverResource(); 20 | } else { 21 | this.pickupResource(); 22 | } 23 | } 24 | 25 | housekeeping() { 26 | // If low TTL return home and recycle 27 | if (this.creep.ticksToLive < 75) { 28 | this.creep.memory.destination = undefined; 29 | return this.creep.recycleCreep(); 30 | } 31 | } 32 | 33 | pickupResource() { 34 | if (this.room.name !== this.creep.memory.destination) { 35 | return this.creep.shibMove(new RoomPosition(25, 25, this.creep.memory.destination), {range: 23}); 36 | } 37 | let power = this.room.find(FIND_DROPPED_RESOURCES, {filter: (r) => r.resourceType === RESOURCE_POWER})[0]; 38 | if (power) { 39 | switch (this.creep.pickup(power)) { 40 | case OK: 41 | this.creep.memory.hauling = true; 42 | break; 43 | case ERR_NOT_IN_RANGE: 44 | this.creep.shibMove(power); 45 | break; 46 | } 47 | } else { 48 | if (!_.find(this.room.impassibleStructures, (s) => s.structureType === STRUCTURE_POWER_BANK)) { 49 | Memory.auxiliaryTargets[this.room.name] = undefined; 50 | this.creep.suicide(); 51 | } 52 | } 53 | } 54 | 55 | deliverResource() { 56 | this.creep.memory.closestRoom = this.creep.memory.closestRoom || findClosestOwnedRoom(this.room.name, false, 6); 57 | if (this.room.name !== this.creep.memory.closestRoom) { 58 | return this.creep.shibMove(new RoomPosition(25, 25, this.creep.memory.closestRoom), {range: 23}); 59 | } else { 60 | let deliver = _.filter(this.room.impassibleStructures, (s) => s.structureType === STRUCTURE_POWER_SPAWN && s.power < s.store.getFreeCapacity(RESOURCE_POWER))[0] || this.room.terminal || this.room.storage; 61 | if (deliver) { 62 | switch (this.creep.transfer(deliver, RESOURCE_POWER)) { 63 | case OK: 64 | this.creep.memory.hauling = _.sum(this.creep.store) > 0; 65 | break; 66 | case ERR_NOT_IN_RANGE: 67 | this.creep.shibMove(deliver); 68 | break; 69 | } 70 | } 71 | } 72 | } 73 | 74 | roomDelivery() { 75 | this.creep.say('Delivery!', true); 76 | if (_.sum(this.creep.store)) { 77 | const deliveryRoom = Game.rooms[this.creep.memory.misc.deliveryRoom]; 78 | if (this.room.name !== deliveryRoom.name) { 79 | return this.creep.shibMove(new RoomPosition(25, 25, this.creep.memory.misc.deliveryRoom), {range: 23}); 80 | } else { 81 | let deliver = this.room.terminal || this.room.storage; 82 | if (deliver) { 83 | for (let resourceType in this.creep.store) { 84 | switch (this.creep.transfer(deliver, resourceType)) { 85 | case ERR_NOT_IN_RANGE: 86 | this.creep.shibMove(deliver); 87 | } 88 | } 89 | } else { 90 | this.creep.shibMove(deliveryRoom.controller, {range: 3}); 91 | } 92 | } 93 | } else { 94 | if (this.room.name !== this.creep.memory.colony) { 95 | return this.creep.shibMove(new RoomPosition(25, 25, this.creep.memory.colony), {range: 23}); 96 | } else { 97 | if (this.creep.memory.energyDestination || this.creep.locateEnergy()) { 98 | this.creep.say('Energy!', true); 99 | this.creep.withdrawResource(); 100 | } 101 | } 102 | } 103 | } 104 | } 105 | 106 | profiler.registerClass(RolePowerHauler, 'PowerHauler'); 107 | module.exports = RolePowerHauler; 108 | -------------------------------------------------------------------------------- /default/roles/role.powerHealer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const profiler = require("tools.profiler"); 6 | 7 | class RolePowerHealer { 8 | constructor(creep) { 9 | this.creep = creep; 10 | this.room = creep.room; 11 | this.performRoleActions(); 12 | } 13 | 14 | performRoleActions() { 15 | if (this.housekeeping()) return; 16 | if (this.room.name !== this.creep.memory.destination) { 17 | this.travel(); 18 | } else { 19 | this.healerDuty(); 20 | } 21 | } 22 | 23 | housekeeping() { 24 | if (!Memory.auxiliaryTargets[this.creep.memory.destination]) return this.creep.recycleCreep(); 25 | } 26 | 27 | travel() { 28 | this.creep.shibMove(new RoomPosition(25, 25, this.creep.memory.destination), {range: 23}); 29 | } 30 | 31 | healerDuty() { 32 | let powerBank = _.filter(this.room.impassibleStructures, (s) => s.structureType === STRUCTURE_POWER_BANK)[0]; 33 | if (powerBank && this.creep.pos.isNearTo(powerBank)) this.creep.moveRandom(); 34 | if (this.creep.memory.assigned) { 35 | let assignment = Game.getObjectById(this.creep.memory.assigned); 36 | if (!assignment) return this.creep.memory.assigned = undefined; 37 | switch (this.creep.heal(assignment)) { 38 | case ERR_NOT_IN_RANGE: 39 | this.creep.shibMove(assignment, {ignoreCreeps: false}); 40 | this.creep.rangedHeal(assignment); 41 | } 42 | } else { 43 | let attacker = _.filter(this.room.myCreeps, (c) => c.memory.role === 'powerAttacker' && !_.filter(this.room.creeps, (h) => h.my && h.memory.assigned === c.id)[0])[0]; 44 | if (attacker) this.creep.memory.assigned = attacker.id; else { 45 | if (this.creep.pos.getRangeTo(powerBank) > 2) this.creep.shibMove(powerBank, {range: 2}); 46 | this.creep.healInRange(); 47 | } 48 | } 49 | } 50 | } 51 | 52 | profiler.registerClass(RolePowerHealer, 'PowerHealer'); 53 | module.exports = RolePowerHealer; 54 | -------------------------------------------------------------------------------- /default/roles/role.powerManager.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | //TODO: move to labTech 5 | module.exports.role = function (creep) { 6 | //INITIAL CHECKS 7 | // Hauler mode 8 | if (creep.memory.haulerMode) { 9 | if (!creep.store[RESOURCE_ENERGY] && creep.memory.haulerMode + 50 < Game.time) return delete creep.memory.haulerMode; 10 | const haulerRole = require('role.hauler'); 11 | return new haulerRole(creep); 12 | } 13 | creep.say(ICONS.power, true); 14 | let powerSpawn = _.filter(creep.room.impassibleStructures, (s) => s.structureType === STRUCTURE_POWER_SPAWN)[0]; 15 | if (!powerSpawn) return creep.recycleCreep(); 16 | let powerSource, energySource; 17 | if (creep.room.storage.store[RESOURCE_POWER]) powerSource = creep.room.storage; else if (creep.room.terminal.store[RESOURCE_POWER]) powerSource = creep.room.terminal; 18 | if (creep.room.storage.store[RESOURCE_ENERGY]) energySource = creep.room.storage; else if (creep.room.terminal.store[RESOURCE_ENERGY]) energySource = creep.room.terminal; 19 | if (creep.store[RESOURCE_ENERGY] && powerSpawn.store[RESOURCE_ENERGY] < POWER_SPAWN_ENERGY_CAPACITY) { 20 | switch (creep.transfer(powerSpawn, RESOURCE_ENERGY)) { 21 | case OK: 22 | return true; 23 | case ERR_NOT_IN_RANGE: 24 | creep.shibMove(powerSpawn); 25 | return true; 26 | } 27 | } else if (creep.store[RESOURCE_POWER] && powerSpawn.power !== powerSpawn.powerCapacity) { 28 | switch (creep.transfer(powerSpawn, RESOURCE_POWER)) { 29 | case OK: 30 | return false; 31 | case ERR_NOT_IN_RANGE: 32 | creep.shibMove(powerSpawn); 33 | return true; 34 | } 35 | } else if (_.sum(creep.store)) { 36 | creep.memory.storageDestination = creep.room.terminal.id || creep.room.storage.id; 37 | if (creep.memory.storageDestination) { 38 | let storageItem = Game.getObjectById(creep.memory.storageDestination); 39 | for (const resourceType in creep.store) { 40 | switch (creep.transfer(storageItem, resourceType)) { 41 | case OK: 42 | creep.memory.storageDestination = undefined; 43 | break; 44 | case ERR_NOT_IN_RANGE: 45 | creep.shibMove(storageItem); 46 | break; 47 | case ERR_FULL: 48 | creep.memory.storageDestination = undefined; 49 | break; 50 | } 51 | } 52 | } 53 | } else if (creep.memory.energyDestination) { 54 | creep.withdrawResource(); 55 | } else if (energySource && powerSpawn.store[RESOURCE_ENERGY] < 1000) { 56 | switch (creep.withdraw(energySource, RESOURCE_ENERGY)) { 57 | case OK: 58 | return true; 59 | case ERR_NOT_IN_RANGE: 60 | creep.shibMove(energySource); 61 | return true; 62 | } 63 | } else if (powerSource && powerSpawn.power !== powerSpawn.powerCapacity) { 64 | switch (creep.withdraw(powerSource, RESOURCE_POWER)) { 65 | case OK: 66 | return true; 67 | case ERR_NOT_IN_RANGE: 68 | creep.shibMove(powerSource); 69 | return true; 70 | } 71 | } else { 72 | // If nothing to do, be a hauler for 50 ticks 73 | creep.memory.haulerMode = Game.time; 74 | } 75 | }; -------------------------------------------------------------------------------- /default/roles/role.remoteHarvester.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const profiler = require("tools.profiler"); 6 | 7 | class RoleRemoteHarvester { 8 | constructor(creep) { 9 | this.creep = creep; 10 | this.room = creep.room; 11 | this.container = Game.getObjectById(this.creep.memory.containerID) || Game.getObjectById(this.creep.memory.containerSite); 12 | this.performRoleActions(); 13 | } 14 | 15 | performRoleActions() { 16 | if (this.housekeeping()) return; 17 | this.harvestSource(); 18 | } 19 | 20 | housekeeping() { 21 | // SK Safety 22 | if (this.creep.skSafety()) { 23 | this.creep.memory.onContainer = undefined; 24 | return true; 25 | } 26 | // Handle room reservation/ownership 27 | if (this.room.controller && (this.room.controller.reservation && this.room.controller.reservation.username !== MY_USERNAME)) { 28 | this.room.cacheRoomIntel(true); 29 | return this.creep.recycleCreep(); 30 | } 31 | // Periodically check the container 32 | if (this.creep.memory.onContainer && this.container && this.creep.pos.getRangeTo(this.container)) { 33 | this.creep.memory.onContainer = undefined; 34 | } 35 | } 36 | 37 | harvestSource() { 38 | const source = Game.getObjectById(this.creep.memory.other.source); 39 | if (!source) { 40 | // Move to a general area if source not found 41 | return this.creep.shibMove(new RoomPosition(25, 25, this.creep.memory.destination), {range: 15}); 42 | } 43 | 44 | // Move to or stay on container 45 | if (this.container && !this.creep.memory.onContainer) { 46 | if (this.creep.pos.getRangeTo(this.container)) { 47 | return this.creep.shibMove(this.container, {range: 0}); 48 | } 49 | this.creep.memory.onContainer = true; 50 | } else if (!this.container) { 51 | harvestDepositContainer(Game.getObjectById(this.creep.memory.other.source), this.creep); 52 | } else if (!this.creep.memory.onContainer && !this.creep.pos.isNearTo(source)) { 53 | return this.creep.shibMove(source); 54 | } 55 | 56 | // Harvest logic 57 | switch (this.creep.harvest(source)) { 58 | case ERR_NOT_IN_RANGE: 59 | return this.creep.shibMove(source); 60 | case ERR_NOT_ENOUGH_RESOURCES: 61 | return this.creep.idleFor(source.ticksToRegeneration + 1); 62 | case OK: 63 | // Set harvest power if not set 64 | if (!this.creep.memory.other.haulingRequired) { 65 | const power = this.creep.getActiveBodyparts(WORK) * HARVEST_POWER; 66 | const sourceInfo = _.find(ROOM_REMOTE_TARGETS[this.creep.memory.colony], (s) => s.source === this.creep.memory.other.source); 67 | const sourceScore = sourceInfo ? sourceInfo.score : 30; 68 | this.creep.memory.other.haulingRequired = power * (sourceScore * 2); 69 | } 70 | // Handle container or construction site 71 | if (this.container) { 72 | this.handleContainer(); 73 | } else { 74 | this.handleDroppedResources(); 75 | } 76 | break; 77 | } 78 | } 79 | 80 | handleContainer() { 81 | // Repair or manage container status 82 | if (this.container.hits) { 83 | if (this.creep.store[RESOURCE_ENERGY] && this.container.hits < this.container.hitsMax * 0.5) { 84 | return this.creep.repair(this.container); 85 | } 86 | const containerStore = _.sum(this.container.store); 87 | if (containerStore >= CONTAINER_CAPACITY * 0.75 && this.container.hits < this.container.hitsMax) { 88 | return this.creep.repair(this.container); 89 | } else if (containerStore >= CONTAINER_CAPACITY * 0.8) { 90 | this.handleHaulerCheck(); 91 | } else if (Game.rooms[this.creep.memory.colony].memory.additionalRemoteHaulingNeeded < Game.time) { 92 | Game.rooms[this.creep.memory.colony].memory.additionalRemoteHaulingNeeded = undefined; 93 | } 94 | this.creep.memory.energyAmount = containerStore; 95 | this.creep.memory.energyId = this.container.id; 96 | } else if (this.container.progressTotal) { // If it's a construction site 97 | const dropped = this.creep.pos.lookFor(LOOK_RESOURCES)[0]; 98 | if (dropped && dropped.amount > 500 && !this.creep.store.getFreeCapacity()) { 99 | this.creep.build(this.container); 100 | } 101 | this.creep.memory.energyAmount = dropped ? dropped.amount : 0; 102 | this.creep.memory.energyId = dropped ? dropped.id : undefined; 103 | } 104 | } 105 | 106 | handleDroppedResources() { 107 | const dropped = this.creep.pos.lookFor(LOOK_RESOURCES)[0]; 108 | if (dropped) { 109 | this.creep.memory.energyAmount = dropped.amount; 110 | this.creep.memory.energyId = dropped.id; 111 | } 112 | } 113 | 114 | handleHaulerCheck() { 115 | if (this.creep.memory.other.hauler) { 116 | const hauler = _.find(Game.creeps, (c) => c.my && c.memory.other.harvester === this.creep.id); 117 | if (!hauler) this.creep.memory.other.hauler = undefined; 118 | } 119 | Game.rooms[this.creep.memory.colony].memory.additionalRemoteHaulingNeeded = Game.time + 500; 120 | this.creep.idleFor(20); 121 | } 122 | } 123 | 124 | function harvestDepositContainer(source, creep) { 125 | let container = source.pos.findClosestByRange(creep.room.structures, {filter: (s) => s.structureType === STRUCTURE_CONTAINER && s.pos.getRangeTo(source) === 1}); 126 | if (container) { 127 | creep.memory.containerID = container.id; 128 | return container.id; 129 | } else { 130 | let site = source.pos.findInRange(creep.room.constructionSites, 3, {filter: (s) => s.structureType === STRUCTURE_CONTAINER})[0]; 131 | if (!creep.memory.siteAttempt && !site && creep.pos.getRangeTo(source) === 1 && !creep.pos.checkForWall()) { 132 | creep.memory.siteAttempt = true; 133 | creep.pos.createConstructionSite(STRUCTURE_CONTAINER); 134 | } else if (!site && creep.pos.checkForWall()) { 135 | findContainerSpot(creep.room, source.pos); 136 | } else if (site && site.pos.getRangeTo(source) === 1) { 137 | creep.memory.containerSite = site.id; 138 | } 139 | } 140 | } 141 | 142 | function findContainerSpot(room, position) { 143 | for (let xOff = -1; xOff <= 1; xOff++) { 144 | for (let yOff = -1; yOff <= 1; yOff++) { 145 | if (xOff !== 0 || yOff !== 0) { 146 | let pos = new RoomPosition(position.x + xOff, position.y + yOff, room.name); 147 | if (!pos.checkForImpassible()) pos.createConstructionSite(STRUCTURE_CONTAINER); 148 | } 149 | } 150 | } 151 | } 152 | 153 | profiler.registerClass(RoleRemoteHarvester, 'RemoteHarvester'); 154 | module.exports = RoleRemoteHarvester; 155 | -------------------------------------------------------------------------------- /default/roles/role.reserver.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const profiler = require("tools.profiler"); 6 | 7 | class RoleReserver { 8 | constructor(creep) { 9 | this.creep = creep; 10 | this.room = creep.room; 11 | this.performRoleActions(); 12 | } 13 | 14 | performRoleActions() { 15 | if (this.creep.skSafety()) { 16 | this.creep.memory.other.stationary = undefined; 17 | return true; 18 | } else if (this.room.name !== this.creep.memory.destination) { 19 | this.travel() 20 | } else if (!this.creep.memory.inPlace) { 21 | this.getToController(); 22 | } else if (!this.room.controller.reservation || this.room.controller.reservation.username === MY_USERNAME) { 23 | this.reserveController(); 24 | } else if (this.room.controller.reservation) { 25 | this.attackController(); 26 | } 27 | } 28 | 29 | travel() { 30 | let destination = new RoomPosition(25, 25, this.creep.memory.destination); 31 | if (this.creep.memory.controllerTarget) { 32 | const controller = JSON.parse(this.creep.memory.controllerTarget) 33 | destination = new RoomPosition(controller.x, controller.y, this.creep.memory.destination); 34 | return this.creep.shibMove(destination); 35 | } 36 | this.creep.shibMove(destination, {range: 23}); 37 | } 38 | 39 | getToController() { 40 | this.creep.memory.controllerTarget = JSON.stringify(this.room.controller.pos); 41 | if (!this.creep.pos.isNearTo(this.room.controller)) return this.creep.shibMove(this.room.controller); else this.creep.memory.inPlace = true; 42 | } 43 | 44 | reserveController() { 45 | switch (this.creep.reserveController(this.room.controller)) { 46 | case OK: 47 | this.creep.memory.other.stationary = true; 48 | if (!this.creep.memory.signed) { 49 | let signs = RESERVE_ROOM_SIGNS; 50 | this.creep.signController(this.creep.room.controller, _.sample(signs)); 51 | this.creep.memory.signed = true; 52 | } 53 | break; 54 | case ERR_NOT_IN_RANGE: 55 | this.creep.shibMove(this.room.controller); 56 | } 57 | const ticks = this.room.controller.reservation && this.room.controller.reservation.username === MY_USERNAME ? this.room.controller.reservation.ticksToEnd : 0; 58 | INTEL[this.room.name].reservationExpires = Game.time + ticks; 59 | if (!INTEL[this.room.name].reserverCap) INTEL[this.room.name].reserverCap = this.room.controller.pos.countOpenTerrainAround(); 60 | } 61 | 62 | attackController() { 63 | switch (this.creep.attackController(this.room.controller)) { 64 | case OK: 65 | this.creep.memory.other.stationary = true; 66 | if (!this.creep.memory.signed) { 67 | let signs = RESERVE_ROOM_SIGNS; 68 | this.creep.signController(this.room.controller, _.sample(signs)); 69 | this.creep.memory.signed = true; 70 | } 71 | if (!INTEL[this.room.name].reserverCap) INTEL[this.room.name].reserverCap = this.room.controller.pos.countOpenTerrainAround(); 72 | break; 73 | case ERR_NOT_IN_RANGE: 74 | this.creep.shibMove(this.room.controller); 75 | } 76 | } 77 | } 78 | 79 | profiler.registerClass(RoleReserver, 'Reserver'); 80 | module.exports = RoleReserver; 81 | -------------------------------------------------------------------------------- /default/roles/role.scout.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const profiler = require("tools.profiler"); 6 | 7 | class RoleScout { 8 | constructor(creep) { 9 | this.creep = creep; 10 | this.room = creep.room; 11 | this.performRoleActions(); 12 | } 13 | 14 | performRoleActions() { 15 | this.housekeeping(); 16 | this.scoutRoom(); 17 | } 18 | 19 | housekeeping() { 20 | this.creep.say(ICONS.eye, true); 21 | } 22 | 23 | scoutRoom() { 24 | this.creep.scoutRoom(); 25 | } 26 | } 27 | 28 | profiler.registerClass(RoleScout, 'Scout'); 29 | module.exports = RoleScout; 30 | -------------------------------------------------------------------------------- /default/roles/role.shuttle.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const profiler = require("tools.profiler"); 6 | 7 | class RoleShuttle { 8 | constructor(creep) { 9 | this.creep = creep; 10 | this.room = creep.room; 11 | this.performRoleActions(); 12 | } 13 | 14 | performRoleActions() { 15 | if (this.housekeeping()) return; 16 | if (_.sum(this.creep.store) > this.creep.store.getCapacity() * 0.2) { 17 | this.hauling(); 18 | } else { 19 | this.pickup(); 20 | } 21 | } 22 | 23 | housekeeping() { 24 | this.creep.say(ICONS.haul, true); 25 | this.creep.opportunisticFill(); 26 | } 27 | 28 | hauling() { 29 | if (_.sum(this.creep.store) > this.creep.store[RESOURCE_ENERGY]) { 30 | let storageItem = this.creep.room.storage || _.filter(this.creep.room.structures, (s) => s.structureType === STRUCTURE_CONTAINER && s.store.getFreeCapacity() >= CONTAINER_CAPACITY * 0.5)[0] 31 | for (const resourceType in this.creep.store) { 32 | if (resourceType === RESOURCE_ENERGY) continue; 33 | if (!storageItem) return this.creep.drop(resourceType); 34 | switch (this.creep.transfer(storageItem, resourceType)) { 35 | case OK: 36 | break; 37 | case ERR_NOT_IN_RANGE: 38 | this.creep.shibMove(storageItem); 39 | break; 40 | } 41 | } 42 | } else { 43 | if (!this.creep.memory.storageDestination) { 44 | let controllerContainer = Game.getObjectById(this.creep.room.memory.controllerContainer); 45 | if (this.creep.room.storage && this.creep.room.energyState > 1 && controllerContainer && controllerContainer.store.getFreeCapacity(RESOURCE_ENERGY) > 100) this.creep.memory.storageDestination = controllerContainer.id; 46 | else if (this.creep.room.storage) this.creep.memory.storageDestination = this.creep.room.storage.id; else if (!this.creep.haulerDelivery()) this.creep.idleFor(this.creep.room.level); 47 | } else this.creep.haulerDelivery(); 48 | } 49 | } 50 | 51 | pickup() { 52 | if (this.creep.memory.energyDestination || this.creep.locateEnergy()) { 53 | this.creep.withdrawResource() 54 | } else { 55 | this.creep.idleFor(this.creep.room.level) 56 | } 57 | } 58 | } 59 | 60 | profiler.registerClass(RoleShuttle, 'Shuttle'); 61 | module.exports = RoleShuttle; -------------------------------------------------------------------------------- /default/roles/role.siegeDuo.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const profiler = require("tools.profiler"); 6 | 7 | class RoleSiegeDuo { 8 | constructor(creep) { 9 | this.creep = creep; 10 | this.room = creep.room; 11 | this.performRoleActions(); 12 | } 13 | 14 | performRoleActions() { 15 | if (this.housekeeping()) return; 16 | // If partner set 17 | if (this.creep.memory.partner) { 18 | if (this.creep.memory.leader) { 19 | this.handleLeader(); 20 | } else { 21 | this.handleFollower(); 22 | } 23 | } else { 24 | this.handleSolo(); 25 | } 26 | } 27 | 28 | housekeeping() { 29 | // Boosting 30 | if (this.creep.tryToBoost()) return true; 31 | // Blinky mode 32 | if (this.room.hostileCreeps.length || this.room.hostileStructures.length) { 33 | this.creep.healInRange(true); 34 | } else { 35 | this.creep.healInRange(); 36 | } 37 | // Check and set partner 38 | if (!Game.getObjectById(this.creep.memory.partner)) { 39 | this.creep.memory.leader = undefined; 40 | this.creep.memory.partner = undefined; 41 | } 42 | if (!this.creep.memory.partner && this.creep.hasActiveBodyparts(WORK)) { 43 | const availablePartner = _.find(Game.creeps, (c) => c.id !== this.creep.id && c.my && !c.spawning 44 | && c.memory.role === this.creep.memory.role && c.hasActiveBodyparts(HEAL) && !c.memory.partner 45 | && c.memory.destination === this.creep.memory.destination); 46 | if (availablePartner) { 47 | this.creep.memory.leader = true; 48 | this.creep.memory.partner = availablePartner.id; 49 | availablePartner.memory.partner = this.creep.id; 50 | } 51 | } 52 | } 53 | 54 | handleLeader() { 55 | const partner = Game.getObjectById(this.creep.memory.partner); 56 | const isReady = this.hasFullSquad(this.creep) && this.isPartnerNearby(partner, this.creep); 57 | 58 | if (isReady) { 59 | if (!this.creep.memory.initialFormUp) this.creep.memory.initialFormUp = true; 60 | if (this.creep.memory.operation) this.operationManagement(); else if (this.creep.memory.destination) this.destinationManagement(); 61 | else this.creep.handleMilitaryCreep(); 62 | } else { 63 | this.creep.findDefensivePosition(); 64 | } 65 | } 66 | 67 | handleFollower() { 68 | const partner = Game.getObjectById(this.creep.memory.partner); 69 | this.creep.shibMove(partner, {range: 0}); 70 | if (!partner) { 71 | return this.creep.memory.partner = undefined; 72 | } 73 | if (partner.memory.idle) { 74 | this.creep.memory.idle = partner.memory.idle; 75 | } 76 | } 77 | 78 | handleSolo() { 79 | if (this.creep.handleMilitaryCreep()) return; 80 | if (this.creep.findDefensivePosition()) this.creep.idleFor(5); 81 | } 82 | 83 | operationManagement() { 84 | switch (this.creep.memory.operation) { 85 | case 'stronghold': 86 | this.creep.strongholdAttack(); 87 | break; 88 | case 'roomDenial': 89 | this.creep.denyRoom(); 90 | break; 91 | } 92 | } 93 | 94 | destinationManagement() { 95 | if (this.room.name !== this.creep.memory.destination) { 96 | return this.creep.shibMove(new RoomPosition(25, 25, this.creep.memory.destination), {range: 22}); 97 | } else { 98 | // If we can't get to the controller or sources, clear a path 99 | if (!this.creep.scorchedEarth()) { 100 | this.room.cacheRoomIntel(true); 101 | this.creep.recycleCreep(); 102 | } 103 | } 104 | } 105 | 106 | hasFullSquad(creep) { 107 | if (creep.memory.initialFormUp) return true; 108 | // Check if any squadmember needs to renew 109 | const partner = Game.getObjectById(this.creep.memory.partner); 110 | if (!partner.memory.boostAttempt) return false; 111 | return !!partner; 112 | } 113 | 114 | isPartnerNearby(partner, leader) { 115 | if (!partner || partner.pos.roomName !== partner.pos.roomName || partner.pos.roomName !== leader.pos.roomName) return true; 116 | if (!partner.room.hostileCreeps.length && !partner.room.hostileStructures.length && !this.nearDestination(leader)) return true; 117 | if (partner.pos.x <= 0 || partner.pos.x >= 49 || partner.pos.y <= 0 || partner.pos.y >= 49) return true; 118 | if (!partner.pos.isNearTo(leader.pos) && (!this.nearDestination(leader) || partner.pos.roomName === leader.pos.roomName) && !partner.pos.checkIfOutOfBounds()) return false 119 | return true 120 | } 121 | 122 | nearDestination(leader) { 123 | if (!leader.memory.destination) return false; 124 | return Game.map.getRoomLinearDistance(this.creep.room.name, leader.memory.destination) <= 1; 125 | } 126 | } 127 | 128 | profiler.registerClass(RoleSiegeDuo, 'siegeDuo'); 129 | module.exports = RoleSiegeDuo; 130 | -------------------------------------------------------------------------------- /default/roles/role.stationaryHarvester.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const profiler = require("tools.profiler"); 6 | 7 | class RoleStationaryHarvester { 8 | constructor(creep) { 9 | this.creep = creep; 10 | this.room = creep.room; 11 | this.performRoleActions(); 12 | } 13 | 14 | performRoleActions() { 15 | //if (this.creep.id ==='67c119bddaa16e04f1589692') this.creep.pathingDebug(); 16 | if (!this.creep.memory.other.source) { 17 | this.findSource(); 18 | } else { 19 | this.harvestSource(); 20 | } 21 | } 22 | 23 | findSource() { 24 | if (!this.creep.findSource()) { 25 | let oldestHarvester = _.min(_.filter(this.room.creeps, (c) => c.memory && c.ticksToLive < 500 && c.memory.role === "stationaryHarvester"), "ticksToLive") || 26 | _.find(this.room.creeps, (c) => c.memory && c.memory.role === "stationaryHarvester" && c.memory.other.reboot); 27 | if (!oldestHarvester || !oldestHarvester.id) return this.creep.suicide(); 28 | else { 29 | this.creep.memory.other.source = oldestHarvester.memory.other.source; 30 | oldestHarvester.suicide(); 31 | } 32 | } 33 | } 34 | 35 | harvestSource() { 36 | let source = Game.getObjectById(this.creep.memory.other.source); 37 | // If in place harvest 38 | if (this.creep.memory.onContainer) { 39 | let container = Game.getObjectById(source.memory.container); 40 | // Build container 41 | if (!container && this.creep.store[RESOURCE_ENERGY]) { 42 | source.memory.container = undefined; 43 | let dropped = this.creep.pos.lookFor(LOOK_RESOURCES)[0]; 44 | let site = this.creep.pos.lookFor(LOOK_CONSTRUCTION_SITES)[0]; 45 | if (site && dropped && dropped.amount >= 250) { 46 | if (site) { 47 | this.creep.build(site); 48 | this.creep.pickup(dropped); 49 | } 50 | return; 51 | } 52 | } 53 | switch (this.creep.harvest(source)) { 54 | case ERR_NOT_IN_RANGE: 55 | this.creep.memory.onContainer = undefined; 56 | break; 57 | case ERR_NOT_ENOUGH_RESOURCES: 58 | if (container && this.creep.store[RESOURCE_ENERGY]) { 59 | this.creep.repair(container); 60 | } else this.creep.idleFor(source.ticksToRegeneration + 1); 61 | break; 62 | case OK: 63 | // Set stationary so we don't get bumped 64 | this.creep.memory.other.stationary = true; 65 | // Check if the link is still good 66 | if (!this.creep.memory.other.linkCheck && container && source.memory.link) { 67 | const link = Game.getObjectById(source.memory.link); 68 | if (!link || !link.pos.isNearTo(container) || !link.isActive()) { 69 | if (link) link.destroy(); 70 | source.memory.link = undefined; 71 | } else this.creep.memory.link = link.id; 72 | this.creep.memory.other.linkCheck = true; 73 | } 74 | // If we have a link and container, empty the container of overflow 75 | if (source.memory.link && container && container.store[RESOURCE_ENERGY]) this.creep.withdraw(container, RESOURCE_ENERGY); 76 | // Every other tick check for deposit ability 77 | if (isEven(Game.time)) { 78 | if ((container && !container.store.getFreeCapacity(RESOURCE_ENERGY)) || this.creep.store[RESOURCE_ENERGY]) depositEnergy(this.creep); 79 | } 80 | break; 81 | } 82 | } else { 83 | let container = Game.getObjectById(source.memory.container) || _.find(source.pos.findInRange(FIND_CONSTRUCTION_SITES, 1), (s) => s.structureType === STRUCTURE_CONTAINER); 84 | //Make sure you're on the container 85 | if (container) { 86 | if (this.creep.pos.getRangeTo(container)) { 87 | return this.creep.shibMove(container, {range: 0}); 88 | } else { 89 | this.creep.memory.onContainer = true; 90 | } 91 | } else { 92 | if (this.creep.pos.getRangeTo(source) > 1) { 93 | return this.creep.shibMove(source); 94 | } else { 95 | this.creep.memory.onContainer = true; 96 | } 97 | } 98 | } 99 | } 100 | } 101 | 102 | // Rotate between link and container if we don't have a hub and controller link 103 | function depositEnergy(creep) { 104 | let source = Game.getObjectById(creep.memory.other.source); 105 | let container = Game.getObjectById(source.memory.container); 106 | // Fill nearby 107 | if (extensionFiller(creep)) return; 108 | if (container && container.hits < container.hitsMax * 0.5) return creep.repair(container); 109 | if (source.memory.link && (creep.room.memory.hubLink || creep.room.memory.controllerLink)) { 110 | let link = Game.getObjectById(source.memory.link); 111 | if (link && link.store[RESOURCE_ENERGY] < LINK_CAPACITY) { 112 | creep.transfer(link, RESOURCE_ENERGY); 113 | creep.withdraw(container, RESOURCE_ENERGY); 114 | } else if (container && !container.store.getFreeCapacity(RESOURCE_ENERGY)) { 115 | if (container.hits < container.hitsMax) creep.repair(container); else if (creep.pos.checkForRampart()) creep.repair(creep.pos.checkForRampart()); 116 | } 117 | } else if (container) { 118 | if (!container.store.getFreeCapacity(RESOURCE_ENERGY)) { 119 | if (container.hits < container.hitsMax) creep.repair(container); 120 | } 121 | } else { 122 | creep.memory.containerID = undefined; 123 | creep.memory.linkID = undefined; 124 | } 125 | } 126 | 127 | function extensionFiller(creep) { 128 | if (!ROOM_HARVESTER_EXTENSIONS[creep.room.name] || !creep.memory.extensionsFound) { 129 | creep.memory.extensionsFound = true; 130 | let container = Game.getObjectById(creep.memory.containerID) || creep; 131 | let extension = container.pos.findInRange(_.filter(creep.room.impassibleStructures, (s) => s.structureType === STRUCTURE_SPAWN || s.structureType === STRUCTURE_EXTENSION), 1); 132 | let sourceExtensions = ROOM_HARVESTER_EXTENSIONS[creep.room.name] || []; 133 | ROOM_HARVESTER_EXTENSIONS[creep.room.name] = _.union(sourceExtensions, _.pluck(extension, 'id')); 134 | // Rampart check if near border or outside 135 | if (extension.length && creep.room.level >= 3) { 136 | let nearbyBunkerWall = _.find(container.pos.lookForNearby(LOOK_STRUCTURES, true, 3), (s) => (s.structure.structureType === STRUCTURE_RAMPART && !s.structure.pos.checkForObstacleStructure()) || s.structure.structureType === STRUCTURE_WALL); 137 | if (nearbyBunkerWall) { 138 | if (!container.pos.checkForRampart()) container.pos.createConstructionSite(STRUCTURE_RAMPART); 139 | for (let e of extension) { 140 | if (!e.pos.checkForRampart()) { 141 | e.pos.createConstructionSite(STRUCTURE_RAMPART); 142 | } 143 | } 144 | } 145 | } 146 | } else { 147 | if (creep.opportunisticFill()) return true; 148 | } 149 | } 150 | 151 | profiler.registerClass(RoleStationaryHarvester, 'StationaryHarvester'); 152 | module.exports = RoleStationaryHarvester; 153 | -------------------------------------------------------------------------------- /default/roles/role.upgrader.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright for Bob "Shibdib" Sardinia - See license file for more information,(c) 2023. 3 | */ 4 | 5 | const profiler = require("tools.profiler"); 6 | 7 | class RoleUpgrader { 8 | constructor(creep) { 9 | this.creep = creep; 10 | this.room = creep.room; 11 | this.container = Game.getObjectById(this.room.memory.controllerContainer); 12 | if (!this.container) this.room.memory.controllerContainer = undefined; 13 | this.link = Game.getObjectById(this.room.memory.controllerLink); 14 | if (!this.link) this.room.memory.controllerLink = undefined; 15 | else if (!this.creep.memory.other.linkCheck) { 16 | if (this.container && !this.link.pos.isNearTo(this.container)) { 17 | this.link.destroy(); 18 | this.room.memory.controllerLink = undefined; 19 | } 20 | this.creep.memory.other.linkCheck = true; 21 | } 22 | this.performRoleActions(); 23 | } 24 | 25 | performRoleActions() { 26 | if (this.housekeeping()) return; 27 | if (this.creep.memory.other.noMove || !this.creep.hasActiveBodyparts(MOVE)) { 28 | this.stationaryUpgrading(); 29 | } else { 30 | this.mobileUpgrading(); 31 | } 32 | } 33 | 34 | housekeeping() { 35 | // Boosting 36 | if (this.creep.tryToBoost()) return true; 37 | } 38 | 39 | stationaryUpgrading() { 40 | if (!this.container && !this.link) { 41 | return this.creep.recycleCreep(); 42 | } 43 | this.creep.memory.other.stationary = true; 44 | this.creep.memory.other.noMove = true; 45 | // Handle getting in place 46 | if (!this.creep.memory.inPosition) { 47 | if (!this.link && this.container) { 48 | if (this.container.pos.checkForCreep() && this.creep.pos.isNearTo(this.container)) this.creep.memory.inPosition = true; 49 | else return this.creep.shibMove(this.container, {range: 0}); 50 | } else if (this.link && !this.container) { 51 | if (this.creep.pos.isNearTo(this.link)) this.creep.memory.inPosition = true; 52 | else return this.creep.shibMove(this.link, {range: 1}) 53 | } else { 54 | if (this.container.pos.checkForCreep() && (this.creep.pos.isNearTo(this.container) || this.creep.pos.isNearTo(this.link))) this.creep.memory.inPosition = true; 55 | else if (!this.container.pos.checkForCreep()) return this.creep.shibMove(this.container, {range: 0}) 56 | else return this.creep.shibMove([this.container, this.link], {range: 1}) 57 | } 58 | } 59 | switch (this.creep.upgradeController(Game.rooms[this.creep.memory.colony].controller)) { 60 | case OK: 61 | // Handle resource withdraw 62 | this.withdraw(); 63 | return; 64 | case ERR_NOT_IN_RANGE: 65 | return this.creep.shibMove(Game.rooms[this.creep.memory.colony].controller, {range: 3}); 66 | case ERR_NOT_ENOUGH_RESOURCES: 67 | // Handle resource withdraw 68 | this.withdraw(); 69 | } 70 | } 71 | 72 | mobileUpgrading() { 73 | if (this.creep.store[RESOURCE_ENERGY]) { 74 | switch (this.creep.upgradeController(Game.rooms[this.creep.memory.colony].controller)) { 75 | case OK: 76 | this.creep.memory.other.stationary = true; 77 | return; 78 | case ERR_NOT_IN_RANGE: 79 | this.creep.shibMove(this.room.controller, {range: 3}); 80 | return; 81 | case ERR_NOT_ENOUGH_RESOURCES: 82 | // Handle resource withdraw 83 | this.creep.memory.other.stationary = undefined; 84 | this.withdraw(); 85 | } 86 | } else if (this.creep.memory.energyDestination) { 87 | this.creep.memory.other.stationary = undefined; 88 | this.creep.withdrawResource(); 89 | } else if (this.container && this.container.store[RESOURCE_ENERGY]) { 90 | this.creep.memory.other.stationary = undefined; 91 | this.creep.withdrawResource(this.container); 92 | } else if (!this.creep.locateEnergy()) { 93 | this.creep.memory.other.stationary = undefined; 94 | this.creep.idleFor(15); 95 | } 96 | } 97 | 98 | withdraw() { 99 | // Handle resource withdraw 100 | const nearbyUpgrader = this.creep.pos.findInRange(this.room.myCreeps, 1, {filter: c => c.id !== this.creep.id && c.memory.role === 'upgrader' && c.store[RESOURCE_ENERGY]})[0]; 101 | if (this.link && this.creep.pos.isNearTo(this.link) && this.link.store[RESOURCE_ENERGY]) { 102 | this.creep.withdrawResource(this.link); 103 | } else if (this.container && this.creep.pos.isNearTo(this.container) && this.container.store[RESOURCE_ENERGY]) { 104 | this.creep.withdrawResource(this.container); 105 | } else if (nearbyUpgrader && nearbyUpgrader.store[RESOURCE_ENERGY]) { 106 | this.creep.withdrawResource(nearbyUpgrader, RESOURCE_ENERGY, this.creep.getActiveBodyparts(WORK)); 107 | } 108 | } 109 | } 110 | 111 | profiler.registerClass(RoleUpgrader, 'Upgrader'); 112 | module.exports = RoleUpgrader; 113 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "screeps", 3 | "version": "0.0.1", 4 | "dependencies": { 5 | "grunt": "latest", 6 | "nodejs": "latest", 7 | "screeps-api": "latest", 8 | "grunt-screeps": "latest" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Sloth Bot 2 | 3 | Artificial Intelligence for the programming game Screeps. 4 | 5 | This code is designed to function fine with no user interaction. It also uses flags to allow the user to guide it if 6 | they desire. 7 | 8 | For discussion or support message **Shibdib** on Discord. 9 | 10 | _This was made as a way for me to learn JS, so excuse any horrible practices you may encounter. Slowly refactoring the 11 | really ugly stuff._ 12 | 13 | Releases represent the "Stable" branch and are deemed working "good enough". Dev may contain some really bad bugs. 14 | 15 | ## Current Features 16 | - Automated Room Building 17 | - Automated Room Claiming 18 | - Automated Room Defense 19 | - Automated Boost Creation and Use 20 | - Automated Market Interaction 21 | - Automated Factory Code 22 | - Automated Power Creep Code 23 | - Automated Alliance Integration (This bot will recognize LOAN alliance members and not engage them) 24 | - LOAN is current not working so this is non functional. 25 | - Manual Attack Target Designation (Description forthcoming) 26 | - Automated Attack Target Code 27 | 28 | ## Setup 29 | 30 | 1. You either need to setup your IDE to use the grunt file to upload (HIGHLY RECOMMENDED) or... manually seperate the 31 | folders. 32 | 2. Modify the config.default.js file for your needs. 33 | 34 | ## In The Works 35 | 36 | - Quad code 37 | - Smarter boosting 38 | - Better portal pathing 39 | 40 | Please makes Issues and feel free to PR. 41 | --------------------------------------------------------------------------------