Index: ps/trunk/binaries/data/mods/public/simulation/components/Promotion.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/Promotion.js (revision 22807) +++ ps/trunk/binaries/data/mods/public/simulation/components/Promotion.js (revision 22808) @@ -1,97 +1,100 @@ 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 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); let cmpPosition = Engine.QueryInterface(this.promotedUnitEntity, IID_Position); let cmpUnitAI = Engine.QueryInterface(this.promotedUnitEntity, IID_UnitAI); 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) { 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_Promotion.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Promotion.js (revision 22807) +++ ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Promotion.js (revision 22808) @@ -1,66 +1,77 @@ Engine.LoadComponentScript("interfaces/Health.js"); Engine.LoadComponentScript("interfaces/Promotion.js"); Engine.LoadComponentScript("interfaces/UnitAI.js"); Engine.LoadComponentScript("Promotion.js"); (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", }; 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 cmpPromotion = ConstructComponent(ENT_ID, "Promotion", { "Entity": "template_b", "RequiredXp": 1000 }); let ChangeEntityTemplate = function(ent, template) { cmpPromotion = ConstructComponent(ent + 1, "Promotion", { "Entity": entTemplates[ent + 1], "RequiredXp": 1000 }); return ent + 1; }; Engine.RegisterGlobal("ChangeEntityTemplate", ChangeEntityTemplate); 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); + +cmpPromotion = ConstructComponent(ENT_ID, "Promotion", { + "Entity": "template_b", + "RequiredXp": 1000 +}); + +let cmpHealth = AddMock(ENT_ID, IID_Health, { + "GetHitpoints": () => 0, +}); + +cmpPromotion.IncreaseXp(1000); })();