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