mirror of
https://github.com/spencershepard/RotorOps.git
synced 2025-11-10 15:45:30 +00:00
1795 lines
67 KiB
Lua
1795 lines
67 KiB
Lua
--ROTOROPS PERKS by GRIMM
|
|
--Points and rewards system
|
|
--Check out RotorOps at dcs-helicopters.com
|
|
--Full documentation on Github (see the Wiki: RotorOps PERKS)
|
|
|
|
--How to use: load the script in do script trigger after the mission begins. Requires MIST, but CTLD is optional. Load scripts in this order: 1) MIST 2) CTLD 3) RotorOpsPerks
|
|
--This script will add a new menu to the F10 menu called "RotorOps Perks". This menu will allow you to select a perk to use.
|
|
--You can define the points earner per action, and the perk options below.
|
|
|
|
-- 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:
|
|
|
|
|
|
RotorOpsPerks = {}
|
|
RotorOpsPerks.version = "1.7.1"
|
|
env.warning('ROTOROPS PERKS STARTED: '..RotorOpsPerks.version)
|
|
trigger.action.outText('ROTOROPS PERKS STARTED: '..RotorOpsPerks.version, 10)
|
|
RotorOpsPerks.perks = {}
|
|
RotorOpsPerks.players = {}
|
|
RotorOpsPerks.players_temp = {}
|
|
RotorOpsPerks.troops = {} --by group name
|
|
RotorOpsPerks.fat_cow_farps = {}
|
|
RotorOpsPerks.callbacks = {}
|
|
|
|
---- OPTIONS ----
|
|
|
|
RotorOpsPerks.silent_points = false --set to true to disable text on points scoring
|
|
RotorOpsPerks.player_update_messages = true --set to false to disable messages when players are added/updated to score keeping
|
|
RotorOpsPerks.debug = false
|
|
|
|
RotorOpsPerks.points = {
|
|
player_default=0, --how many points each player will start with
|
|
kill=10,
|
|
kill_inf=5,
|
|
kill_heli=20,
|
|
kill_plane=20,
|
|
kill_armor=15,
|
|
kill_ship=15,
|
|
cas_bonus=5, --you killed something in proximity of friendly troops
|
|
dropped_troops_kill_inf=10, --your troops killed infantry
|
|
dropped_troops_kill=20, --your troops killed a vehicle
|
|
dropped_troops_kill_armor=30, --your troops killed armor
|
|
dropped_troops_kill_plane=50, --your troops killed an plane
|
|
dropped_troops_kill_heli=50, --your troops killed a helicopter
|
|
rearm=15, --ctld rearm/repair of ground units
|
|
unpack=15, --ctld unpack of ground units
|
|
}
|
|
|
|
RotorOpsPerks.player_fatcow_types = {
|
|
"UH-60L",
|
|
"Mi-8MT",
|
|
"CH-47Fbl1"
|
|
}
|
|
|
|
RotorOpsPerks.static_helicopters_at_farp = true --spawn static helicopters at the FARP if player reslots
|
|
RotorOpsPerks.static_helicopter_types = { --mapping of player helicopters to their static equivalents
|
|
["UH-60L"] = "UH-60A",
|
|
["Mi-8MT"] = "Mi-8MT",
|
|
["CH-47Fbl1"] = "CH-47D"
|
|
}
|
|
|
|
---- END OPTIONS ----
|
|
local function log(msg, log_type)
|
|
if type(msg) == 'table' then
|
|
msg = mist.utils.tableShow(msg)
|
|
end
|
|
if not msg then
|
|
msg = "nil"
|
|
end
|
|
if log_type == "error" then
|
|
env.error("ROTOROPS PERKS ERROR: " .. msg)
|
|
else
|
|
env.info("ROTOROPS PERKS: " .. msg)
|
|
end
|
|
end
|
|
|
|
|
|
local function debugMsg(msg)
|
|
if RotorOpsPerks.debug then
|
|
if msg then
|
|
log(msg)
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
---- FATCOW PERK ----
|
|
--Fat Cow FARP requires static farp objects to work (they are teleported to the landing zone), and a late activated helicopter called 'FAT COW'. See the wiki for more details.
|
|
|
|
RotorOpsPerks.perks["fatcow"] = {
|
|
perk_name='fatcow',
|
|
display_name='FatCow FARP',
|
|
cost=150,
|
|
cooldown=60,
|
|
max_per_player=4,
|
|
max_per_mission=4,
|
|
at_mark=true,
|
|
at_position=true,
|
|
enabled=true,
|
|
sides={0,1,2},
|
|
}
|
|
|
|
RotorOpsPerks.perks.fatcow["menu_condition"] = function(group_name)
|
|
local player_unit = Group.getByName(group_name):getUnit(1)
|
|
-- if player IS a fatcow, return false
|
|
for _, unit_type in pairs(RotorOpsPerks.player_fatcow_types) do
|
|
if player_unit:getDesc().typeName == unit_type then
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
RotorOpsPerks.perks.fatcow["action_condition"] = function(args)
|
|
if #RotorOpsPerks.fat_cow_farps < 1 then
|
|
return {msg="No FARP resources available!", valid=false}
|
|
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
|
|
|
|
RotorOpsPerks.perks.fatcow["action_function"] = function(args)
|
|
local farp = RotorOpsPerks.getFatCowFarp()
|
|
if farp == nil then
|
|
env.error("No FARP resources available!")
|
|
return
|
|
end
|
|
RotorOpsPerks.spawnAiChinook(args.target_point, farp.index)
|
|
return true
|
|
end
|
|
|
|
---- End of FATCOW PERK ----
|
|
|
|
|
|
|
|
---- INSTANT STRIKE PERK ----
|
|
-- Here's a very simple example of how to create a Perk!
|
|
|
|
RotorOpsPerks.perks["strike"] = {
|
|
perk_name='strike',
|
|
display_name='Instant Strike',
|
|
cost=150,
|
|
cooldown=60,
|
|
max_per_player=3,
|
|
max_per_mission=10,
|
|
at_mark=true,
|
|
at_position=false,
|
|
enabled=true,
|
|
sides={0,1,2},
|
|
}
|
|
|
|
RotorOpsPerks.perks.strike["action_function"] = function(args)
|
|
--explosion at dest_point after 10 seconds
|
|
timer.scheduleFunction(function()
|
|
trigger.action.explosion(args.target_point, 1000)
|
|
end, nil, timer.getTime() + 10)
|
|
return true
|
|
end
|
|
|
|
---- End of INSTANT STRIKE PERK ----
|
|
|
|
|
|
---- JTAC DRONE PERK ----
|
|
|
|
RotorOpsPerks.perks["drone"] = {
|
|
perk_name='drone',
|
|
display_name='JTAC Drone',
|
|
cost=75,
|
|
cooldown=60,
|
|
max_per_player=3,
|
|
max_per_mission=6,
|
|
at_mark=true,
|
|
at_position=false,
|
|
enabled=true,
|
|
sides={0,1,2},
|
|
}
|
|
|
|
|
|
RotorOpsPerks.perks.drone["action_function"] = function(args)
|
|
local player_country = Unit.getByName(args.player_unit_name):getCountry()
|
|
|
|
--set a timer for one minute
|
|
timer.scheduleFunction(function()
|
|
local code = table.remove(ctld.jtacGeneratedLaserCodes, 1)
|
|
RotorOpsPerks.spawnJtacDrone(args.target_point, player_country, code)
|
|
table.insert(ctld.jtacGeneratedLaserCodes, code)
|
|
end, nil, timer.getTime() + 60)
|
|
return true
|
|
end
|
|
|
|
function RotorOpsPerks.spawnJtacDrone(dest_point, country, laser_code)
|
|
|
|
local drone = {
|
|
x = dest_point.x+1500,
|
|
y = dest_point.z,
|
|
type = "RQ-1A Predator",
|
|
speed = 70,
|
|
heading = 0,
|
|
altitude = 5000,
|
|
country = player_country,
|
|
skill = "High",
|
|
category = "plane",
|
|
livery_id = "USAF Standard",
|
|
payload = {
|
|
["pylons"] = {},
|
|
["fuel"] = 200,
|
|
["flare"] = 0,
|
|
["chaff"] = 0,
|
|
["gun"] = 0,
|
|
},
|
|
}
|
|
|
|
local drone_route = {
|
|
[1] = {
|
|
["alt"] = 5000,
|
|
["x"] = dest_point.x,
|
|
["action"] = "Turning Point",
|
|
["alt_type"] = "BARO",
|
|
["speed"] = 70,
|
|
["form"] = "Turning Point",
|
|
["type"] = "Turning Point",
|
|
["y"] = dest_point.z+1000,
|
|
},
|
|
}
|
|
|
|
local drone_group = {
|
|
units = {drone,},
|
|
country = country,
|
|
category = "airplane",
|
|
route = drone_route,
|
|
name="Reaper"
|
|
}
|
|
|
|
local orbit = {
|
|
id = 'Orbit',
|
|
params = {
|
|
pattern = 'Circle',
|
|
point = {x = dest_point.x, y = dest_point.z},
|
|
speed = 70,
|
|
altitude = 5000,
|
|
}
|
|
}
|
|
|
|
|
|
local new_group = mist.dynAdd(drone_group)
|
|
if new_group == nil then
|
|
return
|
|
end
|
|
trigger.action.outText('JTAC DRONE IS ON STATION!', 10)
|
|
|
|
|
|
--set a timer for one minute
|
|
timer.scheduleFunction(function()
|
|
local _code = table.remove(ctld.jtacGeneratedLaserCodes, 1)
|
|
log('Setting up JTAC DRONE ' .. new_group.name .. ' to lase targets on: '.._code)
|
|
Group.getByName(new_group.name):getController():setTask(orbit)
|
|
Group.getByName(new_group.name):getController():setOption(AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.NO_REACTION)
|
|
ctld.JTACAutoLase(new_group.name, laser_code, true, "vehicle")
|
|
table.insert(ctld.jtacGeneratedLaserCodes, _code)
|
|
|
|
end, nil, timer.getTime() + 60)
|
|
|
|
end
|
|
|
|
|
|
---- End of JTAC DRONE PERK ----
|
|
|
|
|
|
---- PLAYER FAT COW ----
|
|
|
|
RotorOpsPerks.perks["player_fatcow"] = {
|
|
perk_name='player_fatcow',
|
|
display_name='Player Fat Cow',
|
|
cost=0,
|
|
cooldown=0,
|
|
at_mark=false,
|
|
at_position=true,
|
|
enabled=true,
|
|
sides={0,1,2},
|
|
active = {}, --holds a list of active fat cows
|
|
}
|
|
|
|
RotorOpsPerks.perks.player_fatcow["menu_condition"] = function(group_name)
|
|
local player_unit = Group.getByName(group_name):getUnit(1)
|
|
-- check if player unit is in list of our fat cow heli types
|
|
for _, unit_type in pairs(RotorOpsPerks.player_fatcow_types) do
|
|
if player_unit:getDesc().typeName == unit_type then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
RotorOpsPerks.perks.player_fatcow["action_condition"] = function(args)
|
|
local player_unit = Group.getByName(args.player_group_name):getUnit(1)
|
|
local player_name = player_unit:getPlayerName()
|
|
|
|
if player_name and RotorOpsPerks.perks.player_fatcow.active[player_name] then
|
|
return {msg="You already have a Fat Cow farp!", valid=false}
|
|
end
|
|
|
|
if #RotorOpsPerks.fat_cow_farps < 1 then
|
|
return {msg="No FARP resources available!", valid=false}
|
|
end
|
|
|
|
if player_unit:inAir() then
|
|
return {msg="You must be on the ground!", valid=false}
|
|
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)
|
|
local farp = RotorOpsPerks.getFatCowFarp()
|
|
if farp == nil then
|
|
env.error("No FARP resources available!")
|
|
return
|
|
end
|
|
local dest_point = {x=args.target_point.x, z=args.target_point.z}
|
|
RotorOpsPerks.teleportStatic('FAT COW FARP ' .. farp.index, {x=args.target_point.x, y=args.target_point.z})
|
|
local owner_player_name = Group.getByName(args.player_group_name):getUnit(1):getPlayerName()
|
|
local monitored_unit = Group.getByName(args.player_group_name):getUnit(1)
|
|
local request = {
|
|
dest_point = dest_point,
|
|
owner_player_name = owner_player_name,
|
|
monitored_unit = monitored_unit,
|
|
immediate_deploy = true,}
|
|
RotorOpsPerks.changeFarpState(farp.index, "requested", request)
|
|
RotorOpsPerks.perks.player_fatcow.active[owner_player_name] = true
|
|
farp = RotorOpsPerks.fat_cow_farps[farp.index]
|
|
return { farp = farp, request = request }
|
|
end
|
|
|
|
---- End of PLAYER FAT COW ----
|
|
|
|
---- FLARE PERK ----
|
|
|
|
RotorOpsPerks.perks["flare"] = {
|
|
perk_name='flare',
|
|
display_name='Illumination Flare',
|
|
cost=15,
|
|
cooldown=0,
|
|
at_mark=true,
|
|
at_position=false,
|
|
enabled=true,
|
|
sides={0,1,2},
|
|
}
|
|
|
|
RotorOpsPerks.perks.flare["action_function"] = function(args)
|
|
args.target_point.y = args.target_point.y + 600
|
|
trigger.action.illuminationBomb(args.target_point, 1000000)
|
|
return true
|
|
end
|
|
|
|
---- End of FLARE PERK ----
|
|
|
|
|
|
function RotorOpsPerks.getPlayerGroupSum(player_group_name, player_attribute, table_name)
|
|
--loop through RotorOpsPerks.playersByGroupName
|
|
local players = RotorOpsPerks.playersByGroupName(player_group_name)
|
|
if not players then
|
|
return false
|
|
end
|
|
|
|
local total = 0
|
|
for _, player in pairs(players) do
|
|
if table_name then
|
|
total = total + (player[table_name][player_attribute] or 0)
|
|
else
|
|
total = total + (player[player_attribute] or 0)
|
|
end
|
|
end
|
|
return total
|
|
|
|
end
|
|
|
|
|
|
function RotorOpsPerks.spendPoints(player_group_name, points, deduct_points)
|
|
local players = RotorOpsPerks.playersByGroupName(player_group_name)
|
|
local total_points = RotorOpsPerks.getPlayerGroupSum(player_group_name, "points")
|
|
--if players have enough combined points
|
|
if total_points < points then
|
|
--there was insufficient points
|
|
return false
|
|
end
|
|
|
|
--this function can be called to check points and/or deduct points
|
|
if deduct_points then
|
|
--divide points by the number of players to get an integer
|
|
local points_per_player = math.floor(points/#players)
|
|
|
|
--subtract points from each player equally. If a player doesn't have enough points, subtract the remainder from the next player
|
|
local remainder = 0
|
|
for _, player in pairs(players) do
|
|
local points_to_subtract = points_per_player + remainder
|
|
if player.points < points_to_subtract then
|
|
remainder = points_to_subtract - player.points
|
|
player.points_used = player.points_used + points_to_subtract - remainder
|
|
player.points = 0
|
|
else
|
|
player.points = player.points - points_to_subtract
|
|
player.points_used = player.points_used + points_to_subtract
|
|
remainder = 0
|
|
end
|
|
RotorOpsPerks.processCallbacks({action='onPointsSpent', player=player, points_spent=points_to_subtract})
|
|
end
|
|
RotorOpsPerks.processCallbacks({action='onGroupPointsSpent', player_group_name=player_group_name, points_spent=points})
|
|
end
|
|
--there was sufficient points
|
|
return true
|
|
end
|
|
|
|
function RotorOpsPerks.scorePoints(player_group_name, points, message)
|
|
--score points for all players in the group
|
|
local players = RotorOpsPerks.playersByGroupName(player_group_name)
|
|
if players then
|
|
for _, player in pairs(players) do
|
|
player.points = player.points + points
|
|
player.points_earned = player.points_earned + points
|
|
RotorOpsPerks.processCallbacks({action='onPointsScored', player=player, points_scored=points})
|
|
end
|
|
if message and not RotorOpsPerks.silent_points then
|
|
local total = RotorOpsPerks.getPlayerGroupSum(player_group_name, "points")
|
|
if #players > 1 then
|
|
message = message.." +"..points.." points (" .. total .. " group total)"
|
|
else
|
|
message = message.." +"..points.." points (" .. total .. ")"
|
|
end
|
|
trigger.action.outTextForGroup(Group.getByName(player_group_name):getID(), message, 10)
|
|
end
|
|
end
|
|
RotorOpsPerks.processCallbacks({action='onGroupPointsScored', player_group_name=player_group_name, points_scored=points})
|
|
|
|
end
|
|
|
|
function RotorOpsPerks.getPointsBalance(player_group_name)
|
|
local players = RotorOpsPerks.playersByGroupName(player_group_name)
|
|
if not players then
|
|
return false
|
|
end
|
|
|
|
local total_points = 0
|
|
for _, player in pairs(players) do
|
|
total_points = total_points + player.points
|
|
end
|
|
return total_points
|
|
end
|
|
|
|
function RotorOpsPerks.getPlayerMissionPointsBalance(id)
|
|
local player = RotorOpsPerks.players[id]
|
|
if not player then
|
|
return false
|
|
end
|
|
return player.points_earned - player.points_used
|
|
end
|
|
|
|
function RotorOpsPerks.checkPoints(player_group_name)
|
|
local groupId = Group.getByName(player_group_name):getID()
|
|
local players = RotorOpsPerks.playersByGroupName(player_group_name)
|
|
if not players then
|
|
return false
|
|
end
|
|
|
|
log("Checking points for "..player_group_name.."...")
|
|
log(mist.utils.tableShow(players, "players"))
|
|
|
|
--get combined points from all Players
|
|
local total_points = 0
|
|
for _, player in pairs(players) do
|
|
total_points = total_points + player.points
|
|
end
|
|
if #players == 1 then
|
|
trigger.action.outTextForGroup(groupId, 'You have ' .. total_points .. ' points.', 10)
|
|
else
|
|
trigger.action.outTextForGroup(groupId, 'Your group has ' .. total_points .. ' total points.', 10)
|
|
end
|
|
end
|
|
|
|
function RotorOpsPerks.buildPlayer(identifier, groupName, name, slot, temp_id)
|
|
-- if we're missing any of the required attributes, add to temp table until we collect all attributes
|
|
if not groupName or not name or not slot then
|
|
--create the temp player object if doesn't exist yet
|
|
if not RotorOpsPerks.players_temp[temp_id] then
|
|
RotorOpsPerks.players_temp[temp_id] = {
|
|
identifier=identifier,
|
|
name=name,
|
|
slot=slot,
|
|
groupName = groupName,
|
|
}
|
|
end
|
|
--store individual attributes if available
|
|
RotorOpsPerks.players_temp[temp_id].identifier = identifier or RotorOpsPerks.players_temp[temp_id].identifier
|
|
RotorOpsPerks.players_temp[temp_id].name = name or RotorOpsPerks.players_temp[temp_id].name
|
|
RotorOpsPerks.players_temp[temp_id].slot = slot or RotorOpsPerks.players_temp[temp_id].slot
|
|
RotorOpsPerks.players_temp[temp_id].groupName = groupName or RotorOpsPerks.players_temp[temp_id].groupName
|
|
--reassign the function args
|
|
identifier = RotorOpsPerks.players_temp[temp_id].identifier
|
|
name = RotorOpsPerks.players_temp[temp_id].name
|
|
slot = RotorOpsPerks.players_temp[temp_id].slot
|
|
groupName = RotorOpsPerks.players_temp[temp_id].groupName
|
|
--if we're still missing attributes, return
|
|
if not groupName or not name or not slot or not identifier then
|
|
-- env.warning('MISSING ATTRIBUTES FOR ' .. temp_id)
|
|
debugMsg(mist.utils.tableShow(RotorOpsPerks.players_temp[temp_id]))
|
|
return
|
|
end
|
|
|
|
--we have all we need, so add to the players table
|
|
debugMsg('BUILDPLAYER: Now adding ' .. temp_id .. ' to players table as ' .. identifier)
|
|
RotorOpsPerks.updatePlayer(identifier, groupName, name, slot)
|
|
|
|
end
|
|
end
|
|
|
|
|
|
function RotorOpsPerks.updatePlayer(identifier, groupName, name, slot)
|
|
if not Group.getByName(groupName) then
|
|
debugMsg('GROUP ' .. groupName .. ' DOES NOT EXIST')
|
|
return
|
|
end
|
|
|
|
local groupId = Group.getByName(groupName):getID()
|
|
local side = Group.getByName(groupName):getCoalition()
|
|
|
|
|
|
--add a new player
|
|
if not RotorOpsPerks.players[identifier] then
|
|
RotorOpsPerks.players[identifier] = {
|
|
identifier=identifier,
|
|
name=name,
|
|
slot=slot,
|
|
points = RotorOpsPerks.points.player_default,
|
|
groupId = groupId,
|
|
groupName = groupName,
|
|
side = side,
|
|
menu = {},
|
|
perks_used = {},
|
|
points_used = 0,
|
|
points_earned = 0,
|
|
}
|
|
env.warning('ADDED ' .. identifier .. ' TO PLAYERS TABLE')
|
|
log(mist.utils.tableShow(RotorOpsPerks.players[identifier]))
|
|
missionCommands.removeItemForGroup(groupId, {[1] = 'ROTOROPS PERKS'})
|
|
RotorOpsPerks.addRadioMenuForGroup(groupName)
|
|
if RotorOpsPerks.player_update_messages then
|
|
trigger.action.outText('PERKS: Added ' .. name .. ' to '.. groupName, 10)
|
|
end
|
|
RotorOpsPerks.processCallbacks({action='onPlayerAdded', player=RotorOpsPerks.players[identifier], perk=nil})
|
|
|
|
--update an existing player
|
|
elseif RotorOpsPerks.players[identifier].groupId ~= groupId then
|
|
env.warning('UPDATING ' .. identifier .. ' TO GROUP NAME: ' .. groupName)
|
|
log(mist.utils.tableShow(RotorOpsPerks.players[identifier]))
|
|
if RotorOpsPerks.player_update_messages then
|
|
trigger.action.outText('PERKS: ' .. name .. ' moved to '.. groupName, 10)
|
|
end
|
|
|
|
--update player
|
|
RotorOpsPerks.players[identifier].groupId = groupId
|
|
RotorOpsPerks.players[identifier].groupName = groupName
|
|
RotorOpsPerks.players[identifier].side = side
|
|
RotorOpsPerks.players[identifier].slot = slot
|
|
RotorOpsPerks.players[identifier].name = name
|
|
|
|
|
|
--REMOVE RADIO ITEMS FOR GROUP (since another player may have been in the group previously)
|
|
-- missionCommands.removeItemForGroup(groupId, RotorOpsPerks.players[identifier].menu.root)
|
|
missionCommands.removeItemForGroup(groupId, {[1] = 'ROTOROPS PERKS'})
|
|
RotorOpsPerks.addRadioMenuForGroup(groupName)
|
|
|
|
RotorOpsPerks.processCallbacks({action='onPlayerUpdated', player=RotorOpsPerks.players[identifier], perk=nil})
|
|
end
|
|
end
|
|
|
|
--returns a table of players matching the group name
|
|
function RotorOpsPerks.playersByGroupName(group_name)
|
|
local players = {}
|
|
for identifier, player in pairs(RotorOpsPerks.players) do
|
|
if player.groupName == group_name then
|
|
players[#players + 1] = player
|
|
end
|
|
end
|
|
return players
|
|
end
|
|
|
|
|
|
function RotorOpsPerks.addRadioMenuForGroup(groupName)
|
|
local groupId = Group.getByName(groupName):getID()
|
|
local group_side = Group.getByName(groupName):getCoalition()
|
|
|
|
local menu_root = missionCommands.addSubMenuForGroup(groupId, 'ROTOROPS PERKS')
|
|
missionCommands.addCommandForGroup(groupId, 'Check points balance', menu_root, RotorOpsPerks.checkPoints, groupName)
|
|
|
|
for perk_name, perk in pairs(RotorOpsPerks.perks) do
|
|
|
|
local avail_for_side = false
|
|
local avail_for_group = true
|
|
if perk.menu_condition ~= nil then
|
|
avail_for_group = perk.menu_condition(groupName)
|
|
end
|
|
for _, side in pairs(perk.sides) do
|
|
if group_side == side then
|
|
avail_for_side = true
|
|
end
|
|
end
|
|
|
|
|
|
if perk.enabled and avail_for_side and avail_for_group then
|
|
if perk.at_mark then
|
|
--addPerkCommand(groupId, groupName, perk, menu_root, {target='mark'})
|
|
missionCommands.addCommandForGroup(groupId, perk.cost .. ':' .. perk.display_name .. ' at mark (' .. perk.perk_name ..')', menu_root , RotorOpsPerks.requestPerk, {player_group_name=groupName, perk_name=perk.perk_name, target='mark'})
|
|
end
|
|
if perk.at_position then
|
|
--addPerkCommand(groupId, groupName, perk, menu_root, {target='position'})
|
|
missionCommands.addCommandForGroup(groupId, perk.cost .. ':' .. perk.display_name .. ' on me', menu_root , RotorOpsPerks.requestPerk, {player_group_name=groupName, perk_name=perk.perk_name, target='position'})
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
---- FATCOW FARP SUPPORTING FUNCTIONS ----
|
|
|
|
function RotorOpsPerks.monitorFarps()
|
|
--log(mist.utils.tableShow(RotorOpsPerks.fat_cow_farps))
|
|
|
|
local function farpExists(i)
|
|
local farp = StaticObject.getByName('FAT COW FARP ' .. i)
|
|
local tent = StaticObject.getByName('FAT COW TENT ' .. i)
|
|
local ammo = StaticObject.getByName('FAT COW AMMO ' .. i)
|
|
local fuel = StaticObject.getByName('FAT COW FUEL ' .. i)
|
|
if farp:isExist() and tent:isExist() and ammo:isExist() and fuel:isExist() then
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
--schedule the function
|
|
timer.scheduleFunction(RotorOpsPerks.monitorFarps, nil, timer.getTime() + 11)
|
|
--loop over RotorOpsPerks.fat_cow_farps
|
|
for i, farp in pairs(RotorOpsPerks.fat_cow_farps) do
|
|
--check if farp is damaged/destroyed
|
|
if not farpExists(i) then
|
|
trigger.action.outText('Some FARP resources have been destroyed', 30)
|
|
env.warning('FAT COW FARP ' .. i .. ' RESOURCES DESTROYED')
|
|
RotorOpsPerks.fat_cow_farps[i] = nil
|
|
else --farp still exists
|
|
|
|
--**** monitor player farps
|
|
local despawn_farp = false
|
|
if farp.state == "deployed_occupied" then
|
|
|
|
if not farp.monitored_unit:isExist() then
|
|
--despawn_farp = true
|
|
log("FARP monitored_unit doesn't exist, doing NOTHING!")
|
|
elseif farp.monitored_unit:inAir() then
|
|
despawn_farp = true
|
|
log("FARP monitored_unit no longer on the ground, despawning FARP!")
|
|
elseif math.abs(farp.monitored_unit:getPosition().p.x - farp.deployed_p.x) > 50 or math.abs(farp.monitored_unit:getPosition().p.z - farp.deployed_p.z) > 50 then
|
|
log("FARP monitored_unit moved from the FARP, despawning FARP!")
|
|
despawn_farp = true
|
|
end
|
|
end
|
|
|
|
if farp.expiration_time and timer.getTime() > farp.expiration_time then
|
|
log("Fat Cow FARP expired, despawning FARP!")
|
|
trigger.action.outText('Fat Cow FARP expired, despawning FARP!', 5)
|
|
despawn_farp = true
|
|
end
|
|
|
|
if despawn_farp then
|
|
RotorOpsPerks.changeFarpState(farp.index, 'not_deployed', nil)
|
|
end
|
|
--*****
|
|
|
|
end
|
|
end
|
|
end
|
|
|
|
function RotorOpsPerks.buildFatCowFarpTable()
|
|
local farp_found=true
|
|
local i = 1
|
|
while(farp_found) do
|
|
--find static invisible farps that start with name 'FAT COW FARP'
|
|
local farp = StaticObject.getByName('FAT COW FARP ' .. i)
|
|
local tent = StaticObject.getByName('FAT COW TENT ' .. i)
|
|
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
|
|
log("FAT COW FARP " .. i .. " FOUND")
|
|
|
|
RotorOpsPerks.fat_cow_farps[i] = {
|
|
index = i,
|
|
farp = farp,
|
|
farp_p = farp:getPosition().p,
|
|
tent = tent,
|
|
tent_p = tent:getPosition().p,
|
|
ammo = ammo,
|
|
ammo_p = ammo:getPosition().p,
|
|
fuel = fuel,
|
|
fuel_p = fuel:getPosition().p,
|
|
mark_id_base = 5550 + (i * 10), --arbitrary id for map drawing; allows 10 marks per FARP
|
|
state = 'uninitialized',
|
|
|
|
}
|
|
RotorOpsPerks.changeFarpState(i, 'not_deployed', nil) --initialize the farp
|
|
i = i + 1
|
|
else
|
|
farp_found = false
|
|
end
|
|
end
|
|
end
|
|
|
|
function RotorOpsPerks.getFatCowFarp()
|
|
for i, farp in pairs(RotorOpsPerks.fat_cow_farps) do
|
|
if farp.available then
|
|
RotorOpsPerks.updateFarpAttribute(farp.index, 'available', false)
|
|
farp.available = false
|
|
return farp
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
function RotorOpsPerks.changeFarpState(farp_index, new_state, request)
|
|
local farp = RotorOpsPerks.fat_cow_farps[farp_index]
|
|
|
|
local old_state = farp.state
|
|
log("Changing FARP state from " .. old_state .. " to " .. new_state)
|
|
|
|
|
|
if new_state == old_state then
|
|
return
|
|
end
|
|
|
|
local next_state = nil
|
|
|
|
if new_state == 'requested' then --set the farp attributes
|
|
if not request then
|
|
log("No request object provided for FARP state change", "error")
|
|
return
|
|
end
|
|
farp.deployed = false
|
|
farp.available = false
|
|
farp.deployed_p = request.dest_point
|
|
farp.owner_player_name = request.owner_player_name
|
|
farp.monitored_unit = request.monitored_unit
|
|
--log all vars
|
|
log("FARP REQUESTED AT: "..request.dest_point.x..", "..request.dest_point.z)
|
|
|
|
if request.immediate_deploy then --for player fat cow farps
|
|
farp.expiration_time = timer.getTime() + 3600
|
|
RotorOpsPerks.spawnFatCowFarp(farp.deployed_p.x, farp.deployed_p.z, farp.index, 15, "deployed_occupied")
|
|
end
|
|
|
|
elseif new_state == 'deployed_ai' then
|
|
farp.deployed = true
|
|
farp.expiration_time = timer.getTime() + 3600
|
|
RotorOpsPerks.spawnFatCowFarp(farp.deployed_p.x, farp.deployed_p.z, farp.index, 15, "deployed_empty")
|
|
|
|
elseif new_state == 'deployed_occupied' then
|
|
farp.deployed = true
|
|
if old_state == 'deployed_static' then
|
|
RotorOpsPerks.despawnStaticHelicopter(farp.index)
|
|
end
|
|
log("Player is on station. Now monitoring: " .. farp.monitored_unit:getName())
|
|
|
|
elseif new_state == 'deployed_static' then
|
|
local static_type = nil
|
|
if farp.monitored_unit then
|
|
local player_type = farp.monitored_unit:getTypeName()
|
|
log("Player type: " .. player_type .. " left the station. Now deploying static helicopter.")
|
|
static_type = RotorOpsPerks.static_helicopter_types[player_type]
|
|
end
|
|
|
|
if not static_type then
|
|
static_type = 'CH-47D'
|
|
end
|
|
|
|
farp.deployed = true
|
|
farp.monitored_unit = nil
|
|
RotorOpsPerks.spawnStaticFatCowHeli(farp.index, static_type)
|
|
|
|
elseif new_state == 'deployed_empty' then
|
|
farp.deployed = true
|
|
farp.monitored_unit = nil
|
|
if old_state == 'deployed_static' then
|
|
RotorOpsPerks.despawnStaticHelicopter(farp.index)
|
|
end
|
|
|
|
elseif new_state == 'not_deployed' then --reset the farp
|
|
if farp.owner_player_name then
|
|
RotorOpsPerks.perks.player_fatcow.active[farp.owner_player_name] = nil
|
|
end
|
|
|
|
RotorOpsPerks.despawnFatCowFarp(farp.index)
|
|
if old_state == 'deployed_static' then
|
|
RotorOpsPerks.despawnStaticHelicopter(farp.index)
|
|
elseif old_state == 'deployed_occupied' then
|
|
RotorOpsPerks.perks.player_fatcow.active[farp.owner_player_name] = nil --what about the AI fatcow perk??
|
|
if farp.monitored_unit and farp.monitored_unit:getGroup() then
|
|
trigger.action.outTextForGroup(farp.monitored_unit:getGroup():getID(), "FAT COW FARP despawned.", 10)
|
|
end
|
|
end
|
|
|
|
farp.deployed = false
|
|
farp.deployed_p = nil
|
|
farp.owner_player_name = nil
|
|
farp.monitored_unit = nil
|
|
farp.expiration_time = nil
|
|
farp.available = true
|
|
farp.static_heli_name = nil
|
|
|
|
end
|
|
|
|
--update F10 map
|
|
if farp.deployed then
|
|
RotorOpsPerks.updateFarpMarks(farp.index)
|
|
else
|
|
RotorOpsPerks.removeFarpMarks(farp.index)
|
|
end
|
|
|
|
--update the farp state
|
|
farp.state = new_state
|
|
RotorOpsPerks.fat_cow_farps[farp_index] = farp
|
|
|
|
if next_state then
|
|
RotorOpsPerks.changeFarpState(farp_index, next_state, nil)
|
|
end
|
|
end
|
|
|
|
|
|
function RotorOpsPerks.updateFarpAttribute(farp_index, attribute, value)
|
|
local value_str = tostring(value)
|
|
if type(value) ~= 'string' and type(value) ~= 'number' and type(value) ~= 'boolean' then
|
|
value_str = "type " .. type(value)
|
|
end
|
|
debugMsg('RotorOpsPerks.updateFarpAttribute: ' .. farp_index .. ' ' .. attribute .. ' ' .. value_str)
|
|
local farp = RotorOpsPerks.fat_cow_farps[farp_index]
|
|
farp[attribute] = value
|
|
RotorOpsPerks.fat_cow_farps[farp_index] = farp
|
|
end
|
|
|
|
|
|
function RotorOpsPerks.teleportStatic(source_name, dest_point)
|
|
debugMsg('RotorOpsPerks.teleportStatic: ' .. source_name)
|
|
local source = StaticObject.getByName(source_name)
|
|
if not source then
|
|
log('RotorOpsPerks.teleportStatic: source not found: ' .. source_name)
|
|
return
|
|
end
|
|
local vars = {}
|
|
vars.gpName = source_name
|
|
vars.action = 'teleport'
|
|
vars.point = mist.utils.makeVec3(dest_point)
|
|
local res = mist.teleportToPoint(vars)
|
|
if res then
|
|
log('RotorOpsPerks.teleportStatic: ' .. source_name .. ' success')
|
|
else
|
|
log('RotorOpsPerks.teleportStatic: ' .. source_name .. ' failed')
|
|
end
|
|
end
|
|
|
|
function RotorOpsPerks.spawnFatCowFarp(pt_x, pt_y, index, delay, new_state)
|
|
--log all vars
|
|
log("spawnFatCowFarp called with:")
|
|
log("FARP DEPLOYED AT: "..pt_x..", "..pt_y)
|
|
log("FARP INDEX: "..index)
|
|
log("DELAY: "..delay)
|
|
log("NEW STATE: "..new_state)
|
|
local farp = RotorOpsPerks.fat_cow_farps[index]
|
|
local dest_point = mist.utils.makeVec3GL({x = pt_x, y = pt_y})
|
|
trigger.action.smoke(dest_point, 2)
|
|
|
|
trigger.action.outText('Fat Cow FARP will deploy in ' ..delay .. ' seconds.', 20)
|
|
timer.scheduleFunction(function()
|
|
local fuel_point = {x = dest_point.x + 35, y = dest_point.y, z = dest_point.z}
|
|
RotorOpsPerks.teleportStatic('FAT COW FUEL ' .. index, fuel_point)
|
|
RotorOpsPerks.teleportStatic('FAT COW TENT ' .. index, fuel_point)
|
|
|
|
local ammo_point = {x = dest_point.x - 35, y = dest_point.y, z = dest_point.z}
|
|
RotorOpsPerks.teleportStatic('FAT COW AMMO ' .. index, ammo_point)
|
|
|
|
RotorOpsPerks.changeFarpState(farp.index, new_state, dest_point, nil, nil, nil)
|
|
|
|
end, nil, timer.getTime() + delay)
|
|
end
|
|
|
|
function RotorOpsPerks.despawnFatCowFarp(farp_index)
|
|
local farp = RotorOpsPerks.fat_cow_farps[farp_index]
|
|
RotorOpsPerks.teleportStatic('FAT COW FARP '..farp.index, {x=farp.farp_p.x, y=farp.farp_p.z})
|
|
RotorOpsPerks.teleportStatic('FAT COW TENT '..farp.index, {x=farp.tent_p.x, y=farp.tent_p.z})
|
|
RotorOpsPerks.teleportStatic('FAT COW AMMO '..farp.index, {x=farp.ammo_p.x, y=farp.ammo_p.z})
|
|
RotorOpsPerks.teleportStatic('FAT COW FUEL '..farp.index, {x=farp.fuel_p.x, y=farp.fuel_p.z})
|
|
end
|
|
|
|
function RotorOpsPerks.updateFarpMarks(farp_index)
|
|
local farp = RotorOpsPerks.fat_cow_farps[farp_index]
|
|
|
|
-- get the current farp position
|
|
local farp_point = farp.deployed_p
|
|
|
|
--add map marker
|
|
local coal = -1
|
|
local color = {0, 0.4, 0.95, 0.7}
|
|
local fill_color = {0, 0.4, 0.95, 0.4}
|
|
local text_fill_color = {0, 0, 0, 0}
|
|
local line_type = 3 --1 Solid 2 Dashed 3 Dotted 4 Dot Dash 5 Long Dash 6 Two Dash
|
|
local font_size = 20
|
|
local read_only = false
|
|
local text = "FAT COW FARP " .. farp.index
|
|
if farp.owner_player_name then
|
|
text = farp.owner_player_name .. "'s " .. text
|
|
end
|
|
|
|
local radius = 150 --ctld FOB pickup radius
|
|
|
|
trigger.action.circleToAll(coal, farp.mark_id_base + 0, farp_point, radius, color, fill_color, line_type)
|
|
trigger.action.textToAll(coal, farp.mark_id_base + 1, farp_point, color, text_fill_color, font_size, read_only, text)
|
|
|
|
end
|
|
|
|
function RotorOpsPerks.removeFarpMarks(farp_index)
|
|
local farp = RotorOpsPerks.fat_cow_farps[farp_index]
|
|
--for loop with 10 iterations to remove all marks
|
|
-- maybe we should store each mark in the farp object
|
|
for i = 0, 9 do
|
|
trigger.action.removeMark(farp.mark_id_base + i)
|
|
end
|
|
end
|
|
|
|
function RotorOpsPerks.spawnStaticFatCowHeli(farp_index, type)
|
|
-- this will dynamically create a static helicopter at the farp, as a placeholder for player who chose another slot
|
|
-- or to replace the AI Chinook which will eventually despawn
|
|
if not RotorOpsPerks.static_helicopters_at_farp then
|
|
return false
|
|
end
|
|
|
|
log('spawnStaticFatCowHeli called with farp_index:' .. farp_index)
|
|
|
|
local farp = RotorOpsPerks.fat_cow_farps[farp_index]
|
|
if not farp then
|
|
return false
|
|
end
|
|
|
|
local static_unit_name = 'FAT COW ' .. farp.index .. ' STATIC'
|
|
|
|
--use mist.dynAddStatic at the farp position
|
|
local heli = {
|
|
category = 'helicopters',
|
|
type = type,
|
|
country = 'USA',
|
|
x = farp.deployed_p.x,
|
|
y = farp.deployed_p.z,
|
|
name = static_unit_name,
|
|
heading = 0,
|
|
}
|
|
local new_heli = mist.dynAddStatic(heli)
|
|
if new_heli then
|
|
log('Static helicopter spawned as ' .. static_unit_name)
|
|
RotorOpsPerks.updateFarpAttribute(farp_index, 'static_heli_name', static_unit_name)
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
function RotorOpsPerks.despawnStaticHelicopter(farp_index)
|
|
local farp = RotorOpsPerks.fat_cow_farps[farp_index]
|
|
if farp.static_heli_name then
|
|
log('Despawning static helicopter ' .. farp.static_heli_name)
|
|
local unit = StaticObject.getByName(farp.static_heli_name)
|
|
if not unit then
|
|
log('Static helicopter not found: ' .. farp.static_heli_name, 'error')
|
|
return false
|
|
end
|
|
local sphere = {
|
|
point = unit:getPosition().p,
|
|
radius = 20
|
|
}
|
|
unit:destroy()
|
|
RotorOpsPerks.updateFarpAttribute(farp_index, 'static_heli_name', nil)
|
|
|
|
sphere.point.y = land.getHeight({x = sphere.point.x, y = sphere.point.z})
|
|
local volS = {
|
|
id = world.VolumeType.SPHERE,
|
|
params = {
|
|
point = sphere.point,
|
|
radius = sphere.radius
|
|
}
|
|
}
|
|
world.removeJunk(volS)
|
|
return true
|
|
end
|
|
end
|
|
|
|
|
|
function RotorOpsPerks.spawnAiChinook(dest_point, farp_index) --the AI Chinook with spawn and fly to the FARP
|
|
local farp = RotorOpsPerks.fat_cow_farps[farp_index]
|
|
local request = {
|
|
dest_point = dest_point,
|
|
farp_index = farp_index,
|
|
monitored_unit = nil,
|
|
immediate_deploy = false,
|
|
owner_player_name = nil,
|
|
}
|
|
RotorOpsPerks.changeFarpState(farp_index, 'requested', request)
|
|
|
|
local index = farp_index
|
|
local fatcow_name = 'FAT COW'
|
|
local source_farp_name = 'FAT COW FARP ' .. index
|
|
|
|
log('spawnAiChinook called with ' .. source_farp_name)
|
|
|
|
dest_point = mist.utils.makeVec2(dest_point)
|
|
local approach_point = mist.getRandPointInCircle(dest_point, 1000, 900)
|
|
trigger.action.smoke(mist.utils.makeVec3GL(dest_point), 2)
|
|
|
|
|
|
local fatcow_group = Group.getByName(fatcow_name)
|
|
if not fatcow_group then
|
|
env.warning('FatCow group not found')
|
|
return
|
|
end
|
|
|
|
RotorOpsPerks.teleportStatic(source_farp_name, dest_point)
|
|
|
|
local airbasefarp = Airbase.getByName(source_farp_name)
|
|
if not airbasefarp then
|
|
env.warning('FatCow FARP not found: ' .. source_farp_name)
|
|
return
|
|
end
|
|
|
|
local airbase_pos = mist.utils.makeVec2(airbasefarp:getPoint())
|
|
|
|
|
|
|
|
local script = [[
|
|
RotorOpsPerks.spawnFatCowFarp(]] .. dest_point.x ..[[,]] .. dest_point.y .. [[,]] .. index .. [[, 235, "deployed_ai")
|
|
]]
|
|
|
|
|
|
local myscriptaction = {
|
|
id = 'WrappedAction',
|
|
params = {
|
|
action = {
|
|
id = 'Script',
|
|
params = {
|
|
command = script,
|
|
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
local script_string = [[local this_grp = ...
|
|
this_grp:getController():setOption(AI.Option.Air.id.REACTION_ON_THREAT , AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE)
|
|
this_grp:getController():setOption(AI.Option.Air.id.FLARE_USING , AI.Option.Air.val.FLARE_USING.WHEN_FLYING_NEAR_ENEMIES)]]
|
|
|
|
local setOptions = {
|
|
id = 'WrappedAction',
|
|
params = {
|
|
action = {
|
|
id = 'Script',
|
|
params = {
|
|
command = script_string,
|
|
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
|
|
local group = Group.getByName(fatcow_name)
|
|
local initial_point = group:getUnits()[1]:getPoint()
|
|
local gp = mist.getGroupData(fatcow_name)
|
|
--debugTable(gp)
|
|
|
|
|
|
gp.route = {points = {}}
|
|
gp.route.points[1] = mist.heli.buildWP(initial_point, initial, 'flyover', 0, 0, 'agl')
|
|
gp.route.points[2] = mist.heli.buildWP(initial_point, initial, 'flyover', 150, 100, 'agl')
|
|
gp.route.points[2].task = setOptions
|
|
|
|
gp.route.points[3] = mist.heli.buildWP(approach_point, 'flyover', 150, 400, 'agl')
|
|
gp.route.points[4] = mist.heli.buildWP(approach_point, 'flyover', 20, 200, 'agl')
|
|
gp.route.points[5] = mist.heli.buildWP(dest_point, 'turning point', 10, 70, 'agl')
|
|
gp.route.points[5].task = myscriptaction
|
|
gp.route.points[6] = {
|
|
alt = 70,
|
|
alt_type = "RADIO",
|
|
speed = 10,
|
|
x = airbase_pos.x,
|
|
y = airbase_pos.y,
|
|
helipadId = airbasefarp:getID(),
|
|
aerodromeId = airbasefarp:getID(),
|
|
type = "Land",
|
|
action = "Landing",
|
|
}
|
|
|
|
|
|
gp.clone = true
|
|
local new_group_data = mist.dynAdd(gp)
|
|
end
|
|
|
|
function RotorOpsPerks.requestPerk(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()
|
|
local player_pos = player_unit:getPoint()
|
|
local pilot_name = player_unit:getPlayerName() --doesn't work
|
|
local players = RotorOpsPerks.playersByGroupName(args.player_group_name)
|
|
if not players then
|
|
env.warning('No players found in group ' .. args.player_group_name)
|
|
return
|
|
end
|
|
|
|
--get the perk object
|
|
local perk = RotorOpsPerks.perks[args.perk_name]
|
|
|
|
--init some essential variables
|
|
if not perk.used then
|
|
perk.used = 0
|
|
end
|
|
if not perk.last_used then
|
|
perk.last_used = 0
|
|
end
|
|
|
|
--find the intended point
|
|
local target_point = nil
|
|
if args.target == 'position' then
|
|
target_point = player_pos
|
|
|
|
elseif args.target == 'mark' then
|
|
local temp_mark = nil
|
|
|
|
for _, mark in pairs(mist.DBs.markList) do
|
|
debugMsg('mark: ' .. mist.utils.tableShow(mark, 'mark'))
|
|
local perk_name_matches = false
|
|
|
|
--determine if mark name matches the perk name
|
|
local mark_name = mark.text
|
|
--remove new line from mark name
|
|
mark_name = mark_name:gsub("\n", "")
|
|
if mark_name == args.perk_name then
|
|
perk_name_matches = true
|
|
log("mark name matches perk name")
|
|
end
|
|
|
|
if perk_name_matches then
|
|
--if MULTIPLAYER (initiator property missing in single player)
|
|
if mark.initiator then
|
|
--if mark is from player's group
|
|
if mark.initiator.id_ == player_unit.id_ then
|
|
target_point = mark.pos
|
|
if temp_mark then
|
|
--if there is already a mark from the player's group, use the most recent one
|
|
if mark.time > temp_mark.time then
|
|
temp_mark = mark
|
|
end
|
|
else
|
|
temp_mark = mark
|
|
end
|
|
end
|
|
|
|
else --we assume single player
|
|
if temp_mark then
|
|
--if there is already a mark from the player's group, use the most recent one
|
|
if mark.time > temp_mark.time then
|
|
temp_mark = mark
|
|
end
|
|
else
|
|
temp_mark = mark
|
|
end
|
|
end
|
|
|
|
end
|
|
end
|
|
-- 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
|
|
|
|
end
|
|
|
|
|
|
local perk_used_count = RotorOpsPerks.getPlayerGroupSum(args.player_group_name, args.perk_name, "perks_used")
|
|
if perk.max_per_player ~= nil and perk_used_count >= (perk.max_per_player*#players) then --multiply by number of players in group
|
|
if #players > 1 then
|
|
trigger.action.outTextForGroup(player_group:getID(), 'UNABLE. You already used this perk ' .. perk_used_count .. ' times.', 10)
|
|
else
|
|
trigger.action.outTextForGroup(player_group:getID(), 'UNABLE. Your group already used this perk ' .. perk_used_count .. ' times.', 10)
|
|
end
|
|
debugMsg('max_per_group reached for ' .. args.perk_name)
|
|
return
|
|
end
|
|
|
|
-- check if the max per mission has been reached
|
|
if perk.max_per_mission ~= nil then
|
|
if perk.used >= perk.max_per_mission then
|
|
debugMsg(args.player_group_name.. ' requested ' .. args.perk_name .. ' but max per mission reached')
|
|
trigger.action.outTextForGroup(player_group:getID(), 'UNABLE. Used too many times in the mission.', 10)
|
|
return
|
|
end
|
|
end
|
|
|
|
--check if position requirements for action are met
|
|
if args.target == "mark" then
|
|
if not target_point then
|
|
debugMsg(args.player_group_name.. ' requested ' .. args.perk_name .. ' but no target was found')
|
|
trigger.action.outTextForGroup(player_group:getID(), 'UNABLE. Add a mark called "' .. args.perk_name .. '" to the F10 map first', 10)
|
|
return
|
|
end
|
|
end
|
|
|
|
|
|
--check if cooldown is over in perks object
|
|
if perk.cooldown and perk.cooldown > 0 then
|
|
if perk.last_used + perk.cooldown > timer.getTime() then
|
|
local time_remaining = perk.last_used + perk.cooldown - timer.getTime()
|
|
--round time_remaining
|
|
time_remaining = math.floor(time_remaining + 0.5)
|
|
debugMsg(args.player_group_name.. ' tried to use ' .. args.perk_name .. ' but cooldown was not over')
|
|
trigger.action.outTextForGroup(player_group:getID(), 'UNABLE. Wait for '.. time_remaining .. ' seconds.', 10)
|
|
return
|
|
end
|
|
end
|
|
|
|
--add some useful data to pass to perk condition and action functions
|
|
args.target_point = target_point
|
|
args.player_group = player_group
|
|
args.player_unit = player_unit
|
|
args.player_unit_name = player_unit_name
|
|
args.player_coalition = player_group:getCoalition()
|
|
args.pilot_name = pilot_name
|
|
|
|
|
|
--show all variables available to perk actions and conditions
|
|
--log('args: ' .. mist.utils.tableShow(args, 'args'))
|
|
|
|
|
|
--check the perk's unique prerequisite conditions
|
|
if perk.action_condition then
|
|
local r = perk.action_condition(args)
|
|
|
|
if r and not r.valid then
|
|
local message = r.msg or "UNABLE. Requirements not met."
|
|
debugMsg(args.player_group_name.. ' tried to use ' .. args.perk_name .. ' but prereq failed with message: ' .. message)
|
|
trigger.action.outTextForGroup(player_group:getID(), message, 10)
|
|
return
|
|
end
|
|
|
|
if r and r.valid and r.msg then
|
|
trigger.action.outTextForGroup(player_group:getID(), r.msg, 10)
|
|
end
|
|
end
|
|
|
|
|
|
--check points
|
|
if RotorOpsPerks.spendPoints(args.player_group_name, perk.cost, false) then
|
|
log(args.player_group_name.. ' has sufficient (' .. perk.cost .. ') points for ' .. args.perk_name)
|
|
else
|
|
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
|
|
trigger.action.outTextForGroup(player_group:getID(), 'NEGATIVE. Your group has ' .. RotorOpsPerks.getPlayerGroupSum(args.player_group_name, "total points") .. ' points. (cost '.. perk.cost .. ')', 10)
|
|
end
|
|
return
|
|
end
|
|
|
|
|
|
--call perk action and deduct points if successful
|
|
local action_result = perk.action_function(args)
|
|
if action_result then
|
|
local data = {action_result = action_result}
|
|
RotorOpsPerks.spendPoints(args.player_group_name, perk.cost, true)
|
|
RotorOpsPerks.processCallbacks({action='onPerkUsed', perk=perk, request=args, data=data })
|
|
end
|
|
|
|
--update last_used
|
|
perk.last_used = timer.getTime()
|
|
perk.used = perk.used or 0 + 1
|
|
|
|
log('Player ' .. args.player_unit_name .. ' requested ' .. args.perk_name .. ' at ' .. args.target)
|
|
|
|
--increment player used for perk type, and initialize if it doesn't exist.
|
|
local perk_user_per_player = 1/(#players or 1)
|
|
--round perk_user_per_player to one decimal place
|
|
perk_user_per_player = math.floor(perk_user_per_player*10 + 0.5)/10
|
|
--loop through players
|
|
for _, player in pairs(players) do
|
|
if player.perks_used[args.perk_name] then
|
|
player.perks_used[args.perk_name] = player.perks_used[args.perk_name] + perk_user_per_player
|
|
else
|
|
player.perks_used[args.perk_name] = perk_user_per_player
|
|
end
|
|
end
|
|
|
|
--message players with humansByName DB
|
|
for _player_name, _player in pairs(mist.DBs.humansByName) do
|
|
--get unit object from id
|
|
local _player_unit = Unit.getByName(_player_name)
|
|
if _player_unit and _player_unit:isExist() then
|
|
local player_position = _player_unit:getPosition().p
|
|
local position_string = ' at ' .. RotorOpsPerks.BRString(target_point, player_position)
|
|
if _player.groupName == args.player_group_name then --if the player is the one who requested the perk
|
|
if args.target == 'position' then
|
|
position_string = ' at your position'
|
|
end
|
|
-- send affirmative message to the the requesting player
|
|
trigger.action.outTextForGroup(_player.groupId, 'AFFIRM. ' .. RotorOpsPerks.perks[args.perk_name].display_name .. position_string, 10)
|
|
else
|
|
-- send messages to all other players
|
|
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
|
|
end
|
|
|
|
end
|
|
|
|
function RotorOpsPerks.BRString(point_a, point_b)
|
|
point_a = mist.utils.makeVec3(point_a, 0)
|
|
point_b = mist.utils.makeVec3(point_b, 0)
|
|
local vec = {x = point_a.x - point_b.x, y = point_a.y - point_b.y, z = point_a.z - point_b.z}
|
|
local dir = mist.utils.getDir(vec, point_b)
|
|
local dist = mist.utils.get2DDist(point_a, point_b)
|
|
local bearing = mist.utils.round(mist.utils.toDegree(dir), 0)
|
|
local range_nm = mist.utils.round(mist.utils.metersToNM(dist), 0)
|
|
local range_ft = mist.utils.round(mist.utils.metersToFeet(dist), 0)
|
|
local br_string = ''
|
|
if range_nm > 0 then
|
|
br_string = bearing .. '° ' .. range_nm .. 'nm'
|
|
else
|
|
br_string = bearing .. '° ' .. range_ft .. 'ft'
|
|
end
|
|
return br_string
|
|
end
|
|
|
|
function RotorOpsPerks.findUnitsInVolume(args)
|
|
local foundUnits = {}
|
|
local volS = {
|
|
id = args.volume_type,
|
|
params = {
|
|
point = args.point,
|
|
radius = args.radius
|
|
}
|
|
}
|
|
|
|
local ifFound = function(foundObject, val)
|
|
foundUnits[#foundUnits + 1] = foundObject
|
|
end
|
|
world.searchObjects(Object.Category.UNIT, volS, ifFound)
|
|
return foundUnits
|
|
end
|
|
|
|
|
|
local handle = {}
|
|
function handle:onEvent(e)
|
|
|
|
if e.id == world.event.S_EVENT_BIRTH then
|
|
log('S_EVENT_BIRTH')
|
|
if not e.initiator then
|
|
log('BIRTH: initiator is not a unit. Returning.')
|
|
return
|
|
end
|
|
|
|
if e.initiator.getPlayerName == nil then
|
|
log('BIRTH: initiator is not a player. Returning.')
|
|
return
|
|
end
|
|
---- fat cow dynamic spawn hack: the following code relates to dynamic spawning at the fatcow site, which no longer works as intended
|
|
--
|
|
--local player_name = e.initiator:getPlayerName()
|
|
--local player_group_name = e.initiator:getGroup():getName()
|
|
--log('player_name is ' .. player_name) --nil error in single player
|
|
--log('player_group_name is ' .. player_group_name)
|
|
--
|
|
--
|
|
--for i, farp in pairs(RotorOpsPerks.fat_cow_farps) do
|
|
--
|
|
--
|
|
-- if farp.owner_player_name and player_name and farp.deployed then
|
|
-- log('farps owner_player_name is ' .. farp.owner_player_name)
|
|
-- if player_name == farp.owner_player_name and player_group_name ~= farp.chinook.groupName then
|
|
-- --if a farp owner has spawned into a slot other than the farp chinook slot, we'll spawn the static chinook
|
|
-- -- in the case where a player changes from spectator to a slot, we can't get the player from the S_EVENT_PLAYER_ENTER_UNIT event
|
|
-- -- we will try to change to 'deployed_empty' again, but it may not despawn the static chinook fast enough
|
|
-- RotorOpsPerks.changeFarpState(farp.index, 'deployed_empty', nil)
|
|
-- RotorOpsPerks.updateFarpAttribute(farp.index, 'monitored_unit', nil)
|
|
-- RotorOpsPerks.changeFarpState(farp.index, 'deployed_static', nil)
|
|
-- elseif player_name == farp.owner_player_name and player_group_name == farp.chinook.groupName then
|
|
-- --player spawned into the farp chinook slot
|
|
-- RotorOpsPerks.changeFarpState(farp.index, 'deployed_empty', nil)
|
|
-- RotorOpsPerks.updateFarpAttribute(farp.index, 'monitored_unit', e.initiator)
|
|
-- RotorOpsPerks.changeFarpState(farp.index, 'deployed_occupied', nil)
|
|
-- end
|
|
--
|
|
-- end
|
|
--end
|
|
end
|
|
|
|
if e.id == world.event.S_EVENT_PLAYER_ENTER_UNIT then
|
|
log('S_EVENT_PLAYER_ENTER_UNIT')
|
|
log('e table' .. mist.utils.tableShow(e, 'e'))
|
|
if not e.initiator then
|
|
log('ENTER: initiator is not a unit. Returning.')
|
|
return
|
|
end
|
|
|
|
|
|
end
|
|
|
|
if e.id == world.event.S_EVENT_PLAYER_LEAVE_UNIT then --getGroup will not work here
|
|
log('S_EVENT_PLAYER_LEAVE_UNIT')
|
|
log('e table' .. mist.utils.tableShow(e, 'e'))
|
|
if not e.initiator then
|
|
log('LEAVE: initiator is not a unit. Returning.')
|
|
return
|
|
end
|
|
|
|
if not e.initiator.getName then
|
|
log('LEAVE: cannot get initiator player name. Returning.')
|
|
return
|
|
end
|
|
|
|
local player_unit_name = e.initiator:getName()
|
|
log('player_unit_name is ' .. player_unit_name)
|
|
local player_name = e.initiator:getPlayerName()
|
|
log('player_name is ' .. player_name)
|
|
|
|
-- if the player is leaving the monitored_unit slot
|
|
for i, farp in pairs(RotorOpsPerks.fat_cow_farps) do
|
|
if farp.monitored_unit and player_unit_name == farp.monitored_unit:getName() then
|
|
RotorOpsPerks.changeFarpState(farp.index, 'deployed_static', nil)
|
|
end
|
|
end
|
|
|
|
---- fat cow dynamic spawn hack: the following code relates to dynamic spawning at the fatcow site, which no longer works as intended
|
|
--for i, farp in pairs(RotorOpsPerks.fat_cow_farps) do
|
|
--
|
|
-- --if the player is a farp owner, we'll despawn the static helicopter, even if we don't know what slot they'll choose
|
|
-- -- as the birth event does not let us despawn the static before the player enters the unit
|
|
--
|
|
-- if farp.owner_player_name and player_name then
|
|
-- if player_name == farp.owner_player_name then
|
|
-- RotorOpsPerks.changeFarpState(farp.index, 'deployed_static', nil)
|
|
-- end
|
|
-- end
|
|
--end
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
--if enemy unit destroyed
|
|
if e.id == world.event.S_EVENT_KILL then
|
|
if e.initiator and e.target then
|
|
if not Unit.getGroup(e.initiator) then
|
|
env.warning('KILL: initiator is not a unit')
|
|
return
|
|
end
|
|
if e.initiator:getCoalition() and e.target:getCoalition() and e.initiator:getCoalition() ~= e.target:getCoalition() then
|
|
debugMsg('KILL: initiator groupname: ' .. e.initiator:getGroup():getName())
|
|
|
|
local initiator_group_name = e.initiator:getGroup():getName()
|
|
|
|
-- if initiator is a player's dropped troops
|
|
local dropped_troops = RotorOpsPerks.troops[e.initiator:getGroup():getName()]
|
|
if dropped_troops then
|
|
if e.target:getDesc().category == Unit.Category.GROUND_UNIT == true then
|
|
if e.target:hasAttribute("Infantry") then
|
|
RotorOpsPerks.scorePoints(dropped_troops.player_group, RotorOpsPerks.points.dropped_troops_kill_inf, 'Your troops killed infantry!')
|
|
--else if target is armor
|
|
elseif e.target:hasAttribute("Tanks") then
|
|
RotorOpsPerks.scorePoints(dropped_troops.player_group, RotorOpsPerks.points.dropped_troops_kill_armor, 'Your troops killed armor!')
|
|
else
|
|
RotorOpsPerks.scorePoints(dropped_troops.player_group, RotorOpsPerks.points.dropped_troops_kill, 'Your troops killed a vehicle!')
|
|
end
|
|
elseif e.target:getDesc().category == Unit.Category.HELICOPTER == true then
|
|
RotorOpsPerks.scorePoints(dropped_troops.player_group, RotorOpsPerks.points.dropped_troops_kill_heli, 'Your troops killed a helicopter!')
|
|
elseif e.target:getDesc().category == Unit.Category.AIRPLANE == true then
|
|
RotorOpsPerks.scorePoints(dropped_troops.player_group, RotorOpsPerks.points.dropped_troops_kill_plane, 'Your troops killed a plane!')
|
|
end
|
|
end
|
|
|
|
--if the initiator is a player
|
|
if e.initiator:getPlayerName() then
|
|
|
|
--if target is a ground unit
|
|
if e.target:getDesc().category == Unit.Category.GROUND_UNIT == true then
|
|
if e.target:hasAttribute("Infantry") then
|
|
RotorOpsPerks.scorePoints(initiator_group_name, RotorOpsPerks.points.kill_inf, 'Killed infantry!')
|
|
elseif e.target:hasAttribute("Tanks") then
|
|
RotorOpsPerks.scorePoints(initiator_group_name, RotorOpsPerks.points.kill_armor, 'Killed armor!')
|
|
else
|
|
RotorOpsPerks.scorePoints(initiator_group_name, RotorOpsPerks.points.kill, 'Killed a vehicle!')
|
|
end
|
|
end
|
|
|
|
--if target is a helicopter
|
|
if e.target:getDesc().category == Unit.Category.HELICOPTER == true then
|
|
RotorOpsPerks.scorePoints(initiator_group_name, RotorOpsPerks.points.kill_heli, 'Killed a helicopter!')
|
|
end
|
|
|
|
--if target is a plane
|
|
if e.target:getDesc().category == Unit.Category.AIRPLANE == true then
|
|
RotorOpsPerks.scorePoints(initiator_group_name, RotorOpsPerks.points.kill_plane, 'Killed a plane!')
|
|
end
|
|
|
|
--if target is a ship
|
|
if e.target:getDesc().category == Unit.Category.SHIP == true then
|
|
RotorOpsPerks.scorePoints(initiator_group_name, RotorOpsPerks.points.kill_ship, 'Killed a ship!')
|
|
end
|
|
|
|
|
|
|
|
--CAS BONUS---
|
|
|
|
--we'll look for ground units in proximity to the player to apply a CAS bonus
|
|
local units_in_proximity = RotorOpsPerks.findUnitsInVolume({
|
|
volume_type = world.VolumeType.SPHERE,
|
|
point = e.initiator:getPoint(),
|
|
radius = 1852
|
|
})
|
|
|
|
local cas_bonus = false
|
|
|
|
for _, unit in pairs(units_in_proximity) do
|
|
|
|
--if we found friendly grund units near the player
|
|
if unit:getDesc().category == Unit.Category.GROUND_UNIT then
|
|
if unit:getCoalition() == e.initiator:getCoalition() then
|
|
cas_bonus = true
|
|
end
|
|
end
|
|
end
|
|
|
|
if cas_bonus then
|
|
RotorOpsPerks.scorePoints(e.initiator:getGroup():getName(), RotorOpsPerks.points.cas_bonus, '[CAS Bonus]')
|
|
end
|
|
|
|
--END CAS BONUS---
|
|
end
|
|
|
|
--end if the initiator is a player
|
|
|
|
end
|
|
end
|
|
end
|
|
end
|
|
world.addEventHandler(handle)
|
|
|
|
function RotorOpsPerks.registerCtldCallbacks()
|
|
if not ctld then
|
|
trigger.action.outText("CTLD Not Found", 10)
|
|
return
|
|
end
|
|
|
|
--if ctld.callbacks does not exist yet, loop until it does
|
|
if not ctld.callbacks then
|
|
timer.scheduleFunction(RotorOpsPerks.registerCtldCallbacks, nil, timer.getTime() + 1)
|
|
env.warning('CTLD callbacks not loaded yet, trying again in 1 second')
|
|
return
|
|
end
|
|
|
|
RotorOpsPerks.addFarpsToCtld()
|
|
|
|
ctld.addCallback(function(_args)
|
|
local action = _args.action
|
|
local unit = _args.unit
|
|
local picked_troops = _args.onboard
|
|
local dropped_troops = _args.unloaded
|
|
--log("ctld callback: ".. mist.utils.tableShow(_args))
|
|
|
|
if dropped_troops then
|
|
--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
|
|
|
|
|
|
local playername = unit:getPlayerName()
|
|
if playername then
|
|
if action == "unload_troops_zone" or action == "dropped_troops" then
|
|
|
|
elseif action == "rearm" or action == "repair" then
|
|
RotorOpsPerks.scorePoints(unit:getGroup():getName(), RotorOpsPerks.points.rearm, 'Rearm/repair!')
|
|
|
|
elseif action == "unpack" then
|
|
RotorOpsPerks.scorePoints(unit:getGroup():getName(), RotorOpsPerks.points.unpack, 'Crates unpacked!')
|
|
|
|
end
|
|
end
|
|
end)
|
|
|
|
end
|
|
|
|
function RotorOpsPerks.addFarpsToCtld()
|
|
for i, farp in pairs(RotorOpsPerks.fat_cow_farps) do
|
|
if farp.tent then
|
|
log('Adding fat cow tent to ctld.buildFOBS for troop pickup: ' .. farp.tent:getName())
|
|
table.insert(ctld.builtFOBS, farp.tent:getName())
|
|
else
|
|
log('No tent found for FARP ' .. farp.index)
|
|
end
|
|
end
|
|
end
|
|
|
|
function RotorOpsPerks.monitorPlayers()
|
|
--This function, along with buildPlayer and updatePlayer, have been crafted through much trial and error in order to work with the 'nuances' of the DCS APIs in single player and multiplayer environments.
|
|
--If it's not broke, don't fix it. If it's broke... ED probably changed the behaviour of coalition.getPlayers, net.get_player_list, or net.get_player_info
|
|
|
|
timer.scheduleFunction(RotorOpsPerks.monitorPlayers, nil, timer.getTime() + 2)
|
|
|
|
-- GET PILOTS
|
|
local pilots = coalition.getPlayers(coalition.side.BLUE)
|
|
local red_pilots = coalition.getPlayers(coalition.side.RED)
|
|
-- add red pilots to pilots
|
|
for _, red_pilot in pairs(red_pilots) do
|
|
table.insert(pilots, red_pilot)
|
|
end
|
|
|
|
debugMsg('PILOTS: '.. mist.utils.tableShow(pilots))
|
|
|
|
for _, player in pairs(pilots) do
|
|
|
|
local player_group_name = player:getGroup():getName()
|
|
debugMsg('GET PILOTS Player group: ' .. player:getGroup():getName())
|
|
debugMsg('GET PILOTS PLAYER: ' .. mist.utils.tableShow(player))
|
|
|
|
--player info works in single player
|
|
local player_info = net.get_player_info(player)
|
|
if player_info then
|
|
debugMsg('GET PILOTS player info: '.. mist.utils.tableShow(player_info))
|
|
RotorOpsPerks.updatePlayer(player_info.ucid, player_group_name, player_info.name, player_info.slot)
|
|
|
|
else --player_info is nil in multiplayer, so we'll have to compile the data we need in multiple steps
|
|
--env.warning('GET PILOTS player_info for coalition.getPlayers is nil. Setting attributes to nil to be picked up by GET CREW METHODs')
|
|
RotorOpsPerks.buildPlayer(nil, player_group_name, nil, nil, player:getPlayerName()) --we don't have all the data we need to add to players yet
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--GET CREW
|
|
|
|
local server_players = net.get_player_list() --empty in single player
|
|
debugMsg('GET CREW ALL PLAYERS: '.. mist.utils.tableShow(server_players))
|
|
|
|
for _, player in pairs(server_players) do
|
|
local player_info = net.get_player_info(player) --works with multicrew, but we need to find the group name
|
|
debugMsg('GET CREW player info:')
|
|
debugMsg(mist.utils.tableShow(player_info))
|
|
|
|
--find the group from slot relationship to pilots with the base slot
|
|
|
|
--client slot patterns are like 6_1, 6_2, etc where 6 is the host slot
|
|
--if the player slot is like 6_1, 6_2, etc then find the player with slot 6 and use that player's group name
|
|
if string.find(player_info.slot, '_') then --found a multicrew slot
|
|
local base_slot = string.sub(player_info.slot, 1, string.find(player_info.slot, '_')-1)
|
|
debugMsg('GET CREW found multicrew with base slot: '.. base_slot)
|
|
for _i, pilot in pairs(RotorOpsPerks.players) do
|
|
if pilot.slot == base_slot then
|
|
local player_group_name = pilot.groupName
|
|
debugMsg('GET CREW player group name: '.. player_group_name)
|
|
RotorOpsPerks.updatePlayer(player_info.ucid, player_group_name, player_info.name, player_info.slot)
|
|
end
|
|
end
|
|
else --we can't get the group name from here, so we'll have to compile the data we need in multiple steps
|
|
RotorOpsPerks.buildPlayer(player_info.ucid, nil, player_info.name, player_info.slot, player_info.name) --we don't have all the data we need to add to players yet
|
|
end
|
|
|
|
end
|
|
|
|
--IF PLAYER HAS DISCONNECTED FROM SERVER
|
|
if #server_players > 0 then
|
|
for identifier, player in pairs(RotorOpsPerks.players) do
|
|
--if the player is not in server_players, then they have disconnected
|
|
local player_still_connected = false
|
|
for _, server_player in pairs(server_players) do
|
|
if identifier == net.get_player_info(server_player).ucid then
|
|
player_still_connected = true
|
|
debugMsg(player.name .. ' is still connected')
|
|
end
|
|
end
|
|
if not player_still_connected then
|
|
log(player.name .. ' has disconnected')
|
|
RotorOpsPerks.processCallbacks({action='onPlayerDisconnected', player=player})
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
function RotorOpsPerks.processCallbacks(args)
|
|
log(mist.utils.tableShow(RotorOpsPerks.callbacks, 'RotorOpsPerks.callbacks'))
|
|
log('processCallbacks called with ' .. args.action)
|
|
--log(mist.utils.tableShow(args, 'args'))
|
|
for _, callback in pairs(RotorOpsPerks.callbacks) do
|
|
|
|
local success, response = pcall(function()
|
|
callback(args)
|
|
end)
|
|
|
|
if (not success) then
|
|
log(string.format("Callback Error: %s", response))
|
|
end
|
|
end
|
|
end
|
|
|
|
function RotorOpsPerks.addCallback(callback)
|
|
table.insert(RotorOpsPerks.callbacks, callback)
|
|
end
|
|
|
|
if mist.grimm_version then
|
|
log("GRIMM's version of MIST is loaded")
|
|
else
|
|
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()
|
|
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
|
|
timer.scheduleFunction(RotorOpsPerks.monitorPlayers, nil, timer.getTime() + 5)
|
|
|