mirror of
https://github.com/iTracerFacer/DCS_MissionDev.git
synced 2025-12-03 04:14:46 +00:00
2493 lines
89 KiB
Lua
2493 lines
89 KiB
Lua
-- ============================================================================
|
|
-- MOOSE TANKER MANAGEMENT SYSTEM
|
|
-- Comprehensive tanker lifecycle management with auto-respawn, fuel monitoring,
|
|
-- TACAN/frequency announcements, and menu controls
|
|
-- ============================================================================
|
|
|
|
-- ============================================================================
|
|
-- USER CONFIGURATION
|
|
-- ============================================================================
|
|
|
|
-- Tanker Configuration
|
|
local TANKER_CONFIG = {
|
|
KC135 = {
|
|
groupName = "TANKER 135",
|
|
unitName = "TANKER 135-1",
|
|
displayName = "TANKER KC-135",
|
|
aircraftType = "KC-135", -- DCS aircraft type name
|
|
livery = nil, -- nil for default, or livery_id string
|
|
callsign = "SHELL", -- Map marker prefix for custom routes
|
|
tacan = "50X", -- Set to match ME or nil if none
|
|
frequency = "251.000", -- Set to match ME or nil if none
|
|
respawnDelay = 180, -- seconds before auto-respawn after destruction
|
|
emergencyRespawnDelay = 60, -- Emergency spawn delay
|
|
fuelWarningPercent = 25, -- Warn when fuel drops below this %
|
|
fuelBingoPercent = 15, -- RTB fuel level
|
|
defaultAltitude = 22000, -- Default altitude in feet (FL220)
|
|
defaultSpeed = 330, -- Default speed in knots
|
|
},
|
|
KC135_MPRS = {
|
|
groupName = "TANKER 135 MPRS",
|
|
unitName = "TANKER 135 MPRS-1",
|
|
displayName = "TANKER KC-135 MPRS",
|
|
aircraftType = "KC135MPRS", -- DCS aircraft type name
|
|
livery = nil,
|
|
callsign = "ARCO", -- Map marker prefix for custom routes
|
|
tacan = "51X",
|
|
frequency = "252.000",
|
|
respawnDelay = 180,
|
|
emergencyRespawnDelay = 60,
|
|
fuelWarningPercent = 25,
|
|
fuelBingoPercent = 15,
|
|
defaultAltitude = 22000,
|
|
defaultSpeed = 330,
|
|
}
|
|
}
|
|
|
|
-- Custom Route Configuration
|
|
local ROUTE_CONFIG = {
|
|
minWaypoints = 2, -- Minimum waypoints required
|
|
maxWaypoints = 10, -- Maximum waypoints allowed
|
|
deleteMarkersAfterUse = true, -- Delete markers after route creation
|
|
waypointPrefix = { -- Recognized marker prefixes
|
|
SHELL = "KC135", -- SHELL1, SHELL2, etc. → KC-135
|
|
ARCO = "KC135_MPRS", -- ARCO1, ARCO2, etc. → KC-135 MPRS
|
|
}
|
|
}
|
|
|
|
-- Monitoring Configuration
|
|
local FUEL_CHECK_INTERVAL = 60 -- Check fuel every 60 seconds
|
|
local DAMAGE_RTB_THRESHOLD = 50 -- RTB if hull damage exceeds this %
|
|
|
|
-- Default Spawn Location (for non-custom route spawns)
|
|
-- Note: Using lat/lon with SetAltitude to ensure proper altitude MSL
|
|
local DEFAULT_SPAWN_COORD = COORDINATE:NewFromLLDD(34.564, 69.212):SetAltitude(22000 * 0.3048, true) -- Kabul area, FL220
|
|
|
|
-- ============================================================================
|
|
-- GLOBAL STATE TRACKING
|
|
-- ============================================================================
|
|
|
|
TANKER_STATE = {
|
|
KC135 = {
|
|
active = false,
|
|
group = nil,
|
|
fuelWarned = false,
|
|
bingoWarned = false,
|
|
respawnScheduler = nil,
|
|
fuelMonitor = nil,
|
|
},
|
|
KC135_MPRS = {
|
|
active = false,
|
|
group = nil,
|
|
fuelWarned = false,
|
|
bingoWarned = false,
|
|
respawnScheduler = nil,
|
|
fuelMonitor = nil,
|
|
}
|
|
}
|
|
|
|
-- ============================================================================
|
|
-- MENU REFERENCES (for enable/disable)
|
|
-- ============================================================================
|
|
|
|
local MENU_TANKER_ROOT = nil
|
|
local MENU_KC135_LAUNCH = nil
|
|
local MENU_KC135_MPRS_LAUNCH = nil
|
|
|
|
-- ============================================================================
|
|
-- MESSAGE POOLS FOR VARIETY
|
|
-- Randomized messages provide immersive variety across tanker operations.
|
|
-- Each category contains 100 variations selected randomly via GetRandomMessage()
|
|
-- ============================================================================
|
|
|
|
local TANKER_MESSAGES = {
|
|
-- Spawn Confirmation (success)
|
|
SPAWN_SUCCESS = {
|
|
"%s is airborne and ready for refueling operations.",
|
|
"%s has launched and is standing by for fuel.",
|
|
"%s is now on station and ready to pump gas.",
|
|
"%s has departed and is available for refueling.",
|
|
"%s is up and ready to service aircraft.",
|
|
"%s is airborne. Refueling services now available.",
|
|
"%s has checked in on station.",
|
|
"%s is overhead and ready for business.",
|
|
"%s is now available for aerial refueling.",
|
|
"%s has arrived on station. Ready to refuel.",
|
|
"%s is up! Time to get your drink on.",
|
|
"%s has joined the party. Bring your cups!",
|
|
"%s reporting. The bar is now open.",
|
|
"%s is flying. Get in line for your juice.",
|
|
"%s on station. Don't be shy, we got plenty.",
|
|
"%s airborne. Unlike Mo's last attempt at flying.",
|
|
"%s has successfully launched. No thanks to Mo.",
|
|
"%s is ready. Mo said he could do this but we know better.",
|
|
"%s in position. Fuel truck of the sky is open for business!",
|
|
"%s has arrived fashionably late but ready to pump.",
|
|
"%s checking in. Your gas station with wings is here.",
|
|
"%s is up there doing tanker things.",
|
|
"%s launched without hitting anything. Good start!",
|
|
"%s airborne and hasn't broken anything yet.",
|
|
"%s is ready to make it rain... JP-8.",
|
|
"%s in the pattern. Come get some dinosaur juice!",
|
|
"%s reporting for duty. Time to feed some thirsty birds.",
|
|
"%s has spawned successfully. Mo's jealous.",
|
|
"%s is flying high and ready to share the wealth.",
|
|
"%s on station. Dispensary is OPEN.",
|
|
"%s has graced you with its presence. You're welcome.",
|
|
"%s is here to save your ass from flameout.",
|
|
"%s launched. The sky gas station is open 24/7.",
|
|
"%s airborne. Better than Mo's last tanker spawn attempt.",
|
|
"%s ready to refuel. Unlike your love life, this actually works.",
|
|
"%s has arrived to keep you from embarrassing yourself.",
|
|
"%s on station and totally not judging your fuel planning.",
|
|
"%s is up. Try not to break the boom this time.",
|
|
"%s launched successfully. Mo couldn't get his off the ground.",
|
|
"%s airborne. Your aerial bartender has arrived!",
|
|
"%s ready for action. The juice is loose!",
|
|
"%s has spawned. Time to get wet... with fuel.",
|
|
"%s on station. We promise not to tell anyone you needed us.",
|
|
"%s reporting. Because someone forgot to fuel before takeoff.",
|
|
"%s is here! The flying fuel truck has arrived!",
|
|
"%s airborne and ready to fill your tanks. That's what she said.",
|
|
"%s launched. Even Mo could refuel from this... maybe.",
|
|
"%s on station. Your poor planning is our opportunity!",
|
|
"%s has arrived. The aerial milk truck is ready.",
|
|
"%s ready to pump. Get your minds out of the gutter.",
|
|
"%s airborne because you can't manage fuel apparently.",
|
|
"%s is up there waiting. Don't keep us hovering forever.",
|
|
"%s has joined the fight. By 'fight' we mean 'hovering lazily.'",
|
|
"%s on station. Premium unleaded is on tap!",
|
|
"%s launched and looking sexy up here.",
|
|
"%s ready to refuel. Try not to scratch the paint this time.",
|
|
"%s has arrived. Mo said this was impossible but here we are.",
|
|
"%s airborne. The sky's full service station is open!",
|
|
"%s on station ready to save your bacon.",
|
|
"%s has launched into the wild blue yonder!",
|
|
"%s reporting for gas pumping duty.",
|
|
"%s is up and Mo isn't. Winner: us.",
|
|
"%s airborne. Fuel flows like wine at a wedding!",
|
|
"%s on station. Unlike Mo, we actually showed up.",
|
|
"%s ready to top you off. No phrasing.",
|
|
"%s has successfully taken off. Mo's still taxiing.",
|
|
"%s airborne and operational. The real MVP.",
|
|
"%s on station doing the lord's work.",
|
|
"%s has arrived to prevent your walk of shame.",
|
|
"%s ready for refueling ops. Try to connect this time.",
|
|
"%s launched because someone has to be the adult here.",
|
|
"%s airborne. Probably more reliable than your ex.",
|
|
"%s on station ready to give you the good stuff.",
|
|
"%s has spawned. Mo's tanker is still in the hangar.",
|
|
"%s reporting. Your airborne gas station awaits!",
|
|
"%s is up! Time for some hot refueling action.",
|
|
"%s airborne and Mo's not invited to this party.",
|
|
"%s on station. We have fuel, you have need. Let's dance.",
|
|
"%s ready to dispense freedom molecules!",
|
|
"%s has arrived to fix your fuel management issues.",
|
|
"%s launched. The flying gas can is ready for customers.",
|
|
"%s airborne because apparently nobody can calculate bingo.",
|
|
"%s on station. Come get your fix!",
|
|
"%s ready to pump premium into your thirsty bird.",
|
|
"%s has spawned successfully. Suck it, Mo.",
|
|
"%s reporting. Your aerial enabler is on station.",
|
|
"%s is up there waiting like a patient parent.",
|
|
"%s airborne and ready to make your fuel gauge happy.",
|
|
"%s on station. Mo said we couldn't do it. We did it.",
|
|
"%s launched with more grace than Mo's last landing.",
|
|
"%s ready for business. The boom is ready to boom.",
|
|
"%s has arrived fashionably and ready to serve.",
|
|
"%s airborne. Your fuel problems are about to be solved!",
|
|
"%s on station doing God's work up here.",
|
|
"%s ready to refuel. We got the good stuff.",
|
|
"%s has spawned. Time to feed the hungry jets!",
|
|
"%s reporting for duty with full tanks!",
|
|
"%s launched successfully without Mo's help, thank God.",
|
|
"%s airborne. The flying filling station is OPEN!",
|
|
},
|
|
|
|
-- Already Active Warning
|
|
ALREADY_ACTIVE = {
|
|
"%s is already airborne!",
|
|
"%s is currently active.",
|
|
"%s is already on station.",
|
|
"%s is already flying. Check status for details.",
|
|
"%s is already up - can't spawn another.",
|
|
"%s is currently operating.",
|
|
"Cannot spawn - %s already active.",
|
|
"%s is already out there!",
|
|
"%s already flying. One at a time, please.",
|
|
"%s is already working the pattern.",
|
|
"%s is already up there, genius.",
|
|
"Dude, %s is ALREADY flying. Pay attention.",
|
|
"%s is currently active. Are you even looking?",
|
|
"Hey Einstein, %s is already airborne!",
|
|
"%s is up there right now. Use your eyes.",
|
|
"What part of '%s is active' don't you understand?",
|
|
"%s is already flying. Did Mo program this button?",
|
|
"Seriously? %s is already up. Check your radar.",
|
|
"%s is currently operational. Nice try though.",
|
|
"Negative. %s is already in the air.",
|
|
"%s is already active. Mo would have known that.",
|
|
"Can't spawn two, buttercup. %s is already flying.",
|
|
"%s is already out there doing tanker things.",
|
|
"Nice try. %s is already airborne, hotshot.",
|
|
"%s is currently flying. One's enough.",
|
|
"Hold your horses! %s is already active.",
|
|
"%s is already up. We're not running a bus service here.",
|
|
"Bruh. %s is already flying.",
|
|
"%s is currently active. Maybe learn to read?",
|
|
"Negative ghostrider. %s is already up.",
|
|
"%s is already airborne. Unlike your awareness.",
|
|
"Um, %s is ALREADY flying. Hello?",
|
|
"%s is currently on station. Wake up.",
|
|
"You can't spawn %s twice. Physics doesn't work that way.",
|
|
"%s is already active. Not sure what you expected.",
|
|
"Denied! %s is already in the pattern.",
|
|
"%s is currently flying around. Look outside.",
|
|
"News flash: %s is already airborne!",
|
|
"%s is already up there. Mo makes better decisions than this.",
|
|
"Request denied. %s is already active, chief.",
|
|
"%s is currently operational. Check your instruments.",
|
|
"Already got one! %s is flying right now.",
|
|
"%s is already airborne. Reading is fundamental.",
|
|
"Uh, no. %s is currently active.",
|
|
"%s is already out there. Situational awareness: zero.",
|
|
"Can't spawn %s again. Not a video game, buddy.",
|
|
"%s is currently flying. We only get one.",
|
|
"That's a negative. %s is already up.",
|
|
"%s is already on station. Did you even check?",
|
|
"Seriously? %s has been flying for 20 minutes.",
|
|
"%s is already active. This isn't rocket science.",
|
|
"Request rejected. %s is currently airborne.",
|
|
"%s is already up there pumping gas. Pay attention!",
|
|
"Nope. %s is already flying. Check the status board.",
|
|
"%s is currently active. Even Mo knew this.",
|
|
"Cannot comply. %s is already operational.",
|
|
"%s is already airborne. Try the status menu next time.",
|
|
"That's a no-go. %s is currently flying.",
|
|
"%s is already up. Did you think we had two?",
|
|
"Denied. %s is already on station doing its thing.",
|
|
"%s is currently active. Surprised you didn't notice.",
|
|
"Can't do it. %s is already flying around up there.",
|
|
"%s is already operational. One tanker at a time, pal.",
|
|
"Negative. %s has been active for a while now.",
|
|
"%s is already up there. Spawn button isn't a toy.",
|
|
"Request denied. %s is currently on station.",
|
|
"%s is already flying. Not cloning aircraft today.",
|
|
"Can't spawn another. %s is already airborne.",
|
|
"%s is currently active. Stop button mashing.",
|
|
"That's not happening. %s is already up.",
|
|
"%s is already operational. One's all you get.",
|
|
"No can do. %s is already in the pattern.",
|
|
"%s is currently flying. Check before clicking, maybe?",
|
|
"Request rejected. %s is already on duty.",
|
|
"%s is already airborne. Unlike your attention span.",
|
|
"Nope! %s is currently active and doing fine.",
|
|
"%s is already up there. Stop spamming the spawn button.",
|
|
"Cannot spawn duplicate. %s is already flying.",
|
|
"%s is currently operational. Mo's spawn would work better.",
|
|
"Denied! %s is already on station, genius.",
|
|
"%s is already flying. Check your tanker status!",
|
|
"That's a negative. %s is currently active.",
|
|
"%s is already airborne. One tanker per customer.",
|
|
"Can't do that. %s is already up and working.",
|
|
"%s is currently on station. Read the room.",
|
|
"Request denied. %s is already operational, chief.",
|
|
"%s is already active. Try paying attention.",
|
|
"No dice. %s is already flying the pattern.",
|
|
"%s is currently airborne. Spawn limit: 1.",
|
|
"Negative. %s is already up there doing tanker stuff.",
|
|
"%s is already active. Maybe check the status screen?",
|
|
"Can't spawn %s again. We're not made of tankers here.",
|
|
"%s is currently flying. One at a time, hotshot.",
|
|
"Request rejected. %s is already on station.",
|
|
"%s is already operational. Even Mo knows you only get one.",
|
|
"That's not possible. %s is currently airborne.",
|
|
"%s is already up there. Better situational awareness needed.",
|
|
"Denied. %s is currently active and wondering why you asked.",
|
|
"%s is already flying. The spawn button isn't for spam.",
|
|
"Cannot comply. %s is already operational, Einstein.",
|
|
},
|
|
|
|
-- Spawn Failure
|
|
SPAWN_FAILURE = {
|
|
"Failed to spawn %s!",
|
|
"Unable to launch %s. Try again.",
|
|
"%s spawn aborted!",
|
|
"Cannot spawn %s at this time.",
|
|
"%s failed to launch!",
|
|
"Error spawning %s. Contact support.",
|
|
"%s launch unsuccessful.",
|
|
"Unable to activate %s. Retry required.",
|
|
"%s spawn failed. Check logs.",
|
|
"Launch failure for %s!",
|
|
"%s spawn went sideways. Oops.",
|
|
"Well that didn't work. %s failed to spawn.",
|
|
"%s couldn't get off the ground. Awkward.",
|
|
"Houston, we have a problem. %s didn't spawn.",
|
|
"%s spawn failed harder than Mo's last landing.",
|
|
"Oof. %s spawn went to hell.",
|
|
"%s launch aborted. This is embarrassing.",
|
|
"Yeah, %s didn't spawn. Our bad.",
|
|
"Spawn failed for %s. Not our finest moment.",
|
|
"%s couldn't launch. Try again, genius.",
|
|
"That's a big negative on %s spawn.",
|
|
"%s failed to spawn. Did Mo write this code?",
|
|
"Error: %s spawn went boom. The bad kind.",
|
|
"%s launch unsuccessful. Better luck next time.",
|
|
"Spawn failed. %s is still in the hangar.",
|
|
"%s didn't want to fly today apparently.",
|
|
"Well crap. %s spawn totally failed.",
|
|
"%s launch aborted. Something broke.",
|
|
"That didn't work. %s spawn failed miserably.",
|
|
"%s couldn't spawn. Technical difficulties.",
|
|
"Negative spawn for %s. Try again maybe?",
|
|
"%s spawn went tits up. Sorry.",
|
|
"Launch failure! %s is grounded.",
|
|
"%s spawn crashed and burned. Not literally.",
|
|
"Unable to spawn %s. Computer says no.",
|
|
"%s launch failed. Mo could have done better.",
|
|
"Spawn error for %s. This is awkward.",
|
|
"%s didn't spawn. The universe said no.",
|
|
"Failed to launch %s. Not our day.",
|
|
"%s spawn aborted. Probably for the best.",
|
|
"Yeah... %s spawn didn't happen.",
|
|
"%s failed to spawn. Check your setup.",
|
|
"Spawn unsuccessful for %s. Womp womp.",
|
|
"%s launch went south. Way south.",
|
|
"That's a no-go. %s failed to spawn.",
|
|
"%s spawn error. Better call tech support.",
|
|
"Launch failure! %s stayed on the ground.",
|
|
"%s couldn't spawn. Even we're confused.",
|
|
"Spawn failed for %s. Mo's laughing right now.",
|
|
"%s launch aborted. Something went wrong.",
|
|
"Unable to activate %s. Try turning it off and on again.",
|
|
"%s spawn went nowhere fast.",
|
|
"Failed spawn alert: %s is still parked.",
|
|
"%s launch unsuccessful. This is fine. Everything's fine.",
|
|
"Spawn error for %s. Not ideal.",
|
|
"%s couldn't get airborne. Rough.",
|
|
"Launch aborted. %s is taking a day off.",
|
|
"%s spawn failed spectacularly.",
|
|
"Cannot spawn %s. System said 'nah.'",
|
|
"%s launch went sideways. Try again.",
|
|
"Spawn failure! %s is grounded indefinitely.",
|
|
"%s didn't spawn. Murphy's Law in effect.",
|
|
"Failed to launch %s. Mo's spawn worked better.",
|
|
"%s spawn unsuccessful. Check the logs.",
|
|
"Error spawning %s. This shouldn't happen.",
|
|
"%s launch aborted. Technical difficulties ahead.",
|
|
"Spawn failed. %s is staying home today.",
|
|
"%s couldn't spawn. Better luck next time, champ.",
|
|
"Launch failure for %s. Not sure why.",
|
|
"%s spawn went wrong. Very wrong.",
|
|
"Unable to spawn %s. Try again later.",
|
|
"%s launch failed. Mo would be disappointed.",
|
|
"Spawn error: %s didn't make it.",
|
|
"%s failed to spawn. Computer threw a tantrum.",
|
|
"Launch aborted for %s. Sorry about that.",
|
|
"%s spawn unsuccessful. Try again maybe?",
|
|
"Cannot activate %s. Spawn failed.",
|
|
"%s launch went nowhere. Like Mo's career.",
|
|
"Spawn failure! %s is MIA.",
|
|
"%s couldn't spawn. System error.",
|
|
"Failed to launch %s. This is awkward.",
|
|
"%s spawn aborted. Not today, apparently.",
|
|
"Launch error for %s. Check your setup.",
|
|
"%s didn't spawn. Computer says no way.",
|
|
"Spawn unsuccessful. %s is grounded.",
|
|
"%s launch failed. Mo's code was better.",
|
|
"Cannot spawn %s. Technical issues.",
|
|
"%s failed to activate. Try again.",
|
|
"Launch aborted. %s spawn went south.",
|
|
"%s spawn error. This isn't good.",
|
|
"Failed to spawn %s. Maybe next time.",
|
|
"%s launch unsuccessful. Something broke.",
|
|
"Spawn failure for %s. Not our best work.",
|
|
"%s couldn't get off the ground. Awkward moment.",
|
|
"Launch error! %s is still parked.",
|
|
"%s spawn went wrong. Very, very wrong.",
|
|
"Unable to spawn %s. System malfunction.",
|
|
"%s launch failed harder than expected.",
|
|
"Spawn aborted. %s is taking a sick day.",
|
|
},
|
|
|
|
-- Custom Route Accepted
|
|
ROUTE_ACCEPTED = {
|
|
"%s accepting custom route with %d waypoints.%s",
|
|
"%s has your route. %d waypoints loaded.%s",
|
|
"%s acknowledges custom flight plan. %d waypoints.%s",
|
|
"%s route confirmed. %d waypoints programmed.%s",
|
|
"%s copy your route. %d waypoints accepted.%s",
|
|
"%s roger. %d waypoint route loaded.%s",
|
|
"%s has the route. %d points confirmed.%s",
|
|
"%s flight plan accepted. %d waypoints.%s",
|
|
"%s confirms route. %d waypoints in the box.%s",
|
|
"%s routing confirmed with %d waypoints.%s",
|
|
"%s has your custom route. %d waypoints loaded.%s",
|
|
"%s accepts your flight plan. %d points confirmed.%s",
|
|
"%s copies custom route with %d waypoints.%s",
|
|
"%s acknowledges %d waypoint route.%s",
|
|
"%s route programmed. %d waypoints locked in.%s",
|
|
"%s flight plan confirmed with %d points.%s",
|
|
"%s roger your route. %d waypoints loaded.%s",
|
|
"%s accepts %d waypoint custom plan.%s",
|
|
"%s has your %d waypoint route locked in.%s",
|
|
"%s confirms %d waypoint flight plan.%s",
|
|
"%s copy that. %d waypoint route programmed.%s",
|
|
"%s routing accepted. %d points confirmed.%s",
|
|
"%s has the route. %d waypoints ready.%s",
|
|
"%s acknowledges %d point route.%s",
|
|
"%s flight plan loaded with %d waypoints.%s",
|
|
"%s custom route confirmed. %d points.%s",
|
|
"%s accepts your %d waypoint plan.%s",
|
|
"%s roger. %d waypoints programmed.%s",
|
|
"%s has your %d waypoint custom route.%s",
|
|
"%s routing confirmed with %d points.%s",
|
|
"%s copies %d waypoint route. Unlike Mo's attempt.%s",
|
|
"%s accepts your custom %d waypoint plan.%s",
|
|
"%s has loaded %d waypoint route.%s",
|
|
"%s confirms %d waypoint routing.%s",
|
|
"%s flight plan locked in. %d waypoints.%s",
|
|
"%s roger your %d waypoint route.%s",
|
|
"%s accepts custom route with %d points.%s",
|
|
"%s has programmed %d waypoint plan.%s",
|
|
"%s acknowledges %d waypoint custom route.%s",
|
|
"%s routing confirmed. %d waypoints ready.%s",
|
|
"%s copies %d waypoint flight plan.%s",
|
|
"%s accepts your %d point custom route.%s",
|
|
"%s has %d waypoint route confirmed.%s",
|
|
"%s roger custom plan with %d waypoints.%s",
|
|
"%s routing accepted. %d points programmed.%s",
|
|
"%s flight plan confirmed. %d waypoints loaded.%s",
|
|
"%s has your custom %d waypoint routing.%s",
|
|
"%s accepts %d waypoint plan.%s",
|
|
"%s confirms custom route. %d waypoints.%s",
|
|
"%s roger that. %d waypoint route accepted.%s",
|
|
"%s has %d waypoint custom plan loaded.%s",
|
|
"%s acknowledges %d point custom route.%s",
|
|
"%s routing programmed. %d waypoints confirmed.%s",
|
|
"%s flight plan accepted with %d points.%s",
|
|
"%s copies your %d waypoint custom route.%s",
|
|
"%s has %d waypoint route ready.%s",
|
|
"%s accepts custom plan. %d waypoints.%s",
|
|
"%s confirms %d point flight plan.%s",
|
|
"%s roger custom %d waypoint route.%s",
|
|
"%s routing locked in. %d waypoints.%s",
|
|
"%s has %d waypoint plan confirmed.%s",
|
|
"%s flight plan loaded. %d waypoints accepted.%s",
|
|
"%s acknowledges custom %d waypoint route.%s",
|
|
"%s accepts %d waypoint routing.%s",
|
|
"%s copies custom %d waypoint plan.%s",
|
|
"%s has %d waypoint route programmed.%s",
|
|
"%s confirms your %d waypoint custom route.%s",
|
|
"%s roger. %d waypoint custom plan loaded.%s",
|
|
"%s routing accepted with %d points.%s",
|
|
"%s flight plan programmed. %d waypoints.%s",
|
|
"%s has custom route with %d waypoints.%s",
|
|
"%s accepts %d waypoint custom plan.%s",
|
|
"%s acknowledges %d waypoint routing.%s",
|
|
"%s copies %d waypoint custom route.%s",
|
|
"%s has %d waypoint flight plan confirmed.%s",
|
|
"%s roger custom route. %d waypoints.%s",
|
|
"%s routing confirmed with %d waypoints.%s",
|
|
"%s flight plan accepted. %d points loaded.%s",
|
|
"%s has your %d waypoint custom plan.%s",
|
|
"%s accepts custom %d waypoint route.%s",
|
|
"%s confirms %d waypoint custom plan.%s",
|
|
"%s acknowledges %d waypoint flight plan.%s",
|
|
"%s copies %d waypoint route confirmed.%s",
|
|
"%s has custom %d waypoint routing ready.%s",
|
|
"%s roger. %d waypoints accepted and locked.%s",
|
|
"%s routing programmed with %d waypoints.%s",
|
|
"%s flight plan confirmed with %d points.%s",
|
|
"%s has %d waypoint custom route loaded.%s",
|
|
"%s accepts your custom %d waypoint routing.%s",
|
|
"%s confirms %d waypoint plan confirmed.%s",
|
|
"%s acknowledges custom route with %d points.%s",
|
|
"%s copies %d waypoint custom flight plan.%s",
|
|
"%s has %d waypoint route locked and loaded.%s",
|
|
"%s roger that. %d waypoint custom route ready.%s",
|
|
"%s routing accepted. %d waypoints programmed.%s",
|
|
"%s flight plan loaded with %d waypoints.%s",
|
|
},
|
|
|
|
-- Emergency Spawn
|
|
EMERGENCY_SPAWN = {
|
|
"EMERGENCY: %s launching immediately!",
|
|
"PRIORITY LAUNCH: %s is scrambling now!",
|
|
"EMERGENCY TANKER: %s departing expedited!",
|
|
"URGENT: %s is launching on priority status!",
|
|
"EMERGENCY RESPONSE: %s airborne ASAP!",
|
|
"PRIORITY: %s scrambling for emergency fuel!",
|
|
"EMERGENCY: %s launching hot!",
|
|
"URGENT LAUNCH: %s is wheels up now!",
|
|
"EMERGENCY TANKER: %s responding immediately!",
|
|
"PRIORITY STATUS: %s emergency launch in progress!",
|
|
"EMERGENCY! %s wheels up NOW!",
|
|
"SCRAMBLE SCRAMBLE: %s launching immediately!",
|
|
"PRIORITY LAUNCH: %s getting airborne right now!",
|
|
"EMERGENCY TANKER: %s departing hot and fast!",
|
|
"URGENT: %s scrambling for emergency refuel!",
|
|
"PRIORITY: %s launching on expedited status!",
|
|
"EMERGENCY RESPONSE: %s airborne immediately!",
|
|
"URGENT LAUNCH: %s departing NOW!",
|
|
"EMERGENCY: %s getting up there ASAP!",
|
|
"PRIORITY STATUS: %s scrambling right now!",
|
|
"EMERGENCY TANKER: %s wheels up immediately!",
|
|
"URGENT: %s launching on priority!",
|
|
"SCRAMBLE: %s departing expedited!",
|
|
"EMERGENCY: %s getting airborne fast!",
|
|
"PRIORITY LAUNCH: %s launching NOW!",
|
|
"URGENT TANKER: %s scrambling immediately!",
|
|
"EMERGENCY: %s departing hot!",
|
|
"PRIORITY: %s wheels up ASAP!",
|
|
"URGENT LAUNCH: %s airborne right now!",
|
|
"EMERGENCY TANKER: %s launching immediately!",
|
|
"SCRAMBLE SCRAMBLE: %s getting up there now!",
|
|
"PRIORITY: %s launching on emergency status!",
|
|
"URGENT: %s departing immediately!",
|
|
"EMERGENCY: %s scrambling for urgent refuel!",
|
|
"PRIORITY LAUNCH: %s wheels up hot!",
|
|
"URGENT TANKER: %s airborne ASAP!",
|
|
"EMERGENCY: %s launching right now!",
|
|
"PRIORITY: %s scrambling expedited!",
|
|
"URGENT LAUNCH: %s departing NOW NOW NOW!",
|
|
"EMERGENCY TANKER: %s getting airborne fast!",
|
|
"PRIORITY STATUS: %s launching immediately!",
|
|
"URGENT: %s wheels up on priority!",
|
|
"EMERGENCY: %s scrambling now!",
|
|
"PRIORITY LAUNCH: %s departing fast!",
|
|
"URGENT TANKER: %s launching ASAP!",
|
|
"EMERGENCY: %s airborne immediately!",
|
|
"PRIORITY: %s scrambling hot!",
|
|
"URGENT LAUNCH: %s wheels up right now!",
|
|
"EMERGENCY TANKER: %s departing expedited!",
|
|
"PRIORITY: %s launching on urgent status!",
|
|
"URGENT: %s getting airborne now!",
|
|
"EMERGENCY SCRAMBLE: %s departing immediately!",
|
|
"PRIORITY TANKER: %s wheels up fast!",
|
|
"URGENT: %s launching right now!",
|
|
"EMERGENCY: %s airborne ASAP!",
|
|
"PRIORITY LAUNCH: %s scrambling now!",
|
|
"URGENT TANKER: %s departing hot!",
|
|
"EMERGENCY: %s wheels up immediately!",
|
|
"PRIORITY: %s getting airborne fast!",
|
|
"URGENT LAUNCH: %s scrambling ASAP!",
|
|
"EMERGENCY TANKER: %s launching on priority!",
|
|
"PRIORITY: %s departing right now!",
|
|
"URGENT: %s airborne expedited!",
|
|
"EMERGENCY: %s scrambling immediately!",
|
|
"PRIORITY LAUNCH: %s wheels up NOW!",
|
|
"URGENT TANKER: %s launching fast!",
|
|
"EMERGENCY: %s departing ASAP!",
|
|
"PRIORITY: %s airborne right now!",
|
|
"URGENT LAUNCH: %s scrambling hot!",
|
|
"EMERGENCY TANKER: %s wheels up expedited!",
|
|
"PRIORITY: %s launching immediately!",
|
|
"URGENT: %s getting airborne ASAP!",
|
|
"EMERGENCY: %s scrambling fast!",
|
|
"PRIORITY LAUNCH: %s departing NOW!",
|
|
"URGENT TANKER: %s wheels up right now!",
|
|
"EMERGENCY: %s airborne hot!",
|
|
"PRIORITY: %s scrambling ASAP!",
|
|
"URGENT LAUNCH: %s launching immediately!",
|
|
"EMERGENCY TANKER: %s departing fast!",
|
|
"PRIORITY: %s wheels up expedited!",
|
|
"URGENT: %s airborne NOW!",
|
|
"EMERGENCY: %s launching hot and fast!",
|
|
"PRIORITY LAUNCH: %s scrambling expedited!",
|
|
"URGENT TANKER: %s departing immediately!",
|
|
"EMERGENCY: %s wheels up ASAP!",
|
|
"PRIORITY: %s getting airborne now!",
|
|
"URGENT LAUNCH: %s airborne fast!",
|
|
"EMERGENCY TANKER: %s scrambling NOW!",
|
|
"PRIORITY: %s launching expedited!",
|
|
"URGENT: %s departing hot!",
|
|
"EMERGENCY: %s airborne immediately unlike Mo!",
|
|
"PRIORITY LAUNCH: %s wheels up faster than Mo!",
|
|
"URGENT TANKER: %s scrambling (Mo couldn't do this)!",
|
|
"EMERGENCY: %s launching while Mo watches!",
|
|
"PRIORITY: %s departing - Mo take notes!",
|
|
"URGENT LAUNCH: %s airborne (unlike Mo's attempts)!",
|
|
"EMERGENCY TANKER: %s scrambling successfully!",
|
|
"PRIORITY: %s wheels up for real!",
|
|
"URGENT: %s launching like professionals do!",
|
|
},
|
|
|
|
-- Low Fuel Warning
|
|
LOW_FUEL = {
|
|
"%s reports fuel at %d%%. Recommend expedite refueling.",
|
|
"%s low on fuel - %d%% remaining. RTB soon.",
|
|
"%s fuel state: %d%%. Time is limited.",
|
|
"%s down to %d%% fuel. Get your gas quick.",
|
|
"%s running low - %d%% remaining.",
|
|
"%s fuel advisory: %d%% left. Don't delay.",
|
|
"%s reports %d%% fuel state. Limited time remaining.",
|
|
"%s low fuel warning at %d%%. RTB imminent.",
|
|
"%s fuel: %d%%. Better hurry up.",
|
|
"%s getting thirsty at %d%% fuel remaining.",
|
|
"%s fuel down to %d%%. Time's ticking.",
|
|
"%s running on fumes at %d%%. Get moving.",
|
|
"%s reports %d%% fuel. Clock is running.",
|
|
"%s fuel state critical at %d%%.",
|
|
"%s getting low at %d%%. Don't dawdle.",
|
|
"%s fuel: %d%%. Window is closing.",
|
|
"%s reports %d%% remaining. Hurry it up.",
|
|
"%s fuel advisory: %d%%. Time's short.",
|
|
"%s down to %d%%. Better move fast.",
|
|
"%s fuel at %d%%. RTB soon or refuel now.",
|
|
"%s running thin at %d%%. Expedite.",
|
|
"%s reports %d%% fuel. Not much time left.",
|
|
"%s fuel state %d%%. Don't mess around.",
|
|
"%s getting low - %d%% and dropping.",
|
|
"%s fuel: %d%%. Better get some quick.",
|
|
"%s reports %d%%. Running out of time.",
|
|
"%s fuel down to %d%%. Tick tock.",
|
|
"%s low on gas at %d%%. Move it.",
|
|
"%s reports %d%% fuel state. Limited window.",
|
|
"%s fuel: %d%%. Don't be slow about it.",
|
|
"%s getting thirsty - %d%% remaining.",
|
|
"%s reports %d%%. Better hurry your ass up.",
|
|
"%s fuel at %d%%. Time ain't on your side.",
|
|
"%s running low - %d%%. Get in here.",
|
|
"%s fuel state: %d%%. Mo could refuel faster.",
|
|
"%s reports %d%%. Don't be a hero, get fuel.",
|
|
"%s fuel down to %d%%. Unlike Mo we're warning you.",
|
|
"%s getting low at %d%%. Stop screwing around.",
|
|
"%s reports %d%% fuel. This isn't a drill.",
|
|
"%s fuel: %d%%. Better not screw this up.",
|
|
"%s running thin - %d%% remaining.",
|
|
"%s reports %d%%. Time to get your ass over here.",
|
|
"%s fuel state %d%%. Seriously, hurry up.",
|
|
"%s getting thirsty at %d%%. Don't be stupid.",
|
|
"%s fuel: %d%%. We're leaving soon.",
|
|
"%s reports %d%%. Better expedite refueling.",
|
|
"%s fuel down to %d%%. Window closing fast.",
|
|
"%s low on juice - %d%% remaining.",
|
|
"%s reports %d%% fuel. Get moving or RTB.",
|
|
"%s fuel state: %d%%. Don't drag ass.",
|
|
"%s getting low at %d%%. Time's running out.",
|
|
"%s reports %d%%. Stop dicking around.",
|
|
"%s fuel: %d%%. Get in the basket.",
|
|
"%s running thin at %d%%. Move faster.",
|
|
"%s reports %d%% remaining. Chop chop.",
|
|
"%s fuel down to %d%%. Unlike Mo's planning.",
|
|
"%s getting thirsty - %d%%. Don't be slow.",
|
|
"%s reports %d%% fuel state. Hurry.",
|
|
"%s fuel: %d%%. Better not flame out.",
|
|
"%s low on gas at %d%%. Get over here.",
|
|
"%s reports %d%%. Time to move it.",
|
|
"%s fuel state %d%%. We don't have all day.",
|
|
"%s getting low - %d%% and dropping fast.",
|
|
"%s reports %d%%. Stop being a pussy.",
|
|
"%s fuel: %d%%. Refuel or die trying.",
|
|
"%s running thin - %d%%. Better hurry.",
|
|
"%s reports %d%% fuel. Move your ass.",
|
|
"%s fuel down to %d%%. Not kidding here.",
|
|
"%s getting thirsty at %d%%. Expedite.",
|
|
"%s reports %d%%. Don't be like Mo.",
|
|
"%s fuel state: %d%%. Get fuel or get bent.",
|
|
"%s low at %d%%. Time's wasting.",
|
|
"%s reports %d%% remaining. Hurry up.",
|
|
"%s fuel: %d%%. Stop fucking around.",
|
|
"%s running low - %d%%. Get here now.",
|
|
"%s reports %d%%. We're not waiting forever.",
|
|
"%s fuel down to %d%%. Better get moving.",
|
|
"%s getting low at %d%%. Tick tock motherfucker.",
|
|
"%s reports %d%% fuel state. Move it.",
|
|
"%s fuel: %d%%. Don't be a jackass.",
|
|
"%s running thin at %d%%. Expedite refuel.",
|
|
"%s reports %d%%. Time's running short.",
|
|
"%s fuel state %d%%. Get in the pattern.",
|
|
"%s getting thirsty - %d%%. Don't delay.",
|
|
"%s reports %d%%. Unlike Mo we're still here.",
|
|
"%s fuel: %d%%. Better not screw this up.",
|
|
"%s low on gas - %d%% remaining.",
|
|
"%s reports %d%% fuel. Window closing.",
|
|
"%s fuel down to %d%%. Get your shit together.",
|
|
"%s getting low at %d%%. Seriously move.",
|
|
"%s reports %d%%. Don't make us leave.",
|
|
"%s fuel state: %d%%. Better expedite.",
|
|
"%s running thin - %d%%. Time's up soon.",
|
|
"%s reports %d%% remaining. Get here.",
|
|
"%s fuel: %d%%. Stop dragging ass.",
|
|
"%s getting thirsty at %d%%. Hurry.",
|
|
"%s reports %d%%. Mo would have flamed out by now.",
|
|
},
|
|
|
|
-- Bingo Fuel (RTB)
|
|
BINGO_FUEL = {
|
|
"%s is BINGO fuel. Returning to base immediately!",
|
|
"%s has reached BINGO. RTB in progress!",
|
|
"%s calling BINGO fuel. Departing the pattern now!",
|
|
"%s is at BINGO state. Returning to base!",
|
|
"%s BINGO fuel - heading home now!",
|
|
"%s has hit BINGO. No more refueling available!",
|
|
"%s fuel critical - RTB initiated!",
|
|
"%s at BINGO state. Breaking off now!",
|
|
"%s calling BINGO. Pattern is clear!",
|
|
"%s BINGO fuel declared. Returning to base!",
|
|
"%s is BINGO. Getting the hell out!",
|
|
"%s calling BINGO fuel. We're done here!",
|
|
"%s has reached BINGO state. Leaving NOW!",
|
|
"%s BINGO declared. RTB in progress!",
|
|
"%s at BINGO fuel. Heading home!",
|
|
"%s calling BINGO. Pattern clear!",
|
|
"%s has hit BINGO. See ya!",
|
|
"%s BINGO fuel state. Departing!",
|
|
"%s is at BINGO. RTB immediately!",
|
|
"%s calling BINGO. We're out!",
|
|
"%s has reached BINGO. Breaking off!",
|
|
"%s BINGO fuel declared. Leaving!",
|
|
"%s at BINGO state. Going home!",
|
|
"%s calling BINGO. Adios!",
|
|
"%s has hit BINGO fuel. Departing now!",
|
|
"%s BINGO declared. RTB active!",
|
|
"%s is at BINGO. Bye bye!",
|
|
"%s calling BINGO fuel. Out of here!",
|
|
"%s has reached BINGO state. Later!",
|
|
"%s BINGO fuel. Heading back!",
|
|
"%s at BINGO. Returning immediately!",
|
|
"%s calling BINGO. Pattern's yours!",
|
|
"%s has hit BINGO. Going home!",
|
|
"%s BINGO declared. Leaving the AO!",
|
|
"%s is at BINGO fuel. RTB now!",
|
|
"%s calling BINGO. Peace out!",
|
|
"%s has reached BINGO. Departing!",
|
|
"%s BINGO fuel state. We're done!",
|
|
"%s at BINGO. Heading to base!",
|
|
"%s calling BINGO. Catch you later!",
|
|
"%s has hit BINGO fuel. RTB!",
|
|
"%s BINGO declared. Getting out!",
|
|
"%s is at BINGO state. Later gator!",
|
|
"%s calling BINGO. We out!",
|
|
"%s has reached BINGO. Returning!",
|
|
"%s BINGO fuel. Leaving now!",
|
|
"%s at BINGO. Going home finally!",
|
|
"%s calling BINGO. Done pumping gas!",
|
|
"%s has hit BINGO. RTB initiated!",
|
|
"%s BINGO declared. Out of here!",
|
|
"%s is at BINGO fuel. Bye!",
|
|
"%s calling BINGO. Pattern clear!",
|
|
"%s has reached BINGO state. Departing!",
|
|
"%s BINGO fuel. Heading back!",
|
|
"%s at BINGO. RTB in progress!",
|
|
"%s calling BINGO. See ya later!",
|
|
"%s has hit BINGO fuel. Leaving!",
|
|
"%s BINGO declared. Going home!",
|
|
"%s is at BINGO. Out!",
|
|
"%s calling BINGO fuel. We're outta here!",
|
|
"%s has reached BINGO. RTB now!",
|
|
"%s BINGO fuel state. Later!",
|
|
"%s at BINGO. Returning to base!",
|
|
"%s calling BINGO. Adios amigos!",
|
|
"%s has hit BINGO. Departing!",
|
|
"%s BINGO declared. Heading home!",
|
|
"%s is at BINGO fuel. Peace!",
|
|
"%s calling BINGO. We done!",
|
|
"%s has reached BINGO state. Leaving!",
|
|
"%s BINGO fuel. RTB active!",
|
|
"%s at BINGO. Going back!",
|
|
"%s calling BINGO. That's it folks!",
|
|
"%s has hit BINGO fuel. Out of here!",
|
|
"%s BINGO declared. Returning!",
|
|
"%s is at BINGO. Later suckers!",
|
|
"%s calling BINGO fuel. Bye!",
|
|
"%s has reached BINGO. Heading home!",
|
|
"%s BINGO fuel state. Departing!",
|
|
"%s at BINGO. RTB initiated!",
|
|
"%s calling BINGO. We're gone!",
|
|
"%s has hit BINGO. Leaving now!",
|
|
"%s BINGO declared. Getting out of dodge!",
|
|
"%s is at BINGO fuel. Later!",
|
|
"%s calling BINGO. Don't wait up!",
|
|
"%s has reached BINGO state. Out!",
|
|
"%s BINGO fuel. Going home!",
|
|
"%s at BINGO. Returning immediately!",
|
|
"%s calling BINGO. Unlike Mo we planned this!",
|
|
"%s has hit BINGO fuel. Peace out!",
|
|
"%s BINGO declared. Heading back!",
|
|
"%s is at BINGO. Bye felicia!",
|
|
"%s calling BINGO fuel. That's a wrap!",
|
|
"%s has reached BINGO. We're out!",
|
|
"%s BINGO fuel state. Later gator!",
|
|
"%s at BINGO. RTB right now!",
|
|
"%s calling BINGO. Smell ya later!",
|
|
"%s has hit BINGO. Departing!",
|
|
"%s BINGO declared. Mo would've flamed out!",
|
|
"%s is at BINGO fuel. Catch you on the flip side!",
|
|
},
|
|
|
|
-- Tanker Destroyed
|
|
DESTROYED = {
|
|
"%s has been destroyed!",
|
|
"%s is down! Aircraft lost!",
|
|
"%s has been shot down!",
|
|
"%s destroyed in combat!",
|
|
"%s is gone - aircraft destroyed!",
|
|
"%s has been lost!",
|
|
"We've lost %s!",
|
|
"%s destroyed! No survivors!",
|
|
"%s is down and out!",
|
|
"%s has been eliminated!",
|
|
"%s has been blown to hell!",
|
|
"%s is toast! Aircraft destroyed!",
|
|
"%s went down in flames!",
|
|
"%s has been obliterated!",
|
|
"%s is scrap metal now!",
|
|
"%s got smoked!",
|
|
"RIP %s. Aircraft destroyed!",
|
|
"%s has been vaporized!",
|
|
"%s is no more!",
|
|
"%s went down hard!",
|
|
"%s has been wasted!",
|
|
"%s is KIA! Aircraft lost!",
|
|
"%s got shot the fuck down!",
|
|
"%s has been annihilated!",
|
|
"%s is sleeping with the fishes!",
|
|
"%s went boom!",
|
|
"%s has been terminated!",
|
|
"%s is dead! No survivors!",
|
|
"%s got fucked up!",
|
|
"%s has ceased to exist!",
|
|
"%s went down like Mo's career!",
|
|
"%s is destroyed! Total loss!",
|
|
"%s got hammered!",
|
|
"%s has been neutralized!",
|
|
"%s is history!",
|
|
"%s went down in a ball of fire!",
|
|
"%s has been taken out!",
|
|
"%s is gone forever!",
|
|
"%s got massacred!",
|
|
"%s has been deleted!",
|
|
"%s is pushing up daisies!",
|
|
"%s went down screaming!",
|
|
"%s has been liquidated!",
|
|
"%s is scattered across the landscape!",
|
|
"%s got wrecked!",
|
|
"%s has been erased!",
|
|
"%s is no longer operational!",
|
|
"%s went down like a brick!",
|
|
"%s has been dispatched!",
|
|
"%s is gone to the great hangar in the sky!",
|
|
"%s got absolutely demolished!",
|
|
"%s has been removed from existence!",
|
|
"%s is dead as fuck!",
|
|
"%s went down faster than Mo!",
|
|
"%s has been exterminated!",
|
|
"%s is now a smoking crater!",
|
|
"%s got absolutely destroyed!",
|
|
"%s has been converted to debris!",
|
|
"%s is no longer with us!",
|
|
"%s went down in a spectacular fashion!",
|
|
"%s has been sent to hell!",
|
|
"%s is totally fucked!",
|
|
"%s got blown out of the sky!",
|
|
"%s has been utterly destroyed!",
|
|
"%s is burning on the ground!",
|
|
"%s went down like a sack of shit!",
|
|
"%s has been wiped out!",
|
|
"%s is permanently grounded!",
|
|
"%s got turned into confetti!",
|
|
"%s has been removed from service!",
|
|
"%s is now spare parts!",
|
|
"%s went down hard and fast!",
|
|
"%s has been completely destroyed!",
|
|
"%s is toast and then some!",
|
|
"%s got absolutely annihilated!",
|
|
"%s has been blown to smithereens!",
|
|
"%s is no longer flying!",
|
|
"%s went down like the Hindenburg!",
|
|
"%s has been totally wrecked!",
|
|
"%s is deader than dead!",
|
|
"%s got straight up murdered!",
|
|
"%s has been completely obliterated!",
|
|
"%s is scattered across three counties!",
|
|
"%s went down in flames like Mo's reputation!",
|
|
"%s has been catastrophically destroyed!",
|
|
"%s is now a fireball!",
|
|
"%s got absolutely smoked!",
|
|
"%s has been reduced to atoms!",
|
|
"%s is gone gone gone!",
|
|
"%s went down and ain't coming back!",
|
|
"%s has been utterly annihilated!",
|
|
"%s is now a lawn dart!",
|
|
"%s got completely fucked!",
|
|
"%s has been sent to the shadow realm!",
|
|
"%s is now in aircraft heaven!",
|
|
"%s went down faster than your hopes and dreams!",
|
|
"%s has been totally destroyed!",
|
|
"%s is no longer a thing!",
|
|
},
|
|
|
|
-- Hostile Fire
|
|
TAKING_FIRE = {
|
|
"%s is taking fire!",
|
|
"%s under attack!",
|
|
"%s receiving hostile fire!",
|
|
"%s taking hits!",
|
|
"%s is being engaged!",
|
|
"%s under hostile fire!",
|
|
"Hostile fire on %s!",
|
|
"%s taking enemy fire!",
|
|
"%s is under attack!",
|
|
"%s being fired upon!",
|
|
"%s is getting shot at!",
|
|
"%s under hostile fire!",
|
|
"%s taking incoming!",
|
|
"%s is being lit up!",
|
|
"%s receiving enemy fire!",
|
|
"%s getting hammered!",
|
|
"%s under attack right now!",
|
|
"%s taking fire from hostiles!",
|
|
"%s is being engaged by enemy!",
|
|
"%s getting shot to shit!",
|
|
"%s under hostile attack!",
|
|
"%s taking heavy fire!",
|
|
"%s is being targeted!",
|
|
"%s receiving hostile rounds!",
|
|
"%s getting fucked up!",
|
|
"%s under enemy fire!",
|
|
"%s taking hits from hostiles!",
|
|
"%s is being shot at!",
|
|
"%s receiving incoming fire!",
|
|
"%s getting attacked!",
|
|
"%s under hostile engagement!",
|
|
"%s taking enemy rounds!",
|
|
"%s is being hit!",
|
|
"%s receiving fire!",
|
|
"%s getting lit up!",
|
|
"%s under attack from enemy!",
|
|
"%s taking hostile fire!",
|
|
"%s is being engaged!",
|
|
"%s receiving enemy rounds!",
|
|
"%s getting shot!",
|
|
"%s under fire right now!",
|
|
"%s taking incoming rounds!",
|
|
"%s is being attacked!",
|
|
"%s receiving hostile fire!",
|
|
"%s getting hammered by hostiles!",
|
|
"%s under enemy attack!",
|
|
"%s taking fire from below!",
|
|
"%s is being targeted by enemy!",
|
|
"%s receiving heavy fire!",
|
|
"%s getting shot at hard!",
|
|
"%s under hostile fire!",
|
|
"%s taking enemy fire now!",
|
|
"%s is being engaged by hostiles!",
|
|
"%s receiving incoming!",
|
|
"%s getting attacked by enemy!",
|
|
"%s under fire from hostiles!",
|
|
"%s taking hits!",
|
|
"%s is being shot up!",
|
|
"%s receiving hostile rounds!",
|
|
"%s getting fucked up by enemy!",
|
|
"%s under hostile engagement!",
|
|
"%s taking fire!",
|
|
"%s is being hammered!",
|
|
"%s receiving enemy fire!",
|
|
"%s getting shot at!",
|
|
"%s under enemy fire!",
|
|
"%s taking hostile rounds!",
|
|
"%s is being lit up!",
|
|
"%s receiving fire from hostiles!",
|
|
"%s getting attacked hard!",
|
|
"%s under hostile attack!",
|
|
"%s taking incoming fire!",
|
|
"%s is being engaged!",
|
|
"%s receiving hostile fire!",
|
|
"%s getting shot to hell!",
|
|
"%s under fire!",
|
|
"%s taking enemy rounds!",
|
|
"%s is being targeted!",
|
|
"%s receiving fire!",
|
|
"%s getting hammered!",
|
|
"%s under attack by hostiles!",
|
|
"%s taking fire from enemy!",
|
|
"%s is being shot at!",
|
|
"%s receiving incoming rounds!",
|
|
"%s getting attacked!",
|
|
"%s under hostile fire right now!",
|
|
"%s taking hits from enemy!",
|
|
"%s is being engaged by hostiles!",
|
|
"%s receiving hostile fire!",
|
|
"%s getting lit up by enemy!",
|
|
"%s under fire from below!",
|
|
"%s taking enemy fire!",
|
|
"%s is being attacked by hostiles!",
|
|
"%s receiving fire from enemy!",
|
|
"%s getting shot at hard!",
|
|
"%s under enemy attack!",
|
|
"%s taking hostile fire unlike Mo who'd be dead!",
|
|
"%s is being hammered by hostiles!",
|
|
"%s receiving enemy rounds!",
|
|
},
|
|
|
|
-- Invalid Waypoint Count (too few)
|
|
TOO_FEW_WAYPOINTS = {
|
|
"Custom route requires at least %d waypoints!\nPlace markers: %s1, %s2, etc.",
|
|
"Not enough waypoints! Need at least %d.\nUse markers: %s1, %s2, etc.",
|
|
"Insufficient waypoints - need %d minimum.\nCreate markers: %s1, %s2, etc.",
|
|
"Route rejected: need %d waypoints minimum.\nPlace %s1, %s2, etc.",
|
|
"At least %d waypoints required!\nDrop markers: %s1, %s2, etc.",
|
|
"Need more waypoints - minimum is %d.\nUse: %s1, %s2, etc.",
|
|
"Route incomplete. Need %d waypoints.\nCreate: %s1, %s2, etc.",
|
|
"Waypoint count too low - need %d.\nPlace: %s1, %s2, etc.",
|
|
"Minimum %d waypoints required!\nMark: %s1, %s2, etc.",
|
|
"Can't route with less than %d points.\nAdd markers: %s1, %s2, etc.",
|
|
},
|
|
|
|
-- Too Many Waypoints
|
|
TOO_MANY_WAYPOINTS = {
|
|
"Too many waypoints! Maximum is %d",
|
|
"Waypoint limit exceeded. Max: %d",
|
|
"Can't route with more than %d waypoints!",
|
|
"Route rejected - too many points. Max: %d",
|
|
"Waypoint overflow! Maximum is %d",
|
|
"Too complex - max %d waypoints allowed!",
|
|
"Exceeded waypoint limit of %d!",
|
|
"Route too long! Maximum: %d waypoints",
|
|
"Cannot accept more than %d waypoints!",
|
|
"Waypoint maximum is %d. Route rejected.",
|
|
},
|
|
|
|
-- No RTB Airbase Found
|
|
NO_RTB_AIRBASE = {
|
|
"No friendly airbase found for RTB!",
|
|
"Cannot locate RTB destination!",
|
|
"No suitable airbase available for recovery!",
|
|
"Unable to find friendly base for RTB!",
|
|
"No recovery airfield located!",
|
|
"RTB destination unavailable!",
|
|
"Cannot identify friendly airbase for return!",
|
|
"No airbase in range for RTB!",
|
|
"Recovery base not found!",
|
|
"Unable to locate RTB airfield!",
|
|
},
|
|
-- EMERGENCY SPAWN MESSAGES (100 total)
|
|
EMERGENCY_SPAWN = {
|
|
"EMERGENCY: %s launching immediately!",
|
|
"PRIORITY LAUNCH: %s is scrambling now!",
|
|
"EMERGENCY TANKER: %s departing expedited!",
|
|
"URGENT: %s is launching on priority status!",
|
|
"EMERGENCY RESPONSE: %s airborne ASAP!",
|
|
"PRIORITY: %s scrambling for emergency fuel!",
|
|
"EMERGENCY: %s launching hot!",
|
|
"URGENT LAUNCH: %s is wheels up now!",
|
|
"EMERGENCY TANKER: %s responding immediately!",
|
|
"PRIORITY STATUS: %s emergency launch in progress!",
|
|
"EMERGENCY! %s wheels up NOW!",
|
|
"SCRAMBLE SCRAMBLE: %s launching immediately!",
|
|
"PRIORITY LAUNCH: %s getting airborne right now!",
|
|
"EMERGENCY TANKER: %s departing hot and fast!",
|
|
"URGENT: %s scrambling for emergency refuel!",
|
|
"PRIORITY: %s launching on expedited status!",
|
|
"EMERGENCY RESPONSE: %s airborne immediately!",
|
|
"URGENT LAUNCH: %s departing NOW!",
|
|
"EMERGENCY: %s getting up there ASAP!",
|
|
"PRIORITY STATUS: %s scrambling right now!",
|
|
"EMERGENCY TANKER: %s wheels up immediately!",
|
|
"URGENT: %s launching on priority!",
|
|
"SCRAMBLE: %s departing expedited!",
|
|
"EMERGENCY: %s getting airborne fast!",
|
|
"PRIORITY LAUNCH: %s launching NOW!",
|
|
"URGENT TANKER: %s scrambling immediately!",
|
|
"EMERGENCY: %s departing hot!",
|
|
"PRIORITY: %s wheels up ASAP!",
|
|
"URGENT LAUNCH: %s airborne right now!",
|
|
"EMERGENCY TANKER: %s launching immediately!",
|
|
"SCRAMBLE SCRAMBLE: %s getting up there now!",
|
|
"PRIORITY: %s launching on emergency status!",
|
|
"URGENT: %s departing immediately!",
|
|
"EMERGENCY: %s scrambling for urgent refuel!",
|
|
"PRIORITY LAUNCH: %s wheels up hot!",
|
|
"URGENT TANKER: %s airborne ASAP!",
|
|
"EMERGENCY: %s launching right now!",
|
|
"PRIORITY: %s scrambling expedited!",
|
|
"URGENT LAUNCH: %s departing NOW NOW NOW!",
|
|
"EMERGENCY TANKER: %s getting airborne fast!",
|
|
"PRIORITY STATUS: %s launching immediately!",
|
|
"URGENT: %s wheels up on priority!",
|
|
"EMERGENCY: %s scrambling now!",
|
|
"PRIORITY LAUNCH: %s departing fast!",
|
|
"URGENT TANKER: %s launching ASAP!",
|
|
"EMERGENCY: %s airborne immediately!",
|
|
"PRIORITY: %s scrambling hot!",
|
|
"URGENT LAUNCH: %s wheels up right now!",
|
|
"EMERGENCY TANKER: %s departing expedited!",
|
|
"PRIORITY: %s launching on urgent status!",
|
|
"URGENT: %s getting airborne now!",
|
|
"EMERGENCY SCRAMBLE: %s departing immediately!",
|
|
"PRIORITY TANKER: %s wheels up fast!",
|
|
"URGENT: %s launching right now!",
|
|
"EMERGENCY: %s airborne ASAP!",
|
|
"PRIORITY LAUNCH: %s scrambling now!",
|
|
"URGENT TANKER: %s departing hot!",
|
|
"EMERGENCY: %s wheels up immediately!",
|
|
"PRIORITY: %s getting airborne fast!",
|
|
"URGENT LAUNCH: %s scrambling ASAP!",
|
|
"EMERGENCY TANKER: %s launching on priority!",
|
|
"PRIORITY: %s departing right now!",
|
|
"URGENT: %s airborne expedited!",
|
|
"EMERGENCY: %s scrambling immediately!",
|
|
"PRIORITY LAUNCH: %s wheels up NOW!",
|
|
"URGENT TANKER: %s launching fast!",
|
|
"EMERGENCY: %s departing ASAP!",
|
|
"PRIORITY: %s airborne right now!",
|
|
"URGENT LAUNCH: %s scrambling hot!",
|
|
"EMERGENCY TANKER: %s wheels up expedited!",
|
|
"PRIORITY: %s launching immediately!",
|
|
"URGENT: %s getting airborne ASAP!",
|
|
"EMERGENCY: %s scrambling fast!",
|
|
"PRIORITY LAUNCH: %s departing NOW!",
|
|
"URGENT TANKER: %s wheels up right now!",
|
|
"EMERGENCY: %s airborne hot!",
|
|
"PRIORITY: %s scrambling ASAP!",
|
|
"URGENT LAUNCH: %s launching immediately!",
|
|
"EMERGENCY TANKER: %s departing fast!",
|
|
"PRIORITY: %s wheels up expedited!",
|
|
"URGENT: %s airborne NOW!",
|
|
"EMERGENCY: %s launching hot and fast!",
|
|
"PRIORITY LAUNCH: %s scrambling expedited!",
|
|
"URGENT TANKER: %s departing immediately!",
|
|
"EMERGENCY: %s wheels up ASAP!",
|
|
"PRIORITY: %s getting airborne now!",
|
|
"URGENT LAUNCH: %s airborne fast!",
|
|
"EMERGENCY TANKER: %s scrambling NOW!",
|
|
"PRIORITY: %s launching expedited!",
|
|
"URGENT: %s departing hot!",
|
|
"EMERGENCY: %s airborne immediately unlike Mo!",
|
|
"PRIORITY LAUNCH: %s wheels up faster than Mo!",
|
|
"URGENT TANKER: %s scrambling (Mo couldn't do this)!",
|
|
"EMERGENCY: %s launching while Mo watches!",
|
|
"PRIORITY: %s departing - Mo take notes!",
|
|
"URGENT LAUNCH: %s airborne (unlike Mo's attempts)!",
|
|
"EMERGENCY TANKER: %s scrambling successfully!",
|
|
"PRIORITY: %s wheels up for real!",
|
|
"URGENT: %s launching like professionals do!",
|
|
},
|
|
}
|
|
|
|
--- Get a random message from a category
|
|
--- @param category string Message category key
|
|
--- @param ... any Format arguments for string.format
|
|
--- @return string Formatted message
|
|
local function GetRandomMessage(category, ...)
|
|
local pool = TANKER_MESSAGES[category]
|
|
if not pool or #pool == 0 then
|
|
return "Message unavailable"
|
|
end
|
|
|
|
local template = pool[math.random(1, #pool)]
|
|
|
|
if select("#", ...) > 0 then
|
|
return string.format(template, ...)
|
|
else
|
|
return template
|
|
end
|
|
end
|
|
|
|
-- ============================================================================
|
|
-- UTILITY FUNCTIONS
|
|
-- ============================================================================
|
|
|
|
--- Update menu state based on tanker availability
|
|
local function UpdateTankerMenus()
|
|
if MENU_KC135_LAUNCH then
|
|
if TANKER_STATE.KC135.active then
|
|
MENU_KC135_LAUNCH:Remove()
|
|
MENU_KC135_LAUNCH = nil
|
|
elseif not MENU_KC135_LAUNCH then
|
|
MENU_KC135_LAUNCH = MENU_COALITION_COMMAND:New(
|
|
coalition.side.BLUE,
|
|
"Launch " .. TANKER_CONFIG.KC135.displayName,
|
|
MENU_TANKER_ROOT,
|
|
SpawnTanker
|
|
)
|
|
end
|
|
end
|
|
|
|
if MENU_KC135_MPRS_LAUNCH then
|
|
if TANKER_STATE.KC135_MPRS.active then
|
|
MENU_KC135_MPRS_LAUNCH:Remove()
|
|
MENU_KC135_MPRS_LAUNCH = nil
|
|
elseif not MENU_KC135_MPRS_LAUNCH then
|
|
MENU_KC135_MPRS_LAUNCH = MENU_COALITION_COMMAND:New(
|
|
coalition.side.BLUE,
|
|
"Launch " .. TANKER_CONFIG.KC135_MPRS.displayName,
|
|
MENU_TANKER_ROOT,
|
|
SpawnTankerMPRS
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Announce tanker information to coalition
|
|
local function AnnounceTankerInfo(config, spawned)
|
|
local msg = GetRandomMessage("SPAWN_SUCCESS", config.displayName) .. "\n"
|
|
|
|
if config.tacan then
|
|
msg = msg .. string.format("TACAN: %s\n", config.tacan)
|
|
end
|
|
|
|
if config.frequency then
|
|
msg = msg .. string.format("Radio: %s MHz", config.frequency)
|
|
end
|
|
|
|
MESSAGE:New(msg, 20):ToBlue()
|
|
env.info(string.format("[TANKER] %s spawned successfully", config.displayName))
|
|
end
|
|
|
|
--- Monitor tanker fuel levels
|
|
local function MonitorTankerFuel(stateKey, config)
|
|
return function()
|
|
local state = TANKER_STATE[stateKey]
|
|
|
|
if not state.active or not state.group then
|
|
return
|
|
end
|
|
|
|
-- Check if group still exists
|
|
if not state.group:IsAlive() then
|
|
return
|
|
end
|
|
|
|
local fuelPercent = state.group:GetFuel() * 100
|
|
|
|
-- Bingo fuel check
|
|
if fuelPercent <= config.fuelBingoPercent and not state.bingoWarned then
|
|
MESSAGE:New(GetRandomMessage("BINGO_FUEL", config.displayName), 15, "WARNING"):ToBlue()
|
|
state.bingoWarned = true
|
|
env.info(string.format("[TANKER] %s bingo fuel: %.1f%%", config.displayName, fuelPercent))
|
|
|
|
-- Low fuel warning
|
|
elseif fuelPercent <= config.fuelWarningPercent and not state.fuelWarned then
|
|
MESSAGE:New(GetRandomMessage("LOW_FUEL", config.displayName, math.floor(fuelPercent)), 15):ToBlue()
|
|
state.fuelWarned = true
|
|
env.info(string.format("[TANKER] %s low fuel warning: %.1f%%", config.displayName, fuelPercent))
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Start (or restart) the fuel monitor scheduler for a tanker
|
|
local function StartFuelMonitor(stateKey, config)
|
|
local state = TANKER_STATE[stateKey]
|
|
if not state then
|
|
return
|
|
end
|
|
|
|
if state.fuelMonitor then
|
|
state.fuelMonitor:Stop()
|
|
state.fuelMonitor = nil
|
|
end
|
|
|
|
state.fuelMonitor = SCHEDULER:New(
|
|
nil,
|
|
MonitorTankerFuel(stateKey, config),
|
|
{},
|
|
FUEL_CHECK_INTERVAL,
|
|
FUEL_CHECK_INTERVAL
|
|
)
|
|
end
|
|
|
|
--- Schedule auto-respawn after tanker loss
|
|
local function ScheduleRespawn(stateKey, config, spawnFunc)
|
|
local state = TANKER_STATE[stateKey]
|
|
|
|
-- Cancel existing respawn if any
|
|
if state.respawnScheduler then
|
|
state.respawnScheduler:Stop()
|
|
end
|
|
|
|
local countdown = config.respawnDelay
|
|
|
|
MESSAGE:New(string.format("%s will respawn in %d seconds",
|
|
config.displayName, countdown), 10):ToBlue()
|
|
|
|
-- Respawn scheduler
|
|
state.respawnScheduler = SCHEDULER:New(nil, function()
|
|
env.info(string.format("[TANKER] Auto-respawning %s", config.displayName))
|
|
spawnFunc()
|
|
end, {}, config.respawnDelay)
|
|
end
|
|
|
|
--- Clean up tanker state
|
|
local function CleanupTankerState(stateKey)
|
|
local state = TANKER_STATE[stateKey]
|
|
|
|
state.active = false
|
|
state.group = nil
|
|
state.fuelWarned = false
|
|
state.bingoWarned = false
|
|
|
|
if state.fuelMonitor then
|
|
state.fuelMonitor:Stop()
|
|
state.fuelMonitor = nil
|
|
end
|
|
|
|
if state.respawnScheduler then
|
|
state.respawnScheduler:Stop()
|
|
state.respawnScheduler = nil
|
|
end
|
|
end
|
|
|
|
-- ============================================================================
|
|
-- CUSTOM ROUTE FUNCTIONS
|
|
-- ============================================================================
|
|
|
|
--- Parse waypoint marker text for altitude and speed overrides
|
|
--- Supports formats: SHELL1, SHELL1:FL220, SHELL1:FL220:SP330, SHELL1::SP300, SHELL1:RTB
|
|
--- @param markerText string The text from the map marker
|
|
--- @param defaultAlt number Default altitude in feet
|
|
--- @param defaultSpeed number Default speed in knots
|
|
--- @return table Parsed waypoint data {altitude, speed, rtb, isValid}
|
|
local function ParseWaypointMarker(markerText, defaultAlt, defaultSpeed)
|
|
local result = {
|
|
altitude = defaultAlt,
|
|
speed = defaultSpeed,
|
|
rtb = false,
|
|
isValid = true,
|
|
originalText = markerText
|
|
}
|
|
|
|
-- Split by colon
|
|
local parts = {}
|
|
for part in string.gmatch(markerText, "[^:]+") do
|
|
table.insert(parts, part)
|
|
end
|
|
|
|
-- Check for RTB command
|
|
for _, part in ipairs(parts) do
|
|
if string.upper(part) == "RTB" then
|
|
result.rtb = true
|
|
return result
|
|
end
|
|
end
|
|
|
|
-- Parse FL (Flight Level)
|
|
for _, part in ipairs(parts) do
|
|
local fl = string.match(part, "FL(%d+)")
|
|
if fl then
|
|
result.altitude = tonumber(fl) * 100 -- Convert FL to feet
|
|
end
|
|
end
|
|
|
|
-- Parse SP (Speed)
|
|
for _, part in ipairs(parts) do
|
|
local sp = string.match(part, "SP(%d+)")
|
|
if sp then
|
|
result.speed = tonumber(sp)
|
|
end
|
|
end
|
|
|
|
return result
|
|
end
|
|
|
|
--- Scan map for waypoint markers matching callsign pattern
|
|
--- @param callsign string The callsign prefix to search for (e.g., "SHELL", "ARCO")
|
|
--- @return table Array of waypoint data sorted by sequence number
|
|
local function ScanForWaypointMarkers(callsign)
|
|
local waypoints = {}
|
|
local markerIds = {}
|
|
|
|
-- Iterate through all possible marker IDs (DCS markers are numbered)
|
|
-- We'll scan up to 1000 markers (should be more than enough)
|
|
for i = 1, 1000 do
|
|
local markerData = world.getMarkPanels()
|
|
if markerData and markerData[i] then
|
|
local marker = markerData[i]
|
|
local markerText = marker.text
|
|
|
|
if markerText then
|
|
-- Check if marker matches pattern: CALLSIGN + number
|
|
local upperText = string.upper(markerText)
|
|
local upperCallsign = string.upper(callsign)
|
|
local sequence = string.match(upperText, "^" .. upperCallsign .. "(%d+)")
|
|
|
|
if sequence then
|
|
local seqNum = tonumber(sequence)
|
|
local pos = marker.pos
|
|
|
|
table.insert(waypoints, {
|
|
sequence = seqNum,
|
|
coordinate = COORDINATE:NewFromVec3(pos),
|
|
markerId = marker.idx,
|
|
markerText = markerText
|
|
})
|
|
|
|
table.insert(markerIds, marker.idx)
|
|
|
|
env.info(string.format("[TANKER] Found waypoint marker: %s at seq %d (ID: %d)",
|
|
markerText, seqNum, marker.idx))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Sort by sequence number
|
|
table.sort(waypoints, function(a, b) return a.sequence < b.sequence end)
|
|
|
|
return waypoints, markerIds
|
|
end
|
|
|
|
-- ============================================================================
|
|
|
|
--- Spawn a tanker directly from config (no Mission Editor template required)
|
|
--- @param config table Tanker configuration
|
|
--- @param coord COORDINATE Where to spawn
|
|
--- @param heading number Initial heading in degrees
|
|
--- @return GROUP The spawned tanker group
|
|
local function SpawnTankerFromConfig(config, coord, heading)
|
|
-- Generate unique group/unit IDs
|
|
local groupId = math.random(10000, 99999)
|
|
local unitId = math.random(10000, 99999)
|
|
|
|
-- Ensure we have valid altitude (coord.y is altitude in meters MSL)
|
|
local spawnAlt = coord.y
|
|
env.info(string.format("[TANKER] Spawn altitude: %.1f meters (FL%03d)", spawnAlt, spawnAlt * 3.28084 / 100))
|
|
|
|
-- Create group data structure
|
|
local groupData = {
|
|
["visible"] = false,
|
|
["taskSelected"] = true,
|
|
["route"] = {
|
|
["points"] = {
|
|
[1] = {
|
|
["alt"] = spawnAlt,
|
|
["type"] = "Turning Point",
|
|
["action"] = "Turning Point",
|
|
["alt_type"] = "BARO",
|
|
["speed"] = config.defaultSpeed * 0.514444,
|
|
["task"] = {
|
|
["id"] = "ComboTask",
|
|
["params"] = {
|
|
["tasks"] = {
|
|
[1] = {
|
|
["id"] = "Tanker",
|
|
["params"] = {}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
["x"] = coord.x,
|
|
["y"] = coord.z,
|
|
}
|
|
}
|
|
},
|
|
["hidden"] = false,
|
|
["units"] = {
|
|
[1] = {
|
|
["alt"] = spawnAlt,
|
|
["alt_type"] = "BARO",
|
|
["livery_id"] = config.livery,
|
|
["skill"] = "High",
|
|
["speed"] = config.defaultSpeed * 0.514444,
|
|
["type"] = config.aircraftType,
|
|
["unitId"] = unitId,
|
|
["psi"] = -heading, -- Negative for correct heading
|
|
["unitName"] = config.unitName,
|
|
["x"] = coord.x,
|
|
["y"] = coord.z,
|
|
["heading"] = math.rad(heading),
|
|
["onboard_num"] = "010",
|
|
},
|
|
},
|
|
["groupId"] = groupId,
|
|
["y"] = coord.z,
|
|
["x"] = coord.x,
|
|
["name"] = config.groupName,
|
|
["task"] = "Refueling",
|
|
}
|
|
|
|
-- Spawn the group using coalition.addGroup
|
|
local spawnedGroup = coalition.addGroup(country.id.USA, Group.Category.AIRPLANE, groupData)
|
|
|
|
if spawnedGroup then
|
|
env.info(string.format("[TANKER] Spawned %s (ID: %d)", config.groupName, groupId))
|
|
return GROUP:Find(spawnedGroup:getName())
|
|
else
|
|
env.error(string.format("[TANKER] Failed to spawn %s", config.groupName))
|
|
return nil
|
|
end
|
|
end
|
|
|
|
--- Ensure default spawns immediately enter a holding pattern so they do not RTB
|
|
--- @param group GROUP The spawned tanker group
|
|
--- @param coord COORDINATE Center point for the orbit
|
|
--- @param config table Tanker configuration for speed/altitude
|
|
local function ApplyDefaultOrbitRoute(group, coord, config)
|
|
if not group or not coord or not config then
|
|
return
|
|
end
|
|
|
|
local orbitCenter = coord:SetAltitude(config.defaultAltitude * 0.3048, true)
|
|
local orbitWP = orbitCenter:WaypointAirTurningPoint(
|
|
COORDINATE.WaypointAltType.BARO,
|
|
config.defaultSpeed * 0.514444,
|
|
config.defaultAltitude * 0.3048,
|
|
{},
|
|
"DEFAULT-ORBIT"
|
|
)
|
|
|
|
orbitWP.task = {
|
|
id = "ComboTask",
|
|
params = {
|
|
tasks = {
|
|
{
|
|
id = "Tanker",
|
|
params = {}
|
|
},
|
|
{
|
|
id = "Orbit",
|
|
params = {
|
|
pattern = "Circle",
|
|
speed = config.defaultSpeed * 0.514444,
|
|
altitude = config.defaultAltitude * 0.3048,
|
|
point = {
|
|
x = orbitCenter.x,
|
|
y = orbitCenter.z
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
group:Route({ orbitWP })
|
|
env.info(string.format("[TANKER] Applied default orbit for %s", config.displayName))
|
|
end
|
|
|
|
--- Create custom route tanker spawn
|
|
--- @param callsign string Callsign prefix used for markers
|
|
--- @param config table Tanker configuration
|
|
--- @param stateKey string State key for tracking
|
|
--- @param isEmergency boolean Whether this is an emergency spawn
|
|
--- @return boolean Success status
|
|
local function SpawnCustomRouteTanker(callsign, config, stateKey, isEmergency)
|
|
local state = TANKER_STATE[stateKey]
|
|
|
|
-- Check if already active
|
|
if state.active then
|
|
MESSAGE:New(GetRandomMessage("ALREADY_ACTIVE", config.displayName), 10):ToBlue()
|
|
return false
|
|
end
|
|
|
|
-- Scan for waypoint markers
|
|
local waypoints, markerIds = ScanForWaypointMarkers(callsign)
|
|
|
|
-- Validate waypoint count
|
|
if #waypoints < ROUTE_CONFIG.minWaypoints then
|
|
MESSAGE:New(GetRandomMessage("TOO_FEW_WAYPOINTS",
|
|
ROUTE_CONFIG.minWaypoints, callsign, callsign), 15, "ERROR"):ToBlue()
|
|
return false
|
|
end
|
|
|
|
if #waypoints > ROUTE_CONFIG.maxWaypoints then
|
|
MESSAGE:New(GetRandomMessage("TOO_MANY_WAYPOINTS",
|
|
ROUTE_CONFIG.maxWaypoints), 15, "ERROR"):ToBlue()
|
|
return false
|
|
end
|
|
|
|
-- Build route description and validate waypoints
|
|
local routeDesc = ""
|
|
local routePoints = {}
|
|
local hasRTB = false
|
|
|
|
for i, wp in ipairs(waypoints) do
|
|
local parsed = ParseWaypointMarker(wp.markerText, config.defaultAltitude, config.defaultSpeed)
|
|
|
|
if parsed.rtb then
|
|
hasRTB = true
|
|
-- Find nearest friendly airbase from last waypoint position
|
|
local lastPos = #routePoints > 0 and routePoints[#routePoints].coord or wp.coordinate
|
|
local nearestAirbase = lastPos:GetClosestAirbase(Airbase.Category.AIRDROME, coalition.side.BLUE)
|
|
|
|
if nearestAirbase then
|
|
local airbaseName = nearestAirbase:GetName()
|
|
local airbaseCoord = nearestAirbase:GetCoordinate()
|
|
routeDesc = routeDesc .. string.format("\n WP%d: RTB to %s", i, airbaseName)
|
|
|
|
table.insert(routePoints, {
|
|
coord = airbaseCoord,
|
|
altitude = 0, -- Will land
|
|
speed = parsed.speed,
|
|
rtb = true,
|
|
airbase = nearestAirbase,
|
|
airbaseName = airbaseName
|
|
})
|
|
|
|
env.info(string.format("[TANKER] RTB destination: %s", airbaseName))
|
|
else
|
|
routeDesc = routeDesc .. string.format("\n WP%d: RTB (no airbase found)", i)
|
|
env.warning("[TANKER] No friendly airbase found for RTB")
|
|
end
|
|
break -- RTB is terminal command
|
|
else
|
|
routeDesc = routeDesc .. string.format("\n WP%d: FL%03d @ %d kts",
|
|
i, math.floor(parsed.altitude / 100), parsed.speed)
|
|
|
|
table.insert(routePoints, {
|
|
coord = wp.coordinate,
|
|
altitude = parsed.altitude,
|
|
speed = parsed.speed,
|
|
rtb = false
|
|
})
|
|
end
|
|
end
|
|
|
|
-- Confirm route to player
|
|
local emergencyText = isEmergency and " [EMERGENCY]" or ""
|
|
local routeMsg = GetRandomMessage("ROUTE_ACCEPTED", config.displayName, #routePoints, routeDesc)
|
|
if isEmergency then
|
|
routeMsg = GetRandomMessage("EMERGENCY_SPAWN", config.displayName) .. "\n" .. routeMsg
|
|
end
|
|
MESSAGE:New(routeMsg, 20):ToBlue()
|
|
|
|
env.info(string.format("[TANKER] Spawning %s with custom route: %d waypoints",
|
|
config.displayName, #routePoints))
|
|
|
|
-- Debug: log route point data
|
|
for i, rp in ipairs(routePoints) do
|
|
env.info(string.format("[TANKER] RoutePoint %d: coord=%s, alt=%.0f, spd=%.0f, rtb=%s",
|
|
i, tostring(rp.coord), rp.altitude, rp.speed, tostring(rp.rtb)))
|
|
end
|
|
|
|
-- Delete markers if configured
|
|
if ROUTE_CONFIG.deleteMarkersAfterUse then
|
|
for _, markerId in ipairs(markerIds) do
|
|
trigger.action.removeMark(markerId)
|
|
end
|
|
env.info(string.format("[TANKER] Deleted %d waypoint markers", #markerIds))
|
|
end
|
|
|
|
-- Spawn tanker with custom route
|
|
-- Calculate initial heading
|
|
local headingCoord
|
|
if routePoints[2] and routePoints[2].coord then
|
|
headingCoord = routePoints[2].coord
|
|
else
|
|
headingCoord = routePoints[1].coord
|
|
end
|
|
|
|
local initialHeading = routePoints[1].coord:HeadingTo(headingCoord)
|
|
|
|
-- Set the spawn coordinate with correct altitude (convert feet to meters)
|
|
local spawnCoord = routePoints[1].coord:SetAltitude(routePoints[1].altitude * 0.3048)
|
|
|
|
local spawnedGroup = SpawnTankerFromConfig(
|
|
config,
|
|
spawnCoord,
|
|
initialHeading
|
|
)
|
|
|
|
if not spawnedGroup then
|
|
MESSAGE:New(GetRandomMessage("SPAWN_FAILURE", config.displayName), 10, "ERROR"):ToBlue()
|
|
return false
|
|
end
|
|
|
|
-- Route the group through all waypoints
|
|
local taskRoute = {}
|
|
for i, rp in ipairs(routePoints) do
|
|
local wp
|
|
|
|
-- RTB waypoint - land at airbase
|
|
if rp.rtb and rp.airbase then
|
|
wp = rp.coord:WaypointAirLanding(
|
|
rp.speed * 0.514444,
|
|
rp.airbase:GetDCSObject(),
|
|
{},
|
|
"RTB"
|
|
)
|
|
else
|
|
-- Normal waypoint
|
|
wp = rp.coord:WaypointAirFlyOverPoint(
|
|
COORDINATE.WaypointAltType.BARO,
|
|
rp.speed * 0.514444, -- Convert knots to m/s
|
|
rp.altitude * 0.3048, -- Convert feet to meters
|
|
{},
|
|
"WP" .. i
|
|
)
|
|
|
|
-- Add tanker task to all waypoints
|
|
wp.task = {
|
|
id = "ComboTask",
|
|
params = {
|
|
tasks = {
|
|
{
|
|
id = "Tanker",
|
|
params = {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
end
|
|
|
|
table.insert(taskRoute, wp)
|
|
end
|
|
|
|
-- If last waypoint is not RTB, loop back to first waypoint to create continuous patrol
|
|
if not hasRTB and #routePoints > 1 then
|
|
local firstPoint = routePoints[1]
|
|
local loopWP = firstPoint.coord:WaypointAirFlyOverPoint(
|
|
COORDINATE.WaypointAltType.BARO,
|
|
firstPoint.speed * 0.514444,
|
|
firstPoint.altitude * 0.3048,
|
|
{},
|
|
"LOOP-WP1"
|
|
)
|
|
|
|
-- Add tanker task to loop waypoint
|
|
loopWP.task = {
|
|
id = "ComboTask",
|
|
params = {
|
|
tasks = {
|
|
{
|
|
id = "Tanker",
|
|
params = {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
table.insert(taskRoute, loopWP)
|
|
env.info(string.format("[TANKER] Added loop waypoint back to WP1 for continuous patrol"))
|
|
elseif not hasRTB and #routePoints == 1 then
|
|
-- Single waypoint - add circular orbit pattern
|
|
local singlePoint = routePoints[1]
|
|
local orbitWP = singlePoint.coord:WaypointAirTurningPoint(
|
|
COORDINATE.WaypointAltType.BARO,
|
|
singlePoint.speed * 0.514444,
|
|
singlePoint.altitude * 0.3048,
|
|
{},
|
|
"ORBIT"
|
|
)
|
|
orbitWP.task = {
|
|
id = "ComboTask",
|
|
params = {
|
|
tasks = {
|
|
{
|
|
id = "Tanker",
|
|
params = {}
|
|
},
|
|
{
|
|
id = "Orbit",
|
|
params = {
|
|
pattern = "Circle",
|
|
speed = singlePoint.speed * 0.514444,
|
|
altitude = singlePoint.altitude * 0.3048
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
table.insert(taskRoute, orbitWP)
|
|
env.info(string.format("[TANKER] Single waypoint - added circular orbit pattern"))
|
|
end
|
|
|
|
-- Apply route to group
|
|
spawnedGroup:Route(taskRoute)
|
|
|
|
-- Update state
|
|
local state = TANKER_STATE[stateKey]
|
|
state.active = true
|
|
state.group = spawnedGroup
|
|
state.fuelWarned = false
|
|
state.bingoWarned = false
|
|
|
|
-- Announce spawn with details
|
|
AnnounceTankerInfo(config, true)
|
|
|
|
-- Start fuel monitoring
|
|
StartFuelMonitor(stateKey, config)
|
|
|
|
-- Update menus
|
|
UpdateTankerMenus()
|
|
|
|
return true
|
|
end
|
|
|
|
-- ============================================================================
|
|
-- EVENT HANDLER
|
|
-- ============================================================================
|
|
|
|
BlueTankerEventHandler = EVENTHANDLER:New()
|
|
|
|
function BlueTankerEventHandler:OnEventBirth(EventData)
|
|
local groupName = EventData.IniDCSGroupName
|
|
|
|
if groupName and string.find(groupName, "TANKER 135") then
|
|
env.info(string.format("[TANKER] Birth event: %s", groupName))
|
|
|
|
-- Determine which tanker spawned
|
|
local stateKey, config
|
|
if string.find(groupName, "MPRS") then
|
|
stateKey = "KC135_MPRS"
|
|
config = TANKER_CONFIG.KC135_MPRS
|
|
else
|
|
stateKey = "KC135"
|
|
config = TANKER_CONFIG.KC135
|
|
end
|
|
|
|
-- Update state
|
|
local state = TANKER_STATE[stateKey]
|
|
state.active = true
|
|
state.group = GROUP:FindByName(groupName)
|
|
state.fuelWarned = false
|
|
state.bingoWarned = false
|
|
|
|
-- Announce spawn with details
|
|
AnnounceTankerInfo(config, true)
|
|
|
|
-- Start fuel monitoring
|
|
StartFuelMonitor(stateKey, config)
|
|
|
|
-- Update menus
|
|
UpdateTankerMenus()
|
|
end
|
|
end
|
|
|
|
function BlueTankerEventHandler:OnEventDead(EventData)
|
|
local groupName = EventData.IniDCSGroupName
|
|
|
|
if groupName and string.find(groupName, "TANKER 135") then
|
|
env.info(string.format("[TANKER] Dead event: %s", groupName))
|
|
|
|
-- Determine which tanker died
|
|
local stateKey, config, spawnFunc
|
|
if string.find(groupName, "MPRS") then
|
|
stateKey = "KC135_MPRS"
|
|
config = TANKER_CONFIG.KC135_MPRS
|
|
spawnFunc = SpawnTankerMPRS
|
|
else
|
|
stateKey = "KC135"
|
|
config = TANKER_CONFIG.KC135
|
|
spawnFunc = SpawnTanker
|
|
end
|
|
|
|
MESSAGE:New(GetRandomMessage("DESTROYED", config.displayName),
|
|
15, "ALERT"):ToBlue()
|
|
|
|
-- Clean up and schedule respawn
|
|
CleanupTankerState(stateKey)
|
|
ScheduleRespawn(stateKey, config, spawnFunc)
|
|
|
|
-- Update menus
|
|
UpdateTankerMenus()
|
|
end
|
|
end
|
|
|
|
function BlueTankerEventHandler:OnEventCrash(EventData)
|
|
-- Treat crash same as dead
|
|
self:OnEventDead(EventData)
|
|
end
|
|
|
|
function BlueTankerEventHandler:OnEventEngineShutdown(EventData)
|
|
local groupName = EventData.IniDCSGroupName
|
|
|
|
if groupName and string.find(groupName, "TANKER 135") then
|
|
env.info(string.format("[TANKER] Engine shutdown event: %s", groupName))
|
|
|
|
-- Determine which tanker
|
|
local stateKey, config, spawnFunc
|
|
if string.find(groupName, "MPRS") then
|
|
stateKey = "KC135_MPRS"
|
|
config = TANKER_CONFIG.KC135_MPRS
|
|
spawnFunc = SpawnTankerMPRS
|
|
else
|
|
stateKey = "KC135"
|
|
config = TANKER_CONFIG.KC135
|
|
spawnFunc = SpawnTanker
|
|
end
|
|
|
|
MESSAGE:New(string.format("%s has returned to base", config.displayName),
|
|
10):ToBlue()
|
|
|
|
-- Clean up and schedule respawn
|
|
CleanupTankerState(stateKey)
|
|
ScheduleRespawn(stateKey, config, spawnFunc)
|
|
|
|
-- Update menus
|
|
UpdateTankerMenus()
|
|
end
|
|
end
|
|
|
|
function BlueTankerEventHandler:OnEventHit(EventData)
|
|
local groupName = EventData.IniDCSGroupName
|
|
|
|
if groupName and string.find(groupName, "TANKER 135") then
|
|
local config = string.find(groupName, "MPRS") and TANKER_CONFIG.KC135_MPRS or TANKER_CONFIG.KC135
|
|
|
|
MESSAGE:New(GetRandomMessage("TAKING_FIRE", config.displayName),
|
|
15, "WARNING"):ToBlue()
|
|
|
|
env.info(string.format("[TANKER] %s hit by hostile fire", config.displayName))
|
|
end
|
|
end
|
|
|
|
-- ============================================================================
|
|
-- SPAWN OBJECTS AND FUNCTIONS
|
|
-- ============================================================================
|
|
|
|
-- Function to spawn KC-135
|
|
function SpawnTanker()
|
|
if TANKER_STATE.KC135.active then
|
|
MESSAGE:New(GetRandomMessage("ALREADY_ACTIVE", TANKER_CONFIG.KC135.displayName), 10):ToBlue()
|
|
return
|
|
end
|
|
|
|
env.info("[TANKER] Spawning KC-135")
|
|
local spawnedGroup = SpawnTankerFromConfig(
|
|
TANKER_CONFIG.KC135,
|
|
DEFAULT_SPAWN_COORD,
|
|
0 -- heading north
|
|
)
|
|
|
|
if spawnedGroup then
|
|
ApplyDefaultOrbitRoute(spawnedGroup, DEFAULT_SPAWN_COORD, TANKER_CONFIG.KC135)
|
|
TANKER_STATE.KC135.active = true
|
|
TANKER_STATE.KC135.group = spawnedGroup
|
|
AnnounceTankerInfo(TANKER_CONFIG.KC135, true)
|
|
|
|
-- Start fuel monitoring
|
|
StartFuelMonitor("KC135", TANKER_CONFIG.KC135)
|
|
|
|
UpdateTankerMenus()
|
|
else
|
|
MESSAGE:New(GetRandomMessage("SPAWN_FAILURE", TANKER_CONFIG.KC135.displayName), 10, "ERROR"):ToBlue()
|
|
end
|
|
end
|
|
|
|
-- Function to spawn KC-135 MPRS
|
|
function SpawnTankerMPRS()
|
|
if TANKER_STATE.KC135_MPRS.active then
|
|
MESSAGE:New(GetRandomMessage("ALREADY_ACTIVE", TANKER_CONFIG.KC135_MPRS.displayName), 10):ToBlue()
|
|
return
|
|
end
|
|
|
|
env.info("[TANKER] Spawning KC-135 MPRS")
|
|
local spawnedGroup = SpawnTankerFromConfig(
|
|
TANKER_CONFIG.KC135_MPRS,
|
|
DEFAULT_SPAWN_COORD,
|
|
0 -- heading north
|
|
)
|
|
|
|
if spawnedGroup then
|
|
ApplyDefaultOrbitRoute(spawnedGroup, DEFAULT_SPAWN_COORD, TANKER_CONFIG.KC135_MPRS)
|
|
TANKER_STATE.KC135_MPRS.active = true
|
|
TANKER_STATE.KC135_MPRS.group = spawnedGroup
|
|
AnnounceTankerInfo(TANKER_CONFIG.KC135_MPRS, true)
|
|
|
|
-- Start fuel monitoring
|
|
StartFuelMonitor("KC135_MPRS", TANKER_CONFIG.KC135_MPRS)
|
|
|
|
UpdateTankerMenus()
|
|
else
|
|
MESSAGE:New(GetRandomMessage("SPAWN_FAILURE", TANKER_CONFIG.KC135_MPRS.displayName), 10, "ERROR"):ToBlue()
|
|
end
|
|
end
|
|
|
|
-- Function to spawn KC-135 with custom route
|
|
function SpawnCustomTanker()
|
|
SpawnCustomRouteTanker(
|
|
TANKER_CONFIG.KC135.callsign,
|
|
TANKER_CONFIG.KC135,
|
|
"KC135",
|
|
false
|
|
)
|
|
end
|
|
|
|
-- Function to spawn KC-135 MPRS with custom route
|
|
function SpawnCustomTankerMPRS()
|
|
SpawnCustomRouteTanker(
|
|
TANKER_CONFIG.KC135_MPRS.callsign,
|
|
TANKER_CONFIG.KC135_MPRS,
|
|
"KC135_MPRS",
|
|
false
|
|
)
|
|
end
|
|
|
|
-- Function to spawn emergency KC-135 with custom route
|
|
function SpawnEmergencyTanker()
|
|
-- Use emergency respawn delay
|
|
local originalDelay = TANKER_CONFIG.KC135.respawnDelay
|
|
TANKER_CONFIG.KC135.respawnDelay = TANKER_CONFIG.KC135.emergencyRespawnDelay
|
|
|
|
local success = SpawnCustomRouteTanker(
|
|
TANKER_CONFIG.KC135.callsign,
|
|
TANKER_CONFIG.KC135,
|
|
"KC135",
|
|
true
|
|
)
|
|
|
|
-- Restore original delay
|
|
TANKER_CONFIG.KC135.respawnDelay = originalDelay
|
|
|
|
return success
|
|
end
|
|
|
|
-- Function to spawn emergency KC-135 MPRS with custom route
|
|
function SpawnEmergencyTankerMPRS()
|
|
local originalDelay = TANKER_CONFIG.KC135_MPRS.respawnDelay
|
|
TANKER_CONFIG.KC135_MPRS.respawnDelay = TANKER_CONFIG.KC135_MPRS.emergencyRespawnDelay
|
|
|
|
local success = SpawnCustomRouteTanker(
|
|
TANKER_CONFIG.KC135_MPRS.callsign,
|
|
TANKER_CONFIG.KC135_MPRS,
|
|
"KC135_MPRS",
|
|
true
|
|
)
|
|
|
|
TANKER_CONFIG.KC135_MPRS.respawnDelay = originalDelay
|
|
|
|
return success
|
|
end
|
|
|
|
-- Function to display tanker status
|
|
function ShowTankerStatus()
|
|
local msg = "=== TANKER STATUS ===\n\n"
|
|
|
|
-- KC-135 Status
|
|
local kc135State = TANKER_STATE.KC135
|
|
if kc135State.active and kc135State.group and kc135State.group:IsAlive() then
|
|
local fuel = kc135State.group:GetFuel() * 100
|
|
local coord = kc135State.group:GetCoordinate()
|
|
local alt = coord:GetLandHeight() + coord.y
|
|
msg = msg .. string.format("%s: ACTIVE\n", TANKER_CONFIG.KC135.displayName)
|
|
msg = msg .. string.format(" Fuel: %.0f%%\n", fuel)
|
|
msg = msg .. string.format(" Altitude: FL%03d\n", math.floor(alt * 3.28084 / 100))
|
|
if TANKER_CONFIG.KC135.tacan then
|
|
msg = msg .. string.format(" TACAN: %s\n", TANKER_CONFIG.KC135.tacan)
|
|
end
|
|
if TANKER_CONFIG.KC135.frequency then
|
|
msg = msg .. string.format(" Radio: %s MHz\n", TANKER_CONFIG.KC135.frequency)
|
|
end
|
|
else
|
|
msg = msg .. string.format("%s: NOT ACTIVE\n", TANKER_CONFIG.KC135.displayName)
|
|
end
|
|
|
|
msg = msg .. "\n"
|
|
|
|
-- KC-135 MPRS Status
|
|
local mprsState = TANKER_STATE.KC135_MPRS
|
|
if mprsState.active and mprsState.group and mprsState.group:IsAlive() then
|
|
local fuel = mprsState.group:GetFuel() * 100
|
|
local coord = mprsState.group:GetCoordinate()
|
|
local alt = coord:GetLandHeight() + coord.y
|
|
msg = msg .. string.format("%s: ACTIVE\n", TANKER_CONFIG.KC135_MPRS.displayName)
|
|
msg = msg .. string.format(" Fuel: %.0f%%\n", fuel)
|
|
msg = msg .. string.format(" Altitude: FL%03d\n", math.floor(alt * 3.28084 / 100))
|
|
if TANKER_CONFIG.KC135_MPRS.tacan then
|
|
msg = msg .. string.format(" TACAN: %s\n", TANKER_CONFIG.KC135_MPRS.tacan)
|
|
end
|
|
if TANKER_CONFIG.KC135_MPRS.frequency then
|
|
msg = msg .. string.format(" Radio: %s MHz\n", TANKER_CONFIG.KC135_MPRS.frequency)
|
|
end
|
|
else
|
|
msg = msg .. string.format("%s: NOT ACTIVE\n", TANKER_CONFIG.KC135_MPRS.displayName)
|
|
end
|
|
|
|
MESSAGE:New(msg, 25):ToBlue()
|
|
end
|
|
|
|
-- Function to show custom route help
|
|
function ShowCustomRouteHelp()
|
|
local msg = "╔════════════════════════════════════════════╗\n"
|
|
msg = msg .. "║ TANKER MANAGEMENT SYSTEM - GUIDE ║\n"
|
|
msg = msg .. "╚════════════════════════════════════════════╝\n\n"
|
|
|
|
msg = msg .. "━━━ QUICK START ━━━\n\n"
|
|
msg = msg .. "1. SIMPLE SPAWN:\n"
|
|
msg = msg .. " • F10 → Tanker Management → Launch KC-135\n"
|
|
msg = msg .. " • Tanker spawns at default location (FL220)\n"
|
|
msg = msg .. " • Automatically orbits and provides refueling\n\n"
|
|
|
|
msg = msg .. "2. CUSTOM ROUTE SPAWN:\n"
|
|
msg = msg .. " • Place numbered F10 map markers\n"
|
|
msg = msg .. " • Launch from Custom Route menu\n"
|
|
msg = msg .. " • Tanker follows your waypoints\n\n"
|
|
|
|
msg = msg .. "━━━ AVAILABLE TANKERS ━━━\n\n"
|
|
msg = msg .. string.format("• %s (SHELL)\n", TANKER_CONFIG.KC135.displayName)
|
|
msg = msg .. string.format(" TACAN: %s | Radio: %s MHz\n",
|
|
TANKER_CONFIG.KC135.tacan or "N/A", TANKER_CONFIG.KC135.frequency or "N/A")
|
|
msg = msg .. string.format(" Marker Prefix: %s\n\n", TANKER_CONFIG.KC135.callsign)
|
|
|
|
msg = msg .. string.format("• %s (ARCO)\n", TANKER_CONFIG.KC135_MPRS.displayName)
|
|
msg = msg .. string.format(" TACAN: %s | Radio: %s MHz\n",
|
|
TANKER_CONFIG.KC135_MPRS.tacan or "N/A", TANKER_CONFIG.KC135_MPRS.frequency or "N/A")
|
|
msg = msg .. string.format(" Marker Prefix: %s\n\n", TANKER_CONFIG.KC135_MPRS.callsign)
|
|
|
|
msg = msg .. "━━━ CUSTOM ROUTE MARKERS ━━━\n\n"
|
|
msg = msg .. "BASIC USAGE:\n"
|
|
msg = msg .. " Place markers in sequence: SHELL1, SHELL2, SHELL3\n"
|
|
msg = msg .. " Minimum 2 waypoints required\n"
|
|
msg = msg .. " Defaults: FL220 @ 330 knots\n\n"
|
|
|
|
msg = msg .. "ADVANCED SYNTAX:\n"
|
|
msg = msg .. " SHELL1:FL180 → Altitude override\n"
|
|
msg = msg .. " SHELL2::SP300 → Speed override\n"
|
|
msg = msg .. " SHELL3:FL200:SP280 → Both overrides\n"
|
|
msg = msg .. " SHELL4:RTB → Return to nearest base\n\n"
|
|
|
|
msg = msg .. "EXAMPLES:\n"
|
|
msg = msg .. " Simple 3-point orbit:\n"
|
|
msg = msg .. " ARCO1, ARCO2, ARCO3\n\n"
|
|
msg = msg .. " High altitude route with RTB:\n"
|
|
msg = msg .. " SHELL1:FL280, SHELL2:FL280, SHELL3:RTB\n\n"
|
|
msg = msg .. " Low-level tanker track:\n"
|
|
msg = msg .. " ARCO1:FL120:SP250, ARCO2:FL120:SP250\n\n"
|
|
|
|
msg = msg .. "━━━ REROUTING ACTIVE TANKERS ━━━\n\n"
|
|
msg = msg .. "Change an active tanker's route mid-mission:\n"
|
|
msg = msg .. " 1. Place new waypoint markers\n"
|
|
msg = msg .. " 2. F10 → Custom Route → Reroute Active Tanker\n"
|
|
msg = msg .. " 3. Tanker immediately follows new route\n\n"
|
|
|
|
msg = msg .. "Use cases:\n"
|
|
msg = msg .. " • Reposition for different theater\n"
|
|
msg = msg .. " • Avoid threat areas\n"
|
|
msg = msg .. " • Send tanker home (use :RTB)\n\n"
|
|
|
|
msg = msg .. "━━━ NOTES ━━━\n\n"
|
|
msg = msg .. "• Markers are auto-deleted after use\n"
|
|
msg = msg .. "• Tankers auto-respawn after 3 minutes if lost\n"
|
|
msg = msg .. "• Use Emergency Tanker for 1-minute respawn\n"
|
|
msg = msg .. "• RTB finds nearest friendly airbase & lands\n"
|
|
msg = msg .. "• Check Tanker Status for current position/fuel\n"
|
|
|
|
MESSAGE:New(msg, 45):ToBlue()
|
|
end
|
|
|
|
-- Function to reroute an active tanker with new waypoints
|
|
function RerouteTanker()
|
|
if not TANKER_STATE.KC135.active or not TANKER_STATE.KC135.group then
|
|
MESSAGE:New("KC-135 is not active! Spawn it first.", 10):ToBlue()
|
|
return
|
|
end
|
|
|
|
-- Scan for waypoint markers
|
|
local waypoints, markerIds = ScanForWaypointMarkers(TANKER_CONFIG.KC135.callsign)
|
|
|
|
if #waypoints < ROUTE_CONFIG.minWaypoints then
|
|
MESSAGE:New(string.format("Reroute requires at least %d waypoints!\nPlace markers: %s1, %s2, etc.",
|
|
ROUTE_CONFIG.minWaypoints, TANKER_CONFIG.KC135.callsign, TANKER_CONFIG.KC135.callsign), 15, "ERROR"):ToBlue()
|
|
return
|
|
end
|
|
|
|
-- Build new route
|
|
local routePoints = {}
|
|
local routeDesc = ""
|
|
local hasRTB = false
|
|
|
|
for i, wp in ipairs(waypoints) do
|
|
local parsed = ParseWaypointMarker(wp.markerText, TANKER_CONFIG.KC135.defaultAltitude, TANKER_CONFIG.KC135.defaultSpeed)
|
|
|
|
if parsed.rtb then
|
|
hasRTB = true
|
|
local lastPos = #routePoints > 0 and routePoints[#routePoints].coord or TANKER_STATE.KC135.group:GetCoordinate()
|
|
local nearestAirbase = lastPos:GetClosestAirbase(Airbase.Category.AIRDROME, coalition.side.BLUE)
|
|
|
|
if nearestAirbase then
|
|
local airbaseName = nearestAirbase:GetName()
|
|
local airbaseCoord = nearestAirbase:GetCoordinate()
|
|
routeDesc = routeDesc .. string.format("\n WP%d: RTB to %s", i, airbaseName)
|
|
|
|
table.insert(routePoints, {
|
|
coord = airbaseCoord,
|
|
altitude = 0,
|
|
speed = parsed.speed,
|
|
rtb = true,
|
|
airbase = nearestAirbase,
|
|
airbaseName = airbaseName
|
|
})
|
|
end
|
|
break
|
|
else
|
|
routeDesc = routeDesc .. string.format("\n WP%d: FL%03d @ %d kts",
|
|
i, math.floor(parsed.altitude / 100), parsed.speed)
|
|
table.insert(routePoints, {
|
|
coord = wp.coordinate,
|
|
altitude = parsed.altitude,
|
|
speed = parsed.speed,
|
|
rtb = false
|
|
})
|
|
end
|
|
end
|
|
|
|
-- Build task route
|
|
local taskRoute = {}
|
|
for i, rp in ipairs(routePoints) do
|
|
local wp
|
|
|
|
if rp.rtb and rp.airbase then
|
|
wp = rp.coord:WaypointAirLanding(
|
|
rp.speed * 0.514444,
|
|
rp.airbase:GetDCSObject(),
|
|
{},
|
|
"RTB"
|
|
)
|
|
else
|
|
wp = rp.coord:WaypointAirFlyOverPoint(
|
|
COORDINATE.WaypointAltType.BARO,
|
|
rp.speed * 0.514444,
|
|
rp.altitude * 0.3048,
|
|
{},
|
|
"WP" .. i
|
|
)
|
|
|
|
if not rp.rtb then
|
|
wp.task = {
|
|
id = "ComboTask",
|
|
params = {
|
|
tasks = {
|
|
{id = "Tanker", params = {}}
|
|
}
|
|
}
|
|
}
|
|
end
|
|
end
|
|
|
|
table.insert(taskRoute, wp)
|
|
end
|
|
|
|
-- Apply new route
|
|
TANKER_STATE.KC135.group:Route(taskRoute)
|
|
|
|
MESSAGE:New(string.format("%s accepting new route with %d waypoints:%s",
|
|
TANKER_CONFIG.KC135.displayName, #routePoints, routeDesc), 20):ToBlue()
|
|
|
|
-- Delete markers
|
|
if ROUTE_CONFIG.deleteMarkersAfterUse then
|
|
for _, markerId in ipairs(markerIds) do
|
|
trigger.action.removeMark(markerId)
|
|
end
|
|
end
|
|
|
|
env.info(string.format("[TANKER] Rerouted %s with %d waypoints", TANKER_CONFIG.KC135.displayName, #routePoints))
|
|
end
|
|
|
|
-- Function to reroute KC-135 MPRS
|
|
function RerouteTankerMPRS()
|
|
if not TANKER_STATE.KC135_MPRS.active or not TANKER_STATE.KC135_MPRS.group then
|
|
MESSAGE:New("KC-135 MPRS is not active! Spawn it first.", 10):ToBlue()
|
|
return
|
|
end
|
|
|
|
local waypoints, markerIds = ScanForWaypointMarkers(TANKER_CONFIG.KC135_MPRS.callsign)
|
|
|
|
if #waypoints < ROUTE_CONFIG.minWaypoints then
|
|
MESSAGE:New(string.format("Reroute requires at least %d waypoints!\nPlace markers: %s1, %s2, etc.",
|
|
ROUTE_CONFIG.minWaypoints, TANKER_CONFIG.KC135_MPRS.callsign, TANKER_CONFIG.KC135_MPRS.callsign), 15, "ERROR"):ToBlue()
|
|
return
|
|
end
|
|
|
|
local routePoints = {}
|
|
local routeDesc = ""
|
|
local hasRTB = false
|
|
|
|
for i, wp in ipairs(waypoints) do
|
|
local parsed = ParseWaypointMarker(wp.markerText, TANKER_CONFIG.KC135_MPRS.defaultAltitude, TANKER_CONFIG.KC135_MPRS.defaultSpeed)
|
|
|
|
if parsed.rtb then
|
|
hasRTB = true
|
|
local lastPos = #routePoints > 0 and routePoints[#routePoints].coord or TANKER_STATE.KC135_MPRS.group:GetCoordinate()
|
|
local nearestAirbase = lastPos:GetClosestAirbase(Airbase.Category.AIRDROME, coalition.side.BLUE)
|
|
|
|
if nearestAirbase then
|
|
local airbaseName = nearestAirbase:GetName()
|
|
local airbaseCoord = nearestAirbase:GetCoordinate()
|
|
routeDesc = routeDesc .. string.format("\n WP%d: RTB to %s", i, airbaseName)
|
|
|
|
table.insert(routePoints, {
|
|
coord = airbaseCoord,
|
|
altitude = 0,
|
|
speed = parsed.speed,
|
|
rtb = true,
|
|
airbase = nearestAirbase,
|
|
airbaseName = airbaseName
|
|
})
|
|
end
|
|
break
|
|
else
|
|
routeDesc = routeDesc .. string.format("\n WP%d: FL%03d @ %d kts",
|
|
i, math.floor(parsed.altitude / 100), parsed.speed)
|
|
table.insert(routePoints, {
|
|
coord = wp.coordinate,
|
|
altitude = parsed.altitude,
|
|
speed = parsed.speed,
|
|
rtb = false
|
|
})
|
|
end
|
|
end
|
|
|
|
local taskRoute = {}
|
|
for i, rp in ipairs(routePoints) do
|
|
local wp
|
|
|
|
if rp.rtb and rp.airbase then
|
|
wp = rp.coord:WaypointAirLanding(
|
|
rp.speed * 0.514444,
|
|
rp.airbase:GetDCSObject(),
|
|
{},
|
|
"RTB"
|
|
)
|
|
else
|
|
wp = rp.coord:WaypointAirFlyOverPoint(
|
|
COORDINATE.WaypointAltType.BARO,
|
|
rp.speed * 0.514444,
|
|
rp.altitude * 0.3048,
|
|
{},
|
|
"WP" .. i
|
|
)
|
|
|
|
if not rp.rtb then
|
|
wp.task = {
|
|
id = "ComboTask",
|
|
params = {
|
|
tasks = {
|
|
{id = "Tanker", params = {}}
|
|
}
|
|
}
|
|
}
|
|
end
|
|
end
|
|
|
|
table.insert(taskRoute, wp)
|
|
end
|
|
|
|
TANKER_STATE.KC135_MPRS.group:Route(taskRoute)
|
|
|
|
MESSAGE:New(string.format("%s accepting new route with %d waypoints:%s",
|
|
TANKER_CONFIG.KC135_MPRS.displayName, #routePoints, routeDesc), 20):ToBlue()
|
|
|
|
if ROUTE_CONFIG.deleteMarkersAfterUse then
|
|
for _, markerId in ipairs(markerIds) do
|
|
trigger.action.removeMark(markerId)
|
|
end
|
|
end
|
|
|
|
env.info(string.format("[TANKER] Rerouted %s with %d waypoints", TANKER_CONFIG.KC135_MPRS.displayName, #routePoints))
|
|
end
|
|
|
|
-- ============================================================================
|
|
-- MISSION MENU SETUP
|
|
-- ============================================================================
|
|
|
|
-- Create mission menu for tanker requests
|
|
-- Integrates with MenuManager to place under "Mission Options"
|
|
-- This keeps CTLD at F2 and AFAC at F3 as intended
|
|
if MenuManager and MenuManager.CreateCoalitionMenu then
|
|
-- Use MenuManager to create menu under "Mission Options"
|
|
MENU_TANKER_ROOT = MenuManager.CreateCoalitionMenu(coalition.side.BLUE, "Tanker Operations")
|
|
env.info("[TANKER] Using MenuManager - menu created under Mission Options")
|
|
else
|
|
-- Fallback: create root menu if MenuManager not available
|
|
MENU_TANKER_ROOT = MENU_COALITION:New(coalition.side.BLUE, "Tanker Operations")
|
|
env.warning("[TANKER] MenuManager not found - creating root menu (load MenuManager first!)")
|
|
end
|
|
|
|
-- Standard tanker spawns
|
|
MENU_KC135_LAUNCH = MENU_COALITION_COMMAND:New(
|
|
coalition.side.BLUE,
|
|
"Launch " .. TANKER_CONFIG.KC135.displayName,
|
|
MENU_TANKER_ROOT,
|
|
SpawnTanker
|
|
)
|
|
|
|
MENU_KC135_MPRS_LAUNCH = MENU_COALITION_COMMAND:New(
|
|
coalition.side.BLUE,
|
|
"Launch " .. TANKER_CONFIG.KC135_MPRS.displayName,
|
|
MENU_TANKER_ROOT,
|
|
SpawnTankerMPRS
|
|
)
|
|
|
|
-- Custom route submenu
|
|
local MENU_CUSTOM_ROUTE = MENU_COALITION:New(
|
|
coalition.side.BLUE,
|
|
"Custom Route",
|
|
MENU_TANKER_ROOT
|
|
)
|
|
|
|
MENU_COALITION_COMMAND:New(
|
|
coalition.side.BLUE,
|
|
"How to Use Custom Routes",
|
|
MENU_CUSTOM_ROUTE,
|
|
ShowCustomRouteHelp
|
|
)
|
|
|
|
MENU_COALITION_COMMAND:New(
|
|
coalition.side.BLUE,
|
|
string.format("Launch %s (%s markers)", TANKER_CONFIG.KC135.displayName, TANKER_CONFIG.KC135.callsign),
|
|
MENU_CUSTOM_ROUTE,
|
|
SpawnCustomTanker
|
|
)
|
|
|
|
MENU_COALITION_COMMAND:New(
|
|
coalition.side.BLUE,
|
|
string.format("Launch %s (%s markers)", TANKER_CONFIG.KC135_MPRS.displayName, TANKER_CONFIG.KC135_MPRS.callsign),
|
|
MENU_CUSTOM_ROUTE,
|
|
SpawnCustomTankerMPRS
|
|
)
|
|
|
|
-- Reroute submenu for changing active tanker routes
|
|
local MENU_REROUTE = MENU_COALITION:New(
|
|
coalition.side.BLUE,
|
|
"Reroute Active Tanker",
|
|
MENU_CUSTOM_ROUTE
|
|
)
|
|
|
|
MENU_COALITION_COMMAND:New(
|
|
coalition.side.BLUE,
|
|
string.format("Reroute %s (%s markers)", TANKER_CONFIG.KC135.displayName, TANKER_CONFIG.KC135.callsign),
|
|
MENU_REROUTE,
|
|
RerouteTanker
|
|
)
|
|
|
|
MENU_COALITION_COMMAND:New(
|
|
coalition.side.BLUE,
|
|
string.format("Reroute %s (%s markers)", TANKER_CONFIG.KC135_MPRS.displayName, TANKER_CONFIG.KC135_MPRS.callsign),
|
|
MENU_REROUTE,
|
|
RerouteTankerMPRS
|
|
)
|
|
|
|
-- Emergency spawns submenu
|
|
local MENU_EMERGENCY = MENU_COALITION:New(
|
|
coalition.side.BLUE,
|
|
"Emergency Tanker",
|
|
MENU_TANKER_ROOT
|
|
)
|
|
|
|
MENU_COALITION_COMMAND:New(
|
|
coalition.side.BLUE,
|
|
string.format("Emergency %s (%s markers)", TANKER_CONFIG.KC135.displayName, TANKER_CONFIG.KC135.callsign),
|
|
MENU_EMERGENCY,
|
|
SpawnEmergencyTanker
|
|
)
|
|
|
|
MENU_COALITION_COMMAND:New(
|
|
coalition.side.BLUE,
|
|
string.format("Emergency %s (%s markers)", TANKER_CONFIG.KC135_MPRS.displayName, TANKER_CONFIG.KC135_MPRS.callsign),
|
|
MENU_EMERGENCY,
|
|
SpawnEmergencyTankerMPRS
|
|
)
|
|
|
|
-- Status and info
|
|
MENU_COALITION_COMMAND:New(
|
|
coalition.side.BLUE,
|
|
"Tanker Status Report",
|
|
MENU_TANKER_ROOT,
|
|
ShowTankerStatus
|
|
)
|
|
|
|
-- ============================================================================
|
|
-- EVENT HANDLER REGISTRATION
|
|
-- ============================================================================
|
|
|
|
BlueTankerEventHandler:HandleEvent(EVENTS.Birth)
|
|
BlueTankerEventHandler:HandleEvent(EVENTS.Dead)
|
|
BlueTankerEventHandler:HandleEvent(EVENTS.Crash)
|
|
BlueTankerEventHandler:HandleEvent(EVENTS.EngineShutdown)
|
|
BlueTankerEventHandler:HandleEvent(EVENTS.Hit)
|
|
|
|
env.info("[TANKER] Tanker Management System initialized")
|
|
MESSAGE:New("Tanker Management System online - Use F10 menu to request tankers", 15):ToBlue() |