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