Ticket #3102: t3102_survival.diff
File t3102_survival.diff, 16.8 KB (added by , 8 years ago) |
---|
-
binaries/data/mods/public/maps/random/survivalofthefittest.js
202 202 var femaleLocation = getTIPIADBON([ix, iz], [mapSize / 2, mapSize / 2], [-3 , 3.5], 1, 3); 203 203 if (femaleLocation !== undefined) 204 204 { 205 var spawnPoints = ["C","D","E","F"]; 205 206 placeObject(femaleLocation[0], femaleLocation[1], "skirmish/units/default_support_female_citizen", id, playerAngle[i] + PI); 206 207 addToClass(floor(femaleLocation[0]), floor(femaleLocation[1]), clWomen); 208 placeObject(femaleLocation[0], femaleLocation[1], "special/trigger_point_" + spawnPoints[id-1], id, playerAngle[i] + PI); 209 // addToClass(floor(femaleLocation[0]), floor(femaleLocation[1]), clPlayer); 207 210 } 208 211 } 209 212 -
binaries/data/mods/public/maps/random/survivalofthefittest_triggers.js
1 var treasures = 2 [ 1 /* 2 * This is the Script containing all the Triggers for the "Survival of the Fittest" Map. 3 */ 4 5 var g_treasures = [ 3 6 "gaia/special_treasure_food_barrel", 4 7 "gaia/special_treasure_food_bin", 5 8 "gaia/special_treasure_food_crate", … … 10 13 "gaia/special_treasure_wood", 11 14 "gaia/special_treasure_wood" 12 15 ]; 13 var attackerEntityTemplates = 14 [ 16 17 var g_resourceLoopCount = 0; 18 19 var g_resourceLoopTime = 1.5; // Time in minutes when resources are distributed periodically 20 var g_resourceLoopOffset = 15; // Time in minutes after which resources are distributed periodically. 21 22 // These are the templates of the attacking units, sorted by civ 23 var g_attackerEntityTemplates = [ 15 24 [ 16 25 "units/athen_champion_infantry", 17 26 "units/athen_champion_marine", 18 27 "units/athen_champion_ranged", 19 "units/athen_ siege_lithobolos_packed",20 "units/athen_ siege_oxybeles_packed",28 "units/athen_mechanical_siege_lithobolos_packed", 29 "units/athen_mechanical_siege_oxybeles_packed" 21 30 ], 22 31 [ 23 32 "units/brit_champion_cavalry", 24 33 "units/brit_champion_infantry", 25 "units/brit_mechanical_siege_ram" ,34 "units/brit_mechanical_siege_ram" 26 35 ], 27 36 [ 28 37 "units/cart_champion_cavalry", 29 38 "units/cart_champion_elephant", 30 39 "units/cart_champion_infantry", 31 "units/cart_champion_pikeman" ,40 "units/cart_champion_pikeman" 32 41 ], 33 42 [ 34 43 "units/gaul_champion_cavalry", 35 44 "units/gaul_champion_fanatic", 36 45 "units/gaul_champion_infantry", 37 "units/gaul_mechanical_siege_ram" ,46 "units/gaul_mechanical_siege_ram" 38 47 ], 39 48 [ 40 49 "units/iber_champion_cavalry", 41 50 "units/iber_champion_infantry", 42 "units/iber_mechanical_siege_ram" ,51 "units/iber_mechanical_siege_ram" 43 52 ], 44 53 [ 45 54 "units/mace_champion_cavalry", … … 46 55 "units/mace_champion_infantry_a", 47 56 "units/mace_champion_infantry_e", 48 57 "units/mace_mechanical_siege_lithobolos_packed", 49 "units/mace_mechanical_siege_oxybeles_packed" ,58 "units/mace_mechanical_siege_oxybeles_packed" 50 59 ], 51 60 [ 52 61 "units/maur_champion_chariot", … … 53 62 "units/maur_champion_elephant", 54 63 "units/maur_champion_infantry", 55 64 "units/maur_champion_maiden", 56 "units/maur_champion_maiden_archer" ,65 "units/maur_champion_maiden_archer" 57 66 ], 58 67 [ 59 68 "units/pers_champion_cavalry", 60 "units/pers_champion_infantry",61 69 "units/pers_champion_elephant", 70 "units/pers_champion_infantry" 71 62 72 ], 63 73 [ 64 74 "units/ptol_champion_cavalry", 65 "units/ptol_champion_elephant" ,66 ] ,75 "units/ptol_champion_elephant" 76 ] 67 77 [ 68 78 "units/rome_champion_cavalry", 69 79 "units/rome_champion_infantry", 70 80 "units/rome_mechanical_siege_ballista_packed", 71 "units/rome_mechanical_siege_scorpio_packed" ,81 "units/rome_mechanical_siege_scorpio_packed" 72 82 ], 73 83 [ 74 84 "units/sele_champion_cavalry", … … 75 85 "units/sele_champion_chariot", 76 86 "units/sele_champion_elephant", 77 87 "units/sele_champion_infantry_pikeman", 78 "units/sele_champion_infantry_swordsman" ,88 "units/sele_champion_infantry_swordsman" 79 89 ], 80 90 [ 81 91 "units/spart_champion_infantry_pike", 82 92 "units/spart_champion_infantry_spear", 83 93 "units/spart_champion_infantry_sword", 84 "units/spart_mechanical_siege_ram" ,94 "units/spart_mechanical_siege_ram" 85 95 ], 86 96 ]; 87 97 … … 88 98 Trigger.prototype.StartAnEnemyWave = function() 89 99 { 90 100 let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); 91 let attackerEntities = attackerEntityTemplates[Math.floor(Math.random() *attackerEntityTemplates.length)];101 let attackerEntities = g_attackerEntityTemplates[Math.floor(Math.random() * g_attackerEntityTemplates.length)]; 92 102 // A soldier for each 2-3 minutes of the game. Should be waves of 20 soldiers after an hour 93 103 let nextTime = Math.round(120000 + Math.random() * 60000); 94 104 let attackerCount = Math.round(cmpTimer.GetTime() / nextTime / attackerEntities.length); 95 105 96 // spawn attackers106 // Spawn attackers 97 107 let attackers = []; 98 108 for (let attackerEntity of attackerEntities) 99 109 attackers.push(TriggerHelper.SpawnUnitsFromTriggerPoints("A", attackerEntity, attackerCount, 0)); … … 103 113 for (let origin in entityType) 104 114 { 105 115 let cmpPlayer = QueryOwnerInterface(+origin, IID_Player); 106 if ( cmpPlayer.GetState() != "active")116 if (!cmpPlayer || !cmpTrigger.playerCivicCenter[cmpPlayer.GetPlayerID()]) 107 117 continue; 108 118 109 let cmpPosition = Engine.QueryInterface( this.playerCivicCenter[cmpPlayer.GetPlayerID()], IID_Position);110 // this shouldn't happen if the player is still active119 let cmpPosition = Engine.QueryInterface(cmpTrigger.playerCivicCenter[cmpPlayer.GetPlayerID()], IID_Position); 120 // This shouldn't happen if the player is still active 111 121 if (!cmpPosition || !cmpPosition.IsInWorld) 112 122 continue; 113 123 114 // store the x and z coordinates in the command124 // Store the x and z coordinates in the command 115 125 let cmd = cmpPosition.GetPosition(); 116 126 cmd.type = "attack-walk"; 117 127 cmd.entities = entityType[origin]; 118 128 cmd.queued = true; 119 129 cmd.targetClasses = undefined; 120 // send the attack-walk command130 // Send the attack-walk command 121 131 ProcessCommand(0, cmd); 122 132 } 123 133 } … … 130 140 cmpTrigger.DoAfterDelay(nextTime, "StartAnEnemyWave", {}); // The next wave will come in 3 minutes 131 141 }; 132 142 143 /* 144 * Try to mark inactive players by tracking the number of entities they own. 145 * An unassigned Player will never produce a new entity. 146 * This will stop running once all players have been marked. 147 * So, when checking cmpTrigger.playerTrack[playerID].unassigned, you must always check if cmpTrigger.playerTrack[playerID] exists. 148 * 149 * For each player marked as Unassigned, less treasures will spawn. 150 */ 151 Trigger.prototype.MarkUnassignedPlayers = function(data) 152 { 153 if (!data || !data.playerID) 154 { 155 warn("no information provided to player tracker"); 156 return; 157 } 158 if (!cmpTrigger.playerTrack[data.playerID]) 159 cmpTrigger.playerTrack[data.playerID] = { 160 "eCount": data.eCount, 161 "unassigned": false, 162 "loop": 1 163 }; 164 else 165 { 166 ++cmpTrigger.playerTrack[data.playerID].loop; 167 168 let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 169 let playerEntities = cmpRangeManager.GetEntitiesByPlayer(data.playerID); 170 let eCount = playerEntities.length; 171 172 // The player must have produced something, so being for real. Exit the loop. 173 if (eCount > cmpTrigger.playerTrack[data.playerID].eCount) 174 return; 175 176 cmpTrigger.playerTrack[data.playerID].eCount = eCount; 177 178 if (cmpTrigger.playerTrack[data.playerID].loop > 5) 179 { 180 cmpTrigger.playerTrack[data.playerID].unassigned = true; // So long created nothing, must be placeholder 181 return; 182 } 183 } 184 cmpTrigger.DoAfterDelay(0.95 * 60 * 1000, "MarkUnassignedPlayers", { "playerID": data.playerID }); 185 }; 186 187 /* 188 * The main init function, called at the start 189 */ 133 190 Trigger.prototype.InitGame = function() 134 191 { 135 192 let numberOfPlayers = TriggerHelper.GetNumberOfPlayers(); 136 // Find all of the civic centers , disable some structures193 // Find all of the civic centers and females, disable some structures, setup activity watch 137 194 for (let i = 1; i < numberOfPlayers; ++i) 138 195 { 196 /*// At first, determine how many players are actually playing (humans) 197 let cmpPlayer = TriggerHelper.GetPlayerComponent(i); 198 warn(cmpPlayer.GetState());*/ 199 200 // Now handle the other stuff 139 201 let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 140 202 let playerEntities = cmpRangeManager.GetEntitiesByPlayer(i); // Get all of each player's entities 141 203 142 204 for (let entity of playerEntities) 205 { 143 206 if (TriggerHelper.EntityHasClass(entity, "CivilCentre")) 144 207 cmpTrigger.playerCivicCenter[i] = entity; 208 if (TriggerHelper.EntityHasClass(entity, "Female")) 209 cmpTrigger.females[i] = entity; 210 } 211 this.MarkUnassignedPlayers({ "eCount": playerEntities.length, "playerID": i }); 145 212 } 146 213 147 214 // Fix alliances … … 183 250 let triggerPoints = cmpTrigger.GetTriggerPoints(point); 184 251 for (let point of triggerPoints) 185 252 { 186 let template = treasures[Math.floor(Math.random() * treasures.length)] 187 TriggerHelper.SpawnUnits(point, template, 1, 0); 253 let template = g_treasures[Math.floor(Math.random() * g_treasures.length)]; 254 let cmpPosition = Engine.QueryInterface(point, IID_Position); 255 let ent = Engine.AddEntity(template); 256 257 let cmpEntOwnership = Engine.QueryInterface(ent, IID_Ownership); 258 let cmpEntPosition = Engine.QueryInterface(ent, IID_Position); 259 260 if (cmpEntOwnership) 261 cmpEntOwnership.SetOwner(0); 262 263 let xOffset = RandomInt(0,23); 264 let zOffset = RandomInt(0,23); 265 266 if (Math.random() >= 0.8 ) 267 { 268 xOffset += RandomInt(5,8); 269 zOffset += RandomInt(5,8); 270 } 271 272 if (Math.round(Math.random()) == 1 ) 273 xOffset = xOffset * -1; 274 275 if (Math.round(Math.random()) == 1 ) 276 zOffset = zOffset * -1; 277 278 cmpEntPosition.JumpTo(cmpPosition.GetPosition().x + xOffset, cmpPosition.GetPosition().z - zOffset); 188 279 } 189 cmpTrigger.DoAfterDelay(4*60*1000, "PlaceTreasures", {}); //Place more treasures after 4 minutes 280 let time = (3 + Math.random() * 10) * 60 * 1000; // Place more treasures after 2-4 minutes 281 cmpTrigger.DoAfterDelay(time, "PlaceTreasures", {}); 190 282 }; 191 283 284 /* 285 * This function starts the timer for the waves 286 */ 192 287 Trigger.prototype.InitializeEnemyWaves = function() 193 288 { 194 289 let time = (5 + Math.round(Math.random() * 10)) * 60 * 1000; … … 200 295 cmpTrigger.DoAfterDelay(time, "StartAnEnemyWave", {}); 201 296 }; 202 297 203 Trigger.prototype.DefeatPlayerOnceCCIsDestroyed= function(data)298 var TimeString = function(data) 204 299 { 300 if (!data.time) 301 { 302 warn("No time provided in TimeString"); 303 return ""; 304 } 305 let displayTime = data.time/60000; 306 let grammar = ""; 307 if (displayTime > 1) 308 grammar = "minutes"; 309 else if (data.time/1000 < 60) 310 { 311 grammar = "seconds"; 312 displayTime = data.time/1000; 313 } 314 else if (displayTime == 1) 315 grammar = "minute"; 316 else if (data.time/1000 == 1) 317 { 318 grammar = "second"; 319 displayTime = data.time/1000; 320 } 321 return displayTime + " " + grammar; 322 }; 323 324 /* 325 * NotifyPlayers: Keep the active players up to date about whats happening. This is called from inside the other functions. 326 * "data" mut be an object which contains string "situation", and any "argument" properties. "argument" is optional. 327 */ 328 Trigger.prototype.NotifyPlayers = function(data) 329 { 330 if (!data || !data.situation) 331 { 332 error("Nothing to talk :-("); 333 return; 334 } 335 336 let cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); 337 let players = []; 338 let numberOfPlayers = TriggerHelper.GetNumberOfPlayers(); 339 340 // Only deliver messages to active players (not e.g. dead) 341 for (let i = 1; i < numberOfPlayers; ++i) 342 if (QueryPlayerIDInterface(i).GetState() == "active") 343 players.push(i); 344 345 switch (data.situation) 346 { 347 case "beginGame": 348 cmpGUIInterface.PushNotification({ 349 "players": players, 350 "message": markForTranslation("Welcome to Survival of the Fittest"), 351 "translateMessage": true 352 }); 353 cmpGUIInterface.PushNotification({ 354 "players": players, 355 "message": markForTranslation("Collect treasures with your woman to prepare for the enemies."), 356 "translateMessage": true 357 }); 358 cmpGUIInterface.PushNotification({ 359 "players": players, 360 "message": markForTranslation("The first wave will start in " + TimeString({ "time": data.argument }) + "!"), 361 "translateMessage": true 362 }); 363 return; 364 365 case "beforeStart": 366 let timeRemaining = data.argument; 367 if (!timeRemaining) 368 return; 369 cmpGUIInterface.PushNotification({ 370 "players": players, 371 "message": markForTranslation("The first wave will start in " + TimeString({ "time": timeRemaining }) +", be prepared!"), 372 "translateMessage": true 373 }); 374 return; 375 376 case "treasuresPlaced": 377 cmpGUIInterface.PushNotification({ 378 "players": players, 379 "message": markForTranslation("New treasures have been placed!"), 380 "translateMessage": true 381 }); 382 return; 383 384 case "waveIncoming": 385 cmpGUIInterface.PushNotification({ 386 "players": players, 387 "message": markForTranslation("Rioting soldiers are attacking you! Defend your City!"), 388 "translateMessage": true 389 }); 390 return; 391 392 case "notifyRespawn": 393 if (!data.argument.time || !data.argument.playerIDs) 394 { 395 warn("NotifyPlayers doesnt know whom to address"); 396 return; 397 } 398 cmpGUIInterface.PushNotification({ 399 "players": data.argument.playerIDs, 400 "message": markForTranslation("Your treasure seeker has died! You will get a new one in " + TimeString({ "time": data.argument.time }) + "."), 401 "translateMessage": true 402 }); 403 return; 404 405 case "resourceLoopStarted": 406 cmpGUIInterface.PushNotification({ 407 "players": players, 408 "message": markForTranslation("From now on, you will receive some resources from time to time"), 409 "translateMessage": true 410 }); 411 return; 412 413 default: 414 warn("NotifyPlayers is panicking, some unknown gossip came in"); 415 return; 416 } 417 }; 418 419 Trigger.prototype.SpawnNewWoman = function(data) 420 { 421 if (!data.playerId || data.playerId < 1) 422 { 423 warn("No women for gaia"); 424 return; 425 } 426 427 if (!cmpTrigger.females[data.playerId]) 428 { 429 warn("This is not a treasure seeker: Player " + data.playerId); 430 return; 431 } 432 433 let spawnPoints = ["C","D","E","F"]; 434 if (TriggerHelper.GetOwner(cmpTrigger.females[data.playerId]) != -1) 435 { 436 warn("Female still alive, exiting"); 437 return; 438 } 439 440 let triggerPoints = cmpTrigger.GetTriggerPoints(spawnPoints[data.playerId-1]); 441 let newWoman = TriggerHelper.SpawnUnits(triggerPoints[0], data.template, 1, data.playerId); 442 cmpTrigger.females[data.playerId] = newWoman[0]; 443 }; 444 445 /* 446 * HandleSpecialEntities occurs when a unit dies, and checks for anything to be done in case its a treasure woman/civic center 447 */ 448 Trigger.prototype.HandleSpecialEntities = function(data) 449 { 205 450 // Defeat a player that has lost his civic center 206 if (data.entity == cmpTrigger.playerCivicCenter[data.from] )451 if (data.entity == cmpTrigger.playerCivicCenter[data.from] && data.to == -1) 207 452 { 208 453 TriggerHelper.DefeatPlayer(data.from); 209 454 … … 222 467 if (numPlayersStanding == 1) 223 468 TriggerHelper.SetPlayerWon(lastPlayerStanding); 224 469 } 470 471 if (cmpTrigger.females[data.from] && data.from >= 1 && data.entity == cmpTrigger.females[data.from] && data.to == -1) 472 { 473 let cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); 474 let template = cmpTemplateManager.GetCurrentTemplateName(data.entity); 475 let time = 15 * 1000; // Time after which women respawn 476 477 cmpTrigger.DoAfterDelay(time, "SpawnNewWoman", { 478 "playerId": data.from, 479 "template": template 480 }); 481 482 this.NotifyPlayers({ 483 "situation": "notifyRespawn", 484 "argument": { 485 "time": time, 486 "playerIDs": [data.from] 487 } 488 }); 489 } 225 490 }; 226 491 492 /* 493 * This function is constantly Looping and provides resources to all players. 494 * It starts after resourceLoopOffset 495 */ 496 Trigger.prototype.ResourceLoop = function(data) 497 { 498 ++g_resourceLoopCount; 499 500 if (g_resourceLoopCount == 1) 501 this.NotifyPlayers({ "situation": "resourceLoopStarted" }); 502 503 let numberOfPlayers = TriggerHelper.GetNumberOfPlayers(); 504 for (let i = 1; i < numberOfPlayers; ++i) 505 { 506 let cmpPlayer = QueryPlayerIDInterface(i); 507 cmpPlayer.AddResources({ 508 "wood": 100, 509 "stone": 100, 510 "metal": 100, 511 "food": 100 512 }); 513 } 514 cmpTrigger.DoAfterDelay(g_resourceLoopTime * 60 * 1000, "ResourceLoop" , {}); 515 }; 516 227 517 var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); 228 518 cmpTrigger.playerCivicCenter = {}; 519 cmpTrigger.females = {}; 520 cmpTrigger.playerTrack = {}; 521 229 522 cmpTrigger.DoAfterDelay(0, "InitGame", {}); 230 523 cmpTrigger.DoAfterDelay(1000, "InitializeEnemyWaves", {}); 524 cmpTrigger.DoAfterDelay(g_resourceLoopOffset * 60 * 1000, "ResourceLoop" , {}); 231 525 232 cmpTrigger.RegisterTrigger("OnOwnershipChanged", " DefeatPlayerOnceCCIsDestroyed", { "enabled": true });526 cmpTrigger.RegisterTrigger("OnOwnershipChanged", "HandleSpecialEntities", { "enabled": true });