Jump to content

Recommended Posts

Posted

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

 

Posted (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.

image.png

Edited by cfrag
Posted (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 

image.png

No gap, no glory STANDALONE.miz

Edited by cfrag
  • Thanks 1
Posted
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.

image.png

 

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

Posted (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 by cfrag
  • Like 1
Posted

@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

image.jpeg

Screen_230520_122638.jpg

Posted
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

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...