diff --git a/RotorOps.lua b/RotorOps.lua index c7b449a..99bb12d 100644 --- a/RotorOps.lua +++ b/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.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 + + local defending_ground_units + local defending_infantry + local defending_vehicles + local attacking_ground_units + local attacking_infantry + local attacking_vehicles ---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) + + + --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 - for index, group in pairs(RotorOps.ai_red_infantry_groups) do + + --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 #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 #red_ground_units <= RotorOps.max_units_left then --if we should declare the zone cleared - active_zone_initial_enemy_units = nil + 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!] " - else - header = "[BATTLE FOR "..RotorOps.active_zone .. "] " + 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 = "[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 @@ -790,17 +1029,27 @@ function RotorOps.startConflict() --local conflict_zones_menu = commandDB['conflict_zones_menu'] --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}) + 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 - gameMsg(gameMsgs.start) - gameMsg(gameMsgs.push, 1) - processMsgBuffer() - - 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 + + + diff --git a/sound/embedded/almost_cleared.ogg b/sound/embedded/almost_cleared.ogg new file mode 100644 index 0000000..54578b7 Binary files /dev/null and b/sound/embedded/almost_cleared.ogg differ diff --git a/sound/embedded/enemy_almost_cleared.ogg b/sound/embedded/enemy_almost_cleared.ogg new file mode 100644 index 0000000..c437f93 Binary files /dev/null and b/sound/embedded/enemy_almost_cleared.ogg differ diff --git a/sound/embedded/enemy_decimating_forces.ogg b/sound/embedded/enemy_decimating_forces.ogg new file mode 100644 index 0000000..b264a98 Binary files /dev/null and b/sound/embedded/enemy_decimating_forces.ogg differ diff --git a/sound/embedded/enemy_destroying_ground.ogg b/sound/embedded/enemy_destroying_ground.ogg new file mode 100644 index 0000000..2e06af3 Binary files /dev/null and b/sound/embedded/enemy_destroying_ground.ogg differ diff --git a/sound/enemy_destroying_us.ogg b/sound/embedded/enemy_destroying_us.ogg similarity index 100% rename from sound/enemy_destroying_us.ogg rename to sound/embedded/enemy_destroying_us.ogg diff --git a/sound/embedded/enemy_pushing_alpha.ogg b/sound/embedded/enemy_pushing_alpha.ogg new file mode 100644 index 0000000..a0c144d Binary files /dev/null and b/sound/embedded/enemy_pushing_alpha.ogg differ diff --git a/sound/embedded/enemy_pushing_bravo.ogg b/sound/embedded/enemy_pushing_bravo.ogg new file mode 100644 index 0000000..91e6cfe Binary files /dev/null and b/sound/embedded/enemy_pushing_bravo.ogg differ diff --git a/sound/embedded/enemy_pushing_charlie.ogg b/sound/embedded/enemy_pushing_charlie.ogg new file mode 100644 index 0000000..c35159e Binary files /dev/null and b/sound/embedded/enemy_pushing_charlie.ogg differ diff --git a/sound/embedded/enemy_pushing_delta.ogg b/sound/embedded/enemy_pushing_delta.ogg new file mode 100644 index 0000000..7305fc1 Binary files /dev/null and b/sound/embedded/enemy_pushing_delta.ogg differ diff --git a/sound/embedded/enemy_pushing_zone.ogg b/sound/embedded/enemy_pushing_zone.ogg new file mode 100644 index 0000000..bc57d2c Binary files /dev/null and b/sound/embedded/enemy_pushing_zone.ogg differ diff --git a/sound/embedded/enemy_reinforcements_inbound.ogg b/sound/embedded/enemy_reinforcements_inbound.ogg new file mode 100644 index 0000000..70d7301 Binary files /dev/null and b/sound/embedded/enemy_reinforcements_inbound.ogg differ diff --git a/sound/embedded/friendly_troops_dropped_active.ogg b/sound/embedded/friendly_troops_dropped_active.ogg new file mode 100644 index 0000000..2d414f4 Binary files /dev/null and b/sound/embedded/friendly_troops_dropped_active.ogg differ diff --git a/sound/embedded/hold_our_ground.ogg b/sound/embedded/hold_our_ground.ogg new file mode 100644 index 0000000..7308634 Binary files /dev/null and b/sound/embedded/hold_our_ground.ogg differ diff --git a/sound/jtac_dropped.ogg b/sound/embedded/jtac_dropped.ogg similarity index 100% rename from sound/jtac_dropped.ogg rename to sound/embedded/jtac_dropped.ogg diff --git a/sound/embedded/mission_failure.ogg b/sound/embedded/mission_failure.ogg new file mode 100644 index 0000000..267280e Binary files /dev/null and b/sound/embedded/mission_failure.ogg differ diff --git a/sound/embedded/push_back.ogg b/sound/embedded/push_back.ogg new file mode 100644 index 0000000..3e49ccc Binary files /dev/null and b/sound/embedded/push_back.ogg differ diff --git a/sound/embedded/tearing_them_up.ogg b/sound/embedded/tearing_them_up.ogg new file mode 100644 index 0000000..07cde11 Binary files /dev/null and b/sound/embedded/tearing_them_up.ogg differ diff --git a/sound/embedded/theyre_weak.ogg b/sound/embedded/theyre_weak.ogg new file mode 100644 index 0000000..b8a4442 Binary files /dev/null and b/sound/embedded/theyre_weak.ogg differ diff --git a/sound/embedded/troops_load_ao.ogg b/sound/embedded/troops_load_ao.ogg new file mode 100644 index 0000000..27a352a Binary files /dev/null and b/sound/embedded/troops_load_ao.ogg differ diff --git a/sound/embedded/troops_load_ready.ogg b/sound/embedded/troops_load_ready.ogg new file mode 100644 index 0000000..1fd8e62 Binary files /dev/null and b/sound/embedded/troops_load_ready.ogg differ diff --git a/sound/embedded/troops_load_to_action.ogg b/sound/embedded/troops_load_to_action.ogg new file mode 100644 index 0000000..7acb64d Binary files /dev/null and b/sound/embedded/troops_load_to_action.ogg differ diff --git a/sound/embedded/troops_unload_everybody_off.ogg b/sound/embedded/troops_unload_everybody_off.ogg new file mode 100644 index 0000000..c5ba371 Binary files /dev/null and b/sound/embedded/troops_unload_everybody_off.ogg differ diff --git a/sound/embedded/troops_unload_get_off.ogg b/sound/embedded/troops_unload_get_off.ogg new file mode 100644 index 0000000..939d7a0 Binary files /dev/null and b/sound/embedded/troops_unload_get_off.ogg differ diff --git a/sound/embedded/troops_unload_here_we_go.ogg b/sound/embedded/troops_unload_here_we_go.ogg new file mode 100644 index 0000000..1151bf5 Binary files /dev/null and b/sound/embedded/troops_unload_here_we_go.ogg differ diff --git a/sound/embedded/troops_unload_moving_out.ogg b/sound/embedded/troops_unload_moving_out.ogg new file mode 100644 index 0000000..d07923f Binary files /dev/null and b/sound/embedded/troops_unload_moving_out.ogg differ diff --git a/sound/embedded/troops_unload_thanks.ogg b/sound/embedded/troops_unload_thanks.ogg new file mode 100644 index 0000000..4b4f4ef Binary files /dev/null and b/sound/embedded/troops_unload_thanks.ogg differ diff --git a/sound/enemy_fighters_inbound.ogg b/sound/enemy_fighters_inbound.ogg new file mode 100644 index 0000000..cc1cd09 Binary files /dev/null and b/sound/enemy_fighters_inbound.ogg differ diff --git a/sound/enemy_reinforcements_inbound.ogg b/sound/enemy_reinforcements_inbound.ogg new file mode 100644 index 0000000..70d7301 Binary files /dev/null and b/sound/enemy_reinforcements_inbound.ogg differ diff --git a/sound/enemy_strike_inbound.ogg b/sound/enemy_strike_inbound.ogg new file mode 100644 index 0000000..1d465df Binary files /dev/null and b/sound/enemy_strike_inbound.ogg differ diff --git a/sound/enemy_units_destroyed.ogg b/sound/enemy_units_destroyed.ogg new file mode 100644 index 0000000..2f9233e Binary files /dev/null and b/sound/enemy_units_destroyed.ogg differ diff --git a/sound/friendly_cap.ogg b/sound/friendly_cap.ogg new file mode 100644 index 0000000..0a95ae0 Binary files /dev/null and b/sound/friendly_cap.ogg differ diff --git a/sound/friendly_cas_inbound.ogg b/sound/friendly_cas_inbound.ogg new file mode 100644 index 0000000..59c4a18 Binary files /dev/null and b/sound/friendly_cas_inbound.ogg differ diff --git a/sound/friendly_chopper_inbound.ogg b/sound/friendly_chopper_inbound.ogg new file mode 100644 index 0000000..a1990f7 Binary files /dev/null and b/sound/friendly_chopper_inbound.ogg differ diff --git a/sound/friendly_convoy.ogg b/sound/friendly_convoy.ogg new file mode 100644 index 0000000..569569a Binary files /dev/null and b/sound/friendly_convoy.ogg differ diff --git a/sound/friendly_convoy_under_attack.ogg b/sound/friendly_convoy_under_attack.ogg new file mode 100644 index 0000000..dfc2a96 Binary files /dev/null and b/sound/friendly_convoy_under_attack.ogg differ diff --git a/sound/friendly_reinforcements_inbound.ogg b/sound/friendly_reinforcements_inbound.ogg new file mode 100644 index 0000000..1685e91 Binary files /dev/null and b/sound/friendly_reinforcements_inbound.ogg differ diff --git a/sound/friendly_sead.ogg b/sound/friendly_sead.ogg new file mode 100644 index 0000000..c6a8bf6 Binary files /dev/null and b/sound/friendly_sead.ogg differ diff --git a/sound/friendly_strike_inbound.ogg b/sound/friendly_strike_inbound.ogg new file mode 100644 index 0000000..7790405 Binary files /dev/null and b/sound/friendly_strike_inbound.ogg differ diff --git a/sound/friendly_units_under_attack.ogg b/sound/friendly_units_under_attack.ogg new file mode 100644 index 0000000..842b7dd Binary files /dev/null and b/sound/friendly_units_under_attack.ogg differ diff --git a/sound/hostile_target_destroyed.ogg b/sound/hostile_target_destroyed.ogg new file mode 100644 index 0000000..f5cb692 Binary files /dev/null and b/sound/hostile_target_destroyed.ogg differ diff --git a/sound/losing_battle.ogg b/sound/losing_battle.ogg new file mode 100644 index 0000000..78685f5 Binary files /dev/null and b/sound/losing_battle.ogg differ diff --git a/sound/primary_target_destroyed.ogg b/sound/primary_target_destroyed.ogg new file mode 100644 index 0000000..0717eb6 Binary files /dev/null and b/sound/primary_target_destroyed.ogg differ diff --git a/sound/target_destroyed.ogg b/sound/target_destroyed.ogg new file mode 100644 index 0000000..c9d124a Binary files /dev/null and b/sound/target_destroyed.ogg differ