diff --git a/binaries/data/mods/public/simulation/ai/aegis/attack_plan.js b/binaries/data/mods/public/simulation/ai/aegis/attack_plan.js index 59786d666d..6383a415a4 100755 --- a/binaries/data/mods/public/simulation/ai/aegis/attack_plan.js +++ b/binaries/data/mods/public/simulation/ai/aegis/attack_plan.js @@ -544,6 +544,8 @@ m.CityAttack.prototype.updatePreparation = function(gameState, HQ,events) { count++; if (count > 1000){ m.debug("No target with a valid position found"); + Engine.ProfileStop(); + Engine.ProfileStop(); return false; } } diff --git a/binaries/data/mods/public/simulation/ai/aegis/base-manager.js b/binaries/data/mods/public/simulation/ai/aegis/base-manager.js index 1f89cd0ac6..0a814ba874 100644 --- a/binaries/data/mods/public/simulation/ai/aegis/base-manager.js +++ b/binaries/data/mods/public/simulation/ai/aegis/base-manager.js @@ -908,10 +908,6 @@ m.BaseManager.prototype.assignToFoundations = function(gameState, noRepair) { } } - // auras/techs are buggy and the AI tries to repair healthy buildings. - // TODO: reimplement once that's fixed. - return; - // don't repair if we're still under attack, unless it's like a vital (civcentre or wall) building that's getting destroyed. for (var i in damagedBuildings) { var target = damagedBuildings[i]; @@ -955,7 +951,7 @@ m.BaseManager.prototype.update = function(gameState, queues, events) { Engine.ProfileStart("Assign builders"); this.assignToFoundations(gameState); - Engine.ProfileStop() + Engine.ProfileStop(); if (this.constructing && this.anchor) { diff --git a/binaries/data/mods/public/simulation/ai/aegis/headquarters.js b/binaries/data/mods/public/simulation/ai/aegis/headquarters.js index 68cd1770b7..a5fe2db393 100644 --- a/binaries/data/mods/public/simulation/ai/aegis/headquarters.js +++ b/binaries/data/mods/public/simulation/ai/aegis/headquarters.js @@ -1121,7 +1121,6 @@ m.HQ.prototype.update = function(gameState, queues, events) { this.checkEvents(gameState,events,queues); //this.buildMoreHouses(gameState); - //Engine.ProfileStart("Train workers and build farms, houses. Research techs."); this.trainMoreWorkers(gameState, queues); // sandbox doesn't expand. diff --git a/binaries/data/mods/public/simulation/ai/common-api/entity.js b/binaries/data/mods/public/simulation/ai/common-api/entity.js index 6aa76033ca..0749a0786e 100644 --- a/binaries/data/mods/public/simulation/ai/common-api/entity.js +++ b/binaries/data/mods/public/simulation/ai/common-api/entity.js @@ -1,56 +1,78 @@ var API3 = function(m) { -m.EntityTemplate = m.Class({ +// defines a template. +// It's completely raw data, except it's slightly cleverer now and then. +m.Template = m.Class({ - // techModifications should be the tech modifications of only one player. - // gamestates handle "GetTemplate" and should push the player's - // while entities should push the owner's - _init: function(template, techModifications) + _init: function(template) { - this._techModifications = techModifications; this._template = template; + this._tpCache = {}; }, - genericName: function() { - if (!this._template.Identity || !this._template.Identity.GenericName) - return undefined; - return this._template.Identity.GenericName; + // helper function to return a template value, optionally adjusting for tech. + // TODO: there's no support for "_string" values here. + get: function(string) + { + var value = this._template; + if (this._auraTemplateModif && this._auraTemplateModif[string]) { + return this._auraTemplateModif[string]; + } else if (this._techModif && this._techModif[string]) { + return this._techModif[string]; + } else { + if (this._tpCache[string] === undefined) + { + var args = string.split("/"); + for (var i = 0; i < args.length; ++i) + if (value[args[i]]) + value = value[args[i]]; + else + { + value = undefined; + break; + } + this._tpCache[string] = value; + } + return this._tpCache[string]; + } }, - - rank: function() { - if (!this._template.Identity) + + genericName: function() { + if (!this.get("Identity") || !this.get("Identity/GenericName")) return undefined; - return this._template.Identity.Rank; + return this.get("Identity/GenericName"); + }, + + rank: function() { + if (!this.get("Identity")) + return undefined; + return this.get("Identity/Rank"); }, classes: function() { - if (!this._template.Identity || !this._template.Identity.Classes || !this._template.Identity.Classes._string) + if (!this.get("Identity") || !this.get("Identity/Classes") || !this.get("Identity/Classes/_string")) return undefined; - return this._template.Identity.Classes._string.split(/\s+/); + return this.get("Identity/Classes/_string").split(/\s+/); }, requiredTech: function() { - if (!this._template.Identity || !this._template.Identity.RequiredTechnology) - return undefined; - return this._template.Identity.RequiredTechnology; + return this.get("Identity/RequiredTechnology"); }, - + available: function(gameState) { - if (!this._template.Identity || !this._template.Identity.RequiredTechnology) - return true; - return gameState.isResearched(this._template.Identity.RequiredTechnology); + return gameState.isResearched(this.get("Identity/RequiredTechnology")); }, - + // specifically phase: function() { - if (!this._template.Identity || !this._template.Identity.RequiredTechnology) + if (!this.get("Identity/RequiredTechnology")) return 0; - if (this.template.Identity.RequiredTechnology == "phase_village") + if (this.get("Identity/RequiredTechnology") == "phase_village") return 1; - if (this.template.Identity.RequiredTechnology == "phase_town") + if (this.get("Identity/RequiredTechnology") == "phase_town") return 2; - if (this.template.Identity.RequiredTechnology == "phase_city") + if (this.get("Identity/RequiredTechnology") == "phase_city") return 3; return 0; }, @@ -72,28 +94,26 @@ m.EntityTemplate = m.Class({ }, civ: function() { - if (!this._template.Identity) - return undefined; - return this._template.Identity.Civ; + return this.get("Identity/Civ"); }, cost: function() { - if (!this._template.Cost) + if (!this.get("Cost")) return undefined; var ret = {}; - for (var type in this._template.Cost.Resources) - ret[type] = GetTechModifiedProperty(this._techModifications, this._template, "Cost/Resources/"+type, +this._template.Cost.Resources[type]); + for (var type in this.get("Cost/Resources")) + ret[type] = +this.get("Cost/Resources/" + type); return ret; }, costSum: function() { - if (!this._template.Cost) + if (!this.get("Cost")) return undefined; var ret = 0; - for (var type in this._template.Cost.Resources) - ret += +this._template.Cost.Resources[type]; + for (var type in this.get("Cost/Resources")) + ret += +this.get("Cost/Resources/" + type); return ret; }, @@ -102,134 +122,134 @@ m.EntityTemplate = m.Class({ * obstruction shape, or undefined if no obstruction. */ obstructionRadius: function() { - if (!this._template.Obstruction) + if (!this.get("Obstruction")) return undefined; - if (this._template.Obstruction.Static) + if (this.get("Obstruction/Static")) { - var w = +this._template.Obstruction.Static["@width"]; - var h = +this._template.Obstruction.Static["@depth"]; + var w = +this.get("Obstruction/Static/@width"); + var h = +this.get("Obstruction/Static/@depth"); return Math.sqrt(w*w + h*h) / 2; } - if (this._template.Obstruction.Unit) - return +this._template.Obstruction.Unit["@radius"]; + if (this.get("Obstruction/Unit")) + return +this.get("Obstruction/Unit/@radius"); return 0; // this should never happen }, - + /** * Returns the radius of a circle surrounding this entity's * footprint. */ footprintRadius: function() { - if (!this._template.Footprint) + if (!this.get("Footprint")) return undefined; - if (this._template.Footprint.Square) + if (this.get("Footprint/Square")) { - var w = +this._template.Footprint.Square["@width"]; - var h = +this._template.Footprint.Square["@depth"]; + var w = +this.get("Footprint/Square/@width"); + var h = +this.get("Footprint/Square/@depth"); return Math.sqrt(w*w + h*h) / 2; } - if (this._template.Footprint.Circle) - return +this._template.Footprint.Circle["@radius"]; + if (this.get("Footprint/Circle")) + return +this.get("Footprint/Circle/@radius"); return 0; // this should never happen }, maxHitpoints: function() { - if (this._template.Health !== undefined) - return GetTechModifiedProperty(this._techModifications, this._template, "Health/Max",+this._template.Health.Max); + if (this.get("Health") !== undefined) + return +this.get("Health/Max"); return 0; }, + isHealable: function() { - if (this._template.Health !== undefined) - return this._template.Health.Unhealable !== "true"; + if (this.get("Health") !== undefined) + return this.get("Health/Unhealable") !== "true"; return false; }, + isRepairable: function() { - if (this._template.Health !== undefined) - return this._template.Health.Repairable === "true"; + if (this.get("Health") !== undefined) + return this.get("Health/Repairable") === "true"; return false; }, getPopulationBonus: function() { - if (!this._template.Cost || !this._template.Cost.PopulationBonus) - return undefined; - return this._template.Cost.PopulationBonus; + return this.get("Cost/PopulationBonus"); }, armourStrengths: function() { - if (!this._template.Armour) + if (!this.get("Armour")) return undefined; return { - hack: GetTechModifiedProperty(this._techModifications, this._template, "Armour/Hack", +this._template.Armour.Hack), - pierce: GetTechModifiedProperty(this._techModifications, this._template, "Armour/Pierce", +this._template.Armour.Pierce), - crush: GetTechModifiedProperty(this._techModifications, this._template, "Armour/Crush", +this._template.Armour.Crush) + hack: +this.get("Armour/Hack"), + pierce: +this.get("Armour/Pierce"), + crush: +this.get("Armour/Crush") }; }, attackTypes: function() { - if (!this._template.Attack) + if (!this.get("Attack")) return undefined; var ret = []; - for (var type in this._template.Attack) + for (var type in this.get("Attack")) ret.push(type); return ret; }, attackRange: function(type) { - if (!this._template.Attack || !this._template.Attack[type]) + if (!this.get("Attack/" + type +"")) return undefined; return { - max: GetTechModifiedProperty(this._techModifications, this._template, "Attack/MaxRange", +this._template.Attack[type].MaxRange), - min: GetTechModifiedProperty(this._techModifications, this._template, "Attack/MinRange", +(this._template.Attack[type].MinRange || 0)) + max: +this.get("Attack/" + type +"/MaxRange"), + min: +(this.get("Attack/" + type +"/MinRange") || 0) }; }, attackStrengths: function(type) { - if (!this._template.Attack || !this._template.Attack[type]) + if (!this.get("Attack/" + type +"")) return undefined; return { - hack: GetTechModifiedProperty(this._techModifications, this._template, "Attack/"+type+"/Hack", +(this._template.Attack[type].Hack || 0)), - pierce: GetTechModifiedProperty(this._techModifications, this._template, "Attack/"+type+"/Pierce", +(this._template.Attack[type].Pierce || 0)), - crush: GetTechModifiedProperty(this._techModifications, this._template, "Attack/"+type+"/Crush", +(this._template.Attack[type].Crush || 0)) + hack: +(this.get("Attack/" + type + "/Hack") || 0), + pierce: +(this.get("Attack/" + type + "/Pierce") || 0), + crush: +(this.get("Attack/" + type + "/Crush") || 0) }; }, attackTimes: function(type) { - if (!this._template.Attack || !this._template.Attack[type]) + if (!this.get("Attack/" + type +"")) return undefined; return { - prepare: GetTechModifiedProperty(this._techModifications, this._template, "Attack/"+type+"/PrepareTime", +(this._template.Attack[type].PrepareTime || 0)), - repeat: GetTechModifiedProperty(this._techModifications, this._template, "Attack/"+type+"/RepeatTime", +(this._template.Attack[type].RepeatTime || 1000)) + prepare: +(this.get("Attack/" + type + "/PrepareTime") || 0), + repeat: +(this.get("Attack/" + type + "/RepeatTime") || 1000) }; }, // returns the classes this templates counters: - // Return type is [ [-neededClasses-] , multiplier ]. + // Return type is [ [-neededClasses- , multiplier], … ]. getCounteredClasses: function() { - if (!this._template.Attack) + if (!this.get("Attack")) return undefined; var Classes = []; - for (var i in this._template.Attack) { - if (!this._template.Attack[i].Bonuses) + for (var i in this.get("Attack")) { + if (!this.get("Attack/" + i + "/Bonuses")) continue; - for (var o in this._template.Attack[i].Bonuses) - if (this._template.Attack[i].Bonuses[o].Classes) - Classes.push([this._template.Attack[i].Bonuses[o].Classes.split(" "), +this._template.Attack[i].Bonuses[o].Multiplier]); + for (var o in this.get("Attack/" + i + "/Bonuses")) + if (this.get("Attack/" + i + "/Bonuses/" + o + "/Classes")) + Classes.push([this.get("Attack/" + i +"/Bonuses/" + o +"/Classes").split(" "), +this.get("Attack/" + i +"/Bonuses" +o +"/Multiplier")]); } return Classes; }, @@ -237,15 +257,15 @@ m.EntityTemplate = m.Class({ // returns true if the entity counters those classes. // TODO: refine using the multiplier countersClasses: function(classes) { - if (!this._template.Attack) + if (!this.get("Attack")) return false; var mcounter = []; - for (var i in this._template.Attack) { - if (!this._template.Attack[i].Bonuses) + for (var i in this.get("Attack")) { + if (!this.get("Attack/" + i + "/Bonuses")) continue; - for (var o in this._template.Attack[i].Bonuses) - if (this._template.Attack[i].Bonuses[o].Classes) - mcounter.concat(this._template.Attack[i].Bonuses[o].Classes.split(" ")); + for (var o in this.get("Attack/" + i + "/Bonuses")) + if (this.get("Attack/" + i + "/Bonuses/" + o + "/Classes")) + mcounter.concat(this.get("Attack/" + i + "/Bonuses/" + o + "/Classes").split(" ")); } for (var i in classes) { @@ -257,30 +277,30 @@ m.EntityTemplate = m.Class({ // returns, if it exists, the multiplier from each attack against a given class getMultiplierAgainst: function(type, againstClass) { - if (!this._template.Attack || !this._template.Attack[type]) + if (!this.get("Attack/" + type +"")) return undefined; - if (this._template.Attack[type].Bonuses) - for (var o in this._template.Attack[type].Bonuses) { - if (!this._template.Attack[type].Bonuses[o].Classes) + if (this.get("Attack/" + type + "/Bonuses")) + for (var o in this.get("Attack/" + type + "/Bonuses")) { + if (!this.get("Attack/" + type + "/Bonuses/" + o + "/Classes")) continue; - var total = this._template.Attack[type].Bonuses[o].Classes.split(" "); + var total = this.get("Attack/" + type + "/Bonuses/" + o + "/Classes").split(" "); for (var j in total) if (total[j] === againstClass) - return this._template.Attack[type].Bonuses[o].Multiplier; + return this.get("Attack/" + type + "/Bonuses/" + o + "/Multiplier"); } return 1; }, // returns true if the entity can attack the given class canAttackClass: function(saidClass) { - if (!this._template.Attack) + if (!this.get("Attack")) return false; - for (var i in this._template.Attack) { - if (!this._template.Attack[i].RestrictedClasses || !this._template.Attack[i].RestrictedClasses._string) + for (var i in this.get("Attack")) { + if (!this.get("Attack/" + i + "/RestrictedClasses") || !this.get("Attack/" + i + "/RestrictedClasses/_string")) continue; - var cannotAttack = this._template.Attack[i].RestrictedClasses._string.split(" "); + var cannotAttack = this.get("Attack/" + i + "/RestrictedClasses/_string").split(" "); if (cannotAttack.indexOf(saidClass) !== -1) return false; } @@ -288,86 +308,84 @@ m.EntityTemplate = m.Class({ }, buildableEntities: function() { - if (!this._template.Builder) - return undefined; - if (!this._template.Builder.Entities._string) + if (!this.get("Builder/Entities/_string")) return []; var civ = this.civ(); - var templates = this._template.Builder.Entities._string.replace(/\{civ\}/g, civ).split(/\s+/); + var templates = this.get("Builder/Entities/_string").replace(/\{civ\}/g, civ).split(/\s+/); return templates; // TODO: map to Entity? }, trainableEntities: function() { - if (!this._template.ProductionQueue || !this._template.ProductionQueue.Entities || !this._template.ProductionQueue.Entities._string) + if (!this.get("ProductionQueue/Entities/_string")) return undefined; var civ = this.civ(); - var templates = this._template.ProductionQueue.Entities._string.replace(/\{civ\}/g, civ).split(/\s+/); + var templates = this.get("ProductionQueue/Entities/_string").replace(/\{civ\}/g, civ).split(/\s+/); return templates; }, researchableTechs: function() { - if (!this._template.ProductionQueue || !this._template.ProductionQueue.Technologies || !this._template.ProductionQueue.Technologies._string) + if (!this.get("ProductionQueue/Technologies/_string")) return undefined; - var templates = this._template.ProductionQueue.Technologies._string.split(/\s+/); + var templates = this.get("ProductionQueue/Technologies/_string").split(/\s+/); return templates; }, resourceSupplyType: function() { - if (!this._template.ResourceSupply) + if (!this.get("ResourceSupply")) return undefined; - var [type, subtype] = this._template.ResourceSupply.Type.split('.'); + var [type, subtype] = this.get("ResourceSupply/Type").split('.'); return { "generic": type, "specific": subtype }; }, // will return either "food", "wood", "stone", "metal" and not treasure. getResourceType: function() { - if (!this._template.ResourceSupply) + if (!this.get("ResourceSupply")) return undefined; - var [type, subtype] = this._template.ResourceSupply.Type.split('.'); + var [type, subtype] = this.get("ResourceSupply/Type").split('.'); if (type == "treasure") return subtype; return type; }, resourceSupplyMax: function() { - if (!this._template.ResourceSupply) + if (!this.get("ResourceSupply")) return undefined; - return +this._template.ResourceSupply.Amount; + return +this.get("ResourceSupply/Amount"); }, maxGatherers: function() { - if (this._template.ResourceSupply !== undefined) - return +this._template.ResourceSupply.MaxGatherers; + if (this.get("ResourceSupply") !== undefined) + return +this.get("ResourceSupply/MaxGatherers"); return 0; }, resourceGatherRates: function() { - if (!this._template.ResourceGatherer) + if (!this.get("ResourceGatherer")) return undefined; var ret = {}; - var baseSpeed = GetTechModifiedProperty(this._techModifications, this._template, "ResourceGatherer/BaseSpeed", +this._template.ResourceGatherer.BaseSpeed); - for (var r in this._template.ResourceGatherer.Rates) - ret[r] = GetTechModifiedProperty(this._techModifications, this._template, "ResourceGatherer/Rates/"+r, +this._template.ResourceGatherer.Rates[r]) * baseSpeed; + var baseSpeed = +this.get("ResourceGatherer/BaseSpeed"); + for (var r in this.get("ResourceGatherer/Rates")) + ret[r] = +this.get("ResourceGatherer/Rates/" + r) * baseSpeed; return ret; }, resourceDropsiteTypes: function() { - if (!this._template.ResourceDropsite) + if (!this.get("ResourceDropsite")) return undefined; - return this._template.ResourceDropsite.Types.split(/\s+/); + return this.get("ResourceDropsite/Types").split(/\s+/); }, garrisonableClasses: function() { - if (!this._template.GarrisonHolder || !this._template.GarrisonHolder.List._string) + if (!this.get("GarrisonHolder") || !this.get("GarrisonHolder/List/_string")) return undefined; - return this._template.GarrisonHolder.List._string.split(/\s+/); + return this.get("GarrisonHolder/List/_string").split(/\s+/); }, garrisonMax: function() { - if (!this._template.GarrisonHolder) + if (!this.get("GarrisonHolder")) return undefined; - return this._template.GarrisonHolder.Max; + return this.get("GarrisonHolder/Max"); }, /** @@ -375,47 +393,47 @@ m.EntityTemplate = m.Class({ * (Any non domestic currently.) */ isUnhuntable: function() { - if (!this._template.UnitAI || !this._template.UnitAI.NaturalBehaviour) + if (!this.get("UnitAI") || !this.get("UnitAI/NaturalBehaviour")) return false; // only attack domestic animals since they won't flee nor retaliate. - return this._template.UnitAI.NaturalBehaviour !== "domestic"; + return this.get("UnitAI/NaturalBehaviour") !== "domestic"; }, - + walkSpeed: function() { - if (!this._template.UnitMotion || !this._template.UnitMotion.WalkSpeed) + if (!this.get("UnitMotion") || !this.get("UnitMotion/WalkSpeed")) return undefined; - return this._template.UnitMotion.WalkSpeed; + return this.get("UnitMotion/WalkSpeed"); }, buildCategory: function() { - if (!this._template.BuildRestrictions || !this._template.BuildRestrictions.Category) + if (!this.get("BuildRestrictions") || !this.get("BuildRestrictions/Category")) return undefined; - return this._template.BuildRestrictions.Category; + return this.get("BuildRestrictions/Category"); }, buildTime: function() { - if (!this._template.Cost || !this._template.Cost.BuildTime) + if (!this.get("Cost") || !this.get("Cost/BuildTime")) return undefined; - return this._template.Cost.BuildTime; + return this.get("Cost/BuildTime"); }, buildDistance: function() { - if (!this._template.BuildRestrictions || !this._template.BuildRestrictions.Distance) + if (!this.get("BuildRestrictions") || !this.get("BuildRestrictions/Distance")) return undefined; - return this._template.BuildRestrictions.Distance; + return this.get("BuildRestrictions/Distance"); }, buildPlacementType: function() { - if (!this._template.BuildRestrictions || !this._template.BuildRestrictions.PlacementType) + if (!this.get("BuildRestrictions") || !this.get("BuildRestrictions/PlacementType")) return undefined; - return this._template.BuildRestrictions.PlacementType; + return this.get("BuildRestrictions/PlacementType"); }, buildTerritories: function() { - if (!this._template.BuildRestrictions || !this._template.BuildRestrictions.Territory) + if (!this.get("BuildRestrictions") || !this.get("BuildRestrictions/Territory")) return undefined; - return this._template.BuildRestrictions.Territory.split(/\s+/); + return this.get("BuildRestrictions/Territory").split(/\s+/); }, hasBuildTerritory: function(territory) { @@ -424,42 +442,45 @@ m.EntityTemplate = m.Class({ }, hasTerritoryInfluence: function() { - return (this._template.TerritoryInfluence !== undefined); + return (this.get("TerritoryInfluence") !== undefined); }, territoryInfluenceRadius: function() { - if (this._template.TerritoryInfluence !== undefined) - return (this._template.TerritoryInfluence.Radius); + if (this.get("TerritoryInfluence") !== undefined) + return (this.get("TerritoryInfluence/Radius")); else return -1; }, territoryInfluenceWeight: function() { - if (this._template.TerritoryInfluence !== undefined) - return (this._template.TerritoryInfluence.Weight); + if (this.get("TerritoryInfluence") !== undefined) + return (this.get("TerritoryInfluence/Weight")); else return -1; }, visionRange: function() { - if (!this._template.Vision) - return undefined; - return this._template.Vision.Range; + return this.get("Vision/Range"); } }); - +// defines an entity, with a super Template. +// also redefines several of the template functions where the only change is applying aura and tech modifications. m.Entity = m.Class({ - _super: m.EntityTemplate, + _super: m.Template, _init: function(sharedAI, entity) { - this._super.call(this, sharedAI.GetTemplate(entity.template), sharedAI._techModifications[entity.owner]); + this._super.call(this, sharedAI.GetTemplate(entity.template)); - this._ai = sharedAI; this._templateName = entity.template; this._entity = entity; + this._auraTemplateModif = {}; // template modification from auras. this is only for this entity. + this._ai = sharedAI; + if (!sharedAI._techModifications[entity.owner][this._templateName]) + sharedAI._techModifications[entity.owner][this._templateName] = {}; + this._techModif = sharedAI._techModifications[entity.owner][this._templateName]; // save a reference to the template tech modifications }, toString: function() { @@ -477,7 +498,7 @@ m.Entity = m.Class({ /** * Returns extra data that the AI scripts have associated with this entity, * for arbitrary local annotations. - * (This data is not shared with any other AI scripts.) + * (This data should not be shared with any other AI scripts.) */ getMetadata: function(player, key) { return this._ai.getMetadata(player, this, key); @@ -493,7 +514,7 @@ m.Entity = m.Class({ deleteAllMetadata: function(player) { delete this._ai._entityMetadata[player][this.id()]; }, - + deleteMetadata: function(player, key) { this._ai.deleteMetadata(player, this, key); }, @@ -508,7 +529,8 @@ m.Entity = m.Class({ unitAIState: function() { return this._entity.unitAIState; }, unitAIOrderData: function() { return this._entity.unitAIOrderData; }, - hitpoints: function() { if (this._entity.hitpoints !== undefined) return this._entity.hitpoints; return undefined; }, + + hitpoints: function() {if (this._entity.hitpoints !== undefined) return this._entity.hitpoints; return undefined; }, isHurt: function() { return this.hitpoints() < this.maxHitpoints(); }, healthLevel: function() { return (this.hitpoints() / this.maxHitpoints()); }, needsHeal: function() { return this.isHurt() && this.isHealable(); }, @@ -540,14 +562,17 @@ m.Entity = m.Class({ owner: function() { return this._entity.owner; }, + isOwn: function(player) { if (typeof(this._entity.owner) === "undefined") return false; return this._entity.owner === player; }, + isFriendly: function(player) { return this.isOwn(player); // TODO: diplomacy }, + isEnemy: function(player) { return !this.isOwn(player); // TODO: diplomacy }, @@ -557,18 +582,18 @@ m.Entity = m.Class({ return undefined; return this._entity.resourceSupplyAmount; }, - + resourceSupplyGatherers: function(player) { if (this._entity.resourceSupplyGatherers !== undefined) - return this._entity.resourceSupplyGatherers[player-1]; + return this._entity.resourceSupplyGatherers[player]; return []; }, - + isFull: function(player) { if (this._entity.resourceSupplyGatherers !== undefined) - return (this.maxGatherers() === this._entity.resourceSupplyGatherers[player-1].length); + return (this.maxGatherers() === this._entity.resourceSupplyGatherers[player].length); return undefined; }, @@ -578,10 +603,10 @@ m.Entity = m.Class({ return undefined; return this._entity.resourceCarrying; }, - + currentGatherRate: function() { // returns the gather rate for the current target if applicable. - if (!this._template.ResourceGatherer) + if (!this.get("ResourceGatherer")); return undefined; if (this.unitAIOrderData().length && @@ -599,13 +624,13 @@ m.Entity = m.Class({ var type = ress.resourceSupplyType(); var tstring = type.generic + "." + type.specific; - + if (type.generic == "treasure") return 1000; - var speed = GetTechModifiedProperty(this._techModifications, this._template, "ResourceGatherer/BaseSpeed", +this._template.ResourceGatherer.BaseSpeed); - speed *= GetTechModifiedProperty(this._techModifications, this._template, "ResourceGatherer/Rates/"+tstring, +this._template.ResourceGatherer.Rates[tstring]); - + var speed = +this.get("ResourceGatherer/BaseSpeed"); + speed *= +this.get("ResourceGatherer/Rates/" +tstring); + if (speed) return speed; return 0; @@ -644,7 +669,7 @@ m.Entity = m.Class({ }, unload: function(id) { - if (!this._template.GarrisonHolder) + if (!this.get("GarrisonHolder")) return undefined; Engine.PostCommand(PlayerID,{"type": "unload", "garrisonHolder": this.id(), "entities": [id]}); return this; @@ -652,7 +677,7 @@ m.Entity = m.Class({ // Unloads all owned units, don't unload allies unloadAll: function() { - if (!this._template.GarrisonHolder) + if (!this.get("GarrisonHolder")) return undefined; Engine.PostCommand(PlayerID,{"type": "unload-all-own", "garrisonHolders": [this.id()]}); return this; @@ -751,7 +776,7 @@ m.Entity = m.Class({ }); return this; }, - + research: function(template) { Engine.PostCommand(PlayerID,{ "type": "research", "entity": this.id(), "template": template }); return this; diff --git a/binaries/data/mods/public/simulation/ai/common-api/gamestate.js b/binaries/data/mods/public/simulation/ai/common-api/gamestate.js index a16cf09b5e..9cbacacf0e 100644 --- a/binaries/data/mods/public/simulation/ai/common-api/gamestate.js +++ b/binaries/data/mods/public/simulation/ai/common-api/gamestate.js @@ -126,7 +126,7 @@ m.GameState.prototype.getTemplate = function(type) if (!this.templates[type]) return null; - return new m.EntityTemplate(this.templates[type], this.techModifications); + return new m.Template(this.templates[type], this.techModifications); }; m.GameState.prototype.applyCiv = function(str) { diff --git a/binaries/data/mods/public/simulation/ai/common-api/shared.js b/binaries/data/mods/public/simulation/ai/common-api/shared.js index bad69420fe..b4dc583de5 100644 --- a/binaries/data/mods/public/simulation/ai/common-api/shared.js +++ b/binaries/data/mods/public/simulation/ai/common-api/shared.js @@ -58,7 +58,7 @@ m.SharedScript.prototype.Deserialize = function(data) // Components that will be disabled in foundation entity templates. // (This is a bit yucky and fragile since it's the inverse of // CCmpTemplateManager::CopyFoundationSubset and only includes components -// that our EntityTemplate class currently uses.) +// that our Template class currently uses.) m.g_FoundationForbiddenComponents = { "ProductionQueue": 1, "ResourceSupply": 1, @@ -119,6 +119,8 @@ m.SharedScript.prototype.GetTemplate = function(name) // We need to now the initial state of the game for this, as we will use it. // This is called right at the end of the map generation. m.SharedScript.prototype.init = function(state) { + this.ApplyTemplatesDelta(state); + this.passabilityClasses = state.passabilityClasses; this.passabilityMap = state.passabilityMap; this.players = this._players; @@ -127,9 +129,6 @@ m.SharedScript.prototype.init = function(state) { this.timeElapsed = state.timeElapsed; this.barterPrices = state.barterPrices; - for (var o in state.players) - this._techModifications[o] = state.players[o].techModifications; - this._entities = {}; for (var id in state.entities) this._entities[id] = new m.Entity(this, state.entities[id]); @@ -166,6 +165,7 @@ m.SharedScript.prototype.onUpdate = function(state) return; // deals with updating based on create and destroy messages. this.ApplyEntitiesDelta(state); + this.ApplyTemplatesDelta(state); Engine.ProfileStart("onUpdate"); @@ -178,9 +178,6 @@ m.SharedScript.prototype.onUpdate = function(state) this.timeElapsed = state.timeElapsed; this.barterPrices = state.barterPrices; - for (var o in state.players) - this._techModifications[o] = state.players[o].techModifications; - for (var i in this.gameState) this.gameState[i].update(this,state); @@ -313,6 +310,34 @@ m.SharedScript.prototype.ApplyEntitiesDelta = function(state) this.updateEntityCollections(prop, this._entities[id]); } } + + // apply per-entity aura-related changes. + // this supersedes tech-related changes. + for (var id in state.changedEntityTemplateInfo) + { + var changes = state.changedEntityTemplateInfo[id]; + for each (var change in changes) + this._entities[id]._auraTemplateModif[change.variable] = change.value; + } + Engine.ProfileStop(); +}; + +m.SharedScript.prototype.ApplyTemplatesDelta = function(state) +{ + Engine.ProfileStart("Shared ApplyTemplatesDelta"); + + for (var player in state.changedTemplateInfo) + { + var playerDiff = state.changedTemplateInfo[player]; + for (var template in playerDiff) + { + var changes = playerDiff[template]; + if (!this._techModifications[player][template]) + this._techModifications[player][template] = {}; + for each (var change in changes) + this._techModifications[player][template][change.variable] = change.value; + } + } Engine.ProfileStop(); }; diff --git a/binaries/data/mods/public/simulation/components/AIInterface.js b/binaries/data/mods/public/simulation/components/AIInterface.js index f3bd9f76ee..f7ba2f1a61 100644 --- a/binaries/data/mods/public/simulation/components/AIInterface.js +++ b/binaries/data/mods/public/simulation/components/AIInterface.js @@ -18,15 +18,21 @@ AIInterface.prototype.Init = function() 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.GetRepresentation = function() +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"]; @@ -39,7 +45,6 @@ AIInterface.prototype.GetRepresentation = function() 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"] = []; @@ -51,7 +56,14 @@ AIInterface.prototype.GetRepresentation = function() 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 = {}; @@ -64,29 +76,18 @@ AIInterface.prototype.GetRepresentation = function() 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 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"]; - +{ + var state = this.GetNonEntityRepresentation(); if (flushEvents) { @@ -102,19 +103,6 @@ AIInterface.prototype.GetFullRepresentation = function(flushEvents) state.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"] = []; - - // Add entity representations Engine.ProfileStart("proxy representations"); state.entities = {}; @@ -127,6 +115,11 @@ AIInterface.prototype.GetFullRepresentation = function(flushEvents) } Engine.ProfileStop(); + state.changedTemplateInfo = this.changedTemplateInfo; + this.changedTemplateInfo = {}; + state.changedEntityTemplateInfo = this.changedEntityTemplateInfo; + this.changedEntityTemplateInfo = {}; + return state; }; @@ -156,4 +149,97 @@ 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); diff --git a/binaries/data/mods/public/simulation/components/AIProxy.js b/binaries/data/mods/public/simulation/components/AIProxy.js index ab46a7a2b2..a00f7b9fac 100644 --- a/binaries/data/mods/public/simulation/components/AIProxy.js +++ b/binaries/data/mods/public/simulation/components/AIProxy.js @@ -36,10 +36,19 @@ AIProxy.prototype.Init = function() this.needsFullGet = true; this.owner = -1; // for convenience now and then. + this.cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface); + // Let the AIInterface know that we exist and that it should query us this.NotifyChange(); }; +AIProxy.prototype.Serialize = null; // we have no dynamic state to save + +AIProxy.prototype.Deserialize = function () +{ + this.cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface); +}; + AIProxy.prototype.GetRepresentation = function() { // Return the full representation the first time we're called @@ -67,9 +76,7 @@ AIProxy.prototype.NotifyChange = function() if (!this.changes) { this.changes = {}; - - var cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface); - cmpAIInterface.ChangedEntity(this.entity); + this.cmpAIInterface.ChangedEntity(this.entity); } }; @@ -261,55 +268,47 @@ AIProxy.prototype.OnOwnershipChanged = function(msg) if (msg.from === -1) { - var cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface); - cmpAIInterface.PushEvent("Create", {"entity" : msg.entity}); + this.cmpAIInterface.PushEvent("Create", {"entity" : msg.entity}); return; } else if (msg.to === -1) { - var cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface); - cmpAIInterface.PushEvent("Destroy", {"entity" : msg.entity}); + this.cmpAIInterface.PushEvent("Destroy", {"entity" : msg.entity}); return; } this.owner = msg.to; this.changes.owner = msg.to; - var cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface); - cmpAIInterface.PushEvent("OwnershipChanged", msg); + this.cmpAIInterface.PushEvent("OwnershipChanged", msg); }; AIProxy.prototype.OnAttacked = function(msg) { - var cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface); - cmpAIInterface.PushEvent("Attacked", msg); + this.cmpAIInterface.PushEvent("Attacked", msg); }; /* Deactivated for actually not really being practical for most uses. AIProxy.prototype.OnRangeUpdate = function(msg) { - var cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface); msg.owner = this.owner; - cmpAIInterface.PushEvent("RangeUpdate", msg); + this.cmpAIInterface.PushEvent("RangeUpdate", msg); warn(uneval(msg)); };*/ AIProxy.prototype.OnConstructionFinished = function(msg) { - var cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface); - cmpAIInterface.PushEvent("ConstructionFinished", msg); + this.cmpAIInterface.PushEvent("ConstructionFinished", msg); }; AIProxy.prototype.OnTrainingFinished = function(msg) { - var cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface); - cmpAIInterface.PushEvent("TrainingFinished", msg); + this.cmpAIInterface.PushEvent("TrainingFinished", msg); }; AIProxy.prototype.OnAIMetadata = function(msg) { - var cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface); - cmpAIInterface.PushEvent("AIMetadata", msg); + this.cmpAIInterface.PushEvent("AIMetadata", msg); }; Engine.RegisterComponentType(IID_AIProxy, "AIProxy", AIProxy); diff --git a/binaries/data/mods/public/simulation/components/AuraManager.js b/binaries/data/mods/public/simulation/components/AuraManager.js index 8c025d15ae..89adfc90d8 100644 --- a/binaries/data/mods/public/simulation/components/AuraManager.js +++ b/binaries/data/mods/public/simulation/components/AuraManager.js @@ -46,7 +46,7 @@ AuraManager.prototype.ApplyBonus = function(value, ent, data, key) this.modificationsCache[value][ent].add += data.add; // post message to the entity to notify it about the change - Engine.PostMessage(ent, MT_ValueModification, { "component": value.split("/")[0] }); + Engine.PostMessage(ent, MT_ValueModification, { "entities": [ent], "component": value.split("/")[0], "valueNames": [value] }); }; AuraManager.prototype.ApplyTemplateBonus = function(value, player, classes, data, key) @@ -73,6 +73,7 @@ AuraManager.prototype.ApplyTemplateBonus = function(value, player, classes, data if (data.add) this.templateModificationsCache[value][player][c][key].add += data.add; + Engine.PostMessage(SYSTEM_ENTITY, MT_TemplateModification, { "player": player, "component": value.split("/")[0], "valueNames": [value] }); } }; @@ -98,7 +99,9 @@ AuraManager.prototype.RemoveBonus = function(value, ent, key) this.modificationsCache[value][ent].multiply /= data.multiply; // post message to the entity to notify it about the change - Engine.PostMessage(ent, MT_ValueModification, { "component": value.split("/")[0] }); + var effects = {}; + effects[value] = this.modificationsCache[value][ent]; + Engine.PostMessage(ent, MT_ValueModification, { "entities": [ent], "component": value.split("/")[0], "valueNames": [value] }); }; AuraManager.prototype.RemoveTemplateBonus = function(value, player, classes, key) @@ -119,6 +122,7 @@ AuraManager.prototype.RemoveTemplateBonus = function(value, player, classes, key this.templateModificationsCache[value][player][c][key].multiply = 1; this.templateModificationsCache[value][player][c][key].add = 0; } + Engine.PostMessage(SYSTEM_ENTITY, MT_TemplateModification, { "player": player, "component": value.split("/")[0], "valueNames": [value] }); }; AuraManager.prototype.ApplyModifications = function(valueName, value, ent) diff --git a/binaries/data/mods/public/simulation/components/GuiInterface.js b/binaries/data/mods/public/simulation/components/GuiInterface.js index 3f25673484..e9121e82b0 100644 --- a/binaries/data/mods/public/simulation/components/GuiInterface.js +++ b/binaries/data/mods/public/simulation/components/GuiInterface.js @@ -97,7 +97,6 @@ GuiInterface.prototype.GetSimulationState = function(player) "entityLimits": cmpPlayerEntityLimits.GetLimits(), "entityCounts": cmpPlayerEntityLimits.GetCounts(), "entityLimitChangers": cmpPlayerEntityLimits.GetLimitChangers(), - "techModifications": cmpTechnologyManager.GetTechModifications(), "researchQueued": cmpTechnologyManager.GetQueuedResearch(), "researchStarted": cmpTechnologyManager.GetStartedResearch(), "researchedTechs": cmpTechnologyManager.GetResearchedTechs(), @@ -475,8 +474,8 @@ GuiInterface.prototype.GetTemplateData = function(player, extendedName) { ret.armour = { "hack": ApplyValueModificationsToTemplate("Armour/Hack", +template.Armour.Hack, player, template), - "pierce": ApplyValueModificationsToTemplate("Armour/Pierce", +template.Armour.Hack, player, template), - "crush": ApplyValueModificationsToTemplate("Armour/Crush", +template.Armour.Hack, player, template), + "pierce": ApplyValueModificationsToTemplate("Armour/Pierce", +template.Armour.Pierce, player, template), + "crush": ApplyValueModificationsToTemplate("Armour/Crush", +template.Armour.Crush, player, template), }; } diff --git a/binaries/data/mods/public/simulation/components/TechnologyManager.js b/binaries/data/mods/public/simulation/components/TechnologyManager.js index 408b125d13..b24b816353 100644 --- a/binaries/data/mods/public/simulation/components/TechnologyManager.js +++ b/binaries/data/mods/public/simulation/components/TechnologyManager.js @@ -226,13 +226,17 @@ TechnologyManager.prototype.OnGlobalOwnershipChanged = function (msg) var modifications = this.modifications[name]; var component = name.split("/")[0]; for (var i in modifications) - if (!modifiedComponents[component] && DoesModificationApply(modifications[i], classes)) - modifiedComponents[component] = true; + if (DoesModificationApply(modifications[i], classes)) + { + if (!modifiedComponents[component]) + modifiedComponents[component] = []; + modifiedComponents[component].push(name); + } } // Send mesage(s) to the entity so it knows about researched techs for (var component in modifiedComponents) - Engine.PostMessage(msg.entity, MT_ValueModification, { "component": component }); + Engine.PostMessage(msg.entity, MT_ValueModification, { "entities": [msg.entity], "component": component, "valueNames": modifiedComponents[component] }); } } if (msg.from == playerID) @@ -322,16 +326,28 @@ TechnologyManager.prototype.ResearchTechnology = function (tech) mod[j] = modification[j]; this.modifications[modification.value].push(mod); - modifiedComponents[modification.value.split("/")[0]] = true; + var component = modification.value.split("/")[0]; + if (!modifiedComponents[component]) + modifiedComponents[component] = []; + modifiedComponents[component].push(modification.value); this.modificationCache[modification.value] = {}; } } this.UpdateAutoResearch(); + var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); + if (!cmpPlayer || ! cmpPlayer.GetPlayerID()) + return; + var playerID = cmpPlayer.GetPlayerID(); + var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); + var ents = cmpRangeManager.GetEntitiesByPlayer(playerID); // TODO: Handle technology broadcasting for autoresearch properly (some components might not be initialized currently) for (var component in modifiedComponents) - Engine.BroadcastMessage(MT_ValueModification, { "component": component }); + { + Engine.PostMessage(SYSTEM_ENTITY, MT_TemplateModification, { "player": playerID, "component": component, "valueNames": modifiedComponents[component]}); + Engine.BroadcastMessage(MT_ValueModification, { "entities": ents, "component": component, "valueNames": modifiedComponents[component]}); + } }; // Clears the cached data for an entity from the modifications cache diff --git a/source/simulation2/MessageTypes.h b/source/simulation2/MessageTypes.h index a097104336..76079afe0c 100644 --- a/source/simulation2/MessageTypes.h +++ b/source/simulation2/MessageTypes.h @@ -366,19 +366,43 @@ public: }; /** - * Sent by value modification manager when a value of a certain component is changed + * Sent by aura manager when a value of a certain entity's component is changed */ class CMessageValueModification : public CMessage { public: DEFAULT_MESSAGE_IMPL(ValueModification) - CMessageValueModification(std::wstring component) : - component(component) + CMessageValueModification(const std::vector& entities, std::wstring component, const std::vector& valueNames) : + entities(entities), + component(component), + valueNames(valueNames) { } - + + std::vector entities; std::wstring component; + std::vector valueNames; +}; + +/** + * Sent by aura and tech managers when a value of a certain template's component is changed + */ +class CMessageTemplateModification : public CMessage +{ +public: + DEFAULT_MESSAGE_IMPL(TemplateModification) + + CMessageTemplateModification(player_id_t player, std::wstring component, const std::vector& valueNames) : + player(player), + component(component), + valueNames(valueNames) + { + } + + player_id_t player; + std::wstring component; + std::vector valueNames; }; /** diff --git a/source/simulation2/TypeList.h b/source/simulation2/TypeList.h index cde9784fe1..8183c2f00e 100644 --- a/source/simulation2/TypeList.h +++ b/source/simulation2/TypeList.h @@ -48,6 +48,7 @@ MESSAGE(TerrainChanged) MESSAGE(TerritoriesChanged) MESSAGE(PathResult) MESSAGE(ValueModification) +MESSAGE(TemplateModification) MESSAGE(VisionRangeChanged) MESSAGE(MinimapPing) diff --git a/source/simulation2/components/ICmpTemplateManager.cpp b/source/simulation2/components/ICmpTemplateManager.cpp index 6792180706..c0af33ac8f 100644 --- a/source/simulation2/components/ICmpTemplateManager.cpp +++ b/source/simulation2/components/ICmpTemplateManager.cpp @@ -23,6 +23,7 @@ BEGIN_INTERFACE_WRAPPER(TemplateManager) DEFINE_INTERFACE_METHOD_1("GetTemplate", const CParamNode*, ICmpTemplateManager, GetTemplate, std::string) +DEFINE_INTERFACE_METHOD_1("GetTemplateWithoutValidation", const CParamNode*, ICmpTemplateManager, GetTemplateWithoutValidation, std::string) DEFINE_INTERFACE_METHOD_1("GetCurrentTemplateName", std::string, ICmpTemplateManager, GetCurrentTemplateName, entity_id_t) DEFINE_INTERFACE_METHOD_1("FindAllTemplates", std::vector, ICmpTemplateManager, FindAllTemplates, bool) DEFINE_INTERFACE_METHOD_1("GetEntitiesUsingTemplate", std::vector, ICmpTemplateManager, GetEntitiesUsingTemplate, std::string) diff --git a/source/simulation2/scripting/MessageTypeConversions.cpp b/source/simulation2/scripting/MessageTypeConversions.cpp index 781dec975c..a2180de468 100644 --- a/source/simulation2/scripting/MessageTypeConversions.cpp +++ b/source/simulation2/scripting/MessageTypeConversions.cpp @@ -303,15 +303,39 @@ CMessage* CMessagePathResult::FromJSVal(ScriptInterface& UNUSED(scriptInterface) jsval CMessageValueModification::ToJSVal(ScriptInterface& scriptInterface) const { TOJSVAL_SETUP(); + SET_MSG_PROPERTY(entities); SET_MSG_PROPERTY(component); + SET_MSG_PROPERTY(valueNames); return OBJECT_TO_JSVAL(obj); } CMessage* CMessageValueModification::FromJSVal(ScriptInterface& scriptInterface, jsval val) { FROMJSVAL_SETUP(); + GET_MSG_PROPERTY(std::vector, entities); GET_MSG_PROPERTY(std::wstring, component); - return new CMessageValueModification(component); + GET_MSG_PROPERTY(std::vector, valueNames); + return new CMessageValueModification(entities, component, valueNames); +} + +//////////////////////////////// + +jsval CMessageTemplateModification::ToJSVal(ScriptInterface& scriptInterface) const +{ + TOJSVAL_SETUP(); + SET_MSG_PROPERTY(player); + SET_MSG_PROPERTY(component); + SET_MSG_PROPERTY(valueNames); + return OBJECT_TO_JSVAL(obj); +} + +CMessage* CMessageTemplateModification::FromJSVal(ScriptInterface& scriptInterface, jsval val) +{ + FROMJSVAL_SETUP(); + GET_MSG_PROPERTY(player_id_t, player); + GET_MSG_PROPERTY(std::wstring, component); + GET_MSG_PROPERTY(std::vector, valueNames); + return new CMessageTemplateModification(player, component, valueNames); } ////////////////////////////////