mirror of
https://github.com/spencershepard/RotorOps.git
synced 2025-11-10 15:45:30 +00:00
commit
a30d251ebd
411
RotorOps.lua
411
RotorOps.lua
@ -1,22 +1,34 @@
|
||||
RotorOps = {}
|
||||
RotorOps.version = "1.0.11"
|
||||
RotorOps.version = "1.2.1"
|
||||
|
||||
--RotorOps settings that are safe to change dynamically (ideally from the mission editor in DO SCRIPT for ease of use)
|
||||
|
||||
---[[ROTOROPS OPTIONS]]---
|
||||
--- Protip: change these options from the mission editor rather than changing the script file itself. See documentation on github for details.
|
||||
|
||||
|
||||
--RotorOps settings that are safe to change dynamically (ideally from the mission editor in DO SCRIPT for portability). You can change these while the script is running, at any time.
|
||||
RotorOps.voice_overs = true
|
||||
RotorOps.ground_speed = 60 --max speed for ground vehicles moving between zones
|
||||
RotorOps.zone_status_display = true --constantly show units remaining and zone status on screen
|
||||
RotorOps.max_units_left = 0 --allow clearing the zone when a few units are left to prevent frustration with units getting stuck in buildings etc
|
||||
RotorOps.force_offroad = false --affects "move_to_zone" tasks only
|
||||
RotorOps.apcs_spawn_infantry = false --apcs will unload troops when arriving to a new zone
|
||||
|
||||
|
||||
--RotorOps settings that are proabably safe to change
|
||||
--RotorOps settings that are safe to change only before calling setupConflict()
|
||||
RotorOps.transports = {'UH-1H', 'Mi-8MT', 'Mi-24P', 'SA342M', 'SA342L', 'SA342Mistral'} --players flying these will have ctld transport access
|
||||
RotorOps.auto_push = true --should attacking ground units move to the next zone after clearing?
|
||||
RotorOps.CTLD_crates = false
|
||||
RotorOps.CTLD_sound_effects = true --sound effects for troop pickup/dropoffs
|
||||
RotorOps.exclude_ai_group_name = "noai" --include this somewhere in a group name to exclude the group from being tasked in the active zone
|
||||
|
||||
|
||||
---[[END OF OPTIONS]]---
|
||||
|
||||
|
||||
|
||||
|
||||
--RotorOps variables that are safe to read only
|
||||
RotorOps.game_states = {not_started = 0, alpha_active = 1, bravo_active = 2, charlie_active = 3, delta_active = 4, won = 99} --game level user flag will use these values
|
||||
RotorOps.game_states = {not_started = 0, alpha_active = 1, bravo_active = 2, charlie_active = 3, delta_active = 4, lost = 98, won = 99} --game level user flag will use these values
|
||||
RotorOps.game_state = 0
|
||||
RotorOps.zones = {}
|
||||
RotorOps.active_zone = "" --name of the active zone
|
||||
@ -24,22 +36,27 @@ RotorOps.active_zone_index = 0
|
||||
RotorOps.game_state_flag = 1 --user flag to store the game state
|
||||
RotorOps.staging_zone = ""
|
||||
RotorOps.ctld_pickup_zones = {} --keep track of ctld zones we've added, mainly for map markup
|
||||
RotorOps.ai_red_infantry_groups = {}
|
||||
RotorOps.ai_blue_infantry_groups = {}
|
||||
RotorOps.ai_red_vehicle_groups = {}
|
||||
RotorOps.ai_blue_vehicle_groups = {}
|
||||
RotorOps.ai_defending_infantry_groups = {}
|
||||
RotorOps.ai_attacking_infantry_groups = {}
|
||||
RotorOps.ai_defending_vehicle_groups = {}
|
||||
RotorOps.ai_attacking_vehicle_groups = {}
|
||||
RotorOps.ai_tasks = {}
|
||||
RotorOps.defending = false
|
||||
|
||||
trigger.action.outText("ROTOR OPS STARTED: "..RotorOps.version, 5)
|
||||
env.info("ROTOR OPS STARTED: "..RotorOps.version)
|
||||
|
||||
local staged_units --table of ground units that started in the staging zone
|
||||
RotorOps.staged_units = {} --table of ground units that started in the staging zone
|
||||
RotorOps.eventHandler = {}
|
||||
local commandDB = {}
|
||||
local game_message_buffer = {}
|
||||
local active_zone_initial_enemy_units
|
||||
local active_zone_initial_defenders
|
||||
local apcs = {} --table to keep track of infantry vehicles
|
||||
local low_units_message_fired = false
|
||||
|
||||
|
||||
local gameMsgs = {
|
||||
|
||||
RotorOps.gameMsgs = {
|
||||
push = {
|
||||
{'ALL GROUND UNITS, PUSH TO THE ACTIVE ZONE!', 'push_next_zone.ogg'},
|
||||
{'ALL GROUND UNITS, PUSH TO ALPHA!', 'push_alpha.ogg'},
|
||||
@ -74,10 +91,89 @@ local gameMsgs = {
|
||||
{'GET OUR TROOPS TO CHARLIE!', 'get_troops_charlie.ogg'},
|
||||
{'GET OUR TROOPS TO DELTA!', 'get_troops_delta.ogg'},
|
||||
},
|
||||
|
||||
jtac = {
|
||||
{'JTAC DROPPED!', 'jtac_dropped.ogg'},
|
||||
},
|
||||
enemy_almost_cleared = {
|
||||
{'ENEMY HAS NEARLY CAPTURED THE ZONE!', 'enemy_almost_cleared.ogg'},
|
||||
{'ENEMY HAS NEARLY CAPTURED THE ZONE!', 'enemy_decimating_forces.ogg'},
|
||||
{'ENEMY HAS NEARLY CAPTURED THE ZONE!', 'enemy_destroying_ground.ogg'},
|
||||
},
|
||||
almost_cleared = {
|
||||
{'WE HAVE NEARLY CLEARED THE ZONE!', 'almost_cleared.ogg'},
|
||||
{'WE HAVE NEARLY CLEARED THE ZONE!', 'theyre_weak.ogg'},
|
||||
{'WE HAVE NEARLY CLEARED THE ZONE!', 'tearing_them_up.ogg'},
|
||||
},
|
||||
enemy_pushing = {
|
||||
{'ENEMY PUSHING TO THE NEXT ZONE!', 'enemy_pushing_zone.ogg'},
|
||||
{'ENEMY PUSHING TO ALPHA!', 'enemy_pushing_alpha.ogg'},
|
||||
{'ENEMY PUSHING TO BRAVO!', 'enemy_pushing_bravo.ogg'},
|
||||
{'ENEMY PUSHING TO CHARLIE!', 'enemy_pushing_charlie.ogg'},
|
||||
{'ENEMY PUSHING TO DELTA!', 'enemy_pushing_delta.ogg'},
|
||||
},
|
||||
start_defense = {
|
||||
{'SUPPORT THE WAR ON THE GROUND! PUSH BACK AGAINST THE ENEMY!', 'push_back.ogg'},
|
||||
},
|
||||
failure = {
|
||||
{'GROUND MISSION FAILED!', 'mission_failure.ogg'},
|
||||
},
|
||||
friendly_troops_dropped = {
|
||||
{'FRIENDLY TROOPS DROPPED INTO ZONE!', 'friendly_troops_dropped_active.ogg'},
|
||||
},
|
||||
hold_ground = {
|
||||
{'HOLD GROUND TO WIN!', 'hold_our_ground.ogg'},
|
||||
},
|
||||
enemy_cleared_zone = {
|
||||
{'ENEMY TOOK THE ACTIVE ZONE!', 'enemy_destroying_us.ogg'},
|
||||
{'ENEMY TOOK ALPHA!', 'enemy_destroying_us.ogg'},
|
||||
{'ENEMY TOOK BRAVO!', 'enemy_destroying_us.ogg'},
|
||||
{'ENEMY TOOK CHARLIE!', 'enemy_destroying_us.ogg'},
|
||||
{'ENEMY TOOK DELTA!', 'enemy_destroying_us.ogg'},
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
|
||||
local sound_effects = {
|
||||
["troop_pickup"] = {'troops_load_ao.ogg', 'troops_load_ready.ogg', 'troops_load_to_action.ogg',force_offroad = true},
|
||||
["troop_dropoff"] = {'troops_unload_thanks.ogg', 'troops_unload_everybody_off.ogg', 'troops_unload_get_off.ogg', 'troops_unload_here_we_go.ogg', 'troops_unload_moving_out.ogg',},
|
||||
}
|
||||
|
||||
function RotorOps.eventHandler:onEvent(event)
|
||||
if (world.event.S_EVENT_ENGINE_STARTUP == event.id) then --play some sound files when a player starts engines
|
||||
local initaitor = event.initiator:getGroup():getID()
|
||||
if RotorOps.defending then
|
||||
trigger.action.outSoundForGroup(initaitor , RotorOps.gameMsgs.enemy_pushing[RotorOps.active_zone_index + 1][2])
|
||||
else
|
||||
trigger.action.outSoundForGroup(initaitor , RotorOps.gameMsgs.push[RotorOps.active_zone_index + 1][2])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
function RotorOps.registerCtldCallbacks(var)
|
||||
ctld.addCallback(function(_args)
|
||||
local action = _args.action
|
||||
local unit = _args.unit
|
||||
local picked_troops = _args.onboard
|
||||
local dropped_troops = _args.unloaded
|
||||
--trigger.action.outText("dbg: ".. mist.utils.tableShow(_args), 5)
|
||||
if action == "load_troops" or action == "extract_troops" then
|
||||
trigger.action.outSoundForGroup(unit:getGroup():getID() , sound_effects.troop_pickup[math.random(1, #sound_effects.troop_pickup)])
|
||||
elseif action == "unload_troops_zone" or action == "dropped_troops" then
|
||||
trigger.action.outSoundForGroup(unit:getGroup():getID() , sound_effects.troop_dropoff[math.random(1, #sound_effects.troop_dropoff)])
|
||||
if RotorOps.isUnitInZone(unit, RotorOps.active_zone) then
|
||||
local id = timer.scheduleFunction(RotorOps.gameMsgHandler, RotorOps.gameMsgs.friendly_troops_dropped, timer.getTime() + 6) --allow some extra time so we don't step on the player's troop/unload sound effects
|
||||
end
|
||||
if dropped_troops.jtac == true then
|
||||
local id = timer.scheduleFunction(RotorOps.gameMsgHandler, RotorOps.gameMsgs.jtac, timer.getTime() + 6) --allow some extra time so we don't step on the player's troop/unload sound effects
|
||||
end
|
||||
end
|
||||
|
||||
end)
|
||||
end
|
||||
|
||||
---UTILITY FUNCTIONS---
|
||||
|
||||
local function debugMsg(text)
|
||||
@ -98,7 +194,12 @@ end
|
||||
|
||||
|
||||
local function tableHasKey(table,key)
|
||||
if table then
|
||||
return table[key] ~= nil
|
||||
else
|
||||
env.warning("table parameter not provided")
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -131,7 +232,7 @@ local function getDistance(point1, point2)
|
||||
return distance
|
||||
end
|
||||
|
||||
local function isUnitInZone(unit, zone_name)
|
||||
function RotorOps.isUnitInZone(unit, zone_name)
|
||||
local zone = trigger.misc.getZone(zone_name)
|
||||
local distance = getDistance(unit:getPoint(), zone.point)
|
||||
if distance <= zone.radius then
|
||||
@ -156,17 +257,26 @@ function RotorOps.groupsFromUnits(units, table)
|
||||
end
|
||||
|
||||
|
||||
local function gameMsg(event, _index)
|
||||
function RotorOps.gameMsg(event, _index)
|
||||
if not event then
|
||||
env.warning("event parameter is nil")
|
||||
return
|
||||
end
|
||||
local index = 1
|
||||
if _index ~= nill then
|
||||
if _index ~= nil then
|
||||
index = _index + 1
|
||||
end
|
||||
if tableHasKey(event, index) then
|
||||
game_message_buffer[#game_message_buffer + 1] = {event[index][1], event[index][2]}
|
||||
else env.info("ROTOR OPS could not find entry for "..key)
|
||||
else env.warning("ROTOR OPS could not find sound file entry")
|
||||
end
|
||||
end
|
||||
|
||||
function RotorOps.gameMsgHandler(event) --for use with scheduled functions
|
||||
RotorOps.gameMsg(event)
|
||||
end
|
||||
|
||||
|
||||
|
||||
local function processMsgBuffer(vars)
|
||||
if #game_message_buffer > 0 then
|
||||
@ -239,7 +349,7 @@ end
|
||||
|
||||
|
||||
--Easy way to deploy troops from a vehicle with waypoint action. Spawns from the first valid unit found in a group
|
||||
function RotorOps.deployTroops(quantity, target_group)
|
||||
function RotorOps.deployTroops(quantity, target_group, announce)
|
||||
local target_group_obj
|
||||
if type(target_group) == 'string' then
|
||||
target_group_obj = Group.getByName(target_group)
|
||||
@ -257,17 +367,18 @@ function RotorOps.deployTroops(quantity, target_group)
|
||||
-- voiceover trigger stuff
|
||||
for index, zone in pairs(RotorOps.zones)
|
||||
do
|
||||
if isUnitInZone(valid_unit, zone.name) then
|
||||
if RotorOps.isUnitInZone(valid_unit, zone.name) and announce == true then
|
||||
if side == "red" then
|
||||
gameMsg(gameMsgs.troops_dropped, index)
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.troops_dropped, index)
|
||||
else
|
||||
gameMsg(gameMsgs.friendly_troops_dropped, index)
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.friendly_troops_dropped)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--see list of tasks in aiExecute. Zone is optional for many tasks
|
||||
function RotorOps.aiTask(grp, task, zone)
|
||||
local group_name
|
||||
@ -276,6 +387,9 @@ function RotorOps.aiTask(grp, task, zone)
|
||||
else
|
||||
group_name = Group.getName(grp)
|
||||
end
|
||||
if string.find(group_name:lower(), RotorOps.exclude_ai_group_name:lower()) then --exclude groups that the user specifies with a special group name
|
||||
return
|
||||
end
|
||||
if tableHasKey(RotorOps.ai_tasks, group_name) == true then --if we already have this group in our list to manage
|
||||
--debugMsg("timer already exists, updating task for "..group_name.." : ".. RotorOps.ai_tasks[group_name].ai_task.." to "..task)
|
||||
RotorOps.ai_tasks[group_name].ai_task = task
|
||||
@ -455,14 +569,14 @@ function RotorOps.aiExecute(vars)
|
||||
if task == "patrol" then
|
||||
local vars = {}
|
||||
vars.grp = Group.getByName(group_name)
|
||||
vars.radius = 500
|
||||
vars.radius = 300
|
||||
RotorOps.patrolRadius(vars) --takes a group object, not name
|
||||
update_interval = math.random(40,70)
|
||||
update_interval = math.random(150,200)
|
||||
elseif task == "aggressive" then
|
||||
local vars = {}
|
||||
vars.grp = Group.getByName(group_name)
|
||||
vars.radius = 5000
|
||||
update_interval = math.random(20,40)
|
||||
update_interval = math.random(60,90)
|
||||
RotorOps.chargeEnemy(vars) --takes a group object, not name
|
||||
elseif task == "clear_zone" then
|
||||
local vars = {}
|
||||
@ -501,61 +615,88 @@ end
|
||||
function RotorOps.assessUnitsInZone(var)
|
||||
if RotorOps.game_state == RotorOps.game_states.not_started then return end
|
||||
|
||||
--find and sort units found in the active zone
|
||||
local red_ground_units = mist.getUnitsInZones(mist.makeUnitTable({'[red][vehicle]'}), {RotorOps.active_zone})
|
||||
local red_infantry = RotorOps.sortOutInfantry(red_ground_units).infantry
|
||||
local red_vehicles = RotorOps.sortOutInfantry(red_ground_units).not_infantry
|
||||
local blue_ground_units = mist.getUnitsInZones(mist.makeUnitTable({'[blue][vehicle]'}), {RotorOps.active_zone})
|
||||
local blue_infantry = RotorOps.sortOutInfantry(blue_ground_units).infantry
|
||||
local blue_vehicles = RotorOps.sortOutInfantry(blue_ground_units).not_infantry
|
||||
|
||||
--ground unit ai stuff
|
||||
RotorOps.ai_red_infantry_groups = RotorOps.groupsFromUnits(red_infantry)
|
||||
RotorOps.ai_blue_infantry_groups = RotorOps.groupsFromUnits(blue_infantry)
|
||||
RotorOps.ai_red_vehicle_groups = RotorOps.groupsFromUnits(red_vehicles)
|
||||
RotorOps.ai_blue_vehicle_groups = RotorOps.groupsFromUnits(blue_vehicles)
|
||||
local defending_ground_units
|
||||
local defending_infantry
|
||||
local defending_vehicles
|
||||
local attacking_ground_units
|
||||
local attacking_infantry
|
||||
local attacking_vehicles
|
||||
|
||||
for index, group in pairs(RotorOps.ai_red_infantry_groups) do
|
||||
|
||||
|
||||
--find and sort units found in the active zone
|
||||
if RotorOps.defending then
|
||||
defending_ground_units = mist.getUnitsInZones(mist.makeUnitTable({'[blue][vehicle]'}), {RotorOps.active_zone})
|
||||
defending_infantry = RotorOps.sortOutInfantry(defending_ground_units).infantry
|
||||
defending_vehicles = RotorOps.sortOutInfantry(defending_ground_units).not_infantry
|
||||
attacking_ground_units = mist.getUnitsInZones(mist.makeUnitTable({'[red][vehicle]'}), {RotorOps.active_zone})
|
||||
attacking_infantry = RotorOps.sortOutInfantry(attacking_ground_units).infantry
|
||||
attacking_vehicles = RotorOps.sortOutInfantry(attacking_ground_units).not_infantry
|
||||
else --attacking
|
||||
defending_ground_units = mist.getUnitsInZones(mist.makeUnitTable({'[red][vehicle]'}), {RotorOps.active_zone})
|
||||
defending_infantry = RotorOps.sortOutInfantry(defending_ground_units).infantry
|
||||
defending_vehicles = RotorOps.sortOutInfantry(defending_ground_units).not_infantry
|
||||
attacking_ground_units = mist.getUnitsInZones(mist.makeUnitTable({'[blue][vehicle]'}), {RotorOps.active_zone})
|
||||
attacking_infantry = RotorOps.sortOutInfantry(attacking_ground_units).infantry
|
||||
attacking_vehicles = RotorOps.sortOutInfantry(attacking_ground_units).not_infantry
|
||||
end
|
||||
|
||||
|
||||
--ground unit ai stuff
|
||||
RotorOps.ai_defending_infantry_groups = RotorOps.groupsFromUnits(defending_infantry)
|
||||
RotorOps.ai_defending_vehicle_groups = RotorOps.groupsFromUnits(defending_vehicles)
|
||||
RotorOps.ai_attacking_infantry_groups = RotorOps.groupsFromUnits(attacking_infantry)
|
||||
RotorOps.ai_attacking_vehicle_groups = RotorOps.groupsFromUnits(attacking_vehicles)
|
||||
|
||||
for index, group in pairs(RotorOps.ai_defending_infantry_groups) do
|
||||
if group then
|
||||
RotorOps.aiTask(group, "patrol")
|
||||
end
|
||||
end
|
||||
|
||||
for index, group in pairs(RotorOps.ai_blue_infantry_groups) do
|
||||
for index, group in pairs(RotorOps.ai_attacking_infantry_groups) do
|
||||
if group then
|
||||
RotorOps.aiTask(group, "clear_zone", RotorOps.active_zone)
|
||||
end
|
||||
end
|
||||
|
||||
for index, group in pairs(RotorOps.ai_blue_vehicle_groups) do
|
||||
for index, group in pairs(RotorOps.ai_attacking_vehicle_groups) do
|
||||
if group then
|
||||
RotorOps.aiTask(group, "clear_zone", RotorOps.active_zone)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--FIRES ONCE PER ZONE ACTIVATION
|
||||
--let's compare the defending units in zone vs their initial numbers and set a game flag
|
||||
if not active_zone_initial_enemy_units then
|
||||
if not active_zone_initial_defenders then
|
||||
--debugMsg("taking stock of the active zone")
|
||||
active_zone_initial_enemy_units = red_ground_units
|
||||
active_zone_initial_defenders = defending_ground_units
|
||||
low_units_message_fired = false
|
||||
env.info("ROTOR OPS: zone activated: "..RotorOps.active_zone)
|
||||
end
|
||||
|
||||
local defenders_status_flag = RotorOps.zones[RotorOps.active_zone_index].defenders_status_flag
|
||||
if #active_zone_initial_enemy_units == 0 then active_zone_initial_enemy_units = 1 end --prevent divide by zero
|
||||
local defenders_remaining_percent = math.floor((#red_ground_units / #active_zone_initial_enemy_units) * 100)
|
||||
|
||||
if #red_ground_units <= RotorOps.max_units_left then --if we should declare the zone cleared
|
||||
active_zone_initial_enemy_units = nil
|
||||
local defenders_status_flag = RotorOps.zones[RotorOps.active_zone_index].defenders_status_flag
|
||||
--if #active_zone_initial_defenders == 0 then active_zone_initial_defenders = 1 end --prevent divide by zero
|
||||
local defenders_remaining_percent = math.floor((#defending_ground_units / #active_zone_initial_defenders) * 100)
|
||||
|
||||
if #defending_ground_units <= RotorOps.max_units_left then --if we should declare the zone cleared
|
||||
active_zone_initial_defenders = nil
|
||||
defenders_remaining_percent = 0
|
||||
trigger.action.setUserFlag(defenders_status_flag, 0) --set the zone's flag to cleared
|
||||
gameMsg(gameMsgs.cleared, RotorOps.active_zone_index)
|
||||
|
||||
if RotorOps.defending == true then
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.enemy_cleared_zone, RotorOps.active_zone_index)
|
||||
else
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.cleared, RotorOps.active_zone_index)
|
||||
end
|
||||
if RotorOps.auto_push then --push units to the next zone
|
||||
RotorOps.setActiveZone(RotorOps.active_zone_index + 1)
|
||||
end
|
||||
|
||||
else
|
||||
trigger.action.setUserFlag(defenders_status_flag, defenders_remaining_percent) --set the zones flag to indicate the status of remaining enemies
|
||||
trigger.action.setUserFlag(defenders_status_flag, defenders_remaining_percent) --set the zones flag to indicate the status of remaining defenders
|
||||
end
|
||||
|
||||
--are all zones clear?
|
||||
@ -569,23 +710,104 @@ function RotorOps.assessUnitsInZone(var)
|
||||
|
||||
--is the game finished?
|
||||
if all_zones_clear then
|
||||
RotorOps.changeGameState(RotorOps.game_states.won)
|
||||
gameMsg(gameMsgs.success)
|
||||
if RotorOps.defending == true then
|
||||
RotorOps.game_state = RotorOps.game_states.lost
|
||||
trigger.action.setUserFlag(RotorOps.game_state_flag, RotorOps.game_states.lost)
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.failure)
|
||||
else
|
||||
RotorOps.game_state = RotorOps.game_states.won
|
||||
trigger.action.setUserFlag(RotorOps.game_state_flag, RotorOps.game_states.won)
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.success)
|
||||
end
|
||||
return --we won't reset our timer to fire this function again
|
||||
end
|
||||
|
||||
--is the defending game finished?
|
||||
local defending_game_won = true
|
||||
for key, staged_unit in pairs(RotorOps.staged_units) do
|
||||
if staged_unit:isExist() then --check if the enemy has staged units left
|
||||
defending_game_won = false
|
||||
end
|
||||
end
|
||||
if RotorOps.defending and defending_game_won then
|
||||
RotorOps.game_state = RotorOps.game_states.won
|
||||
trigger.action.setUserFlag(RotorOps.game_state_flag, RotorOps.game_states.won)
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.success)
|
||||
return --we won't reset our timer to fire this function again
|
||||
end
|
||||
|
||||
--APCs unload
|
||||
local function unloadAPCs()
|
||||
local units_table = attacking_vehicles
|
||||
|
||||
for index, vehicle in pairs(units_table) do
|
||||
local should_deploy = false
|
||||
if vehicle:hasAttribute("Infantry carriers") and RotorOps.isUnitInZone(vehicle, RotorOps.active_zone) then --if a vehicle is an APC and in zone
|
||||
local apc_name = vehicle:getName()
|
||||
|
||||
if tableHasKey(apcs, apc_name) == true then --if we have this apc in our table already
|
||||
|
||||
for key, apc_details in pairs(apcs[apc_name]) do
|
||||
if hasValue(apc_details, RotorOps.active_zone) then --if our apc table has the current zone
|
||||
|
||||
else --our apc table does not have the current zone
|
||||
apcs[apc_name].deployed_zones = {RotorOps.active_zone,}
|
||||
should_deploy = true
|
||||
end
|
||||
end
|
||||
|
||||
else --we don't have the apc in our table
|
||||
should_deploy = true
|
||||
apcs[apc_name] = {['deployed_zones'] = {RotorOps.active_zone,}}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if should_deploy then
|
||||
local function timedDeploy()
|
||||
if vehicle:isExist() then
|
||||
RotorOps.deployTroops(4, vehicle:getGroup(), false)
|
||||
end
|
||||
end
|
||||
|
||||
local id = timer.scheduleFunction(timedDeploy, nil, timer.getTime() + math.random(90, 180))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if RotorOps.apcs_spawn_infantry then
|
||||
unloadAPCs() --this should really be an aitask
|
||||
end
|
||||
|
||||
|
||||
--voiceovers based on remaining defenders
|
||||
if not low_units_message_fired then
|
||||
if defenders_remaining_percent <= 40 then
|
||||
low_units_message_fired = true
|
||||
env.info("ROTOR OPS: low units remaining in zone")
|
||||
if RotorOps.defending then
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.enemy_almost_cleared, math.random(1, #RotorOps.gameMsgs.enemy_almost_cleared))
|
||||
else
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.almost_cleared, math.random(1, #RotorOps.gameMsgs.almost_cleared))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--zone status display
|
||||
local message = ""
|
||||
local header = ""
|
||||
local body = ""
|
||||
if defenders_remaining_percent == 0 then
|
||||
header = "["..RotorOps.active_zone .. " CLEARED!] "
|
||||
if RotorOps.defending == true then
|
||||
header = "[DEFEND "..RotorOps.active_zone .. "] "
|
||||
body = "RED: " ..#attacking_infantry.. " infantry, " .. #attacking_vehicles .. " vehicles. BLUE: "..#defending_infantry.. " infantry, " .. #defending_vehicles.." vehicles. ["..defenders_remaining_percent.."%]"
|
||||
else
|
||||
header = "[BATTLE FOR "..RotorOps.active_zone .. "] "
|
||||
header = "[ATTACK "..RotorOps.active_zone .. "] "
|
||||
body = "RED: " ..#defending_infantry.. " infantry, " .. #defending_vehicles .. " vehicles. BLUE: "..#attacking_infantry.. " infantry, " .. #attacking_vehicles.." vehicles. ["..defenders_remaining_percent.."%]"
|
||||
end
|
||||
body = "RED: " ..#red_infantry.. " infantry, " .. #red_vehicles .. " vehicles. BLUE: "..#blue_infantry.. " infantry, " .. #blue_vehicles.." vehicles. ["..defenders_remaining_percent.."%]"
|
||||
|
||||
message = header .. body
|
||||
if RotorOps.zone_status_display then
|
||||
@ -655,12 +877,6 @@ end
|
||||
|
||||
|
||||
|
||||
function RotorOps.changeGameState(new_state)
|
||||
RotorOps.game_state = new_state
|
||||
trigger.action.setUserFlag(RotorOps.game_state_flag, new_state)
|
||||
end
|
||||
|
||||
|
||||
function RotorOps.setActiveZone(new_index)
|
||||
local old_index = RotorOps.active_zone_index
|
||||
if new_index > #RotorOps.zones then
|
||||
@ -674,18 +890,33 @@ function RotorOps.setActiveZone(new_index)
|
||||
RotorOps.active_zone = RotorOps.zones[new_index].name
|
||||
|
||||
if new_index ~= old_index then --the active zone is changing
|
||||
if old_index > 0 then
|
||||
ctld.activatePickupZone(RotorOps.zones[old_index].name)
|
||||
end
|
||||
ctld.deactivatePickupZone(RotorOps.zones[new_index].name)
|
||||
RotorOps.changeGameState(new_index)
|
||||
if new_index < old_index then gameMsg(gameMsgs.fallback, new_index) end
|
||||
if new_index > old_index then gameMsg(gameMsgs.get_troops_to_zone, new_index) end
|
||||
|
||||
local staged_groups = RotorOps.groupsFromUnits(staged_units)
|
||||
if not RotorOps.defending then
|
||||
|
||||
if old_index > 0 then
|
||||
ctld.activatePickupZone(RotorOps.zones[old_index].name) --make the captured zone a pickup zone
|
||||
end
|
||||
ctld.deactivatePickupZone(RotorOps.zones[new_index].name)
|
||||
end
|
||||
|
||||
RotorOps.game_state = new_index
|
||||
trigger.action.setUserFlag(RotorOps.game_state_flag, new_index)
|
||||
|
||||
if new_index > old_index then
|
||||
if RotorOps.defending == true then
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.enemy_pushing, new_index)
|
||||
else
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.push, new_index)
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.get_troops_to_zone, new_index)
|
||||
end
|
||||
end
|
||||
|
||||
local staged_groups = RotorOps.groupsFromUnits(RotorOps.staged_units)
|
||||
for index, group in pairs(staged_groups) do
|
||||
RotorOps.aiTask(group,"move_to_active_zone", RotorOps.zones[RotorOps.active_zone_index].name) --send vehicles to next zone; use move_to_active_zone so units don't get stuck if the active zone moves before they arrive
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
@ -697,6 +928,11 @@ end
|
||||
|
||||
--make some changes to the CTLD script/settings
|
||||
function RotorOps.setupCTLD()
|
||||
if type(ctld.pickupZones[1][2]) == "number" then --ctld converts its string table to integer on load, so we'll see if that's happened already
|
||||
trigger.action.outText("ERROR: CTLD Loaded Too Soon!!", 90)
|
||||
return
|
||||
end
|
||||
|
||||
ctld.enableCrates = RotorOps.CTLD_crates
|
||||
ctld.enabledFOBBuilding = false
|
||||
ctld.JTAC_lock = "vehicle"
|
||||
@ -716,15 +952,14 @@ function RotorOps.setupCTLD()
|
||||
}
|
||||
|
||||
ctld.loadableGroups = {
|
||||
{name = "Small Standard Group (4)", inf = 2, mg = 1, at = 1 },
|
||||
{name = "Standard Group (8)", inf = 4, mg = 2, at = 2 }, -- will make a loadable group with 6 infantry, 2 MGs and 2 anti-tank for both coalitions
|
||||
{name = "Anti Air (5)", inf = 2, aa = 3 },
|
||||
{name = "Anti Tank (8)", inf = 2, at = 6 },
|
||||
{name = "Mortar Squad (6)", mortar = 6 },
|
||||
{name = "Small Standard Group (4)", inf = 2, mg = 1, at = 1 },
|
||||
{name = "JTAC Group (5)", inf = 4, jtac = 1 },
|
||||
{name = "Single JTAC (1)", jtac = 1 },
|
||||
{name = "Small Platoon (16)", inf = 10, mg = 3, at = 3 },
|
||||
{name = "Platoon (24)", inf = 12, mg = 4, at = 3, aa = 1 },
|
||||
{name = "JTAC Group (4)", inf = 3, jtac = 1 },
|
||||
{name = "Small Platoon (16)", inf = 9, mg = 3, at = 3, aa = 1 },
|
||||
{name = "Platoon (24)", inf = 10, mg = 5, at = 6, aa = 3 },
|
||||
|
||||
}
|
||||
end
|
||||
@ -747,7 +982,7 @@ function RotorOps.addZone(_name, _zone_defenders_flag)
|
||||
end
|
||||
|
||||
function RotorOps.stagingZone(_name)
|
||||
RotorOps.addPickupZone(_name, "blue", -1, "yes", 0)
|
||||
RotorOps.addPickupZone(_name, "blue", -1, "no", 0)
|
||||
RotorOps.staging_zone = _name
|
||||
end
|
||||
|
||||
@ -771,9 +1006,13 @@ function RotorOps.setupConflict(_game_state_flag)
|
||||
RotorOps.setupCTLD()
|
||||
--RotorOps.setupRadioMenu()
|
||||
RotorOps.game_state_flag = _game_state_flag
|
||||
RotorOps.changeGameState(RotorOps.game_states.not_started)
|
||||
RotorOps.game_state = RotorOps.game_states.not_started
|
||||
processMsgBuffer()
|
||||
trigger.action.setUserFlag(RotorOps.game_state_flag, RotorOps.game_states.not_started)
|
||||
trigger.action.outText("ALL TROOPS GET TO TRANSPORT AND PREPARE FOR DEPLOYMENT!" , 10, false)
|
||||
|
||||
if RotorOps.CTLD_sound_effects == true then
|
||||
local timer_id = timer.scheduleFunction(RotorOps.registerCtldCallbacks, 1, timer.getTime() + 5)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -791,16 +1030,26 @@ function RotorOps.startConflict()
|
||||
--missionCommands.removeItem(commandDB['start_conflict'])
|
||||
--commandDB['clear_zone'] = missionCommands.addCommand( "[CHEAT] Force Clear Zone" , conflict_zones_menu , RotorOps.clearActiveZone)
|
||||
|
||||
RotorOps.staged_units = mist.getUnitsInZones(mist.makeUnitTable({'[all][vehicle]'}), {RotorOps.staging_zone})
|
||||
|
||||
gameMsg(gameMsgs.start)
|
||||
gameMsg(gameMsgs.push, 1)
|
||||
processMsgBuffer()
|
||||
if RotorOps.staged_units[1]:getCoalition() == 1 then --check the coalition in the staging zone to see if we're defending
|
||||
RotorOps.defending = true
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.start_defense)
|
||||
ctld.activatePickupZone(RotorOps.zones[#RotorOps.zones].name) --make the last zone a pickup zone for defenders
|
||||
ctld.deactivatePickupZone(RotorOps.staging_zone)
|
||||
else
|
||||
RotorOps.gameMsg(RotorOps.gameMsgs.start)
|
||||
ctld.activatePickupZone(RotorOps.staging_zone)
|
||||
end
|
||||
|
||||
staged_units = mist.getUnitsInZones(mist.makeUnitTable({'[all][vehicle]'}), {RotorOps.staging_zone})
|
||||
RotorOps.setActiveZone(1)
|
||||
|
||||
local id = timer.scheduleFunction(RotorOps.assessUnitsInZone, 1, timer.getTime() + 5)
|
||||
world.addEventHandler(RotorOps.eventHandler)
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
BIN
sound/embedded/almost_cleared.ogg
Normal file
BIN
sound/embedded/almost_cleared.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/enemy_almost_cleared.ogg
Normal file
BIN
sound/embedded/enemy_almost_cleared.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/enemy_decimating_forces.ogg
Normal file
BIN
sound/embedded/enemy_decimating_forces.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/enemy_destroying_ground.ogg
Normal file
BIN
sound/embedded/enemy_destroying_ground.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/enemy_pushing_alpha.ogg
Normal file
BIN
sound/embedded/enemy_pushing_alpha.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/enemy_pushing_bravo.ogg
Normal file
BIN
sound/embedded/enemy_pushing_bravo.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/enemy_pushing_charlie.ogg
Normal file
BIN
sound/embedded/enemy_pushing_charlie.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/enemy_pushing_delta.ogg
Normal file
BIN
sound/embedded/enemy_pushing_delta.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/enemy_pushing_zone.ogg
Normal file
BIN
sound/embedded/enemy_pushing_zone.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/enemy_reinforcements_inbound.ogg
Normal file
BIN
sound/embedded/enemy_reinforcements_inbound.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/friendly_troops_dropped_active.ogg
Normal file
BIN
sound/embedded/friendly_troops_dropped_active.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/hold_our_ground.ogg
Normal file
BIN
sound/embedded/hold_our_ground.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/mission_failure.ogg
Normal file
BIN
sound/embedded/mission_failure.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/push_back.ogg
Normal file
BIN
sound/embedded/push_back.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/tearing_them_up.ogg
Normal file
BIN
sound/embedded/tearing_them_up.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/theyre_weak.ogg
Normal file
BIN
sound/embedded/theyre_weak.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/troops_load_ao.ogg
Normal file
BIN
sound/embedded/troops_load_ao.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/troops_load_ready.ogg
Normal file
BIN
sound/embedded/troops_load_ready.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/troops_load_to_action.ogg
Normal file
BIN
sound/embedded/troops_load_to_action.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/troops_unload_everybody_off.ogg
Normal file
BIN
sound/embedded/troops_unload_everybody_off.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/troops_unload_get_off.ogg
Normal file
BIN
sound/embedded/troops_unload_get_off.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/troops_unload_here_we_go.ogg
Normal file
BIN
sound/embedded/troops_unload_here_we_go.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/troops_unload_moving_out.ogg
Normal file
BIN
sound/embedded/troops_unload_moving_out.ogg
Normal file
Binary file not shown.
BIN
sound/embedded/troops_unload_thanks.ogg
Normal file
BIN
sound/embedded/troops_unload_thanks.ogg
Normal file
Binary file not shown.
BIN
sound/enemy_fighters_inbound.ogg
Normal file
BIN
sound/enemy_fighters_inbound.ogg
Normal file
Binary file not shown.
BIN
sound/enemy_reinforcements_inbound.ogg
Normal file
BIN
sound/enemy_reinforcements_inbound.ogg
Normal file
Binary file not shown.
BIN
sound/enemy_strike_inbound.ogg
Normal file
BIN
sound/enemy_strike_inbound.ogg
Normal file
Binary file not shown.
BIN
sound/enemy_units_destroyed.ogg
Normal file
BIN
sound/enemy_units_destroyed.ogg
Normal file
Binary file not shown.
BIN
sound/friendly_cap.ogg
Normal file
BIN
sound/friendly_cap.ogg
Normal file
Binary file not shown.
BIN
sound/friendly_cas_inbound.ogg
Normal file
BIN
sound/friendly_cas_inbound.ogg
Normal file
Binary file not shown.
BIN
sound/friendly_chopper_inbound.ogg
Normal file
BIN
sound/friendly_chopper_inbound.ogg
Normal file
Binary file not shown.
BIN
sound/friendly_convoy.ogg
Normal file
BIN
sound/friendly_convoy.ogg
Normal file
Binary file not shown.
BIN
sound/friendly_convoy_under_attack.ogg
Normal file
BIN
sound/friendly_convoy_under_attack.ogg
Normal file
Binary file not shown.
BIN
sound/friendly_reinforcements_inbound.ogg
Normal file
BIN
sound/friendly_reinforcements_inbound.ogg
Normal file
Binary file not shown.
BIN
sound/friendly_sead.ogg
Normal file
BIN
sound/friendly_sead.ogg
Normal file
Binary file not shown.
BIN
sound/friendly_strike_inbound.ogg
Normal file
BIN
sound/friendly_strike_inbound.ogg
Normal file
Binary file not shown.
BIN
sound/friendly_units_under_attack.ogg
Normal file
BIN
sound/friendly_units_under_attack.ogg
Normal file
Binary file not shown.
BIN
sound/hostile_target_destroyed.ogg
Normal file
BIN
sound/hostile_target_destroyed.ogg
Normal file
Binary file not shown.
BIN
sound/losing_battle.ogg
Normal file
BIN
sound/losing_battle.ogg
Normal file
Binary file not shown.
BIN
sound/primary_target_destroyed.ogg
Normal file
BIN
sound/primary_target_destroyed.ogg
Normal file
Binary file not shown.
BIN
sound/target_destroyed.ogg
Normal file
BIN
sound/target_destroyed.ogg
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user