This code is intended to train a 10 spearman defense force (the "defenders" role does nothing, it's just there as a placeholder really) as top priority, then cycle round spears then javelins. Depending on a strategy which is chosen at random, it then either counts to ten and sends a raiding force or forty for a full-on assault. Almost needless to say, this does not happen. The defense force is generally larger than it should be, and once it is trained the attack forces are entirely made of javelinmen. It also seems to do a lot more full-scale assaults than probability would dictate. I'm not sure if after it goes onto training javelins it will then go back to training defenders if I kill some, though it should do in theory. Any help much appreciated... var MilitaryAttackManager = Class({ _init: function() { this.targetSquadSize = 20; this.findatype = 1; this.squadTypes = [ "units/{civ}_infantry_spearman_b", "units/{civ}_infantry_javelinist_b", // "units/{civ}_infantry_archer_b", // TODO: should only include this if hele ]; }, /** * Returns the unit type we should begin training. * (Currently this is whatever we have least of.) */ findBestNewUnit: function(gameState) { // Count each type var types = []; for each (var t in this.squadTypes) types.push([t, gameState.countEntitiesAndQueuedWithType(t)]); // Sort by increasing count types.sort(function (a, { return a[1] - b[1]; }); // TODO: we shouldn't return units that we don't have any // buildings capable of training // Let's make this shizz random... var randomiser = Math.floor(Math.random()*types.length); return types[0][0]; }, update: function(gameState, planGroups) { // Pause for a minute before starting any work, to give the economy a chance // to start up if (gameState.getTimeElapsed() < 60*1000) return; Engine.ProfileStart("military update"); // Also train up some defenders var pendingdefense = gameState.getOwnEntitiesWithRole("defenders"); if (pendingdefense.length <= 10){ planGroups.economyPersonnel.addPlan(110, new UnitTrainingPlan(gameState, "units/{civ}_infantry_spearman_b", 5, { "role": "defenders" }) ); } // Continually try training new units, in batches of 5 //planGroups.militaryPersonnel.addPlan(100, // new UnitTrainingPlan(gameState, // this.findBestNewUnit(gameState), 5, { "role": "attack-pending" }) //); //this.chat("Hmm, let's pick a number. I think " + findatype + " sounds good."); if (this.findatype == 1){ planGroups.economyPersonnel.addPlan(100, new UnitTrainingPlan(gameState, "units/{civ}_infantry_spearman_b", 5, { "role": "attack-pending" }) ); this.findatype = 0; } else { planGroups.economyPersonnel.addPlan(100, new UnitTrainingPlan(gameState, "units/{civ}_infantry_javelinist_b", 5, { "role": "attack-pending" }) ); this.findatype = 1; } // Find the units ready to join the attack var pending = gameState.getOwnEntitiesWithRole("attack-pending"); // Variable for impetuousness, so squads vary in size. if (this.killstrat == 1){ this.baserate = 41; } else { this.baserate = 11; } // If we have enough units yet, start the attack if (pending.length >= this.baserate) { // Find the enemy CCs we could attack var targets = gameState.entities.filter(function(ent) { return (ent.isEnemy() && ent.hasClass("CivCentre")); }); // If there's no CCs, attack anything else that's critical if (targets.length == 0) { targets = gameState.entities.filter(function(ent) { return (ent.isEnemy() && ent.hasClass("ConquestCritical")); }); } // If we have a target, move to it if (targets.length) { // Remove the pending role pending.forEach(function(ent) { ent.setMetadata("role", "attack"); }); var target = targets.toEntityArray()[0]; var targetPos = target.position(); // TODO: this should be an attack-move command pending.move(targetPos[0], targetPos[1]); } //Now set whether to do a raid or full attack next time var whatnext = Math.random(); if (whatnext > 0.25){ this.killstrat = 0; } else { this.killstrat = 1; } } Engine.ProfileStop(); }, });