Index: ps/trunk/binaries/data/mods/public/simulation/components/Mirage.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/Mirage.js (revision 25078)
+++ ps/trunk/binaries/data/mods/public/simulation/components/Mirage.js (revision 25079)
@@ -1,233 +1,277 @@
const VIS_HIDDEN = 0;
const VIS_FOGGED = 1;
const VIS_VISIBLE = 2;
function Mirage() {}
Mirage.prototype.Schema =
"Mirage entities replace real entities in the fog-of-war." +
"";
Mirage.prototype.Init = function()
{
- this.player = null;
this.parent = INVALID_ENTITY;
+ this.player = null;
- this.miragedIids = new Set();
-
- this.classesList = [];
-
- this.numBuilders = 0;
- this.buildTime = {};
-
- this.maxHitpoints = null;
- this.hitpoints = null;
- this.repairable = null;
- this.unhealable = null;
- this.injured = null;
-
- this.capturePoints = [];
- this.maxCapturePoints = 0;
-
- this.maxAmount = null;
- this.amount = null;
- this.type = null;
- this.isInfinite = null;
- this.killBeforeGather = null;
- this.maxGatherers = null;
- this.numGatherers = null;
-
- this.traders = null;
- this.marketType = null;
- this.internationalBonus = null;
+ this.miragedIids = new Map();
};
Mirage.prototype.SetParent = function(ent)
{
this.parent = ent;
};
Mirage.prototype.GetParent = function()
{
return this.parent;
};
Mirage.prototype.SetPlayer = function(player)
{
this.player = player;
};
Mirage.prototype.GetPlayer = function()
{
return this.player;
};
Mirage.prototype.Mirages = function(iid)
{
return this.miragedIids.has(iid);
};
+Mirage.prototype.Get = function(iid)
+{
+ return this.miragedIids.get(iid);
+};
+
// ============================
// Parent entity data
-Mirage.prototype.CopyIdentity = function(cmpIdentity)
+function MiragedIdentity() {}
+MiragedIdentity.prototype.Init = function(cmpIdentity)
{
- this.miragedIids.add(IID_Identity);
// Mirages don't get identity classes via the template-filter, so that code can query
// identity components via Engine.QueryInterface without having to explicitly check for mirages.
// This is cloned as otherwise we get a reference to Identity's property,
// and that array is deleted when serializing (as it's not seralized), which ends in OOS.
- this.classesList = clone(cmpIdentity.GetClassesList());
+ this.classes = clone(cmpIdentity.GetClassesList());
};
-Mirage.prototype.GetClassesList = function() { return this.classesList; };
+MiragedIdentity.prototype.GetClassesList = function() { return this.classes; };
+Engine.RegisterGlobal("MiragedIdentity", MiragedIdentity);
+
+Mirage.prototype.CopyIdentity = function(cmpIdentity)
+{
+ let mirage = new MiragedIdentity();
+ mirage.Init(cmpIdentity);
+ this.miragedIids.set(IID_Identity, mirage);
+};
// Foundation data
-Mirage.prototype.CopyFoundation = function(cmpFoundation)
+function MiragedFoundation() {}
+MiragedFoundation.prototype.Init = function(cmpFoundation)
{
- this.miragedIids.add(IID_Foundation);
this.numBuilders = cmpFoundation.GetNumBuilders();
this.buildTime = cmpFoundation.GetBuildTime();
};
-Mirage.prototype.GetNumBuilders = function() { return this.numBuilders; };
-Mirage.prototype.GetBuildTime = function() { return this.buildTime; };
+MiragedFoundation.prototype.GetNumBuilders = function() { return this.numBuilders; };
+MiragedFoundation.prototype.GetBuildTime = function() { return this.buildTime; };
+Engine.RegisterGlobal("MiragedFoundation", MiragedFoundation);
-// Repairable data (numBuilders and buildTime shared with foundation as entities can't have both)
+Mirage.prototype.CopyFoundation = function(cmpFoundation)
+{
+ let mirage = new MiragedFoundation();
+ mirage.Init(cmpFoundation);
+ this.miragedIids.set(IID_Foundation, mirage);
+};
-Mirage.prototype.CopyRepairable = function(cmpRepairable)
+// Repairable data
+
+function MiragedRepairable() {}
+MiragedRepairable.prototype.Init = function(cmpRepairable)
{
- this.miragedIids.add(IID_Repairable);
this.numBuilders = cmpRepairable.GetNumBuilders();
this.buildTime = cmpRepairable.GetBuildTime();
};
+MiragedRepairable.prototype.GetNumBuilders = function() { return this.numBuilders; };
+MiragedRepairable.prototype.GetBuildTime = function() { return this.buildTime; };
+Engine.RegisterGlobal("MiragedRepairable", MiragedRepairable);
+
+Mirage.prototype.CopyRepairable = function(cmpRepairable)
+{
+ let mirage = new MiragedRepairable();
+ mirage.Init(cmpRepairable);
+ this.miragedIids.set(IID_Repairable, mirage);
+};
+
// Health data
-Mirage.prototype.CopyHealth = function(cmpHealth)
+function MiragedHealth() {}
+MiragedHealth.prototype.Init = function(cmpHealth)
{
- this.miragedIids.add(IID_Health);
this.maxHitpoints = cmpHealth.GetMaxHitpoints();
this.hitpoints = cmpHealth.GetHitpoints();
this.repairable = cmpHealth.IsRepairable();
this.injured = cmpHealth.IsInjured();
this.unhealable = cmpHealth.IsUnhealable();
};
-Mirage.prototype.GetMaxHitpoints = function() { return this.maxHitpoints; };
-Mirage.prototype.GetHitpoints = function() { return this.hitpoints; };
-Mirage.prototype.IsRepairable = function() { return this.repairable; };
-Mirage.prototype.IsInjured = function() { return this.injured; };
-Mirage.prototype.IsUnhealable = function() { return this.unhealable; };
+MiragedHealth.prototype.GetMaxHitpoints = function() { return this.maxHitpoints; };
+MiragedHealth.prototype.GetHitpoints = function() { return this.hitpoints; };
+MiragedHealth.prototype.IsRepairable = function() { return this.repairable; };
+MiragedHealth.prototype.IsInjured = function() { return this.injured; };
+MiragedHealth.prototype.IsUnhealable = function() { return this.unhealable; };
+Engine.RegisterGlobal("MiragedHealth", MiragedHealth);
+
+Mirage.prototype.CopyHealth = function(cmpHealth)
+{
+ let mirage = new MiragedHealth();
+ mirage.Init(cmpHealth);
+ this.miragedIids.set(IID_Health, mirage);
+};
// Capture data
-Mirage.prototype.CopyCapturable = function(cmpCapturable)
+function MiragedCapturable() {}
+MiragedCapturable.prototype.Init = function(cmpCapturable)
{
- this.miragedIids.add(IID_Capturable);
this.capturePoints = clone(cmpCapturable.GetCapturePoints());
this.maxCapturePoints = cmpCapturable.GetMaxCapturePoints();
};
-Mirage.prototype.GetMaxCapturePoints = function() { return this.maxCapturePoints; };
-Mirage.prototype.GetCapturePoints = function() { return this.capturePoints; };
+MiragedCapturable.prototype.GetCapturePoints = function() { return this.capturePoints; };
+MiragedCapturable.prototype.GetMaxCapturePoints = function() { return this.maxCapturePoints; };
+MiragedCapturable.prototype.CanCapture = Capturable.prototype.CanCapture;
+Engine.RegisterGlobal("MiragedCapturable", MiragedCapturable);
-Mirage.prototype.CanCapture = Capturable.prototype.CanCapture;
+Mirage.prototype.CopyCapturable = function(cmpCapturable)
+{
+ let mirage = new MiragedCapturable();
+ mirage.Init(cmpCapturable);
+ this.miragedIids.set(IID_Capturable, mirage);
+};
// ResourceSupply data
-
-Mirage.prototype.CopyResourceSupply = function(cmpResourceSupply)
+function MiragedResourceSupply() {}
+MiragedResourceSupply.prototype.Init = function(cmpResourceSupply)
{
- this.miragedIids.add(IID_ResourceSupply);
this.maxAmount = cmpResourceSupply.GetMaxAmount();
this.amount = cmpResourceSupply.GetCurrentAmount();
this.type = cmpResourceSupply.GetType();
this.isInfinite = cmpResourceSupply.IsInfinite();
this.killBeforeGather = cmpResourceSupply.GetKillBeforeGather();
this.maxGatherers = cmpResourceSupply.GetMaxGatherers();
this.numGatherers = cmpResourceSupply.GetNumGatherers();
};
-Mirage.prototype.GetMaxAmount = function() { return this.maxAmount; };
-Mirage.prototype.GetCurrentAmount = function() { return this.amount; };
-Mirage.prototype.GetType = function() { return this.type; };
-Mirage.prototype.IsInfinite = function() { return this.isInfinite; };
-Mirage.prototype.GetKillBeforeGather = function() { return this.killBeforeGather; };
-Mirage.prototype.GetMaxGatherers = function() { return this.maxGatherers; };
-Mirage.prototype.GetNumGatherers = function() { return this.numGatherers; };
+MiragedResourceSupply.prototype.GetMaxAmount = function() { return this.maxAmount; };
+MiragedResourceSupply.prototype.GetCurrentAmount = function() { return this.amount; };
+MiragedResourceSupply.prototype.GetType = function() { return this.type; };
+MiragedResourceSupply.prototype.IsInfinite = function() { return this.isInfinite; };
+MiragedResourceSupply.prototype.GetKillBeforeGather = function() { return this.killBeforeGather; };
+MiragedResourceSupply.prototype.GetMaxGatherers = function() { return this.maxGatherers; };
+MiragedResourceSupply.prototype.GetNumGatherers = function() { return this.numGatherers; };
+
+// Apply diminishing returns with more gatherers, for e.g. infinite farms. For most resources this has no effect
+// (GetDiminishingReturns will return null). We can assume that for resources that are miraged this is the case.
+MiragedResourceSupply.prototype.GetDiminishingReturns = function() { return null; };
-// Market data
+Engine.RegisterGlobal("MiragedResourceSupply", MiragedResourceSupply);
-Mirage.prototype.CopyMarket = function(cmpMarket)
+Mirage.prototype.CopyResourceSupply = function(cmpResourceSupply)
+{
+ let mirage = new MiragedResourceSupply();
+ mirage.Init(cmpResourceSupply);
+ this.miragedIids.set(IID_ResourceSupply, mirage);
+};
+
+// Market data
+function MiragedMarket() {}
+MiragedMarket.prototype.Init = function(cmpMarket, entity, parent, player)
{
- this.miragedIids.add(IID_Market);
+ this.entity = entity;
+ this.parent = parent;
+ this.player = player;
+
this.traders = new Set();
for (let trader of cmpMarket.GetTraders())
{
let cmpTrader = Engine.QueryInterface(trader, IID_Trader);
let cmpOwnership = Engine.QueryInterface(trader, IID_Ownership);
if (!cmpTrader || !cmpOwnership)
{
cmpMarket.RemoveTrader(trader);
continue;
}
if (this.player != cmpOwnership.GetOwner())
continue;
cmpTrader.SwitchMarket(cmpMarket.entity, this.entity);
cmpMarket.RemoveTrader(trader);
this.AddTrader(trader);
}
this.marketType = cmpMarket.GetType();
this.internationalBonus = cmpMarket.GetInternationalBonus();
};
-Mirage.prototype.HasType = function(type) { return this.marketType.has(type); };
-Mirage.prototype.GetInternationalBonus = function() { return this.internationalBonus; };
-Mirage.prototype.AddTrader = function(trader) { this.traders.add(trader); };
-Mirage.prototype.RemoveTrader = function(trader) { this.traders.delete(trader); };
+MiragedMarket.prototype.HasType = function(type) { return this.marketType.has(type); };
+MiragedMarket.prototype.GetInternationalBonus = function() { return this.internationalBonus; };
+MiragedMarket.prototype.AddTrader = function(trader) { this.traders.add(trader); };
+MiragedMarket.prototype.RemoveTrader = function(trader) { this.traders.delete(trader); };
-Mirage.prototype.UpdateTraders = function(msg)
+MiragedMarket.prototype.UpdateTraders = function(msg)
{
let cmpMarket = Engine.QueryInterface(this.parent, IID_Market);
if (!cmpMarket) // The parent market does not exist anymore
{
for (let trader of this.traders)
{
let cmpTrader = Engine.QueryInterface(trader, IID_Trader);
if (cmpTrader)
cmpTrader.RemoveMarket(this.entity);
}
return;
}
// The market becomes visible, switch all traders from the mirage to the market
for (let trader of this.traders)
{
let cmpTrader = Engine.QueryInterface(trader, IID_Trader);
if (!cmpTrader)
continue;
cmpTrader.SwitchMarket(this.entity, cmpMarket.entity);
this.RemoveTrader(trader);
cmpMarket.AddTrader(trader);
}
};
+Engine.RegisterGlobal("MiragedMarket", MiragedMarket);
+
+Mirage.prototype.CopyMarket = function(cmpMarket)
+{
+ let mirage = new MiragedMarket();
+ mirage.Init(cmpMarket, this.entity, this.parent, this.player);
+ this.miragedIids.set(IID_Market, mirage);
+};
// ============================
Mirage.prototype.OnVisibilityChanged = function(msg)
{
// Mirages get VIS_HIDDEN when the original entity becomes VIS_VISIBLE.
if (msg.player != this.player || msg.newVisibility != VIS_HIDDEN)
return;
if (this.miragedIids.has(IID_Market))
- this.UpdateTraders(msg);
+ this.miragedIids.get(IID_Market).UpdateTraders(msg);
if (this.parent == INVALID_ENTITY)
Engine.DestroyEntity(this.entity);
else
Engine.PostMessage(this.entity, MT_EntityRenamed, { "entity": this.entity, "newentity": this.parent });
};
Engine.RegisterComponentType(IID_Mirage, "Mirage", Mirage);
Index: ps/trunk/binaries/data/mods/public/simulation/components/ResourceGatherer.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/ResourceGatherer.js (revision 25078)
+++ ps/trunk/binaries/data/mods/public/simulation/components/ResourceGatherer.js (revision 25079)
@@ -1,397 +1,391 @@
function ResourceGatherer() {}
ResourceGatherer.prototype.Schema =
"Lets the unit gather resources from entities that have the ResourceSupply component." +
"" +
"2.0" +
"1.0" +
"" +
"1" +
"3" +
"3" +
"2" +
"" +
"" +
"10" +
"10" +
"10" +
"10" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
Resources.BuildSchema("positiveDecimal", [], true) +
"" +
"" +
Resources.BuildSchema("positiveDecimal") +
"";
ResourceGatherer.prototype.Init = function()
{
this.capacities = {};
this.carrying = {}; // { generic type: integer amount currently carried }
// (Note that this component supports carrying multiple types of resources,
// each with an independent capacity, but the rest of the game currently
// ensures and assumes we'll only be carrying one type at once)
// The last exact type gathered, so we can render appropriate props
this.lastCarriedType = undefined; // { generic, specific }
};
/**
* Returns data about what resources the unit is currently carrying,
* in the form [ {"type":"wood", "amount":7, "max":10} ]
*/
ResourceGatherer.prototype.GetCarryingStatus = function()
{
let ret = [];
for (let type in this.carrying)
{
ret.push({
"type": type,
"amount": this.carrying[type],
"max": +this.GetCapacity(type)
});
}
return ret;
};
/**
* Used to instantly give resources to unit
* @param resources The same structure as returned form GetCarryingStatus
*/
ResourceGatherer.prototype.GiveResources = function(resources)
{
for (let resource of resources)
this.carrying[resource.type] = +resource.amount;
Engine.PostMessage(this.entity, MT_ResourceCarryingChanged, { "to": this.GetCarryingStatus() });
};
/**
* Returns the generic type of one particular resource this unit is
* currently carrying, or undefined if none.
*/
ResourceGatherer.prototype.GetMainCarryingType = function()
{
// Return the first key, if any
for (let type in this.carrying)
return type;
return undefined;
};
/**
* Returns the exact resource type we last picked up, as long as
* we're still carrying something similar enough, in the form
* { generic, specific }
*/
ResourceGatherer.prototype.GetLastCarriedType = function()
{
if (this.lastCarriedType && this.lastCarriedType.generic in this.carrying)
return this.lastCarriedType;
return undefined;
};
ResourceGatherer.prototype.SetLastCarriedType = function(lastCarriedType)
{
this.lastCarriedType = lastCarriedType;
};
// Since this code is very performancecritical and applying technologies quite slow, cache it.
ResourceGatherer.prototype.RecalculateGatherRates = function()
{
this.baseSpeed = ApplyValueModificationsToEntity("ResourceGatherer/BaseSpeed", +this.template.BaseSpeed, this.entity);
this.rates = {};
for (let r in this.template.Rates)
{
let type = r.split(".");
if (!Resources.GetResource(type[0]).subtypes[type[1]])
{
error("Resource subtype not found: " + type[0] + "." + type[1]);
continue;
}
let rate = ApplyValueModificationsToEntity("ResourceGatherer/Rates/" + r, +this.template.Rates[r], this.entity);
this.rates[r] = rate * this.baseSpeed;
}
};
ResourceGatherer.prototype.RecalculateCapacities = function()
{
this.capacities = {};
for (let r in this.template.Capacities)
this.capacities[r] = ApplyValueModificationsToEntity("ResourceGatherer/Capacities/" + r, +this.template.Capacities[r], this.entity);
};
ResourceGatherer.prototype.RecalculateCapacity = function(type)
{
if (type in this.capacities)
this.capacities[type] = ApplyValueModificationsToEntity("ResourceGatherer/Capacities/" + type, +this.template.Capacities[type], this.entity);
};
ResourceGatherer.prototype.GetGatherRates = function()
{
return this.rates;
};
ResourceGatherer.prototype.GetGatherRate = function(resourceType)
{
if (!this.template.Rates[resourceType])
return 0;
return this.rates[resourceType];
};
ResourceGatherer.prototype.GetCapacity = function(resourceType)
{
if (!this.template.Capacities[resourceType])
return 0;
return this.capacities[resourceType];
};
ResourceGatherer.prototype.GetRange = function()
{
return { "max": +this.template.MaxDistance, "min": 0 };
// maybe this should depend on the unit or target or something?
};
/**
* Gather from the target entity. This should only be called after a successful range check,
* and if the target has a compatible ResourceSupply.
* Call interval will be determined by gather rate, so always gather 1 amount when called.
*/
ResourceGatherer.prototype.PerformGather = function(target)
{
if (!this.GetTargetGatherRate(target))
return { "exhausted": true };
let gatherAmount = 1;
let cmpResourceSupply = Engine.QueryInterface(target, IID_ResourceSupply);
let type = cmpResourceSupply.GetType();
// Initialise the carried count if necessary
if (!this.carrying[type.generic])
this.carrying[type.generic] = 0;
// Find the maximum so we won't exceed our capacity
let maxGathered = this.GetCapacity(type.generic) - this.carrying[type.generic];
let status = cmpResourceSupply.TakeResources(Math.min(gatherAmount, maxGathered));
this.carrying[type.generic] += status.amount;
this.lastCarriedType = type;
// Update stats of how much the player collected.
// (We have to do it here rather than at the dropsite, because we
// need to know what subtype it was)
let cmpStatisticsTracker = QueryOwnerInterface(this.entity, IID_StatisticsTracker);
if (cmpStatisticsTracker)
cmpStatisticsTracker.IncreaseResourceGatheredCounter(type.generic, status.amount, type.specific);
Engine.PostMessage(this.entity, MT_ResourceCarryingChanged, { "to": this.GetCarryingStatus() });
return {
"amount": status.amount,
"exhausted": status.exhausted,
"filled": this.carrying[type.generic] >= this.GetCapacity(type.generic)
};
};
/**
* Compute the amount of resources collected per second from the target.
* Returns 0 if resources cannot be collected (e.g. the target doesn't
* exist, or is the wrong type).
*/
ResourceGatherer.prototype.GetTargetGatherRate = function(target)
{
let cmpResourceSupply = QueryMiragedInterface(target, IID_ResourceSupply);
if (!cmpResourceSupply)
return 0;
let type = cmpResourceSupply.GetType();
let rate = 0;
if (type.specific)
rate = this.GetGatherRate(type.generic+"."+type.specific);
if (rate == 0 && type.generic)
rate = this.GetGatherRate(type.generic);
- if ("Mirages" in cmpResourceSupply)
- return rate;
-
- // Apply diminishing returns with more gatherers, for e.g. infinite farms. For most resources this has no effect
- // (GetDiminishingReturns will return null). We can assume that for resources that are miraged this is the case
- // (else just add the diminishing returns data to the mirage data and remove the early return above)
let diminishingReturns = cmpResourceSupply.GetDiminishingReturns();
if (diminishingReturns)
rate *= diminishingReturns;
return rate;
};
/**
* Returns whether this unit can carry more of the given type of resource.
* (This ignores whether the unit is actually able to gather that
* resource type or not.)
*/
ResourceGatherer.prototype.CanCarryMore = function(type)
{
let amount = this.carrying[type] || 0;
return amount < this.GetCapacity(type);
};
ResourceGatherer.prototype.IsCarrying = function(type)
{
let amount = this.carrying[type] || 0;
return amount > 0;
};
/**
* Returns whether this unit is carrying any resources of a type that is
* not the requested type. (This is to support cases where the unit is
* only meant to be able to carry one type at once.)
*/
ResourceGatherer.prototype.IsCarryingAnythingExcept = function(exceptedType)
{
for (let type in this.carrying)
if (type != exceptedType)
return true;
return false;
};
/**
* Transfer our carried resources to our owner immediately.
* Only resources of the appropriate types will be transferred.
* (This should typically be called after reaching a dropsite.)
*
* @param {number} target - The target entity ID to drop resources at.
*/
ResourceGatherer.prototype.CommitResources = function(target)
{
let cmpResourceDropsite = Engine.QueryInterface(target, IID_ResourceDropsite);
if (!cmpResourceDropsite)
return;
let change = cmpResourceDropsite.ReceiveResources(this.carrying, this.entity);
let changed = false;
for (let type in change)
{
this.carrying[type] -= change[type];
if (this.carrying[type] == 0)
delete this.carrying[type];
changed = true;
}
if (changed)
Engine.PostMessage(this.entity, MT_ResourceCarryingChanged, { "to": this.GetCarryingStatus() });
};
/**
* Drop all currently-carried resources.
* (Currently they just vanish after being dropped - we don't bother depositing
* them onto the ground.)
*/
ResourceGatherer.prototype.DropResources = function()
{
this.carrying = {};
Engine.PostMessage(this.entity, MT_ResourceCarryingChanged, { "to": this.GetCarryingStatus() });
};
/**
* @param {string} type - A generic resource type.
*/
ResourceGatherer.prototype.AddToPlayerCounter = function(type)
{
// We need to be removed from the player counter first.
if (this.lastGathered)
return;
let cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
if (cmpPlayer)
cmpPlayer.AddResourceGatherer(type);
this.lastGathered = type;
};
/**
* @param {number} playerid - Optionally a player ID.
*/
ResourceGatherer.prototype.RemoveFromPlayerCounter = function(playerid)
{
if (!this.lastGathered)
return;
let cmpPlayer = playerid != undefined ?
QueryPlayerIDInterface(playerid) :
QueryOwnerInterface(this.entity, IID_Player);
if (cmpPlayer)
cmpPlayer.RemoveResourceGatherer(this.lastGathered);
delete this.lastGathered;
};
// Since we cache gather rates, we need to make sure we update them when tech changes.
// and when our owner change because owners can had different techs.
ResourceGatherer.prototype.OnValueModification = function(msg)
{
if (msg.component != "ResourceGatherer")
return;
// NB: at the moment, 0 A.D. always uses the fast path, the other is mod support.
if (msg.valueNames.length === 1)
{
if (msg.valueNames[0].indexOf("Capacities") !== -1)
this.RecalculateCapacity(msg.valueNames[0].substr(28));
else
this.RecalculateGatherRates();
}
else
{
this.RecalculateGatherRates();
this.RecalculateCapacities();
}
};
ResourceGatherer.prototype.OnOwnershipChanged = function(msg)
{
if (msg.to == INVALID_PLAYER)
{
this.RemoveFromPlayerCounter(msg.from);
return;
}
this.RecalculateGatherRates();
this.RecalculateCapacities();
};
ResourceGatherer.prototype.OnGlobalInitGame = function(msg)
{
this.RecalculateGatherRates();
this.RecalculateCapacities();
};
ResourceGatherer.prototype.OnMultiplierChanged = function(msg)
{
let cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
if (cmpPlayer && msg.player == cmpPlayer.GetPlayerID())
this.RecalculateGatherRates();
};
Engine.RegisterComponentType(IID_ResourceGatherer, "ResourceGatherer", ResourceGatherer);
Index: ps/trunk/binaries/data/mods/public/simulation/helpers/Player.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/helpers/Player.js (revision 25078)
+++ ps/trunk/binaries/data/mods/public/simulation/helpers/Player.js (revision 25079)
@@ -1,407 +1,407 @@
/**
* Used to create player entities prior to reading the rest of a map,
* all other initialization must be done after loading map (terrain/entities).
* DO NOT use other components here, as they may fail unpredictably.
* settings is the object containing settings for this map.
* newPlayers if true will remove old player entities or add new ones until
* the new number of player entities is obtained
* (used when loading a map or when Atlas changes the number of players).
*/
function LoadPlayerSettings(settings, newPlayers)
{
var playerDefaults = Engine.ReadJSONFile("simulation/data/settings/player_defaults.json").PlayerData;
// Default settings
if (!settings)
settings = {};
// Add gaia to simplify iteration
// (if gaia is not already the first civ such as when called from Atlas' ActorViewer)
if (settings.PlayerData && settings.PlayerData[0] &&
(!settings.PlayerData[0].Civ || settings.PlayerData[0].Civ != "gaia"))
settings.PlayerData.unshift(null);
var playerData = settings.PlayerData;
// Disable the AIIinterface when no AI players are present
if (playerData && !playerData.some(v => v && !!v.AI))
Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface).Disable();
var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
var numPlayers = cmpPlayerManager.GetNumPlayers();
var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
// Remove existing players or add new ones
if (newPlayers)
{
var settingsNumPlayers = 9; // default 8 players + gaia
if (playerData)
settingsNumPlayers = playerData.length; // includes gaia (see above)
else
warn("Player.js: Setup has no player data - using defaults");
while (settingsNumPlayers > numPlayers)
{
// Add player entity to engine
var entID = Engine.AddEntity(GetPlayerTemplateName(getSetting(playerData, playerDefaults, numPlayers, "Civ")));
var cmpPlayer = Engine.QueryInterface(entID, IID_Player);
if (!cmpPlayer)
throw new Error("Player.js: Error creating player entity " + numPlayers);
cmpPlayerManager.AddPlayer(entID);
++numPlayers;
}
while (settingsNumPlayers < numPlayers)
{
cmpPlayerManager.RemoveLastPlayer();
--numPlayers;
}
}
// Even when no new player, we must check the template compatibility as player template may be civ dependent
for (var i = 0; i < numPlayers; ++i)
{
var template = GetPlayerTemplateName(getSetting(playerData, playerDefaults, i, "Civ"));
var entID = cmpPlayerManager.GetPlayerByID(i);
if (cmpTemplateManager.GetCurrentTemplateName(entID) === template)
continue;
// We need to recreate this player to have the right template
entID = Engine.AddEntity(template);
cmpPlayerManager.ReplacePlayer(i, entID);
}
// Initialize the player data
for (var i = 0; i < numPlayers; ++i)
{
let cmpPlayer = QueryPlayerIDInterface(i);
cmpPlayer.SetName(getSetting(playerData, playerDefaults, i, "Name"));
cmpPlayer.SetCiv(getSetting(playerData, playerDefaults, i, "Civ"));
var color = getSetting(playerData, playerDefaults, i, "Color");
cmpPlayer.SetColor(color.r, color.g, color.b);
// Special case for gaia
if (i == 0)
{
// Gaia should be its own ally.
cmpPlayer.SetAlly(0);
// Gaia is everyone's enemy
for (var j = 1; j < numPlayers; ++j)
cmpPlayer.SetEnemy(j);
continue;
}
// PopulationLimit
{
let maxPopulation =
settings.PlayerData[i].PopulationLimit !== undefined ?
settings.PlayerData[i].PopulationLimit :
settings.PopulationCap !== undefined ?
settings.PopulationCap :
playerDefaults[i].PopulationLimit !== undefined ?
playerDefaults[i].PopulationLimit :
undefined;
if (maxPopulation !== undefined)
cmpPlayer.SetMaxPopulation(maxPopulation);
}
// StartingResources
if (settings.PlayerData[i].Resources !== undefined)
cmpPlayer.SetResourceCounts(settings.PlayerData[i].Resources);
else if (settings.StartingResources)
{
let resourceCounts = cmpPlayer.GetResourceCounts();
let newResourceCounts = {};
for (let resouces in resourceCounts)
newResourceCounts[resouces] = settings.StartingResources;
cmpPlayer.SetResourceCounts(newResourceCounts);
}
else if (playerDefaults[i].Resources !== undefined)
cmpPlayer.SetResourceCounts(playerDefaults[i].Resources);
// StartingTechnologies
{
let startingTechnologies =
settings.PlayerData[i].StartingTechnologies ||
settings.StartingTechnologies ||
playerDefaults[i].StartingTechnologies ||
[];
if (startingTechnologies.length)
cmpPlayer.SetStartingTechnologies(startingTechnologies);
}
// DisabledTechnologies
{
let disabledTechnologies =
settings.PlayerData[i].DisabledTechnologies ||
settings.DisabledTechnologies ||
playerDefaults[i].DisabledTechnologies ||
[];
if (disabledTechnologies.length)
cmpPlayer.SetDisabledTechnologies(disabledTechnologies);
}
// DisabledTemplates
{
let disabledTemplates =
settings.PlayerData[i].DisabledTemplates ||
settings.DisabledTemplates ||
playerDefaults[i].DisabledTemplates ||
[];
if (disabledTemplates.length)
cmpPlayer.SetDisabledTemplates(disabledTemplates);
}
// DisableSpies
if (settings.DisableSpies)
{
cmpPlayer.AddDisabledTechnology("unlock_spies");
cmpPlayer.AddDisabledTemplate("special/spy");
}
// If diplomacy explicitly defined, use that; otherwise use teams
if (getSetting(playerData, playerDefaults, i, "Diplomacy") !== undefined)
cmpPlayer.SetDiplomacy(getSetting(playerData, playerDefaults, i, "Diplomacy"));
else
{
// Init diplomacy
var myTeam = getSetting(playerData, playerDefaults, i, "Team");
// Set all but self as enemies as SetTeam takes care of allies
for (var j = 0; j < numPlayers; ++j)
{
if (i == j)
cmpPlayer.SetAlly(j);
else
cmpPlayer.SetEnemy(j);
}
cmpPlayer.SetTeam(myTeam === undefined ? -1 : myTeam);
}
cmpPlayer.SetFormations(
getSetting(playerData, playerDefaults, i, "Formations") ||
Engine.ReadJSONFile("simulation/data/civs/" + cmpPlayer.GetCiv() + ".json").Formations);
var startCam = getSetting(playerData, playerDefaults, i, "StartingCamera");
if (startCam !== undefined)
cmpPlayer.SetStartingCamera(startCam.Position, startCam.Rotation);
}
// NOTE: We need to do the team locking here, as otherwise
// SetTeam can't ally the players.
if (settings.LockTeams)
for (let i = 0; i < numPlayers; ++i)
QueryPlayerIDInterface(i).SetLockTeams(true);
}
// Get a setting if it exists or return default
function getSetting(settings, defaults, idx, property)
{
if (settings && settings[idx] && (property in settings[idx]))
return settings[idx][property];
// Use defaults
if (defaults && defaults[idx] && (property in defaults[idx]))
return defaults[idx][property];
return undefined;
}
function GetPlayerTemplateName(civ)
{
let path = "special/player/player";
if (Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager).TemplateExists(path + "_" + civ))
return path + "_" + civ;
return path;
}
/**
* @param id An entity's ID
* @returns The entity ID of the owner player (not his player ID) or ent if ent is a player entity.
*/
function QueryOwnerEntityID(ent)
{
let cmpPlayer = Engine.QueryInterface(ent, IID_Player);
if (cmpPlayer)
return ent;
let cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
if (!cmpOwnership)
return null;
let owner = cmpOwnership.GetOwner();
if (owner == INVALID_PLAYER)
return null;
let cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
if (!cmpPlayerManager)
return null;
return cmpPlayerManager.GetPlayerByID(owner);
}
/**
* Similar to Engine.QueryInterface but applies to the player entity
* that owns the given entity.
* iid is typically IID_Player.
*/
function QueryOwnerInterface(ent, iid = IID_Player)
{
var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
if (!cmpOwnership)
return null;
var owner = cmpOwnership.GetOwner();
if (owner == INVALID_PLAYER)
return null;
return QueryPlayerIDInterface(owner, iid);
}
/**
* Similar to Engine.QueryInterface but applies to the player entity
* with the given ID number.
* iid is typically IID_Player.
*/
function QueryPlayerIDInterface(id, iid = IID_Player)
{
var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
var playerEnt = cmpPlayerManager.GetPlayerByID(id);
if (!playerEnt)
return null;
return Engine.QueryInterface(playerEnt, iid);
}
/**
* Similar to Engine.QueryInterface but first checks if the entity
* mirages the interface.
*/
function QueryMiragedInterface(ent, iid)
{
- var cmp = Engine.QueryInterface(ent, IID_Mirage);
- if (cmp && !cmp.Mirages(iid))
+ let cmpMirage = Engine.QueryInterface(ent, IID_Mirage);
+ if (cmpMirage && !cmpMirage.Mirages(iid))
return null;
- else if (!cmp)
- cmp = Engine.QueryInterface(ent, iid);
+ else if (!cmpMirage)
+ return Engine.QueryInterface(ent, iid);
- return cmp;
+ return cmpMirage.Get(iid);
}
/**
* Similar to Engine.QueryInterface, but checks for all interfaces
* implementing a builder list (currently Foundation and Repairable)
* TODO Foundation and Repairable could both implement a BuilderList component
*/
function QueryBuilderListInterface(ent)
{
return Engine.QueryInterface(ent, IID_Foundation) || Engine.QueryInterface(ent, IID_Repairable);
}
/**
* Returns true if the entity 'target' is owned by an ally of
* the owner of 'entity'.
*/
function IsOwnedByAllyOfEntity(entity, target)
{
return IsOwnedByEntityHelper(entity, target, "IsAlly");
}
function IsOwnedByMutualAllyOfEntity(entity, target)
{
return IsOwnedByEntityHelper(entity, target, "IsMutualAlly");
}
function IsOwnedByEntityHelper(entity, target, check)
{
// Figure out which player controls us
let owner = 0;
let cmpOwnership = Engine.QueryInterface(entity, IID_Ownership);
if (cmpOwnership)
owner = cmpOwnership.GetOwner();
// Figure out which player controls the target entity
let targetOwner = 0;
let cmpOwnershipTarget = Engine.QueryInterface(target, IID_Ownership);
if (cmpOwnershipTarget)
targetOwner = cmpOwnershipTarget.GetOwner();
let cmpPlayer = QueryPlayerIDInterface(owner);
return cmpPlayer && cmpPlayer[check](targetOwner);
}
/**
* Returns true if the entity 'target' is owned by player
*/
function IsOwnedByPlayer(player, target)
{
var cmpOwnershipTarget = Engine.QueryInterface(target, IID_Ownership);
return cmpOwnershipTarget && player == cmpOwnershipTarget.GetOwner();
}
function IsOwnedByGaia(target)
{
return IsOwnedByPlayer(0, target);
}
/**
* Returns true if the entity 'target' is owned by an ally of player
*/
function IsOwnedByAllyOfPlayer(player, target)
{
return IsOwnedByHelper(player, target, "IsAlly");
}
function IsOwnedByMutualAllyOfPlayer(player, target)
{
return IsOwnedByHelper(player, target, "IsMutualAlly");
}
function IsOwnedByNeutralOfPlayer(player, target)
{
return IsOwnedByHelper(player, target, "IsNeutral");
}
function IsOwnedByEnemyOfPlayer(player, target)
{
return IsOwnedByHelper(player, target, "IsEnemy");
}
function IsOwnedByHelper(player, target, check)
{
let targetOwner = 0;
let cmpOwnershipTarget = Engine.QueryInterface(target, IID_Ownership);
if (cmpOwnershipTarget)
targetOwner = cmpOwnershipTarget.GetOwner();
let cmpPlayer = QueryPlayerIDInterface(player);
return cmpPlayer && cmpPlayer[check](targetOwner);
}
Engine.RegisterGlobal("LoadPlayerSettings", LoadPlayerSettings);
Engine.RegisterGlobal("QueryOwnerEntityID", QueryOwnerEntityID);
Engine.RegisterGlobal("QueryOwnerInterface", QueryOwnerInterface);
Engine.RegisterGlobal("QueryPlayerIDInterface", QueryPlayerIDInterface);
Engine.RegisterGlobal("QueryMiragedInterface", QueryMiragedInterface);
Engine.RegisterGlobal("QueryBuilderListInterface", QueryBuilderListInterface);
Engine.RegisterGlobal("IsOwnedByAllyOfEntity", IsOwnedByAllyOfEntity);
Engine.RegisterGlobal("IsOwnedByMutualAllyOfEntity", IsOwnedByMutualAllyOfEntity);
Engine.RegisterGlobal("IsOwnedByPlayer", IsOwnedByPlayer);
Engine.RegisterGlobal("IsOwnedByGaia", IsOwnedByGaia);
Engine.RegisterGlobal("IsOwnedByAllyOfPlayer", IsOwnedByAllyOfPlayer);
Engine.RegisterGlobal("IsOwnedByMutualAllyOfPlayer", IsOwnedByMutualAllyOfPlayer);
Engine.RegisterGlobal("IsOwnedByNeutralOfPlayer", IsOwnedByNeutralOfPlayer);
Engine.RegisterGlobal("IsOwnedByEnemyOfPlayer", IsOwnedByEnemyOfPlayer);