Adalla Posted May 20, 2023 Posted May 20, 2023 Hi all, I'm aware of the SWAPR script, but it's not working and so I'm trying to look for alternatives to bring DCS airbases (and carriers if possible?) to life by trying to find a way to populate them with Static or Uncontrolled AI aircraft, and removing them when Client spawns. Essentially, a simpler/dumber version of SWAPR, even if it's more manual scripting work. MOOSE has a sample mission SAP-140 Spawn Client Slots https://github.com/FlightControl-Master/MOOSE_MISSIONS/blob/master/SPA - Spawning/SPA-140 - Spawn Client Slots/SPA-140 - Spawn Client Slots.lua It's supposed to -- A plane will be spawned Uncontrolled and later one will be spawned Controlled. -- Only the Controlled plane will move, the other will remain idle at the parking spot. Closest thing I've found. Any help to get some sort of basic script going to achieve this would be great. I'm really tired of spawning on empty airbases. Thanks Adalla
cfrag Posted May 20, 2023 Posted May 20, 2023 (edited) 2 hours ago, Adalla said: I'm aware of the SWAPR script, but it's not working Let's briefly pause here, and make sure that we all agree on this. Because: SWAPR does work (for me, checked today with most recent OB) - mostly. It just has some quirks that we need to work around, namely: you must start the mission un-paused (by default, single-player missions start paused until you have selected a plane), and allow SWAPR to do its job. This means that you and any other players must not spawn into the mission before SPAWR has done its set-up. If you do, a static plane will spawn inside your aircraft during SWAPR initialization, and you'll blow up. This is due to the way that SWAPR starts: MOOSE is loaded on start, and SWAPR starts two seconds into a running mission. So, let the mission start, or unpause while you are at the briefing screen, and only jump into your cockpit after all stand-in planes have spawned. For newer aircraft (released after SWAPR was last released, e.g. Apache, F1, Mossie, Hind) you need to patch SWAPR if you want it to support these correctly. Mind the "Game_Mode" switch in line 5 of the script! Taking those points above into account, you may find that SWAPR can do a lot for you, and you no longer need a substitute. Here's a screenshot where I'm sitting in the 'Tross, with a bunch of swapped planes in the background. Edited May 20, 2023 by cfrag
cfrag Posted May 20, 2023 Posted May 20, 2023 (edited) 9 hours ago, Adalla said: It's supposed to -- A plane will be spawned Uncontrolled and later one will be spawned Controlled. -- Only the Controlled plane will move, the other will remain idle at the parking spot. Writing a simple SWAPR analogue was a lot less difficult than I anticipated. The script below (run on MISSION START) will do this for you. The aircraft are static objects for less performance drain until converted to player aircraft by slotting into them. The new script has no restrictions on when to run, does not require other frameworks. It works best with single-unit player groups. The script is called 'stopGaps' and will be part of DML, here's an even more light-weight stand-alone version, with demo mission Spoiler stopGap = {} stopGap.version = "1.0.1 STANDALONE" stopGap.verbose = false --[[-- Written and (c) 2023 by Christian Franz Replace all player units with static aircraft until the first time that a player slots into that plane. Static is then repalced with live player unit. For aircraft/helo carriers, no player planes are replaced with statics STRONGLY RECOMMENDED: - Use single-unit player groups. - Use 'start from ground hot/cold' to be able to control initial aircraft orientation Version History 1.0.0 - Initial version 1.0.1 - update / replace statics after slots become free --]]-- stopGap.standInGroups ={} stopGap.myGroups = {} -- for fast look-up of mx orig data -- -- one-time start-up processing -- -- in DCS, a group with one or more players only allocates when -- the first player in the group enters the game. -- cfxMX = {} -- local copy of cfxMX mission data cross reference tool cfxMX.playerGroupByName = {} -- returns data only if a player is in group cfxMX.countryByName ={} -- county of group named function cfxMX.createCrossReferences() -- tip o' hat to Mist for scanning mission struct. for coa_name_miz, coa_data in pairs(env.mission.coalition) do -- iterate all coalitions local coa_name = coa_name_miz if string.lower(coa_name_miz) == 'neutrals' then -- remove 's' at neutralS coa_name = 'neutral' end -- directly convert coalition into number for easier access later local coaNum = 0 if coa_name == "red" then coaNum = 1 end if coa_name == "blue" then coaNum = 2 end if type(coa_data) == 'table' then -- coalition = {bullseye, nav_points, name, county}, -- with county being an array if coa_data.country then -- make sure there a country table for this coalition for cntry_id, cntry_data in pairs(coa_data.country) do -- iterate all countries for this -- per country = {id, name, vehicle, helicopter, plane, ship, static} local countryName = string.lower(cntry_data.name) local countryID = cntry_data.id if type(cntry_data) == 'table' then -- filter strings .id and .name for obj_type_name, obj_type_data in pairs(cntry_data) do if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" -- what about "cargo"? then -- (so it's not id or name) local category = obj_type_name if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's at least one group! for group_num, group_data in pairs(obj_type_data.group) do local aName = group_data.name cfxMX.countryByName[aName] = countryID -- now iterate all units in this group -- for player info and ID for unit_num, unit_data in pairs(group_data.units) do if unit_data.skill then if unit_data.skill == "Client" or unit_data.skill == "Player" then cfxMX.playerGroupByName[aName] = group_data -- inefficient, but works end -- if unit skill client end -- if is player/client skill end -- for all units end -- for all groups end --if has category data end --if plane, helo etc... category end --for all objects in country end --if has country data end --for all countries in coalition end --if coalition has country table end -- if there is coalition data end --for all coalitions in mission end function stopGap.staticMXFromUnitMX(theGroup, theUnit) -- enter with MX data blocks -- build a static object from mx unit data local theStatic = {} theStatic.x = theUnit.x theStatic.y = theUnit.y theStatic.livery_id = theUnit.livery_id -- if exists theStatic.heading = theUnit.heading -- may need some attention theStatic.type = theUnit.type theStatic.name = theUnit.name -- will magically be replaced with player unit theStatic.cty = cfxMX.countryByName[theGroup.name] return theStatic end function stopGap.isGroundStart(theGroup) -- look at route if not theGroup.route then return false end local route = theGroup.route local points = route.points if not points then return false end local ip = points[1] if not ip then return end local action = ip.action if action == "Fly Over Point" then return false end if action == "Turning Point" then return false end if action == "Landing" then return false end -- looks like aircraft is on the ground -- but is it in water (carrier)? local u1 = theGroup.units[1] local sType = land.getSurfaceType(u1) -- has fields x and y if sType == 3 then return false end if stopGap.verbose then trigger.action.outText("Player Group <" .. theGroup.name .. "> GROUND BASED: " .. action .. " land type " .. sType, 30) end return true end function stopGap.createStandInsForMXGroup(group) local allUnits = group.units local theStaticGroup = {} for idx, theUnit in pairs (allUnits) do if theUnit.skill == "Client" or theUnit.skill == "Player" then local theStaticMX = stopGap.staticMXFromUnitMX(group, theUnit) local theStatic = coalition.addStaticObject(theStaticMX.cty, theStaticMX) theStaticGroup[theUnit.name] = theStatic -- remember me if stopGap.verbose then trigger.action.outText("Stop-gap-ing <" .. theUnit.name .. ">", 30) end else if stopGap.verbose then trigger.action.outText("<<skipping " .. theUnit.name .. ">>", 30) end end end return theStaticGroup end function stopGap.initGaps() -- when we enter, all slots are emptry -- and we populate all slots -- with their static representations for name, group in pairs (cfxMX.playerGroupByName) do -- check to see if this group is on the ground at parking -- by looking at the first waypoint if stopGap.isGroundStart(group) then -- this is one of ours! stopGap.myGroups[name] = group -- replace all groups entirely with static objects ---local allUnits = group.units local theStaticGroup = stopGap.createStandInsForMXGroup(group) -- remember this static group by its real name stopGap.standInGroups[group.name] = theStaticGroup end -- if groundtstart end end -- -- event handling -- function stopGap:onEvent(event) if not event then return end if not event.id then return end if event.id == 15 then if not event.initiator then return end local theUnit = event.initiator if (not theUnit.getPlayerName) or (not theUnit:getPlayerName()) then return end -- no player unit. local uName = theUnit:getName() local theGroup = theUnit:getGroup() local gName = theGroup:getName() if stopGap.myGroups[gName] then -- in case there were more than one units in this group, -- also clear out the others. better safe than sorry if stopGap.standInGroups[gName] then for name, theStatic in pairs(stopGap.standInGroups[gName]) do StaticObject.destroy(theStatic) end stopGap.standInGroups[gName] = nil end end end end -- -- update -- function stopGap.update() -- re-invoke in 1 second timer.scheduleFunction(stopGap.update, {}, timer.getTime() + 1) -- regularly check if slots can be refilled for name, theGroup in pairs(stopGap.myGroups) do -- if there is no stand-in group, that group was slotted if not stopGap.standInGroups[name] then local busy = true local pGroup = Group.getByName(name) if pGroup then if Group.isExist(pGroup) then else busy = false -- no longer exists end else busy = false -- nil group end if busy then -- players active in this group else local theStaticGroup = stopGap.createStandInsForMXGroup(theGroup) stopGap.standInGroups[name] = theStaticGroup end end end end -- -- get going -- function stopGap.start() -- run a cross reference on all mission data for palyer info cfxMX.createCrossReferences() -- fill player slots with static objects stopGap.initGaps() -- connect event handler world.addEventHandler(stopGap) -- start update in 10 seconds timer.scheduleFunction(stopGap.update, {}, timer.getTime() + 1) -- say hi! trigger.action.outText("stopGap v" .. stopGap.version .. " running", 30) return true end if not stopGap.start() then trigger.action.outText("+++ aborted stopGap v" .. stopGap.version .. " -- start failed", 30) stopGap = nil end As seen from the game No gap, no glory STANDALONE.miz Edited May 20, 2023 by cfrag 1
Adalla Posted May 20, 2023 Author Posted May 20, 2023 8 hours ago, cfrag said: Let's briefly pause here, and make sure that we all agree on this. Because: SWAPR does work (for me, checked today with most recent OB) - mostly. It just has some quirks that we need to work around, namely: you must start the mission un-paused (by default, single-player missions start paused until you have selected a plane), and allow SWAPR to do its job. This means that you and any other players must not spawn into the mission before SPAWR has done its set-up. If you do, a static plane will spawn inside your aircraft during SWAPR initialization, and you'll blow up. This is due to the way that SWAPR starts: MOOSE is loaded on start, and SWAPR starts two seconds into a running mission. So, let the mission start, or unpause while you are at the briefing screen, and only jump into your cockpit after all stand-in planes have spawned. For newer aircraft (released after SWAPR was last released, e.g. Apache, F1, Mossie, Hind) you need to patch SWAPR if you want it to support these correctly. Mind the "Game_Mode" switch in line 5 of the script! Taking those points above into account, you may find that SWAPR can do a lot for you, and you no longer need a substitute. Here's a screenshot where I'm sitting in the 'Tross, with a bunch of swapped planes in the background. Hi cfrag, Thanks for the feedback. Yeah I do let the mission unpause and run the scripts before I choose a client slot. Just yesterday I tested it with 4 A10s and 4 Hornets, they all spawn in fine. But when I select one of them, if the option of "Static" is chosen, my plane spawns on top of the static without it despawning. If I select "AI" option, it spawns me into a 5th spot and not one of the 4 spots of the flight. The A10 and Hornets, are 2 Groups of 4, so maybe this is causing some trouble. I'll double check the "SP" vs "MP" line item, but I'm pretty sure I tested both. In SP or self-hosted, seems to work. On a Dedicated Server, the statics replacements do not even spawn in the first place. I will test again right now with your feedback. Thanks for sharing the other script. I will test that too. Is there anything I need to do besides loading the script in the mission, and I'm guessing checking the Static unit names match with the statics in ME? THanks Adalla
cfrag Posted May 20, 2023 Posted May 20, 2023 (edited) 5 minutes ago, Adalla said: Thanks for sharing the other script. I will test that too. Is there anything I need to do besides loading the script in the mission, and I'm guessing checking the Static unit names match with the statics in ME? No, StopGap simply works. No messing with names or settings. And no Moose. I'll set up a dedicated thread for StopGap so everyone can always have access to the most recent version. I'll link to it here as soon as I'm done. Edited May 20, 2023 by cfrag 1
cfrag Posted May 20, 2023 Posted May 20, 2023 StopGap's "official" thread is now here. Thanks making me try to get this done, @Adalla!
Adalla Posted May 20, 2023 Author Posted May 20, 2023 @cfrag your an absolute Hero. Just tested your script, it works like a charm (except for spawn cold from ramp/airfield parking spot, but that's ok). I tested in SP and on my Dedicated Server, it works amazing. The only thing is, for mods, I was able to get it to work on my machine but the replacement statics dont show up on my Dedicated Server. I copied my aircraft mods to the servers Saved Games/DCS/mods/aircraft folder and the mods work for the client (my machine) but did not work for the statics. What am I doing wrong there or missing? null
Rudel_chw Posted May 20, 2023 Posted May 20, 2023 2 hours ago, cfrag said: I'll set up a dedicated thread for StopGap so everyone can always have access to the most recent version. I'll link to it here as soon as I'm done. That would be great, thanks a lot .. I'm editing a series of missions for the Mirage F1 with separate slots for CE and EE variants, and your script cames really handy For work: iMac mid-2010 of 27" - Core i7 870 - 6 GB DDR3 1333 MHz - ATI HD5670 - SSD 256 GB - HDD 2 TB - macOS High Sierra For Gaming: 34" Monitor - Ryzen 3600 - 32 GB DDR4 2400 - nVidia RTX2080 - SSD 1.25 TB - HDD 10 TB - Win10 Pro - TM HOTAS Cougar Mobile: iPad Pro 12.9" of 256 GB
Recommended Posts