Index: ps/trunk/binaries/data/mods/public/simulation/components/Promotion.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/Promotion.js (revision 25355)
+++ ps/trunk/binaries/data/mods/public/simulation/components/Promotion.js (revision 25356)
@@ -1,138 +1,135 @@
function Promotion() {}
Promotion.prototype.Schema =
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"";
Promotion.prototype.Init = function()
{
this.currentXp = 0;
this.ComputeTrickleRate();
};
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
let cmpHealth = Engine.QueryInterface(this.entity, IID_Health);
if (cmpHealth && cmpHealth.GetHitpoints() == 0)
{
this.promotedUnitEntity = INVALID_ENTITY;
return;
}
// Save the entity id.
this.promotedUnitEntity = ChangeEntityTemplate(this.entity, promotedTemplateName);
};
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)
{
let cmpPromotion = Engine.QueryInterface(this.promotedUnitEntity, IID_Promotion);
if (cmpPromotion)
cmpPromotion.IncreaseXp(amount);
return;
}
this.currentXp += +amount;
let requiredXp = this.GetRequiredXp();
if (this.currentXp >= requiredXp)
{
let cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
let cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
if (!cmpPlayer)
return;
let playerID = cmpPlayer.GetPlayerID();
this.currentXp -= requiredXp;
let promotedTemplateName = this.GetPromotedTemplateName();
// check if we can upgrade a second time (or even more)
while (true)
{
let 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.ComputeTrickleRate = function()
{
this.trickleRate = ApplyValueModificationsToEntity("Promotion/TrickleRate", +(this.template.TrickleRate || 0), this.entity);
this.CheckTrickleTimer();
};
Promotion.prototype.CheckTrickleTimer = function()
{
if (!this.trickleRate)
{
if (this.trickleTimer)
{
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
cmpTimer.CancelTimer(this.trickleTimer);
delete this.trickleTimer;
}
return;
}
if (this.trickleTimer)
return;
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
this.trickleTimer = cmpTimer.SetInterval(this.entity, IID_Promotion, "TrickleTick", 1000, 1000, null);
};
Promotion.prototype.TrickleTick = function()
{
this.IncreaseXp(this.trickleRate);
};
Promotion.prototype.OnValueModification = function(msg)
{
if (msg.component != "Promotion")
return;
this.ComputeTrickleRate();
this.IncreaseXp(0);
};
Engine.RegisterComponentType(IID_Promotion, "Promotion", Promotion);
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 25355)
+++ ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Promotion.js (revision 25356)
@@ -1,120 +1,122 @@
Engine.LoadComponentScript("interfaces/Health.js");
Engine.LoadComponentScript("interfaces/Promotion.js");
Engine.LoadComponentScript("interfaces/Timer.js");
Engine.LoadComponentScript("interfaces/UnitAI.js");
Engine.LoadComponentScript("Promotion.js");
Engine.LoadComponentScript("Timer.js");
let cmpPromotion;
const entity = 60;
let modifier = 0;
let ApplyValueModificationsToEntity = (_, val) => val + modifier;
Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity);
Engine.RegisterGlobal("ApplyValueModificationsToTemplate", ApplyValueModificationsToEntity);
let QueryOwnerInterface = () => ({ "GetPlayerID": () => 1 });
Engine.RegisterGlobal("QueryOwnerInterface", QueryOwnerInterface);
let entTemplates = {
"60": "template_b",
"61": "template_f",
"62": "end"
};
let promote = {
"template_b": "template_c",
"template_c": "template_d",
"template_d": "template_e",
"template_e": "template_f"
};
AddMock(SYSTEM_ENTITY, IID_TemplateManager, {
"GetTemplate": (t) => ({
"Promotion": {
"Entity": promote[t],
"RequiredXp": 1000
},
}),
});
let ChangeEntityTemplate = function(ent, template)
{
let newEnt = ent + 1;
+ let exp = cmpPromotion.GetCurrentXp();
cmpPromotion = ConstructComponent(newEnt, "Promotion", {
"Entity": entTemplates[newEnt],
"RequiredXp": 1000
});
+ cmpPromotion.IncreaseXp(exp);
return newEnt;
};
Engine.RegisterGlobal("ChangeEntityTemplate", ChangeEntityTemplate);
cmpPromotion = ConstructComponent(entity, "Promotion", {
"Entity": "template_b",
"RequiredXp": 1000
});
// Test getters/setters.
TS_ASSERT_EQUALS(cmpPromotion.GetRequiredXp(), 1000);
TS_ASSERT_EQUALS(cmpPromotion.GetCurrentXp(), 0);
TS_ASSERT_EQUALS(cmpPromotion.GetPromotedTemplateName(), "template_b");
modifier = 111;
TS_ASSERT_EQUALS(cmpPromotion.GetRequiredXp(), 1111);
modifier = 0;
cmpPromotion.IncreaseXp(200);
TS_ASSERT_EQUALS(cmpPromotion.GetCurrentXp(), 200);
// Test promotion itself.
cmpPromotion.IncreaseXp(800);
TS_ASSERT_EQUALS(cmpPromotion.entity, 61);
TS_ASSERT_EQUALS(cmpPromotion.GetCurrentXp(), 0);
TS_ASSERT_EQUALS(cmpPromotion.GetRequiredXp(), 1000);
TS_ASSERT_EQUALS(cmpPromotion.GetPromotedTemplateName(), "template_f");
// Test multiple promotions at once.
cmpPromotion.IncreaseXp(4200);
TS_ASSERT_EQUALS(cmpPromotion.entity, 62);
TS_ASSERT_EQUALS(cmpPromotion.template.Entity, "end");
TS_ASSERT_EQUALS(cmpPromotion.GetCurrentXp(), 200);
TS_ASSERT_EQUALS(cmpPromotion.GetPromotedTemplateName(), "end");
// Test a dead entity can't promote.
cmpPromotion = ConstructComponent(entity, "Promotion", {
"Entity": "template_b",
"RequiredXp": 1000
});
let cmpHealth = AddMock(entity, IID_Health, {
"GetHitpoints": () => 0,
});
cmpPromotion.IncreaseXp(1000);
TS_ASSERT_EQUALS(cmpPromotion.entity, entity);
TS_ASSERT_EQUALS(cmpPromotion.GetCurrentXp(), 0);
DeleteMock(entity, IID_Health);
// Test XP trickle.
let cmpTimer = ConstructComponent(SYSTEM_ENTITY, "Timer", {});
cmpPromotion = ConstructComponent(entity, "Promotion", {
"Entity": "template_b",
"RequiredXp": "100",
"TrickleRate": "10"
});
TS_ASSERT_EQUALS(cmpPromotion.GetCurrentXp(), 0);
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(cmpPromotion.GetCurrentXp(), 10);
cmpTimer.OnUpdate({ "turnLength": 2 });
TS_ASSERT_EQUALS(cmpPromotion.GetCurrentXp(), 30);
// Test promoted due to trickle.
cmpTimer.OnUpdate({ "turnLength": 8 });
TS_ASSERT_EQUALS(cmpPromotion.GetCurrentXp(), 10);
TS_ASSERT_EQUALS(cmpPromotion.entity, 61);
// Test valuemodification applies.
modifier = 10;
cmpPromotion.OnValueModification({ "component": "Promotion" });
cmpTimer.OnUpdate({ "turnLength": 4 });
TS_ASSERT_EQUALS(cmpPromotion.GetCurrentXp(), 90);