Index: ps/trunk/binaries/data/mods/public/simulation/components/Promotion.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/Promotion.js (revision 22752)
+++ ps/trunk/binaries/data/mods/public/simulation/components/Promotion.js (revision 22753)
@@ -1,179 +1,97 @@
function Promotion() {}
Promotion.prototype.Schema =
"" +
"" +
"" +
"" +
"" +
"";
Promotion.prototype.Init = function()
{
this.currentXp = 0;
};
Promotion.prototype.GetRequiredXp = function()
{
return ApplyValueModificationsToEntity("Promotion/RequiredXp", +this.template.RequiredXp, this.entity);
};
Promotion.prototype.GetCurrentXp = function()
{
return this.currentXp;
};
Promotion.prototype.GetPromotedTemplateName = function()
{
return this.template.Entity;
};
Promotion.prototype.Promote = function(promotedTemplateName)
{
// If the unit is dead, don't promote it
- var cmpCurrentUnitHealth = Engine.QueryInterface(this.entity, IID_Health);
- if (cmpCurrentUnitHealth.GetHitpoints() == 0)
+ let cmpHealth = Engine.QueryInterface(this.entity, IID_Health);
+ if (cmpHealth && cmpHealth.GetHitpoints() == 0)
return;
- // Create promoted unit entity
- var promotedUnitEntity = Engine.AddEntity(promotedTemplateName);
+ // Save the entity id.
+ this.promotedUnitEntity = ChangeEntityTemplate(this.entity, promotedTemplateName);
- // Copy parameters from current entity to promoted one
- var cmpCurrentUnitPosition = Engine.QueryInterface(this.entity, IID_Position);
- var cmpPromotedUnitPosition = Engine.QueryInterface(promotedUnitEntity, IID_Position);
- if (cmpCurrentUnitPosition.IsInWorld())
- {
- var pos = cmpCurrentUnitPosition.GetPosition2D();
- cmpPromotedUnitPosition.JumpTo(pos.x, pos.y);
- }
- var rot = cmpCurrentUnitPosition.GetRotation();
- cmpPromotedUnitPosition.SetYRotation(rot.y);
- cmpPromotedUnitPosition.SetXZRotation(rot.x, rot.z);
- var heightOffset = cmpCurrentUnitPosition.GetHeightOffset();
- cmpPromotedUnitPosition.SetHeightOffset(heightOffset);
-
- var cmpCurrentUnitOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
- var cmpPromotedUnitOwnership = Engine.QueryInterface(promotedUnitEntity, IID_Ownership);
- cmpPromotedUnitOwnership.SetOwner(cmpCurrentUnitOwnership.GetOwner());
-
- // change promoted unit health to the same percent of hitpoints as unit had before promotion
- var cmpPromotedUnitHealth = Engine.QueryInterface(promotedUnitEntity, IID_Health);
- var healthFraction = Math.max(0, Math.min(1, cmpCurrentUnitHealth.GetHitpoints() / cmpCurrentUnitHealth.GetMaxHitpoints()));
- var promotedUnitHitpoints = cmpPromotedUnitHealth.GetMaxHitpoints() * healthFraction;
- cmpPromotedUnitHealth.SetHitpoints(promotedUnitHitpoints);
-
- var cmpPromotedUnitPromotion = Engine.QueryInterface(promotedUnitEntity, IID_Promotion);
- if (cmpPromotedUnitPromotion)
- cmpPromotedUnitPromotion.IncreaseXp(this.currentXp);
-
- var cmpCurrentUnitResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer);
- var cmpPromotedUnitResourceGatherer = Engine.QueryInterface(promotedUnitEntity, IID_ResourceGatherer);
- if (cmpCurrentUnitResourceGatherer && cmpPromotedUnitResourceGatherer)
- {
- var carriedResorces = cmpCurrentUnitResourceGatherer.GetCarryingStatus();
- cmpPromotedUnitResourceGatherer.GiveResources(carriedResorces);
- }
-
- var cmpCurrentUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI);
- var cmpPromotedUnitAI = Engine.QueryInterface(promotedUnitEntity, IID_UnitAI);
- var heldPos = cmpCurrentUnitAI.GetHeldPosition();
- if (heldPos)
- cmpPromotedUnitAI.SetHeldPosition(heldPos.x, heldPos.z);
- if (cmpCurrentUnitAI.GetStanceName())
- cmpPromotedUnitAI.SwitchToStance(cmpCurrentUnitAI.GetStanceName());
-
- var orders = cmpCurrentUnitAI.GetOrders();
- if (cmpCurrentUnitPosition.IsInWorld()) // do not cheer if not visibly garrisoned
- cmpPromotedUnitAI.Cheer();
- if (cmpCurrentUnitAI.IsGarrisoned())
- cmpPromotedUnitAI.SetGarrisoned();
- cmpPromotedUnitAI.AddOrders(orders);
-
- var workOrders = cmpCurrentUnitAI.GetWorkOrders();
- cmpPromotedUnitAI.SetWorkOrders(workOrders);
-
- if (cmpCurrentUnitAI.IsGuardOf())
- {
- let guarded = cmpCurrentUnitAI.IsGuardOf();
- let cmpGuard = Engine.QueryInterface(guarded, IID_Guard);
- if (cmpGuard)
- {
- cmpGuard.RenameGuard(this.entity, promotedUnitEntity);
- cmpPromotedUnitAI.SetGuardOf(guarded);
- }
- }
+ let cmpPosition = Engine.QueryInterface(this.promotedUnitEntity, IID_Position);
+ let cmpUnitAI = Engine.QueryInterface(this.promotedUnitEntity, IID_UnitAI);
- let cmpCurrentUnitGuard = Engine.QueryInterface(this.entity, IID_Guard);
- let cmpPromotedUnitGuard = Engine.QueryInterface(promotedUnitEntity, IID_Guard);
- if (cmpCurrentUnitGuard && cmpPromotedUnitGuard)
- {
- let entities = cmpCurrentUnitGuard.GetEntities();
- if (entities.length)
- {
- cmpPromotedUnitGuard.SetEntities(entities);
- for (let ent of entities)
- {
- let cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
- if (cmpUnitAI)
- cmpUnitAI.SetGuardOf(promotedUnitEntity);
- }
- }
- }
-
- Engine.PostMessage(this.entity, MT_EntityRenamed, { "entity": this.entity, "newentity": promotedUnitEntity });
-
- // Destroy current entity
- if (cmpCurrentUnitPosition && cmpCurrentUnitPosition.IsInWorld())
- cmpCurrentUnitPosition.MoveOutOfWorld();
- Engine.DestroyEntity(this.entity);
- // save the entity id
- this.promotedUnitEntity = promotedUnitEntity;
+ if (cmpPosition && cmpPosition.IsInWorld() && cmpUnitAI)
+ cmpUnitAI.Cheer();
};
Promotion.prototype.IncreaseXp = function(amount)
{
// if the unit was already promoted, but is waiting for the engine to be destroyed
// transfer the gained xp to the promoted unit if applicable
if (this.promotedUnitEntity)
{
- var cmpPromotion = Engine.QueryInterface(this.promotedUnitEntity, IID_Promotion);
+ let cmpPromotion = Engine.QueryInterface(this.promotedUnitEntity, IID_Promotion);
if (cmpPromotion)
cmpPromotion.IncreaseXp(amount);
return;
}
this.currentXp += +(amount);
var requiredXp = this.GetRequiredXp();
if (this.currentXp >= requiredXp)
{
var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
var playerID = QueryOwnerInterface(this.entity, IID_Player).GetPlayerID();
this.currentXp -= requiredXp;
var promotedTemplateName = this.GetPromotedTemplateName();
// check if we can upgrade a second time (or even more)
while (true)
{
var template = cmpTemplateManager.GetTemplate(promotedTemplateName);
if (!template.Promotion)
break;
requiredXp = ApplyValueModificationsToTemplate("Promotion/RequiredXp", +template.Promotion.RequiredXp, playerID, template);
// compare the current xp to the required xp of the promoted entity
if (this.currentXp < requiredXp)
break;
this.currentXp -= requiredXp;
promotedTemplateName = template.Promotion.Entity;
}
this.Promote(promotedTemplateName);
+ let cmpPromotion = Engine.QueryInterface(this.promotedUnitEntity, IID_Promotion);
+ if (cmpPromotion)
+ cmpPromotion.IncreaseXp(this.currentXp);
}
Engine.PostMessage(this.entity, MT_ExperienceChanged, {});
};
Promotion.prototype.OnValueModification = function(msg)
{
if (msg.component == "Promotion")
this.IncreaseXp(0);
};
Engine.RegisterComponentType(IID_Promotion, "Promotion", Promotion);
Index: ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Pack.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Pack.js (revision 22752)
+++ ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Pack.js (revision 22753)
@@ -1,158 +1,160 @@
Engine.LoadHelperScript("Player.js");
Engine.LoadHelperScript("Sound.js");
Engine.LoadHelperScript("Transform.js");
Engine.LoadHelperScript("ValueModification.js");
Engine.LoadComponentScript("interfaces/TechnologyManager.js");
Engine.LoadComponentScript("interfaces/AuraManager.js");
Engine.LoadComponentScript("interfaces/Capturable.js");
Engine.LoadComponentScript("interfaces/GarrisonHolder.js");
Engine.LoadComponentScript("interfaces/Guard.js");
Engine.LoadComponentScript("interfaces/Health.js");
Engine.LoadComponentScript("interfaces/Pack.js");
Engine.LoadComponentScript("interfaces/Player.js");
+Engine.LoadComponentScript("interfaces/Promotion.js");
+Engine.LoadComponentScript("interfaces/ResourceGatherer.js");
Engine.LoadComponentScript("interfaces/Timer.js");
Engine.LoadComponentScript("interfaces/UnitAI.js");
Engine.LoadComponentScript("Pack.js");
Engine.RegisterGlobal("MT_EntityRenamed", "entityRenamed");
const ent = 170;
const newEnt = 171;
const PACKING_INTERVAL = 250;
let timerActivated = false;
AddMock(ent, IID_Visual, {
"SelectAnimation": (name, once, speed) => name
});
AddMock(ent, IID_Ownership, {
"GetOwner": () => 1
});
AddMock(SYSTEM_ENTITY, IID_PlayerManager, {
"GetPlayerByID": id => 11
});
AddMock(11, IID_Player, {
"GetTimeMultiplier": () => 1
});
AddMock(ent, IID_Sound, {
"PlaySoundGroup": name => {}
});
AddMock(SYSTEM_ENTITY, IID_Timer, {
"CancelTimer": id => { timerActivated = false; return; },
"SetInterval": (ent, iid, funcname, time, repeattime, data) => { timerActivated = true; return 7; }
});
Engine.AddEntity = function(template) {
TS_ASSERT_EQUALS(template, "finalTemplate");
return true;
};
// Test Packing
let template = {
"Entity": "finalTemplate",
"Time": "2000",
"State": "unpacked"
};
let cmpPack = ConstructComponent(ent, "Pack", template);
// Check internals
TS_ASSERT(!cmpPack.packed);
TS_ASSERT(!cmpPack.packing);
TS_ASSERT_EQUALS(cmpPack.elapsedTime, 0);
TS_ASSERT_EQUALS(cmpPack.timer, undefined);
TS_ASSERT(!cmpPack.IsPacked());
TS_ASSERT(!cmpPack.IsPacking());
TS_ASSERT_EQUALS(cmpPack.GetProgress(), 0);
// Pack
cmpPack.Pack();
TS_ASSERT_EQUALS(cmpPack.timer, 7);
TS_ASSERT(cmpPack.IsPacking());
// Listen to destroy message
cmpPack.OnDestroy();
TS_ASSERT(!cmpPack.timer);
TS_ASSERT(!timerActivated);
// Test UnPacking
template = {
"Entity": "finalTemplate",
"Time": "2000",
"State": "packed"
};
cmpPack = ConstructComponent(ent, "Pack", template);
// Check internals
TS_ASSERT(cmpPack.packed);
TS_ASSERT(!cmpPack.packing);
TS_ASSERT_EQUALS(cmpPack.elapsedTime, 0);
TS_ASSERT_EQUALS(cmpPack.timer, undefined);
TS_ASSERT(cmpPack.IsPacked());
TS_ASSERT(!cmpPack.IsPacking());
TS_ASSERT_EQUALS(cmpPack.GetProgress(), 0);
// Unpack
cmpPack.Unpack();
TS_ASSERT(cmpPack.IsPacking());
TS_ASSERT_EQUALS(cmpPack.timer, 7);
// Unpack progress
cmpPack.elapsedTime = 400;
cmpPack.PackProgress({}, 100);
TS_ASSERT(cmpPack.IsPacking());
TS_ASSERT_EQUALS(cmpPack.GetElapsedTime(), 400 + 100 + PACKING_INTERVAL);
TS_ASSERT_EQUALS(cmpPack.GetProgress(), (400 + 100 + PACKING_INTERVAL) / 2000);
// Try to Pack or Unpack while packing, nothing happen
cmpPack.elapsedTime = 400;
cmpPack.Unpack();
TS_ASSERT(cmpPack.IsPacking());
TS_ASSERT_EQUALS(cmpPack.GetElapsedTime(), 400);
TS_ASSERT_EQUALS(cmpPack.timer, 7);
TS_ASSERT(timerActivated);
cmpPack.Pack();
TS_ASSERT(cmpPack.IsPacking());
TS_ASSERT_EQUALS(cmpPack.GetElapsedTime(), 400);
TS_ASSERT_EQUALS(cmpPack.timer, 7);
TS_ASSERT(timerActivated);
// Cancel
cmpPack.CancelPack();
TS_ASSERT(!cmpPack.IsPacking());
TS_ASSERT_EQUALS(cmpPack.GetElapsedTime(), 0);
TS_ASSERT_EQUALS(cmpPack.GetProgress(), 0);
TS_ASSERT_EQUALS(cmpPack.timer, undefined);
TS_ASSERT(!timerActivated);
// Progress until completing
cmpPack.Unpack();
cmpPack.elapsedTime = 1800;
cmpPack.PackProgress({}, 100);
TS_ASSERT(cmpPack.IsPacking());
TS_ASSERT_EQUALS(cmpPack.GetElapsedTime(), 1800 + 100 + PACKING_INTERVAL);
// Cap progress at 100%
TS_ASSERT_EQUALS(cmpPack.GetProgress(), 1);
TS_ASSERT_EQUALS(cmpPack.timer, 7);
TS_ASSERT(timerActivated);
// Unpack completing
cmpPack.Unpack();
cmpPack.elapsedTime = 2100;
cmpPack.PackProgress({}, 100);
TS_ASSERT(!cmpPack.IsPacking());
TS_ASSERT(!cmpPack.timer);
TS_ASSERT(!timerActivated);
Index: ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Promotion.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Promotion.js (revision 22752)
+++ ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Promotion.js (revision 22753)
@@ -1,69 +1,66 @@
-Engine.LoadComponentScript("interfaces/Guard.js");
Engine.LoadComponentScript("interfaces/Health.js");
Engine.LoadComponentScript("interfaces/Promotion.js");
-Engine.LoadComponentScript("interfaces/ResourceGatherer.js");
Engine.LoadComponentScript("interfaces/UnitAI.js");
Engine.LoadComponentScript("Promotion.js");
-Engine.RegisterGlobal("MT_EntityRenamed", "entityRenamed");
-// Test Promote
-
-let cmpPromotion = ConstructComponent(60, "Promotion", {
- "Entity": "infantry_melee_spearman_a",
- "RequiredXP": 1000
-});
-
-// Health, Position, Ownership, UnitAI are mandatory in the Promotion code
-AddMock(60, IID_Health, {
- "GetHitpoints": () => 102,
- "GetMaxHitpoints": () => 102,
-});
+(function testMultipleXPIncrease()
+{
+let ApplyValueModificationsToEntity = (_, val) => val;
+Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
+Engine.RegisterGlobal("ApplyValueModificationsToTemplate", ApplyValueModificationsToEntity);
+
+let QueryOwnerInterface = () => ({ "GetPlayerID": () => 2 });
+Engine.RegisterGlobal("QueryOwnerInterface", QueryOwnerInterface);
+
+const ENT_ID = 60;
+
+let entTemplates = {
+ "60": "template_b",
+ "61": "template_f",
+ "62": "end",
+};
-AddMock(60, IID_Position, {
- "GetPosition2D": () => new Vector2D(1, 0, 0),
- "GetRotation": () => new Vector3D(3, 4, 5),
- "GetHeightOffset": () => {},
- "IsInWorld": () => true,
- "MoveOutOfWorld": () => {}
-});
+let promote = {
+ "template_b": "template_c",
+ "template_c": "template_d",
+ "template_d": "template_e",
+ "template_e": "template_f",
+};
-AddMock(60, IID_Ownership, {
- "GetOwner": () => 1,
+AddMock(SYSTEM_ENTITY, IID_TemplateManager, {
+ "GetTemplate": (t) => ({
+ "Promotion": {
+ "Entity": promote[t],
+ "RequiredXp": 1000
+ },
+ }),
});
-AddMock(60, IID_UnitAI, {
- "GetHeldPosition": () => {},
- "GetStanceName": () => {},
- "GetOrders": () => {},
- "IsGarrisoned": () => {},
- "GetWorkOrders": () => {},
- "IsGuardOf": () => {},
+let cmpPromotion = ConstructComponent(ENT_ID, "Promotion", {
+ "Entity": "template_b",
+ "RequiredXp": 1000
});
-Engine.AddEntity = function(name)
+let ChangeEntityTemplate = function(ent, template)
{
- if (name != "infantry_melee_spearman_a")
- return undefined;
- AddMock(61, IID_Health, {
- "GetMaxHitpoints": () => 102 * 1.2,
- "SetHitpoints": hp => TS_ASSERT_EQUALS(hp, 102 * 1.2)
- });
- AddMock(61, IID_Position, {
- "JumpTo": () => {},
- "SetYRotation": () => {},
- "SetXZRotation": () => {},
- "SetHeightOffset": () => {},
- "IsInWorld": () => true,
- });
- AddMock(61, IID_Ownership, {
- "SetOwner": id => TS_ASSERT_EQUALS(id, 1),
- });
- AddMock(61, IID_UnitAI, {
- "Cheer": () => {},
- "AddOrders": () => {},
- "SetWorkOrders": () => {},
+ cmpPromotion = ConstructComponent(ent + 1, "Promotion", {
+ "Entity": entTemplates[ent + 1],
+ "RequiredXp": 1000
});
- return 61;
+ return ent + 1;
};
+Engine.RegisterGlobal("ChangeEntityTemplate", ChangeEntityTemplate);
-cmpPromotion.Promote("infantry_melee_spearman_a");
+TS_ASSERT_EQUALS(cmpPromotion.GetCurrentXp(), 0);
+cmpPromotion.IncreaseXp(200);
+TS_ASSERT_EQUALS(cmpPromotion.GetCurrentXp(), 200);
+cmpPromotion.IncreaseXp(800);
+
+TS_ASSERT_EQUALS(cmpPromotion.entity, 61);
+TS_ASSERT_EQUALS(cmpPromotion.GetCurrentXp(), 0);
+TS_ASSERT_EQUALS(cmpPromotion.GetRequiredXp(), 1000);
+cmpPromotion.IncreaseXp(4200);
+TS_ASSERT_EQUALS(cmpPromotion.entity, 62);
+TS_ASSERT_EQUALS(cmpPromotion.template.Entity, "end");
+TS_ASSERT_EQUALS(cmpPromotion.GetCurrentXp(), 200);
+})();
Index: ps/trunk/binaries/data/mods/public/simulation/helpers/Transform.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/helpers/Transform.js (revision 22752)
+++ ps/trunk/binaries/data/mods/public/simulation/helpers/Transform.js (revision 22753)
@@ -1,245 +1,259 @@
// Helper functions to change an entity's template and check if the transformation is possible
// returns the ID of the new entity or INVALID_ENTITY.
function ChangeEntityTemplate(oldEnt, newTemplate)
{
// Done un/packing, copy our parameters to the final entity
var newEnt = Engine.AddEntity(newTemplate);
if (newEnt == INVALID_ENTITY)
{
error("Transform.js: Error replacing entity " + oldEnt + " for a '" + newTemplate + "'");
return INVALID_ENTITY;
}
var cmpPosition = Engine.QueryInterface(oldEnt, IID_Position);
var cmpNewPosition = Engine.QueryInterface(newEnt, IID_Position);
if (cmpPosition && cmpNewPosition)
{
if (cmpPosition.IsInWorld())
{
let pos = cmpPosition.GetPosition2D();
cmpNewPosition.JumpTo(pos.x, pos.y);
}
let rot = cmpPosition.GetRotation();
cmpNewPosition.SetYRotation(rot.y);
cmpNewPosition.SetXZRotation(rot.x, rot.z);
cmpNewPosition.SetHeightOffset(cmpPosition.GetHeightOffset());
}
var cmpOwnership = Engine.QueryInterface(oldEnt, IID_Ownership);
var cmpNewOwnership = Engine.QueryInterface(newEnt, IID_Ownership);
if (cmpOwnership && cmpNewOwnership)
cmpNewOwnership.SetOwner(cmpOwnership.GetOwner());
// Copy control groups
CopyControlGroups(oldEnt, newEnt);
// Rescale capture points
var cmpCapturable = Engine.QueryInterface(oldEnt, IID_Capturable);
var cmpNewCapturable = Engine.QueryInterface(newEnt, IID_Capturable);
if (cmpCapturable && cmpNewCapturable)
{
let scale = cmpCapturable.GetMaxCapturePoints() / cmpNewCapturable.GetMaxCapturePoints();
let newCp = cmpCapturable.GetCapturePoints().map(v => v / scale);
cmpNewCapturable.SetCapturePoints(newCp);
}
// Maintain current health level
var cmpHealth = Engine.QueryInterface(oldEnt, IID_Health);
var cmpNewHealth = Engine.QueryInterface(newEnt, IID_Health);
if (cmpHealth && cmpNewHealth)
{
var healthLevel = Math.max(0, Math.min(1, cmpHealth.GetHitpoints() / cmpHealth.GetMaxHitpoints()));
cmpNewHealth.SetHitpoints(cmpNewHealth.GetMaxHitpoints() * healthLevel);
}
var cmpUnitAI = Engine.QueryInterface(oldEnt, IID_UnitAI);
var cmpNewUnitAI = Engine.QueryInterface(newEnt, IID_UnitAI);
if (cmpUnitAI && cmpNewUnitAI)
{
let pos = cmpUnitAI.GetHeldPosition();
if (pos)
cmpNewUnitAI.SetHeldPosition(pos.x, pos.z);
if (cmpUnitAI.GetStanceName())
cmpNewUnitAI.SwitchToStance(cmpUnitAI.GetStanceName());
cmpNewUnitAI.AddOrders(cmpUnitAI.GetOrders());
if (cmpUnitAI.IsGuardOf())
{
let guarded = cmpUnitAI.IsGuardOf();
let cmpGuard = Engine.QueryInterface(guarded, IID_Guard);
if (cmpGuard)
{
cmpGuard.RenameGuard(oldEnt, newEnt);
cmpNewUnitAI.SetGuardOf(guarded);
}
}
}
+ let cmpPromotion = Engine.QueryInterface(oldEnt, IID_Promotion);
+ let cmpNewPromotion = Engine.QueryInterface(newEnt, IID_Promotion);
+ if (cmpPromotion && cmpNewPromotion)
+ cmpNewPromotion.IncreaseXp(cmpPromotion.GetCurrentXp());
+
+ let cmpResGatherer = Engine.QueryInterface(oldEnt, IID_ResourceGatherer);
+ let cmpNewResGatherer = Engine.QueryInterface(newEnt, IID_ResourceGatherer);
+ if (cmpResGatherer && cmpNewResGatherer)
+ {
+ let carriedResources = cmpResGatherer.GetCarryingStatus();
+ cmpNewResGatherer.GiveResources(carriedResources);
+ }
+
+
// Maintain the list of guards
let cmpGuard = Engine.QueryInterface(oldEnt, IID_Guard);
let cmpNewGuard = Engine.QueryInterface(newEnt, IID_Guard);
if (cmpGuard && cmpNewGuard)
{
let entities = cmpGuard.GetEntities();
if (entities.length)
{
cmpNewGuard.SetEntities(entities);
for (let ent of entities)
{
let cmpEntUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
if (cmpEntUnitAI)
cmpEntUnitAI.SetGuardOf(newEnt);
}
}
}
TransferGarrisonedUnits(oldEnt, newEnt);
Engine.PostMessage(oldEnt, MT_EntityRenamed, { "entity": oldEnt, "newentity": newEnt });
if (cmpPosition && cmpPosition.IsInWorld())
cmpPosition.MoveOutOfWorld();
Engine.DestroyEntity(oldEnt);
return newEnt;
}
function CanGarrisonedChangeTemplate(ent, template)
{
var cmpPosition = Engine.QueryInterface(ent, IID_Position);
var unitAI = Engine.QueryInterface(ent, IID_UnitAI);
if (cmpPosition && !cmpPosition.IsInWorld() && unitAI && unitAI.IsGarrisoned())
{
// We're a garrisoned unit, assume impossibility as I've been unable to find a way to get the holder ID.
// TODO: change this if that ever becomes possibles
return false;
}
return true;
}
function CopyControlGroups(oldEnt, newEnt)
{
let cmpObstruction = Engine.QueryInterface(oldEnt, IID_Obstruction);
let cmpNewObstruction = Engine.QueryInterface(newEnt, IID_Obstruction);
if (cmpObstruction && cmpNewObstruction)
{
cmpNewObstruction.SetControlGroup(cmpObstruction.GetControlGroup());
cmpNewObstruction.SetControlGroup2(cmpObstruction.GetControlGroup2());
}
}
function ObstructionsBlockingTemplateChange(ent, templateArg)
{
var previewEntity = Engine.AddEntity("preview|"+templateArg);
if (previewEntity == INVALID_ENTITY)
return true;
CopyControlGroups(ent, previewEntity);
var cmpBuildRestrictions = Engine.QueryInterface(previewEntity, IID_BuildRestrictions);
var cmpPosition = Engine.QueryInterface(ent, IID_Position);
var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
var cmpNewPosition = Engine.QueryInterface(previewEntity, IID_Position);
// Return false if no ownership as BuildRestrictions.CheckPlacement needs an owner and I have no idea if false or true is better
// Plus there are no real entities without owners currently.
if (!cmpBuildRestrictions || !cmpPosition || !cmpOwnership)
return DeleteEntityAndReturn(previewEntity, cmpPosition, null, null, cmpNewPosition, false);
var pos = cmpPosition.GetPosition2D();
var angle = cmpPosition.GetRotation();
// move us away to prevent our own obstruction from blocking the upgrade.
cmpPosition.MoveOutOfWorld();
cmpNewPosition.JumpTo(pos.x, pos.y);
cmpNewPosition.SetYRotation(angle.y);
var cmpNewOwnership = Engine.QueryInterface(previewEntity, IID_Ownership);
cmpNewOwnership.SetOwner(cmpOwnership.GetOwner());
var checkPlacement = cmpBuildRestrictions.CheckPlacement();
if (checkPlacement && !checkPlacement.success)
return DeleteEntityAndReturn(previewEntity, cmpPosition, pos, angle, cmpNewPosition, true);
var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
var template = cmpTemplateManager.GetTemplate(cmpTemplateManager.GetCurrentTemplateName(ent));
var newTemplate = cmpTemplateManager.GetTemplate(templateArg);
// Check if units are blocking our template change
if (template.Obstruction && newTemplate.Obstruction)
{
// This only needs to be done if the new template is strictly bigger than the old one
// "Obstructions" are annoying to test so just check.
if (newTemplate.Obstruction.Obstructions ||
newTemplate.Obstruction.Static && template.Obstruction.Static &&
(newTemplate.Obstruction.Static["@width"] > template.Obstruction.Static["@width"] ||
newTemplate.Obstruction.Static["@depth"] > template.Obstruction.Static["@depth"]) ||
newTemplate.Obstruction.Static && template.Obstruction.Unit &&
(newTemplate.Obstruction.Static["@width"] > template.Obstruction.Unit["@radius"] ||
newTemplate.Obstruction.Static["@depth"] > template.Obstruction.Unit["@radius"]) ||
newTemplate.Obstruction.Unit && template.Obstruction.Unit &&
newTemplate.Obstruction.Unit["@radius"] > template.Obstruction.Unit["@radius"] ||
newTemplate.Obstruction.Unit && template.Obstruction.Static &&
(newTemplate.Obstruction.Unit["@radius"] > template.Obstruction.Static["@width"] ||
newTemplate.Obstruction.Unit["@radius"] > template.Obstruction.Static["@depth"]))
{
var cmpNewObstruction = Engine.QueryInterface(previewEntity, IID_Obstruction);
if (cmpNewObstruction && cmpNewObstruction.GetBlockMovementFlag())
{
// Remove all obstructions at the new entity, especially animal corpses
for (let ent of cmpNewObstruction.GetEntitiesDeletedUponConstruction())
Engine.DestroyEntity(ent);
let collisions = cmpNewObstruction.GetEntitiesBlockingConstruction();
if (collisions.length)
return DeleteEntityAndReturn(previewEntity, cmpPosition, pos, angle, cmpNewPosition, true);
}
}
}
return DeleteEntityAndReturn(previewEntity, cmpPosition, pos, angle, cmpNewPosition, false);
}
function DeleteEntityAndReturn(ent, cmpPosition, position, angle, cmpNewPosition, ret)
{
// prevent preview from interfering in the world
cmpNewPosition.MoveOutOfWorld();
if (position !== null)
{
cmpPosition.JumpTo(position.x, position.y);
cmpPosition.SetYRotation(angle.y);
}
Engine.DestroyEntity(ent);
return ret;
}
function TransferGarrisonedUnits(oldEnt, newEnt)
{
// Transfer garrisoned units if possible, or unload them
let cmpOldGarrison = Engine.QueryInterface(oldEnt, IID_GarrisonHolder);
if (!cmpOldGarrison || !cmpOldGarrison.GetEntities().length)
return;
let cmpNewGarrison = Engine.QueryInterface(newEnt, IID_GarrisonHolder);
let entities = cmpOldGarrison.GetEntities().slice();
for (let ent of entities)
{
cmpOldGarrison.Eject(ent);
if (!cmpNewGarrison)
continue;
let cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
if (!cmpUnitAI)
continue;
cmpUnitAI.Autogarrison(newEnt);
cmpNewGarrison.Garrison(ent);
}
}
Engine.RegisterGlobal("ChangeEntityTemplate", ChangeEntityTemplate);
Engine.RegisterGlobal("CanGarrisonedChangeTemplate", CanGarrisonedChangeTemplate);
Engine.RegisterGlobal("ObstructionsBlockingTemplateChange", ObstructionsBlockingTemplateChange);