- Timestamp:
- 09/29/13 15:32:52 (11 years ago)
- Location:
- ps/trunk/binaries/data/mods/public/simulation/ai
- Files:
-
- 7 added
- 5 deleted
- 19 edited
-
aegis/aegis.js (modified) (14 diffs)
-
aegis/attack_plan.js (modified) (35 diffs)
-
aegis/base-manager.js (added)
-
aegis/config.js (modified) (3 diffs)
-
aegis/data.json (modified) (1 diff)
-
aegis/defence.js (modified) (15 diffs)
-
aegis/economy.js (deleted)
-
aegis/enemy-watcher.js (modified) (1 diff)
-
aegis/entitycollection-extend.js (modified) (1 diff)
-
aegis/headquarters.js (added)
-
aegis/map-module.js (modified) (9 diffs)
-
aegis/military.js (deleted)
-
aegis/naval-manager.js (added)
-
aegis/plan-building.js (deleted)
-
aegis/plan-research.js (deleted)
-
aegis/plan-training.js (deleted)
-
aegis/plan-transport.js (added)
-
aegis/queue-manager.js (modified) (9 diffs)
-
aegis/queue.js (modified) (7 diffs)
-
aegis/queueplan-building.js (added)
-
aegis/queueplan-research.js (added)
-
aegis/queueplan-training.js (added)
-
aegis/worker.js (modified) (29 diffs)
-
common-api-v3/base.js (modified) (3 diffs)
-
common-api-v3/entity.js (modified) (9 diffs)
-
common-api-v3/entitycollection.js (modified) (12 diffs)
-
common-api-v3/filters.js (modified) (5 diffs)
-
common-api-v3/gamestate.js (modified) (10 diffs)
-
common-api-v3/map-module.js (modified) (9 diffs)
-
common-api-v3/shared.js (modified) (10 diffs)
-
common-api-v3/terrain-analysis.js (modified) (10 diffs)
Legend:
- Unmodified
- Added
- Removed
-
ps/trunk/binaries/data/mods/public/simulation/ai/aegis/aegis.js
r13683 r13907 1 function QBotAI(settings) { 1 // "local" global variables for stuffs that will need a unique ID 2 // Note that since order of loading is alphabetic, this means this file must go before any other file using them. 3 var uniqueIDBOPlans = 0; // training/building/research plans 4 var uniqueIDBases = 1; // base manager ID. Starts at one because "0" means "no base" on the map 5 var uniqueIDTPlans = 1; // transport plans. starts at 1 because 0 might be used as none. 6 7 function AegisBot(settings) { 2 8 BaseAI.call(this, settings); 3 9 … … 7 13 8 14 this.playedTurn = 0; 9 10 this.modules = { 11 "economy": new EconomyManager(), 12 "military": new MilitaryAttackManager() 13 }; 15 16 this.priorities = Config.priorities; 14 17 15 18 // this.queues can only be modified by the queue manager or things will go awry. 16 this.queues = { 17 house : new Queue(), 18 citizenSoldier : new Queue(), 19 villager : new Queue(), 20 economicBuilding : new Queue(), 21 dropsites : new Queue(), 22 field : new Queue(), 23 militaryBuilding : new Queue(), 24 defenceBuilding : new Queue(), 25 civilCentre: new Queue(), 26 majorTech: new Queue(), 27 minorTech: new Queue() 28 }; 29 30 this.productionQueues = []; 31 32 this.priorities = Config.priorities; 33 19 this.queues = {}; 20 for (i in this.priorities) 21 this.queues[i] = new Queue(); 22 34 23 this.queueManager = new QueueManager(this.queues, this.priorities); 35 24 25 this.HQ = new HQ(); 26 36 27 this.firstTime = true; 37 28 38 29 this.savedEvents = []; 39 40 this.waterMap = false;41 30 42 31 this.defcon = 5; … … 44 33 } 45 34 46 QBotAI.prototype = new BaseAI(); 47 48 // Bit of a hack: I run the pathfinder early, before the map apears, to avoid a sometimes substantial lag right at the start. 49 QBotAI.prototype.InitShared = function(gameState, sharedScript) { 35 AegisBot.prototype = new BaseAI(); 36 37 AegisBot.prototype.InitShared = function(gameState, sharedScript) { 38 39 this.HQ.init(gameState,sharedScript.events,this.queues); 40 debug ("Initialized with the difficulty " + Config.difficulty); 41 50 42 var ents = gameState.getEntities().filter(Filters.byOwner(PlayerID)); 51 43 var myKeyEntities = ents.filter(function(ent) { … … 64 56 } 65 57 58 this.myIndex = this.accessibility.getAccessValue(myKeyEntities.toEntityArray()[0].position()); 59 66 60 this.pathFinder = new aStarPath(gameState, false, true); 67 61 this.pathsToMe = []; … … 83 77 84 78 this.pathInfo.angle += Math.PI/3.0; 85 }86 87 //Some modules need the gameState to fully initialise88 QBotAI.prototype.runInit = function(gameState, events){89 79 90 80 this.chooseRandomStrategy(); 91 92 for (var i in this.modules){ 93 if (this.modules[i].init){ 94 this.modules[i].init(gameState, events); 95 } 96 } 97 debug ("Inited, diff is " + Config.difficulty); 98 this.timer = new Timer(); 99 100 101 var ents = gameState.getOwnEntities(); 102 var myKeyEntities = gameState.getOwnEntities().filter(function(ent) { 103 return ent.hasClass("CivCentre"); 104 }); 105 106 if (myKeyEntities.length == 0){ 107 myKeyEntities = gameState.getOwnEntities(); 108 } 109 110 // disband the walls themselves 111 if (gameState.playerData.civ == "iber") { 112 gameState.getOwnEntities().filter(function(ent) { //}){ 113 if (ent.hasClass("StoneWall") && !ent.hasClass("Tower")) 114 ent.destroy(); 115 }); 116 } 117 118 var filter = Filters.byClass("CivCentre"); 119 var enemyKeyEntities = gameState.getEnemyEntities().filter(filter); 120 121 if (enemyKeyEntities.length == 0){ 122 enemyKeyEntities = gameState.getEnemyEntities(); 123 } 124 125 //this.accessibility = new Accessibility(gameState, myKeyEntities.toEntityArray()[0].position()); 126 127 this.myIndex = this.accessibility.getAccessValue(myKeyEntities.toEntityArray()[0].position()); 128 129 if (enemyKeyEntities.length == 0) 130 return; 131 132 this.templateManager = new TemplateManager(gameState); 133 }; 134 135 QBotAI.prototype.OnUpdate = function(sharedScript) { 81 } 82 83 AegisBot.prototype.OnUpdate = function(sharedScript) { 136 84 if (this.gameFinished){ 137 85 return; 138 86 } 139 87 140 if (this.events.length > 0 ){88 if (this.events.length > 0 && this.turn !== 0){ 141 89 this.savedEvents = this.savedEvents.concat(this.events); 142 90 } 143 91 144 92 145 146 93 // Run the update every n turns, offset depending on player ID to balance the load 147 // this also means that init at turn 0 always happen and is never run in parallel to the first played turn so I use an else if. 148 if (this.turn == 0) { 149 150 //Engine.DumpImage("terrain.png", this.accessibility.map, this.accessibility.width,this.accessibility.height,255) 151 //Engine.DumpImage("Access.png", this.accessibility.passMap, this.accessibility.width,this.accessibility.height,this.accessibility.regionID+1) 152 153 var gameState = sharedScript.gameState[PlayerID]; 154 gameState.ai = this; 155 156 this.runInit(gameState, this.savedEvents); 157 158 // Delete creation events 159 delete this.savedEvents; 160 this.savedEvents = []; 161 } else if ((this.turn + this.player) % 8 == 5) { 162 163 Engine.ProfileStart("Aegis bot"); 94 if ((this.turn + this.player) % 8 == 5) { 95 96 Engine.ProfileStart("Aegis bot (player " + this.player +")"); 164 97 165 98 this.playedTurn++; 166 99 167 var gameState = sharedScript.gameState[PlayerID]; 168 gameState.ai = this; 169 170 if (gameState.getOwnEntities().length === 0){ 100 if (this.gameState.getOwnEntities().length === 0){ 171 101 Engine.ProfileStop(); 172 102 return; // With no entities to control the AI cannot do anything … … 176 106 { 177 107 var pos = [this.pathInfo.mkeyPos[0] + 150*Math.cos(this.pathInfo.angle),this.pathInfo.mkeyPos[1] + 150*Math.sin(this.pathInfo.angle)]; 178 var path = this.pathFinder.getPath(this.pathInfo.ekeyPos, pos, 6, 5);// uncomment for debug:*/, 300000, gameState);108 var path = this.pathFinder.getPath(this.pathInfo.ekeyPos, pos, 6, 5);// uncomment for debug:*/, 300000, this.gameState); 179 109 if (path !== undefined && path[1] !== undefined && path[1] == false) { 180 110 // path is viable and doesn't require boating. … … 192 122 { 193 123 debug ("Assuming this is a water map"); 194 this. waterMap = true;124 this.HQ.waterMap = true; 195 125 } 196 126 delete this.pathFinder; … … 199 129 } 200 130 201 var sfx = "_generic"; 202 if (gameState.civ() == "athen") 203 sfx = "_athen" 204 131 var townPhase = this.gameState.townPhase(); 132 var cityPhase = this.gameState.cityPhase(); 205 133 // try going up phases. 206 if (gameState.canResearch("phase_town" + sfx,true) && gameState.getTimeElapsed() > (Config.Economy.townPhase*1000) 207 && gameState.findResearchers("phase_town" + sfx,true).length != 0 && this.queues.majorTech.totalLength() === 0) { 208 this.queues.majorTech.addItem(new ResearchPlan(gameState, "phase_town" + sfx,true)); // we rush the town phase. 134 // TODO: softcode this. 135 if (this.gameState.canResearch(townPhase,true) && this.gameState.getTimeElapsed() > (Config.Economy.townPhase*1000) && this.gameState.getPopulation() > 40 136 && this.gameState.findResearchers(townPhase,true).length != 0 && this.queues.majorTech.length() === 0 137 && this.gameState.getOwnEntities().filter(Filters.byClass("Village")).length > 5) 138 { 139 this.queueManager.pauseQueue("villager", true); 140 this.queueManager.pauseQueue("citizenSoldier", true); 141 this.queueManager.pauseQueue("house", true); 142 this.queues.majorTech.addItem(new ResearchPlan(this.gameState, townPhase,0,-1,true)); // we rush the town phase. 209 143 debug ("Trying to reach town phase"); 210 var nb = gameState.getOwnEntities().filter(Filters.byClass("Village")).length-1; 211 if (nb < 5) 212 { 213 while (nb < 5 && ++nb) 214 this.queues.house.addItem(new BuildingConstructionPlan(gameState, "structures/{civ}_house")); 215 } 216 } else if (gameState.canResearch("phase_city_generic",true) && gameState.getTimeElapsed() > (Config.Economy.cityPhase*1000) 217 && gameState.getOwnEntitiesByRole("worker").length > 85 218 && gameState.findResearchers("phase_city_generic", true).length != 0 && this.queues.majorTech.totalLength() === 0) { 144 } 145 else if (this.gameState.canResearch(cityPhase,true) && this.gameState.getTimeElapsed() > (Config.Economy.cityPhase*1000) 146 && this.gameState.getOwnEntitiesByRole("worker").length > 85 147 && this.gameState.findResearchers(cityPhase, true).length != 0 && this.queues.majorTech.length() === 0) { 219 148 debug ("Trying to reach city phase"); 220 this.queues.majorTech.addItem(new ResearchPlan( gameState, "phase_city_generic"));149 this.queues.majorTech.addItem(new ResearchPlan(this.gameState, cityPhase)); 221 150 } 222 151 // defcon cooldown 223 if (this.defcon < 5 && gameState.timeSinceDefconChange() > 20000)152 if (this.defcon < 5 && this.gameState.timeSinceDefconChange() > 20000) 224 153 { 225 154 this.defcon++; 226 155 debug ("updefconing to " +this.defcon); 227 if (this.defcon >= 4 && this.modules.military.hasGarrisonedFemales) 228 this.modules.military.ungarrisonAll(gameState); 229 } 230 231 for (var i in this.modules){ 232 this.modules[i].update(gameState, this.queues, this.savedEvents); 233 } 234 235 this.queueManager.update(gameState); 236 156 if (this.defcon >= 4 && this.HQ.hasGarrisonedFemales) 157 this.HQ.ungarrisonAll(this.gameState); 158 } 159 160 this.HQ.update(this.gameState, this.queues, this.savedEvents); 161 162 this.queueManager.update(this.gameState); 163 237 164 /* 238 165 // Use this to debug informations about the metadata. … … 240 167 { 241 168 // some debug informations about units. 242 var units = gameState.getOwnEntities();169 var units = this.gameState.getOwnEntities(); 243 170 for (var i in units._entities) 244 171 { … … 266 193 267 194 //if (this.playedTurn % 5 === 0) 268 // this.queueManager.printQueues( gameState);195 // this.queueManager.printQueues(this.gameState); 269 196 270 197 // Generate some entropy in the random numbers (against humans) until the engine gets random initialised numbers … … 284 211 }; 285 212 286 QBotAI.prototype.chooseRandomStrategy = function()213 AegisBot.prototype.chooseRandomStrategy = function() 287 214 { 288 215 // deactivated for now. … … 293 220 this.strategy = "rush"; 294 221 // going to rush. 295 this. modules.economy.targetNumWorkers = 0;222 this.HQ.targetNumWorkers = 0; 296 223 Config.Economy.townPhase = 480; 297 224 Config.Economy.cityPhase = 900; … … 303 230 // TODO: Remove override when the whole AI state is serialised 304 231 // TODO: this currently is very much equivalent to "rungamestateinit" with a few hacks. Should deserialize/serialize properly someday. 305 QBotAI.prototype.Deserialize = function(data, sharedScript)232 AegisBot.prototype.Deserialize = function(data, sharedScript) 306 233 { 307 234 BaseAI.prototype.Deserialize.call(this, data); … … 346 273 347 274 // Override the default serializer 348 QBotAI.prototype.Serialize = function()275 AegisBot.prototype.Serialize = function() 349 276 { 350 277 //var ret = BaseAI.prototype.Serialize.call(this); -
ps/trunk/binaries/data/mods/public/simulation/ai/aegis/attack_plan.js
r13761 r13907 7 7 */ 8 8 9 function CityAttack(gameState, militaryManager, uniqueID, targetEnemy, type , targetFinder) {9 function CityAttack(gameState, HQ, uniqueID, targetEnemy, type , targetFinder) { 10 10 11 11 //This is the list of IDs of the units in the plan … … 331 331 // Three returns possible: 1 is "keep going", 0 is "failed plan", 2 is "start" 332 332 // 3 is a special case: no valid path returned. Right now I stop attacking alltogether. 333 CityAttack.prototype.updatePreparation = function(gameState, militaryManager,events) {333 CityAttack.prototype.updatePreparation = function(gameState, HQ,events) { 334 334 var self = this; 335 335 … … 338 338 if (this.target == undefined) 339 339 { 340 var targets = this.targetFinder(gameState, militaryManager);340 var targets = this.targetFinder(gameState, HQ); 341 341 if (targets.length === 0) 342 targets = this.defaultTargetFinder(gameState, militaryManager);342 targets = this.defaultTargetFinder(gameState, HQ); 343 343 344 344 if (targets.length !== 0) { … … 366 366 // It will probably carry over a few turns but that's no issue. 367 367 if (this.path === undefined) 368 this.path = this.pathFinder.getPath(this.rallyPoint,this.targetPos, this.pathSampling, this.pathWidth, 250);//,gameState);368 this.path = this.pathFinder.getPath(this.rallyPoint,this.targetPos, this.pathSampling, this.pathWidth,175);//,gameState); 369 369 else if (this.path === "toBeContinued") 370 370 this.path = this.pathFinder.continuePath();//gameState); … … 384 384 // okay so we need a ship. 385 385 // Basically we'll add it as a new class to train compulsorily, and we'll recompute our path. 386 if (!gameState.ai. waterMap)386 if (!gameState.ai.HQ.waterMap) 387 387 { 388 388 debug ("This is actually a water map."); 389 gameState.ai.waterMap = true; 389 gameState.ai.HQ.waterMap = true; 390 return 0; 390 391 } 391 392 debug ("We need a ship."); 392 var stat = { "priority" : 1.1, "minSize" : 2, "targetSize" : 2, "batchSize" : 1, "classes" : ["Warship"],393 "interests" : [ ["strength",1], ["cost",1] ] ,"templates" : [] };394 if (type === "superSized") {395 this.unitStat["TransportShip"]["minSize"] = 4;396 this.unitStat["TransportShip"]["targetSize"] = 4;397 }398 this.addBuildOrder(gameState, "TransportShip", stat);399 393 this.needsShip = true; 400 394 this.pathWidth = 3; … … 415 409 else 416 410 this.rallyPoint = this.path[0][0]; 411 if (i >= 1) 412 this.path.splice(0,i-1); 417 413 break; 418 414 } … … 474 470 // We still have time left to recruit units and do stuffs. 475 471 476 // TODO: check why this can happen instead of resorting to this "hack".477 if (this.buildOrder.length === 0 || this.buildOrder[0] === undefined) {478 debug ("Ending plan: no build orders");479 return 0; // will abort the plan, should return something else480 }481 482 483 472 // let's sort by training advancement, ie 'current size / target size' 484 473 // count the number of queued units too. … … 486 475 this.buildOrder.sort(function (a,b) { //}) { 487 476 var aQueued = gameState.countOwnQueuedEntitiesWithMetadata("special","Plan_"+self.name+"_"+a[4]); 488 aQueued += self.queue.count TotalQueuedUnitsWithMetadata("special","Plan_"+self.name+"_"+a[4]);489 aQueued += self.queueChamp.count TotalQueuedUnitsWithMetadata("special","Plan_"+self.name+"_"+a[4]);477 aQueued += self.queue.countQueuedUnitsWithMetadata("special","Plan_"+self.name+"_"+a[4]); 478 aQueued += self.queueChamp.countQueuedUnitsWithMetadata("special","Plan_"+self.name+"_"+a[4]); 490 479 a[0] = (a[2].length + aQueued)/a[3]["targetSize"]; 491 480 492 481 var bQueued = gameState.countOwnQueuedEntitiesWithMetadata("special","Plan_"+self.name+"_"+b[4]); 493 bQueued += self.queue.count TotalQueuedUnitsWithMetadata("special","Plan_"+self.name+"_"+b[4]);494 bQueued += self.queueChamp.count TotalQueuedUnitsWithMetadata("special","Plan_"+self.name+"_"+b[4]);482 bQueued += self.queue.countQueuedUnitsWithMetadata("special","Plan_"+self.name+"_"+b[4]); 483 bQueued += self.queueChamp.countQueuedUnitsWithMetadata("special","Plan_"+self.name+"_"+b[4]); 495 484 b[0] = (b[2].length + bQueued)/b[3]["targetSize"]; 496 485 … … 513 502 var inTraining = gameState.countOwnQueuedEntitiesWithMetadata("special",specialData); 514 503 515 var queued = this.queue.count TotalQueuedUnitsWithMetadata("special",specialData) + this.queueChamp.countTotalQueuedUnitsWithMetadata("special",specialData)504 var queued = this.queue.countQueuedUnitsWithMetadata("special",specialData) + this.queueChamp.countQueuedUnitsWithMetadata("special",specialData) 516 505 517 506 if (queued + inTraining + this.buildOrder[0][2].length <= this.buildOrder[0][3]["targetSize"]) { … … 522 511 523 512 if (this.buildOrder[0][0] < 1 && queue.length() <= 5) { 524 var template = militaryManager.findBestTrainableUnit(gameState, this.buildOrder[0][1], this.buildOrder[0][3]["interests"] );513 var template = HQ.findBestTrainableSoldier(gameState, this.buildOrder[0][1], this.buildOrder[0][3]["interests"] ); 525 514 //debug ("tried " + uneval(this.buildOrder[0][1]) +", and " + template); 526 515 // HACK (TODO replace) : if we have no trainable template... Then we'll simply remove the buildOrder, effectively removing the unit from the plan. 527 516 if (template === undefined) { 528 517 // TODO: this is a complete hack. 529 if (this.needsShip && this.buildOrder[0][4] == "TransportShip") {530 Engine.ProfileStop();531 Engine.ProfileStop();532 return 0;533 }534 518 delete this.unitStat[this.buildOrder[0][4]]; // deleting the associated unitstat. 535 519 this.buildOrder.splice(0,1); … … 539 523 if (gameState.getTimeElapsed() > 1800000) 540 524 max *= 2; 541 if (gameState.getTemplate(template).hasClass es(["CitizenSoldier", "Infantry"]))542 queue.addItem( new UnitTrainingPlan(gameState,template, { "role" : "worker", "plan" : this.name, "special" : specialData}, this.buildOrder[0][3]["batchSize"],max ) );525 if (gameState.getTemplate(template).hasClass("CitizenSoldier")) 526 queue.addItem( new TrainingPlan(gameState,template, { "role" : "worker", "plan" : this.name, "special" : specialData, "base" : 1 }, this.buildOrder[0][3]["batchSize"],max ) ); 543 527 else 544 queue.addItem( new UnitTrainingPlan(gameState,template, { "role" : "attack", "plan" : this.name, "special" : specialData}, this.buildOrder[0][3]["batchSize"],max ) );528 queue.addItem( new TrainingPlan(gameState,template, { "role" : "attack", "plan" : this.name, "special" : specialData, "base" : 1 }, this.buildOrder[0][3]["batchSize"],max ) ); 545 529 } 546 530 } … … 550 534 551 535 // find our target 552 var targets = this.targetFinder(gameState, militaryManager);536 var targets = this.targetFinder(gameState, HQ); 553 537 if (targets.length === 0){ 554 targets = this.defaultTargetFinder(gameState, militaryManager);538 targets = this.defaultTargetFinder(gameState, HQ); 555 539 } 556 540 if (targets.length) { … … 633 617 for (var unitCat in this.unit) { 634 618 this.unit[unitCat].forEach(function (ent) { 635 if (ent.getMetadata(PlayerID, "role") != "defence" && !ent.hasClass("Warship"))619 if (ent.getMetadata(PlayerID, "role") != "defence") 636 620 { 637 621 ent.setMetadata(PlayerID,"role", "attack"); … … 643 627 for (var unitCat in this.unit) { 644 628 this.unit[unitCat].forEach(function (ent) { 645 if (ent.getMetadata(PlayerID, "role") != "worker" && ent.getMetadata(PlayerID, "role") != "defence" && !ent.hasClass("Warship"))629 if (ent.getMetadata(PlayerID, "role") != "worker" && ent.getMetadata(PlayerID, "role") != "defence") 646 630 ent.move(self.rallyPoint[0],self.rallyPoint[1]); 647 631 }); … … 651 635 652 636 // Default target finder aims for conquest critical targets 653 CityAttack.prototype.defaultTargetFinder = function(gameState, militaryManager){637 CityAttack.prototype.defaultTargetFinder = function(gameState, HQ){ 654 638 var targets = undefined; 655 639 656 targets = militaryManager.enemyWatchers[this.targetPlayer].getEnemyBuildings(gameState, "CivCentre",true);640 targets = HQ.enemyWatchers[this.targetPlayer].getEnemyBuildings(gameState, "CivCentre",true); 657 641 if (targets.length == 0) { 658 targets = militaryManager.enemyWatchers[this.targetPlayer].getEnemyBuildings(gameState, "ConquestCritical");642 targets = HQ.enemyWatchers[this.targetPlayer].getEnemyBuildings(gameState, "ConquestCritical"); 659 643 } 660 644 // If there's nothing, attack anything else that's less critical 661 645 if (targets.length == 0) { 662 targets = militaryManager.enemyWatchers[this.targetPlayer].getEnemyBuildings(gameState, "Town",true);646 targets = HQ.enemyWatchers[this.targetPlayer].getEnemyBuildings(gameState, "Town",true); 663 647 } 664 648 if (targets.length == 0) { 665 targets = militaryManager.enemyWatchers[this.targetPlayer].getEnemyBuildings(gameState, "Village",true);649 targets = HQ.enemyWatchers[this.targetPlayer].getEnemyBuildings(gameState, "Village",true); 666 650 } 667 651 // no buildings, attack anything conquest critical, even units (it's assuming it won't move). … … 673 657 674 658 // tupdate 675 CityAttack.prototype.raidingTargetFinder = function(gameState, militaryManager, Target){659 CityAttack.prototype.raidingTargetFinder = function(gameState, HQ, Target){ 676 660 var targets = undefined; 677 661 if (Target == "villager") … … 694 678 return targets; 695 679 } else { 696 return this.defaultTargetFinder(gameState, militaryManager);680 return this.defaultTargetFinder(gameState, HQ); 697 681 } 698 682 }; … … 701 685 // If we're here, it's because we have in our IDlist enough units. 702 686 // now the IDlist units are treated turn by turn 703 CityAttack.prototype.StartAttack = function(gameState, militaryManager){687 CityAttack.prototype.StartAttack = function(gameState, HQ){ 704 688 705 689 // check we have a target and a path. … … 712 696 713 697 this.unitCollection.forEach(function(ent) { ent.setMetadata(PlayerID, "subrole", "walking"); ent.setMetadata(PlayerID, "role", "attack") ;}); 714 715 this.unitCollection NoWarship = this.unitCollection.filter(Filters.not(Filters.byClass("Warship")));716 this.unitCollection NoWarship.registerUpdates();717 718 this.unitCollection.move Indiv(this.path[0][0][0], this.path[0][0][1]);698 // optimize our collection now. 699 this.unitCollection.freeze(); 700 this.unitCollection.allowQuickIter(); 701 702 this.unitCollection.move(this.path[0][0][0], this.path[0][0][1]); 719 703 this.unitCollection.setStance("aggressive"); 720 704 this.unitCollection.filter(Filters.byClass("Siege")).setStance("defensive"); … … 730 714 731 715 // Runs every turn after the attack is executed 732 CityAttack.prototype.update = function(gameState, militaryManager, events){716 CityAttack.prototype.update = function(gameState, HQ, events){ 733 717 var self = this; 734 718 … … 772 756 attackedNB++; 773 757 } 774 //if ( militaryManager.enemyWatchers[attacker.owner()]) {758 //if (HQ.enemyWatchers[attacker.owner()]) { 775 759 //toProcess[attacker.id()] = attacker; 776 //var armyID = militaryManager.enemyWatchers[attacker.owner()].getArmyFromMember(attacker.id());760 //var armyID = HQ.enemyWatchers[attacker.owner()].getArmyFromMember(attacker.id()); 777 761 //armyToProcess[armyID[0]] = armyID[1]; 778 762 //} … … 852 836 if (this.state === "walking"){ 853 837 854 this.position = this.unitCollection NoWarship.getCentrePosition();838 this.position = this.unitCollection.getCentrePosition(); 855 839 856 840 // probably not too good. … … 881 865 882 866 if (this.lastPosition && SquareVectorDistance(this.position, this.lastPosition) < 20 && this.path.length > 0) { 883 this.unitCollection NoWarship.moveIndiv(this.path[0][0][0], this.path[0][0][1]);867 this.unitCollection.moveIndiv(this.path[0][0][0], this.path[0][0][1]); 884 868 // We're stuck, presumably. Check if there are no walls just close to us. If so, we're arrived, and we're gonna tear down some serious stone. 885 869 var walls = gameState.getEnemyEntities().filter(Filters.and(Filters.byOwner(this.targetPlayer), Filters.byClass("StoneWall"))); … … 903 887 904 888 // check if our land units are close enough from the next waypoint. 905 if (SquareVectorDistance(this.unitCollection NoWarship.getCentrePosition(), this.targetPos) < 7500 ||906 SquareVectorDistance(this.unitCollection NoWarship.getCentrePosition(), this.path[0][0]) < 850) {889 if (SquareVectorDistance(this.unitCollection.getCentrePosition(), this.targetPos) < 7500 || 890 SquareVectorDistance(this.unitCollection.getCentrePosition(), this.path[0][0]) < 650) { 907 891 if (this.unitCollection.filter(Filters.byClass("Siege")).length !== 0 908 && SquareVectorDistance(this.unitCollection NoWarship.getCentrePosition(), this.targetPos) >7500909 && SquareVectorDistance(this.unitCollection.filter(Filters.byClass("Siege")).getCentrePosition(), this.path[0][0]) > 850)892 && SquareVectorDistance(this.unitCollection.getCentrePosition(), this.targetPos) >= 7500 893 && SquareVectorDistance(this.unitCollection.filter(Filters.byClass("Siege")).getCentrePosition(), this.path[0][0]) >= 650) 910 894 { 911 895 } else { 896 897 for (var i = 0; i < this.path.length; ++i) 898 { 899 debug ("path waypoint " + i + "," + this.path[i][1] + " at " + uneval(this.path[i][0])); 900 } 901 debug ("position is " + this.unitCollection.getCentrePosition()); 902 912 903 // okay so here basically two cases. The first one is "we need a boat at this point". 913 904 // the second one is "we need to unload at this point". The third is "normal". … … 916 907 this.path.shift(); 917 908 if (this.path.length > 0){ 918 this.unitCollection NoWarship.moveIndiv(this.path[0][0][0], this.path[0][0][1]);909 this.unitCollection.move(this.path[0][0][0], this.path[0][0][1]); 919 910 } else { 920 911 debug ("Attack Plan " +this.type +" " +this.name +" has arrived to destination."); … … 922 913 this.state = "arrived"; 923 914 } 924 } else if (this.path[0][1] === true)915 } else 925 916 { 926 // okay we must load our units. 927 // check if we have some kind of ships. 928 var ships = this.unitCollection.filter(Filters.byClass("Warship")); 929 if (ships.length === 0) { 917 // TODO: make this require an escort later on. 918 this.path.shift(); 919 if (this.path.length === 0) { 920 debug ("Attack Plan " +this.type +" " +this.name +" has arrived to destination."); 921 // we must assume we've arrived at the end of the trail. 922 this.state = "arrived"; 923 } else { 924 /* 925 var plan = new TransportPlan(gameState, this.unitCollection.toIdArray(), this.path[0][0], false); 926 this.tpPlanID = plan.ID; 927 HQ.navalManager.transportPlans.push(plan); 928 debug ("Transporting over sea"); 929 this.state = "transporting"; 930 */ 931 // TODO: fix this above 932 //right now we'll abort. 930 933 Engine.ProfileStop(); 931 return 0; // abort934 return 0; 932 935 } 933 934 debug ("switch to boarding"); 935 this.state = "boarding"; 936 } 937 } 938 } 939 } else if (this.state === "shipping") { 940 this.position = this.unitCollection.filter(Filters.byClass("Warship")).getCentrePosition(); 941 942 if (!this.lastPosition) 943 this.lastPosition = [0,0]; 944 945 if (SquareVectorDistance(this.position, this.lastPosition) < 20 && this.path.length > 0) { 946 this.unitCollection.filter(Filters.byClass("Warship")).move(this.path[0][0][0], this.path[0][0][1]); 947 } 948 if (SquareVectorDistance(this.position, this.path[0][0]) < 1600) { 949 if (this.path[0][1] !== true) 950 { 951 this.path.shift(); 952 if (this.path.length > 0){ 953 this.unitCollection.filter(Filters.byClass("Warship")).move(this.path[0][0][0], this.path[0][0][1]); 954 } else { 955 debug ("Attack Plan " +this.type +" " +this.name +" has arrived to destination, but it's still on the ship…"); 956 Engine.ProfileStop(); 957 return 0; // abort 958 } 959 } else if (this.path[0][1] === true) 960 { 961 debug ("switch to unboarding"); 962 // we unload 963 this.state = "unboarding"; 964 } 965 } 966 } else if (this.state === "boarding") { 967 this.position = this.unitCollectionNoWarship.getCentrePosition(); 968 969 var ships = this.unitCollection.filter(Filters.byClass("Warship")); 970 if (ships.length === 0) { 971 Engine.ProfileStop(); 972 return 0; // abort 973 } 974 975 var globalPos = this.unitCollectionNoWarship.getCentrePosition(); 976 var shipPos = ships.getCentrePosition(); 977 978 if (globalPos !== undefined && SquareVectorDistance(globalPos,shipPos) > 800) 979 { // get them closer 980 ships.moveIndiv(globalPos[0],globalPos[1]); 981 this.unitCollectionNoWarship.moveIndiv(shipPos[0],shipPos[1]); 982 } else { 983 // okay try to garrison. 984 var shipsArray = ships.toEntityArray(); 985 this.unitCollectionNoWarship.forEach(function (ent) { //}){ 986 if (ent.position()) // if we're not garrisoned 987 for (var shipId = 0; shipId < shipsArray.length; shipId++) { 988 if (shipsArray[shipId].garrisoned().length < shipsArray[shipId].garrisonMax()) 989 { 990 ent.garrison(shipsArray[shipId]); 991 break; 992 } 993 } 994 }); 995 var garrLength = 0; 996 for (var shipId = 0; shipId < shipsArray.length; shipId++) 997 garrLength += shipsArray[shipId].garrisoned().length; 998 999 if (garrLength == this.unitCollectionNoWarship.length) { 1000 // okay. 1001 this.path.shift(); 1002 if (this.path.length > 0){ 1003 ships.move(this.path[0][0][0], this.path[0][0][1]); 1004 debug ("switch to shipping"); 1005 this.state = "shipping"; 1006 } else { 1007 debug ("Attack Plan " +this.type +" " +this.name +" has arrived to destination."); 1008 // we must assume we've arrived at the end of the trail. 1009 this.state = "arrived"; 1010 } 1011 } 1012 } 1013 } else if (this.state === "unboarding") { 1014 1015 var ships = this.unitCollection.filter(Filters.byClass("Warship")); 1016 if (ships.length === 0) { 1017 Engine.ProfileStop(); 1018 return 0; // abort 1019 } 1020 1021 this.position = ships.getCentrePosition(); 1022 1023 // the procedure is pretty simple: we move the ships to the next point and try to unload until all units are over. 1024 // TODO: make it better, like avoiding collisions, and so on. 1025 1026 if (this.path.length > 1) 1027 ships.move(this.path[1][0][0], this.path[1][0][1]); 1028 1029 ships.forEach(function (ship) { 1030 ship.unloadAll(); 1031 }); 1032 1033 var shipsArray = ships.toEntityArray(); 1034 var garrLength = 0; 1035 for (var shipId = 0; shipId < shipsArray.length; shipId++) 1036 garrLength += shipsArray[shipId].garrisoned().length; 1037 1038 if (garrLength == 0) { 1039 // release the ships 1040 1041 ships.forEach(function (ent) { 1042 ent.setMetadata(PlayerID, "role",undefined); 1043 ent.setMetadata(PlayerID, "subrole",undefined); 1044 ent.setMetadata(PlayerID, "plan",undefined); 1045 }); 1046 for (var shipId = 0; shipId < shipsArray.length; shipId++) 1047 this.unitCollection.removeEnt(shipsArray[shipId]); 1048 1049 this.path.shift(); 1050 if (this.path.length > 0){ 1051 this.unitCollection.moveIndiv(this.path[0][0][0], this.path[0][0][1]); 1052 debug ("switch to walking"); 1053 this.state = "walking"; 1054 } else { 1055 debug ("Attack Plan " +this.type +" " +this.name +" has arrived to destination."); 1056 // we must assume we've arrived at the end of the trail. 1057 this.state = "arrived"; 1058 } 1059 } 1060 936 } 937 } 938 } 939 } else if (this.state === "transporting") { 940 // check that we haven't finished transporting, ie the plan 941 if (!HQ.navalManager.checkActivePlan(this.tpPlanID)) 942 { 943 this.state = "walking"; 944 } 1061 945 } 1062 946 … … 1095 979 1096 980 if (this.state === "") { 1097 1098 981 // Units attacked will target their attacker unless they're siege. Then we take another non-siege unit to attack them. 1099 982 for (var key in events) { … … 1107 990 if (ourUnit.hasClass("Siege")) 1108 991 { 1109 var help = this.unitCollection.filter(Filters.and(Filters.not(Filters.byClass("Siege")),Filters.isIdle())); 1110 if (help.length === 0) 1111 help = this.unitCollection.filter(Filters.not(Filters.byClass("Siege"))); 1112 if (help.length > 0) 1113 help.toEntityArray()[0].attack(attacker.id()); 1114 if (help.length > 1) 1115 help.toEntityArray()[1].attack(attacker.id()); 1116 992 var collec = this.unitCollection.filterNearest(ourUnit.position(), 8).filter(Filters.not(Filters.byClass("Siege"))).toEntityArray(); 993 if (collec.length !== 0) 994 { 995 collec[0].attack(attacker.id()); 996 if (collec.length !== 1) 997 { 998 collec[1].attack(attacker.id()); 999 if (collec.length !== 2) 1000 { 1001 collec[2].attack(attacker.id()); 1002 } 1003 } 1004 } 1117 1005 } else { 1118 1006 ourUnit.attack(attacker.id()); … … 1123 1011 } 1124 1012 1125 1126 var enemyUnits = gameState.getEnemyEntities().filter(Filters.and(Filters.byOwner(this.targetPlayer), Filters.byClass("Unit"))); 1127 var enemyStructures = gameState.getEnemyEntities().filter(Filters.and(Filters.byOwner(this.targetPlayer), Filters.byClass("Structure"))); 1013 var enemyUnits = gameState.getGEC("player-" +this.targetPlayer + "-units"); 1014 var enemyStructures = gameState.getGEC("player-" +this.targetPlayer + "-structures"); 1128 1015 1129 1016 if (this.unitCollUpdateArray === undefined || this.unitCollUpdateArray.length === 0) 1130 1017 { 1131 this.unitCollUpdateArray = this.unitCollection.to EntityArray();1018 this.unitCollUpdateArray = this.unitCollection.toIdArray(); 1132 1019 } else { 1020 // some stuffs for locality and speed 1021 var territoryMap = Map.createTerritoryMap(gameState); 1022 var timeElapsed = gameState.getTimeElapsed(); 1023 1133 1024 // Let's check a few units each time we update. Currently 10 1134 for (var check = 0; check < Math.min(this.unitCollUpdateArray.length,10); check++) 1025 var lgth = Math.min(this.unitCollUpdateArray.length,10); 1026 for (var check = 0; check < lgth; check++) 1135 1027 { 1136 var ent = this.unitCollUpdateArray[0]; 1137 1138 // if the unit is not in my territory, make it move. 1139 var territoryMap = Map.createTerritoryMap(gameState); 1028 var ent = gameState.getEntityById(this.unitCollUpdateArray[0]); 1029 if (!ent) 1030 continue; 1031 var orderData = ent.unitAIOrderData(); 1032 if (orderData.length !== 0) 1033 orderData = orderData[0]; 1034 else 1035 orderData = undefined; 1036 1037 // if the unit is in my territory, make it move. 1140 1038 if (territoryMap.point(ent.position()) - 64 === PlayerID) 1141 1039 ent.move(this.targetPos[0],this.targetPos[1]); 1040 1142 1041 // update it. 1143 1042 var needsUpdate = false; 1144 1043 if (ent.isIdle()) 1145 1044 needsUpdate = true; 1146 if (ent.hasClass("Siege") && (! ent.unitAIOrderData() || !ent.unitAIOrderData()["target"] || !gameState.getEntityById(ent.unitAIOrderData()["target"]).hasClass("ConquestCritical")) )1045 if (ent.hasClass("Siege") && (!orderData || !orderData["target"] || !gameState.getEntityById(orderData["target"]) || !gameState.getEntityById(orderData["target"]).hasClass("ConquestCritical")) ) 1147 1046 needsUpdate = true; 1148 else if ( ent.unitAIOrderData() && ent.unitAIOrderData()["target"] && gameState.getEntityById(ent.unitAIOrderData()["target"]).hasClass("Structure"))1047 else if (!ent.hasClass("Siege") && orderData && orderData["target"] && gameState.getEntityById(orderData["target"]) && gameState.getEntityById(orderData["target"]).hasClass("Structure")) 1149 1048 needsUpdate = true; // try to make it attack a unit instead 1150 1151 if ( gameState.getTimeElapsed()- ent.getMetadata(PlayerID, "lastAttackPlanUpdateTime") < 10000)1049 1050 if (timeElapsed - ent.getMetadata(PlayerID, "lastAttackPlanUpdateTime") < 10000) 1152 1051 needsUpdate = false; 1153 1052 1154 if (needsUpdate || arrivedthisTurn)1053 if (needsUpdate === true || arrivedthisTurn) 1155 1054 { 1156 ent.setMetadata(PlayerID, "lastAttackPlanUpdateTime", gameState.getTimeElapsed());1055 ent.setMetadata(PlayerID, "lastAttackPlanUpdateTime", timeElapsed); 1157 1056 var mStruct = enemyStructures.filter(function (enemy) { //}){ 1158 1057 if (!enemy.position() || (enemy.hasClass("StoneWall") && ent.canAttackClass("StoneWall"))) { 1159 1058 return false; 1160 1059 } 1161 if (SquareVectorDistance(enemy.position(),ent.position()) > 2000) {1060 if (SquareVectorDistance(enemy.position(),ent.position()) > 3000) { 1162 1061 return false; 1163 1062 } … … 1165 1064 }); 1166 1065 var mUnit; 1167 if (ent.hasClass("Cavalry") ) {1066 if (ent.hasClass("Cavalry") && ent.countersClasses(["Support"])) { 1168 1067 mUnit = enemyUnits.filter(function (enemy) { //}){ 1169 1068 if (!enemy.position()) { … … 1172 1071 if (!enemy.hasClass("Support")) 1173 1072 return false; 1174 if (SquareVectorDistance(enemy.position(),ent.position()) > 2000) {1073 if (SquareVectorDistance(enemy.position(),ent.position()) > 10000) { 1175 1074 return false; 1176 1075 } … … 1178 1077 }); 1179 1078 } 1180 if (! ent.hasClass("Cavalry") || mUnit.length === 0) {1079 if (!(ent.hasClass("Cavalry") && ent.countersClasses(["Support"])) || mUnit.length === 0) { 1181 1080 mUnit = enemyUnits.filter(function (enemy) { //}){ 1182 1081 if (!enemy.position()) { 1183 1082 return false; 1184 1083 } 1185 if (SquareVectorDistance(enemy.position(),ent.position()) > 2000) {1084 if (SquareVectorDistance(enemy.position(),ent.position()) > 10000) { 1186 1085 return false; 1187 1086 } … … 1212 1111 return (valb - vala); 1213 1112 }); 1214 1113 // TODO: handle ballistas here 1215 1114 if (mStruct.length !== 0) { 1216 1115 if (isGate) … … 1227 1126 } 1228 1127 } else { 1229 if (mUnit.length !== 0 && !isGate) {1128 if (mUnit.length !== 0) { 1230 1129 var rand = Math.floor(Math.random() * mUnit.length*0.99); 1231 1130 ent.attack(mUnit[(+rand)].id()); 1232 1131 //debug ("Units attacking a unit from " +mUnit[+rand].owner() + " , " +mUnit[+rand].templateName()); 1132 } else if (SquareVectorDistance(self.targetPos, ent.position()) > 900 ){ 1133 //debug ("Units moving to " + uneval(self.targetPos)); 1134 ent.move(self.targetPos[0],self.targetPos[1]); 1233 1135 } else if (mStruct.length !== 0) { 1234 1136 mStruct.sort(function (structa,structb) { //}){ … … 1257 1159 //debug ("Units attacking a structure from " +mStruct[+rand].owner() + " , " +mStruct[+rand].templateName()); 1258 1160 } 1259 } else if (SquareVectorDistance(self.targetPos, ent.position()) > 900 ){1260 //debug ("Units moving to " + uneval(self.targetPos));1261 ent.move(self.targetPos[0],self.targetPos[1]);1262 1161 } 1263 1162 } 1264 1163 } 1265 1266 this.unitCollUpdateArray.splice(0,1); 1267 } 1164 } 1165 this.unitCollUpdateArray.splice(0,10); 1268 1166 } 1269 1167 // updating targets. 1270 1168 if (!gameState.getEntityById(this.target.id())) 1271 1169 { 1272 var targets = this.targetFinder(gameState, militaryManager);1170 var targets = this.targetFinder(gameState, HQ); 1273 1171 if (targets.length === 0){ 1274 targets = this.defaultTargetFinder(gameState, militaryManager);1172 targets = this.defaultTargetFinder(gameState, HQ); 1275 1173 } 1276 1174 if (targets.length) { -
ps/trunk/binaries/data/mods/public/simulation/ai/aegis/config.js
r13683 r13907 4 4 "fortressLapseTime" : 540, // Time to wait between building 2 fortresses 5 5 "defenceBuildingTime" : 600, // Time to wait before building towers or fortresses 6 "attackPlansStartTime" : 0 // time to wait before attacking. Start as soon as possible (first barracks) 6 "attackPlansStartTime" : 0, // time to wait before attacking. Start as soon as possible (first barracks) 7 "techStartTime" : 120, // time to wait before teching. Will only start after town phase so it's irrelevant. 8 "popForBarracks1" : 15, 9 "popForBarracks2" : 95, 10 "timeForBlacksmith" : 900, 7 11 }, 8 12 "Economy" : { 9 13 "townPhase" : 180, // time to start trying to reach town phase (might be a while after. Still need the requirements + ress ) 10 "cityPhase" : 540, // time to start trying to reach city phase 11 "farmsteadStartTime" : 400, // Time to wait before building a farmstead. 14 "cityPhase" : 840000, // time to start trying to reach city phase 15 "popForMarket" : 80, 16 "popForFarmstead" : 45, 12 17 "dockStartTime" : 240, // Time to wait before building the dock 13 "techStartTime" : 600, // time to wait before teching. 14 "targetNumBuilders" : 1.5, // Base number of builders per foundation. Later updated, but this remains a multiplier. 15 "femaleRatio" : 0.6 // percent of females among the workforce. 18 "techStartTime" : 0, // time to wait before teching. 19 "targetNumBuilders" : 1.5, // Base number of builders per foundation. 20 "femaleRatio" : 0.4, // percent of females among the workforce. 21 "initialFields" : 2 16 22 }, 17 23 … … 50 56 // qbot 51 57 "priorities" : { // Note these are dynamic, you are only setting the initial values 52 "house" : 200, 53 "citizenSoldier" : 70, 54 "villager" : 55, 55 "economicBuilding" : 70, 58 "house" : 350, 59 "villager" : 40, 60 "citizenSoldier" : 60, 61 "ships" : 70, 62 "economicBuilding" : 90, 56 63 "dropsites" : 120, 57 "field" : 1000,58 "militaryBuilding" : 90,64 "field" : 500, 65 "militaryBuilding" : 110, 59 66 "defenceBuilding" : 70, 60 "majorTech" : 400,61 "minorTech" : 40,62 "civilCentre" : 10000 // will hog all resources67 "majorTech" : 700, 68 "minorTech" : 50, 69 "civilCentre" : 400 63 70 }, 64 "difficulty" : 2, // for now 2 is "hard", ie default. 1 is normal, 0 is easy. 3 is very hard71 "difficulty" : 2, // 0 is sandbox, 1 is easy, 2 is medium, 3 is hard, 4 is very hard. 65 72 "debug" : false 66 73 }; … … 75 82 if (Config.difficulty === 1) 76 83 { 77 Config["Military"] = { 78 "fortressLapseTime" : 900, 79 "defenceBuildingTime" : 720, 80 "attackPlansStartTime" : 1200 81 }; 82 Config["Economy"] = { 83 "townPhase" : 360, 84 "cityPhase" : 900, 85 "farmsteadStartTime" : 600, 86 "dockStartTime" : 240, 87 "techStartTime" : 1320, 88 "targetNumBuilders" : 2, 89 "femaleRatio" : 0.5, 90 "targetNumWorkers" : 110 // should not make more than 2 barracks. 91 }; 92 Config["Defence"] = { 93 "defenceRatio" : 4.0, 94 "armyCompactSize" : 700, 95 "armyBreakawaySize" : 900 96 }; 97 } else if (Config.difficulty === 0) 84 Config.Military.defenceBuildingTime = 1200; 85 Config.Military.attackPlansStartTime = 960; 86 Config.Military.popForBarracks1 = 35; 87 Config.Military.popForBarracks2 = 150; // shouldn't reach it 88 Config.Military.popForBlacksmith = 150; // shouldn't reach it 89 90 Config.Economy.cityPhase = 1800; 91 Config.Economy.popForMarket = 80; 92 Config.Economy.techStartTime = 600; 93 Config.Economy.femaleRatio = 0.6; 94 Config.Economy.initialFields = 1; 95 // Config.Economy.targetNumWorkers will be set by AI scripts. 96 } 97 else if (Config.difficulty === 0) 98 98 { 99 Config["Military"] = { 100 "fortressLapseTime" : 1000000, // never 101 "defenceBuildingTime" : 900, 102 "attackPlansStartTime" : 120000 // never 103 }; 104 Config["Economy"] = { 105 "townPhase" : 480, 106 "cityPhase" : 1200, 107 "farmsteadStartTime" : 1200, 108 "dockStartTime" : 240, 109 "techStartTime" : 600000, // never 110 "targetNumBuilders" : 1, 111 "femaleRatio" : 0.0, // makes us slower, but also less sucky at defending so it's still fun to attack it. 112 "targetNumWorkers" : 70 113 }; 114 Config["Defence"] = { 115 "defenceRatio" : 2.0, 116 "armyCompactSize" : 700, 117 "armyBreakawaySize" : 900 118 }; 99 Config.Military.defenceBuildingTime = 450; 100 Config.Military.attackPlansStartTime = 9600000; // never 101 Config.Military.popForBarracks1 = 60; 102 Config.Military.popForBarracks2 = 150; // shouldn't reach it 103 Config.Military.popForBlacksmith = 150; // shouldn't reach it 104 105 Config.Economy.cityPhase = 240000; 106 Config.Economy.popForMarket = 200; 107 Config.Economy.techStartTime = 1800; 108 Config.Economy.femaleRatio = 0.2; 109 Config.Economy.initialFields = 1; 110 // Config.Economy.targetNumWorkers will be set by AI scripts. 119 111 } 120 112 } -
ps/trunk/binaries/data/mods/public/simulation/ai/aegis/data.json
r13683 r13907 2 2 "name": "Aegis Bot", 3 3 "description": "Wraitii's improvement of qBot. It is more reliable and generally a better player. Note that it doesn't support saved games yet, and there may be other bugs. Please report issues to Wildfire Games (see the link in the main menu).", 4 "constructor": " QBotAI",4 "constructor": "AegisBot", 5 5 "useShared": true 6 6 } -
ps/trunk/binaries/data/mods/public/simulation/ai/aegis/defence.js
r13683 r13907 49 49 50 50 51 Defence.prototype.update = function(gameState, events, militaryManager){51 Defence.prototype.update = function(gameState, events, HQ){ 52 52 53 53 Engine.ProfileStart("Defence Manager"); … … 81 81 82 82 // First step: we deal with enemy armies, those are the highest priority. 83 this.defendFromEnemies(gameState, events, militaryManager);83 this.defendFromEnemies(gameState, events, HQ); 84 84 85 85 // second step: we loop through messages, and sort things as needed (dangerous buildings, attack by animals, ships, lone units, whatever). 86 86 // TODO : a lot. 87 this.MessageProcess(gameState,events, militaryManager);88 89 this.DealWithWantedUnits(gameState,events, militaryManager);87 this.MessageProcess(gameState,events,HQ); 88 89 this.DealWithWantedUnits(gameState,events,HQ); 90 90 91 91 /* … … 139 139 // Incorporates an entity in an army. If no army fits, it creates a new one around this one. 140 140 // an army is basically an entity collection. 141 Defence.prototype.armify = function(gameState, entity, militaryManager, minNBForArmy) {141 Defence.prototype.armify = function(gameState, entity, HQ, minNBForArmy) { 142 142 if (entity.position() === undefined) 143 143 return; … … 161 161 } 162 162 } 163 if ( militaryManager)163 if (HQ) 164 164 { 165 165 var self = this; 166 var close = militaryManager.enemyWatchers[entity.owner()].enemySoldiers.filter(Filters.byDistance(entity.position(), self.armyCompactSize));166 var close = HQ.enemyWatchers[entity.owner()].enemySoldiers.filter(Filters.byDistance(entity.position(), self.armyCompactSize)); 167 167 if (!minNBForArmy || close.length >= minNBForArmy) 168 168 { … … 224 224 // This deals with incoming enemy armies, setting the defcon if needed. It will take new soldiers, and assign them to attack 225 225 // TODO: still is still pretty dumb, it could use improvements. 226 Defence.prototype.defendFromEnemies = function(gameState, events, militaryManager) {226 Defence.prototype.defendFromEnemies = function(gameState, events, HQ) { 227 227 var self = this; 228 228 … … 265 265 for (var enemyID in this.enemyArmy) 266 266 { 267 //this.enemyUnits[enemyID] = militaryManager.enemyWatchers[enemyID].getAllEnemySoldiers();267 //this.enemyUnits[enemyID] = HQ.enemyWatchers[enemyID].getAllEnemySoldiers(); 268 268 if (this.enemyUnits[enemyID] === undefined || this.enemyUnits[enemyID].length === 0) 269 269 { 270 this.enemyUnits[enemyID] = militaryManager.enemyWatchers[enemyID].enemySoldiers.toEntityArray();270 this.enemyUnits[enemyID] = HQ.enemyWatchers[enemyID].enemySoldiers.toEntityArray(); 271 271 } else { 272 272 // we have some units still to check in this array. Check 15 (TODO: DIFFLEVEL) … … 284 284 var dangerous = this.evaluateEntity(gameState, this.enemyUnits[enemyID][0]); 285 285 if (dangerous) 286 this.armify(gameState, this.enemyUnits[enemyID][0], militaryManager,2);286 this.armify(gameState, this.enemyUnits[enemyID][0], HQ,2); 287 287 this.enemyUnits[enemyID].splice(0,1); 288 288 } … … 380 380 self.nbDefenders--; 381 381 }); 382 militaryManager.ungarrisonAll(gameState);383 militaryManager.unpauseAllPlans(gameState);382 HQ.ungarrisonAll(gameState); 383 HQ.unpauseAllPlans(gameState); 384 384 return; 385 385 } else if (this.nbAttackers === 0 && this.nbDefenders !== 0) { … … 394 394 self.nbDefenders--; 395 395 }); 396 militaryManager.ungarrisonAll(gameState);397 militaryManager.unpauseAllPlans(gameState);396 HQ.ungarrisonAll(gameState); 397 HQ.unpauseAllPlans(gameState); 398 398 return; 399 399 } 400 400 if ( (this.nbDefenders < 4 && this.nbAttackers >= 5) || this.nbDefenders === 0) { 401 militaryManager.ungarrisonAll(gameState);401 HQ.ungarrisonAll(gameState); 402 402 } 403 403 … … 450 450 451 451 if (gameState.defcon() > 3) 452 militaryManager.unpauseAllPlans(gameState);452 HQ.unpauseAllPlans(gameState); 453 453 454 454 if ( (nonDefenders.length + this.nbDefenders > newEnemies.length + this.nbAttackers) … … 468 468 if (gameState.defcon() < 2 && (this.nbAttackers-this.nbDefenders) > 15) 469 469 { 470 militaryManager.pauseAllPlans(gameState);470 HQ.pauseAllPlans(gameState); 471 471 } else if (gameState.defcon() < 3 && this.nbDefenders === 0 && newEnemies.length === 0) { 472 militaryManager.ungarrisonAll(gameState);472 HQ.ungarrisonAll(gameState); 473 473 }*/ 474 474 … … 550 550 defs.forEach(function (defender) { //}){ 551 551 if (defender.getMetadata(PlayerID, "plan") != undefined && (gameState.defcon() < 4 || defender.getMetadata(PlayerID,"subrole") == "walking")) 552 militaryManager.pausePlan(gameState, defender.getMetadata(PlayerID, "plan"));552 HQ.pausePlan(gameState, defender.getMetadata(PlayerID, "plan")); 553 553 //debug ("Against " +enemy.id() + " Assigning " + defender.id()); 554 554 if (defender.getMetadata(PlayerID, "role") == "worker" || defender.getMetadata(PlayerID, "role") == "attack") … … 606 606 // So that a unit that gets attacked will not be completely dumb. 607 607 // warning: huge levels of indentation coming. 608 Defence.prototype.MessageProcess = function(gameState,events, militaryManager) {608 Defence.prototype.MessageProcess = function(gameState,events, HQ) { 609 609 var self = this; 610 610 … … 678 678 { 679 679 var position = attacker.position(); 680 var close = militaryManager.enemyWatchers[attacker.owner()].enemySoldiers.filter(Filters.byDistance(position, self.armyCompactSize));680 var close = HQ.enemyWatchers[attacker.owner()].enemySoldiers.filter(Filters.byDistance(position, self.armyCompactSize)); 681 681 682 682 if (close.length > 2 || ourUnit.hasClass("Support") || attacker.hasClass("Siege")) … … 737 737 738 738 // At most, this will put defcon to 4 739 Defence.prototype.DealWithWantedUnits = function(gameState, events, militaryManager) {739 Defence.prototype.DealWithWantedUnits = function(gameState, events, HQ) { 740 740 //if (gameState.defcon() < 3) 741 741 // return; -
ps/trunk/binaries/data/mods/public/simulation/ai/aegis/enemy-watcher.js
r13683 r13907 13 13 var filter = Filters.and(Filters.byClass("Structure"), Filters.byOwner(this.watched)); 14 14 this.enemyBuildings = gameState.updatingGlobalCollection("player-" +this.watched + "-structures", filter); 15 15 16 filter = Filters.and(Filters.byClass("Unit"), Filters.byOwner(this.watched)); 17 this.enemyUnits = gameState.updatingGlobalCollection("player-" +this.watched + "-units", filter); 18 16 19 filter = Filters.and(Filters.byClass("Worker"), Filters.byOwner(this.watched)); 17 20 this.enemyCivilians = gameState.updatingGlobalCollection("player-" +this.watched + "-civilians", filter); -
ps/trunk/binaries/data/mods/public/simulation/ai/aegis/entitycollection-extend.js
r13683 r13907 7 7 } 8 8 } 9 return new EntityCollection(gameState. ai, ents);9 return new EntityCollection(gameState.sharedScript, ents); 10 10 } -
ps/trunk/binaries/data/mods/public/simulation/ai/aegis/map-module.js
r13683 r13907 1 const TERRITORY_PLAYER_MASK = 0x3F; 1 // other map functions 2 2 3 //TODO: Make this cope with negative cell values 4 // This is by default a 16-bit map but can be adapted into 8-bit. 5 function Map(gameState, originalMap, actualCopy){ 6 // get the map to find out the correct dimensions 7 var gameMap = gameState.getMap(); 8 this.width = gameMap.width; 9 this.height = gameMap.height; 10 this.length = gameMap.data.length; 11 12 this.maxVal = 65535; 13 14 if (originalMap && actualCopy){ 15 this.map = new Uint16Array(this.length); 16 for (var i = 0; i < originalMap.length; ++i) 17 this.map[i] = originalMap[i]; 18 } else if (originalMap) { 19 this.map = originalMap; 20 } else { 21 this.map = new Uint16Array(this.length); 22 } 23 this.cellSize = gameState.cellSize; 24 } 25 Map.prototype.setMaxVal = function(val){ 26 this.maxVal = val; 27 }; 28 29 Map.prototype.gamePosToMapPos = function(p){ 30 return [Math.floor(p[0]/this.cellSize), Math.floor(p[1]/this.cellSize)]; 31 }; 32 33 Map.prototype.point = function(p){ 34 var q = this.gamePosToMapPos(p); 35 return this.map[q[0] + this.width * q[1]]; 36 }; 37 38 // returns an 8-bit map. 39 Map.createObstructionMap = function(gameState, template){ 3 Map.createObstructionMap = function(gameState, accessIndex, template){ 40 4 var passabilityMap = gameState.getMap(); 41 var territoryMap = gameState.ai.territoryMap; 5 var territoryMap = gameState.ai.territoryMap; 42 6 43 7 // default values … … 50 14 if (template){ 51 15 placementType = template.buildPlacementType(); 52 buildOwn = template.hasBuildTerritory("own"); 53 buildAlly = template.hasBuildTerritory("ally"); 54 buildNeutral = template.hasBuildTerritory("neutral"); 16 buildOwn = template.hasBuildTerritory("own"); 17 buildAlly = template.hasBuildTerritory("ally"); 18 buildNeutral = template.hasBuildTerritory("neutral"); 55 19 buildEnemy = template.hasBuildTerritory("enemy"); 56 20 } 57 21 58 22 var obstructionMask = gameState.getPassabilityClassMask("foundationObstruction") | gameState.getPassabilityClassMask("building-land"); 59 23 … … 67 31 for (var y = 0; y < passabilityMap.height; ++y) 68 32 { 69 okay = false;70 33 var i = x + y*passabilityMap.width; 71 34 var tilePlayer = (territoryMap.data[i] & TERRITORY_PLAYER_MASK); 35 36 if (gameState.ai.myIndex !== gameState.ai.accessibility.landPassMap[i]) 37 { 38 obstructionTiles[i] = 0; 39 continue; 40 } 41 if (gameState.isPlayerEnemy(tilePlayer) && tilePlayer !== 0) 42 { 43 obstructionTiles[i] = 0; 44 continue; 45 } 46 if ((passabilityMap.data[i] & (gameState.getPassabilityClassMask("building-shore") | gameState.getPassabilityClassMask("default")))) 47 { 48 obstructionTiles[i] = 0; 49 continue; 50 } 72 51 52 okay = false; 73 53 var positions = [[0,1], [1,1], [1,0], [1,-1], [0,-1], [-1,-1], [-1,0], [-1,1]]; 74 54 var available = 0; … … 79 59 var index3 = x + stuff[0]*3 + (y+stuff[1]*3)*passabilityMap.width; 80 60 var index4 = x + stuff[0]*4 + (y+stuff[1]*4)*passabilityMap.width; 81 if ((passabilityMap.data[index] & gameState.getPassabilityClassMask("default")) && gameState.ai.accessibility.getRegionSizei(index) > 500) 82 if ((passabilityMap.data[index2] & gameState.getPassabilityClassMask("default")) && gameState.ai.accessibility.getRegionSizei(index2) > 500) 83 if ((passabilityMap.data[index3] & gameState.getPassabilityClassMask("default")) && gameState.ai.accessibility.getRegionSizei(index3) > 500) 84 if ((passabilityMap.data[index4] & gameState.getPassabilityClassMask("default")) && gameState.ai.accessibility.getRegionSizei(index4) > 500) { 61 62 if ((passabilityMap.data[index] & gameState.getPassabilityClassMask("default")) && gameState.ai.accessibility.getRegionSizei(index,true) > 500) 63 if ((passabilityMap.data[index2] & gameState.getPassabilityClassMask("default")) && gameState.ai.accessibility.getRegionSizei(index2,true) > 500) 64 if ((passabilityMap.data[index3] & gameState.getPassabilityClassMask("default")) && gameState.ai.accessibility.getRegionSizei(index3,true) > 500) 65 if ((passabilityMap.data[index4] & gameState.getPassabilityClassMask("default")) && gameState.ai.accessibility.getRegionSizei(index4,true) > 500) { 85 66 if (available < 2) 86 67 available++; … … 99 80 okay = false; 100 81 } 101 if (gameState.ai.myIndex !== gameState.ai.accessibility.passMap[i])102 okay = false;103 if (gameState.isPlayerEnemy(tilePlayer) && tilePlayer !== 0)104 okay = false;105 if ((passabilityMap.data[i] & (gameState.getPassabilityClassMask("building-shore") | gameState.getPassabilityClassMask("default"))))106 okay = false;107 82 obstructionTiles[i] = okay ? 255 : 0; 108 83 } … … 121 96 (!buildEnemy && gameState.isPlayerEnemy(tilePlayer) && tilePlayer != 0) 122 97 ); 123 var tileAccessible = (gameState.ai.myIndex === gameState.ai.accessibility.passMap[i]); 98 if (accessIndex) 99 var tileAccessible = (accessIndex === gameState.ai.accessibility.landPassMap[i]); 100 else 101 var tileAccessible = true; 124 102 if (placementType === "shore") 125 103 tileAccessible = true; … … 127 105 } 128 106 } 129 130 var map = new Map(gameState , obstructionTiles);107 108 var map = new Map(gameState.sharedScript, obstructionTiles); 131 109 map.setMaxVal(255); 132 110 … … 136 114 if (minDist !== undefined && category !== undefined){ 137 115 gameState.getOwnEntities().forEach(function(ent) { 138 if (ent.buildCategory() === category && ent.position()){139 var pos = ent.position();140 var x = Math.round(pos[0] / gameState.cellSize);141 var z = Math.round(pos[1] / gameState.cellSize);142 map.addInfluence(x, z, minDist/gameState.cellSize, -255, 'constant');143 }144 });116 if (ent.buildCategory() === category && ent.position()){ 117 var pos = ent.position(); 118 var x = Math.round(pos[0] / gameState.cellSize); 119 var z = Math.round(pos[1] / gameState.cellSize); 120 map.addInfluence(x, z, minDist/gameState.cellSize, -255, 'constant'); 121 } 122 }); 145 123 } 146 124 } 147 148 125 return map; 149 126 }; 127 128 150 129 151 130 Map.createTerritoryMap = function(gameState) { 152 131 var map = gameState.ai.territoryMap; 153 132 154 var ret = new Map(gameState , map.data);133 var ret = new Map(gameState.sharedScript, map.data); 155 134 156 135 ret.getOwner = function(p) { … … 162 141 return ret; 163 142 }; 164 165 Map.prototype.addInfluence = function(cx, cy, maxDist, strength, type) {166 strength = strength ? +strength : +maxDist;167 type = type ? type : 'linear';168 169 var x0 = Math.max(0, cx - maxDist);170 var y0 = Math.max(0, cy - maxDist);171 var x1 = Math.min(this.width, cx + maxDist);172 var y1 = Math.min(this.height, cy + maxDist);173 var maxDist2 = maxDist * maxDist;174 175 var str = 0.0;176 switch (type){177 case 'linear':178 str = +strength / +maxDist;179 break;180 case 'quadratic':181 str = +strength / +maxDist2;182 break;183 case 'constant':184 str = +strength;185 break;186 }187 188 for ( var y = y0; y < y1; ++y) {189 for ( var x = x0; x < x1; ++x) {190 var dx = x - cx;191 var dy = y - cy;192 var r2 = dx*dx + dy*dy;193 if (r2 < maxDist2){194 var quant = 0;195 switch (type){196 case 'linear':197 var r = Math.sqrt(r2);198 quant = str * (maxDist - r);199 break;200 case 'quadratic':201 quant = str * (maxDist2 - r2);202 break;203 case 'constant':204 quant = str;205 break;206 }207 if (this.map[x + y * this.width] + quant < 0)208 this.map[x + y * this.width] = 0;209 else if (this.map[x + y * this.width] + quant > this.maxVal)210 this.map[x + y * this.width] = this.maxVal; // avoids overflow.211 else212 this.map[x + y * this.width] += quant;213 }214 }215 }216 };217 218 Map.prototype.multiplyInfluence = function(cx, cy, maxDist, strength, type) {219 strength = strength ? +strength : +maxDist;220 type = type ? type : 'constant';221 222 var x0 = Math.max(0, cx - maxDist);223 var y0 = Math.max(0, cy - maxDist);224 var x1 = Math.min(this.width, cx + maxDist);225 var y1 = Math.min(this.height, cy + maxDist);226 var maxDist2 = maxDist * maxDist;227 228 var str = 0.0;229 switch (type){230 case 'linear':231 str = strength / maxDist;232 break;233 case 'quadratic':234 str = strength / maxDist2;235 break;236 case 'constant':237 str = strength;238 break;239 }240 241 for ( var y = y0; y < y1; ++y) {242 for ( var x = x0; x < x1; ++x) {243 var dx = x - cx;244 var dy = y - cy;245 var r2 = dx*dx + dy*dy;246 if (r2 < maxDist2){247 var quant = 0;248 switch (type){249 case 'linear':250 var r = Math.sqrt(r2);251 quant = str * (maxDist - r);252 break;253 case 'quadratic':254 quant = str * (maxDist2 - r2);255 break;256 case 'constant':257 quant = str;258 break;259 }260 var machin = this.map[x + y * this.width] * quant;261 if (machin < 0)262 this.map[x + y * this.width] = 0;263 else if (machin > this.maxVal)264 this.map[x + y * this.width] = this.maxVal;265 else266 this.map[x + y * this.width] = machin;267 }268 }269 }270 };271 // doesn't check for overflow.272 Map.prototype.setInfluence = function(cx, cy, maxDist, value) {273 value = value ? value : 0;274 275 var x0 = Math.max(0, cx - maxDist);276 var y0 = Math.max(0, cy - maxDist);277 var x1 = Math.min(this.width, cx + maxDist);278 var y1 = Math.min(this.height, cy + maxDist);279 var maxDist2 = maxDist * maxDist;280 281 for ( var y = y0; y < y1; ++y) {282 for ( var x = x0; x < x1; ++x) {283 var dx = x - cx;284 var dy = y - cy;285 var r2 = dx*dx + dy*dy;286 if (r2 < maxDist2){287 this.map[x + y * this.width] = value;288 }289 }290 }291 };292 293 /**294 * Make each cell's 16-bit/8-bit value at least one greater than each of its295 * neighbours' values. (If the grid is initialised with 0s and 65535s or 255s, the296 * result of each cell is its Manhattan distance to the nearest 0.)297 */298 Map.prototype.expandInfluences = function() {299 var w = this.width;300 var h = this.height;301 var grid = this.map;302 for ( var y = 0; y < h; ++y) {303 var min = this.maxVal;304 for ( var x = 0; x < w; ++x) {305 var g = grid[x + y * w];306 if (g > min)307 grid[x + y * w] = min;308 else if (g < min)309 min = g;310 ++min;311 }312 313 for ( var x = w - 2; x >= 0; --x) {314 var g = grid[x + y * w];315 if (g > min)316 grid[x + y * w] = min;317 else if (g < min)318 min = g;319 ++min;320 }321 }322 323 for ( var x = 0; x < w; ++x) {324 var min = this.maxVal;325 for ( var y = 0; y < h; ++y) {326 var g = grid[x + y * w];327 if (g > min)328 grid[x + y * w] = min;329 else if (g < min)330 min = g;331 ++min;332 }333 334 for ( var y = h - 2; y >= 0; --y) {335 var g = grid[x + y * w];336 if (g > min)337 grid[x + y * w] = min;338 else if (g < min)339 min = g;340 ++min;341 }342 }343 };344 345 Map.prototype.findBestTile = function(radius, obstructionTiles){346 // Find the best non-obstructed tile347 var bestIdx = 0;348 var bestVal = -1;349 for ( var i = 0; i < this.length; ++i) {350 if (obstructionTiles.map[i] > radius) {351 var v = this.map[i];352 if (v > bestVal) {353 bestVal = v;354 bestIdx = i;355 }356 }357 }358 359 return [bestIdx, bestVal];360 };361 362 // add to current map by the parameter map pixelwise363 Map.prototype.add = function(map){364 for (var i = 0; i < this.length; ++i) {365 if (this.map[i] + map.map[i] < 0)366 this.map[i] = 0;367 else if (this.map[i] + map.map[i] > this.maxVal)368 this.map[i] = this.maxVal;369 else370 this.map[i] += map.map[i];371 }372 };373 374 Map.prototype.dumpIm = function(name, threshold){375 name = name ? name : "default.png";376 threshold = threshold ? threshold : this.maxVal;377 Engine.DumpImage(name, this.map, this.width, this.height, threshold);378 }; -
ps/trunk/binaries/data/mods/public/simulation/ai/aegis/queue-manager.js
r13683 r13907 16 16 // Particularly noticeable when phasing: the AI often overshoots by a good 200/300 resources before starting. 17 17 // 18 // The fact that there is an outqueue is mostly a relic of qBot.19 //20 18 // This system should be improved. It's probably not flexible enough. 21 19 … … 37 35 38 36 this.curItemQueue = []; 39 40 37 }; 41 38 … … 50 47 }; 51 48 52 QueueManager.prototype.futureNeeds = function(gameState, EcoManager) { 49 QueueManager.prototype.getTotalAccountedResources = function(gameState) { 50 var resources = new Resources(); 51 for (var key in this.queues) { 52 resources.add(this.accounts[key]); 53 } 54 return resources; 55 }; 56 57 QueueManager.prototype.currentNeeds = function(gameState) { 53 58 var needs = new Resources(); 54 // get ou ycurrent resources, not removing accounts.59 // get out current resources, not removing accounts. 55 60 var current = this.getAvailableResources(gameState, true); 56 61 //queueArrays because it's faster. … … 59 64 var name = this.queueArrays[i][0]; 60 65 var queue = this.queueArrays[i][1]; 61 for (var j = 0; j < Math.min(2,queue.length()); ++j) 62 { 63 needs.add(queue.queue[j].getCost()); 64 } 65 } 66 if (EcoManager === false) { 67 return { 68 "food" : Math.max(needs.food - current.food, 0), 69 "wood" : Math.max(needs.wood - current.wood, 0), 70 "stone" : Math.max(needs.stone - current.stone, 0), 71 "metal" : Math.max(needs.metal - current.metal, 0) 72 }; 73 } else { 74 // Return predicted values minus the current stockpiles along with a base rater for all resources 75 return { 76 "food" : (Math.max(needs.food - current.food, 0) + EcoManager.baseNeed["food"])/2, 77 "wood" : (Math.max(needs.wood - current.wood, 0) + EcoManager.baseNeed["wood"])/2, 78 "stone" : (Math.max(needs.stone - current.stone, 0) + EcoManager.baseNeed["stone"])/2, 79 "metal" : (Math.max(needs.metal - current.metal, 0) + EcoManager.baseNeed["metal"])/2 80 }; 81 } 82 }; 66 if (queue.length() > 0 && queue.getNext().isGo(gameState)) 67 needs.add(queue.getNext().getCost()); 68 else if (queue.length() > 0 && !queue.getNext().isGo(gameState)) 69 { 70 var cost = queue.getNext().getCost(); 71 cost.multiply(0.5); 72 needs.add(cost); 73 } 74 if (queue.length() > 1 && queue.queue[1].isGo(gameState)) 75 needs.add(queue.queue[1].getCost()); 76 } 77 return { 78 "food" : Math.max(25 + needs.food - current.food, 0), 79 "wood" : Math.max(needs.wood - current.wood, 0), 80 "stone" : Math.max(needs.stone - current.stone, 0), 81 "metal" : Math.max(needs.metal - current.metal, 0) 82 }; 83 }; 84 85 QueueManager.prototype.futureNeeds = function(gameState) { 86 var needs = new Resources(); 87 // get out current resources, not removing accounts. 88 var current = this.getAvailableResources(gameState, true); 89 //queueArrays because it's faster. 90 for (var i in this.queueArrays) 91 { 92 var name = this.queueArrays[i][0]; 93 var queue = this.queueArrays[i][1]; 94 for (var j = 0; j < queue.length(); ++j) 95 { 96 var costs = queue.queue[j].getCost(); 97 if (!queue.queue[j].isGo(gameState)) 98 costs.multiply(0.5); 99 needs.add(costs); 100 } 101 } 102 return { 103 "food" : Math.max(25 + needs.food - current.food, 10), 104 "wood" : Math.max(needs.wood - current.wood, 10), 105 "stone" : Math.max(needs.stone - current.stone, 0), 106 "metal" : Math.max(needs.metal - current.metal, 0) 107 }; 108 }; 109 110 // calculate the gather rates we'd want to be able to use all elements in our queues 111 QueueManager.prototype.wantedGatherRates = function(gameState) { 112 var rates = { "food" : 0, "wood" : 0, "stone" : 0, "metal" : 0 }; 113 var qTime = gameState.getTimeElapsed(); 114 var qCosts = { "food" : 0, "wood" : 0, "stone" : 0, "metal" : 0 }; 115 116 var currentRess = this.getAvailableResources(gameState); 117 118 //queueArrays because it's faster. 119 for (var i in this.queueArrays) 120 { 121 qCosts = { "food" : 0, "wood" : 0, "stone" : 0, "metal" : 0 }; 122 qTime = gameState.getTimeElapsed(); 123 var name = this.queueArrays[i][0]; 124 var queue = this.queueArrays[i][1]; 125 126 for (var j = 0; j < queue.length(); ++j) 127 { 128 var elem = queue.queue[j]; 129 var cost = elem.getCost(); 130 if (qTime < elem.startTime) 131 qTime = elem.startTime; 132 if (!elem.isGo(gameState)) 133 { 134 // assume 2 minutes. 135 // TODO work on this. 136 for (type in qCosts) 137 qCosts[type] += cost[type]; 138 qTime += 120000; 139 break; // disregard other stuffs. 140 } 141 if (!elem.endTime) 142 { 143 // estimate time based on priority + cost + nb 144 // TODO: work on this. 145 for (type in qCosts) 146 qCosts[type] += (cost[type] + Math.min(cost[type],this.priorities[name])); 147 qTime += 30000; 148 } else { 149 // TODO: work on this. 150 for (type in qCosts) 151 qCosts[type] += (cost[type] + Math.min(cost[type],this.priorities[name])); 152 // TODO: refine based on % completed. 153 qTime += (elem.endTime-elem.startTime); 154 } 155 } 156 for (j in qCosts) 157 { 158 qCosts[j] -= this.accounts[name][j]; 159 var diff = Math.min(qCosts[j], currentRess[j]); 160 qCosts[j] -= diff; 161 currentRess[j] -= diff; 162 rates[j] += qCosts[j]/(qTime/1000); 163 } 164 } 165 return rates; 166 }; 167 168 /*QueueManager.prototype.logNeeds = function(gameState) { 169 if (!this.totor) 170 { 171 this.totor = []; 172 this.currentGathR = []; 173 this.currentGathRWanted = []; 174 this.ressLev = []; 175 } 176 177 if (gameState.ai.playedTurn % 10 !== 0) 178 return; 179 180 181 var array = this.wantedGatherRates(gameState); 182 this.totor.push( array ); 183 184 185 var currentRates = {}; 186 for (var type in array) 187 currentRates[type] = 0; 188 for (i in gameState.ai.HQ.baseManagers) 189 { 190 var base = gameState.ai.HQ.baseManagers[i]; 191 for (var type in array) 192 { 193 base.gatherersByType(gameState,type).forEach (function (ent) { //}){ 194 var worker = ent.getMetadata(PlayerID, "worker-object"); 195 if (worker) 196 currentRates[type] += worker.getGatherRate(gameState); 197 }); 198 } 199 } 200 this.currentGathR.push( currentRates ); 201 202 var types = Object.keys(array); 203 204 types.sort(function(a, b) { 205 var va = (Math.max(0,array[a] - currentRates[a]))/ (currentRates[a]+1); 206 var vb = (Math.max(0,array[b] - currentRates[b]))/ (currentRates[b]+1); 207 if (va === vb) 208 return (array[b]/(currentRates[b]+1)) - (array[a]/(currentRates[a]+1)); 209 return vb-va; 210 }); 211 this.currentGathRWanted.push( types ); 212 213 var rss = gameState.getResources(); 214 this.ressLev.push( {"food" : rss["food"],"stone" : rss["stone"],"wood" : rss["wood"],"metal" : rss["metal"]} ); 215 216 if (gameState.getTimeElapsed() > 20*60*1000 && !this.once) 217 { 218 this.once = true; 219 for (j in array) 220 { 221 log (j + ";"); 222 for (var i = 0; i < this.totor.length; ++i) 223 { 224 log (this.totor[i][j] + ";"); 225 } 226 } 227 log(); 228 for (j in array) 229 { 230 log (j + ";"); 231 for (var i = 0; i < this.totor.length; ++i) 232 { 233 log (this.currentGathR[i][j] + ";"); 234 } 235 } 236 log(); 237 for (j in array) 238 { 239 log (j + ";"); 240 for (var i = 0; i < this.totor.length; ++i) 241 { 242 log (this.currentGathRWanted[i].indexOf(j) + ";"); 243 } 244 } 245 log(); 246 for (j in array) 247 { 248 log (j + ";"); 249 for (var i = 0; i < this.totor.length; ++i) 250 { 251 log (this.ressLev[i][j] + ";"); 252 } 253 } 254 } 255 }; 256 */ 83 257 84 258 QueueManager.prototype.printQueues = function(gameState){ 85 debug("OUTQUEUES"); 86 for (var i in this.queues){ 87 var qStr = ""; 88 var q = this.queues[i]; 89 if (q.outQueue.length > 0) 90 debug((i + ":")); 91 for (var j in q.outQueue){ 92 qStr = " " + q.outQueue[j].type + " "; 93 if (q.outQueue[j].number) 94 qStr += "x" + q.outQueue[j].number; 95 debug (qStr); 96 } 97 } 98 99 debug("INQUEUES"); 259 debug("QUEUES"); 100 260 for (var i in this.queues){ 101 261 var qStr = ""; … … 116 276 } 117 277 debug("Needed Resources:" + uneval(this.futureNeeds(gameState,false))); 278 debug ("Wanted Gather Rates:" + uneval(this.wantedGatherRates(gameState))); 118 279 debug ("Current Resources:" + uneval(gameState.getResources())); 119 280 debug ("Available Resources:" + uneval(this.getAvailableResources(gameState))); 281 }; 282 283 // nice readable HTML version. 284 QueueManager.prototype.HTMLprintQueues = function(gameState){ 285 if (!Config.debug) 286 return; 287 log("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\"> <html> <head> <title>Aegis Queue Manager</title> <link rel=\"stylesheet\" href=\"table.css\"> </head> <body> <table> <caption>Aegis Build Order</caption> "); 288 for (var i in this.queues){ 289 log ("<tr>"); 290 291 var q = this.queues[i]; 292 var str = "<th>" + i +"<br>"; 293 for each (k in this.accounts[i].types) 294 if(k != "population") 295 { 296 str += this.accounts[i][k] + k.substr(0,1).toUpperCase() ; 297 if (k != "metal") str += " / "; 298 } 299 log(str + "</th>"); 300 for (var j in q.queue) { 301 if (q.queue[j].isGo(gameState)) 302 log ("<td>"); 303 else 304 log ("<td class=\"NotGo\">"); 305 306 var qStr = ""; 307 qStr += q.queue[j].type; 308 if (q.queue[j].number) 309 qStr += "x" + q.queue[j].number; 310 log (qStr); 311 log ("</td>"); 312 } 313 log ("</tr>"); 314 } 315 log ("</table>"); 316 /*log ("<h3>Accounts</h3>"); 317 for (var p in this.accounts) 318 { 319 log("<p>" + p + ": " + uneval(this.accounts[p]) + " </p>"); 320 }*/ 321 log ("<p>Needed Resources:" + uneval(this.futureNeeds(gameState,false)) + "</p>"); 322 log ("<p>Wanted Gather Rate:" + uneval(this.wantedGatherRates(gameState)) + "</p>"); 323 log ("<p>Current Resources:" + uneval(gameState.getResources()) + "</p>"); 324 log ("<p>Available Resources:" + uneval(this.getAvailableResources(gameState)) + "</p>"); 325 log("</body></html>"); 120 326 }; 121 327 … … 137 343 138 344 Engine.ProfileStart("Queue Manager"); 139 140 //if (gameState.ai.playedTurn % 10 === 0) 141 // this.printQueues(gameState); 142 143 Engine.ProfileStart("Pick items from queues"); 144 145 // TODO: this only pushes the first object. SHould probably try to push any possible object to maximize productivity. Perhaps a settinh? 146 // looking at queues in decreasing priorities and pushing to the current item queues. 147 for (var i in this.queueArrays) 148 { 149 var name = this.queueArrays[i][0]; 150 var queue = this.queueArrays[i][1]; 151 if (queue.length() > 0) 152 { 153 var item = queue.getNext(); 154 var total = new Resources(); 155 total.add(this.accounts[name]); 156 total.subtract(queue.outQueueCost()); 157 if (total.canAfford(item.getCost())) 158 { 159 queue.nextToOutQueue(); 160 } 161 } else if (queue.totalLength() === 0) { 162 this.accounts[name].reset(); 163 } 164 } 165 345 346 // Let's assign resources to plans that need'em 166 347 var availableRes = this.getAvailableResources(gameState); 167 // assign some accounts to queues. This is done by priority, and by need.168 348 for (var ress in availableRes) 169 349 { … … 174 354 var maxNeed = {}; 175 355 // Okay so this is where it gets complicated. 176 // If a queue requires "ress" for the next elements (in the queue or the outqueue)356 // If a queue requires "ress" for the next elements (in the queue) 177 357 // And the account is not high enough for it. 178 358 // Then we add it to the total priority. … … 183 363 // uselessly while it awaits for other resources. 184 364 for (var j in this.queues) { 185 var outQueueCost = this.queues[j].outQueueCost();186 var queueCost = this.queues[j]. queueCost();187 if (this. accounts[j][ress] < queueCost[ress] + outQueueCost[ress])365 // returns exactly the correct amount, ie 0 if we're not go. 366 var queueCost = this.queues[j].maxAccountWanted(gameState); 367 if (this.queues[j].length() > 0 && this.accounts[j][ress] < queueCost[ress] && !this.queues[j].paused) 188 368 { 369 // check that we're not too forward in this resource compared to others. 370 /*var maxp = this.accounts[j][ress] / (queueCost[ress]+1); 371 var tooFull = false; 372 for (tempRess in availableRes) 373 if (tempRess !== ress && queueCost[tempRess] > 0 && (this.accounts[j][tempRess] / (queueCost[tempRess]+1)) - maxp < -0.2) 374 tooFull = true; 375 if (tooFull) 376 continue;*/ 377 189 378 // adding us to the list of queues that need an update. 190 379 tempPrio[j] = this.priorities[j]; 191 maxNeed[j] = outQueueCost[ress] + this.queues[j].getNext().getCost()[ress]; 192 // if we have enough of that resource for the outqueue and our first resource in the queue, diminish our priority. 193 if (this.accounts[j][ress] >= outQueueCost[ress] + this.queues[j].getNext().getCost()[ress]) 194 { 380 maxNeed[j] = queueCost[ress] - this.accounts[j][ress]; 381 // if we have enough of that resource for our first item in the queue, diminish our priority. 382 if (this.accounts[j][ress] >= this.queues[j].getNext().getCost()[ress]) 195 383 tempPrio[j] /= 2; 196 if (this.queues[j].length() !== 1) 197 { 198 var halfcost = this.queues[j].queue[1].getCost()[ress]*0.8; 199 maxNeed[j] += halfcost; 200 if (this.accounts[j][ress] >= outQueueCost[ress] + this.queues[j].getNext().getCost()[ress] + halfcost) 201 delete tempPrio[j]; 202 } 203 } 384 204 385 if (tempPrio[j]) 205 386 totalPriority += tempPrio[j]; 387 } 388 else if (this.accounts[j][ress] > queueCost[ress]) 389 { 390 this.accounts[j][ress] = queueCost[ress]; 206 391 } 207 392 } … … 210 395 for (var j in tempPrio) { 211 396 // we'll add at much what can be allowed to this queue. 212 var toAdd = Math.floor(tempPrio[j]/totalPriority * availableRes[ress]); 213 // let's check we're not adding too much. 214 var maxAdd = Math.min(maxNeed[j] - this.accounts[j][ress], toAdd); 397 var toAdd = tempPrio[j]/totalPriority * availableRes[ress]; 398 var maxAdd = Math.floor(Math.min(maxNeed[j], toAdd)); 215 399 this.accounts[j][ress] += maxAdd; 216 400 } 217 } 218 } 401 }/* else if (ress != "population" && gameState.ai.playedTurn % 5 === 0) { 402 // okay here we haev no resource available. We'll try to shift resources to complete plans if possible. 403 // So basically if 2 queues have resources, and one is higher priority, and it needs resources 404 // We'll shift from the lower priority to the higher if we can complete it. 405 var queues = []; 406 for (var j in this.queues) { 407 if (this.queues[j].length() && this.queues[j].getNext().isGo(gameState) && this.accounts[j][ress] > 0) 408 queues.push(j); 409 } 410 if (queues.length > 1) 411 { 412 // we'll work from the bottom to the top. ie lowest priority will try to give to highest priority. 413 queues.sort(function (a,b) { return (self.priorities[a] < self.priorities[b]); }); 414 var under = 0, over = queues.length - 1; 415 while (under !== over) 416 { 417 var cost = this.queues[queues[over]].getNext().getCost()[ress]; 418 var totalCost = this.queues[queues[over]].maxAccountWanted(gameState)[ress]; 419 if (this.accounts[queues[over]] >= cost) 420 { 421 --over; // check the next one. 422 continue; 423 } 424 // need some discrepancy in priorities 425 if (this.priorities[queues[under]] < this.priorities[queues[over]] - 20) 426 { 427 if (this.accounts[queues[under]] + this.accounts[queues[over]] >= cost) 428 { 429 var amnt = cost - this.accounts[queues[over]]; 430 this.accounts[queues[under]] -= amnt; 431 this.accounts[queues[over]] += amnt; 432 --over; 433 debug ("Shifting " + amnt + " from " + queues[under] + " to " +queues[over]); 434 continue; 435 } else { 436 ++under; 437 continue; 438 } 439 } else { 440 break; 441 } 442 } 443 // okaaaay. 444 } 445 }*/ 446 } 447 448 Engine.ProfileStart("Pick items from queues"); 449 450 //debug ("start"); 451 //debug (uneval(this.accounts)); 452 // Start the next item in the queue if we can afford it. 453 for (var i in this.queueArrays) 454 { 455 var name = this.queueArrays[i][0]; 456 var queue = this.queueArrays[i][1]; 457 if (queue.length() > 0 && !queue.paused) 458 { 459 var item = queue.getNext(); 460 var total = new Resources(); 461 total.add(this.accounts[name]); 462 if (total.canAfford(item.getCost())) 463 { 464 if (item.canStart(gameState)) 465 { 466 this.accounts[name].subtract(item.getCost()); 467 queue.startNext(gameState); 468 } 469 } 470 } else if (queue.length() === 0) { 471 this.accounts[name].reset(); 472 } 473 } 474 //debug (uneval(this.accounts)); 475 219 476 Engine.ProfileStop(); 220 221 Engine.ProfileStart("Execute items"); 222 223 var units_Techs_passed = 0; 224 // Handle output queues by executing items where possible 225 for (var p in this.queueArrays) { 226 var name = this.queueArrays[p][0]; 227 var queue = this.queueArrays[p][1]; 228 var next = queue.outQueueNext(); 229 if (!next) 230 continue; 231 if (next.category === "building") { 232 if (gameState.buildingsBuilt == 0) { 233 if (next.canExecute(gameState)) { 234 this.accounts[name].subtract(next.getCost()) 235 //debug ("Starting " + next.type + " substracted " + uneval(next.getCost())) 236 queue.executeNext(gameState); 237 gameState.buildingsBuilt += 1; 238 } 239 } 240 } else { 241 if (units_Techs_passed < 2 && queue.outQueueNext().canExecute(gameState)){ 242 //debug ("Starting " + next.type + " substracted " + uneval(next.getCost())) 243 this.accounts[name].subtract(next.getCost()) 244 queue.executeNext(gameState); 245 units_Techs_passed++; 246 } 247 } 248 if (units_Techs_passed >= 2) 249 continue; 250 } 477 478 if (gameState.ai.playedTurn % 30 === 0) 479 this.HTMLprintQueues(gameState); 480 251 481 Engine.ProfileStop(); 252 Engine.ProfileStop(); 253 }; 482 }; 483 484 QueueManager.prototype.pauseQueue = function(queue, scrapAccounts) { 485 if (this.queues[queue]) 486 { 487 this.queues[queue].paused = true; 488 if (scrapAccounts) 489 this.accounts[queue].reset(); 490 } 491 } 492 493 QueueManager.prototype.unpauseQueue = function(queue) { 494 if (this.queues[queue]) 495 this.queues[queue].paused = false; 496 } 497 498 QueueManager.prototype.pauseAll = function(scrapAccounts, but) { 499 for (var p in this.queues) 500 if (p != but) 501 { 502 if (scrapAccounts) 503 this.accounts[p].reset(); 504 this.queues[p].paused = true; 505 } 506 } 507 508 QueueManager.prototype.unpauseAll = function(but) { 509 for (var p in this.queues) 510 if (p != but) 511 this.queues[p].paused = false; 512 } 513 254 514 255 515 QueueManager.prototype.addQueue = function(queueName, priority) { -
ps/trunk/binaries/data/mods/public/simulation/ai/aegis/queue.js
r13683 r13907 5 5 var Queue = function() { 6 6 this.queue = []; 7 this. outQueue = [];7 this.paused = false; 8 8 }; 9 9 10 10 Queue.prototype.empty = function() { 11 11 this.queue = []; 12 this.outQueue = [];13 12 }; 14 15 13 16 14 Queue.prototype.addItem = function(plan) { … … 34 32 }; 35 33 36 Queue.prototype.outQueueNext = function(){ 37 if (this.outQueue.length > 0) { 38 return this.outQueue[0]; 34 Queue.prototype.startNext = function(gameState) { 35 if (this.queue.length > 0) { 36 this.queue.shift().start(gameState); 37 return true; 39 38 } else { 40 return null;39 return false; 41 40 } 42 41 }; 43 42 44 Queue.prototype.outQueueCost = function(){ 43 // returns the maximal account we'll accept for this queue. 44 // Currently 100% of the cost of the first element and 80% of that of the second 45 Queue.prototype.maxAccountWanted = function(gameState) { 45 46 var cost = new Resources(); 46 for (var key in this.outQueue){ 47 cost.add(this.outQueue[key].getCost()); 47 if (this.queue.length > 0 && this.queue[0].isGo(gameState)) 48 cost.add(this.queue[0].getCost()); 49 if (this.queue.length > 1 && this.queue[1].isGo(gameState)) 50 { 51 var costs = this.queue[1].getCost(); 52 costs.multiply(0.8); 53 cost.add(costs); 48 54 } 49 55 return cost; … … 56 62 } 57 63 return cost; 58 };59 60 Queue.prototype.nextToOutQueue = function(){61 if (this.queue.length > 0){62 this.outQueue.push(this.queue.shift());63 }64 };65 66 Queue.prototype.executeNext = function(gameState) {67 if (this.outQueue.length > 0) {68 this.outQueue.shift().execute(gameState);69 return true;70 } else {71 return false;72 }73 64 }; 74 65 … … 85 76 }; 86 77 87 Queue.prototype.countOutQueuedUnits = function(){ 88 var count = 0; 89 for (var i in this.outQueue){ 90 count += this.outQueue[i].number; 91 } 92 return count; 93 }; 94 95 Queue.prototype.countTotalQueuedUnits = function(){ 96 var count = 0; 97 for (var i in this.queue){ 98 count += this.queue[i].number; 99 } 100 for (var i in this.outQueue){ 101 count += this.outQueue[i].number; 102 } 103 return count; 104 }; 105 Queue.prototype.countTotalQueuedUnitsWithClass = function(classe){ 78 Queue.prototype.countQueuedUnitsWithClass = function(classe){ 106 79 var count = 0; 107 80 for (var i in this.queue){ … … 109 82 count += this.queue[i].number; 110 83 } 111 for (var i in this.outQueue){112 if (this.outQueue[i].template && this.outQueue[i].template.hasClass(classe))113 count += this.outQueue[i].number;114 }115 84 return count; 116 85 }; 117 Queue.prototype.count TotalQueuedUnitsWithMetadata = function(data,value){86 Queue.prototype.countQueuedUnitsWithMetadata = function(data,value){ 118 87 var count = 0; 119 88 for (var i in this.queue){ … … 121 90 count += this.queue[i].number; 122 91 } 123 for (var i in this.outQueue){124 if (this.outQueue[i].metadata[data] && this.outQueue[i].metadata[data] == value)125 count += this.outQueue[i].number;126 }127 92 return count; 128 };129 130 Queue.prototype.totalLength = function(){131 return this.queue.length + this.outQueue.length;132 };133 134 Queue.prototype.outQueueLength = function(){135 return this.outQueue.length;136 93 }; 137 94 … … 144 101 } 145 102 } 146 for (var i = 0; i < this.outQueue.length; i++){147 if (this.outQueue[i].type === t){148 count += this.outQueue[i].number;149 }150 }151 103 return count; 152 104 }; -
ps/trunk/binaries/data/mods/public/simulation/ai/aegis/worker.js
r13683 r13907 7 7 this.maxApproachTime = 45000; 8 8 this.unsatisfactoryResource = false; // if true we'll reguarly check if we can't have better now. 9 }; 10 11 Worker.prototype.update = function(gameState) { 12 9 this.baseID = 0; 10 }; 11 12 Worker.prototype.update = function(baseManager, gameState) { 13 this.baseID = baseManager.ID; 13 14 var subrole = this.ent.getMetadata(PlayerID, "subrole"); 14 15 … … 20 21 this.ent.setMetadata(PlayerID,"fleeing", undefined); 21 22 23 // Okay so we have a few tasks. 24 // If we're gathering, we'll check that we haven't run idle. 25 // ANd we'll also check that we're gathering a resource we want to gather. 26 27 // If we're fighting, let's not start gathering, heh? 28 // TODO: remove this when we're hunting? 29 if (this.ent.unitAIState().split(".")[1] === "COMBAT" || this.ent.getMetadata(PlayerID, "role") === "transport") 30 { 31 return; 32 } 33 22 34 if (subrole === "gatherer") { 23 if (this.ent.unitAIState().split(".")[1] !== "GATHER" && this.ent.unitAIState().split(".")[1] !== "COMBAT" && this.ent.unitAIState().split(".")[1] !== "RETURNRESOURCE"){ 24 // TODO: handle combat for hunting animals 25 if (!this.ent.resourceCarrying() || this.ent.resourceCarrying().length === 0 || 35 if (this.ent.isIdle()) { 36 // if we aren't storing resources or it's the same type as what we're about to gather, 37 // let's just pick a new resource. 38 if (!this.ent.resourceCarrying() || this.ent.resourceCarrying().length === 0 || 26 39 this.ent.resourceCarrying()[0].type === this.ent.getMetadata(PlayerID, "gather-type")){ 27 40 Engine.ProfileStart("Start Gathering"); 28 this.startGathering(gameState); 41 this.unsatisfactoryResource = false; 42 this.startGathering(baseManager,gameState); 29 43 Engine.ProfileStop(); 44 45 this.startApproachingResourceTime = gameState.getTimeElapsed(); 46 30 47 } else { 31 48 // Should deposit resources … … 35 52 // no dropsite, abandon cargo. 36 53 37 // if we have a new order54 // if we were ordered to gather something else, try that. 38 55 if (this.ent.resourceCarrying()[0].type !== this.ent.getMetadata(PlayerID, "gather-type")) 39 this.startGathering( gameState);56 this.startGathering(baseManager,gameState); 40 57 else { 58 // okay so we haven't found a proper dropsite for the resource we're supposed to gather 59 // so let's get idle and the base manager will reassign us, hopefully well. 41 60 this.ent.setMetadata(PlayerID, "gather-type",undefined); 42 61 this.ent.setMetadata(PlayerID, "subrole", "idle"); … … 46 65 Engine.ProfileStop(); 47 66 } 48 this.startApproachingResourceTime = gameState.getTimeElapsed(); 49 67 // debug: show the resource we're gathering from 50 68 //Engine.PostCommand({"type": "set-shading-color", "entities": [this.ent.id()], "rgb": [10,0,0]}); 51 69 } else if (this.ent.unitAIState().split(".")[1] === "GATHER") { 52 if (this.unsatisfactoryResource && (this.ent.id() + gameState.ai.playedTurn) % 20 === 0) 70 71 // check for transport. 72 if (gameState.ai.playedTurn % 5 === 0) 73 { 74 if (this.ent.unitAIOrderData().length && this.ent.unitAIState().split(".")[2] === "APPROACHING" && this.ent.unitAIOrderData()[0]["target"]) 75 { 76 var ress = gameState.getEntityById(this.ent.unitAIOrderData()[0]["target"]); 77 if (ress !== undefined) 78 { 79 var index = gameState.ai.accessibility.getAccessValue(ress.position()); 80 var mIndex = gameState.ai.accessibility.getAccessValue(this.ent.position()); 81 if (index !== mIndex && index !== 1) 82 { 83 //gameState.ai.HQ.navalManager.askForTransport(this.ent.id(), this.ent.position(), ress.position()); 84 } 85 } 86 } 87 } 88 89 /* 90 if (gameState.getTimeElapsed() - this.startApproachingResourceTime > this.maxApproachTime) 91 { 92 if (this.ent.unitAIOrderData().length && this.ent.unitAIState().split(".")[1] === "GATHER" 93 && this.ent.unitAIOrderData()[0]["target"]) 94 { 95 var ent = gameState.getEntityById(this.ent.unitAIOrderData()[0]["target"]); 96 debug ("here " + this.startApproachingResourceAmount + "," + ent.resourceSupplyAmount()); 97 if (ent && this.startApproachingResourceAmount == ent.resourceSupplyAmount() && this.startEnt == ent.id()) { 98 debug (ent.toString() + " is inaccessible"); 99 ent.setMetadata(PlayerID, "inaccessible", true); 100 this.ent.flee(ent); 101 this.ent.setMetadata(PlayerID, "subrole", "idle"); 102 } 103 } 104 }*/ 105 106 // we're gathering. Let's check that it's not a resource we'd rather not gather from. 107 if ((this.ent.id() + gameState.ai.playedTurn) % 6 === 0 && this.checkUnsatisfactoryResource(gameState)) 53 108 { 54 109 Engine.ProfileStart("Start Gathering"); 55 this.startGathering( gameState);110 this.startGathering(baseManager,gameState); 56 111 Engine.ProfileStop(); 57 112 } 113 // TODO: reimplement the "reaching time" check. 58 114 /*if (gameState.getTimeElapsed() - this.startApproachingResourceTime > this.maxApproachTime) { 59 115 if (this.gatheringFrom) { … … 68 124 } 69 125 } 70 }*/126 }*/ 71 127 } else if (this.ent.unitAIState().split(".")[1] === "COMBAT") { 72 128 /*if (gameState.getTimeElapsed() - this.startApproachingResourceTime > this.maxApproachTime) { … … 81 137 } 82 138 }*/ 83 } else {84 this.startApproachingResourceTime = gameState.getTimeElapsed();85 139 } 86 140 } else if(subrole === "builder") { 87 if (this.ent.unitAIState().split(".")[1] !== "REPAIR"){ 88 var target = this.ent.getMetadata(PlayerID, "target-foundation"); 89 if (target.foundationProgress() === undefined && target.needsRepair() == false) 90 this.ent.setMetadata(PlayerID, "subrole", "idle"); 141 142 // check for transport. 143 if (gameState.ai.playedTurn % 5 === 0) 144 { 145 if (this.ent.unitAIOrderData().length && this.ent.unitAIState().split(".")[2] === "APPROACHING" && this.ent.unitAIOrderData()[0]["target"]) 146 { 147 var ress = gameState.getEntityById(this.ent.unitAIOrderData()[0]["target"]); 148 if (ress !== undefined) 149 { 150 var index = gameState.ai.accessibility.getAccessValue(ress.position()); 151 var mIndex = gameState.ai.accessibility.getAccessValue(this.ent.position()); 152 if (index !== mIndex && index !== 1) 153 { 154 //gameState.ai.HQ.navalManager.askForTransport(this.ent.id(), this.ent.position(), ress.position()); 155 } 156 } 157 } 158 } 159 160 161 if (this.ent.unitAIState().split(".")[1] !== "REPAIR") { 162 var target = gameState.getEntityById(this.ent.getMetadata(PlayerID, "target-foundation")); 163 // okay so apparently we aren't working. 164 // Unless we've been explicitely told to keep our role, make us idle. 165 if (!target || target.foundationProgress() === undefined && target.needsRepair() == false) 166 { 167 if (!this.ent.getMetadata(PlayerID, "keepSubrole")) 168 this.ent.setMetadata(PlayerID, "subrole", "idle"); 169 } 91 170 else 92 171 this.ent.repair(target); … … 94 173 this.startApproachingResourceTime = gameState.getTimeElapsed(); 95 174 //Engine.PostCommand({"type": "set-shading-color", "entities": [this.ent.id()], "rgb": [0,10,0]}); 175 // TODO: we should maybe decide on our own to build other buildings, not rely on the assigntofoundation stuff. 96 176 } else if(subrole === "hunter") { 97 if ( !this.ent.resourceCarrying() || this.ent.resourceCarrying().length === 0){177 if (this.ent.isIdle()){ 98 178 Engine.ProfileStart("Start Hunting"); 99 this.startHunting(gameState );179 this.startHunting(gameState, baseManager); 100 180 Engine.ProfileStop(); 101 181 } 102 } else { 103 this.startApproachingResourceTime = gameState.getTimeElapsed(); 104 } 105 }; 106 107 Worker.prototype.startGathering = function(gameState){ 182 } 183 }; 184 185 // check if our current resource is unsatisfactory 186 // this can happen in two ways: 187 // -either we were on an unsatisfactory resource last time we started gathering (this.unsatisfactoryResource) 188 // -Or we auto-moved to a bad resource thanks to the great UnitAI. 189 Worker.prototype.checkUnsatisfactoryResource = function(gameState) { 190 if (this.unsatisfactoryResource) 191 return true; 192 if (this.ent.unitAIOrderData().length && this.ent.unitAIState().split(".")[1] === "GATHER" && this.ent.unitAIState().split(".")[2] === "GATHERING" && this.ent.unitAIOrderData()[0]["target"]) 193 { 194 var ress = gameState.getEntityById(this.ent.unitAIOrderData()[0]["target"]); 195 if (!ress || !ress.getMetadata(PlayerID,"linked-dropsite") || !ress.getMetadata(PlayerID,"linked-dropsite-nearby") || gameState.ai.accessibility.getAccessValue(ress.position()) === -1) 196 return true; 197 } 198 return false; 199 }; 200 201 Worker.prototype.startGathering = function(baseManager, gameState) { 108 202 var resource = this.ent.getMetadata(PlayerID, "gather-type"); 109 203 var ent = this.ent; 204 var self = this; 110 205 111 206 if (!ent.position()){ … … 114 209 } 115 210 116 this.unsatisfactoryResource = false;117 118 211 // TODO: this is not necessarily optimal. 119 212 … … 126 219 // TODO: this is a huge part of multi-base support. Count only those in the same base as the worker. 127 220 var number = 0; 128 var ourDropsites = gameState.getOwnDropsites(resource);129 221 222 var ourDropsites = EntityCollectionFromIds(gameState,Object.keys(baseManager.dropsites)); 130 223 if (ourDropsites.length === 0) 131 224 { … … 134 227 } 135 228 229 var maxPerDP = 20; 230 if (resource === "food") 231 maxPerDP = 200; 232 136 233 ourDropsites.forEach(function (dropsite) { 137 if ( dropsite.getMetadata(PlayerID, "linked-resources-" +resource) !== undefined138 && dropsite.getMetadata(PlayerID, "resource-quantity-" +resource) !== undefined && dropsite.getMetadata(PlayerID, "resource-quantity-" +resource) > 200) {234 if (baseManager.dropsites[dropsite.id()][resource] && baseManager.dropsites[dropsite.id()][resource][4] > 1000 235 && baseManager.dropsites[dropsite.id()][resource][5].length < maxPerDP) 139 236 number++; 140 }141 237 }); 142 143 //debug ("Available " +resource + " dropsites: " +ourDropsites.length);144 238 145 239 // Allright second step, if there are any such dropsites, we pick the closest. … … 148 242 { 149 243 ourDropsites.forEach(function (dropsite) { //}){ 150 if (dropsite.getMetadata(PlayerID, "resource-quantity-" +resource) == undefined) 151 return; 152 if (dropsite.position() && (dropsite.getMetadata(PlayerID, "resource-quantity-" +resource) > 700 || (number === 1 && dropsite.getMetadata(PlayerID, "resource-quantity-" +resource) > 200) ) ) { 244 if (baseManager.dropsites[dropsite.id()][resource] === undefined) 245 return; 246 if (dropsite.position() && (baseManager.dropsites[dropsite.id()][resource][4] > 1000 || (number === 1 && baseManager.dropsites[dropsite.id()][resource][4] > 200) ) 247 && baseManager.dropsites[dropsite.id()][resource][5].length < maxPerDP) { 153 248 var dist = SquareVectorDistance(ent.position(), dropsite.position()); 154 249 if (dist < minDropsiteDist){ 155 250 minDropsiteDist = dist; 156 nearestResources = dropsite.getMetadata(PlayerID, "linked-resources-" + resource);251 nearestResources = baseManager.dropsites[dropsite.id()][resource][1]; 157 252 nearestDropsite = dropsite; 158 253 } … … 160 255 }); 161 256 } 162 //debug ("Nearest dropsite: " +nearestDropsite); 163 164 // Now if we have no dropsites, we repeat the process with resources "far" from dropsites but still linked with them. 165 // I add the "close" value for code sanity. 166 // Again, we choose a dropsite with a lot of resources left, or we pick the only one available (in this case whatever happens). 257 // we've found no fitting dropsites close enough from us. 258 // So'll try with far away. 167 259 if (!nearestResources || nearestResources.length === 0) { 168 //debug ("here(1)"); 169 gameState.getOwnDropsites(resource).forEach(function (dropsite){ //}){ 170 var quantity = dropsite.getMetadata(PlayerID, "resource-quantity-" +resource)+dropsite.getMetadata(PlayerID, "resource-quantity-far-" +resource); 171 if (dropsite.position() && (quantity) > 700 || number === 1) { 260 ourDropsites.forEach(function (dropsite) { //}){ 261 if (baseManager.dropsites[dropsite.id()][resource] === undefined) 262 return; 263 if (dropsite.position() && baseManager.dropsites[dropsite.id()][resource][4] > 400 264 && baseManager.dropsites[dropsite.id()][resource][5].length < maxPerDP) { 172 265 var dist = SquareVectorDistance(ent.position(), dropsite.position()); 173 266 if (dist < minDropsiteDist){ 174 267 minDropsiteDist = dist; 175 nearestResources = dropsite.getMetadata(PlayerID, "linked-resources-" + resource);268 nearestResources = baseManager.dropsites[dropsite.id()][resource][1]; 176 269 nearestDropsite = dropsite; 177 270 } 178 271 } 179 272 }); 180 this.unsatisfactoryResource = true; 181 //debug ("Nearest dropsite: " +nearestDropsite); 182 } 183 // If we still haven't found any fitting dropsite... 184 // Then we'll just pick any resource, and we'll check for the closest dropsite to that one 273 } 274 185 275 if (!nearestResources || nearestResources.length === 0){ 276 if (resource === "food") 277 if (this.buildAnyField(gameState)) 278 return; 279 280 if (this.unsatisfactoryResource == true) 281 return; // we were already not satisfied, we're still not, change not. 282 283 if (gameState.ai.HQ.switchWorkerBase(gameState, this.ent, resource)) 284 return; 285 186 286 //debug ("No fitting dropsite for " + resource + " found, iterating the map."); 187 287 nearestResources = gameState.getResourceSupplies(resource); 188 288 this.unsatisfactoryResource = true; 289 // TODO: should try setting up dropsites. 290 } else { 291 this.unsatisfactoryResource = false; 189 292 } 190 293 … … 194 297 if (this.buildAnyField(gameState)) 195 298 return; 299 if (gameState.ai.HQ.switchWorkerBase(gameState, this.ent, resource)) 300 return; 196 301 debug("No " + resource + " found! (1)"); 197 302 } 198 303 else 304 { 305 if (gameState.ai.HQ.switchWorkerBase(gameState, this.ent, resource)) 306 return; 199 307 debug("No " + resource + " found! (1)"); 308 } 200 309 return; 201 310 } … … 214 323 // TODo: add a bonus for resources with a lot of resources left, perhaps, to spread gathering? 215 324 nearestResources.forEach(function(supply) { //}){ 216 217 325 // sanity check, perhaps sheep could be garrisoned? 218 326 if (!supply.position()) { … … 225 333 return; 226 334 } 227 228 if (supply.isFull() === true || (supply.maxGatherers() - supply.resourceSupplyGatherers().length == 0) ||229 (gameState.turnCache["ressGathererNB"] && gameState.turnCache["ressGathererNB"][supply.id()]230 && gameState.turnCache["ressGathererNB"][supply.id()] + supply.resourceSupplyGatherers().length >= supply.maxGatherers ())) {231 return; 232 } 335 336 if (supply.isFull() === true 337 || (gameState.turnCache["ressGathererNB"] && gameState.turnCache["ressGathererNB"][supply.id()] 338 && gameState.turnCache["ressGathererNB"][supply.id()] + supply.resourceSupplyGatherers().length >= supply.maxGatherers)) 339 return; 340 233 341 234 // Don't gather enemy farms 235 if ( !supply.isOwn(PlayerID) && supply.owner() !== 0) {342 // Don't gather enemy farms or farms from another base 343 if ((!supply.isOwn(PlayerID) && supply.owner() !== 0) || (supply.isOwn(PlayerID) && supply.getMetadata(PlayerID,"base") !== self.baseID)) { 236 344 //debug ("enemy"); 237 345 return; … … 239 347 240 348 // quickscope accessbility check. 241 if (!gameState.ai.accessibility.pathAvailable(gameState, ent.position(), supply.position() , true)) {349 if (!gameState.ai.accessibility.pathAvailable(gameState, ent.position(), supply.position())) { 242 350 //debug ("nopath"); 243 351 return; … … 246 354 if (supply.footprintRadius() < 1) 247 355 { 248 var fakeMap = new Map(gameState ,gameState.getMap().data);356 var fakeMap = new Map(gameState.sharedScript,gameState.getMap().data); 249 357 var id = fakeMap.gamePosToMapPos(supply.position())[0] + fakeMap.width*fakeMap.gamePosToMapPos(supply.position())[1]; 250 358 if ( (gameState.sharedScript.passabilityClasses["pathfinderObstruction"] & gameState.getMap().data[id]) ) … … 269 377 var territoryOwner = Map.createTerritoryMap(gameState).getOwner(supply.position()); 270 378 if (territoryOwner != PlayerID && territoryOwner != 0) { 271 dist *= 3.0;379 dist *= 5.0; 272 380 //return; 273 } 274 275 // Go for treasure as a priority 276 if (dist < 40000 && supply.resourceSupplyType().generic == "treasure"){ 381 } else if (dist < 40000 && supply.resourceSupplyType().generic == "treasure"){ 382 // go for treasures if they're not in enemy territory 277 383 dist /= 1000; 278 384 } … … 283 389 } 284 390 }); 285 286 391 if (nearestSupply) { 287 392 var pos = nearestSupply.position(); … … 311 416 tried = this.buildAnyField(gameState); 312 417 if (!tried && SquareVectorDistance(pos,this.ent.position()) > 62500) { 418 // TODO: ought to change behavior here. 313 419 return; // wait. a farm should appear. 314 420 } 315 421 } 316 422 if (!tried) { 317 318 423 if (!gameState.turnCache["ressGathererNB"]) 319 424 { … … 325 430 gameState.turnCache["ressGathererNB"][nearestSupply.id()]++; 326 431 327 this.maxApproachTime = Math.max(25000, VectorDistance(pos,this.ent.position()) * 1000); 432 this.maxApproachTime = Math.max(30000, VectorDistance(pos,this.ent.position()) * 5000); 433 this.startApproachingResourceAmount = ent.resourceSupplyAmount(); 434 this.startEnt = ent.id(); 328 435 ent.gather(nearestSupply); 329 436 ent.setMetadata(PlayerID, "target-foundation", undefined); 330 331 // check if the resource we've started gathering from is now full, in which case inform the dropsite.332 if (gameState.turnCache["ressGathererNB"][nearestSupply.id()] + nearestSupply.resourceSupplyGatherers().length >= nearestSupply.maxGatherers()333 && nearestSupply.getMetadata(PlayerID, "linked-dropsite") != undefined)334 {335 var dropsite = gameState.getEntityById(nearestSupply.getMetadata(PlayerID, "linked-dropsite"));336 if (dropsite == undefined || dropsite.getMetadata(PlayerID, "linked-resources-" + resource) === undefined)337 return;338 if (nearestSupply.getMetadata(PlayerID, "linked-dropsite-nearby") == true) {339 dropsite.setMetadata(PlayerID, "resource-quantity-" + resource, +dropsite.getMetadata(PlayerID, "resource-quantity-" + resource) - (+nearestSupply.getMetadata(PlayerID, "dp-update-value")));340 dropsite.getMetadata(PlayerID, "linked-resources-" + resource).updateEnt(nearestSupply);341 dropsite.getMetadata(PlayerID, "nearby-resources-" + resource).updateEnt(nearestSupply);342 } else {343 dropsite.setMetadata(PlayerID, "resource-quantity-far-" + resource, +dropsite.getMetadata(PlayerID, "resource-quantity-" + resource) - (+nearestSupply.getMetadata(PlayerID, "dp-update-value")));344 dropsite.getMetadata(PlayerID, "linked-resources-" + resource).updateEnt(nearestSupply);345 }346 347 }348 349 437 } 350 438 } else { … … 352 440 return; 353 441 354 debug("No " + resource + " found! (2)"); 442 if (gameState.ai.HQ.switchWorkerBase(gameState, this.ent, resource)) 443 return; 444 445 if (resource !== "food") 446 debug("No " + resource + " found! (2)"); 355 447 // If we had a fitting closest dropsite with a lot of resources, mark it as not good. It means it's probably full. Then retry. 356 448 // it'll be resetted next time it's counted anyway. … … 359 451 nearestDropsite.setMetadata(PlayerID, "resource-quantity-" +resource, 0); 360 452 nearestDropsite.setMetadata(PlayerID, "resource-quantity-far-" +resource, 0); 361 this.startGathering( gameState);453 this.startGathering(baseManager, gameState); 362 454 } 363 455 } … … 398 490 }; 399 491 400 Worker.prototype.startHunting = function(gameState ){492 Worker.prototype.startHunting = function(gameState, baseManager){ 401 493 var ent = this.ent; 402 494 403 if (!ent.position() || ent.getMetadata(PlayerID, "stoppedHunting"))495 if (!ent.position() || !baseManager.isHunting) 404 496 return; 405 497 … … 439 531 440 532 // quickscope accessbility check 441 if (!gameState.ai.accessibility.pathAvailable(gameState, ent.position(), supply.position(), true))533 if (!gameState.ai.accessibility.pathAvailable(gameState, ent.position(), supply.position(),false, true)) 442 534 return; 443 535 … … 465 557 if (!nearestDropsite) 466 558 { 467 ent.setMetadata(PlayerID, "stoppedHunting", true);559 baseManager.isHunting = false; 468 560 ent.setMetadata(PlayerID, "role", undefined); 469 561 debug ("No dropsite for hunting food"); 470 562 return; 471 563 } 472 if (minDropsiteDist > 45000) {473 ent.setMetadata(PlayerID, "stoppedHunting", true);564 if (minDropsiteDist > 35000) { 565 baseManager.isHunting = false; 474 566 ent.setMetadata(PlayerID, "role", undefined); 475 567 } else { … … 478 570 } 479 571 } else { 480 ent.setMetadata(PlayerID, "stoppedHunting", true);572 baseManager.isHunting = false; 481 573 ent.setMetadata(PlayerID, "role", undefined); 482 574 debug("No food found for hunting! (2)"); … … 496 588 }; 497 589 590 Worker.prototype.getGatherRate = function(gameState) { 591 if (this.ent.getMetadata(PlayerID,"subrole") !== "gatherer") 592 return 0; 593 var rates = this.ent.resourceGatherRates(); 594 595 if (this.ent.unitAIOrderData().length && this.ent.unitAIState().split(".")[1] === "GATHER" && this.ent.unitAIOrderData()[0]["target"]) 596 { 597 var ress = gameState.getEntityById(this.ent.unitAIOrderData()[0]["target"]); 598 if (!ress) 599 return 0; 600 var type = ress.resourceSupplyType(); 601 if (type.generic == "treasure") 602 return 1000; 603 var tstring = type.generic + "." + type.specific; 604 //debug (+rates[tstring] + " for " + tstring + " for " + this.ent._templateName); 605 if (rates[tstring]) 606 return rates[tstring]; 607 return 0; 608 } 609 return 0; 610 }; 611 498 612 Worker.prototype.buildAnyField = function(gameState){ 499 613 var self = this; 500 614 var okay = false; 501 var foundations = gameState.getOwnFoundations() ;615 var foundations = gameState.getOwnFoundations().filter(Filters.byMetadata(PlayerID,"base",this.baseID)); 502 616 foundations.filterNearest(this.ent.position(), foundations.length); 503 617 foundations.forEach(function (found) { … … 508 622 } 509 623 }); 624 if (!okay) 625 { 626 var foundations = gameState.getOwnFoundations(); 627 foundations.filterNearest(this.ent.position(), foundations.length); 628 foundations.forEach(function (found) { 629 if (found._template.BuildRestrictions.Category === "Field" && !okay) { 630 self.ent.repair(found); 631 self.ent.setMetadata(PlayerID,"base", found.getMetadata(PlayerID,"base")); 632 okay = true; 633 return; 634 } 635 }); 636 } 510 637 return okay; 511 638 }; -
ps/trunk/binaries/data/mods/public/simulation/ai/common-api-v3/base.js
r13326 r13907 13 13 14 14 this.turn = 0; 15 this.timeElapsed = 0; 15 16 } 16 17 … … 36 37 this.passabilityClasses = sharedAI.passabilityClasses; 37 38 this.passabilityMap = sharedAI.passabilityMap; 39 this.territoryMap = sharedAI.territoryMap; 40 this.timeElapsed = state.timeElapsed; 38 41 39 var gameState = sharedAI.gameState[PlayerID]; 40 gameState.ai = this; 42 this.gameState = sharedAI.gameState[PlayerID]; 43 this.gameState.ai = this; 44 this.sharedScript = sharedAI; 41 45 42 this.InitShared(gameState, sharedAI); 43 44 delete gameState.ai; 46 this.InitShared(this.gameState, this.sharedScript); 45 47 } 46 48 … … 60 62 this.accessibility = sharedAI.accessibility; 61 63 this.terrainAnalyzer = sharedAI.terrainAnalyzer; 62 this.techModifications = sharedAI. techModifications[this._player];64 this.techModifications = sharedAI._techModifications[this._player]; 63 65 64 66 Engine.ProfileStop(); -
ps/trunk/binaries/data/mods/public/simulation/ai/common-api-v3/entity.js
r13683 r13907 33 33 return this._template.Identity.RequiredTechnology; 34 34 }, 35 35 36 available: function(gameState) { 37 if (!this._template.Identity || !this._template.Identity.RequiredTechnology) 38 return true; 39 return gameState.isResearched(this._template.Identity.RequiredTechnology); 40 }, 41 42 // specifically 36 43 phase: function() { 37 44 if (!this._template.Identity || !this._template.Identity.RequiredTechnology) … … 413 420 return (territories && territories.indexOf(territory) != -1); 414 421 }, 415 422 423 hasTerritoryInfluence: function() { 424 return (this._template.TerritoryInfluence !== undefined); 425 }, 426 427 territoryInfluenceRadius: function() { 428 if (this._template.TerritoryInfluence !== undefined) 429 return (this._template.TerritoryInfluence.Radius); 430 else 431 return -1; 432 }, 433 434 territoryInfluenceWeight: function() { 435 if (this._template.TerritoryInfluence !== undefined) 436 return (this._template.TerritoryInfluence.Weight); 437 else 438 return -1; 439 }, 440 416 441 visionRange: function() { 417 442 if (!this._template.Vision) … … 428 453 _init: function(sharedAI, entity) 429 454 { 430 this._super.call(this, sharedAI.GetTemplate(entity.template), sharedAI. techModifications[entity.owner]);455 this._super.call(this, sharedAI.GetTemplate(entity.template), sharedAI._techModifications[entity.owner]); 431 456 432 457 this._ai = sharedAI; … … 463 488 }, 464 489 465 delete Metadata: function(player) {490 deleteAllMetadata: function(player) { 466 491 delete this._ai._entityMetadata[player][this.id()]; 492 }, 493 494 deleteMetadata: function(player, key) { 495 this._ai.deleteMetadata(player, this, key); 467 496 }, 468 497 … … 501 530 502 531 foundationProgress: function() { 503 if (t ypeof this._entity.foundationProgress === "undefined")532 if (this._entity.foundationProgress == undefined) 504 533 return undefined; 505 534 return this._entity.foundationProgress; … … 538 567 if (this._entity.resourceSupplyGatherers !== undefined) 539 568 return (this.maxGatherers() === this._entity.resourceSupplyGatherers.length); 569 540 570 return undefined; 541 571 }, … … 548 578 549 579 garrisoned: function() { return new EntityCollection(this._ai, this._entity.garrisoned); }, 580 581 canGarrisonInside: function() { return this._entity.garrisoned.length < this.garrisonMax(); }, 550 582 551 583 // TODO: visibility … … 659 691 }, 660 692 661 construct: function(template, x, z, angle ) {693 construct: function(template, x, z, angle, metadata) { 662 694 // TODO: verify this unit can construct this, just for internal 663 695 // sanity-checking and error reporting … … 672 704 "autorepair": false, 673 705 "autocontinue": false, 674 "queued": false 706 "queued": false, 707 "metadata" : metadata // can be undefined 675 708 }); 676 709 return this; -
ps/trunk/binaries/data/mods/public/simulation/ai/common-api-v3/entitycollection.js
r13225 r13907 4 4 this._entities = entities || {}; 5 5 this._filters = filters || []; 6 7 this._quickIter = false; // will make the entity collection store an array (not associative) of entities used when calling "foreach". 8 // probably should not be usde for very dynamic entity collections. 6 9 7 10 // Compute length lazily on demand, since it can be … … 24 27 // If an entitycollection is frozen, it will never automatically add a unit. 25 28 // But can remove one. 29 // this makes it easy to create entity collection that will auto-remove dead units 30 // but never add new ones. 26 31 EntityCollection.prototype.freeze = function() 27 32 { … … 31 36 { 32 37 this.frozen = false; 38 }; 39 40 EntityCollection.prototype.allowQuickIter = function() 41 { 42 this._quickIter = true; 43 this._entitiesArray = []; 44 for each (var ent in this._entities) 45 this._entitiesArray.push(ent); 33 46 }; 34 47 … … 43 56 EntityCollection.prototype.toEntityArray = function() 44 57 { 58 if (this._quickIter === true) 59 return this._entitiesArray; 45 60 var ret = []; 46 61 for each (var ent in this._entities) … … 60 75 { 61 76 // Compute the distance of each entity 62 var data = []; // [ [id, ent, distance], ... ] 63 for (var id in this._entities) 64 { 65 var ent = this._entities[id]; 66 if (ent.position()) 67 data.push([id, ent, VectorDistance(targetPos, ent.position())]); 77 var data = []; // [id, ent, distance] 78 79 if (this._quickIter === true) 80 { 81 for (var i in this._entitiesArray) 82 { 83 var ent = this._entitiesArray[i]; 84 if (ent.position() !== -1) 85 data.push([ent.id(), ent, SquareVectorDistance(targetPos, ent.position())]); 86 } 87 } else { 88 for (var id in this._entities) 89 { 90 var ent = this._entities[id]; 91 if (ent.position() !== -1) 92 data.push([id, ent, SquareVectorDistance(targetPos, ent.position())]); 93 } 68 94 } 69 95 … … 73 99 // Extract the first n 74 100 var ret = {}; 75 for each (var val in data.slice(0, n)) 76 ret[val[0]] = val[1]; 101 var length = Math.min(n, entData.length); 102 for (var i = 0; i < length; ++i) 103 ret[data[i][0]] = data[i][1]; 77 104 78 105 return new EntityCollection(this._ai, ret); … … 85 112 86 113 var ret = {}; 87 for (var id in this._entities) 88 { 89 var ent = this._entities[id]; 90 if (filter.func.call(thisp, ent, id, this)) 91 ret[id] = ent; 114 if (this._quickIter === true) 115 { 116 for (var i in this._entitiesArray) 117 { 118 var ent = this._entitiesArray[i]; 119 var id = ent.id(); 120 if (filter.func.call(thisp, ent, id, this)) 121 ret[id] = ent; 122 } 123 } else { 124 for (var id in this._entities) 125 { 126 var ent = this._entities[id]; 127 if (filter.func.call(thisp, ent, id, this)) 128 ret[id] = ent; 129 } 92 130 } 93 131 … … 106 144 } 107 145 return new EntityCollection(this._ai, ret); 146 }; 147 148 EntityCollection.prototype.forEach = function(callback) 149 { 150 if (this._quickIter === true) 151 { 152 for (var id in this._entitiesArray) 153 { 154 callback(this._entitiesArray[id]); 155 } 156 return this; 157 } 158 for (var id in this._entities) 159 { 160 callback(this._entities[id]); 161 } 162 return this; 108 163 }; 109 164 … … 131 186 }; 132 187 133 EntityCollection.prototype.forEach = function(callback, thisp)134 {135 for (var id in this._entities)136 {137 var ent = this._entities[id];138 callback.call(thisp, ent, id, this);139 }140 return this;141 };142 143 188 EntityCollection.prototype.move = function(x, z, queued) 144 189 { … … 242 287 if (this.length !== undefined) 243 288 this._length--; 289 if (this._quickIter === true) 290 this._entitiesArray.splice(this._entitiesArray.indexOf(ent),1); 244 291 delete this._entities[ent.id()]; 245 292 return true; … … 264 311 this._length++; 265 312 this._entities[ent.id()] = ent; 313 if (this._quickIter === true) 314 this._entitiesArray.push(ent); 266 315 return true; 267 316 } … … 298 347 }; 299 348 349 EntityCollection.prototype.unregister = function() 350 { 351 this._ai.removeUpdatingEntityCollection(this); 352 }; 353 300 354 EntityCollection.prototype.dynamicProperties = function() 301 355 { -
ps/trunk/binaries/data/mods/public/simulation/ai/common-api-v3/filters.js
r13266 r13907 42 42 "dynamicProperties": ['metadata.' + key]}; 43 43 }, 44 45 // can be used for stuffs which won't change once entities are created. 46 byStaticMetadata: function(player, key, value){ 47 return {"func" : function(ent){ 48 return (ent.getMetadata(player, key) == value); 49 }, 50 "dynamicProperties": []}; 51 }, 52 44 53 byHasMetadata: function(player, key){ 45 54 return {"func" : function(ent){ … … 98 107 byCanGarrison: function(){ 99 108 return {"func" : function(ent){ 100 return ent.garrisonMax() ;109 return ent.garrisonMax() > 0; 101 110 }, 102 111 "dynamicProperties": []}; … … 127 136 "dynamicProperties": []}; 128 137 }, 138 139 isGarrisoned: function(){ 140 return {"func" : function(ent){ 141 return ent.position() == -1; // assumes garrisoned 142 }, 143 "dynamicProperties": []}; 144 }, 129 145 130 146 isSoldier: function(){ … … 185 201 isDropsite: function(resourceType){ 186 202 return {"func": function(ent){ 187 return (ent.resourceDropsiteTypes() && ent.resourceDropsiteTypes().indexOf(resourceType) !== -1);203 return (ent.resourceDropsiteTypes() && (resourceType === undefined || ent.resourceDropsiteTypes().indexOf(resourceType) !== -1)); 188 204 }, 189 205 "dynamicProperties": []}; … … 204 220 205 221 // And don't go for the bloody fish! TODO: better accessibility checks 206 if (ent.hasClass("SeaCreature")) {222 if (ent.hasClass("SeaCreature")) { 207 223 return false; 208 224 } -
ps/trunk/binaries/data/mods/public/simulation/ai/common-api-v3/gamestate.js
r13683 r13907 3 3 * higher level than the raw data. 4 4 */ 5 var GameState = function(SharedScript, state, player) { 5 var GameState = function() { 6 this.ai = null; // must be updated by the AIs. 7 this.cellSize = 4; // Size of each map tile 8 9 this.buildingsBuilt = 0; 10 this.turnCache = {}; 11 }; 12 13 GameState.prototype.init = function(SharedScript, state, player) { 6 14 this.sharedScript = SharedScript; 7 15 this.EntCollecNames = SharedScript._entityCollectionsName; 8 16 this.EntCollec = SharedScript._entityCollections; 9 this.timeElapsed = state.timeElapsed;17 this.timeElapsed = SharedScript.timeElapsed; 10 18 this.templates = SharedScript._templates; 11 19 this.techTemplates = SharedScript._techTemplates; 12 20 this.entities = SharedScript.entities; 13 21 this.player = player; 14 this.playerData = state.players[player]; 15 this.techModifications = SharedScript.techModifications[player]; 16 this.buildingsBuilt = 0; 17 18 this.ai = null; // must be updated by the AIs. 19 20 this.cellSize = 4; // Size of each map tile 21 22 this.turnCache = {}; 23 }; 22 this.playerData = this.sharedScript.playersData[this.player]; 23 this.techModifications = SharedScript._techModifications[this.player]; 24 }; 25 24 26 GameState.prototype.update = function(SharedScript, state) { 25 27 this.sharedScript = SharedScript; 26 28 this.EntCollecNames = SharedScript._entityCollectionsName; 27 29 this.EntCollec = SharedScript._entityCollections; 28 this.timeElapsed = state.timeElapsed;30 this.timeElapsed = SharedScript.timeElapsed; 29 31 this.templates = SharedScript._templates; 30 32 this.techTemplates = SharedScript._techTemplates; 33 this._entities = SharedScript._entities; 31 34 this.entities = SharedScript.entities; 32 this.playerData = state.players[this.player];33 this.techModifications = SharedScript. techModifications[this.player];35 this.playerData = SharedScript.playersData[this.player]; 36 this.techModifications = SharedScript._techModifications[this.player]; 34 37 35 38 this.buildingsBuilt = 0; … … 37 40 }; 38 41 39 GameState.prototype.updatingCollection = function(id, filter, collection ){42 GameState.prototype.updatingCollection = function(id, filter, collection, allowQuick){ 40 43 // automatically add the player ID 41 44 id = this.player + "-" + id; 42 43 45 if (!this.EntCollecNames[id]){ 44 46 if (collection !== undefined) … … 47 49 this.EntCollecNames[id] = this.entities.filter(filter); 48 50 } 51 if (allowQuick) 52 this.EntCollecNames[id].allowQuickIter(); 49 53 this.EntCollecNames[id].registerUpdates(); 54 // warn ("New Collection named " +id); 50 55 } 51 56 … … 70 75 }; 71 76 72 GameState.prototype.updatingGlobalCollection = function(id, filter, collection ) {77 GameState.prototype.updatingGlobalCollection = function(id, filter, collection, allowQuick) { 73 78 if (!this.EntCollecNames[id]){ 74 79 if (collection !== undefined) … … 76 81 else 77 82 this.EntCollecNames[id] = this.entities.filter(filter); 83 if (allowQuick) 84 this.EntCollecNames[id].allowQuickIter(); 78 85 this.EntCollecNames[id].registerUpdates(); 86 //warn ("New Global Collection named " +id); 79 87 } 80 88 … … 104 112 return 1; 105 113 return 0; 114 }; 115 116 GameState.prototype.townPhase = function() 117 { 118 if (this.playerData.civ == "athen") 119 return "phase_town_athen"; 120 return "phase_town_generic"; 121 }; 122 123 GameState.prototype.cityPhase = function() 124 { 125 return "phase_city_generic"; 106 126 }; 107 127 … … 229 249 return str.replace(/\{civ\}/g, this.playerData.civ); 230 250 }; 251 231 252 GameState.prototype.civ = function() { 232 253 return this.playerData.civ; … … 350 371 }; 351 372 352 GameState.prototype.getOwnEntitiesByMetadata = function(key, value){ 353 return this.updatingCollection(key + "-" + value, Filters.byMetadata(this.player, key, value),this.getOwnEntities()); 373 GameState.prototype.getOwnEntitiesByMetadata = function(key, value, maintain){ 374 if (maintain === true) 375 return this.updatingCollection(key + "-" + value, Filters.byMetadata(this.player, key, value),this.getOwnEntities()); 376 return this.getOwnEntities().filter(Filters.byMetadata(this.player, key, value)); 354 377 }; 355 378 356 379 GameState.prototype.getOwnEntitiesByRole = function(role){ 357 return this.getOwnEntitiesByMetadata("role", role );380 return this.getOwnEntitiesByMetadata("role", role, true); 358 381 }; 359 382 360 383 GameState.prototype.getOwnTrainingFacilities = function(){ 361 return this.updatingCollection("own-training-facilities", Filters.byTrainingQueue(), this.getOwnEntities() );384 return this.updatingCollection("own-training-facilities", Filters.byTrainingQueue(), this.getOwnEntities(), true); 362 385 }; 363 386 364 387 GameState.prototype.getOwnResearchFacilities = function(){ 365 return this.updatingCollection("own-research-facilities", Filters.byResearchAvailable(), this.getOwnEntities() );366 }; 367 368 GameState.prototype.getOwnEntitiesByType = function(type ){388 return this.updatingCollection("own-research-facilities", Filters.byResearchAvailable(), this.getOwnEntities(), true); 389 }; 390 391 GameState.prototype.getOwnEntitiesByType = function(type, maintain){ 369 392 var filter = Filters.byType(type); 370 return this.updatingCollection("own-by-type-" + type, filter, this.getOwnEntities()); 371 }; 372 373 GameState.prototype.countEntitiesByType = function(type) { 374 return this.getOwnEntitiesByType(type).length; 393 if (maintain === true) 394 return this.updatingCollection("own-by-type-" + type, filter, this.getOwnEntities()); 395 return this.getOwnEntities().filter(filter); 396 397 }; 398 399 GameState.prototype.countEntitiesByType = function(type, maintain) { 400 return this.getOwnEntitiesByType(type, maintain).length; 375 401 }; 376 402 377 403 GameState.prototype.countEntitiesAndQueuedByType = function(type) { 378 var count = this.countEntitiesByType(type );404 var count = this.countEntitiesByType(type, true); 379 405 380 406 // Count building foundations 381 count += this.countEntitiesByType("foundation|" + type); 382 383 // Count animal resources 384 count += this.countEntitiesByType("resource|" + type); 385 386 // Count entities in building production queues 387 this.getOwnTrainingFacilities().forEach(function(ent){ 388 ent.trainingQueue().forEach(function(item) { 389 if (item.unitTemplate == type){ 390 count += item.count; 391 } 407 if (this.getTemplate(type).hasClass("Structure") === true) 408 count += this.countEntitiesByType("foundation|" + type, true); 409 else if (this.getTemplate(type).resourceSupplyType() !== undefined) // animal resources 410 count += this.countEntitiesByType("resource|" + type, true); 411 else 412 { 413 // Count entities in building production queues 414 // TODO: maybe this fails for corrals. 415 this.getOwnTrainingFacilities().forEach(function(ent){ 416 ent.trainingQueue().forEach(function(item) { 417 if (item.unitTemplate == type){ 418 count += item.count; 419 } 420 }); 392 421 }); 393 } );422 } 394 423 395 424 return count; … … 509 538 510 539 GameState.prototype.getOwnDropsites = function(resource){ 511 return this.updatingCollection("dropsite-own-" + resource, Filters.isDropsite(resource), this.getOwnEntities()); 540 if (resource !== undefined) 541 return this.updatingCollection("dropsite-own-" + resource, Filters.isDropsite(resource), this.getOwnEntities(), true); 542 return this.updatingCollection("dropsite-own", Filters.isDropsite(), this.getOwnEntities(), true); 512 543 }; 513 544 514 545 GameState.prototype.getResourceSupplies = function(resource){ 515 return this.updatingGlobalCollection("resource-" + resource, Filters.byResource(resource), this.getEntities() );546 return this.updatingGlobalCollection("resource-" + resource, Filters.byResource(resource), this.getEntities(), true); 516 547 }; 517 548 … … 590 621 ret.push([techs[1]._templateName, techs[1]] ); 591 622 } else { 592 if (this.canResearch(allResearchable[i]) && template._templateName != "phase_town_generic" 593 && template._templateName != "phase_town_athens" && template._templateName != "phase_city_generic") 623 if (this.canResearch(allResearchable[i]) && template._templateName != this.townPhase() && template._templateName != this.cityPhase()) 594 624 ret.push( [allResearchable[i], template] ); 595 625 } -
ps/trunk/binaries/data/mods/public/simulation/ai/common-api-v3/map-module.js
r13225 r13907 2 2 * Copied with changes from QuantumState's original for qBot, it's a component for storing 8 bit values. 3 3 */ 4 5 const TERRITORY_PLAYER_MASK = 0x3F; 4 6 5 7 function Map(sharedScript, originalMap, actualCopy){ … … 10 12 this.length = gameMap.data.length; 11 13 14 this.maxVal = 255; 15 12 16 if (originalMap && actualCopy){ 13 17 this.map = new Uint8Array(this.length); … … 22 26 } 23 27 28 Map.prototype.setMaxVal = function(val){ 29 this.maxVal = val; 30 }; 31 24 32 Map.prototype.gamePosToMapPos = function(p){ 25 return [Math. round(p[0]/this.cellSize), Math.round(p[1]/this.cellSize)];33 return [Math.floor(p[0]/this.cellSize), Math.floor(p[1]/this.cellSize)]; 26 34 }; 27 35 … … 44 52 case 'linear': 45 53 str = +strength / +maxDist; 46 break;54 break; 47 55 case 'quadratic': 48 56 str = +strength / +maxDist2; 49 break;57 break; 50 58 case 'constant': 51 59 str = +strength; 52 break;60 break; 53 61 } 54 62 … … 72 80 break; 73 81 } 74 if (this.map[x + y * this.width] + quant > 255){ 75 this.map[x + y * this.width] = 255; 76 } else if (this.map[x + y * this.width] + quant < 0){ 82 if (this.map[x + y * this.width] + quant < 0) 77 83 this.map[x + y * this.width] = 0; 78 } else { 84 else if (this.map[x + y * this.width] + quant > this.maxVal) 85 this.map[x + y * this.width] = this.maxVal; // avoids overflow. 86 else 79 87 this.map[x + y * this.width] += quant; 80 }81 88 } 82 89 } … … 127 134 } 128 135 var machin = this.map[x + y * this.width] * quant; 129 if (machin <= 0){ 130 this.map[x + y * this.width] = 0; //set anything which would have gone negative to 0 131 }else{ 136 if (machin < 0) 137 this.map[x + y * this.width] = 0; 138 else if (machin > this.maxVal) 139 this.map[x + y * this.width] = this.maxVal; 140 else 132 141 this.map[x + y * this.width] = machin; 133 } 134 } 135 } 136 } 137 }; 142 } 143 } 144 } 145 }; 146 147 // doesn't check for overflow. 138 148 Map.prototype.setInfluence = function(cx, cy, maxDist, value) { 139 149 value = value ? value : 0; … … 179 189 }; 180 190 /** 181 * Make each cell's 8-bit value at least one greater than each of its 182 * neighbours' values. Possible assignment of a cap (maximum). 191 * Make each cell's 16-bit/8-bit value at least one greater than each of its 192 * neighbours' values. (If the grid is initialised with 0s and 65535s or 255s, the 193 * result of each cell is its Manhattan distance to the nearest 0.) 183 194 */ 184 195 Map.prototype.expandInfluences = function(maximum, map) { … … 188 199 189 200 if (maximum == undefined) 190 maximum = 255;201 maximum = this.maxVal; 191 202 var w = this.width; 192 203 var h = this.height; … … 267 278 // add to current map by the parameter map pixelwise 268 279 Map.prototype.add = function(map){ 269 for (var i = 0; i < this.length; ++i){ 270 this.map[i] += +map.map[i]; 271 } 272 }; 280 for (var i = 0; i < this.length; ++i) { 281 if (this.map[i] + map.map[i] < 0) 282 this.map[i] = 0; 283 else if (this.map[i] + map.map[i] > this.maxVal) 284 this.map[i] = this.maxVal; 285 else 286 this.map[i] += map.map[i]; 287 } 288 }; 289 290 Map.prototype.dumpIm = function(name, threshold){ 291 name = name ? name : "default.png"; 292 threshold = threshold ? threshold : this.maxVal; 293 Engine.DumpImage(name, this.map, this.width, this.height, threshold); 294 }; -
ps/trunk/binaries/data/mods/public/simulation/ai/common-api-v3/shared.js
r13404 r13907 23 23 this._entityCollectionsByDynProp = {}; 24 24 this._entityCollectionsUID = 0; 25 25 26 // A few notes about these maps. They're updated by checking for "create" and "destroy" events for all resources 27 // TODO: change the map when the resource amounts change for at least stone and metal mines. 28 this.resourceMaps = {}; // Contains maps showing the density of wood, stone and metal 29 this.CCResourceMaps = {}; // Contains maps showing the density of wood, stone and metal, optimized for CC placement. 30 // Resource maps data. 31 // By how much to divide the resource amount for plotting (ie a tree having 200 wood is "4"). 32 this.decreaseFactor = {'wood': 50.0, 'stone': 90.0, 'metal': 90.0, 'food': 40.0}; 33 26 34 this.turn = 0; 27 35 } … … 161 169 // this is called right at the end of map generation, before you actually reach the map. 162 170 SharedScript.prototype.initWithState = function(state) { 163 this.passabilityClasses = state.passabilityClasses;164 this.passabilityMap = state.passabilityMap;165 this.territoryMap = state.territoryMap;166 167 for (var o in state.players)168 this._techModifications[o] = state.players[o].techModifications;169 170 this.techModifications = this._techModifications;171 172 this._entities = {};173 for (var id in state.entities)174 {175 this._entities[id] = new Entity(this, state.entities[id]);176 }177 // entity collection updated on create/destroy event.178 this.entities = new EntityCollection(this, this._entities);179 180 // create the terrain analyzer181 this.terrainAnalyzer = new TerrainAnalysis(this, state);182 this.accessibility = new Accessibility(state, this.terrainAnalyzer);183 184 this.gameState = {};185 for (var i in this._players)186 {187 this.gameState[this._players[i]] = new GameState(this,state,this._players[i]);188 }189 190 };191 192 // General update of the shared script, before each AI's update193 // applies entity deltas, and each gamestate.194 SharedScript.prototype.onUpdate = function(state)195 {196 this.ApplyEntitiesDelta(state);197 198 Engine.ProfileStart("onUpdate");199 200 171 this.events = state.events; 201 172 this.passabilityClasses = state.passabilityClasses; … … 205 176 this.territoryMap = state.territoryMap; 206 177 this.timeElapsed = state.timeElapsed; 207 178 208 179 for (var o in state.players) 209 180 this._techModifications[o] = state.players[o].techModifications; 181 182 this._entities = {}; 183 this.entities = new EntityCollection(this); 184 185 /* (var id in state.entities) 186 { 187 this._entities[id] = new Entity(this, state.entities[id]); 188 } 189 // entity collection updated on create/destroy event. 190 this.entities = new EntityCollection(this, this._entities); 191 */ 192 193 this.ApplyEntitiesDelta(state); 194 195 // create the terrain analyzer 196 this.terrainAnalyzer = new TerrainAnalysis(); 197 this.terrainAnalyzer.init(this, state); 198 this.accessibility = new Accessibility(); 199 this.accessibility.init(state, this.terrainAnalyzer); 200 201 // defined in TerrainAnalysis.js 202 this.updateResourceMaps(this, this.events); 203 204 this.gameState = {}; 205 for (var i in this._players) 206 { 207 this.gameState[this._players[i]] = new GameState(); 208 this.gameState[this._players[i]].init(this,state,this._players[i]); 209 } 210 211 }; 212 213 // General update of the shared script, before each AI's update 214 // applies entity deltas, and each gamestate. 215 SharedScript.prototype.onUpdate = function(state) 216 { 217 if (this.turn !== 0) 218 this.ApplyEntitiesDelta(state); 219 220 Engine.ProfileStart("onUpdate"); 221 222 this.events = state.events; 223 this.passabilityClasses = state.passabilityClasses; 224 this.passabilityMap = state.passabilityMap; 225 this.players = this._players; 226 this.playersData = state.players; 227 this.territoryMap = state.territoryMap; 228 this.timeElapsed = state.timeElapsed; 229 230 for (var o in state.players) 231 this._techModifications[o] = state.players[o].techModifications; 210 232 211 233 for (var i in this.gameState) 212 234 this.gameState[i].update(this,state); 213 235 236 if (this.turn !== 0) 237 this.updateResourceMaps(this, this.events); 214 238 this.terrainAnalyzer.updateMapWithEvents(this); 215 239 … … 224 248 { 225 249 Engine.ProfileStart("Shared ApplyEntitiesDelta"); 250 251 var foundationFinished = {}; 226 252 227 253 for each (var evt in state.events) … … 237 263 238 264 // Update all the entity collections since the create operation affects static properties as well as dynamic 239 for each (var entCollection in this._entityCollections) 240 { 241 entCollection.updateEnt(this._entities[evt.msg.entity]); 242 } 243 265 for (var entCollection in this._entityCollections) 266 { 267 this._entityCollections[entCollection].updateEnt(this._entities[evt.msg.entity]); 268 } 244 269 } 245 270 else if (evt.type == "Destroy") … … 253 278 continue; 254 279 280 if (foundationFinished[evt.msg.entity]) 281 evt.msg["SuccessfulFoundation"] = true; 282 255 283 // The entity was destroyed but its data may still be useful, so 256 284 // remember the entity and this AI's metadata concerning it … … 281 309 } 282 310 } 311 else if (evt.type == "ConstructionFinished") 312 { 313 // we can rely on this being before the "Destroy" command as this is the order defined by FOundation.js 314 // we'll move metadata. 315 if (!this._entities[evt.msg.entity]) 316 continue; 317 var ent = this._entities[evt.msg.entity]; 318 var newEnt = this._entities[evt.msg.newentity]; 319 if (this._entityMetadata[ent.owner()] && this._entityMetadata[ent.owner()][evt.msg.entity] !== undefined) 320 for (var key in this._entityMetadata[ent.owner()][evt.msg.entity]) 321 { 322 this.setMetadata(ent.owner(), newEnt, key, this._entityMetadata[ent.owner()][evt.msg.entity][key]) 323 } 324 foundationFinished[evt.msg.entity] = true; 325 } 326 else if (evt.type == "AIMetadata") 327 { 328 if (!this._entities[evt.msg.id]) 329 continue; // might happen in some rare cases of foundations getting destroyed, perhaps. 330 // Apply metadata (here for buildings for example) 331 for (var key in evt.msg.metadata) 332 { 333 this.setMetadata(evt.msg.owner, this._entities[evt.msg.id], key, evt.msg.metadata[key]) 334 } 335 } 283 336 } 284 337 … … 286 339 { 287 340 var changes = state.entities[id]; 288 341 289 342 for (var prop in changes) 290 343 { 291 344 this._entities[id]._entity[prop] = changes[prop]; 345 292 346 this.updateEntityCollections(prop, this._entities[id]); 293 347 } … … 336 390 if (this._entityCollectionsByDynProp[property] !== undefined) 337 391 { 338 for each (var entCollectionin this._entityCollectionsByDynProp[property])339 { 340 entCollection.updateEnt(ent);392 for (var entCollectionid in this._entityCollectionsByDynProp[property]) 393 { 394 this._entityCollectionsByDynProp[property][entCollectionid].updateEnt(ent); 341 395 } 342 396 } … … 360 414 return undefined; 361 415 return metadata[key]; 416 }; 417 SharedScript.prototype.deleteMetadata = function(player, ent, key) 418 { 419 var metadata = this._entityMetadata[player][ent.id()]; 420 421 if (!metadata || !(key in metadata)) 422 return true; 423 metadata[key] = undefined; 424 delete metadata[key]; 425 return true; 362 426 }; 363 427 -
ps/trunk/binaries/data/mods/public/simulation/ai/common-api-v3/terrain-analysis.js
r13404 r13907 13 13 */ 14 14 15 function TerrainAnalysis(sharedScript,rawState){ 15 function TerrainAnalysis() { 16 this.cellSize = 4; 17 } 18 19 copyPrototype(TerrainAnalysis, Map); 20 21 TerrainAnalysis.prototype.init = function(sharedScript,rawState) { 16 22 var self = this; 17 this.cellSize = 4;18 23 19 24 var passabilityMap = rawState.passabilityMap; 20 21 25 this.width = passabilityMap.width; 22 26 this.height = passabilityMap.height; … … 99 103 this.obstructionMaskWater = null; 100 104 this.obstructionMask = null; 101 }; 102 103 copyPrototype(TerrainAnalysis, Map); 105 delete this.obstructionMaskLand; 106 delete this.obstructionMaskWater; 107 delete this.obstructionMask; 108 }; 104 109 105 110 // Returns the (approximately) closest point which is passable by searching in a spiral pattern … … 239 244 240 245 */ 241 function Accessibility(rawState, terrainAnalyser){ 246 function Accessibility() { 247 248 } 249 250 copyPrototype(Accessibility, TerrainAnalysis); 251 252 Accessibility.prototype.init = function(rawState, terrainAnalyser){ 242 253 var self = this; 243 254 244 255 this.Map(rawState, terrainAnalyser.map); 245 this.passMap = new Uint8Array(terrainAnalyser.length); 256 this.landPassMap = new Uint8Array(terrainAnalyser.length); 257 this.navalPassMap = new Uint8Array(terrainAnalyser.length); 246 258 247 259 this.regionSize = []; 248 this.regionSize.push(0); 260 this.regionType = []; // "inaccessible", "land" or "water"; 261 // ID of the region associated with an array of region IDs. 262 this.regionLinks = []; 249 263 250 264 // initialized to 0, it's more optimized to start at 1 (I'm checking that if it's not 0, then it's already aprt of a region, don't touch); … … 252 266 // So start at 2. 253 267 this.regionID = 2; 254 for (var i = 0; i < this.passMap.length; ++i) { 255 if (this.passMap[i] === 0 && this.map[i] !== 0) { // any non-painted, non-inacessible area. 256 this.regionSize.push(0); // updated 257 this.floodFill(i,this.regionID,false); 258 this.regionID++; 259 } else if (this.passMap[i] === 0) { // any non-painted, inacessible area. 268 269 for (var i = 0; i < this.landPassMap.length; ++i) { 270 if (this.map[i] !== 0) { // any non-painted, non-inacessible area. 271 if (this.landPassMap[i] === 0 && this.floodFill(i,this.regionID,false)) 272 this.regionType[this.regionID++] = "land"; 273 if (this.navalPassMap[i] === 0 && this.floodFill(i,this.regionID,true)) 274 this.regionType[this.regionID++] = "water"; 275 } else if (this.landPassMap[i] === 0) { // any non-painted, inacessible area. 260 276 this.floodFill(i,1,false); 261 } 262 } 277 this.floodFill(i,1,true); 278 } 279 } 280 281 // calculating region links. Regions only touching diagonaly are not linked. 282 // since we're checking all of them, we'll check from the top left to the bottom right 283 var w = this.width; 284 for (var x = 0; x < this.width-1; ++x) 285 { 286 for (var y = 0; y < this.height-1; ++y) 287 { 288 // checking right. 289 var thisLID = this.landPassMap[x+y*w]; 290 var thisNID = this.navalPassMap[x+y*w]; 291 var rightLID = this.landPassMap[x+1+y*w]; 292 var rightNID = this.navalPassMap[x+1+y*w]; 293 var bottomLID = this.landPassMap[x+y*w+w]; 294 var bottomNID = this.navalPassMap[x+y*w+w]; 295 if (thisLID > 1) 296 { 297 if (rightNID > 1) 298 if (this.regionLinks[thisLID].indexOf(rightNID) === -1) 299 this.regionLinks[thisLID].push(rightNID); 300 if (bottomNID > 1) 301 if (this.regionLinks[thisLID].indexOf(bottomNID) === -1) 302 this.regionLinks[thisLID].push(bottomNID); 303 } 304 if (thisNID > 1) 305 { 306 if (rightLID > 1) 307 if (this.regionLinks[thisNID].indexOf(rightLID) === -1) 308 this.regionLinks[thisNID].push(rightLID); 309 if (bottomLID > 1) 310 if (this.regionLinks[thisNID].indexOf(bottomLID) === -1) 311 this.regionLinks[thisNID].push(bottomLID); 312 if (thisLID > 1) 313 if (this.regionLinks[thisNID].indexOf(thisLID) === -1) 314 this.regionLinks[thisNID].push(thisLID); 315 } 316 } 317 } 318 319 //warn(uneval(this.regionLinks)); 320 321 //Engine.DumpImage("LandPassMap.png", this.landPassMap, this.width, this.height, 255); 322 //Engine.DumpImage("NavalPassMap.png", this.navalPassMap, this.width, this.height, 255); 263 323 } 264 copyPrototype(Accessibility, TerrainAnalysis); 265 266 Accessibility.prototype.getAccessValue = function(position){ 324 325 Accessibility.prototype.getAccessValue = function(position, onWater) { 267 326 var gamePos = this.gamePosToMapPos(position); 268 return this.passMap[gamePos[0] + this.width*gamePos[1]]; 327 if (onWater === true) 328 return this.navalPassMap[gamePos[0] + this.width*gamePos[1]]; 329 var ret = this.landPassMap[gamePos[0] + this.width*gamePos[1]]; 330 if (ret === 1) 331 { 332 // quick spiral search. 333 var indx = [ [-1,-1],[-1,0],[-1,1],[0,1],[1,1],[1,0],[1,-1],[0,-1]] 334 for (i in indx) 335 { 336 ret = this.landPassMap[gamePos[0]+indx[0] + this.width*(gamePos[1]+indx[0])] 337 if (ret !== undefined && ret !== 1) 338 return ret; 339 } 340 } 341 return ret; 269 342 }; 270 343 … … 284 357 // Hardcore means is also checks for isAccessible at the end (it checks for either water or land though, beware). 285 358 // This is a blind check and not a pathfinder: for all it knows there is a huge block of trees in the middle. 286 Accessibility.prototype.pathAvailable = function(gameState, start, end, hardcore){359 Accessibility.prototype.pathAvailable = function(gameState, start, end, onWater, hardcore){ 287 360 var pstart = this.gamePosToMapPos(start); 288 361 var istart = pstart[0] + pstart[1]*this.width; 289 362 var pend = this.gamePosToMapPos(end); 290 363 var iend = pend[0] + pend[1]*this.width; 291 292 if (this.passMap[istart] === this.passMap[iend]) { 293 if (hardcore && (this.isAccessible(gameState, end,true) || this.isAccessible(gameState, end,false))) 364 if (onWater) 365 { 366 if (this.navalPassMap[istart] === this.navalPassMap[iend]) { 367 if (hardcore && this.isAccessible(gameState, end,false)) 368 return true; 369 else if (hardcore) 370 return false; 294 371 return true; 295 else if (hardcore) 296 return false; 297 return true; 372 } 373 } else { 374 if (this.landPassMap[istart] === this.landPassMap[iend]) { 375 if (hardcore && this.isAccessible(gameState, end,true)) 376 return true; 377 else if (hardcore) 378 return false; 379 return true; 380 } 298 381 } 299 382 return false; 300 383 }; 301 Accessibility.prototype.getRegionSize = function(position){ 384 385 Accessibility.prototype.getTrajectTo = function(start, end, noBound) { 386 var pstart = this.gamePosToMapPos(start); 387 var istart = pstart[0] + pstart[1]*this.width; 388 var pend = this.gamePosToMapPos(end); 389 var iend = pend[0] + pend[1]*this.width; 390 391 var onLand = true; 392 if (this.landPassMap[istart] <= 1 && this.navalPassMap[istart] > 1) 393 onLand = false; 394 if (this.landPassMap[istart] <= 1 && this.navalPassMap[istart] <= 1) 395 return false; 396 397 var endRegion = this.landPassMap[iend]; 398 if (endRegion <= 1 && this.navalPassMap[iend] > 1) 399 endRegion = this.navalPassMap[iend]; 400 else if (endRegion <= 1) 401 return false; 402 403 if (onLand) 404 var startRegion = this.landPassMap[istart]; 405 else 406 var startRegion = this.navalPassMap[istart]; 407 408 return this.getTrajectToIndex(startRegion, endRegion, noBound); 409 } 410 411 // Return a "path" of accessibility indexes from one point to another, including the start and the end indexes (unless specified otherwise) 412 // this can tell you what sea zone you need to have a dock on, for example. 413 // assumes a land unit unless start point is over deep water. 414 // if the path is more complicated than "land->sea->land" (or "sea->land->sea"), it will run A* to try and figure it out 415 // Thus it can handle arbitrarily complicated paths (theoretically). 416 Accessibility.prototype.getTrajectToIndex = function(istart, iend, noBound){ 417 var startRegion = istart; 418 var currentRegion = istart; 419 420 var endRegion = iend; 421 422 // optimizations to avoid needless memory usage 423 // if it's the same, return the path 424 if (startRegion === endRegion) 425 return [startRegion]; 426 else if (this.regionLinks[startRegion].indexOf(endRegion) !== -1) 427 return [startRegion, endRegion]; 428 else 429 { 430 var rgs = this.regionLinks[startRegion]; 431 for (p in rgs) 432 { 433 if (this.regionLinks[rgs[p]].indexOf(endRegion) !== -1) 434 return [startRegion, rgs[p], endRegion]; 435 } 436 } 437 // it appears to be difficult. 438 // computing A* over a graph with all nodes equally good (might want to change this sometimes), currently it returns the shortest path switch-wise. 439 this.openList = []; 440 this.parentSquare = new Uint8Array(this.regionSize.length); 441 this.isOpened = new Boolean(this.regionSize.length); 442 this.gCostArray = new Uint8Array(this.regionSize.length); 443 444 this.isOpened[currentRegion] = true; 445 this.openList.push(currentRegion); 446 this.gCostArray[currentRegion] = 0; 447 this.parentSquare[currentRegion] = currentRegion; 448 449 var w = this.width; 450 var h = this.height; 451 452 //creation of variables used in the loop 453 var found = false; 454 455 // on to A* 456 while (found === false && this.openList.length !== 0) { 457 var currentDist = 300; 458 var ti = 0; 459 for (var i in this.openList) 460 { 461 var sum = this.gCostArray[this.openList[i]]; 462 if (sum < currentDist) 463 { 464 ti = i; 465 currentRegion = this.openList[i]; 466 currentDist = sum; 467 } 468 } 469 this.openList.splice(ti,1); 470 this.isOpened[currentRegion] = false; 471 472 // special case, might make it faster (usually oceans connect multiple land masses, sometimes all of them) 473 if (this.regionType[currentRegion] == "water" && endLand) 474 { 475 var idx = this.regionLinks[currentRegion].indexOf(endRegion); 476 if (idx !== -1) 477 { 478 this.parentSquare[endRegion] = currentRegion; 479 this.gCostArray[endRegion] = this.gCostArray[currentRegion] + 1; 480 found = true; 481 break; 482 } 483 } 484 for (var i in this.regionLinks[currentRegion]) 485 { 486 var region = this.regionLinks[currentRegion][i]; 487 if(this.isOpened[region] === undefined) 488 { 489 this.parentSquare[region] = currentRegion; 490 this.gCostArray[region] = this.gCostArray[currentRegion] + 1; 491 this.openList.push(region); 492 this.isOpened[region] = true; 493 if (region === endRegion) 494 { 495 found = true; 496 break; 497 } 498 } else { 499 if (this.gCostArray[region] > 1 + this.gCostArray[currentRegion]) 500 { 501 this.parentSquare[region] = currentRegion; 502 this.gCostArray[region] = 1 + this.gCostArray[currentRegion]; 503 } 504 } 505 } 506 } 507 var path = []; 508 if (found) { 509 currentRegion = endRegion; 510 if (!noBound) 511 path.push(currentRegion); 512 while (this.parentSquare[currentRegion] !== startRegion) 513 { 514 currentRegion = this.parentSquare[currentRegion]; 515 path.push(currentRegion); 516 } 517 if (!noBound) 518 path.push(startRegion); 519 } else { 520 delete this.parentSquare; 521 delete this.isOpened; 522 delete this.gCostArray; 523 return false; 524 } 525 526 delete this.parentSquare; 527 delete this.isOpened; 528 delete this.gCostArray; 529 530 return path; 531 }; 532 533 Accessibility.prototype.getRegionSize = function(position, onWater){ 302 534 var pos = this.gamePosToMapPos(position); 303 535 var index = pos[0] + pos[1]*this.width; 304 if (this.regionSize[this.passMap[index]] === undefined) 536 var ID = (onWater === true) ? this.navalPassMap[index] : this.landPassMap[index]; 537 if (this.regionSize[ID] === undefined) 305 538 return 0; 306 return this.regionSize[this.passMap[index]]; 307 }; 308 Accessibility.prototype.getRegionSizei = function(index) { 309 if (this.regionSize[this.passMap[index]] === undefined) 539 return this.regionSize[ID]; 540 }; 541 542 Accessibility.prototype.getRegionSizei = function(index, onWater) { 543 if (this.regionSize[this.landPassMap[index]] === undefined && (!onWater || this.regionSize[this.navalPassMap[index]] === undefined)) 310 544 return 0; 311 return this.regionSize[this.passMap[index]]; 312 }; 313 314 // Implementation of a fast flood fill. Reasonably good performances. Runs once at startup. 545 if (onWater && this.regionSize[this.navalPassMap[index]] > this.regionSize[this.landPassMap[index]]) 546 return this.regionSize[this.navalPassMap[index]]; 547 return this.regionSize[this.landPassMap[index]]; 548 }; 549 550 // Implementation of a fast flood fill. Reasonably good performances for JS. 315 551 // TODO: take big zones of impassable trees into account? 316 552 Accessibility.prototype.floodFill = function(startIndex, value, onWater) 317 553 { 318 554 this.s = startIndex; 319 if ( this.passMap[this.s] !== 0) {555 if ((!onWater && this.landPassMap[this.s] !== 0) || (onWater && this.navalPassMap[this.s] !== 0) ) { 320 556 return false; // already painted. 321 557 } 322 558 323 559 this.floodFor = "land"; 324 if (this.map[this.s] === 200 || (this.map[this.s] === 201 && onWater === true)) 560 if (this.map[this.s] === 0) 561 { 562 this.landPassMap[this.s] = 1; 563 this.navalPassMap[this.s] = 1; 564 return false; 565 } 566 if (onWater === true) 567 { 568 if (this.map[this.s] !== 200 && this.map[this.s] !== 201) 569 { 570 this.navalPassMap[this.s] = 1; // impassable for naval 571 return false; // do nothing 572 } 325 573 this.floodFor = "water"; 326 else if (this.map[this.s] === 0) 327 this.floodFor = "impassable"; 328 574 } else if (this.map[this.s] === 200) { 575 this.landPassMap[this.s] = 1; // impassable for land 576 return false; 577 } 578 579 // here we'll be able to start. 580 for (var i = this.regionSize.length; i <= value; ++i) 581 { 582 this.regionLinks.push([]); 583 this.regionSize.push(0); 584 this.regionType.push("inaccessible"); 585 } 329 586 var w = this.width; 330 587 var h = this.height; … … 348 605 if (index < 0) 349 606 break; 350 if (this.floodFor === " impassable" && this.map[index] === 0 && this.passMap[index] ===0) {607 if (this.floodFor === "land" && this.landPassMap[index] === 0 && this.map[index] !== 0 && this.map[index] !== 200) { 351 608 loop = true; 352 } else if (this.floodFor === "land" && this.passMap[index] === 0 && this.map[index] !== 0 && this.map[index] !== 200) { 353 loop = true; 354 } else if (this.floodFor === "water" && this.passMap[index] === 0 && (this.map[index] === 200 || (this.map[index] === 201 && this.onWater)) ) { 609 } else if (this.floodFor === "water" && this.navalPassMap[index] === 0 && (this.map[index] === 200 || this.map[index] === 201)) { 355 610 loop = true; 356 611 } else { … … 364 619 do { 365 620 var index = +newIndex + w*y; 366 if (this.floodFor === "impassable" && this.map[index] === 0 && this.passMap[index] === 0) { 367 this.passMap[index] = value; 621 622 if (this.floodFor === "land" && this.landPassMap[index] === 0 && this.map[index] !== 0 && this.map[index] !== 200) { 623 this.landPassMap[index] = value; 368 624 this.regionSize[value]++; 369 } else if (this.floodFor === "land" && this.passMap[index] === 0 && this.map[index] !== 0 && this.map[index] !== 200) { 370 this.passMap[index] = value; 371 this.regionSize[value]++; 372 } else if (this.floodFor === "water" && this.passMap[index] === 0 && (this.map[index] === 200 || (this.map[index] === 201 && this.onWater)) ) { 373 this.passMap[index] = value; 625 } else if (this.floodFor === "water" && this.navalPassMap[index] === 0 && (this.map[index] === 200 || this.map[index] === 201)) { 626 this.navalPassMap[index] = value; 374 627 this.regionSize[value]++; 375 628 } else { … … 379 632 if (index%w > 0) 380 633 { 381 if (this.floodFor === " impassable" && this.map[index -1] === 0 && this.passMap[index -1] ===0) {634 if (this.floodFor === "land" && this.landPassMap[index -1] === 0 && this.map[index -1] !== 0 && this.map[index -1] !== 200) { 382 635 if(!reachLeft) { 383 636 IndexArray.push(index -1); 384 637 reachLeft = true; 385 638 } 386 } else if (this.floodFor === " land" && this.passMap[index -1] === 0 && this.map[index -1] !== 0 && this.map[index -1] !== 200) {639 } else if (this.floodFor === "water" && this.navalPassMap[index -1] === 0 && (this.map[index -1] === 200 || this.map[index -1] === 201)) { 387 640 if(!reachLeft) { 388 641 IndexArray.push(index -1); 389 642 reachLeft = true; 390 643 } 391 } else if (this.floodFor === "water" && this.passMap[index -1] === 0 && (this.map[index -1] === 200 || (this.map[index -1] === 201 && this.onWater)) ) {392 if(!reachLeft) {393 IndexArray.push(index -1);394 reachLeft = true;395 }396 644 } else if(reachLeft) { 397 645 reachLeft = false; … … 400 648 if (index%w < w - 1) 401 649 { 402 if (this.floodFor === " impassable" && this.map[index +1] === 0 && this.passMap[index +1] ===0) {650 if (this.floodFor === "land" && this.landPassMap[index +1] === 0 && this.map[index +1] !== 0 && this.map[index +1] !== 200) { 403 651 if(!reachRight) { 404 652 IndexArray.push(index +1); 405 653 reachRight = true; 406 654 } 407 } else if (this.floodFor === " land" && this.passMap[index +1] === 0 && this.map[index +1] !== 0 && this.map[index +1] !== 200) {655 } else if (this.floodFor === "water" && this.navalPassMap[index +1] === 0 && (this.map[index +1] === 200 || this.map[index +1] === 201)) { 408 656 if(!reachRight) { 409 657 IndexArray.push(index +1); 410 658 reachRight = true; 411 659 } 412 } else if (this.floodFor === "water" && this.passMap[index +1] === 0 && (this.map[index +1] === 200 || (this.map[index +1] === 201 && this.onWater)) ) {413 if(!reachRight) {414 IndexArray.push(index +1);415 reachRight = true;416 }417 660 } else if(reachRight) { 418 661 reachRight = false; … … 420 663 } 421 664 ++y; 422 } while (index/w < w ) // should actually break665 } while (index/w < w-1) // should actually break 423 666 } 424 667 return true; 425 668 } 426 669 427 function landSizeCounter(rawState,terrainAnalyzer) { 428 var self = this; 429 430 this.passMap = terrainAnalyzer.map; 431 432 var map = new Uint8Array(this.passMap.length); 433 this.Map(rawState,map); 434 435 436 for (var i = 0; i < this.passMap.length; ++i) { 437 if (this.passMap[i] !== 0) 438 this.map[i] = 255; 439 else 440 this.map[i] = 0; 441 } 442 443 this.expandInfluences(); 444 } 445 copyPrototype(landSizeCounter, TerrainAnalysis); 446 447 // Implementation of A* as a flood fill. Possibility of (clever) oversampling 448 // for efficiency or for disregarding too small passages. 449 // can operate over several turns, though default is only one turn. 450 landSizeCounter.prototype.getAccessibleLandSize = function(position, sampling, mode, OnlyBuildable, sizeLimit, iterationLimit) 451 { 452 if (sampling === undefined) 453 this.Sampling = 1; 454 else 455 this.Sampling = sampling < 1 ? 1 : sampling; 456 457 // this checks from the actual starting point. If that is inaccessible (0), it returns undefined; 458 if (position.length !== undefined) { 459 // this is an array 460 if (position[0] < 0 || this.gamePosToMapPos(position)[0] >= this.width || position[1] < 0 || this.gamePosToMapPos(position)[1] >= this.height) 461 return undefined; 462 463 var s = this.gamePosToMapPos(position); 464 this.s = s[0] + w*s[1]; 465 if (this.map[this.s] === 0 || this.map[this.s] === 200 || (OnlyBuildable === true && this.map[this.s] === 201) ) { 466 return undefined; 467 } 468 } else { 469 this.s = position; 470 if (this.map[this.s] === 0 || this.map[this.s] === 200 || (OnlyBuildable === true && this.map[this.s] === 201) ) { 471 return undefined; 472 } 473 } 474 475 if (mode === undefined) 476 this.mode = "default"; 477 else 478 this.mode = mode; 479 480 if (sizeLimit === undefined) 481 this.sizeLimit = 300000; 482 else 483 this.sizeLimit = sizeLimit; 484 485 var w = this.width; 486 var h = this.height; 487 488 // max map size is 512*512, this is higher. 489 this.iterationLimit = 300000; 490 if (iterationLimit !== undefined) 491 this.iterationLimit = iterationLimit; 492 493 this.openList = []; 494 this.isOpened = new Boolean(this.map.length); 495 this.gCostArray = new Uint16Array(this.map.length); 496 497 this.currentSquare = this.s; 498 this.isOpened[this.s] = true; 499 this.openList.push(this.s); 500 this.gCostArray[this.s] = 0; 501 502 this.countedValue = 1; 503 this.countedArray = [this.s]; 504 505 if (OnlyBuildable !== undefined) 506 this.onlyBuildable = OnlyBuildable; 507 else 508 this.onlyBuildable = true; 509 510 return this.continueLandSizeCalculation(); 511 } 512 landSizeCounter.prototype.continueLandSizeCalculation = function() 513 { 514 var w = this.width; 515 var h = this.height; 516 var positions = [[0,1], [0,-1], [1,0], [-1,0], [1,1], [-1,-1], [1,-1], [-1,1]]; 517 var cost = [10,10,10,10,15,15,15,15]; 518 519 //creation of variables used in the loop 520 var nouveau = false; 521 var shortcut = false; 522 var Sampling = this.Sampling; 523 var infinity = Math.min(); 524 var currentDist = infinity; 525 526 var iteration = 0; 527 while (this.openList.length !== 0 && iteration < this.iterationLimit && this.countedValue < this.sizeLimit && this.countedArray.length < this.sizeLimit){ 528 currentDist = infinity; 529 for (var i in this.openList) 530 { 531 var sum = this.gCostArray[this.openList[i]]; 532 if (sum < currentDist) 533 { 534 this.currentSquare = this.openList[i]; 535 currentDist = sum; 536 } 537 } 538 this.openList.splice(this.openList.indexOf(this.currentSquare),1); 539 540 shortcut = false; 541 this.isOpened[this.currentSquare] = false; 542 for (var i in positions) { 543 var index = 0 + this.currentSquare + positions[i][0]*Sampling + w*Sampling*positions[i][1]; 544 if (this.passMap[index] !== 0 && this.passMap[index] !== 200 && this.map[index] >= Sampling && (!this.onlyBuildable || this.passMap[index] !== 201)) { 545 if(this.isOpened[index] === undefined) { 546 if (this.mode === "default") 547 this.countedValue++; 548 else if (this.mode === "array") 549 this.countedArray.push(index); 550 this.gCostArray[index] = this.gCostArray[this.currentSquare] + cost[i] * Sampling; 551 this.openList.push(index); 552 this.isOpened[index] = true; 553 } 554 } 555 } 556 iteration++; 557 } 558 559 if (iteration === this.iterationLimit && this.openList.length !== 0 && this.countedValue !== this.sizeLimit && this.countedArray.length !== this.sizeLimit) 560 { 561 // we've got to assume that we stopped because we reached the upper limit of iterations 562 return "toBeContinued"; 563 } 564 565 delete this.parentSquare; 566 delete this.isOpened; 567 delete this.fCostArray; 568 delete this.gCostArray; 569 570 if (this.mode === "default") 571 return this.countedValue; 572 else if (this.mode === "array") 573 return this.countedArray; 574 return undefined; 575 } 670 // TODO: make it regularly update stone+metal mines and their resource levels. 671 // creates and maintains a map of unused resource density 672 // this also takes dropsites into account. 673 // resources that are "part" of a dropsite are not counted. 674 SharedScript.prototype.updateResourceMaps = function(sharedScript, events) { 675 676 for (var resource in this.decreaseFactor){ 677 // if there is no resourceMap create one with an influence for everything with that resource 678 if (! this.resourceMaps[resource]){ 679 // We're creting them 8-bit. Things could go above 255 if there are really tons of resources 680 // But at that point the precision is not really important anyway. And it saves memory. 681 this.resourceMaps[resource] = new Map(sharedScript, new Uint8Array(sharedScript.passabilityMap.data.length)); 682 this.resourceMaps[resource].setMaxVal(255); 683 this.CCResourceMaps[resource] = new Map(sharedScript, new Uint8Array(sharedScript.passabilityMap.data.length)); 684 this.CCResourceMaps[resource].setMaxVal(255); 685 } 686 } 687 688 // Look for destroy events and subtract the entities original influence from the resourceMap 689 // TODO: perhaps do something when dropsites appear/disappear. 690 for (var key in events) { 691 var e = events[key]; 692 if (e.type === "Destroy") { 693 if (e.msg.entityObj){ 694 var ent = e.msg.entityObj; 695 if (ent && ent.position() && ent.resourceSupplyType() && ent.resourceSupplyType().generic !== "treasure") { 696 var resource = ent.resourceSupplyType().generic; 697 var x = Math.floor(ent.position()[0] / 4); 698 var z = Math.floor(ent.position()[1] / 4); 699 var strength = Math.floor(ent.resourceSupplyMax()/this.decreaseFactor[resource]); 700 701 if (resource === "wood" || resource === "food") 702 { 703 this.resourceMaps[resource].addInfluence(x, z, 2, 5,'constant'); 704 this.resourceMaps[resource].addInfluence(x, z, 9.0, -strength,'constant'); 705 this.CCResourceMaps[resource].addInfluence(x, z, 15, -strength/2.0,'constant'); 706 } 707 else if (resource === "stone" || resource === "metal") 708 { 709 this.resourceMaps[resource].addInfluence(x, z, 8, 50); 710 this.resourceMaps[resource].addInfluence(x, z, 12.0, -strength/1.5); 711 this.resourceMaps[resource].addInfluence(x, z, 12.0, -strength/2.0,'constant'); 712 this.CCResourceMaps[resource].addInfluence(x, z, 30, -strength,'constant'); 713 } 714 } 715 } 716 } else if (e.type === "Create") { 717 if (e.msg.entity){ 718 var ent = sharedScript._entities[e.msg.entity]; 719 if (ent && ent.position() && ent.resourceSupplyType() && ent.resourceSupplyType().generic !== "treasure"){ 720 var resource = ent.resourceSupplyType().generic; 721 722 var x = Math.floor(ent.position()[0] / 4); 723 var z = Math.floor(ent.position()[1] / 4); 724 var strength = Math.floor(ent.resourceSupplyMax()/this.decreaseFactor[resource]); 725 if (resource === "wood" || resource === "food") 726 { 727 this.CCResourceMaps[resource].addInfluence(x, z, 15, strength/2.0,'constant'); 728 this.resourceMaps[resource].addInfluence(x, z, 9.0, strength,'constant'); 729 this.resourceMaps[resource].addInfluence(x, z, 2, -5,'constant'); 730 } 731 else if (resource === "stone" || resource === "metal") 732 { 733 this.CCResourceMaps[resource].addInfluence(x, z, 30, strength,'constant'); 734 this.resourceMaps[resource].addInfluence(x, z, 12.0, strength/1.5); 735 this.resourceMaps[resource].addInfluence(x, z, 12.0, strength/2.0,'constant'); 736 this.resourceMaps[resource].addInfluence(x, z, 8, -50); 737 } 738 } 739 } 740 } 741 } 742 };
Note:
See TracChangeset
for help on using the changeset viewer.
