1-- Name: Clash in Shangri-La (PVP) 2-- Description: Since its creation, the Shangri-La station was governed by a multi-ethnic consortium that assured the station's independence across the conflicts that shook the sector. 3--- 4--- However, the station's tranquility came to an abrupt end when most of the governing consortium's members were assassinated under a Exuari false flag operation. 5--- 6--- Now the station is in a state of civil war, with infighting breaking out between warring factions. 7--- 8--- Both the neighboring Human Navy and Kraylor are worried that the breakdown of order in Shangri-La could tilt the balance of power in their opponent's favor, and sent "peacekeepers" to shift the situation to their own advantage. 9--- 10--- The Human Navy's HNS Gallipoli and Kraylor's Crusader Naa'Tvek face off in an all-out battle for Shangri-La. 11-- Type: PvP 12 13--- Scenario 14-- @script scenario_81_pvp 15 16-- Global variables in this scenario 17 18-- Independent station 19local shangri_la 20 21-- Human Navy and Kraylor 22local gallipoli 23local crusader 24 25local shipyard_human 26local shipyard_kraylor 27 28local troops_human 29local troops_kraylor 30 31local respawn_human 32local respawn_kraylor 33 34local points_human 35local points_kraylor 36 37-- Timers 38local time 39local wave_timer 40local troop_timer 41 42--- Initialize scenario. 43function init() 44 troops_human = {} 45 troops_kraylor = {} 46 47 -- Stations 48 shangri_la = SpaceStation():setPosition(10000, 10000):setTemplate("Large Station"):setFaction("Independent"):setRotation(random(0, 360)):setCallSign("Shangri-La"):setCommsFunction(shangrilaComms) 49 50 do 51 local faction = "Human Navy" 52 shipyard_human = SpaceStation():setPosition(-7500, 15000):setTemplate("Small Station"):setFaction(faction):setRotation(random(0, 360)):setCallSign("Mobile Shipyard"):setCommsFunction(stationComms) 53 CpuShip():setTemplate("Phobos T3"):setFaction(faction):setPosition(-8000, 16500):orderDefendTarget(shipyard_human):setScannedByFaction(faction, true) 54 CpuShip():setTemplate("Phobos T3"):setFaction(faction):setPosition(-6000, 13500):orderDefendTarget(shipyard_human):setScannedByFaction(faction, true) 55 CpuShip():setTemplate("Phobos T3"):setFaction(faction):setPosition(-7000, 14000):orderDefendTarget(shipyard_human):setScannedByFaction(faction, true) 56 CpuShip():setTemplate("Nirvana R5"):setFaction(faction):setPosition(-8000, 14000):orderDefendTarget(shipyard_human):setScannedByFaction(faction, true) 57 end 58 59 do 60 local faction = "Kraylor" 61 shipyard_kraylor = SpaceStation():setPosition(27500, 5000):setTemplate("Small Station"):setFaction(faction):setRotation(random(0, 360)):setCallSign("Forward Command"):setCommsFunction(stationComms) 62 CpuShip():setTemplate("Phobos T3"):setFaction(faction):setPosition(29000, 5000):orderDefendTarget(shipyard_kraylor):setScannedByFaction(faction, true) 63 CpuShip():setTemplate("Phobos T3"):setFaction(faction):setPosition(25000, 5000):orderDefendTarget(shipyard_kraylor):setScannedByFaction(faction, true) 64 CpuShip():setTemplate("Phobos T3"):setFaction(faction):setPosition(27500, 6000):orderDefendTarget(shipyard_kraylor):setScannedByFaction(faction, true) 65 CpuShip():setTemplate("Nirvana R5"):setFaction(faction):setPosition(27000, 5000):orderDefendTarget(shipyard_kraylor):setScannedByFaction(faction, true) 66 end 67 68 -- Spawn players 69 gallipoli = PlayerSpaceship():setFaction("Human Navy"):setTemplate("Atlantis"):setPosition(-8500, 15000):setCallSign("HNS Gallipoli"):setScannedByFaction("Kraylor", false) 70 crusader = PlayerSpaceship():setFaction("Kraylor"):setTemplate("Atlantis"):setPosition(26500, 5000):setCallSign("Crusader Naa'Tvek"):setScannedByFaction("Human Navy", false) 71 72 -- Initialize timers 73 time = 0 74 wave_timer = 0 75 troop_timer = 0 76 respawn_human = 0 77 respawn_kraylor = 0 78 79 points_human = 0 80 points_kraylor = 0 81 82 -- Create terrain 83 create(Asteroid, 20, 5000, 10000, 10000, 10000) 84 create(VisualAsteroid, 10, 5000, 10000, 10000, 10000) 85 create(Mine, 10, 5000, 10000, 10000, 10000) 86 87 -- Brief the players 88 shipyard_human:sendCommsMessage( 89 gallipoli, 90 [[Captain, it seems that the Kraylor are moving to take the Shangri-La station in sector F5! 91 92Provide cover while our troop transports board the station to reclaim it. 93 94Good luck, and stay safe.]] 95 ) 96 97 shipyard_kraylor:sendCommsMessage( 98 crusader, 99 [[Greetings, Crusader. 100 101Your mission is to secure the Shangri-La station in sector F5. The feeble humans think it's theirs for the taking. 102 103Support our glorious soldiers by preventing the heretics from harming our transports, and cleanse all enemy opposition!]] 104 ) 105 106 -- Spawn the first wave 107 do 108 local faction = "Human Navy" 109 local transport = spawnTransport():setFaction(faction):setPosition(-7000, 15000):orderDock(shangri_la):setScannedByFaction(faction, true) 110 table.insert(troops_human, transport) 111 CpuShip():setTemplate("MT52 Hornet"):setFaction(faction):setPosition(-7000, 15500):orderDefendTarget(transport):setScannedByFaction(faction, true) 112 CpuShip():setTemplate("MT52 Hornet"):setFaction(faction):setPosition(-7000, 14500):orderDefendTarget(transport):setScannedByFaction(faction, true) 113 end 114 115 do 116 local faction = "Kraylor" 117 local transport = spawnTransport():setFaction(faction):setPosition(26500, 5000):orderDock(shangri_la):setScannedByFaction(faction, true) 118 table.insert(troops_kraylor, transport) 119 CpuShip():setTemplate("MT52 Hornet"):setFaction(faction):setPosition(26500, 5500):orderDefendTarget(transport):setScannedByFaction(faction, true) 120 CpuShip():setTemplate("MT52 Hornet"):setFaction(faction):setPosition(26500, 4500):orderDefendTarget(transport):setScannedByFaction(faction, true) 121 end 122end 123 124--- Compile and return status report. 125-- 126-- @treturn string the status report 127function getStatusReport() 128 return table.concat( 129 { 130 "Here's the latest news from the front.", 131 string.format("Human dominance: %d", points_human), 132 string.format("Kraylor dominance: %d", points_kraylor), 133 string.format("Time elapsed: %.0f", time) 134 }, 135 "\n" 136 ) 137end 138 139--- Comms with independent station _Shangri-La_. 140-- 141-- If players call Shangri-La, provide a status report 142function shangrilaComms() 143 setCommsMessage([[Your faction's militia commander picks up: 144 145What can we do for you, Captain?]]) 146 addCommsReply( 147 "Give us a status report.", 148 function() 149 setCommsMessage(getStatusReport()) 150 end 151 ) 152end 153 154--- Comms for station(s). 155-- 156-- If friendly players call a station, provide a status report and offer 157-- reinforcements at a reputation cost. 158function stationComms() 159 if comms_source:isFriendly(comms_target) then 160 if not comms_source:isDocked(comms_target) then 161 setCommsMessage([[A dispatcher responds: 162 163Greetings, Captain. If you want supplies, please dock with us.]]) 164 else 165 setCommsMessage([[A dispatcher responds: 166 167Greetings, Captain. What can we do for you?]]) 168 end 169 170 addCommsReply( 171 "I need a status report.", 172 function() 173 setCommsMessage(getStatusReport()) 174 end 175 ) 176 177 addCommsReply( 178 "Send in more troops. (100 reputation)", 179 function() 180 if not comms_source:takeReputationPoints(100) then 181 setCommsMessage("Not enough reputation.") 182 return 183 end 184 setCommsMessage("Aye, Captain. We've deployed a squad with fighter escort to support the assault on Shangri-La.") 185 if comms_target:getFaction() == "Human Navy" then 186 local transport = spawnTransport():setFaction("Human Navy"):setPosition(comms_target:getPosition()):orderDock(shangri_la):setScannedByFaction("Human Navy", true) 187 table.insert(troops_human, transport) 188 CpuShip():setTemplate("MT52 Hornet"):setFaction(comms_target:getFaction()):setPosition(comms_target:getPosition()):orderDefendTarget(transport):setScannedByFaction(comms_source:getFaction(), true) 189 elseif comms_target:getFaction() == "Kraylor" then 190 local transport = spawnTransport():setFaction("Kraylor"):setPosition(comms_target:getPosition()):orderDock(shangri_la):setScannedByFaction("Kraylor", true) 191 table.insert(troops_kraylor, transport) 192 CpuShip():setTemplate("MT52 Hornet"):setFaction(comms_target:getFaction()):setPosition(comms_target:getPosition()):orderDefendTarget(transport):setScannedByFaction(comms_source:getFaction(), true) 193 else 194 -- Can usually not happen. Do nothing. 195 end 196 end 197 ) 198 199 addCommsReply( 200 "We need some space-based firepower. (150 reputation)", 201 function() 202 if not comms_source:takeReputationPoints(150) then 203 setCommsMessage("Not enough reputation.") 204 return 205 end 206 setCommsMessage("Confirmed. We've dispatched a strike wing to support space superiority around Shangri-La.") 207 local strike_leader = CpuShip():setTemplate("Phobos T3"):setFaction(comms_target:getFaction()):setPosition(comms_target:getPosition()):orderDefendTarget(shangri_la):setScannedByFaction(comms_source:getFaction(), true) 208 CpuShip():setTemplate("MU52 Hornet"):setFaction(comms_target:getFaction()):setPosition(comms_target:getPosition()):orderFlyFormation(strike_leader, -1000, 0):setScannedByFaction(comms_source:getFaction(), true) 209 CpuShip():setTemplate("MU52 Hornet"):setFaction(comms_target:getFaction()):setPosition(comms_target:getPosition()):orderFlyFormation(strike_leader, 1000, 0):setScannedByFaction(comms_source:getFaction(), true) 210 end 211 ) 212 213 if comms_source:isDocked(comms_target) then 214 addCommsReply("We need supplies.", supplyDialogue) 215 end 216 else 217 setCommsMessage("We'll bring your destruction!") 218 end 219end 220 221--- Add reply. 222function addCommsReplySupply(args) 223 local price = args.price 224 local missile_type = args.missile_type 225 addCommsReply( 226 string.format(args.request .. " (%d rep each)", price), 227 function() 228 if not comms_source:isDocked(comms_target) then 229 setCommsMessage("You need to stay docked for that action.") 230 return 231 end 232 if not comms_source:takeReputationPoints(price * (comms_source:getWeaponStorageMax(missile_type) - comms_source:getWeaponStorage(missile_type))) then 233 setCommsMessage("Not enough reputation.") 234 return 235 end 236 if comms_source:getWeaponStorage(missile_type) >= comms_source:getWeaponStorageMax(missile_type) then 237 setCommsMessage(args.reply_full) 238 addCommsReply("Back", supplyDialogue) 239 else 240 comms_source:setWeaponStorage(missile_type, comms_source:getWeaponStorageMax(missile_type)) 241 setCommsMessage(args.reply_filled) 242 addCommsReply("Back", supplyDialogue) 243 end 244 end 245 ) 246end 247 248--- Comms supplyDialogue. 249function supplyDialogue() 250 setCommsMessage("What supplies do you need?") 251 252 addCommsReplySupply { 253 missile_type = "Homing", 254 price = 2, 255 request = "Do you have spare homing missiles for us?", 256 reply_full = "Sorry, Captain, but you are fully stocked with homing missiles.", 257 reply_filled = "We've replenished your homing missile supply." 258 } 259 260 addCommsReplySupply { 261 missile_type = "Mine", 262 price = 2, 263 request = "Please re-stock our mines.", 264 reply_full = "Captain, you already have all the mines you can fit in that ship.", 265 reply_filled = "These mines are yours." 266 } 267 268 addCommsReplySupply { 269 missile_type = "Nuke", 270 price = 15, 271 request = "Can you supply us with some nukes?", 272 reply_full = "Your nukes are already charged and primed for destruction.", 273 reply_filled = "You are fully loaded and ready to explode things." 274 } 275 276 addCommsReplySupply { 277 missile_type = "EMP", 278 price = 10, 279 request = "Please re-stock our EMP missiles.", 280 reply_full = "All storage for EMP missiles is already full, Captain.", 281 reply_filled = "We've recalibrated the electronics and fitted you with all the EMP missiles you can carry." 282 } 283 284 addCommsReplySupply { 285 missile_type = "HVLI", 286 price = 2, 287 request = "Can you restock us with HVLI?", 288 reply_full = "Sorry, Captain, but you are fully stocked with HVLIs.", 289 reply_filled = "We've replenished your HVLI supply." 290 } 291 292 addCommsReply("Back to main menu", stationComms) 293end 294 295--- Update. 296function update(delta) 297 -- Increment timers 298 time = time + delta 299 wave_timer = wave_timer + delta 300 troop_timer = troop_timer + delta 301 302 -- If the Gallipoli is destroyed ... 303 if (not gallipoli:isValid()) then 304 if respawn_human > 20 then 305 -- ... and 20 seconds have passed, spawn the Heinlein. 306 gallipoli = PlayerSpaceship():setFaction("Human Navy"):setTemplate("Atlantis"):setPosition(-8500, 15000):setCallSign("HNS Heinlein"):setScannedByFaction("Kraylor", false) 307 else 308 -- Otherwise, increment the respawn timer. 309 respawn_human = respawn_human + delta 310 end 311 end 312 313 -- Ditto for the Crusader. 314 if (not crusader:isValid()) then 315 if respawn_kraylor > 20 then 316 crusader = PlayerSpaceship():setFaction("Kraylor"):setTemplate("Atlantis"):setPosition(19000, -14500):setCallSign("Crusader Elak'raan"):setScannedByFaction("Human Navy", false) 317 else 318 respawn_kraylor = respawn_kraylor + delta 319 end 320 end 321 322 -- Increment reputation for both sides. 323 gallipoli:addReputationPoints(delta * 0.3) 324 crusader:addReputationPoints(delta * 0.3) 325 326 -- If a faction has no station or flagship, it loses. 327 -- If a faction scores 50 points, it wins. 328 if ((not gallipoli:isValid()) and (not shipyard_human:isValid())) or points_kraylor > 50 then 329 victory("Kraylor") 330 end 331 332 if ((not crusader:isValid()) and (not shipyard_kraylor:isValid())) or points_human > 50 then 333 victory("Human Navy") 334 end 335 336 -- If either flagship is destroyed, its opponent gains a reputation bonus, and 337 -- its opponent's faction gains victory points. 338 if (not gallipoli:isValid()) then 339 shipyard_kraylor:sendCommsMessage( 340 crusader, 341 [[Well done, Crusader! 342 343The pathetic Human flagship has been disabled. Go for the victory!]] 344 ) 345 crusader:addReputationPoints(50) 346 points_kraylor = points_kraylor + 5 347 respawn_human = 0 348 end 349 350 if (not crusader:isValid()) then 351 shipyard_human:sendCommsMessage( 352 gallipoli, 353 [[Good job, Captain! 354 355With the Kraylor flagship out of the way, we can land the final blow!]] 356 ) 357 gallipoli:addReputationPoints(50) 358 points_human = points_human + 5 359 respawn_kraylor = 0 360 end 361 362 -- Every 150 seconds, spawn a troop transport and 2 fighters as escorts for 363 -- each faction. 364 if wave_timer > 150 and (shipyard_human:isValid()) then 365 do 366 local faction = "Human Navy" 367 local line = random(0, 20) * 500 368 local transport = spawnTransport():setFaction(faction):setPosition(-7000, 5000 + line):orderDock(shangri_la):setScannedByFaction(faction, true) 369 table.insert(troops_human, transport) 370 CpuShip():setTemplate("MT52 Hornet"):setFaction(faction):setPosition(-7000, 5500 + line):orderDefendTarget(transport):setScannedByFaction(faction, true) 371 CpuShip():setTemplate("MT52 Hornet"):setFaction(faction):setPosition(-7000, 4500 + line):orderDefendTarget(transport):setScannedByFaction(faction, true) 372 end 373 374 do 375 local faction = "Kraylor" 376 local line = random(0, 20) * 500 377 local transport = spawnTransport():setFaction(faction):setPosition(27000, -5000 + line):orderDock(shangri_la):setScannedByFaction(faction, true) 378 table.insert(troops_kraylor, transport) 379 CpuShip():setTemplate("MT52 Hornet"):setFaction(faction):setPosition(27000, -5500 + line):orderDefendTarget(transport):setScannedByFaction(faction, true) 380 CpuShip():setTemplate("MT52 Hornet"):setFaction(faction):setPosition(27000, -4500 + line):orderDefendTarget(transport):setScannedByFaction(faction, true) 381 end 382 383 wave_timer = 0 384 end 385 386 -- Count transports. Every 10 seconds, award 1 point per transport docked 387 -- with Shangri-La. 388 if troop_timer > 10 then 389 for _, transport in ipairs(troops_kraylor) do 390 if transport:isValid() and transport:isDocked(shangri_la) then 391 points_kraylor = points_kraylor + 1 392 end 393 end 394 395 for _, transport in ipairs(troops_human) do 396 if transport:isValid() and transport:isDocked(shangri_la) then 397 points_human = points_human + 1 398 end 399 end 400 401 troop_timer = 0 402 end 403 404 -- If Shangri-La is destroyed, nobody wins. 405 if (not shangri_la:isValid()) then 406 victory("Independents") 407 end 408end 409 410--- Spawn a troop transport. 411-- 412-- @treturn CpuShip 413function spawnTransport() 414 local ship = CpuShip():setTemplate("Personnel Freighter 2") 415 ship:setHullMax(100):setHull(100) 416 ship:setShieldsMax(50, 50):setShields(50, 50) 417 ship:setImpulseMaxSpeed(100):setRotationMaxSpeed(10) 418 return ship 419end 420 421--[[ Distribute a `number` of `object_type` objects at a random distance 422 between `dist_min` and `dist_max` around the coordinates `cx, cy`. ]] -- 423function create(object_type, number, dist_min, dist_max, cx, cy) 424 for _ = 1, number do 425 local phi = random(0, 2 * math.pi) 426 local distance = random(dist_min, dist_max) 427 local x = cx + math.cos(phi) * distance 428 local y = cy + math.sin(phi) * distance 429 object_type():setPosition(x, y) 430 end 431end 432