0ad/binaries/data/mods/public/simulation/components/AIInterface.js
wraitii c1e86161b5 AIs now properly receive aura and technology updates. Fixes #2377, Refs #1520 . Consequently reimplement repairing for AIs.
Fix a few style issues and a bug with the gatherer count.
Still need to fix the entity.js file to handle properly some things as
this uses raw templates values.
Cache the AIinterface in AIProxy.js, please report if this works
properly.

This was SVN commit r14588.
2014-01-16 20:32:44 +00:00

246 lines
7.9 KiB
JavaScript

function AIInterface() {}
AIInterface.prototype.Schema =
"<a:component type='system'/><empty/>";
AIInterface.prototype.Init = function()
{
this.events = {};
this.events["Create"] = [];
this.events["Destroy"] = [];
this.events["Attacked"] = [];
this.events["RangeUpdate"] = [];
this.events["ConstructionFinished"] = [];
this.events["TrainingFinished"] = [];
this.events["AIMetadata"] = [];
this.events["PlayerDefeated"] = [];
this.events["EntityRenamed"] = [];
this.events["OwnershipChanged"] = [];
this.changedEntities = {};
// cache for technology changes;
// this one is PlayerID->TemplateName->{StringForTheValue, ActualValue}
this.changedTemplateInfo = {};
// this is for auras and is EntityID->{StringForTheValue, ActualValue}
this.changedEntityTemplateInfo = {};
};
AIInterface.prototype.GetNonEntityRepresentation = function()
{
var cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
// Return the same game state as the GUI uses
var state = cmpGuiInterface.GetExtendedSimulationState(-1);
// Add some extra AI-specific data
state.events = {};
state.events["Create"] = this.events["Create"];
state.events["Destroy"] = this.events["Destroy"];
state.events["Attacked"] = this.events["Attacked"];
state.events["RangeUpdate"] = this.events["RangeUpdate"];
state.events["ConstructionFinished"] = this.events["ConstructionFinished"];
state.events["TrainingFinished"] = this.events["TrainingFinished"];
state.events["AIMetadata"] = this.events["AIMetadata"];
state.events["PlayerDefeated"] = this.events["PlayerDefeated"];
state.events["EntityRenamed"] = this.events["EntityRenamed"];
state.events["OwnershipChanged"] = this.events["OwnershipChanged"];
// Reset the event list for the next turn
this.events["Create"] = [];
this.events["Destroy"] = [];
this.events["Attacked"] = [];
this.events["RangeUpdate"] = [];
this.events["ConstructionFinished"] = [];
this.events["TrainingFinished"] = [];
this.events["AIMetadata"] = [];
this.events["PlayerDefeated"] = [];
this.events["EntityRenamed"] = [];
this.events["OwnershipChanged"] = [];
return state;
};
AIInterface.prototype.GetRepresentation = function()
{
var state = this.GetNonEntityRepresentation();
// Add entity representations
Engine.ProfileStart("proxy representations");
state.entities = {};
for (var id in this.changedEntities)
{
var aiProxy = Engine.QueryInterface(+id, IID_AIProxy);
if (aiProxy)
state.entities[id] = aiProxy.GetRepresentation();
}
this.changedEntities = {};
Engine.ProfileStop();
state.changedTemplateInfo = this.changedTemplateInfo;
this.changedTemplateInfo = {};
state.changedEntityTemplateInfo = this.changedEntityTemplateInfo;
this.changedEntityTemplateInfo = {};
return state;
};
// Intended to be called first, during the map initialization: no caching
AIInterface.prototype.GetFullRepresentation = function(flushEvents)
{
var state = this.GetNonEntityRepresentation();
if (flushEvents)
{
state.events["Create"] = [];
state.events["Destroy"] = [];
state.events["Attacked"] = [];
state.events["RangeUpdate"] = [];
state.events["ConstructionFinished"] = [];
state.events["TrainingFinished"] = [];
state.events["AIMetadata"] = [];
state.events["PlayerDefeated"] = [];
state.events["EntityRenamed"] = [];
state.events["OwnershipChanged"] = [];
}
// Add entity representations
Engine.ProfileStart("proxy representations");
state.entities = {};
// all entities are changed in the initial state.
for (var id in this.changedEntities)
{
var aiProxy = Engine.QueryInterface(+id, IID_AIProxy);
if (aiProxy)
state.entities[id] = aiProxy.GetFullRepresentation();
}
Engine.ProfileStop();
state.changedTemplateInfo = this.changedTemplateInfo;
this.changedTemplateInfo = {};
state.changedEntityTemplateInfo = this.changedEntityTemplateInfo;
this.changedEntityTemplateInfo = {};
return state;
};
AIInterface.prototype.ChangedEntity = function(ent)
{
this.changedEntities[ent] = 1;
};
// AIProxy sets up a load of event handlers to capture interesting things going on
// in the world, which we will report to AI. Handle those, and add a few more handlers
// for events that AIProxy won't capture.
AIInterface.prototype.PushEvent = function(type, msg)
{
if (this.events[type] === undefined)
warn("Tried to push unknown event type " + type +", please add it to AIInterface.js");
this.events[type].push(msg);
};
AIInterface.prototype.OnGlobalPlayerDefeated = function(msg)
{
this.events["PlayerDefeated"].push(msg);
};
AIInterface.prototype.OnGlobalEntityRenamed = function(msg)
{
this.events["EntityRenamed"].push(msg);
};
// When a new technology is researched, check which templates it affects,
// and send the updated values to the AI.
// this relies on the fact that any "value" in a technology can only ever change
// one template value, and that the naming is the same (with / in place of .)
// it's not incredibly fast but it's not incredibly slow.
AIInterface.prototype.OnTemplateModification = function(msg)
{
var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
if (!this.templates)
this.templates = cmpTemplateManager.FindAllTemplates(false);
for each (var value in msg.valueNames)
{
for (var o = 0; o < this.templates.length; ++o)
{
var tmp = this.templates[o];
var template = cmpTemplateManager.GetTemplateWithoutValidation(this.templates[o]);
// remove templates that we obviously don't care about.
if (!template || !template.Identity || ! template.Identity.Civ)
{
this.templates.splice(o--,1);
continue;
}
// let's get the base template value.
var strings = value.split("/");
var item = template;
var ended = true;
for (var i = 0; i < strings.length; ++i)
{
if (item !== undefined && item[strings[i]] !== undefined)
item = item[strings[i]];
else
ended = false;
}
if (!ended)
continue;
// item now contains the template value for this.
// check for numerals, they need to be handled properly
item = !isNaN(+item) ? +item : item;
var newValue = ApplyValueModificationsToTemplate(value, item, msg.player, template);
// round the value to 5th decimal or so.
newValue = !isNaN(+newValue) ? (Math.abs((+newValue) - Math.round(+newValue)) < 0.0001 ? Math.round(+newValue) : +newValue) : newValue;
if(item != newValue)
{
if (!this.changedTemplateInfo[msg.player])
this.changedTemplateInfo[msg.player] = {};
if (!this.changedTemplateInfo[msg.player][this.templates[o]])
this.changedTemplateInfo[msg.player][this.templates[o]] = [ { "variable" : value, "value" : newValue} ];
else
this.changedTemplateInfo[msg.player][this.templates[o]].push({ "variable" : value, "value" : newValue });
}
}
}
};
AIInterface.prototype.OnGlobalValueModification = function(msg)
{
var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
for each (var ent in msg.entities)
{
var template = cmpTemplateManager.GetTemplateWithoutValidation(cmpTemplateManager.GetCurrentTemplateName(ent));
for each (var value in msg.valueNames)
{
// let's get the base template value.
var strings = value.split("/");
var item = template;
var ended = true;
for (var i = 0; i < strings.length; ++i)
{
if (item !== undefined && item[strings[i]] !== undefined)
item = item[strings[i]];
else
ended = false;
}
if (!ended)
continue;
// "item" now contains the unmodified template value for this.
var newValue = ApplyValueModificationsToEntity(value, +item, ent);
newValue = typeof(newValue) === "Number" ? Math.round(newValue) : newValue;
if(item != newValue)
{
if (!this.changedEntityTemplateInfo[ent])
this.changedEntityTemplateInfo[ent] = [{ "variable" : value, "value" : newValue }];
else
this.changedEntityTemplateInfo[ent].push({ "variable" : value, "value" : newValue });
}
}
}
};
Engine.RegisterComponentType(IID_AIInterface, "AIInterface", AIInterface);