LCOV - code coverage report
Current view: top level - simulation/helpers - Transform.js (source / functions) Hit Total Coverage
Test: lcov.info Lines: 67 157 42.7 %
Date: 2023-04-02 12:52:40 Functions: 3 6 50.0 %

          Line data    Source code
       1             : // Helper functions to change an entity's template and check if the transformation is possible
       2             : 
       3             : // returns the ID of the new entity or INVALID_ENTITY.
       4             : function ChangeEntityTemplate(oldEnt, newTemplate)
       5             : {
       6             :         // Done un/packing, copy our parameters to the final entity
       7           4 :         var newEnt = Engine.AddEntity(newTemplate);
       8           4 :         if (newEnt == INVALID_ENTITY)
       9             :         {
      10           0 :                 error("Transform.js: Error replacing entity " + oldEnt + " for a '" + newTemplate + "'");
      11           0 :                 return INVALID_ENTITY;
      12             :         }
      13             : 
      14           4 :         Engine.ProfileStart("Transform");
      15             : 
      16           4 :         const cmpVisual = Engine.QueryInterface(oldEnt, IID_Visual);
      17           4 :         const cmpNewVisual = Engine.QueryInterface(newEnt, IID_Visual);
      18           4 :         if (cmpVisual && cmpNewVisual)
      19           0 :                 cmpNewVisual.SetActorSeed(cmpVisual.GetActorSeed());
      20             : 
      21           4 :         var cmpPosition = Engine.QueryInterface(oldEnt, IID_Position);
      22           4 :         var cmpNewPosition = Engine.QueryInterface(newEnt, IID_Position);
      23           4 :         if (cmpPosition && cmpNewPosition)
      24             :         {
      25           4 :                 if (cmpPosition.IsInWorld())
      26             :                 {
      27           4 :                         let pos = cmpPosition.GetPosition2D();
      28           4 :                         cmpNewPosition.JumpTo(pos.x, pos.y);
      29             :                 }
      30           4 :                 let rot = cmpPosition.GetRotation();
      31           4 :                 cmpNewPosition.SetYRotation(rot.y);
      32           4 :                 cmpNewPosition.SetXZRotation(rot.x, rot.z);
      33           4 :                 cmpNewPosition.SetHeightOffset(cmpPosition.GetHeightOffset());
      34             :         }
      35             : 
      36             :         // Prevent spawning subunits on occupied positions.
      37           4 :         let cmpTurretHolder = Engine.QueryInterface(oldEnt, IID_TurretHolder);
      38           4 :         let cmpNewTurretHolder = Engine.QueryInterface(newEnt, IID_TurretHolder);
      39           4 :         if (cmpTurretHolder && cmpNewTurretHolder)
      40           0 :                 for (let entity of cmpTurretHolder.GetEntities())
      41           0 :                         cmpNewTurretHolder.SetReservedTurretPoint(cmpTurretHolder.GetOccupiedTurretPointName(entity));
      42             : 
      43             :         let owner;
      44           4 :         let cmpTerritoryDecay = Engine.QueryInterface(newEnt, IID_TerritoryDecay);
      45           4 :         if (cmpTerritoryDecay && cmpTerritoryDecay.HasTerritoryOwnership() && cmpNewPosition)
      46             :         {
      47           1 :                 let pos = cmpNewPosition.GetPosition2D();
      48           1 :                 let cmpTerritoryManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TerritoryManager);
      49           1 :                 owner = cmpTerritoryManager.GetOwner(pos.x, pos.y);
      50             :         }
      51             :         else
      52             :         {
      53           3 :                 let cmpOwnership = Engine.QueryInterface(oldEnt, IID_Ownership);
      54           3 :                 if (cmpOwnership)
      55           3 :                         owner = cmpOwnership.GetOwner();
      56             :         }
      57           4 :         let cmpNewOwnership = Engine.QueryInterface(newEnt, IID_Ownership);
      58           4 :         if (cmpNewOwnership)
      59           4 :                 cmpNewOwnership.SetOwner(owner);
      60             : 
      61           4 :         CopyControlGroups(oldEnt, newEnt);
      62             : 
      63             :         // Rescale capture points
      64           4 :         var cmpCapturable = Engine.QueryInterface(oldEnt, IID_Capturable);
      65           4 :         var cmpNewCapturable = Engine.QueryInterface(newEnt, IID_Capturable);
      66           4 :         if (cmpCapturable && cmpNewCapturable)
      67             :         {
      68           0 :                 let scale = cmpCapturable.GetMaxCapturePoints() / cmpNewCapturable.GetMaxCapturePoints();
      69           0 :                 let newCapturePoints = cmpCapturable.GetCapturePoints().map(v => v / scale);
      70           0 :                 cmpNewCapturable.SetCapturePoints(newCapturePoints);
      71             :         }
      72             : 
      73             :         // Maintain current health level
      74           4 :         var cmpHealth = Engine.QueryInterface(oldEnt, IID_Health);
      75           4 :         var cmpNewHealth = Engine.QueryInterface(newEnt, IID_Health);
      76           4 :         if (cmpHealth && cmpNewHealth)
      77             :         {
      78           0 :                 var healthLevel = Math.max(0, Math.min(1, cmpHealth.GetHitpoints() / cmpHealth.GetMaxHitpoints()));
      79           0 :                 cmpNewHealth.SetHitpoints(cmpNewHealth.GetMaxHitpoints() * healthLevel);
      80             :         }
      81             : 
      82           4 :         let cmpPromotion = Engine.QueryInterface(oldEnt, IID_Promotion);
      83           4 :         let cmpNewPromotion = Engine.QueryInterface(newEnt, IID_Promotion);
      84           4 :         if (cmpPromotion && cmpNewPromotion)
      85             :         {
      86           0 :                 cmpPromotion.SetPromotedEntity(newEnt);
      87           0 :                 cmpNewPromotion.IncreaseXp(cmpPromotion.GetCurrentXp());
      88             :         }
      89             : 
      90           4 :         let cmpResGatherer = Engine.QueryInterface(oldEnt, IID_ResourceGatherer);
      91           4 :         let cmpNewResGatherer = Engine.QueryInterface(newEnt, IID_ResourceGatherer);
      92           4 :         if (cmpResGatherer && cmpNewResGatherer)
      93             :         {
      94           0 :                 let carriedResources = cmpResGatherer.GetCarryingStatus();
      95           0 :                 cmpNewResGatherer.GiveResources(carriedResources);
      96           0 :                 cmpNewResGatherer.SetLastCarriedType(cmpResGatherer.GetLastCarriedType());
      97             :         }
      98             : 
      99             :         // Maintain the list of guards
     100           4 :         let cmpGuard = Engine.QueryInterface(oldEnt, IID_Guard);
     101           4 :         let cmpNewGuard = Engine.QueryInterface(newEnt, IID_Guard);
     102           4 :         if (cmpGuard && cmpNewGuard)
     103             :         {
     104           0 :                 let entities = cmpGuard.GetEntities();
     105           0 :                 if (entities.length)
     106             :                 {
     107           0 :                         cmpNewGuard.SetEntities(entities);
     108           0 :                         for (let ent of entities)
     109             :                         {
     110           0 :                                 let cmpEntUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
     111           0 :                                 if (cmpEntUnitAI)
     112           0 :                                         cmpEntUnitAI.SetGuardOf(newEnt);
     113             :                         }
     114             :                 }
     115             :         }
     116             : 
     117           4 :         let cmpStatusEffectsReceiver = Engine.QueryInterface(oldEnt, IID_StatusEffectsReceiver);
     118           4 :         let cmpNewStatusEffectsReceiver = Engine.QueryInterface(newEnt, IID_StatusEffectsReceiver);
     119           4 :         if (cmpStatusEffectsReceiver && cmpNewStatusEffectsReceiver)
     120             :         {
     121           0 :                 let activeStatus = cmpStatusEffectsReceiver.GetActiveStatuses();
     122           0 :                 for (let status in activeStatus)
     123             :                 {
     124           0 :                         let newStatus = activeStatus[status];
     125           0 :                         if (newStatus.Duration)
     126           0 :                                 newStatus.Duration -= newStatus._timeElapsed;
     127           0 :                         cmpNewStatusEffectsReceiver.ApplyStatus({ [status]: newStatus }, newStatus.source.entity, newStatus.source.owner);
     128             :                 }
     129             :         }
     130             : 
     131           4 :         TransferGarrisonedUnits(oldEnt, newEnt);
     132             : 
     133           4 :         Engine.PostMessage(oldEnt, MT_EntityRenamed, { "entity": oldEnt, "newentity": newEnt });
     134             : 
     135             :         // UnitAI generally needs other components to be properly initialised.
     136           4 :         let cmpUnitAI = Engine.QueryInterface(oldEnt, IID_UnitAI);
     137           4 :         let cmpNewUnitAI = Engine.QueryInterface(newEnt, IID_UnitAI);
     138           4 :         if (cmpUnitAI && cmpNewUnitAI)
     139             :         {
     140           0 :                 let pos = cmpUnitAI.GetHeldPosition();
     141           0 :                 if (pos)
     142           0 :                         cmpNewUnitAI.SetHeldPosition(pos.x, pos.z);
     143           0 :                 cmpNewUnitAI.SwitchToStance(cmpUnitAI.GetStanceName());
     144           0 :                 cmpNewUnitAI.AddOrders(cmpUnitAI.GetOrders());
     145           0 :                 let guarded = cmpUnitAI.IsGuardOf();
     146           0 :                 if (guarded)
     147             :                 {
     148           0 :                         let cmpGuarded = Engine.QueryInterface(guarded, IID_Guard);
     149           0 :                         if (cmpGuarded)
     150             :                         {
     151           0 :                                 cmpGuarded.RenameGuard(oldEnt, newEnt);
     152           0 :                                 cmpNewUnitAI.SetGuardOf(guarded);
     153             :                         }
     154             :                 }
     155             :         }
     156             : 
     157           4 :         if (cmpPosition && cmpPosition.IsInWorld())
     158           4 :                 cmpPosition.MoveOutOfWorld();
     159             : 
     160           4 :         Engine.ProfileStop();
     161             : 
     162           4 :         Engine.DestroyEntity(oldEnt);
     163             : 
     164           4 :         return newEnt;
     165             : }
     166             : 
     167             : /**
     168             :  * Copy over the obstruction control group IDs.
     169             :  * This is needed to ensure that when a group of structures with the same
     170             :  * control groups is replaced by a new entity, they remains in the same control group(s).
     171             :  * This is the mechanism that is used to e.g. enable wall pieces to be built closely
     172             :  * together, ignoring their mutual obstruction shapes (since they would
     173             :  * otherwise be prevented from being built so closely together).
     174             :  */
     175             : function CopyControlGroups(oldEnt, newEnt)
     176             : {
     177           4 :         let cmpObstruction = Engine.QueryInterface(oldEnt, IID_Obstruction);
     178           4 :         let cmpNewObstruction = Engine.QueryInterface(newEnt, IID_Obstruction);
     179           4 :         if (cmpObstruction && cmpNewObstruction)
     180             :         {
     181           0 :                 cmpNewObstruction.SetControlGroup(cmpObstruction.GetControlGroup());
     182           0 :                 cmpNewObstruction.SetControlGroup2(cmpObstruction.GetControlGroup2());
     183             :         }
     184             : }
     185             : 
     186             : function ObstructionsBlockingTemplateChange(ent, templateArg)
     187             : {
     188           0 :         var previewEntity = Engine.AddEntity("preview|"+templateArg);
     189             : 
     190           0 :         if (previewEntity == INVALID_ENTITY)
     191           0 :                 return true;
     192             : 
     193           0 :         CopyControlGroups(ent, previewEntity);
     194           0 :         var cmpBuildRestrictions = Engine.QueryInterface(previewEntity, IID_BuildRestrictions);
     195           0 :         var cmpPosition = Engine.QueryInterface(ent, IID_Position);
     196           0 :         var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
     197             : 
     198           0 :         var cmpNewPosition = Engine.QueryInterface(previewEntity, IID_Position);
     199             : 
     200             :         // Return false if no ownership as BuildRestrictions.CheckPlacement needs an owner and I have no idea if false or true is better
     201             :         // Plus there are no real entities without owners currently.
     202           0 :         if (!cmpBuildRestrictions || !cmpPosition || !cmpOwnership)
     203           0 :                 return DeleteEntityAndReturn(previewEntity, cmpPosition, null, null, cmpNewPosition, false);
     204             : 
     205           0 :         var pos = cmpPosition.GetPosition2D();
     206           0 :         var angle = cmpPosition.GetRotation();
     207             :         // move us away to prevent our own obstruction from blocking the upgrade.
     208           0 :         cmpPosition.MoveOutOfWorld();
     209             : 
     210           0 :         cmpNewPosition.JumpTo(pos.x, pos.y);
     211           0 :         cmpNewPosition.SetYRotation(angle.y);
     212             : 
     213           0 :         var cmpNewOwnership = Engine.QueryInterface(previewEntity, IID_Ownership);
     214           0 :         cmpNewOwnership.SetOwner(cmpOwnership.GetOwner());
     215             : 
     216           0 :         var checkPlacement = cmpBuildRestrictions.CheckPlacement();
     217             : 
     218           0 :         if (checkPlacement && !checkPlacement.success)
     219           0 :                 return DeleteEntityAndReturn(previewEntity, cmpPosition, pos, angle, cmpNewPosition, true);
     220             : 
     221           0 :         var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
     222           0 :         var template = cmpTemplateManager.GetTemplate(cmpTemplateManager.GetCurrentTemplateName(ent));
     223           0 :         var newTemplate = cmpTemplateManager.GetTemplate(templateArg);
     224             : 
     225             :         // Check if units are blocking our template change
     226           0 :         if (template.Obstruction && newTemplate.Obstruction)
     227             :         {
     228             :                 // This only needs to be done if the new template is strictly bigger than the old one
     229             :                 // "Obstructions" are annoying to test so just check.
     230           0 :                 if (newTemplate.Obstruction.Obstructions ||
     231             : 
     232             :                         newTemplate.Obstruction.Static && template.Obstruction.Static &&
     233             :                                 (newTemplate.Obstruction.Static["@width"] > template.Obstruction.Static["@width"] ||
     234             :                                  newTemplate.Obstruction.Static["@depth"] > template.Obstruction.Static["@depth"]) ||
     235             :                         newTemplate.Obstruction.Static && template.Obstruction.Unit &&
     236             :                                 (newTemplate.Obstruction.Static["@width"] > template.Obstruction.Unit["@radius"] ||
     237             :                                  newTemplate.Obstruction.Static["@depth"] > template.Obstruction.Unit["@radius"]) ||
     238             : 
     239             :                         newTemplate.Obstruction.Unit && template.Obstruction.Unit &&
     240             :                                 newTemplate.Obstruction.Unit["@radius"] > template.Obstruction.Unit["@radius"] ||
     241             :                         newTemplate.Obstruction.Unit && template.Obstruction.Static &&
     242             :                                 (newTemplate.Obstruction.Unit["@radius"] > template.Obstruction.Static["@width"] ||
     243             :                                  newTemplate.Obstruction.Unit["@radius"] > template.Obstruction.Static["@depth"]))
     244             :                 {
     245           0 :                         var cmpNewObstruction = Engine.QueryInterface(previewEntity, IID_Obstruction);
     246           0 :                         if (cmpNewObstruction && cmpNewObstruction.GetBlockMovementFlag())
     247             :                         {
     248             :                                 // Remove all obstructions at the new entity, especially animal corpses
     249           0 :                                 for (let ent of cmpNewObstruction.GetEntitiesDeletedUponConstruction())
     250           0 :                                         Engine.DestroyEntity(ent);
     251             : 
     252           0 :                                 let collisions = cmpNewObstruction.GetEntitiesBlockingConstruction();
     253           0 :                                 if (collisions.length)
     254           0 :                                         return DeleteEntityAndReturn(previewEntity, cmpPosition, pos, angle, cmpNewPosition, true);
     255             :                         }
     256             :                 }
     257             :         }
     258             : 
     259           0 :         return DeleteEntityAndReturn(previewEntity, cmpPosition, pos, angle, cmpNewPosition, false);
     260             : }
     261             : 
     262             : function DeleteEntityAndReturn(ent, cmpPosition, position, angle, cmpNewPosition, ret)
     263             : {
     264             :         // prevent preview from interfering in the world
     265           0 :         cmpNewPosition.MoveOutOfWorld();
     266           0 :         if (position !== null)
     267             :         {
     268           0 :                 cmpPosition.JumpTo(position.x, position.y);
     269           0 :                 cmpPosition.SetYRotation(angle.y);
     270             :         }
     271             : 
     272           0 :         Engine.DestroyEntity(ent);
     273           0 :         return ret;
     274             : }
     275             : 
     276             : function TransferGarrisonedUnits(oldEnt, newEnt)
     277             : {
     278             :         // Transfer garrisoned units if possible, or unload them
     279           4 :         let cmpOldGarrison = Engine.QueryInterface(oldEnt, IID_GarrisonHolder);
     280           4 :         if (!cmpOldGarrison || !cmpOldGarrison.GetEntities().length)
     281           4 :                 return;
     282             : 
     283           0 :         let cmpNewGarrison = Engine.QueryInterface(newEnt, IID_GarrisonHolder);
     284           0 :         let entities = cmpOldGarrison.GetEntities().slice();
     285           0 :         for (let ent of entities)
     286             :         {
     287           0 :                 cmpOldGarrison.Unload(ent);
     288           0 :                 if (!cmpNewGarrison)
     289           0 :                         continue;
     290           0 :                 let cmpGarrisonable = Engine.QueryInterface(ent, IID_Garrisonable);
     291           0 :                 if (!cmpGarrisonable)
     292           0 :                         continue;
     293           0 :                 cmpGarrisonable.Garrison(newEnt);
     294             :         }
     295             : }
     296             : 
     297           2 : Engine.RegisterGlobal("ChangeEntityTemplate", ChangeEntityTemplate);
     298           2 : Engine.RegisterGlobal("ObstructionsBlockingTemplateChange", ObstructionsBlockingTemplateChange);

Generated by: LCOV version 1.14