1
1
forked from 0ad/0ad

A few bugfixes, slight change in logic for smoother play. Makes Aegis gather with cavalry. Allows it to send chat messages to warn about attacks.

This was SVN commit r13266.
This commit is contained in:
wraitii 2013-03-11 19:58:29 +00:00
parent ca40764dc6
commit 9b1b8cc8fd
15 changed files with 421 additions and 147 deletions

View File

@ -22,12 +22,23 @@ function handleNotifications()
// Handle chat notifications specially
if (notification.type == "chat")
{
addChatMessage({
"type": "message",
"guid": findGuidForPlayerID(g_PlayerAssignments, notification.player),
"text": notification.message
});
}
var guid = findGuidForPlayerID(g_PlayerAssignments, notification.player);
if (guid == undefined)
{
addChatMessage({
"type": "message",
"guid": -1,
"player": notification.player,
"text": notification.message
});
} else {
addChatMessage({
"type": "message",
"guid": findGuidForPlayerID(g_PlayerAssignments, notification.player),
"text": notification.message
});
}
}
else if (notification.type == "defeat")
{
addChatMessage({
@ -39,7 +50,7 @@ function handleNotifications()
// If the diplomacy panel is open refresh it.
if (isDiplomacyOpen)
openDiplomacy();
}
}
else if (notification.type == "diplomacy")
{
addChatMessage({
@ -52,7 +63,7 @@ function handleNotifications()
// If the diplomacy panel is open refresh it.
if (isDiplomacyOpen)
openDiplomacy();
}
}
else if (notification.type == "quit")
{
// Used for AI testing
@ -274,6 +285,12 @@ function addChatMessage(msg, playerAssignments)
// This case is hit for AIs, whose names don't exist in playerAssignments.
playerColor = g_Players[msg.player].color.r + " " + g_Players[msg.player].color.g + " " + g_Players[msg.player].color.b;
username = escapeText(g_Players[msg.player].name);
} else if (msg.type == "message")
{
// This case is hit for AIs, whose names don't exist in playerAssignments.
playerColor = g_Players[msg.player].color.r + " " + g_Players[msg.player].color.g + " " + g_Players[msg.player].color.b;
username = escapeText(g_Players[msg.player].name);
parseChatCommands(msg, playerAssignments);
}
else
{
@ -380,7 +397,11 @@ function parseChatCommands(msg, playerAssignments)
if (!msg.text || msg.text[0] != '/')
return;
var sender = playerAssignments[msg.guid].player;
var sender;
if (playerAssignments[msg.guid])
sender = playerAssignments[msg.guid].player;
else
sender = msg.player;
var recurse = false;
var split = msg.text.split(/\s/);

View File

@ -227,6 +227,14 @@ BaseAI.prototype.chat = function(message)
{
Engine.PostCommand({"type": "chat", "message": message});
};
BaseAI.prototype.chatTeam = function(message)
{
Engine.PostCommand({"type": "chat", "message": "/team " +message});
};
BaseAI.prototype.chatEnemies = function(message)
{
Engine.PostCommand({"type": "chat", "message": "/enemy " +message});
};
BaseAI.prototype.registerUpdatingEntityCollection = function(entCollection)
{

View File

@ -579,10 +579,10 @@ var Entity = Class({
// Flees from a unit in the opposite direction.
flee: function(unitToFleeFrom) {
if (this.position() !== undefined && unitToFleeFrom.position() !== undefined) {
var FleeDirection = [unitToFleeFrom.position()[0] - this.position()[0],unitToFleeFrom.position()[1] - this.position()[1]];
var FleeDirection = [this.position()[0] - unitToFleeFrom.position()[0],this.position()[1] - unitToFleeFrom.position()[1]];
var dist = VectorDistance(unitToFleeFrom.position(), this.position() );
FleeDirection[0] = (FleeDirection[0]/dist) * 5;
FleeDirection[1] = (FleeDirection[1]/dist) * 5;
FleeDirection[0] = (FleeDirection[0]/dist) * 8;
FleeDirection[1] = (FleeDirection[1]/dist) * 8;
Engine.PostCommand({"type": "walk", "entities": [this.id()], "x": this.position()[0] + FleeDirection[0]*5, "z": this.position()[1] + FleeDirection[1]*5, "queued": false});
}

View File

@ -41,9 +41,9 @@ var Filters = {
},
"dynamicProperties": ['metadata.' + key]};
},
byHasMetadata: function(key){
byHasMetadata: function(player, key){
return {"func" : function(ent){
return (ent.getMetadata(PlayerID, key) != undefined);
return (ent.getMetadata(player, key) != undefined);
},
"dynamicProperties": ['metadata.' + key]};
},

View File

@ -29,9 +29,19 @@ function SharedScript(settings)
//Return a simple object (using no classes etc) that will be serialized
//into saved games
// TODO: that
//TODO: that
// note: we'll need to serialize much more than that before this can work.
SharedScript.prototype.Serialize = function()
{
// serializing entities without using the class.
var entities = [];
for (var id in this._entities)
{
var ent = this._entities[id];
entities.push( [ent._template, ent._entity, ent._templateName]);
}
// serialiazing metadata will be done by each AI on a AI basis and they shall update the shared script with that info on deserialization (using DeserializeMetadata() ).
return { "entities" : entities };
};
//Called after the constructor when loading a saved game, with 'data' being
@ -39,6 +49,13 @@ SharedScript.prototype.Serialize = function()
// TODO: that
SharedScript.prototype.Deserialize = function(data)
{
this._entities = {};
for (i in data.entities)
{
var entData = data.entities[i];
//this._entities[entData[1].id] = new Entity();
}
};
// Components that will be disabled in foundation entity templates.

View File

@ -67,10 +67,12 @@ aStarPath.prototype.markImpassableArea = function(cx, cy, Distance) {
// sending gamestate creates a map
// Not recommended to use minwidth < sampling. I allow minwidth = sampling - 1.
// (you run the risk of "jumping" over obstacles or weird behavior.
aStarPath.prototype.getPath = function(start, end, Sampling, minWidth, iterationLimit, gamestate)
{
this.Sampling = Sampling >= 1 ? Sampling : 1;
this.minWidth = (minWidth !== undefined && minWidth >= this.Sampling) ? minWidth : this.Sampling;
this.minWidth = (minWidth !== undefined && minWidth+1 >= this.Sampling) ? minWidth : this.Sampling;
if (start[0] < 0 || this.gamePosToMapPos(start)[0] >= this.width || start[1] < 0 || this.gamePosToMapPos(start)[1] >= this.height)
return undefined;

View File

@ -20,7 +20,6 @@ function CityAttack(gameState, militaryManager, uniqueID, targetEnemy, type , ta
max = enemyCount[i];
}
}
warn ("target " + this.targetPlayer);
if (this.targetPlayer === undefined || this.targetPlayer === -1)
{
this.failed = true;
@ -185,7 +184,9 @@ function CityAttack(gameState, militaryManager, uniqueID, targetEnemy, type , ta
else
var position = [-1,-1];
// abort.
if (gameState.ai.accessibility.getAccessValue(position) !== gameState.ai.myIndex)
var position = [-1,-1];
var nearestCCArray = CCs.filterNearest(position, 1).toEntityArray();
var CCpos = nearestCCArray[0].position();
this.rallyPoint = [0,0];
@ -217,6 +218,8 @@ function CityAttack(gameState, militaryManager, uniqueID, targetEnemy, type , ta
// get a good path to an estimated target.
this.pathFinder = new aStarPath(gameState,false);
this.pathWidth = 8; // a path pretty farm from entities
this.pathSampling = 2;
this.onBoat = false; // tells us if our units are loaded on boats.
this.needsShip = false;
@ -265,7 +268,7 @@ CityAttack.prototype.setPaused = function(gameState, boolValue){
}
};
CityAttack.prototype.mustStart = function(gameState){
if (this.isPaused())
if (this.isPaused() || this.path === undefined)
return false;
var MaxReachedEverywhere = true;
for (unitCat in this.unitStat) {
@ -284,65 +287,88 @@ CityAttack.prototype.mustStart = function(gameState){
CityAttack.prototype.updatePreparation = function(gameState, militaryManager,events) {
var self = this;
if (this.path == undefined || this.target == undefined) {
if (this.path == undefined || this.target == undefined || this.path === "toBeContinued") {
// find our target
var targets = this.targetFinder(gameState, militaryManager);
if (targets.length === 0){
targets = this.defaultTargetFinder(gameState, militaryManager);
}
if (targets.length) {
debug ("Aiming for " + targets);
// picking a target
var rand = Math.floor((Math.random()*targets.length));
this.targetPos = undefined;
var count = 0;
while (!this.targetPos){
this.target = targets.toEntityArray()[rand];
this.targetPos = this.target.position();
count++;
if (count > 1000){
debug("No target with a valid position found");
return false;
if (this.target == undefined)
{
var targets = this.targetFinder(gameState, militaryManager);
if (targets.length === 0)
targets = this.defaultTargetFinder(gameState, militaryManager);
if (targets.length) {
debug ("Aiming for " + targets);
// picking a target
var maxDist = 1000000;
var index = 0;
for (i in targets._entities)
{
var dist = SquareVectorDistance(targets._entities[i].position(), this.rallyPoint);
if (dist < maxDist)
{
maxDist = dist;
index = i;
}
}
this.target = targets._entities[index];
this.targetPos = this.target.position();
}
// when we have a target, we path to it.
this.path = this.pathFinder.getPath(this.rallyPoint,this.targetPos, 3, 3);//,300000,gameState);
if (this.path === undefined) {
}
// when we have a target, we path to it.
// I'd like a good high width sampling first.
// Thus I will not do everything at once.
// It will probably carry over a few turns but that's no issue.
if (this.path === undefined)
this.path = this.pathFinder.getPath(this.rallyPoint,this.targetPos, this.pathSampling, this.pathWidth,250,gameState);
else if (this.path === "toBeContinued")
this.path = this.pathFinder.continuePath(gameState);
if (this.path === undefined) {
if (this.pathWidth == 8)
{
this.pathWidth = 2;
delete this.path;
} else {
delete this.pathFinder;
return 3; // no path.
} else if (this.path[1] === true) {
// okay so we need a ship.
// Basically we'll add it as a new class to train compulsorily, and we'll recompute our path.
debug ("We need a ship.");
if (!gameState.ai.waterMap)
{
debug ("This is actually a water map.");
gameState.ai.waterMap = true;
}
this.unitStat["TransportShip"] = { "priority" : 1.1, "minSize" : 2, "targetSize" : 2, "batchSize" : 1, "classes" : ["Warship"],
"interests" : [ ["strength",1], ["cost",1] ] ,"templates" : [] };
if (type === "superSized") {
this.unitStat["TransportShip"]["minSize"] = 4;
this.unitStat["TransportShip"]["targetSize"] = 4;
}
var Unit = this.unitStat["TransportShip"];
var filter = Filters.and(Filters.byClassesAnd(Unit["classes"]),Filters.and(Filters.byMetadata(PlayerID, "plan",this.name),Filters.byOwner(PlayerID)));
this.unit["TransportShip"] = gameState.getOwnEntities().filter(filter);
this.unit["TransportShip"].registerUpdates();
this.unit["TransportShip"].length;
this.buildOrder.push([0, Unit["classes"], this.unit["TransportShip"], Unit, "TransportShip"]);
this.needsShip = true;
}
} else if (this.path === "toBeContinued") {
// carry on.
} else if (this.path[1] === true && this.pathWidth == 2) {
// okay so we need a ship.
// Basically we'll add it as a new class to train compulsorily, and we'll recompute our path.
if (!gameState.ai.waterMap)
{
debug ("This is actually a water map.");
gameState.ai.waterMap = true;
}
debug ("We need a ship.");
this.unitStat["TransportShip"] = { "priority" : 1.1, "minSize" : 2, "targetSize" : 2, "batchSize" : 1, "classes" : ["Warship"],
"interests" : [ ["strength",1], ["cost",1] ] ,"templates" : [] };
if (type === "superSized") {
this.unitStat["TransportShip"]["minSize"] = 4;
this.unitStat["TransportShip"]["targetSize"] = 4;
}
var Unit = this.unitStat["TransportShip"];
var filter = Filters.and(Filters.byClassesAnd(Unit["classes"]),Filters.and(Filters.byMetadata(PlayerID, "plan",this.name),Filters.byOwner(PlayerID)));
this.unit["TransportShip"] = gameState.getOwnEntities().filter(filter);
this.unit["TransportShip"].registerUpdates();
this.unit["TransportShip"].length;
this.buildOrder.push([0, Unit["classes"], this.unit["TransportShip"], Unit, "TransportShip"]);
this.needsShip = true;
this.pathWidth = 3;
this.pathSampling = 3;
this.path = this.path[0].reverse();
delete this.pathFinder;
} else if (this.path[1] === true && this.pathWidth == 8) {
// retry with a smaller pathwidth:
this.pathWidth = 2;
delete this.path;
} else {
this.path = this.path[0].reverse();
delete this.pathFinder;
} else if (targets.length == 0 ) {
gameState.ai.gameFinished = true;
debug ("I do not have any target. So I'll just assume I won the game.");
return 0;
}
}
Engine.ProfileStart("Update Preparation");
// special case: if we're reached max pop, and we can start the plan, start it.
@ -737,7 +763,7 @@ CityAttack.prototype.update = function(gameState, militaryManager, events){
return undefined; // should spawn an error.
// basically haven't moved an inch: very likely stuck)
if (SquareVectorDistance(this.position, this.position10TurnsAgo) < 10 && this.path.length > 0 && gameState.ai.playedTurn % 20 === 0) {
if (SquareVectorDistance(this.position, this.position10TurnsAgo) < 10 && this.path.length > 0 && gameState.ai.playedTurn % 10 === 0) {
// check for stuck siege units
var sieges = this.unitCollection.filter(Filters.byClass("Siege"));
@ -753,7 +779,7 @@ CityAttack.prototype.update = function(gameState, militaryManager, events){
if (farthestEnt !== -1)
farthestEnt.destroy();
}
if (gameState.ai.playedTurn % 20 === 0)
if (gameState.ai.playedTurn % 10 === 0)
this.position10TurnsAgo = this.position;
if (this.lastPosition && SquareVectorDistance(this.position, this.lastPosition) < 20 && this.path.length > 0) {
@ -778,11 +804,11 @@ CityAttack.prototype.update = function(gameState, militaryManager, events){
}
// check if our land units are close enough from the next waypoint.
if (SquareVectorDistance(this.unitCollection.filter(Filters.not(Filters.byClass("Warship"))).getCentrePosition(), this.targetPos) < 8000 ||
SquareVectorDistance(this.unitCollection.filter(Filters.not(Filters.byClass("Warship"))).getCentrePosition(), this.path[0][0]) < 600) {
if (SquareVectorDistance(this.unitCollection.filter(Filters.not(Filters.byClass("Warship"))).getCentrePosition(), this.targetPos) < 7500 ||
SquareVectorDistance(this.unitCollection.filter(Filters.not(Filters.byClass("Warship"))).getCentrePosition(), this.path[0][0]) < 850) {
if (this.unitCollection.filter(Filters.byClass("Siege")).length !== 0
&& SquareVectorDistance(this.unitCollection.filter(Filters.not(Filters.byClass("Warship"))).getCentrePosition(), this.targetPos) > 8000
&& SquareVectorDistance(this.unitCollection.filter(Filters.byClass("Siege")).getCentrePosition(), this.path[0][0]) > 600)
&& SquareVectorDistance(this.unitCollection.filter(Filters.not(Filters.byClass("Warship"))).getCentrePosition(), this.targetPos) > 7500
&& SquareVectorDistance(this.unitCollection.filter(Filters.byClass("Siege")).getCentrePosition(), this.path[0][0]) > 850)
{
} else {
// okay so here basically two cases. The first one is "we need a boat at this point".

View File

@ -439,6 +439,9 @@ Defence.prototype.defendFromEnemies = function(gameState, events, militaryManage
if (nonDefenders.length*2.0 < newEnemies.length && this.nbAttackers > 5)
gameState.setDefcon(1);
if (gameState.defcon() > 3)
militaryManager.unpauseAllPlans(gameState);
if ( (nonDefenders.length + this.nbDefenders > newEnemies.length + this.nbAttackers)
|| this.nbDefenders + nonDefenders.length < 4)
{
@ -496,6 +499,8 @@ Defence.prototype.defendFromEnemies = function(gameState, events, militaryManage
// successfully sorted
defs.forEach(function (defender) { //}){
if (defender.getMetadata(PlayerID, "plan") != undefined && gameState.defcon() < 3)
militaryManager.pausePlan(gameState, defender.getMetadata(PlayerID, "plan"));
//debug ("Against " +enemy.id() + " Assigning " + defender.id());
if (defender.getMetadata(PlayerID, "role") == "worker" || defender.getMetadata(PlayerID, "role") == "attack")
defender.setMetadata(PlayerID, "formerrole", defender.getMetadata(PlayerID, "role"));
@ -535,8 +540,10 @@ Defence.prototype.defendFromEnemies = function(gameState, events, militaryManage
break;
}
}
if (!garrisoned)
if (!garrisoned) {
ent.flee(enemy);
ent.setMetadata(PlayerID,"fleeing", gameState.getTimeElapsed());
}
}
}
});
@ -578,6 +585,7 @@ Defence.prototype.MessageProcess = function(gameState,events, militaryManager) {
ourUnit.attack(e.msg.attacker);
else {
ourUnit.flee(attacker);
ourUnit.setMetadata(PlayerID,"fleeing", gameState.getTimeElapsed());
}
}
// anyway we'll register the animal as dangerous, and attack it.
@ -653,6 +661,7 @@ Defence.prototype.MessageProcess = function(gameState,events, militaryManager) {
// Right now we'll flee from the attacker.
ourUnit.flee(attacker);
ourUnit.setMetadata(PlayerID,"fleeing", gameState.getTimeElapsed());
} else {
// It's a soldier. Right now we'll retaliate
// TODO: check for stronger units against this type, check for fleeing options, etc.

View File

@ -52,24 +52,27 @@ EconomyManager.prototype.init = function(gameState, events){
if (ents.length > 0)
{
gameState.getResourceSupplies("food").forEach( function (ent) {
if (SquareVectorDistance(ent.position(), ents[0].position()) < 5000)
if (ent.resourceSupplyType().generic === "treasure" && SquareVectorDistance(ent.position(), ents[0].position()) < 5000)
availableRess += ent.resourceSupplyMax();
});
gameState.getResourceSupplies("stone").forEach( function (ent) {
if (SquareVectorDistance(ent.position(), ents[0].position()) < 5000)
if (ent.resourceSupplyType().generic === "treasure" && SquareVectorDistance(ent.position(), ents[0].position()) < 5000)
availableRess += ent.resourceSupplyMax();
});
gameState.getResourceSupplies("metal").forEach( function (ent) {
if (SquareVectorDistance(ent.position(), ents[0].position()) < 5000)
if (ent.resourceSupplyType().generic === "treasure" && SquareVectorDistance(ent.position(), ents[0].position()) < 5000)
availableRess += ent.resourceSupplyMax();
});
gameState.getResourceSupplies("wood").forEach( function (ent) {
if (SquareVectorDistance(ent.position(), ents[0].position()) < 5000)
if (ent.resourceSupplyType().generic === "treasure" && SquareVectorDistance(ent.position(), ents[0].position()) < 5000)
availableRess += ent.resourceSupplyMax();
});
}
if (availableRess > 2000)
{
debug ("Assuming a fast start");
this.fastStart = true;
}
// initialize once all the resource maps.
this.updateResourceMaps(gameState, events);
this.updateResourceConcentrations(gameState,"food");
@ -225,6 +228,10 @@ EconomyManager.prototype.pickMostNeededResources = function(gameState) {
// Prefer fewer gatherers (divided by weight)
var va = numGatherers[a] / (self.gatherWeights[a]+1);
var vb = numGatherers[b] / (self.gatherWeights[b]+1);
if (self.gatherWeights[a] === 0)
va = 10000;
if (self.gatherWeights[b] === 0)
vb = 10000;
return va-vb;
});
return types;
@ -235,7 +242,7 @@ EconomyManager.prototype.reassignRolelessUnits = function(gameState) {
var roleless = gameState.getOwnEntitiesByRole(undefined);
roleless.forEach(function(ent) {
if (ent.hasClass("Worker")){
if ((ent.hasClass("Worker") || ent.hasClass("CitizenSoldier")) && !ent.getMetadata(PlayerID, "stoppedHunting")) {
ent.setMetadata(PlayerID, "role", "worker");
}
});
@ -292,10 +299,14 @@ EconomyManager.prototype.reassignIdleWorkers = function(gameState) {
if (ent.position() === undefined){
return;
}
var types = self.pickMostNeededResources(gameState);
ent.setMetadata(PlayerID, "subrole", "gatherer");
ent.setMetadata(PlayerID, "gather-type", types[0]);
if (ent.hasClass("Worker")) {
var types = self.pickMostNeededResources(gameState);
ent.setMetadata(PlayerID, "subrole", "gatherer");
ent.setMetadata(PlayerID, "gather-type", types[0]);
} else {
ent.setMetadata(PlayerID, "subrole", "hunter");
}
});
}
};
@ -319,7 +330,7 @@ EconomyManager.prototype.assignToFoundations = function(gameState, noRepair) {
if (!foundations.length && !damagedBuildings.length){
return;
}
var workers = gameState.getOwnEntitiesByRole("worker");
var workers = gameState.getOwnEntitiesByRole("worker").filter(Filters.not(Filters.byClass("Cavalry")));
var builderWorkers = this.workersBySubrole(gameState, "builder");
var addedWorkers = 0;
@ -336,7 +347,10 @@ EconomyManager.prototype.assignToFoundations = function(gameState, noRepair) {
if (builderWorkers.length + addedWorkers < this.targetNumBuilders*Math.min(4.0,gameState.getTimeElapsed()/60000)) {
var nonBuilderWorkers = workers.filter(function(ent) { return (ent.getMetadata(PlayerID, "subrole") !== "builder" && ent.getMetadata(PlayerID, "gather-type") !== "food" && ent.position() !== undefined); });
var nearestNonBuilders = null;
if (target.hasClass("CivCentre") || (target.hasClass("House") && gameState.getTimeElapsed() < 300000) )
// if it's a civ centre, a house in the start of the game, or the first barracks, double the amount of workers.
if (target.hasClass("CivCentre") || (target.hasClass("House") && gameState.getTimeElapsed() < 300000)
|| (gameState.countEntitiesByType(gameState.applyCiv(gameState.ai.modules.military.bModerate[0])) === 0
&& target._templateName == gameState.applyCiv(gameState.ai.modules.military.bModerate[0])))
nearestNonBuilders = nonBuilderWorkers.filterNearest(target.position(), this.targetNumBuilders*2.0 - assigned);
else
nearestNonBuilders = nonBuilderWorkers.filterNearest(target.position(), this.targetNumBuilders - assigned);
@ -902,7 +916,7 @@ EconomyManager.prototype.buildTemple = function(gameState, queues){
};
EconomyManager.prototype.buildMarket = function(gameState, queues){
if (gameState.getTimeElapsed() > this.marketStartTime && gameState.currentPhase() >= 2 ) {
if (this.numWorkers > 50 && gameState.currentPhase() >= 2 ) {
if (queues.economicBuilding.countTotalQueuedUnitsWithClass("BarterMarket") === 0 &&
gameState.countEntitiesAndQueuedByType(gameState.applyCiv("structures/{civ}_market")) === 0){
//only ever build one mill/CC/market at a time
@ -985,7 +999,7 @@ EconomyManager.prototype.buildDropsites = function(gameState, queues){
};
// build more houses if needed.
EconomyManager.prototype.buildMoreHouses = function(gameState, queues) {
if (gameState.getTimeElapsed() < 25000)
if (gameState.getTimeElapsed() < 15000)
return;
// temporary 'remaining population space' based check, need to do
@ -996,7 +1010,7 @@ EconomyManager.prototype.buildMoreHouses = function(gameState, queues) {
var numConstructing = gameState.countEntitiesByType(gameState.applyCiv("foundation|structures/{civ}_house"));
var numPlanned = queues.house.totalLength();
if (gameState.getTimeElapsed() < 300000 && numConstructing + numPlanned !== 0)
if (gameState.getTimeElapsed() < 120000 && numConstructing + numPlanned !== 0)
return;
var additional = 0;
if (gameState.civ() == "gaul" || gameState.civ() == "brit" || gameState.civ() == "iber")
@ -1113,6 +1127,7 @@ EconomyManager.prototype.update = function(gameState, queues, events) {
Engine.ProfileStop();
return;
}
this.numWorkers = gameState.getOwnEntitiesByRole("worker").filter(Filters.not(Filters.byHasMetadata(PlayerID,"plan"))).length;
// this function also deals with a few things that are number-of-workers related
Engine.ProfileStart("Train workers and build farms, houses. Research techs.");
@ -1142,8 +1157,19 @@ EconomyManager.prototype.update = function(gameState, queues, events) {
this.femaleRatio = Config.Economy.femaleRatio * 0.9; // might expect a rush and build some CS to counter.
else
this.femaleRatio = Config.Economy.femaleRatio;
if (gameState.getTimeElapsed() > 600000 && this.numWorkers < 50)
{
gameState.ai.queueManager.changePriority("villager", 80);
gameState.ai.queueManager.changePriority("citizenSoldier", 70);
} else if (gameState.getTimeElapsed() > 600000 && this.numWorkers > 80
&& gameState.ai.queueManager.priorities["villager"] == 80)
{
gameState.ai.queueManager.changePriority("villager", Config.priorities.villager);
gameState.ai.queueManager.changePriority("citizenSoldier", Config.priorities.citizenSoldier);
}
if (this.baseNeed["metal"] === 0 && gameState.getTimeElapsed() > 540*1000) {
if (this.baseNeed["metal"] === 0 && gameState.currentPhase() !== 1) {
this.baseNeed["food"] = 140;
this.baseNeed["wood"] = 100;
this.baseNeed["stone"] = 50;
@ -1190,6 +1216,8 @@ EconomyManager.prototype.update = function(gameState, queues, events) {
Engine.ProfileStart("Swap Workers");
var gathererGroups = {};
gameState.getOwnEntitiesByRole("worker").forEach(function(ent){
if (ent.hasClass("Cavalry"))
return;
var key = uneval(ent.resourceGatherRates());
if (!gathererGroups[key]){
gathererGroups[key] = {"food": [], "wood": [], "metal": [], "stone": []};

View File

@ -4,7 +4,7 @@
var MilitaryAttackManager = function() {
this.fortressStartTime = Config.Military.fortressStartTime * 1000;
this.fortressStartTime = 0;
this.fortressLapseTime = Config.Military.fortressLapseTime * 1000;
this.defenceBuildingTime = Config.Military.defenceBuildingTime * 1000;
this.advancedMilitaryStartTime = Config.Military.advancedMilitaryStartTime * 1000;
@ -382,6 +382,9 @@ MilitaryAttackManager.prototype.measureEnemyStrength = function(gameState){
// Adds towers to the defenceBuilding queue
MilitaryAttackManager.prototype.buildDefences = function(gameState, queues){
var workersNumber = gameState.getOwnEntitiesByRole("worker").filter(Filters.not(Filters.byHasMetadata(PlayerID,"plan"))).length;
if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv('structures/{civ}_defense_tower'))
+ queues.defenceBuilding.totalLength() < gameState.getEntityLimits()["DefenseTower"] && queues.defenceBuilding.totalLength() < 4
&& gameState.currentPhase() > 1 && queues.defenceBuilding.totalLength() < 3) {
@ -401,16 +404,13 @@ MilitaryAttackManager.prototype.buildDefences = function(gameState, queues){
numFortresses += gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bFort[i]));
}
if (numFortresses + queues.defenceBuilding.totalLength() < 2 && gameState.currentPhase() > 2)
if (queues.defenceBuilding.totalLength() < 1 && gameState.currentPhase() > 2)
{
if (gameState.getTimeElapsed() > this.fortressStartTime + numFortresses * this.fortressLapseTime){
if (gameState.ai.pathsToMe && gameState.ai.pathsToMe.length > 0){
var position = gameState.ai.pathsToMe.shift();
// TODO: pick a fort randomly from the list.
queues.defenceBuilding.addItem(new BuildingConstructionPlan(gameState, this.bFort[0], position));
}else{
queues.defenceBuilding.addItem(new BuildingConstructionPlan(gameState, this.bFort[0]));
}
if (workersNumber >= 95 && gameState.getTimeElapsed() > numFortresses * this.fortressLapseTime + this.fortressStartTime)
{
if (!this.fortressStartTime)
this.fortressStartTime = gameState.getTimeElapsed();
queues.defenceBuilding.addItem(new BuildingConstructionPlan(gameState, this.bFort[0]));
}
}
};
@ -419,24 +419,28 @@ MilitaryAttackManager.prototype.constructTrainingBuildings = function(gameState,
// Build more military buildings
// TODO: make military building better
Engine.ProfileStart("Build buildings");
if (gameState.countEntitiesByType(gameState.applyCiv("units/{civ}_support_female_citizen")) > 25 && (gameState.currentPhase() > 1 || gameState.isResearching("phase_town"))) {
var workersNumber = gameState.getOwnEntitiesByRole("worker").filter(Filters.not(Filters.byHasMetadata(PlayerID, "plan"))).length;
if (workersNumber > 40 && (gameState.currentPhase() > 1 || gameState.isResearching("phase_town"))) {
if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bModerate[0])) + queues.militaryBuilding.totalLength() < 1) {
debug ("Trying to build barracks");
queues.militaryBuilding.addItem(new BuildingConstructionPlan(gameState, this.bModerate[0]));
}
}
if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bModerate[0])) < 2 && gameState.getTimeElapsed() > 15*60*1000)
if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bModerate[0])) < 2 && workersNumber > 85)
if (queues.militaryBuilding.totalLength() < 1)
queues.militaryBuilding.addItem(new BuildingConstructionPlan(gameState, this.bModerate[0]));
if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bModerate[0])) < 3 && gameState.getTimeElapsed() > 23*60*1000 &&
if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bModerate[0])) < 3 && workersNumber > 125 &&
(gameState.civ() == "gaul" || gameState.civ() == "brit" || gameState.civ() == "iber"))
if (queues.militaryBuilding.totalLength() < 1)
queues.militaryBuilding.addItem(new BuildingConstructionPlan(gameState, this.bModerate[0]));
//build advanced military buildings
if (gameState.getTimeElapsed() > this.advancedMilitaryStartTime && gameState.currentPhase() > 2){
if (workersNumber > 75 && gameState.currentPhase() > 2){
if (queues.militaryBuilding.totalLength() === 0){
var inConst = 0;
for (var i in this.bAdvanced)
@ -450,7 +454,7 @@ MilitaryAttackManager.prototype.constructTrainingBuildings = function(gameState,
}
}
if (gameState.civ() !== "gaul" && gameState.civ() !== "brit" && gameState.civ() !== "iber" &&
gameState.getTimeElapsed() > this.advancedMilitaryStartTime && gameState.currentPhase() > 2 && gameState.getTimeElapsed() > 25*60*1000)
workersNumber > 130 && gameState.currentPhase() > 2)
{
var Const = 0;
for (var i in this.bAdvanced)
@ -600,19 +604,35 @@ MilitaryAttackManager.prototype.update = function(gameState, queues, events) {
debug ("Military Manager: " +attack.getType() +" plan " +attack.getName() +" aborted.");
if (updateStep === 3) {
this.attackPlansEncounteredWater = true;
debug("I dare not wet my feet");
debug("No attack path found. Aborting.");
}
attack.Abort(gameState, this);
//this.abortedAttacks.push(attack);
this.upcomingAttacks[attackType].splice(i--,1);
} else if (updateStep === 2) {
var chatText = "I am launching an attack against " + gameState.sharedScript.playersData[attack.targetPlayer].name;
if (Math.random() < 0.2)
chatText = "Attacking " + gameState.sharedScript.playersData[attack.targetPlayer].name;
else if (Math.random() < 0.3)
chatText = "Starting to attack " + gameState.sharedScript.playersData[attack.targetPlayer].name;
else if (Math.random() < 0.3)
chatText = "I'm starting an attack against " + gameState.sharedScript.playersData[attack.targetPlayer].name;
gameState.ai.chatTeam(chatText);
debug ("Military Manager: Starting " +attack.getType() +" plan " +attack.getName());
attack.StartAttack(gameState,this);
this.startedAttacks[attackType].push(attack);
this.upcomingAttacks[attackType].splice(i--,1);
}
} else {
var chatText = "I am launching an attack against " + gameState.sharedScript.playersData[attack.targetPlayer].name;
if (Math.random() < 0.2)
chatText = "Attacking " + gameState.sharedScript.playersData[attack.targetPlayer].name;
else if (Math.random() < 0.3)
chatText = "Starting to attack " + gameState.sharedScript.playersData[attack.targetPlayer].name;
else if (Math.random() < 0.3)
chatText = "I'm starting an attack against " + gameState.sharedScript.playersData[attack.targetPlayer].name;
gameState.ai.chatTeam(chatText);
debug ("Military Manager: Starting " +attack.getType() +" plan " +attack.getName());
this.startedAttacks[attackType].push(attack);
this.upcomingAttacks[attackType].splice(i--,1);
@ -652,25 +672,30 @@ MilitaryAttackManager.prototype.update = function(gameState, queues, events) {
// creating plans after updating because an aborted plan might be reused in that case.
if (gameState.countEntitiesByType(gameState.applyCiv(this.bModerate[0])) >= 1 && !this.attackPlansEncounteredWater
&& gameState.getTimeElapsed() > this.attackPlansStartTime) {
if (this.upcomingAttacks["CityAttack"].length == 0 && (gameState.getTimeElapsed() < 25*60000 || Config.difficulty < 2)) {
var Lalala = new CityAttack(gameState, this,this.TotalAttackNumber, -1);
if (Lalala.failed)
{
this.attackPlansEncounteredWater = true; // hack
} else {
debug ("Military Manager: Creating the plan " +this.TotalAttackNumber);
this.TotalAttackNumber++;
this.upcomingAttacks["CityAttack"].push(Lalala);
}
} else if (this.upcomingAttacks["CityAttack"].length == 0 && Config.difficulty !== 0) {
var Lalala = new CityAttack(gameState, this,this.TotalAttackNumber, -1, "superSized");
if (Lalala.failed)
{
this.attackPlansEncounteredWater = true; // hack
} else {
debug ("Military Manager: Creating the super sized plan " +this.TotalAttackNumber);
this.TotalAttackNumber++;
this.upcomingAttacks["CityAttack"].push(Lalala);
if (gameState.countEntitiesByType(gameState.applyCiv("structures/{civ}_dock")) === 0 && gameState.ai.waterMap)
{
// wait till we get a dock.
} else {
if (this.upcomingAttacks["CityAttack"].length == 0 && (gameState.getTimeElapsed() < 25*60000 || Config.difficulty < 2)) {
var Lalala = new CityAttack(gameState, this,this.TotalAttackNumber, -1);
if (Lalala.failed)
{
this.attackPlansEncounteredWater = true; // hack
} else {
debug ("Military Manager: Creating the plan " +this.TotalAttackNumber);
this.TotalAttackNumber++;
this.upcomingAttacks["CityAttack"].push(Lalala);
}
} else if (this.upcomingAttacks["CityAttack"].length == 0 && Config.difficulty !== 0) {
var Lalala = new CityAttack(gameState, this,this.TotalAttackNumber, -1, "superSized");
if (Lalala.failed)
{
this.attackPlansEncounteredWater = true; // hack
} else {
debug ("Military Manager: Creating the super sized plan " +this.TotalAttackNumber);
this.TotalAttackNumber++;
this.upcomingAttacks["CityAttack"].push(Lalala);
}
}
}
}

View File

@ -115,6 +115,9 @@ BuildingConstructionPlan.prototype.findGoodPosition = function(gameState) {
} else if (template.genericName() == "House") {
friendlyTiles.addInfluence(x, z, Math.ceil(infl/2.0),infl); // houses are farther away from other buildings but houses
friendlyTiles.addInfluence(x, z, Math.ceil(infl/4.0),-infl/2.0); // houses are farther away from other buildings but houses
} else if (template.hasClass("GarrisonFortress"))
{
friendlyTiles.addInfluence(x, z, -infl/3.0, -infl/3.0);
} else if (ent.genericName() != "House") // houses have no influence on other buildings
{
friendlyTiles.addInfluence(x, z, infl);
@ -142,15 +145,15 @@ BuildingConstructionPlan.prototype.findGoodPosition = function(gameState) {
// also not for fields who can be stacked quite a bit
var radius = 0;
if (template.genericName() == "Field")
radius = Math.ceil(template.obstructionRadius() / cellSize) - 0.7;
radius = Math.ceil(template.obstructionRadius() / cellSize) - 0.4;
else if (template.buildCategory() === "Dock")
radius = 1;//Math.floor(template.obstructionRadius() / cellSize);
else if (template.genericName() != "House" && !template.hasClass("DropsiteWood") && !template.hasClass("DropsiteStone") && !template.hasClass("DropsiteMetal"))
radius = Math.ceil(template.obstructionRadius() / cellSize + 0.5);
else if (gameState.civ() === "iber" || gameState.civ() === "gaul" || gameState.civ() === "brit")
radius = Math.ceil(template.obstructionRadius() / cellSize - 0.5);
else
radius = Math.ceil(template.obstructionRadius() / cellSize);
else
radius = Math.ceil(template.obstructionRadius() / cellSize + 0.2);
// further contract cause walls
// Note: I'm currently destroying them so that doesn't matter.
@ -183,9 +186,6 @@ BuildingConstructionPlan.prototype.findGoodPosition = function(gameState) {
// default angle
var angle = 3*Math.PI/4;
if (template.genericName() == "House")
angle = Math.PI;
return {
"x" : x,
"z" : z,

View File

@ -67,7 +67,7 @@ QBotAI.prototype.InitShared = function(gameState, sharedScript) {
// First path has a sampling of 3, which ensures we'll get at least one path even on Acropolis. The others are 6 so might fail.
var pos = [this.pathInfo.mkeyPos[0] + 200*Math.cos(this.pathInfo.angle),this.pathInfo.mkeyPos[1] + 200*Math.sin(this.pathInfo.angle)];
var path = this.pathFinder.getPath(this.pathInfo.ekeyPos, pos, 3, 3);// uncomment for debug:*/, 300000, gameState);
var path = this.pathFinder.getPath(this.pathInfo.ekeyPos, pos, 2, 2);// uncomment for debug:*/, 300000, gameState);
//Engine.DumpImage("initialPath" + PlayerID + ".png", this.pathFinder.TotorMap.map, this.pathFinder.TotorMap.width,this.pathFinder.TotorMap.height,255);
@ -173,7 +173,7 @@ QBotAI.prototype.OnUpdate = function(sharedScript) {
if (this.pathInfo !== undefined)
{
var pos = [this.pathInfo.mkeyPos[0] + 200*Math.cos(this.pathInfo.angle),this.pathInfo.mkeyPos[1] + 200*Math.sin(this.pathInfo.angle)];
var path = this.pathFinder.getPath(this.pathInfo.ekeyPos, pos, 6, 6);// uncomment for debug:*/, 300000, gameState);
var path = this.pathFinder.getPath(this.pathInfo.ekeyPos, pos, 6, 5);// uncomment for debug:*/, 300000, gameState);
if (path !== undefined && path[1] !== undefined && path[1] == false) {
// path is viable and doesn't require boating.
// blackzone the last two waypoints.
@ -228,14 +228,25 @@ QBotAI.prototype.OnUpdate = function(sharedScript) {
if (this.playedTurn % 80 === 0)
{
// some debug informations about units.
var units = gameState.getOwnEntities().filter(Filters.byClass("Unit"));
var units = gameState.getOwnEntities();
for (var i in units._entities)
{
var ent = units._entities[i];
debug ("Unit " + ent.id() + " is a " + ent._templateName);
debug ("It is a " + uneval(ent.getMetadata(PlayerID, "role")) + ", "+ uneval(ent.getMetadata(PlayerID, "subrole")));
if (ent.getMetadata(PlayerID, "plan") != undefined)
debug ("it is part of the plan " + uneval(ent.getMetadata(PlayerID, "plan")));
if (sharedScript._entityMetadata[PlayerID][ent.id()])
{
var metadata = sharedScript._entityMetadata[PlayerID][ent.id()];
for (j in metadata)
{
debug ("Metadata " + j);
if (typeof(metadata[j]) == "object")
warn ("Object");
else if (typeof(metadata[j]) == undefined)
warn ("Undefined");
else
warn(metadata[j]);
}
}
}
}*/

View File

@ -333,4 +333,16 @@ QueueManager.prototype.removeQueue = function(queueName) {
this.queueArrays.sort(function (a,b) { return (self.priorities[b[0]] - self.priorities[a[0]]) });
}
}
QueueManager.prototype.changePriority = function(queueName, newPriority) {
var self = this;
if (this.queues[queueName] !== undefined)
this.priorities[queueName] = newPriority;
this.queueArrays = [];
for (var p in this.queues) {
this.account[p] = 0;
this.accounts[p] = new Resources();
this.queueArrays.push([p,this.queues[p]]);
}
this.queueArrays.sort(function (a,b) { return (self.priorities[b[0]] - self.priorities[a[0]]) });
}

View File

@ -12,12 +12,14 @@ Worker.prototype.update = function(gameState) {
var subrole = this.ent.getMetadata(PlayerID, "subrole");
if (!this.ent.position()){
if (!this.ent.position() || (this.ent.getMetadata(PlayerID,"fleeing") && gameState.getTimeElapsed() - this.ent.getMetadata(PlayerID,"fleeing") < 8000)){
// If the worker has no position then no work can be done
return;
}
if (this.ent.getMetadata(PlayerID,"fleeing"))
this.ent.setMetadata(PlayerID,"fleeing", undefined);
if (subrole === "gatherer") {
if (this.ent.unitAIState().split(".")[1] !== "GATHER" && this.ent.unitAIState().split(".")[1] !== "COMBAT" && this.ent.unitAIState().split(".")[1] !== "RETURNRESOURCE"){
// TODO: handle combat for hunting animals
@ -92,6 +94,12 @@ Worker.prototype.update = function(gameState) {
}
this.startApproachingResourceTime = gameState.getTimeElapsed();
//Engine.PostCommand({"type": "set-shading-color", "entities": [this.ent.id()], "rgb": [0,10,0]});
} else if(subrole === "hunter") {
if (!this.ent.resourceCarrying() || this.ent.resourceCarrying().length === 0){
Engine.ProfileStart("Start Hunting");
this.startHunting(gameState);
Engine.ProfileStop();
}
} else {
this.startApproachingResourceTime = gameState.getTimeElapsed();
}
@ -306,7 +314,10 @@ Worker.prototype.startGathering = function(gameState){
//debug ("noposition");
return;
}
if (supply.isUnhuntable())
return;
if (supply.getMetadata(PlayerID, "inaccessible") === true) {
//debug ("inaccessible");
return;
@ -323,12 +334,6 @@ Worker.prototype.startGathering = function(gameState){
//debug ("enemy");
return;
}
var territoryOwner = Map.createTerritoryMap(gameState).getOwner(supply.position());
if (territoryOwner != PlayerID && territoryOwner != 0) {
dist *= 3.0;
//return;
}
// quickscope accessbility check.
if (!gameState.ai.accessibility.pathAvailable(gameState, ent.position(), supply.position(), true)) {
@ -355,6 +360,12 @@ Worker.prototype.startGathering = function(gameState){
dist += 4*SquareVectorDistance(supply.position(), nearestDropsite.position());
dist /= 5.0;
}
var territoryOwner = Map.createTerritoryMap(gameState).getOwner(supply.position());
if (territoryOwner != PlayerID && territoryOwner != 0) {
dist *= 3.0;
//return;
}
// Go for treasure as a priority
if (dist < 40000 && supply.resourceSupplyType().generic == "treasure"){
@ -452,6 +463,95 @@ Worker.prototype.returnResources = function(gameState){
return true;
};
Worker.prototype.startHunting = function(gameState){
var ent = this.ent;
if (!ent.position() || ent.getMetadata(PlayerID, "stoppedHunting"))
return;
// So here we're doing it basic. We check what we can hunt, we hunt it. No fancies.
var resources = gameState.getResourceSupplies("food");
if (resources.length === 0){
debug("No food found to hunt!");
return;
}
var supplies = [];
var nearestSupplyDist = Math.min();
var nearestSupply = undefined;
resources.forEach(function(supply) { //}){
if (!supply.position())
return;
if (supply.getMetadata(PlayerID, "inaccessible") === true)
return;
//if (supply.isFull === true)
// return;
if (!supply.hasClass("Animal"))
return;
if (supply.walkSpeed() + 0.5 >= ent.walkSpeed())
return;
// measure the distance to the resource
var dist = SquareVectorDistance(supply.position(), ent.position());
var territoryOwner = Map.createTerritoryMap(gameState).getOwner(supply.position());
if (territoryOwner != PlayerID && territoryOwner != 0) {
dist *= 3.0;
}
// quickscope accessbility check
if (!gameState.ai.accessibility.pathAvailable(gameState, ent.position(), supply.position(), true))
return;
if (dist < nearestSupplyDist) {
nearestSupplyDist = dist;
nearestSupply = supply;
}
});
if (nearestSupply) {
var pos = nearestSupply.position();
var nearestDropsite = 0;
var minDropsiteDist = 1000000;
// find a fitting dropsites in case we haven't already.
gameState.getOwnDropsites("food").forEach(function (dropsite){ //}){
if (dropsite.position()){
var dist = SquareVectorDistance(pos, dropsite.position());
if (dist < minDropsiteDist){
minDropsiteDist = dist;
nearestDropsite = dropsite;
}
}
});
if (!nearestDropsite)
{
ent.setMetadata(PlayerID, "stoppedHunting", true);
ent.setMetadata(PlayerID, "role", undefined);
debug ("No dropsite for hunting food");
return;
}
if (minDropsiteDist > 45000) {
ent.setMetadata(PlayerID, "stoppedHunting", true);
ent.setMetadata(PlayerID, "role", undefined);
} else {
ent.gather(nearestSupply);
ent.setMetadata(PlayerID, "target-foundation", undefined);
}
} else {
ent.setMetadata(PlayerID, "stoppedHunting", true);
ent.setMetadata(PlayerID, "role", undefined);
debug("No food found for hunting! (2)");
}
};
Worker.prototype.getResourceType = function(type){
if (!type || !type.generic){
return undefined;

View File

@ -610,6 +610,14 @@ public:
if (!m_ScriptInterface.CallFunction(m_Players[i]->m_Obj.get(), "Serialize", scriptData))
LOGERROR(L"AI script Serialize call failed");
serializer.ScriptVal("data", scriptData);
}
if (m_HasSharedComponent)
{
CScriptVal sharedData;
if (!m_ScriptInterface.CallFunction(m_SharedAIObj.get(), "Serialize", sharedData))
LOGERROR(L"AI shared script Serialize call failed");
serializer.ScriptVal("sharedData", sharedData);
}
}
@ -658,6 +666,13 @@ public:
LOGERROR(L"AI script Deserialize call failed");
}
TryLoadSharedComponent(false);
if (m_HasSharedComponent)
{
CScriptVal sharedData;
deserializer.ScriptVal("sharedData", sharedData);
if (!m_ScriptInterface.CallFunctionVoid(m_SharedAIObj.get(), "Deserialize", sharedData))
LOGERROR(L"AI shared script Deserialize call failed");
}
}
int getPlayerSize()