Fix errors when loading a saved game with Aegis by making sure the AI re-inits properly. Still no real saved game support.
This was SVN commit r14329.
This commit is contained in:
parent
1c890d64ee
commit
1efd47c1ad
@ -227,56 +227,15 @@ AegisBot.prototype.chooseRandomStrategy = function()
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: Remove override when the whole AI state is serialised
|
||||
// TODO: this currently is very much equivalent to "rungamestateinit" with a few hacks. Should deserialize/serialize properly someday.
|
||||
AegisBot.prototype.Deserialize = function(data, sharedScript)
|
||||
/*AegisBot.prototype.Deserialize = function(data, sharedScript)
|
||||
{
|
||||
BaseAI.prototype.Deserialize.call(this, data);
|
||||
|
||||
var ents = sharedScript.entities.filter(Filters.byOwner(PlayerID));
|
||||
var myKeyEntities = ents.filter(function(ent) {
|
||||
return ent.hasClass("CivCentre");
|
||||
});
|
||||
|
||||
if (myKeyEntities.length == 0){
|
||||
myKeyEntities = sharedScript.entities.filter(Filters.byOwner(PlayerID));
|
||||
}
|
||||
|
||||
var filter = Filters.byClass("CivCentre");
|
||||
var enemyKeyEntities = sharedScript.entities.filter(Filters.not(Filters.byOwner(PlayerID))).filter(filter);
|
||||
|
||||
if (enemyKeyEntities.length == 0){
|
||||
enemyKeyEntities = sharedScript.entities.filter(Filters.not(Filters.byOwner(PlayerID)));
|
||||
}
|
||||
|
||||
this.terrainAnalyzer = sharedScript.terrainAnalyzer;
|
||||
this.passabilityMap = sharedScript.passabilityMap;
|
||||
|
||||
var fakeState = { "ai" : this, "sharedScript" : sharedScript };
|
||||
this.pathFinder = new aStarPath(fakeState, false, true);
|
||||
this.pathsToMe = [];
|
||||
this.pathInfo = { "angle" : 0, "needboat" : true, "mkeyPos" : myKeyEntities.toEntityArray()[0].position(), "ekeyPos" : enemyKeyEntities.toEntityArray()[0].position() };
|
||||
|
||||
// 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] + 150*Math.cos(this.pathInfo.angle),this.pathInfo.mkeyPos[1] + 150*Math.sin(this.pathInfo.angle)];
|
||||
var path = this.pathFinder.getPath(this.pathInfo.ekeyPos, pos, 2, 2);
|
||||
|
||||
if (path !== undefined && path[1] !== undefined && path[1] == false) {
|
||||
// path is viable and doesn't require boating.
|
||||
// blackzone the last two waypoints.
|
||||
this.pathFinder.markImpassableArea(path[0][0][0],path[0][0][1],20);
|
||||
this.pathsToMe.push(path[0][0][0]);
|
||||
this.pathInfo.needboat = false;
|
||||
}
|
||||
this.pathInfo.angle += Math.PI/3.0;
|
||||
};
|
||||
|
||||
// Override the default serializer
|
||||
AegisBot.prototype.Serialize = function()
|
||||
{
|
||||
//var ret = BaseAI.prototype.Serialize.call(this);
|
||||
return {};
|
||||
};
|
||||
};*/
|
||||
|
||||
function debug(output){
|
||||
if (Config.debug){
|
||||
|
@ -361,7 +361,10 @@ Defence.prototype.defendFromEnemies = function(gameState, events, HQ) {
|
||||
for (var o in this.attackerCache) {
|
||||
if ((this.attackerCacheLoopIndicator + o) % 2 === 0) {
|
||||
this.attackerCache[o].forEach(function (ent) {
|
||||
ent.attack(+o);
|
||||
var attackPos = gameState.getEntityById(+o).position()
|
||||
if (attackPos)
|
||||
ent.attackMove(attackPos[0],attackPos[1]);
|
||||
ent.setStance("aggressive");
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -555,7 +558,10 @@ Defence.prototype.defendFromEnemies = function(gameState, events, HQ) {
|
||||
defender.setMetadata(PlayerID, "formerrole", defender.getMetadata(PlayerID, "role"));
|
||||
defender.setMetadata(PlayerID, "role","defence");
|
||||
defender.setMetadata(PlayerID, "subrole","defending");
|
||||
defender.attack(+enemy.id());
|
||||
var attackPos = enemy.position();
|
||||
if (attackPos)
|
||||
defender.attackMove(attackPos[0],attackPos[1]);
|
||||
defender.setStance("aggressive");
|
||||
defender._entity.idle = false; // hack to prevent a bug as informations aren't updated during a turn
|
||||
nonDefenders.updateEnt(defender);
|
||||
assigned++;
|
||||
@ -785,7 +791,11 @@ Defence.prototype.DealWithWantedUnits = function(gameState, events, HQ) {
|
||||
ent.setMetadata(PlayerID, "formerrole", ent.getMetadata(PlayerID, "role"));
|
||||
ent.setMetadata(PlayerID, "role","defence");
|
||||
ent.setMetadata(PlayerID, "subrole", "defending");
|
||||
ent.attack(+o);
|
||||
var attackPos = gameState.getEntityById(+o).position();
|
||||
if (attackPos)
|
||||
ent.attackMove(attackPos[0],attackPos[1]);
|
||||
// TODO: should probably be an else here, unless it really can't happen
|
||||
ent.setStance("aggressive");
|
||||
if (addedto[o])
|
||||
addedto[o]++;
|
||||
else
|
||||
@ -814,7 +824,10 @@ Defence.prototype.DealWithWantedUnits = function(gameState, events, HQ) {
|
||||
ent.setMetadata(PlayerID, "formerrole", ent.getMetadata(PlayerID, "role"));
|
||||
ent.setMetadata(PlayerID, "role","defence");
|
||||
ent.setMetadata(PlayerID, "subrole", "defending");
|
||||
ent.attack(+o);
|
||||
var attackPos = gameState.getEntityById(+o).position();
|
||||
if (attackPos)
|
||||
ent.attackMove(attackPos[0],attackPos[1]);
|
||||
ent.setStance("aggressive");
|
||||
if (addedto[o])
|
||||
addedto[o]++;
|
||||
else
|
||||
|
@ -38,6 +38,9 @@ ConstructionPlan.prototype.canStart = function(gameState) {
|
||||
if (gameState.buildingsBuilt > 0)
|
||||
return false;
|
||||
|
||||
if (!this.isGo(gameState))
|
||||
return false;
|
||||
|
||||
// TODO: verify numeric limits etc
|
||||
if (this.template.requiredTech() && !gameState.isResearched(this.template.requiredTech()))
|
||||
{
|
||||
|
@ -26,6 +26,7 @@ BaseAI.prototype.Deserialize = function(data, sharedScript)
|
||||
{
|
||||
// TODO: ought to get the AI script subclass to deserialize its own state
|
||||
// TODO: actually this is part of a larger reflection on wether AIs should or not.
|
||||
this.isDeserialized = true;
|
||||
};
|
||||
|
||||
BaseAI.prototype.Init = function(state, sharedAI)
|
||||
@ -59,6 +60,13 @@ BaseAI.prototype.HandleMessage = function(state, sharedAI)
|
||||
{
|
||||
this.events = sharedAI.events;
|
||||
|
||||
if (this.isDeserialized && this.turn !== 0)
|
||||
{
|
||||
this.isDeserialized = false;
|
||||
this.Init(state, sharedAI);
|
||||
warn("AIs don't work completely with saved games yet. You may run into idle units and unused buildings.");
|
||||
} else if (this.isDeserialized)
|
||||
return;
|
||||
this.OnUpdate(sharedAI);
|
||||
};
|
||||
|
||||
|
@ -588,6 +588,12 @@ var Entity = Class({
|
||||
return this;
|
||||
},
|
||||
|
||||
attackMove: function(x, z, queued) {
|
||||
queued = queued || false;
|
||||
Engine.PostCommand({"type": "attack-walk", "entities": [this.id()], "x": x, "z": z, "queued": queued });
|
||||
return this;
|
||||
},
|
||||
|
||||
// violent, aggressive, defensive, passive, standground
|
||||
setStance: function(stance,queued){
|
||||
Engine.PostCommand({"type": "stance", "entities": [this.id()], "name" : stance, "queued": queued });
|
||||
|
@ -191,6 +191,12 @@ EntityCollection.prototype.move = function(x, z, queued)
|
||||
Engine.PostCommand({"type": "walk", "entities": this.toIdArray(), "x": x, "z": z, "queued": queued});
|
||||
return this;
|
||||
};
|
||||
EntityCollection.prototype.attackMove = function(x, z, queued)
|
||||
{
|
||||
queued = queued || false;
|
||||
Engine.PostCommand({"type": "attack-walk", "entities": this.toIdArray(), "x": x, "z": z, "queued": queued});
|
||||
return this;
|
||||
};
|
||||
EntityCollection.prototype.moveIndiv = function(x, z, queued)
|
||||
{
|
||||
queued = queued || false;
|
||||
|
@ -37,72 +37,19 @@ function SharedScript(settings)
|
||||
//Return a simple object (using no classes etc) that will be serialized
|
||||
//into saved games
|
||||
//TODO: that
|
||||
// note: we'll need to serialize much more than that before this can work.
|
||||
SharedScript.prototype.Serialize = function()
|
||||
{
|
||||
// serializing entities without using the class.
|
||||
var entities = [];
|
||||
for (var id in this._entities)
|
||||
{
|
||||
var ent = this._entities[id];
|
||||
entities.push( [ent._template, ent._entity, ent._templateName]);
|
||||
}
|
||||
|
||||
// serialiazing metadata will be done by each AI on a AI basis and they shall update the shared script with that info on deserialization (using DeserializeMetadata() ).
|
||||
// TODO: this may not be the most clever method.
|
||||
return { "entities" : entities, "techModifs" : this._techModifications, "passabClasses" : this.passabilityClasses, "passabMap" : this.passabilityMap,
|
||||
"timeElapsed" : this.timeElapsed, "techTemplates" : this._techTemplates, "players": this._players};
|
||||
return { "players" : this._players, "templates" : this._templates, "techTp" : this._techTemplates };
|
||||
};
|
||||
|
||||
// Called after the constructor when loading a saved game, with 'data' being
|
||||
// whatever Serialize() returned
|
||||
// todo: very not-finished. Mostly hacky, and ugly too.
|
||||
SharedScript.prototype.Deserialize = function(data)
|
||||
{
|
||||
this._entities = {};
|
||||
|
||||
this._players = data.players;
|
||||
this._entityMetadata = {};
|
||||
for (var i in this._players)
|
||||
this._entityMetadata[this._players[i]] = {};
|
||||
|
||||
this._techModifications = data.techModifs;
|
||||
this.techModifications = data.techModifs; // needed for entities
|
||||
|
||||
this.passabilityClasses = data.passabClasses;
|
||||
this.passabilityMap = data.passabMap;
|
||||
var dataArray = [];
|
||||
for (var i in this.passabilityMap.data)
|
||||
dataArray.push(this.passabilityMap.data[i]);
|
||||
|
||||
this.passabilityMap.data = dataArray;
|
||||
|
||||
// TODO: this is needlessly slow (not to mention a hack)
|
||||
// Should probably call a "init" function rather to avoid this and serialize the terrainanalyzer state.
|
||||
var fakeState = { "passabilityClasses" : this.passabilityClasses, "passabilityMap":this.passabilityMap };
|
||||
this.terrainAnalyzer = new TerrainAnalysis (this, fakeState);
|
||||
this.accessibility = new Accessibility(fakeState, this.terrainAnalyzer);
|
||||
|
||||
this._techTemplates = data.techTemplates;
|
||||
this.timeElapsed = data.timeElapsed;
|
||||
|
||||
// deserializing entities;
|
||||
for (var i in data.entities)
|
||||
{
|
||||
var entData = data.entities[i];
|
||||
entData[1].template = entData[2];
|
||||
this._entities[entData[1].id] = new Entity(this, entData[1]);
|
||||
}
|
||||
// entity collection updated on create/destroy event.
|
||||
this.entities = new EntityCollection(this, this._entities);
|
||||
|
||||
//deserializing game states.
|
||||
fakeState["timeElapsed"] = this.timeElapsed;
|
||||
fakeState["players"] = this._players;
|
||||
|
||||
this.gameState = {};
|
||||
for (var i in this._players)
|
||||
this.gameState[this._players[i]] = new GameState(this, fakeState, this._players[i]);
|
||||
this._templates = data.templates;
|
||||
this._techTemplates = data.techTp;
|
||||
this.isDeserialized = true;
|
||||
};
|
||||
|
||||
// Components that will be disabled in foundation entity templates.
|
||||
@ -207,6 +154,12 @@ SharedScript.prototype.init = function(state) {
|
||||
// applies entity deltas, and each gamestate.
|
||||
SharedScript.prototype.onUpdate = function(state)
|
||||
{
|
||||
if (this.isDeserialized && this.turn !== 0)
|
||||
{
|
||||
this.isDeserialized = false;
|
||||
this.init(state);
|
||||
} else if (this.isDeserialized)
|
||||
return;
|
||||
// deals with updating based on create and destroy messages.
|
||||
this.ApplyEntitiesDelta(state);
|
||||
|
||||
|
@ -892,6 +892,7 @@ public:
|
||||
virtual void Init(const CParamNode& UNUSED(paramNode))
|
||||
{
|
||||
m_TerritoriesDirtyID = 0;
|
||||
m_JustDeserialized = false;
|
||||
|
||||
StartLoadEntityTemplates();
|
||||
}
|
||||
@ -917,6 +918,8 @@ public:
|
||||
ForceLoadEntityTemplates();
|
||||
|
||||
m_Worker.Deserialize(deserialize.GetStream());
|
||||
|
||||
m_JustDeserialized = true;
|
||||
}
|
||||
|
||||
virtual void HandleMessage(const CMessage& msg, bool UNUSED(global))
|
||||
@ -1016,7 +1019,11 @@ public:
|
||||
ENSURE(cmpAIInterface);
|
||||
|
||||
// Get the game state from AIInterface
|
||||
CScriptVal state = cmpAIInterface->GetRepresentation();
|
||||
CScriptVal state;
|
||||
if (m_JustDeserialized)
|
||||
state = cmpAIInterface->GetFullRepresentation(true);
|
||||
else
|
||||
state = cmpAIInterface->GetRepresentation();
|
||||
|
||||
// Get the passability data
|
||||
Grid<u16> dummyGrid;
|
||||
@ -1040,6 +1047,8 @@ public:
|
||||
LoadPathfinderClasses(state);
|
||||
|
||||
m_Worker.StartComputation(scriptInterface.WriteStructuredClone(state.get()), *passabilityMap, *territoryMap, territoryMapDirty);
|
||||
|
||||
m_JustDeserialized = false;
|
||||
}
|
||||
|
||||
virtual void PushCommands()
|
||||
@ -1069,6 +1078,8 @@ private:
|
||||
std::vector<std::pair<std::string, const CParamNode*> > m_Templates;
|
||||
size_t m_TerritoriesDirtyID;
|
||||
|
||||
bool m_JustDeserialized;
|
||||
|
||||
void StartLoadEntityTemplates()
|
||||
{
|
||||
CmpPtr<ICmpTemplateManager> cmpTemplateManager(GetSystemEntity());
|
||||
|
Loading…
Reference in New Issue
Block a user