LCOV - code coverage report
Current view: top level - simulation/components - Health.js (source / functions) Hit Total Coverage
Test: lcov.info Lines: 127 245 51.8 %
Date: 2023-04-02 12:52:40 Functions: 16 33 48.5 %

          Line data    Source code
       1             : function Health() {}
       2             : 
       3           3 : Health.prototype.Schema =
       4             :         "<a:help>Deals with hitpoints and death.</a:help>" +
       5             :         "<a:example>" +
       6             :                 "<Max>100</Max>" +
       7             :                 "<RegenRate>1.0</RegenRate>" +
       8             :                 "<IdleRegenRate>0</IdleRegenRate>" +
       9             :                 "<DeathType>corpse</DeathType>" +
      10             :         "</a:example>" +
      11             :         "<element name='Max' a:help='Maximum hitpoints'>" +
      12             :                 "<ref name='nonNegativeDecimal'/>" +
      13             :         "</element>" +
      14             :         "<optional>" +
      15             :                 "<element name='Initial' a:help='Initial hitpoints. Default if unspecified is equal to Max'>" +
      16             :                         "<ref name='nonNegativeDecimal'/>" +
      17             :                 "</element>" +
      18             :         "</optional>" +
      19             :         "<optional>" +
      20             :                 "<element name='DamageVariants'>" +
      21             :                         "<oneOrMore>" +
      22             :                                 "<element a:help='Name of the variant to select when health drops under the defined ratio'>" +
      23             :                                         "<anyName/>" +
      24             :                                         "<data type='decimal'>" +
      25             :                                                 "<param name='minInclusive'>0</param>" +
      26             :                                                 "<param name='maxInclusive'>1</param>" +
      27             :                                         "</data>" +
      28             :                                 "</element>" +
      29             :                         "</oneOrMore>" +
      30             :                 "</element>" +
      31             :         "</optional>" +
      32             :         "<element name='RegenRate' a:help='Hitpoint regeneration rate per second.'>" +
      33             :                 "<data type='decimal'/>" +
      34             :         "</element>" +
      35             :         "<element name='IdleRegenRate' a:help='Hitpoint regeneration rate per second when idle or garrisoned.'>" +
      36             :                 "<data type='decimal'/>" +
      37             :         "</element>" +
      38             :         "<element name='DeathType' a:help='Behaviour when the unit dies'>" +
      39             :                 "<choice>" +
      40             :                         "<value a:help='Disappear instantly'>vanish</value>" +
      41             :                         "<value a:help='Turn into a corpse'>corpse</value>" +
      42             :                         "<value a:help='Remain in the world with 0 health'>remain</value>" +
      43             :                 "</choice>" +
      44             :         "</element>" +
      45             :         "<optional>" +
      46             :                 "<element name='SpawnEntityOnDeath' a:help='Entity template to spawn when this entity dies. Note: this is different than the corpse, which retains the original entity&apos;s appearance'>" +
      47             :                         "<text/>" +
      48             :                 "</element>" +
      49             :         "</optional>" +
      50             :         "<element name='Unhealable' a:help='Indicates that the entity can not be healed by healer units'>" +
      51             :                 "<data type='boolean'/>" +
      52             :         "</element>";
      53             : 
      54           3 : Health.prototype.Init = function()
      55             : {
      56             :         // Cache this value so it allows techs to maintain previous health level
      57           4 :         this.maxHitpoints = +this.template.Max;
      58             :         // Default to <Initial>, but use <Max> if it's undefined or zero
      59             :         // (Allowing 0 initial HP would break our death detection code)
      60           4 :         this.hitpoints = +(this.template.Initial || this.GetMaxHitpoints());
      61           4 :         this.regenRate = ApplyValueModificationsToEntity("Health/RegenRate", +this.template.RegenRate, this.entity);
      62           4 :         this.idleRegenRate = ApplyValueModificationsToEntity("Health/IdleRegenRate", +this.template.IdleRegenRate, this.entity);
      63           4 :         this.CheckRegenTimer();
      64           4 :         this.UpdateActor();
      65             : };
      66             : 
      67             : /**
      68             :  * Returns the current hitpoint value.
      69             :  * This is 0 if (and only if) the unit is dead.
      70             :  */
      71           3 : Health.prototype.GetHitpoints = function()
      72             : {
      73          16 :         return this.hitpoints;
      74             : };
      75             : 
      76           3 : Health.prototype.GetMaxHitpoints = function()
      77             : {
      78          44 :         return this.maxHitpoints;
      79             : };
      80             : 
      81             : /**
      82             :  * @return {boolean} Whether the units are injured. Dead units are not considered injured.
      83             :  */
      84           3 : Health.prototype.IsInjured = function()
      85             : {
      86          25 :         return this.hitpoints > 0 && this.hitpoints < this.GetMaxHitpoints();
      87             : };
      88             : 
      89           3 : Health.prototype.SetHitpoints = function(value)
      90             : {
      91             :         // If we're already dead, don't allow resurrection
      92           1 :         if (this.hitpoints == 0)
      93           0 :                 return;
      94             : 
      95             :         // Before changing the value, activate Fogging if necessary to hide changes
      96           1 :         let cmpFogging = Engine.QueryInterface(this.entity, IID_Fogging);
      97           1 :         if (cmpFogging)
      98           0 :                 cmpFogging.Activate();
      99             : 
     100           1 :         let old = this.hitpoints;
     101           1 :         this.hitpoints = Math.max(1, Math.min(this.GetMaxHitpoints(), value));
     102             : 
     103           1 :         let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     104           1 :         if (cmpRangeManager)
     105           0 :                 cmpRangeManager.SetEntityFlag(this.entity, "injured", this.IsInjured());
     106             : 
     107           1 :         this.RegisterHealthChanged(old);
     108             : };
     109             : 
     110           3 : Health.prototype.IsRepairable = function()
     111             : {
     112           0 :         let cmpRepairable = Engine.QueryInterface(this.entity, IID_Repairable);
     113           0 :         return cmpRepairable && cmpRepairable.IsRepairable();
     114             : };
     115             : 
     116           3 : Health.prototype.IsUnhealable = function()
     117             : {
     118           4 :         return this.template.Unhealable == "true" ||
     119             :                 this.hitpoints <= 0 || !this.IsInjured();
     120             : };
     121             : 
     122           3 : Health.prototype.GetIdleRegenRate = function()
     123             : {
     124          14 :         return this.idleRegenRate;
     125             : };
     126             : 
     127           3 : Health.prototype.GetRegenRate = function()
     128             : {
     129          14 :         return this.regenRate;
     130             : };
     131             : 
     132           3 : Health.prototype.ExecuteRegeneration = function()
     133             : {
     134           0 :         let regen = this.GetRegenRate();
     135           0 :         if (this.GetIdleRegenRate() != 0)
     136             :         {
     137           0 :                 let cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI);
     138           0 :                 if (cmpUnitAI && cmpUnitAI.IsIdle())
     139           0 :                         regen += this.GetIdleRegenRate();
     140             :         }
     141             : 
     142           0 :         if (regen > 0)
     143           0 :                 this.Increase(regen);
     144             :         else
     145           0 :                 this.Reduce(-regen);
     146             : };
     147             : 
     148             : /*
     149             :  * Check if the regeneration timer needs to be started or stopped
     150             :  */
     151           3 : Health.prototype.CheckRegenTimer = function()
     152             : {
     153             :         // check if we need a timer
     154          14 :         if (this.GetRegenRate() == 0 && this.GetIdleRegenRate() == 0 ||
     155             :             !this.IsInjured() && this.GetRegenRate() >= 0 && this.GetIdleRegenRate() >= 0 ||
     156             :             this.hitpoints == 0)
     157             :         {
     158             :                 // we don't need a timer, disable if one exists
     159          14 :                 if (this.regenTimer)
     160             :                 {
     161           0 :                         let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     162           0 :                         cmpTimer.CancelTimer(this.regenTimer);
     163           0 :                         this.regenTimer = undefined;
     164             :                 }
     165          14 :                 return;
     166             :         }
     167             : 
     168             :         // we need a timer, enable if one doesn't exist
     169           0 :         if (this.regenTimer)
     170           0 :                 return;
     171             : 
     172           0 :         let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     173           0 :         this.regenTimer = cmpTimer.SetInterval(this.entity, IID_Health, "ExecuteRegeneration", 1000, 1000, null);
     174             : };
     175             : 
     176           3 : Health.prototype.Kill = function()
     177             : {
     178           0 :         this.Reduce(this.hitpoints);
     179             : };
     180             : 
     181             : /**
     182             :  * @param {number} amount - The amount of damage to be taken.
     183             :  * @param {number} attacker - The entityID of the attacker.
     184             :  * @param {number} attackerOwner - The playerID of the owner of the attacker.
     185             :  *
     186             :  * @eturn {Object} - Object of the form { "healthChange": number }.
     187             :  */
     188           3 : Health.prototype.TakeDamage = function(amount, attacker, attackerOwner)
     189             : {
     190           0 :         if (!amount || !this.hitpoints)
     191           0 :                 return { "healthChange": 0 };
     192             : 
     193           0 :         let change = this.Reduce(amount);
     194             : 
     195           0 :         let cmpLoot = Engine.QueryInterface(this.entity, IID_Loot);
     196           0 :         if (cmpLoot && cmpLoot.GetXp() > 0 && change.healthChange < 0)
     197           0 :                 change.xp = cmpLoot.GetXp() * -change.healthChange / this.GetMaxHitpoints();
     198             : 
     199           0 :         if (!this.hitpoints)
     200           0 :                 this.KilledBy(attacker, attackerOwner);
     201             : 
     202           0 :         return change;
     203             : };
     204             : 
     205             : /**
     206             :  * Called when an entity kills us.
     207             :  * @param {number} attacker - The entityID of the killer.
     208             :  * @param {number} attackerOwner - The playerID of the attacker.
     209             :  */
     210           3 : Health.prototype.KilledBy = function(attacker, attackerOwner)
     211             : {
     212           0 :         let cmpAttackerOwnership = Engine.QueryInterface(attacker, IID_Ownership);
     213           0 :         if (cmpAttackerOwnership)
     214             :         {
     215           0 :                 let currentAttackerOwner = cmpAttackerOwnership.GetOwner();
     216           0 :                 if (currentAttackerOwner != INVALID_PLAYER)
     217           0 :                         attackerOwner = currentAttackerOwner;
     218             :         }
     219             : 
     220             :         // Add to killer statistics.
     221           0 :         let cmpKillerPlayerStatisticsTracker = QueryPlayerIDInterface(attackerOwner, IID_StatisticsTracker);
     222           0 :         if (cmpKillerPlayerStatisticsTracker)
     223           0 :                 cmpKillerPlayerStatisticsTracker.KilledEntity(this.entity);
     224             : 
     225             :         // Add to loser statistics.
     226           0 :         let cmpTargetPlayerStatisticsTracker = QueryOwnerInterface(this.entity, IID_StatisticsTracker);
     227           0 :         if (cmpTargetPlayerStatisticsTracker)
     228           0 :                 cmpTargetPlayerStatisticsTracker.LostEntity(this.entity);
     229             : 
     230           0 :         let cmpLooter = Engine.QueryInterface(attacker, IID_Looter);
     231           0 :         if (cmpLooter)
     232           0 :                 cmpLooter.Collect(this.entity);
     233             : };
     234             : 
     235             : /**
     236             :  * @param {number} amount - The amount of hitpoints to substract. Kills the entity if required.
     237             :  * @return {{ healthChange:number }} -  Number of health points lost.
     238             :  */
     239           3 : Health.prototype.Reduce = function(amount)
     240             : {
     241             :         // If we are dead, do not do anything
     242             :         // (The entity will exist a little while after calling DestroyEntity so this
     243             :         // might get called multiple times)
     244             :         // Likewise if the amount is 0.
     245           5 :         if (!amount || !this.hitpoints)
     246           1 :                 return { "healthChange": 0 };
     247             : 
     248             :         // Before changing the value, activate Fogging if necessary to hide changes
     249           4 :         let cmpFogging = Engine.QueryInterface(this.entity, IID_Fogging);
     250           4 :         if (cmpFogging)
     251           0 :                 cmpFogging.Activate();
     252             : 
     253           4 :         let oldHitpoints = this.hitpoints;
     254             :         // If we reached 0, then die.
     255           4 :         if (amount >= this.hitpoints)
     256             :         {
     257           2 :                 this.hitpoints = 0;
     258           2 :                 this.RegisterHealthChanged(oldHitpoints);
     259           2 :                 this.HandleDeath();
     260           2 :                 return { "healthChange": -oldHitpoints };
     261             :         }
     262             : 
     263             :         // If we are not marked as injured, do it now
     264           2 :         if (!this.IsInjured())
     265             :         {
     266           2 :                 let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     267           2 :                 if (cmpRangeManager)
     268           2 :                         cmpRangeManager.SetEntityFlag(this.entity, "injured", true);
     269             :         }
     270             : 
     271           2 :         this.hitpoints -= amount;
     272           2 :         this.RegisterHealthChanged(oldHitpoints);
     273           2 :         return { "healthChange": this.hitpoints - oldHitpoints };
     274             : };
     275             : 
     276             : /**
     277             :  * Handle what happens when the entity dies.
     278             :  */
     279           3 : Health.prototype.HandleDeath = function()
     280             : {
     281           2 :         let cmpDeathDamage = Engine.QueryInterface(this.entity, IID_DeathDamage);
     282           2 :         if (cmpDeathDamage)
     283           2 :                 cmpDeathDamage.CauseDeathDamage();
     284           2 :         PlaySound("death", this.entity);
     285             : 
     286           2 :         if (this.template.SpawnEntityOnDeath)
     287           0 :                 this.CreateDeathSpawnedEntity();
     288             : 
     289           2 :         switch (this.template.DeathType)
     290             :         {
     291             :         case "corpse":
     292           2 :                 this.CreateCorpse();
     293           2 :                 break;
     294             : 
     295             :         case "remain":
     296           0 :                 return;
     297             : 
     298             :         case "vanish":
     299           0 :                 break;
     300             : 
     301             :         default:
     302           0 :                 error("Invalid template.DeathType: " + this.template.DeathType);
     303           0 :                 break;
     304             :         }
     305             : 
     306           2 :         Engine.DestroyEntity(this.entity);
     307             : };
     308             : 
     309           3 : Health.prototype.Increase = function(amount)
     310             : {
     311             :         // Before changing the value, activate Fogging if necessary to hide changes
     312           6 :         let cmpFogging = Engine.QueryInterface(this.entity, IID_Fogging);
     313           6 :         if (cmpFogging)
     314           0 :                 cmpFogging.Activate();
     315             : 
     316           6 :         if (!this.IsInjured())
     317           1 :                 return { "old": this.hitpoints, "new": this.hitpoints };
     318             : 
     319             :         // If we're already dead, don't allow resurrection
     320           5 :         if (this.hitpoints == 0)
     321           0 :                 return undefined;
     322             : 
     323           5 :         let old = this.hitpoints;
     324           5 :         this.hitpoints = Math.min(this.hitpoints + amount, this.GetMaxHitpoints());
     325             : 
     326           5 :         if (!this.IsInjured())
     327             :         {
     328           2 :                 let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     329           2 :                 if (cmpRangeManager)
     330           2 :                         cmpRangeManager.SetEntityFlag(this.entity, "injured", false);
     331             :         }
     332             : 
     333           5 :         this.RegisterHealthChanged(old);
     334             : 
     335           5 :         return { "old": old, "new": this.hitpoints };
     336             : };
     337             : 
     338           3 : Health.prototype.CreateCorpse = function()
     339             : {
     340             :         // If the unit died while not in the world, don't create any corpse for it
     341             :         // since there's nowhere for the corpse to be placed.
     342           2 :         let cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
     343           2 :         if (!cmpPosition || !cmpPosition.IsInWorld())
     344           0 :                 return;
     345             : 
     346             :         // Either creates a static local version of the current entity, or a
     347             :         // persistent corpse retaining the ResourceSupply element of the parent.
     348           2 :         let templateName = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager).GetCurrentTemplateName(this.entity);
     349             : 
     350             :         let entCorpse;
     351           2 :         let cmpResourceSupply = Engine.QueryInterface(this.entity, IID_ResourceSupply);
     352           2 :         let resource = cmpResourceSupply && cmpResourceSupply.GetKillBeforeGather();
     353           2 :         if (resource)
     354           0 :                 entCorpse = Engine.AddEntity("resource|" + templateName);
     355             :         else
     356           2 :                 entCorpse = Engine.AddLocalEntity("corpse|" + templateName);
     357             : 
     358             :         // Copy various parameters so it looks just like us.
     359           2 :         let cmpPositionCorpse = Engine.QueryInterface(entCorpse, IID_Position);
     360           2 :         let pos = cmpPosition.GetPosition();
     361           2 :         cmpPositionCorpse.JumpTo(pos.x, pos.z);
     362           2 :         let rot = cmpPosition.GetRotation();
     363           2 :         cmpPositionCorpse.SetYRotation(rot.y);
     364           2 :         cmpPositionCorpse.SetXZRotation(rot.x, rot.z);
     365             : 
     366           2 :         let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
     367           2 :         let cmpOwnershipCorpse = Engine.QueryInterface(entCorpse, IID_Ownership);
     368           2 :         if (cmpOwnership && cmpOwnershipCorpse)
     369           2 :                 cmpOwnershipCorpse.SetOwner(cmpOwnership.GetOwner());
     370             : 
     371           2 :         let cmpVisualCorpse = Engine.QueryInterface(entCorpse, IID_Visual);
     372           2 :         if (cmpVisualCorpse)
     373             :         {
     374           2 :                 let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
     375           2 :                 if (cmpVisual)
     376           2 :                         cmpVisualCorpse.SetActorSeed(cmpVisual.GetActorSeed());
     377             : 
     378           2 :                 cmpVisualCorpse.SelectAnimation("death", true, 1);
     379             :         }
     380             : 
     381           2 :         const cmpIdentityCorpse = Engine.QueryInterface(entCorpse, IID_Identity);
     382           2 :         if (cmpIdentityCorpse)
     383             :         {
     384           0 :                 const cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
     385           0 :                 if (cmpIdentity)
     386             :                 {
     387           0 :                         const oldPhenotype = cmpIdentity.GetPhenotype();
     388           0 :                         if (cmpIdentityCorpse.GetPhenotype() !== oldPhenotype)
     389             :                         {
     390           0 :                                 cmpIdentityCorpse.SetPhenotype(oldPhenotype);
     391           0 :                                 if (cmpVisualCorpse)
     392           0 :                                         cmpVisualCorpse.RecomputeActorName();
     393             :                         }
     394             :                 }
     395             :         }
     396             : 
     397           2 :         if (resource)
     398           0 :                 Engine.PostMessage(this.entity, MT_EntityRenamed, {
     399             :                         "entity": this.entity,
     400             :                         "newentity": entCorpse
     401             :                 });
     402             : };
     403             : 
     404           3 : Health.prototype.CreateDeathSpawnedEntity = function()
     405             : {
     406             :         // If the unit died while not in the world, don't spawn a death entity for it
     407             :         // since there's nowhere for it to be placed
     408           0 :         let cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
     409           0 :         if (!cmpPosition.IsInWorld())
     410           0 :                 return INVALID_ENTITY;
     411             : 
     412             :         // Create SpawnEntityOnDeath entity
     413           0 :         let spawnedEntity = Engine.AddLocalEntity(this.template.SpawnEntityOnDeath);
     414             : 
     415             :         // Move to same position
     416           0 :         let cmpSpawnedPosition = Engine.QueryInterface(spawnedEntity, IID_Position);
     417           0 :         let pos = cmpPosition.GetPosition();
     418           0 :         cmpSpawnedPosition.JumpTo(pos.x, pos.z);
     419           0 :         let rot = cmpPosition.GetRotation();
     420           0 :         cmpSpawnedPosition.SetYRotation(rot.y);
     421           0 :         cmpSpawnedPosition.SetXZRotation(rot.x, rot.z);
     422             : 
     423           0 :         let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
     424           0 :         let cmpSpawnedOwnership = Engine.QueryInterface(spawnedEntity, IID_Ownership);
     425           0 :         if (cmpOwnership && cmpSpawnedOwnership)
     426           0 :                 cmpSpawnedOwnership.SetOwner(cmpOwnership.GetOwner());
     427             : 
     428           0 :         return spawnedEntity;
     429             : };
     430             : 
     431           3 : Health.prototype.UpdateActor = function()
     432             : {
     433          14 :         if (!this.template.DamageVariants)
     434          14 :                 return;
     435           0 :         let ratio = this.hitpoints / this.GetMaxHitpoints();
     436           0 :         let newDamageVariant = "alive";
     437           0 :         if (ratio > 0)
     438             :         {
     439           0 :                 let minTreshold = 1;
     440           0 :                 for (let key in this.template.DamageVariants)
     441             :                 {
     442           0 :                         let treshold = +this.template.DamageVariants[key];
     443           0 :                         if (treshold < ratio || treshold > minTreshold)
     444           0 :                                 continue;
     445           0 :                         newDamageVariant = key;
     446           0 :                         minTreshold = treshold;
     447             :                 }
     448             :         }
     449             :         else
     450           0 :                 newDamageVariant = "death";
     451             : 
     452           0 :         if (this.damageVariant && this.damageVariant == newDamageVariant)
     453           0 :                 return;
     454             : 
     455           0 :         this.damageVariant = newDamageVariant;
     456             : 
     457           0 :         let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
     458           0 :         if (cmpVisual)
     459           0 :                 cmpVisual.SetVariant("health", newDamageVariant);
     460             : };
     461             : 
     462           3 : Health.prototype.RecalculateValues = function()
     463             : {
     464           0 :         let oldMaxHitpoints = this.GetMaxHitpoints();
     465           0 :         let newMaxHitpoints = ApplyValueModificationsToEntity("Health/Max", +this.template.Max, this.entity);
     466           0 :         if (oldMaxHitpoints != newMaxHitpoints)
     467             :         {
     468             :                 // Don't recalculate hitpoints when full health due to float imprecision: #6657.
     469           0 :                 const newHitpoints = (this.hitpoints === oldMaxHitpoints) ? newMaxHitpoints :
     470             :                         this.hitpoints * newMaxHitpoints/oldMaxHitpoints;
     471           0 :                 this.maxHitpoints = newMaxHitpoints;
     472           0 :                 this.SetHitpoints(newHitpoints);
     473             :         }
     474             : 
     475           0 :         let oldRegenRate = this.regenRate;
     476           0 :         this.regenRate = ApplyValueModificationsToEntity("Health/RegenRate", +this.template.RegenRate, this.entity);
     477             : 
     478           0 :         let oldIdleRegenRate = this.idleRegenRate;
     479           0 :         this.idleRegenRate = ApplyValueModificationsToEntity("Health/IdleRegenRate", +this.template.IdleRegenRate, this.entity);
     480             : 
     481           0 :         if (this.regenRate != oldRegenRate || this.idleRegenRate != oldIdleRegenRate)
     482           0 :                 this.CheckRegenTimer();
     483             : };
     484             : 
     485           3 : Health.prototype.OnValueModification = function(msg)
     486             : {
     487           0 :         if (msg.component == "Health")
     488           0 :                 this.RecalculateValues();
     489             : };
     490             : 
     491           3 : Health.prototype.OnOwnershipChanged = function(msg)
     492             : {
     493           0 :         if (msg.to != INVALID_PLAYER)
     494           0 :                 this.RecalculateValues();
     495             : };
     496             : 
     497           3 : Health.prototype.RegisterHealthChanged = function(from)
     498             : {
     499          10 :         this.CheckRegenTimer();
     500          10 :         this.UpdateActor();
     501          10 :         Engine.PostMessage(this.entity, MT_HealthChanged, { "from": from, "to": this.hitpoints });
     502             : };
     503             : 
     504             : function HealthMirage() {}
     505           3 : HealthMirage.prototype.Init = function(cmpHealth)
     506             : {
     507           0 :         this.maxHitpoints = cmpHealth.GetMaxHitpoints();
     508           0 :         this.hitpoints = cmpHealth.GetHitpoints();
     509           0 :         this.repairable = cmpHealth.IsRepairable();
     510           0 :         this.injured = cmpHealth.IsInjured();
     511           0 :         this.unhealable = cmpHealth.IsUnhealable();
     512             : };
     513           3 : HealthMirage.prototype.GetMaxHitpoints = function() { return this.maxHitpoints; };
     514           3 : HealthMirage.prototype.GetHitpoints = function() { return this.hitpoints; };
     515           3 : HealthMirage.prototype.IsRepairable = function() { return this.repairable; };
     516           3 : HealthMirage.prototype.IsInjured = function() { return this.injured; };
     517           3 : HealthMirage.prototype.IsUnhealable = function() { return this.unhealable; };
     518             : 
     519           3 : Engine.RegisterGlobal("HealthMirage", HealthMirage);
     520             : 
     521           3 : Health.prototype.Mirage = function()
     522             : {
     523           0 :         let mirage = new HealthMirage();
     524           0 :         mirage.Init(this);
     525           0 :         return mirage;
     526             : };
     527             : 
     528           3 : Engine.RegisterComponentType(IID_Health, "Health", Health);

Generated by: LCOV version 1.14