From bca47d63d64461fc7209878d8cd6380ff4f2cc17 Mon Sep 17 00:00:00 2001 From: spencershepard Date: Sat, 11 Feb 2023 20:54:37 -0800 Subject: [PATCH] BHD2 and infantry update (#48) * BHD2 and infantry update - default to easy comms - modify ctld for better infantry models - cleaner logging - warn user if no resources for fat cow FARPs - condition for fat cow added; not available if enemies too close * Update README.md --- Generator/MissionGenerator.py | 1 + Generator/RotorOpsMission.py | 26 ++++++- Generator/version.py | 2 +- README.md | 13 +++- scripts/CTLD.lua | 14 ++-- scripts/RotorOps.lua | 16 ++-- scripts/RotorOpsPerks.lua | 134 ++++++++++++++++++++++++---------- 7 files changed, 153 insertions(+), 53 deletions(-) diff --git a/Generator/MissionGenerator.py b/Generator/MissionGenerator.py index f56f25d..4fb90cd 100644 --- a/Generator/MissionGenerator.py +++ b/Generator/MissionGenerator.py @@ -593,6 +593,7 @@ class Window(QMainWindow, Ui_MainWindow): "blue_cap": self.scenario.getConfigValue("blue_cap", default=True), "rotorops_server": self.scenario.getConfigValue("rotorops_server", default=False), "perks": self.perks_checkBox.isChecked(), + "easy_comms": self.scenario.getConfigValue("easy_comms", default=True) } logger.info("Generating mission with options:") diff --git a/Generator/RotorOpsMission.py b/Generator/RotorOpsMission.py index 3c2b1d5..025c76b 100644 --- a/Generator/RotorOpsMission.py +++ b/Generator/RotorOpsMission.py @@ -401,6 +401,12 @@ class RotorOpsMission: self.m.random_daytime(options["time"].lower()) print("Time set to " + options["time"]) + # set the mission options + if options["easy_comms"]: + # to simplify rearm/refuel at FARPs + self.m.options.difficulty.easyCommunication = True + + # Save the mission file window.statusBar().showMessage("Saving mission...", 10000) if window.user_output_dir: @@ -824,7 +830,7 @@ class RotorOpsMission: self.m.triggers.add_triggerzone(f_cap_spawn_point, 30000, hidden=True, name="BLUE_CAP_SPAWN") # Fat Cow - if True: + if options["perks"]: helo_type = dcs.helicopters.CH_47D name = "FAT COW" @@ -852,6 +858,24 @@ class RotorOpsMission: afg.set_skill(dcs.unit.Skill.Excellent) afg.late_activation = True + else: + afg = self.m.flight_group_inflight( + combinedJointTaskForcesBlue, + name, + helo_type, + position=primary_f_airport.position, + altitude=500, + speed=50, + group_size=1 + ) + + if afg: + afg.set_skill(dcs.unit.Skill.Excellent) + afg.late_activation = True + + else: + raise Exception("Unable to insert Fat Cow CH-47") + if options["f_awacs"]: awacs_name = "AWACS" diff --git a/Generator/version.py b/Generator/version.py index 31b6982..2df480d 100644 --- a/Generator/version.py +++ b/Generator/version.py @@ -1,7 +1,7 @@ # ROTOROPS VERSION maj_version = 1 minor_version = 4 -patch_version = 3 +patch_version = 4 version_url = 'https://dcs-helicopters.com/app-updates/versioncheck.yaml' diff --git a/README.md b/README.md index 6d6d1e4..c341851 100644 --- a/README.md +++ b/README.md @@ -24,15 +24,24 @@ At the core of the RotorOps script are AI enhancements that provide a dynamic gr - Single-player and multiplayer slot creation. ## Demo Missions -RotorOps: Aleppo Under Siege https://www.digitalcombatsimulator.com/en/files/3320079/ + +Newest to oldest: + +Black Hawk Down Pt 1 (UH-1H UH-60L) https://www.digitalcombatsimulator.com/en/files/3328428/ + +NightHawks (AH-64D) https://www.digitalcombatsimulator.com/en/files/3322036/ + +RotorOps: Aleppo Under Siege https://www.digitalcombatsimulator.com/en/files/3320079/ Rota Landing (Mr. Nobody) https://www.digitalcombatsimulator.com/en/files/3320186/ + + # RotorOps: Conflict Conflict is a game type in which attacking forces must clear Conflict Zones of defending ground forces. Once a zone is cleared, the next zone is activated and attacking ground vehicles will move to the next Conflict Zone automatically. -![alt text](https://raw.githubusercontent.com/spencershepard/RotorOps/develop/documentation/images/rotorops%20conflict%20zones.png?raw=true) +![alt text](https://raw.githubusercontent.com/spencershepard/RotorOps/main/documentation/images/rotorops%20conflict%20zones.png?raw=true) ## Dynamic Roles A RotorOps Conflict mission has opportunities for a variety of roles and tasks. There's no need to artificially select these roles, as the mission is fully dynamic. diff --git a/scripts/CTLD.lua b/scripts/CTLD.lua index 1791a50..aecb1ab 100644 --- a/scripts/CTLD.lua +++ b/scripts/CTLD.lua @@ -26,7 +26,7 @@ ctld = {} -- DONT REMOVE! ctld.Id = "CTLD - " --- Version. -ctld.Version = "20211113.01" +ctld.Version = "20211113.01 GRIMM01" -- debug level, specific to this module ctld.Debug = true @@ -1976,9 +1976,9 @@ function ctld.generateTroopTypes(_side, _countOrTemplate, _country) if _countOrTemplate.inf then ctld.logTrace(string.format("_countOrTemplate.inf=%s", ctld.p(_countOrTemplate.inf))) if _side == 2 then - _troops = ctld.insertIntoTroopsArray("Soldier M4",_countOrTemplate.inf,_troops) + _troops = ctld.insertIntoTroopsArray("Soldier M4 GRG",_countOrTemplate.inf,_troops) else - _troops = ctld.insertIntoTroopsArray("Soldier AK",_countOrTemplate.inf,_troops) + _troops = ctld.insertIntoTroopsArray("Infantry AK",_countOrTemplate.inf,_troops) end _weight = _weight + getSoldiersWeight(_countOrTemplate.inf, ctld.RIFLE_WEIGHT) ctld.logTrace(string.format("_weight=%s", ctld.p(_weight))) @@ -2012,9 +2012,9 @@ function ctld.generateTroopTypes(_side, _countOrTemplate, _country) if _countOrTemplate.jtac then ctld.logTrace(string.format("_countOrTemplate.jtac=%s", ctld.p(_countOrTemplate.jtac))) if _side == 2 then - _troops = ctld.insertIntoTroopsArray("Soldier M4",_countOrTemplate.jtac,_troops, "JTAC") + _troops = ctld.insertIntoTroopsArray("Soldier M4 GRG",_countOrTemplate.jtac,_troops, "JTAC") else - _troops = ctld.insertIntoTroopsArray("Soldier AK",_countOrTemplate.jtac,_troops, "JTAC") + _troops = ctld.insertIntoTroopsArray("Infantry AK",_countOrTemplate.jtac,_troops, "JTAC") end _hasJTAC = true _weight = _weight + getSoldiersWeight(_countOrTemplate.jtac, ctld.JTAC_WEIGHT + ctld.RIFLE_WEIGHT) @@ -2024,7 +2024,7 @@ function ctld.generateTroopTypes(_side, _countOrTemplate, _country) else for _i = 1, _countOrTemplate do - local _unitType = "Soldier AK" + local _unitType = "Infantry AK" if _side == 2 then if _i <=2 then @@ -2040,7 +2040,7 @@ function ctld.generateTroopTypes(_side, _countOrTemplate, _country) _weight = _weight + getSoldiersWeight(1, ctld.MANPAD_WEIGHT) ctld.logTrace(string.format("_unitType=%s, _weight=%s", ctld.p(_unitType), ctld.p(_weight))) else - _unitType = "Soldier M4" + _unitType = "Soldier M4 GRG" _weight = _weight + getSoldiersWeight(1, ctld.RIFLE_WEIGHT) ctld.logTrace(string.format("_unitType=%s, _weight=%s", ctld.p(_unitType), ctld.p(_weight))) end diff --git a/scripts/RotorOps.lua b/scripts/RotorOps.lua index e4c40b0..30d138a 100644 --- a/scripts/RotorOps.lua +++ b/scripts/RotorOps.lua @@ -1,6 +1,6 @@ RotorOps = {} RotorOps.version = "1.3.4" -local debug = true +local debug = false @@ -65,7 +65,7 @@ RotorOps.game_state = 0 RotorOps.zones = {} RotorOps.active_zone = "" --name of the active zone RotorOps.active_zone_index = 0 -RotorOps.game_state_flag = 1 --user flag to store the game state +RotorOps.game_state_flag = 100 --user flag to store the game state RotorOps.staging_zones = {} RotorOps.ai_defending_infantry_groups = {} RotorOps.ai_attacking_infantry_groups = {} @@ -1200,6 +1200,7 @@ function RotorOps.assessUnitsInZone(var) --RotorOps.inf_spawns_avail = RotorOps.inf_spawns_per_zone * RotorOps.inf_spawn_multiplier[RotorOps.active_zone_index] if total_spawn_zones > 0 then RotorOps.inf_spawns_avail = (RotorOps.inf_spawns_total / total_spawn_zones) * #inf_spawn_zones + RotorOps.inf_spawns_avail = math.ceil(RotorOps.inf_spawns_avail) end env.info("ROTOR OPS: zone activated: "..RotorOps.active_zone..", inf spawns avail:"..RotorOps.inf_spawns_avail..", spawn zones:"..#inf_spawn_zones) @@ -1285,7 +1286,8 @@ function RotorOps.assessUnitsInZone(var) 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 + if vehicle:hasAttribute("Infantry carriers") or vehicle:hasAttribute("Trucks") then --if a vehicle is an APC + if 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 @@ -1303,6 +1305,7 @@ function RotorOps.assessUnitsInZone(var) should_deploy = true apcs[apc_name] = {['deployed_zones'] = {RotorOps.active_zone,}} end + end end @@ -1314,7 +1317,7 @@ function RotorOps.assessUnitsInZone(var) end end - local id = timer.scheduleFunction(timedDeploy, nil, timer.getTime() + math.random(90, 180)) + local id = timer.scheduleFunction(timedDeploy, nil, timer.getTime() + math.random(90, 300)) end end @@ -1332,7 +1335,9 @@ function RotorOps.assessUnitsInZone(var) local zone = inf_spawn_zones[rand_index] ctld.spawnGroupAtTrigger("red", RotorOps.inf_spawn_red, zone, 1000) - RotorOps.gameMsg(RotorOps.gameMsgs.infantry_spawned, math.random(1, #RotorOps.gameMsgs.infantry_spawned)) + if RotorOps.inf_spawn_messages then + RotorOps.gameMsg(RotorOps.gameMsgs.infantry_spawned, math.random(1, #RotorOps.gameMsgs.infantry_spawned)) + end RotorOps.inf_spawns_avail = RotorOps.inf_spawns_avail - 1 env.info("ROTOR OPS: Attempting to spawn infantry. "..RotorOps.inf_spawns_avail.." spawns remaining in "..zone) @@ -2226,3 +2231,4 @@ function RotorOps.predSpawnRedCap() return true end + diff --git a/scripts/RotorOpsPerks.lua b/scripts/RotorOpsPerks.lua index d343516..518caa2 100644 --- a/scripts/RotorOpsPerks.lua +++ b/scripts/RotorOpsPerks.lua @@ -9,13 +9,13 @@ -- Issues: -- - You will not get points for your troops' kills if you leave your group (ie switch aircraft) +-- - Currently requires a modified version of MIST (see rotorops repo /scripts) --Todo: --- - more testing needed in RotorOpsPerks.monitorFarps() to check for destroyed farp objects. RotorOpsPerks = {} -RotorOpsPerks.version = "1.5.1" +RotorOpsPerks.version = "1.5.2" env.warning('ROTOROPS PERKS STARTED: '..RotorOpsPerks.version) trigger.action.outText('ROTOROPS PERKS STARTED: '..RotorOpsPerks.version, 10) RotorOpsPerks.perks = {} @@ -52,11 +52,17 @@ RotorOpsPerks.player_fatcow_types = { } ---- END OPTIONS ---- +local function log(msg) + env.info("ROTOROPS PERKS: " .. msg) +end + local function debugMsg(msg) if RotorOpsPerks.debug then - env.info("ROTOROPS PERKS:") - env.info(msg) + log("ROTOROPS PERKS:") + if msg then + log(msg) + end end end @@ -91,7 +97,28 @@ end RotorOpsPerks.perks.fatcow["action_condition"] = function(args) if #RotorOpsPerks.fat_cow_farps < 1 then return {msg="No FARP resources available!", valid=false} - end + end + + --rearming/refueling doesn't work if enemies are nearby (within 1.1nm) + --this is a DCS feature/limitation so we won't deploy the farp to avoid confusing the players + local units_in_proximity = RotorOpsPerks.findUnitsInVolume({ + volume_type = world.VolumeType.SPHERE, + point = args.target_point, + radius = 2050 + }) + + log("units_in_proximity: "..#units_in_proximity) + + local enemy_coal = 1 + if args.player_coalition == 1 then + enemy_coal = 2 + end + + for _, unit in pairs(units_in_proximity) do + if unit:getCoalition() == enemy_coal then + return {msg="Too close to enemy!", valid=false} + end + end return {valid=true} end @@ -267,17 +294,42 @@ end RotorOpsPerks.perks.player_fatcow["action_condition"] = function(args) local player_unit = Group.getByName(args.player_group_name):getUnit(1) local agl_altitude = player_unit:getPosition().p.y - land.getHeight(player_unit:getPosition().p) + if RotorOpsPerks.perks.player_fatcow.active[args.player_group_name] then return {msg="FARP already deployed at your position!", valid=false} end + if #RotorOpsPerks.fat_cow_farps < 1 then return {msg="No FARP resources available!", valid=false} end + if agl_altitude > 100 then return {msg="You must be on the ground! "..agl_altitude.." AGL", valid=false} - else - return {msg="Stay on the ground.", valid=true} end + + --rearming/refueling doesn't work if enemies are nearby (within 1.1nm) + --this is a DCS feature/limitation so we won't deploy the farp to avoid confusing the players + local units_in_proximity = RotorOpsPerks.findUnitsInVolume({ + volume_type = world.VolumeType.SPHERE, + point = args.target_point, + radius = 2050 + }) + + log("units_in_proximity: "..#units_in_proximity) + + local enemy_coal = 1 + if args.player_coalition == 1 then + enemy_coal = 2 + end + + for _, unit in pairs(units_in_proximity) do + if unit:getCoalition() == enemy_coal then + return {msg="Too close to enemy!", valid=false} + end + end + + + return {msg="Stay on the ground.", valid=true} end RotorOpsPerks.perks.player_fatcow["action_function"] = function(args) @@ -302,10 +354,10 @@ RotorOpsPerks.perks.player_fatcow["monitor_player"] = function(args) local agl_altitude = player_unit:getPosition().p.y - land.getHeight(player_unit:getPosition().p) if agl_altitude > 100 or not player_unit:isExist() then despawn_farp = true - env.info("Player is no longer on the ground, despawning FARP!") + log("Player is no longer on the ground, despawning FARP!") end if math.abs(player_unit:getPosition().p.x - args.target_point.x) > 50 or math.abs(player_unit:getPosition().p.z - args.target_point.z) > 50 then - env.info("Player has moved from target position, despawning FARP!") + log("Player has moved from target position, despawning FARP!") despawn_farp = true end else @@ -435,8 +487,8 @@ function RotorOpsPerks.checkPoints(player_group_name) return false end - env.info("Checking points for "..player_group_name.."...") - env.info(mist.utils.tableShow(players, "players")) + log("Checking points for "..player_group_name.."...") + log(mist.utils.tableShow(players, "players")) --get combined points from all Players local total_points = 0 @@ -510,7 +562,7 @@ function RotorOpsPerks.updatePlayer(identifier, groupName, name, slot) perks_used = {}, } env.warning('ADDED ' .. identifier .. ' TO PLAYERS TABLE') - env.info(mist.utils.tableShow(RotorOpsPerks.players[identifier])) + log(mist.utils.tableShow(RotorOpsPerks.players[identifier])) missionCommands.removeItemForGroup(groupId, {[1] = 'ROTOROPS PERKS'}) RotorOpsPerks.addRadioMenuForGroup(groupName) if RotorOpsPerks.player_update_messages then @@ -520,7 +572,7 @@ function RotorOpsPerks.updatePlayer(identifier, groupName, name, slot) --update an existing player elseif RotorOpsPerks.players[identifier].groupId ~= groupId then env.warning('UPDATING ' .. identifier .. ' TO GROUP NAME: ' .. groupName) - env.info(mist.utils.tableShow(RotorOpsPerks.players[identifier])) + log(mist.utils.tableShow(RotorOpsPerks.players[identifier])) if RotorOpsPerks.player_update_messages then trigger.action.outText('PERKS: ' .. name .. ' moved to '.. groupName, 10) end @@ -590,7 +642,7 @@ end ---- FATCOW FARP SUPPORTING FUNCTIONS ---- function RotorOpsPerks.monitorFarps() - env.info(mist.utils.tableShow(RotorOpsPerks.fat_cow_farps)) + --log(mist.utils.tableShow(RotorOpsPerks.fat_cow_farps)) local function farpExists(i) local farp = StaticObject.getByName('FAT COW FARP ' .. i) @@ -627,7 +679,7 @@ function RotorOpsPerks.buildFatCowFarpTable() local ammo = StaticObject.getByName('FAT COW AMMO ' .. i) local fuel = StaticObject.getByName('FAT COW FUEL ' .. i) if farp and tent and ammo and fuel then - env.info("FAT COW FARP " .. i .. " FOUND") + log("FAT COW FARP " .. i .. " FOUND") RotorOpsPerks.fat_cow_farps[i] = { index = i, farp = farp, @@ -655,7 +707,7 @@ function RotorOpsPerks.teleportStatic(source_name, dest_point) debugMsg('RotorOpsPerks.teleportStatic: ' .. source_name) local source = StaticObject.getByName(source_name) if not source then - env.info('RotorOpsPerks.teleportStatic: source not found: ' .. source_name) + log('RotorOpsPerks.teleportStatic: source not found: ' .. source_name) return end local vars = {} @@ -664,14 +716,14 @@ function RotorOpsPerks.teleportStatic(source_name, dest_point) vars.point = mist.utils.makeVec3(dest_point) local res = mist.teleportToPoint(vars) if res then - env.info('RotorOpsPerks.teleportStatic: ' .. source_name .. ' success') + log('RotorOpsPerks.teleportStatic: ' .. source_name .. ' success') else - env.info('RotorOpsPerks.teleportStatic: ' .. source_name .. ' failed') + log('RotorOpsPerks.teleportStatic: ' .. source_name .. ' failed') end end function RotorOpsPerks.spawnFatCowFarpObjects(pt_x, pt_y, index, delay) - env.info('spawnFatCowFarpObjects called. Looking for static group names ending in ' .. index) + log('spawnFatCowFarpObjects called. Looking for static group names ending in ' .. index) local dest_point = mist.utils.makeVec3GL({x = pt_x, y = pt_y}) trigger.action.smoke(dest_point, 2) @@ -693,12 +745,12 @@ function RotorOpsPerks.spawnFatCow(dest_point, farp) local fatcow_name = 'FAT COW' local source_farp_name = 'FAT COW FARP ' .. index - env.info('spawnFatCow called with ' .. source_farp_name) + log('spawnFatCow called with ' .. source_farp_name) --set a timer to return the farp static resources to be reused timer.scheduleFunction(function() table.insert(RotorOpsPerks.fat_cow_farps, farp) --put it back at the end of the list - env.info('FatCow FARP timer expired, making the farp available to be used again.') + log('FatCow FARP timer expired, making the farp available to be used again.') end, nil, timer.getTime() + 1800) dest_point = mist.utils.makeVec2(dest_point) @@ -793,8 +845,8 @@ function RotorOpsPerks.spawnFatCow(dest_point, farp) end function RotorOpsPerks.requestPerk(args) - env.info('requestPerk called for ' .. args.perk_name) - --env.info(mist.utils.tableShow(args, 'args')) + log('requestPerk called for ' .. args.perk_name) + --log(mist.utils.tableShow(args, 'args')) local player_group = Group.getByName(args.player_group_name) local player_unit = player_group:getUnits()[1] local player_unit_name = player_unit:getName() @@ -834,7 +886,7 @@ function RotorOpsPerks.requestPerk(args) mark_name = mark_name:gsub("\n", "") if mark_name == args.perk_name then perk_name_matches = true - env.info("mark name matches perk name") + log("mark name matches perk name") end if perk_name_matches then @@ -866,9 +918,9 @@ function RotorOpsPerks.requestPerk(args) end end - -- env.info(mist.utils.tableShow(mist.DBs.markList, 'markList')) - -- env.info('player group' .. mist.utils.tableShow(player_group, 'player_group')) - -- env.info('player' .. mist.utils.tableShow(player_unit, 'player_unit')) + -- log(mist.utils.tableShow(mist.DBs.markList, 'markList')) + -- log('player group' .. mist.utils.tableShow(player_group, 'player_group')) + -- log('player' .. mist.utils.tableShow(player_unit, 'player_unit')) if temp_mark then target_point = temp_mark.pos end @@ -923,9 +975,10 @@ function RotorOpsPerks.requestPerk(args) args.player_group = player_group args.player_unit = player_unit args.player_unit_name = player_unit_name + args.player_coalition = player_group:getCoalition() --show all variables available to perk actions and conditions - --env.info('args: ' .. mist.utils.tableShow(args, 'args')) + --log('args: ' .. mist.utils.tableShow(args, 'args')) --check the perk's unique prerequisite conditions @@ -947,9 +1000,9 @@ function RotorOpsPerks.requestPerk(args) --check points if RotorOpsPerks.spendPoints(args.player_group_name, perk.cost, false) then - env.info(args.player_group_name.. ' has sufficient (' .. perk.cost .. ') points for ' .. args.perk_name) + log(args.player_group_name.. ' has sufficient (' .. perk.cost .. ') points for ' .. args.perk_name) else - env.info(args.player_group_name.. ' tried to spend ' .. perk.cost .. ' points for ' .. args.perk_name .. ' but did not have enough points') + log(args.player_group_name.. ' tried to spend ' .. perk.cost .. ' points for ' .. args.perk_name .. ' but did not have enough points') if #players == 1 then trigger.action.outTextForGroup(player_group:getID(), 'NEGATIVE. You have ' .. RotorOpsPerks.getPlayerGroupSum(args.player_group_name, "points") .. ' points. (cost '.. perk.cost .. ')', 10) else @@ -996,7 +1049,7 @@ function RotorOpsPerks.requestPerk(args) trigger.action.outTextForGroup(_player.groupId, 'AFFIRM. ' .. RotorOpsPerks.perks[args.perk_name].display_name .. position_string, 10) else -- send messages to all other players - env.info(player_unit:getPlayerName() .. ' requested ' .. RotorOpsPerks.perks[args.perk_name].display_name .. position_string) + log(player_unit:getPlayerName() .. ' requested ' .. RotorOpsPerks.perks[args.perk_name].display_name .. position_string) trigger.action.outTextForGroup(_player.groupId, player_unit:getPlayerName() .. ' requested ' .. RotorOpsPerks.perks[args.perk_name].display_name .. position_string, 10) end end @@ -1157,11 +1210,11 @@ function RotorOpsPerks.registerCtldCallbacks() local unit = _args.unit local picked_troops = _args.onboard local dropped_troops = _args.unloaded - --env.info("ctld callback: ".. mist.utils.tableShow(_args)) + --log("ctld callback: ".. mist.utils.tableShow(_args)) if dropped_troops then - --env.info('dropped troops: ' .. mist.utils.tableShow(dropped_troops)) - --env.info('dropped troops group name: ' .. dropped_troops:getName()) + --log('dropped troops: ' .. mist.utils.tableShow(dropped_troops)) + --log('dropped troops group name: ' .. dropped_troops:getName()) RotorOpsPerks.troops[dropped_troops:getName()] = {dropped_troops=dropped_troops:getName(), player_group=unit:getGroup():getName(), player_name=unit:getPlayerName(), player_unit=unit:getName(), side=unit:getGroup():getCoalition() , qty=#dropped_troops:getUnits()} end @@ -1253,16 +1306,23 @@ function RotorOpsPerks.monitorPlayers() end if mist.grimm_version then - env.info("GRIMM's version of MIST is loaded") + log("GRIMM's version of MIST is loaded") else - env.warning("ROTOROPS PERKS REQUIRES A MODIFIED VERSION OF MIST TO WORK PROPERLY. PLEASE SEE THE SCRIPTS FOLDER IN THE ROTOROPS GITHUB REPO") + env.warning("ERROR: ROTOROPS PERKS REQUIRES A MODIFIED VERSION OF MIST TO WORK PROPERLY. PLEASE SEE THE SCRIPTS FOLDER IN THE ROTOROPS GITHUB REPO") trigger.action.outText("ERROR: ROTOROPS PERKS REQUIRES A MODIFIED VERSION OF MIST TO WORK PROPERLY.", 30) end RotorOpsPerks.buildFatCowFarpTable() -env.info("Found " .. #RotorOpsPerks.fat_cow_farps .. " Fat Cow FARPs") +log("Found " .. #RotorOpsPerks.fat_cow_farps .. " Fat Cow FARPs") if #RotorOpsPerks.fat_cow_farps > 0 then RotorOpsPerks.monitorFarps() +else + env.warning("NO FAT COW FARPS FOUND. PLEASE SEE THE ROTOROPS WIKI FOR INSTRUCTIONS ON HOW TO SET UP FAT COW FARPS") + trigger.action.outText("WARNING: NO FAT COW FARPS FOUND.", 30) +end +if not Group.getByName('FAT COW') then + env.warning("NO AI FAT COW HELICOPTER FOUND. PLEASE SEE THE ROTOROPS WIKI FOR INSTRUCTIONS ON HOW TO SET UP FAT COW FARPS") + trigger.action.outText("WARNING: NO AI FAT COW HELICOPTER FOUND.", 30) end RotorOpsPerks.registerCtldCallbacks() -- start a 5 second timer to monitor players, to allow other scripts to load