function AIInterface() {} AIInterface.prototype.Schema = ""; AIInterface.prototype.EventNames = [ "Create", "Destroy", "Attacked", "RangeUpdate", "ConstructionFinished", "TrainingStarted", "TrainingFinished", "AIMetadata", "PlayerDefeated", "EntityRenamed", "OwnershipChanged", "Garrison", "UnGarrison", "TributeExchanged" ]; AIInterface.prototype.Init = function() { this.events = {}; for each (var i in this.EventNames) this.events[i] = []; 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.GetSimulationState(-1); // Add some extra AI-specific data // add custom events and reset them for the next turn state.events = {}; for each (var i in this.EventNames) { state.events[i] = this.events[i]; this.events[i] = []; } 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) for each (var i in this.EventNames) state.events[i] = []; // 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) { var cmpMirage = Engine.QueryInterface(msg.entity, IID_Mirage); if (!cmpMirage) this.events["EntityRenamed"].push(msg); }; AIInterface.prototype.OnGlobalTributeExchanged = function(msg) { this.events["TributeExchanged"].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 templateName = cmpTemplateManager.GetCurrentTemplateName(ent); // if there's no template name, the unit is probably killed, ignore it. if (!templateName || !templateName.length) continue; var template = cmpTemplateManager.GetTemplateWithoutValidation(templateName); 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); // Apply the same roundings as in the components if (value === "Health/Max" || value === "Player/MaxPopulation") newValue = Math.round(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.RegisterSystemComponentType(IID_AIInterface, "AIInterface", AIInterface);