Ticket #4121: test_foundation.6.diff
File test_foundation.6.diff, 14.6 KB (added by , 8 years ago) |
---|
-
binaries/data/mods/public/simulation/components/Foundation.js
29 29 // Remember the cost here, so if it changes after construction begins (from auras or technologies) 30 30 // we will use the correct values to refund partial construction costs 31 31 let cmpCost = Engine.QueryInterface(this.entity, IID_Cost); 32 if (!cmpCost) 33 error("A foundation must have a cost component to know the build time"); 34 32 35 this.costs = cmpCost.GetResourceCosts(owner); 33 36 34 37 this.maxProgress = 0; … … 62 65 var hitpoints = cmpHealth.GetHitpoints(); 63 66 var maxHitpoints = cmpHealth.GetMaxHitpoints(); 64 67 65 return (hitpoints / maxHitpoints);68 return hitpoints / maxHitpoints; 66 69 }; 67 70 68 71 Foundation.prototype.GetBuildPercentage = function() … … 116 119 */ 117 120 Foundation.prototype.AddBuilder = function(builderEnt) 118 121 { 119 if (this.builders.indexOf(builderEnt) === -1) 120 { 121 this.builders.push(builderEnt); 122 Engine.QueryInterface(this.entity, IID_Visual).SetVariable("numbuilders", this.builders.length); 123 this.SetBuildMultiplier(); 124 Engine.PostMessage(this.entity, MT_FoundationBuildersChanged, { "to": this.builders }); 125 } 122 if (this.builders.indexOf(builderEnt) != -1) 123 return; 124 125 this.builders.push(builderEnt); 126 let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual); 127 if (cmpVisual) 128 cmpVisual.SetVariable("numbuilders", this.builders.length); 129 130 this.SetBuildMultiplier(); 131 Engine.PostMessage(this.entity, MT_FoundationBuildersChanged, { "to": this.builders }); 126 132 }; 127 133 128 134 Foundation.prototype.RemoveBuilder = function(builderEnt) 129 135 { 130 if (this.builders.indexOf(builderEnt) !== -1) 131 { 132 this.builders.splice(this.builders.indexOf(builderEnt),1); 133 Engine.QueryInterface(this.entity, IID_Visual).SetVariable("numbuilders", this.builders.length); 134 this.SetBuildMultiplier(); 135 Engine.PostMessage(this.entity, MT_FoundationBuildersChanged, { "to": this.builders }); 136 } 136 let index = this.builders.indexOf(builderEnt); 137 if (index == -1) 138 return; 139 140 this.builders.splice(index, 1); 141 let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual); 142 if (cmpVisual) 143 cmpVisual.SetVariable("numbuilders", this.builders.length); 144 145 this.SetBuildMultiplier(); 146 Engine.PostMessage(this.entity, MT_FoundationBuildersChanged, { "to": this.builders }); 137 147 }; 138 148 139 149 /** … … 142 152 */ 143 153 Foundation.prototype.SetBuildMultiplier = function() 144 154 { 155 // TODO get rid of magic 0.7 145 156 let numBuilders = this.builders.length; 146 157 if (numBuilders < 2) 147 158 this.buildMultiplier = 1; … … 199 210 // Handle the initial 'committing' of the foundation 200 211 if (!this.committed) 201 212 { 213 // The obstruction always blocks new foundations/construction, 214 // but we've temporarily allowed units to walk all over it 215 // (via CCmpTemplateManager). Now we need to remove that temporary 216 // blocker-disabling, so that we'll perform standard unit blocking instead. 202 217 if (cmpObstruction && cmpObstruction.GetBlockMovementFlag()) 203 {204 // The obstruction always blocks new foundations/construction,205 // but we've temporarily allowed units to walk all over it206 // (via CCmpTemplateManager). Now we need to remove that temporary207 // blocker-disabling, so that we'll perform standard unit blocking instead.208 218 cmpObstruction.SetDisableBlockMovementPathfinding(false, false, -1); 209 219 210 // Call the related trigger event 211 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 212 cmpTrigger.CallEvent("ConstructionStarted", {"foundation": this.entity, "template": this.finalTemplateName}); 213 } 220 // Call the related trigger event 221 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 222 cmpTrigger.CallEvent("ConstructionStarted", { 223 "foundation": this.entity, 224 "template": this.finalTemplateName 225 }); 214 226 215 227 // Switch foundation to scaffold variant 216 228 var cmpFoundationVisual = Engine.QueryInterface(this.entity, IID_Visual); … … 249 261 250 262 // Add an appropriate proportion of hitpoints 251 263 var cmpHealth = Engine.QueryInterface(this.entity, IID_Health); 264 if (!cmpHealth) 265 { 266 error("Foundation " + this.entity + " does not have a health component."); 267 return; 268 } 252 269 var maxHealth = cmpHealth.GetMaxHitpoints(); 253 270 var deltaHP = Math.max(work, Math.min(maxHealth, Math.floor(work * this.GetBuildRate() * this.buildMultiplier))); 254 271 if (deltaHP > 0) … … 274 291 cmpBuildingVisual.SetActorSeed(cmpVisual.GetActorSeed()); 275 292 276 293 var cmpPosition = Engine.QueryInterface(this.entity, IID_Position); 294 if (!cmpPosition || !cmpPosition.IsInWorld()) 295 { 296 error("Foundation " + this.entity + " does not have a position in-world."); 297 Engine.DestroyEntity(building); 298 return; 299 } 277 300 var cmpBuildingPosition = Engine.QueryInterface(building, IID_Position); 278 var pos = cmpPosition.GetPosition(); 279 cmpBuildingPosition.JumpTo(pos.x, pos.z); 301 if (!cmpBuildingPosition) 302 { 303 error("New building " + building + " has no position component."); 304 Engine.DestroyEntity(building); 305 return; 306 } 307 var pos = cmpPosition.GetPosition2D(); 308 cmpBuildingPosition.JumpTo(pos.x, pos.y); 280 309 var rot = cmpPosition.GetRotation(); 281 310 cmpBuildingPosition.SetYRotation(rot.y); 282 311 cmpBuildingPosition.SetXZRotation(rot.x, rot.z); … … 307 336 else 308 337 { 309 338 let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); 339 if (!cmpOwnership) 340 { 341 error("Foundation " + this.entity + " has no ownership."); 342 Engine.DestroyEntity(building); 343 return; 344 } 310 345 owner = cmpOwnership.GetOwner(); 311 346 } 312 347 var cmpBuildingOwnership = Engine.QueryInterface(building, IID_Ownership); 348 if (!cmpBuildingOwnership) 349 { 350 error("New Building " + building + " has no ownership."); 351 Engine.DestroyEntity(building); 352 return; 353 } 313 354 cmpBuildingOwnership.SetOwner(owner); 314 355 315 // ---------------------------------------------------------------------- 356 /* 357 Copy over the obstruction control group IDs from the foundation 358 entities. This is needed to ensure that when a foundation is completed 359 and replaced by a new entity, it remains in the same control group(s) 360 as any other foundation entities that may surround it. This is the 361 mechanism that is used to e.g. enable wall pieces to be built closely 362 together, ignoring their mutual obstruction shapes (since they would 363 otherwise be prevented from being built so closely together). If the 364 control groups are not copied over, the new entity will default to a 365 new control group containing only itself, and will hence block 366 construction of any surrounding foundations that it was previously in 367 the same control group with. 316 368 317 // Copy over the obstruction control group IDs from the foundation entities. This is needed to ensure that when a foundation318 // is completed and replaced by a new entity, it remains in the same control group(s) as any other foundation entities that319 // may surround it. This is the mechanism that is used to e.g. enable wall pieces to be built closely together, ignoring their320 // mutual obstruction shapes (since they would otherwise be prevented from being built so closely together). If the control321 // groups are not copied over, the new entity will default to a new control group containing only itself, and will hence block322 // construction of any surrounding foundations that it was previously in the same control group with.369 Note that this will result in the completed building entities having 370 control group IDs that equal entity IDs of old (and soon to be deleted) 371 foundation entities. This should not have any consequences, however, 372 since the control group IDs are only meant to be unique identifiers, 373 which is still true when reusing the old ones. 374 */ 323 375 324 // Note that this will result in the completed building entities having control group IDs that equal entity IDs of old (and soon325 // to be deleted) foundation entities. This should not have any consequences, however, since the control group IDs are only meant326 // to be unique identifiers, which is still true when reusing the old ones.327 328 var cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction);329 376 var cmpBuildingObstruction = Engine.QueryInterface(building, IID_Obstruction); 330 cmpBuildingObstruction.SetControlGroup(cmpObstruction.GetControlGroup()); 331 cmpBuildingObstruction.SetControlGroup2(cmpObstruction.GetControlGroup2()); 377 if (cmpObstruction && cmpBuildingObstruction) 378 { 379 cmpBuildingObstruction.SetControlGroup(cmpObstruction.GetControlGroup()); 380 cmpBuildingObstruction.SetControlGroup2(cmpObstruction.GetControlGroup2()); 381 } 332 382 333 // ----------------------------------------------------------------------334 335 383 var cmpPlayerStatisticsTracker = QueryOwnerInterface(this.entity, IID_StatisticsTracker); 336 384 if (cmpPlayerStatisticsTracker) 337 385 cmpPlayerStatisticsTracker.IncreaseConstructedBuildingsCounter(building); 338 386 339 var cmpHealth = Engine.QueryInterface(this.entity, IID_Health);340 387 var cmpBuildingHealth = Engine.QueryInterface(building, IID_Health); 341 cmpBuildingHealth.SetHitpoints(cmpHealth.GetHitpoints()); 388 if (cmpBuildingHealth) 389 cmpBuildingHealth.SetHitpoints(cmpHealth.GetHitpoints()); 342 390 343 391 PlaySound("constructed", building); 344 392 -
binaries/data/mods/public/simulation/components/tests/setup.js
44 44 for (var cid in g_Components[ent]) 45 45 { 46 46 var cmp = g_Components[ent][cid]; 47 if (cmp .Deinit)47 if (cmp && cmp.Deinit) 48 48 cmp.Deinit(); 49 49 } 50 50 … … 76 76 g_Components[ent][iid] = mock; 77 77 }; 78 78 79 global.DeleteMock = function(ent, iid) 80 { 81 if (!g_Components[ent]) 82 g_Components[ent] = {}; 83 delete g_Components[ent][iid]; 84 }; 85 79 86 global.ConstructComponent = function(ent, name, template) 80 87 { 81 88 var cmp = new g_ComponentTypes[name].ctor(); -
binaries/data/mods/public/simulation/components/tests/test_Foundation.js
1 Engine.LoadHelperScript("Player.js"); 2 Engine.LoadComponentScript("interfaces/Cost.js"); 3 Engine.LoadComponentScript("interfaces/Foundation.js"); 4 Engine.LoadComponentScript("interfaces/Health.js"); 5 Engine.LoadComponentScript("interfaces/RallyPoint.js"); 6 Engine.LoadComponentScript("interfaces/StatisticsTracker.js"); 7 Engine.LoadComponentScript("interfaces/TerritoryDecay.js"); 8 Engine.LoadComponentScript("interfaces/Trigger.js"); 9 Engine.LoadComponentScript("Foundation.js"); 10 11 AddMock(SYSTEM_ENTITY, IID_Trigger, { 12 "CallEvent": () => {}, 13 }); 14 15 let player = 1; 16 let playerEnt = 3; 17 18 AddMock(SYSTEM_ENTITY, IID_PlayerManager, { 19 "GetPlayerByID": () => playerEnt, 20 }); 21 22 AddMock(playerEnt, IID_StatisticsTracker, { 23 "IncreaseConstructedBuildingsCounter": ent => 24 { 25 TS_ASSERT_EQUALS(ent, newEnt); 26 }, 27 }); 28 29 let foundationEnt = 20; 30 let newEnt = 21; 31 let finalTemplate = "structures/athen_civil_centre.xml"; 32 let foundationHP = 1; 33 let maxHP = 100; 34 35 AddMock(foundationEnt, IID_Cost, { 36 "GetBuildTime": () => 50, 37 "GetResourceCosts": () => ({ "wood": 100 }), 38 }); 39 40 AddMock(foundationEnt, IID_Health, { 41 "GetHitpoints": () => foundationHP, 42 "GetMaxHitpoints": () => maxHP, 43 "Increase": hp => 44 { 45 foundationHP = Math.min(foundationHP + hp, maxHP); 46 cmpFoundation.OnHealthChanged(); 47 }, 48 }); 49 50 AddMock(foundationEnt, IID_Obstruction, { 51 "GetBlockMovementFlag": () => true, 52 "GetUnitCollisions": () => [], 53 "SetDisableBlockMovementPathfinding": () => {}, 54 }); 55 56 AddMock(foundationEnt, IID_Ownership, { 57 "GetOwner": () => player, 58 }); 59 60 AddMock(foundationEnt, IID_Position, { 61 "GetPosition2D": () => new Vector2D(1, 2), 62 "GetRotation": () => new Vector3D(1, 2, 3), 63 "IsInWorld": () => true, 64 }); 65 66 Engine.AddEntity = function(template) 67 { 68 AddMock(newEnt, IID_Position, { 69 "JumpTo": (x, y) => { TS_ASSERT_EQUALS(x, 1); TS_ASSERT_EQUALS(y, 2); }, 70 "SetYRotation": r => { TS_ASSERT_EQUALS(r, 2); }, 71 "SetXZRotation": (rx, rz) => 72 { 73 TS_ASSERT_EQUALS(rx, 1); 74 TS_ASSERT_EQUALS(rz, 3); 75 }, 76 }); 77 AddMock(newEnt, IID_Ownership, { 78 "SetOwner": owner => { TS_ASSERT_EQUALS(owner, player); }, 79 }); 80 return newEnt; 81 }; 82 83 let PlaySound = function(name, source) 84 { 85 TS_ASSERT_EQUALS(name, "constructed"); 86 TS_ASSERT_EQUALS(source, newEnt); 87 }; 88 Engine.RegisterGlobal("PlaySound", PlaySound); 89 Engine.RegisterGlobal("MT_EntityRenamed", "entityRenamed"); 90 91 let cmpFoundation = ConstructComponent(foundationEnt, "Foundation", {}); 92 93 // INITIALISE 94 cmpFoundation.InitialiseConstruction(player, finalTemplate); 95 96 TS_ASSERT_EQUALS(cmpFoundation.owner, player); 97 TS_ASSERT_EQUALS(cmpFoundation.finalTemplateName, finalTemplate); 98 TS_ASSERT_EQUALS(cmpFoundation.maxProgress, 0); 99 TS_ASSERT_EQUALS(cmpFoundation.initialised, true); 100 101 // BUILDER COUNT 102 TS_ASSERT_EQUALS(cmpFoundation.GetNumBuilders(), 0); 103 cmpFoundation.AddBuilder(10); 104 TS_ASSERT_EQUALS(cmpFoundation.GetNumBuilders(), 1); 105 TS_ASSERT_EQUALS(cmpFoundation.buildMultiplier, 1); 106 cmpFoundation.AddBuilder(11); 107 TS_ASSERT_EQUALS(cmpFoundation.GetNumBuilders(), 2); 108 TS_ASSERT_EQUALS(cmpFoundation.buildMultiplier, Math.pow(2, 0.7) / 2); 109 cmpFoundation.AddBuilder(11); 110 TS_ASSERT_EQUALS(cmpFoundation.GetNumBuilders(), 2); 111 TS_ASSERT_EQUALS(cmpFoundation.buildMultiplier, Math.pow(2, 0.7) / 2); 112 cmpFoundation.RemoveBuilder(11); 113 TS_ASSERT_EQUALS(cmpFoundation.GetNumBuilders(), 1); 114 TS_ASSERT_EQUALS(cmpFoundation.buildMultiplier, 1); 115 cmpFoundation.RemoveBuilder(11); 116 TS_ASSERT_EQUALS(cmpFoundation.GetNumBuilders(), 1); 117 TS_ASSERT_EQUALS(cmpFoundation.buildMultiplier, 1); 118 // with cmpVisual 119 AddMock(foundationEnt, IID_Visual, { 120 "SetVariable": (key, num) => 121 { 122 TS_ASSERT_EQUALS(key, "numbuilders"); 123 TS_ASSERT_EQUALS(num, 2); 124 }, 125 }); 126 cmpFoundation.AddBuilder(11); 127 DeleteMock(foundationEnt, IID_Visual, null); 128 cmpFoundation.RemoveBuilder(11); 129 130 // COMMIT FOUNDATION 131 TS_ASSERT_EQUALS(cmpFoundation.committed, false); 132 let work = 5; 133 cmpFoundation.Build(10, work); 134 TS_ASSERT_EQUALS(cmpFoundation.committed, true); 135 TS_ASSERT_EQUALS(foundationHP, 1 + work * cmpFoundation.GetBuildRate() * cmpFoundation.buildMultiplier); 136 TS_ASSERT_EQUALS(cmpFoundation.maxProgress, foundationHP / maxHP); 137 138 // FINISH CONSTRUCTION 139 cmpFoundation.Build(10, 1000); 140 TS_ASSERT_EQUALS(cmpFoundation.maxProgress, 1); 141 TS_ASSERT_EQUALS(foundationHP, maxHP); 142 143 144