1
0
forked from 0ad/0ad

Fix an issue that caused a crash on serialization. Prepare the AI manager a little more for serialization.

Fix bugs with tests. Fix some other issues in the AI (attack plans deal
with walls better, choose better paths, target selection is better. Dock
building won't be tried hundreds of times if it fails.)
Changes the Oasis random map to only put a path in the middle 50% of the
time.

This was SVN commit r13230.
This commit is contained in:
wraitii 2013-03-06 11:52:41 +00:00
parent f0db42e34e
commit a45a926aeb
13 changed files with 248 additions and 75 deletions

View File

@ -265,7 +265,7 @@ terrainPainter = new LayeredPainter( [pOasisForestLight,tShoreBlend, tWater, tWa
var elevationPainter = new SmoothElevationPainter(ELEVATION_SET, -3, 15 );
createArea(placer, [terrainPainter, elevationPainter, paintClass(clWater)], null);
RMS.SetProgress(50);
if(mapSize > 150) {
if(mapSize > 150 && randInt(0,1)) {
log ("creating path through");
var pAngle = randFloat(0, TWO_PI);
var px = round(fx) + round(fractionToTiles(0.13 * cos(pAngle)));

View File

@ -266,6 +266,21 @@ var EntityTemplate = Class({
return 1;
},
// returns true if the entity can attack the given class
canAttackClass: function(saidClass) {
if (!this._template.Attack)
return false;
for (i in this._template.Attack) {
if (!this._template.Attack[i].RestrictedClasses)
continue;
var cannotAttack = this._template.Attack[i].RestrictedClasses._string.split(" ");
if (cannotAttack.indexOf(saidClass) !== -1)
return false;
}
return true;
},
buildableEntities: function() {
if (!this._template.Builder || !this._template.Builder.Entities._string)
return undefined;

View File

@ -113,6 +113,13 @@ var Filters = {
},
"dynamicProperties": ['unitAIOrderData']};
},
byCanAttack: function(saidClass){
return {"func" : function(ent){
return ent.canAttackClass(saidClass);
},
"dynamicProperties": []};
},
isSoldier: function(){
return {"func" : function(ent){

View File

@ -216,7 +216,7 @@ aStarPath.prototype.continuePath = function(gamestate)
this.openList.push(index);
}
this.isOpened[index] = true;
if (SquareVectorDistance( [this.currentSquare%w, Math.floor(this.currentSquare/w)] , target) <= this.Sampling*this.Sampling) {
if (SquareVectorDistance( [this.currentSquare%w, Math.floor(this.currentSquare/w)] , target) <= this.Sampling*this.Sampling+1) {
if (this.e != this.currentSquare)
this.parentSquare[this.e] = this.currentSquare;
found = true;

View File

@ -236,6 +236,7 @@ TerrainAnalysis.prototype.updateMapWithEvents = function(sharedAI) {
* it can also determine if any point is "probably" reachable, assuming the unit can get close enough
* for optimizations it's called after the TerrainAnalyser has finished initializing his map
* so this can use the land regions already.
*/
function Accessibility(rawState, terrainAnalyser){
var self = this;
@ -245,8 +246,11 @@ function Accessibility(rawState, terrainAnalyser){
this.regionSize = [];
this.regionSize.push(0);
// initialized to 0, so start to 1 for optimization
this.regionID = 1;
// initialized to 0, it's more optimized to start at 1 (I'm checking that if it's not 0, then it's already aprt of a region, don't touch);
// However I actually store all unpassable as region 1 (because if I don't, on some maps the toal nb of region is over 256, and it crashes as the mapis 8bit.)
// So start at 2.
this.regionID = 2;
for (var i = 0; i < this.passMap.length; ++i) {
if (this.passMap[i] === 0 && this.map[i] !== 0) { // any non-painted, non-inacessible area.
this.regionSize.push(0); // updated

View File

@ -20,6 +20,8 @@ function CityAttack(gameState, militaryManager, uniqueID, targetEnemy, type , ta
max = enemyCount[i];
}
}
if (this.targetPlayer === undefined)
return false;
debug ("Target = " +this.targetPlayer);
this.targetFinder = targetFinder || this.defaultTargetFinder;
this.type = type || "normal";
@ -184,6 +186,7 @@ function CityAttack(gameState, militaryManager, uniqueID, targetEnemy, type , ta
//debug ("after");
//Engine.DumpHeap();
return true;
};
CityAttack.prototype.getName = function(){
@ -266,7 +269,7 @@ CityAttack.prototype.updatePreparation = function(gameState, militaryManager,eve
}
}
// when we have a target, we path to it.
this.path = this.pathFinder.getPath(this.rallyPoint,this.targetPos, 2, 2);//,300000,gameState);
this.path = this.pathFinder.getPath(this.rallyPoint,this.targetPos, 4, 4,300000,gameState);
if (this.path === undefined) {
delete this.pathFinder;
@ -560,6 +563,9 @@ CityAttack.prototype.update = function(gameState, militaryManager, events){
var bool_attacked = false;
// raids don't care about attacks much
if (this.unitCollection.length === 0)
return 0;
this.position = this.unitCollection.getCentrePosition();
var IDs = this.unitCollection.toIdArray();
@ -673,32 +679,60 @@ CityAttack.prototype.update = function(gameState, militaryManager, events){
this.position = this.unitCollection.filter(Filters.not(Filters.byClass("Warship"))).getCentrePosition();
// probably not too good.
if (!this.position)
return undefined; // should spawn an error.
if (SquareVectorDistance(this.position, this.lastPosition) < 20 && this.path.length > 0) {
this.unitCollection.filter(Filters.not(Filters.byClass("Warship"))).moveIndiv(this.path[0][0][0], this.path[0][0][1]);
// We're stuck, presumably. Check if there are no walls just close to us. If so, we're arrived, and we're gonna tear down some serious stone.
var walls = gameState.getEnemyEntities().filter(Filters.and(Filters.byOwner(this.targetPlayer), Filters.byClass("StoneWall")));
var nexttoWalls = false;
walls.forEach( function (ent) {
if (!nexttoWalls && SquareVectorDistance(self.position, ent.position()) < 800)
nexttoWalls = true;
});
// there are walls but we can attack
if (nexttoWalls && this.unitCollection.filter(Filters.byCanAttack("StoneWall")).length !== 0)
{
debug ("Attack Plan " +this.type +" " +this.name +" has met walls and is not happy.");
this.state = "arrived";
} else if (nexttoWalls) {
// abort plan.
debug ("Attack Plan " +this.type +" " +this.name +" has met walls and gives up.");
return 0;
}
}
// check if our land units are close enough from the next waypoint.
if (SquareVectorDistance(this.unitCollection.filter(Filters.not(Filters.byClass("Warship"))).getCentrePosition(), this.path[0][0]) < 600) {
// okay so here basically two cases. The first one is "we need a boat at this point".
// the second one is "we need to unload at this point". The third is "normal".
if (this.path[0][1] !== true)
if (this.unitCollection.filter(Filters.byClass("Siege")).length !== 0
&& SquareVectorDistance(this.unitCollection.filter(Filters.byClass("Siege")).getCentrePosition(), this.path[0][0]) > 600)
{
this.path.shift();
if (this.path.length > 0){
this.unitCollection.filter(Filters.not(Filters.byClass("Warship"))).moveIndiv(this.path[0][0][0], this.path[0][0][1]);
} else {
debug ("Attack Plan " +this.type +" " +this.name +" has arrived to destination.");
// we must assume we've arrived at the end of the trail.
this.state = "arrived";
}
} else if (this.path[0][1] === true)
{
// okay we must load our units.
// check if we have some kind of ships.
var ships = this.unitCollection.filter(Filters.byClass("Warship"));
if (ships.length === 0)
return 0; // abort
} else {
// okay so here basically two cases. The first one is "we need a boat at this point".
// the second one is "we need to unload at this point". The third is "normal".
if (this.path[0][1] !== true)
{
this.path.shift();
if (this.path.length > 0){
this.unitCollection.filter(Filters.not(Filters.byClass("Warship"))).moveIndiv(this.path[0][0][0], this.path[0][0][1]);
} else {
debug ("Attack Plan " +this.type +" " +this.name +" has arrived to destination.");
// we must assume we've arrived at the end of the trail.
this.state = "arrived";
}
} else if (this.path[0][1] === true)
{
// okay we must load our units.
// check if we have some kind of ships.
var ships = this.unitCollection.filter(Filters.byClass("Warship"));
if (ships.length === 0)
return 0; // abort
debug ("switch to boarding");
this.state = "boarding";
debug ("switch to boarding");
this.state = "boarding";
}
}
}
} else if (this.state === "shipping") {
@ -854,7 +888,7 @@ CityAttack.prototype.update = function(gameState, militaryManager, events){
this.unitCollection.forEach( function (ent) { //}) {
if (ent.isIdle()) {
var mStruct = enemyStructures.filter(function (enemy) {// }){
if (!enemy.position()) {
if (!enemy.position() || (enemy.hasClass("StoneWall") && ent.canAttackClass("StoneWall"))) {
return false;
}
if (SquareVectorDistance(enemy.position(),ent.position()) > ent.visionRange()*ent.visionRange() + 300) {
@ -871,32 +905,54 @@ CityAttack.prototype.update = function(gameState, militaryManager, events){
}
return true;
});
var isGate = false;
mUnit = mUnit.toEntityArray();
mStruct = mStruct.toEntityArray();
mStruct.sort(function (struct) {
if (struct.hasClass("ConquestCritical"))
return 100 + struct.costSum();
else
return struct.costSum();
})
mStruct.sort(function (structa,structb) {
var vala = structa.costSum();
if (structa.hasClass("Gates") && ent.canAttackClass("StoneWall")) // we hate gates
{
isGate = true;
vala += 10000;
} else if (structa.hasClass("ConquestCritical"))
vala = 100;
var valb = structb.costSum();
if (structb.hasClass("Gates") && ent.canAttackClass("StoneWall")) // we hate gates
{
isGate = true;
valb += 10000;
} else if (structb.hasClass("ConquestCritical"))
valb = 100;
return (valb - vala);
});
if (ent.hasClass("Siege")) {
if (mStruct.length !== 0) {
var rand = Math.floor(Math.random() * mStruct.length*0.2);
ent.attack(mStruct[+rand].id());
//debug ("Siege units attacking a structure from " +mStruct[+rand].owner() + " , " +mStruct[+rand].templateName());
if (isGate)
ent.attack(mStruct[0].id());
else
{
var rand = Math.floor(Math.random() * mStruct.length*0.2);
ent.attack(mStruct[+rand].id());
//debug ("Siege units attacking a structure from " +mStruct[+rand].owner() + " , " +mStruct[+rand].templateName());
}
} else if (SquareVectorDistance(self.targetPos, ent.position()) > 900 ){
//debug ("Siege units moving to " + uneval(self.targetPos));
ent.move((self.targetPos[0] + ent.position()[0])/2,(self.targetPos[1] + ent.position()[1])/2);
}
} else {
if (mUnit.length !== 0) {
if (mUnit.length !== 0 && !isGate) {
var rand = Math.floor(Math.random() * mUnit.length*0.99);
ent.attack(mUnit[(+rand)].id());
//debug ("Units attacking a unit from " +mUnit[+rand].owner() + " , " +mUnit[+rand].templateName());
} else if (mStruct.length !== 0) {
var rand = Math.floor(Math.random() * mStruct.length*0.99);
ent.attack(mStruct[+rand].id());
//debug ("Units attacking a structure from " +mStruct[+rand].owner() + " , " +mStruct[+rand].templateName());
if (isGate)
ent.attack(mStruct[0].id());
else
{
var rand = Math.floor(Math.random() * mStruct.length*0.2);
ent.attack(mStruct[+rand].id());
//debug ("Units attacking a structure from " +mStruct[+rand].owner() + " , " +mStruct[+rand].templateName());
}
} else if (SquareVectorDistance(self.targetPos, ent.position()) > 900 ){
//debug ("Units moving to " + uneval(self.targetPos));
ent.move((self.targetPos[0] + ent.position()[0])/2,(self.targetPos[1] + ent.position()[1])/2);

View File

@ -1,9 +1,9 @@
// Baseconfig is the highest difficulty.
var baseConfig = {
"Military" : {
"fortressStartTime" : 840, // Time to wait before building one fortress.
"fortressStartTime" : 780, // Time to wait before building one fortress.
"fortressLapseTime" : 300, // Time to wait between building 2 fortresses (minimal)
"defenceBuildingTime" : 300, // Time to wait before building towers or fortresses
"defenceBuildingTime" : 600, // Time to wait before building towers or fortresses
"advancedMilitaryStartTime" : 720, // Time to wait before building advanced military buildings. Also limited by phase 2.
"attackPlansStartTime" : 0 // time to wait before attacking. Start as soon as possible (first barracks)
},
@ -11,7 +11,7 @@ var baseConfig = {
"townPhase" : 180, // time to start trying to reach town phase (might be a while after. Still need the requirements + ress )
"cityPhase" : 540, // time to start trying to reach city phase
"farmsteadStartTime" : 240, // Time to wait before building a farmstead.
"marketStartTime" : 620, // Time to wait before building the market.
"marketStartTime" : 480, // Time to wait before building the market.
"dockStartTime" : 240, // Time to wait before building the dock
"techStartTime" : 600, // time to wait before teching.
"targetNumBuilders" : 1.5, // Base number of builders per foundation. Later updated, but this remains a multiplier.
@ -58,7 +58,7 @@ var baseConfig = {
"villager" : 60,
"economicBuilding" : 80,
"dropsites" : 180,
"field" : 500,
"field" : 1000,
"militaryBuilding" : 120,
"defenceBuilding" : 17,
"majorTech" : 100,
@ -83,7 +83,7 @@ if (Config.difficulty === 1)
Config["Military"] = {
"fortressStartTime" : 1000,
"fortressLapseTime" : 400,
"defenceBuildingTime" : 350,
"defenceBuildingTime" : 720,
"advancedMilitaryStartTime" : 1000,
"attackPlansStartTime" : 600
};
@ -106,7 +106,7 @@ if (Config.difficulty === 1)
Config["Military"] = {
"fortressStartTime" : 1500,
"fortressLapseTime" : 1000000,
"defenceBuildingTime" : 500,
"defenceBuildingTime" : 900,
"advancedMilitaryStartTime" : 1300,
"attackPlansStartTime" : 1200 // 20 minutes ought to give enough times for beginners
};

View File

@ -23,6 +23,8 @@ var EconomyManager = function() {
this.farmsteadStartTime = Config.Economy.farmsteadStartTime * 1000;
this.techStartTime = Config.Economy.techStartTime * 1000;
this.dockFailed = false; // sanity check
this.resourceMaps = {}; // Contains maps showing the density of wood, stone and metal
this.CCResourceMaps = {}; // Contains maps showing the density of wood, stone and metal, optimized for CC placement.
@ -844,7 +846,7 @@ EconomyManager.prototype.buildFarmstead = function(gameState, queues){
};
EconomyManager.prototype.buildDock = function(gameState, queues){
if (!gameState.ai.waterMap)
if (!gameState.ai.waterMap || this.dockFailed)
return;
if (gameState.getTimeElapsed() > this.dockStartTime) {
if (queues.economicBuilding.countTotalQueuedUnitsWithClass("NavalMarket") === 0 &&

View File

@ -379,7 +379,7 @@ MilitaryAttackManager.prototype.buildDefences = function(gameState, queues){
+ queues.defenceBuilding.totalLength() < gameState.getEntityLimits()["DefenseTower"]
&& gameState.currentPhase() > 1) {
gameState.getOwnEntities().forEach(function(dropsiteEnt) {
if (dropsiteEnt.resourceDropsiteTypes() && dropsiteEnt.getMetadata(PlayerID, "defenseTower") !== true){
if (dropsiteEnt.resourceDropsiteTypes() && dropsiteEnt.getMetadata(PlayerID, "defenseTower") !== true && !dropsiteEnt.hasClass("CivCentre")){
var position = dropsiteEnt.position();
if (position){
queues.defenceBuilding.addItem(new BuildingConstructionPlan(gameState, 'structures/{civ}_defense_tower', position));
@ -574,14 +574,24 @@ MilitaryAttackManager.prototype.update = function(gameState, queues, events) {
&& 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);
debug ("Military Manager: Creating the plan " +this.TotalAttackNumber);
this.TotalAttackNumber++;
this.upcomingAttacks["CityAttack"].push(Lalala);
if (!Lalala)
{
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");
debug ("Military Manager: Creating the super sized plan " +this.TotalAttackNumber);
this.TotalAttackNumber++;
this.upcomingAttacks["CityAttack"].push(Lalala);
if (!Lalala)
{
this.attackPlansEncounteredWater = true; // hack
} else {
debug ("Military Manager: Creating the super sized plan " +this.TotalAttackNumber);
this.TotalAttackNumber++;
this.upcomingAttacks["CityAttack"].push(Lalala);
}
}
}
/*

View File

@ -38,6 +38,8 @@ BuildingConstructionPlan.prototype.execute = function(gameState) {
var pos = this.findGoodPosition(gameState);
if (!pos){
if (this.template.hasClass("Naval"))
gameState.ai.modules.economy.dockFailed = true;
debug("No room to place " + this.type);
return;
}

View File

@ -65,9 +65,12 @@ QBotAI.prototype.InitShared = function(gameState, sharedScript) {
this.pathsToMe = [];
this.pathInfo = { "angle" : 0, "needboat" : true, "mkeyPos" : myKeyEntities.toEntityArray()[0].position(), "ekeyPos" : enemyKeyEntities.toEntityArray()[0].position() };
var pos = [this.pathInfo.mkeyPos[0] + 140*Math.cos(this.pathInfo.angle),this.pathInfo.mkeyPos[1] + 140*Math.sin(this.pathInfo.angle)];
// 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] + 180*Math.cos(this.pathInfo.angle),this.pathInfo.mkeyPos[1] + 180*Math.sin(this.pathInfo.angle)];
var path = this.pathFinder.getPath(this.pathInfo.ekeyPos, pos, 3, 3);// uncomment for debug:*/, 300000, gameState);
//Engine.DumpImage("initialPath" + PlayerID + ".png", this.pathFinder.TotorMap.map, this.pathFinder.TotorMap.width,this.pathFinder.TotorMap.height,255);
if (path !== undefined && path[1] !== undefined && path[1] == false) {
// path is viable and doesn't require boating.
// blackzone the last two waypoints.
@ -171,7 +174,7 @@ QBotAI.prototype.OnUpdate = function(sharedScript) {
if (this.pathInfo !== undefined)
{
var pos = [this.pathInfo.mkeyPos[0] + 140*Math.cos(this.pathInfo.angle),this.pathInfo.mkeyPos[1] + 140*Math.sin(this.pathInfo.angle)];
var pos = [this.pathInfo.mkeyPos[0] + 180*Math.cos(this.pathInfo.angle),this.pathInfo.mkeyPos[1] + 180*Math.sin(this.pathInfo.angle)];
var path = this.pathFinder.getPath(this.pathInfo.ekeyPos, pos, 6, 6);// uncomment for debug:*/, 300000, gameState);
if (path !== undefined && path[1] !== undefined && path[1] == false) {
// path is viable and doesn't require boating.

View File

@ -128,10 +128,48 @@ AddMock(101, IID_EntityLimits, {
GetCounts: function() { return {"Bar": 0}; },
});
AddMock(101, IID_TechnologyManager, {
AddMock(100, IID_TechnologyManager, {
IsTechnologyResearched: function(tech) { if (tech == "phase_village") return true; else return false; },
GetQueuedResearch: function() { return {}; },
GetStartedResearch: function() { return {}; },
GetResearchedTechs: function() { return {}; },
GetClassCounts: function() { return {}; },
GetTypeCountsByClass: function() { return {}; },
GetTechModifications: function() { return {}; },
});
AddMock(100, IID_StatisticsTracker, {
GetStatistics: function() {
return {
"unitsTrained": 10,
"unitsLost": 9,
"buildingsConstructed": 5,
"buildingsLost": 4,
"civCentresBuilt": 1,
"resourcesGathered": {
"food": 100,
"wood": 0,
"metal": 0,
"stone": 0,
"vegetarianFood": 0,
},
"treasuresCollected": 0,
"percentMapExplored": 10,
};
},
IncreaseTrainedUnitsCounter: function() { return 1; },
IncreaseConstructedBuildingsCounter: function() { return 1; },
IncreaseBuiltCivCentresCounter: function() { return 1; },
});
AddMock(101, IID_TechnologyManager, {
IsTechnologyResearched: function(tech) { if (tech == "phase_village") return true; else return false; },
GetQueuedResearch: function() { return {}; },
GetStartedResearch: function() { return {}; },
GetResearchedTechs: function() { return {}; },
GetClassCounts: function() { return {}; },
GetTypeCountsByClass: function() { return {}; },
GetTechModifications: function() { return {}; },
});
AddMock(101, IID_StatisticsTracker, {
GetStatistics: function() {
@ -174,13 +212,18 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetSimulationState(), {
state: "active",
team: -1,
teamsLocked: false,
phase: "",
phase: "village",
isAlly: [false, false],
isNeutral: [false, false],
isEnemy: [true, true],
entityLimits: {"Foo": 10},
entityCounts: {"Foo": 5},
techModifications: {},
researchQueued: {},
researchStarted: {},
researchedTechs: {},
classCounts: {},
typeCountsByClass: {},
},
{
name: "Player 2",
@ -201,6 +244,11 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetSimulationState(), {
entityLimits: {"Bar": 20},
entityCounts: {"Bar": 0},
techModifications: {},
researchQueued: {},
researchStarted: {},
researchedTechs: {},
classCounts: {},
typeCountsByClass: {},
}
],
circularMap: false,
@ -221,13 +269,18 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetExtendedSimulationState(), {
state: "active",
team: -1,
teamsLocked: false,
phase: "",
phase: "village",
isAlly: [false, false],
isNeutral: [false, false],
isEnemy: [true, true],
entityLimits: {"Foo": 10},
entityCounts: {"Foo": 5},
techModifications: {},
researchQueued: {},
researchStarted: {},
researchedTechs: {},
classCounts: {},
typeCountsByClass: {},
statistics: {
unitsTrained: 10,
unitsLost: 9,
@ -264,6 +317,11 @@ TS_ASSERT_UNEVAL_EQUALS(cmp.GetExtendedSimulationState(), {
entityLimits: {"Bar": 20},
entityCounts: {"Bar": 0},
techModifications: {},
researchQueued: {},
researchStarted: {},
researchedTechs: {},
classCounts: {},
typeCountsByClass: {},
statistics: {
unitsTrained: 10,
unitsLost: 9,

View File

@ -359,7 +359,7 @@ public:
JS_GC(self->m_ScriptInterface.GetContext());
}
bool TryLoadSharedComponent(bool callConstructor)
bool TryLoadSharedComponent(bool hasTechs)
{
// only load if there are AI players.
if (m_Players.size() == 0)
@ -370,7 +370,7 @@ public:
// reset the value so it can be used to determine if we actually initialized it.
m_HasSharedComponent = false;
VfsPaths sharedPathnames;
// Check for "shared" module.
vfs::GetPathnames(g_VFS, L"simulation/ai/common-api-v3/", L"*.js", sharedPathnames);
@ -423,10 +423,27 @@ public:
}
else
{
// For deserialization, we want to create the object with the correct prototype
// but don't want to actually run the constructor again
// XXX: actually we don't currently use this path for deserialization - maybe delete it?
m_SharedAIObj = CScriptValRooted(m_ScriptInterface.GetContext(),m_ScriptInterface.NewObjectFromConstructor(ctor.get()));
// won't get the tech templates directly.
// Set up the data to pass as the constructor argument
CScriptVal settings;
m_ScriptInterface.Eval(L"({})", settings);
CScriptVal playersID;
m_ScriptInterface.Eval(L"({})", playersID);
for (size_t i = 0; i < m_Players.size(); ++i)
{
jsval val = m_ScriptInterface.ToJSVal(m_ScriptInterface.GetContext(), m_Players[i]->m_Player);
m_ScriptInterface.SetPropertyInt(playersID.get(), i, CScriptVal(val), true);
}
m_ScriptInterface.SetProperty(settings.get(), "players", playersID);
CScriptVal m_fakeTech;
m_ScriptInterface.Eval("({})", m_fakeTech);
m_ScriptInterface.SetProperty(settings.get(), "techTemplates", m_fakeTech, false);
ENSURE(m_HasLoadedEntityTemplates);
m_ScriptInterface.SetProperty(settings.get(), "templates", m_EntityTemplates, false);
m_SharedAIObj = CScriptValRooted(m_ScriptInterface.GetContext(),m_ScriptInterface.CallConstructor(ctor.get(), settings.get()));
}
if (m_SharedAIObj.undefined())
@ -640,6 +657,7 @@ public:
if (!m_ScriptInterface.CallFunctionVoid(m_Players.back()->m_Obj.get(), "Deserialize", scriptData))
LOGERROR(L"AI script Deserialize call failed");
}
TryLoadSharedComponent(false);
}
int getPlayerSize()
@ -774,17 +792,6 @@ public:
m_TerritoriesDirtyID = 0;
StartLoadEntityTemplates();
// loading the technology template
ScriptInterface& scriptInterface = GetSimContext().GetScriptInterface();
CmpPtr<ICmpTechnologyTemplateManager> cmpTechTemplateManager(GetSimContext(), SYSTEM_ENTITY);
ENSURE(cmpTechTemplateManager);
// Get the game state from AIInterface
CScriptVal techTemplates = cmpTechTemplateManager->GetAllTechs();
m_Worker.RegisterTechTemplates(scriptInterface.WriteStructuredClone(techTemplates.get()));
}
virtual void Deinit()
@ -847,6 +854,15 @@ public:
virtual void TryLoadSharedComponent()
{
ScriptInterface& scriptInterface = GetSimContext().GetScriptInterface();
// load the technology templates
CmpPtr<ICmpTechnologyTemplateManager> cmpTechTemplateManager(GetSimContext(), SYSTEM_ENTITY);
ENSURE(cmpTechTemplateManager);
// Get the game state from AIInterface
CScriptVal techTemplates = cmpTechTemplateManager->GetAllTechs();
m_Worker.RegisterTechTemplates(scriptInterface.WriteStructuredClone(techTemplates.get()));
m_Worker.TryLoadSharedComponent(true);
}