source: ps/trunk/binaries/data/mods/public/simulation/helpers/Transform.js@ 25234

Last change on this file since 25234 was 25234, checked in by Freagarach, 3 years ago

Profile the Transform helper.

Differential revision: D3825
Comments by: @Stan, @wraitii

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