Ticket #4121: test_foundation.diff
File test_foundation.diff, 13.8 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 let index = this.builders.indexOf(builderEnt); 123 if (index != -1) 124 return; 125 126 this.builders.push(builderEnt); 127 let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual); 128 if (cmpVisual) 129 SetVariable("numbuilders", this.builders.length); 130 131 this.SetBuildMultiplier(); 132 Engine.PostMessage(this.entity, MT_FoundationBuildersChanged, { "to": this.builders }); 126 133 }; 127 134 128 135 Foundation.prototype.RemoveBuilder = function(builderEnt) 129 136 { 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 } 137 let index = this.builders.indexOf(builderEnt); 138 if (index == -1) 139 return; 140 141 this.builders.splice(index ,1); 142 let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual); 143 if (cmpVisual) 144 SetVariable("numbuilders", this.builders.length); 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", {"foundation": this.entity, "template": this.finalTemplateName}); 214 223 215 224 // Switch foundation to scaffold variant 216 225 var cmpFoundationVisual = Engine.QueryInterface(this.entity, IID_Visual); … … 249 258 250 259 // Add an appropriate proportion of hitpoints 251 260 var cmpHealth = Engine.QueryInterface(this.entity, IID_Health); 261 if (!cmpHealth) 262 { 263 error("Foundation " + this.entity + " does not have a health component."); 264 return; 265 } 252 266 var maxHealth = cmpHealth.GetMaxHitpoints(); 253 267 var deltaHP = Math.max(work, Math.min(maxHealth, Math.floor(work * this.GetBuildRate() * this.buildMultiplier))); 254 268 if (deltaHP > 0) … … 274 288 cmpBuildingVisual.SetActorSeed(cmpVisual.GetActorSeed()); 275 289 276 290 var cmpPosition = Engine.QueryInterface(this.entity, IID_Position); 291 if (!cmpPosition || !cmpPosition.IsInWorld()) 292 { 293 error("Foundation " + this.entity + " does not have a position in-world."); 294 Engine.DestroyEntity(building); 295 return; 296 } 277 297 var cmpBuildingPosition = Engine.QueryInterface(building, IID_Position); 278 var pos = cmpPosition.GetPosition(); 279 cmpBuildingPosition.JumpTo(pos.x, pos.z); 298 if (!cmpBuildingPosition) 299 { 300 error("New building " + building + " has no position component."); 301 Engine.DestroyEntity(building); 302 return; 303 } 304 var pos = cmpPosition.GetPosition2D(); 305 cmpBuildingPosition.JumpTo(pos.x, pos.y); 280 306 var rot = cmpPosition.GetRotation(); 281 307 cmpBuildingPosition.SetYRotation(rot.y); 282 308 cmpBuildingPosition.SetXZRotation(rot.x, rot.z); … … 307 333 else 308 334 { 309 335 let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); 336 if (!cmpOwnership) 337 { 338 error("Foundation " + this.entity + " has no ownership."); 339 Engine.DestroyEntity(building); 340 return; 341 } 310 342 owner = cmpOwnership.GetOwner(); 311 343 } 312 344 var cmpBuildingOwnership = Engine.QueryInterface(building, IID_Ownership); 345 if (!cmpBuildingOwnership) 346 { 347 error("New Building " + building + " has no ownership."); 348 Engine.DestroyEntity(building); 349 return; 350 } 313 351 cmpBuildingOwnership.SetOwner(owner); 314 352 315 // ---------------------------------------------------------------------- 353 /* 354 Copy over the obstruction control group IDs from the foundation 355 entities. This is needed to ensure that when a foundation is completed 356 and replaced by a new entity, it remains in the same control group(s) 357 as any other foundation entities that may surround it. This is the 358 mechanism that is used to e.g. enable wall pieces to be built closely 359 together, ignoring their mutual obstruction shapes (since they would 360 otherwise be prevented from being built so closely together). If the 361 control groups are not copied over, the new entity will default to a 362 new control group containing only itself, and will hence block 363 construction of any surrounding foundations that it was previously in 364 the same control group with. 316 365 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.366 Note that this will result in the completed building entities having 367 control group IDs that equal entity IDs of old (and soon to be deleted) 368 foundation entities. This should not have any consequences, however, 369 since the control group IDs are only meant to be unique identifiers, 370 which is still true when reusing the old ones. 371 */ 323 372 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 373 var cmpBuildingObstruction = Engine.QueryInterface(building, IID_Obstruction); 330 cmpBuildingObstruction.SetControlGroup(cmpObstruction.GetControlGroup()); 331 cmpBuildingObstruction.SetControlGroup2(cmpObstruction.GetControlGroup2()); 374 if (cmpObstruction && cmpBuildingObstruction) 375 { 376 cmpBuildingObstruction.SetControlGroup(cmpObstruction.GetControlGroup()); 377 cmpBuildingObstruction.SetControlGroup2(cmpObstruction.GetControlGroup2()); 378 } 332 379 333 // ----------------------------------------------------------------------334 335 380 var cmpPlayerStatisticsTracker = QueryOwnerInterface(this.entity, IID_StatisticsTracker); 336 381 if (cmpPlayerStatisticsTracker) 337 382 cmpPlayerStatisticsTracker.IncreaseConstructedBuildingsCounter(building); 338 383 339 var cmpHealth = Engine.QueryInterface(this.entity, IID_Health);340 384 var cmpBuildingHealth = Engine.QueryInterface(building, IID_Health); 341 cmpBuildingHealth.SetHitpoints(cmpHealth.GetHitpoints()); 385 if (cmpBuildingHealth) 386 cmpBuildingHealth.SetHitpoints(cmpHealth.GetHitpoints()); 342 387 343 388 PlaySound("constructed", building); 344 389 -
binaries/data/mods/public/simulation/components/tests/test_Foundation.js
1 /*Engine.LoadHelperScript("ValueModification.js"); 2 Engine.LoadComponentScript("interfaces/Auras.js"); 3 Engine.LoadComponentScript("interfaces/AuraManager.js"); 4 Engine.LoadComponentScript("interfaces/TechnologyManager.js"); 5 Engine.LoadComponentScript("Auras.js"); 6 Engine.LoadComponentScript("AuraManager.js");*/ 7 8 Engine.LoadHelperScript("Player.js"); 9 Engine.LoadComponentScript("interfaces/Cost.js"); 10 Engine.LoadComponentScript("interfaces/Foundation.js"); 11 Engine.LoadComponentScript("interfaces/Health.js"); 12 Engine.LoadComponentScript("interfaces/RallyPoint.js"); 13 Engine.LoadComponentScript("interfaces/StatisticsTracker.js"); 14 Engine.LoadComponentScript("interfaces/TerritoryDecay.js"); 15 Engine.LoadComponentScript("interfaces/Trigger.js"); 16 Engine.LoadComponentScript("Foundation.js"); 17 18 AddMock(SYSTEM_ENTITY, IID_Trigger, { 19 "CallEvent": () => {}, 20 }); 21 22 let player = 1; 23 let playerEnt = 3; 24 25 AddMock(SYSTEM_ENTITY, IID_PlayerManager, { 26 "GetPlayerByID": () => playerEnt, 27 }); 28 29 AddMock(playerEnt, IID_StatisticsTracker, { 30 "IncreaseConstructedBuildingsCounter": b => { TS_ASSERT_EQUALS(b, newEnt); }, 31 }); 32 33 let foundationEnt = 20; 34 let newEnt = 21; 35 let finalTemplate = "structures/athen_civil_centre.xml"; 36 let foundationHP = 1; 37 let maxHP = 100; 38 39 AddMock(foundationEnt, IID_Cost, { 40 "GetBuildTime": () => 50, 41 "GetResourceCosts": () => ({ "wood": 100 }), 42 }); 43 44 AddMock(foundationEnt, IID_Health, { 45 "GetHitpoints": () => foundationHP, 46 "GetMaxHitpoints": () => maxHP, 47 "Increase": (a) => { foundationHP = Math.min(foundationHP + a, maxHP); cmpFoundation.OnHealthChanged(); }, 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) => { TS_ASSERT_EQUALS(rx, 1); TS_ASSERT_EQUALS(rz, 3); }, 72 }); 73 AddMock(newEnt, IID_Ownership, { 74 "SetOwner": owner => { TS_ASSERT_EQUALS(owner, player); }, 75 }); 76 return newEnt; 77 }; 78 79 let PlaySound = function(name, source) 80 { 81 TS_ASSERT_EQUALS(name, "constructed"); 82 TS_ASSERT_EQUALS(source, newEnt); 83 }; 84 Engine.RegisterGlobal("PlaySound", PlaySound); 85 Engine.RegisterGlobal("MT_EntityRenamed", "entityRenamed"); 86 87 let cmpFoundation = ConstructComponent(foundationEnt, "Foundation", {}); 88 89 // INITIALISE 90 cmpFoundation.InitialiseConstruction(player, finalTemplate); 91 92 TS_ASSERT_EQUALS(cmpFoundation.owner, player); 93 TS_ASSERT_EQUALS(cmpFoundation.finalTemplateName, finalTemplate); 94 TS_ASSERT_EQUALS(cmpFoundation.maxProgress, 0); 95 TS_ASSERT_EQUALS(cmpFoundation.initialised, true); 96 97 // BUILDER COUNT 98 TS_ASSERT_EQUALS(cmpFoundation.GetNumBuilders(), 0); 99 cmpFoundation.AddBuilder(10); 100 TS_ASSERT_EQUALS(cmpFoundation.GetNumBuilders(), 1); 101 TS_ASSERT_EQUALS(cmpFoundation.buildMultiplier, 1); 102 cmpFoundation.AddBuilder(11); 103 TS_ASSERT_EQUALS(cmpFoundation.GetNumBuilders(), 2); 104 TS_ASSERT_EQUALS(cmpFoundation.buildMultiplier, Math.pow(2, 0.7) / 2); 105 cmpFoundation.AddBuilder(11); 106 TS_ASSERT_EQUALS(cmpFoundation.GetNumBuilders(), 2); 107 TS_ASSERT_EQUALS(cmpFoundation.buildMultiplier, Math.pow(2, 0.7) / 2); 108 cmpFoundation.RemoveBuilder(11); 109 TS_ASSERT_EQUALS(cmpFoundation.GetNumBuilders(), 1); 110 TS_ASSERT_EQUALS(cmpFoundation.buildMultiplier, 1); 111 cmpFoundation.RemoveBuilder(11); 112 TS_ASSERT_EQUALS(cmpFoundation.GetNumBuilders(), 1); 113 TS_ASSERT_EQUALS(cmpFoundation.buildMultiplier, 1); 114 115 // COMMIT FOUNDATION 116 TS_ASSERT_EQUALS(cmpFoundation.committed, false); 117 let work = 5; 118 cmpFoundation.Build(10, work); 119 TS_ASSERT_EQUALS(cmpFoundation.committed, true); 120 TS_ASSERT_EQUALS(foundationHP, 1 + work * cmpFoundation.GetBuildRate() * cmpFoundation.buildMultiplier); 121 TS_ASSERT_EQUALS(cmpFoundation.maxProgress, foundationHP / maxHP); 122 123 // FINISH CONSTRUCTION 124 cmpFoundation.Build(10, 1000); 125 TS_ASSERT_EQUALS(cmpFoundation.maxProgress, 1); 126 TS_ASSERT_EQUALS(foundationHP, maxHP); 127 128 129