Updated Moose

This commit is contained in:
iTracerFacer 2025-10-26 08:06:52 -05:00
parent 10c963e6a2
commit 492adeefc0
9 changed files with 1121 additions and 153 deletions

View File

@ -1,4 +1,4 @@
env.info('*** MOOSE GITHUB Commit Hash ID: 2025-10-01T15:26:49+02:00-d8281b01032aa44a579c14d690ca79413b671d9d ***')
env.info('*** MOOSE GITHUB Commit Hash ID: 2025-10-26T07:31:50+01:00-1965e24860936512d2670eb7d41c8440707a12ff ***')
if not MOOSE_DEVELOPMENT_FOLDER then
MOOSE_DEVELOPMENT_FOLDER='Scripts'
end
@ -469,6 +469,7 @@ CH47={},
OH58={},
UH1H={},
AH64D={},
UH60L={},
}
}
ENUMS.Storage.weapons.nurs.SNEB_TYPE253_F1B="weapons.nurs.SNEB_TYPE253_F1B"
@ -1172,6 +1173,24 @@ ENUMS.Storage.weapons.UH1H.M134_MiniGun_Right_Door={4,15,46,175}
ENUMS.Storage.weapons.UH1H.M60_MG_Right_Door={4,15,46,177}
ENUMS.Storage.weapons.UH1H.M134_MiniGun_Left_Door={4,15,46,174}
ENUMS.Storage.weapons.UH1H.M60_MG_Left_Door={4,15,46,176}
ENUMS.Storage.weapons.UH60L.M151_HYDRA={4,7,33,147}
ENUMS.Storage.weapons.UH60L.M156_HYDRA={4,7,33,148}
ENUMS.Storage.weapons.UH60L.M229_HYDRA={4,7,33,148}
ENUMS.Storage.weapons.UH60L.M257_HYDRA={4,7,33,151}
ENUMS.Storage.weapons.UH60L.M259_HYDRA={4,7,33,151}
ENUMS.Storage.weapons.UH60L.M274_HYDRA={4,7,33,150}
ENUMS.Storage.weapons.UH60L.M134_DOOR_GUN={4,15,46,3031}
ENUMS.Storage.weapons.UH60L.M3M={4,15,46,2496}
ENUMS.Storage.weapons.UH60L.M3M_DOOR_GUN={4,15,46,3032}
ENUMS.Storage.weapons.UH60L.M60_DOOR_GUN={4,15,46,3033}
ENUMS.Storage.weapons.UH60L.FUEL_TANK_200={1,3,43,3023}
ENUMS.Storage.weapons.UH60L.FUEL_TANK_230={1,3,43,3024}
ENUMS.Storage.weapons.UH60L.FUEL_TANK_450={1,3,43,3025}
ENUMS.Storage.weapons.UH60L.FUEL_TANK_DUAL_AUX={1,3,43,3026}
ENUMS.Storage.weapons.UH60L.CARGO_SEAT_REAR_ROW={1,3,43,3030}
ENUMS.Storage.weapons.UH60L.CARGO_SEAT_THREE_ROWS={1,3,43,3029}
ENUMS.Storage.weapons.UH60L.EMPTY_GUNNER_SEAT_1={1,3,43,3027}
ENUMS.Storage.weapons.UH60L.EMPTY_GUNNER_SEAT_2={1,3,43,3028}
ENUMS.Storage.weapons.OH58.FIM92={4,4,7,449}
ENUMS.Storage.weapons.OH58.MG_M3P100={4,15,46,2611}
ENUMS.Storage.weapons.OH58.MG_M3P200={4,15,46,2610}
@ -2659,6 +2678,14 @@ if type_name=="UH-60L"and(unit:getDrawArgumentValue(38)>0 or unit:getDrawArgumen
BASE:T(unit_name.." front door(s) are open")
return true
end
if type_name=="UH-60L_DAP"and(unit:getDrawArgumentValue(401)==1 or unit:getDrawArgumentValue(402)==1)then
BASE:T(unit_name.." cargo door is open")
return true
end
if type_name=="UH-60L_DAP"and(unit:getDrawArgumentValue(38)>0 or unit:getDrawArgumentValue(400)==1)then
BASE:T(unit_name.." front door(s) are open")
return true
end
if type_name=="AH-64D_BLK_II"then
BASE:T(unit_name.." front door(s) are open")
return true
@ -3860,7 +3887,55 @@ end
UTILS.lcg.seed=(UTILS.lcg.a*UTILS.lcg.seed+UTILS.lcg.c)%UTILS.lcg.m
return UTILS.lcg.seed/UTILS.lcg.m
end
function UTILS.SpawnFARPAndFunctionalStatics(Name,Coordinate,FARPType,Coalition,Country,CallSign,Frequency,Modulation,ADF,SpawnRadius,VehicleTemplate,Liquids,Equipment,Airframes,F10Text,DynamicSpawns,HotStart)
function UTILS.GenerateGridPoints(startVec2,n,spacingX,spacingY)
local points={}
local gridSize=math.ceil(math.sqrt(n))
local count=0
local n=n or 1
local spacingX=spacingX or 100
local spacingY=spacingY or 100
local startX=startVec2.x or 100
local startY=startVec2.y or 100
for row=0,gridSize-1 do
for col=0,gridSize-1 do
if count>=n then
break
end
local point={
x=startX+(col*spacingX),
y=startY+(row*spacingY)
}
table.insert(points,point)
count=count+1
end
if count>=n then
break
end
end
return points
end
function UTILS.SpawnFARPAndFunctionalStatics(Name,Coordinate,FARPType,Coalition,Country,CallSign,Frequency,Modulation,ADF,SpawnRadius,VehicleTemplate,Liquids,Equipment,Airframes,F10Text,DynamicSpawns,HotStart,NumberPads,SpacingX,SpacingY)
local function PopulateStorage(Name,liquids,equip,airframes)
local newWH=STORAGE:New(Name)
if liquids and liquids>0 then
newWH:SetLiquid(STORAGE.Liquid.DIESEL,liquids)
newWH:SetLiquid(STORAGE.Liquid.GASOLINE,liquids)
newWH:SetLiquid(STORAGE.Liquid.JETFUEL,liquids)
newWH:SetLiquid(STORAGE.Liquid.MW50,liquids)
end
if equip and equip>0 then
for cat,nitem in pairs(ENUMS.Storage.weapons)do
for name,item in pairs(nitem)do
newWH:SetItem(item,equip)
end
end
end
if airframes and airframes>0 then
for typename in pairs(CSAR.AircraftType)do
newWH:SetItem(typename,airframes)
end
end
end
local farplocation=Coordinate
local farptype=FARPType or ENUMS.FARPType.FARP
local Coalition=Coalition or coalition.side.BLUE
@ -3878,11 +3953,62 @@ local STypeName=statictypes.TypeName
local SShapeName=statictypes.ShapeName
local Country=Country or(Coalition==coalition.side.BLUE and country.id.USA or country.id.RUSSIA)
local ReturnObjects={}
local NumberPads=NumberPads or 1
local SpacingX=SpacingX or 100
local SpacingY=SpacingY or 100
local FarpVec2=Coordinate:GetVec2()
if NumberPads>1 then
local Grid=UTILS.GenerateGridPoints(FarpVec2,NumberPads,SpacingX,SpacingY)
local groupData={
["visible"]=true,
["hidden"]=false,
["units"]={},
["y"]=0,
["x"]=0,
["name"]=Name,
}
local unitData={
["category"]="Heliports",
["type"]=STypeName,
["y"]=0,
["x"]=0,
["name"]=Name,
["heading"]=0,
["heliport_modulation"]=mod,
["heliport_frequency"]=freq,
["heliport_callsign_id"]=callsign,
["dead"]=false,
["shape_name"]=SShapeName,
["dynamicSpawn"]=DynamicSpawns,
["allowHotStart"]=HotStart,
}
for id,gridpoint in ipairs(Grid)do
local UnitTemplate=UTILS.DeepCopy(unitData)
UnitTemplate.x=gridpoint.x
UnitTemplate.y=gridpoint.y
UnitTemplate.name=Name.."-"..id
table.insert(groupData.units,UnitTemplate)
if id==1 then
groupData.x=gridpoint.x
groupData.y=gridpoint.y
end
end
local Static=coalition.addGroup(Country,-1,groupData)
local Event={
id=EVENTS.Birth,
time=timer.getTime(),
initiator=Static
}
world.onEvent(Event)
PopulateStorage(Name.."-1",liquids,equip,airframes)
else
local newfarp=SPAWNSTATIC:NewFromType(STypeName,"Heliports",Country)
newfarp:InitShape(SShapeName)
newfarp:InitFARP(callsign,freq,mod,DynamicSpawns,HotStart)
local spawnedfarp=newfarp:SpawnFromCoordinate(farplocation,0,Name)
table.insert(ReturnObjects,spawnedfarp)
PopulateStorage(Name,liquids,equip,airframes)
end
local FARPStaticObjectsNato={
["FUEL"]={TypeName="FARP Fuel Depot",ShapeName="GSM Rus",Category="Fortifications"},
["AMMO"]={TypeName="FARP Ammo Dump Coating",ShapeName="SetkaKP",Category="Fortifications"},
@ -3911,25 +4037,6 @@ vehicles:InitDelayOff()
local spawnedvehicle=vehicles:SpawnFromCoordinate(vcoordinate)
table.insert(ReturnObjects,spawnedvehicle)
end
local newWH=STORAGE:New(Name)
if liquids and liquids>0 then
newWH:SetLiquid(STORAGE.Liquid.DIESEL,liquids)
newWH:SetLiquid(STORAGE.Liquid.GASOLINE,liquids)
newWH:SetLiquid(STORAGE.Liquid.JETFUEL,liquids)
newWH:SetLiquid(STORAGE.Liquid.MW50,liquids)
end
if equip and equip>0 then
for cat,nitem in pairs(ENUMS.Storage.weapons)do
for name,item in pairs(nitem)do
newWH:SetItem(item,equip)
end
end
end
if airframes and airframes>0 then
for typename in pairs(CSAR.AircraftType)do
newWH:SetItem(typename,airframes)
end
end
local ADFName
if ADF and type(ADF)=="number"then
local ADFFreq=ADF*1000
@ -9919,6 +10026,16 @@ self.LastVec2=ZoneUNIT:GetVec2()
_EVENTDISPATCHER:CreateEventNewZone(self)
return self
end
function ZONE_UNIT:UpdateFromUnit(Unit)
if Unit and Unit:IsAlive()then
local vec2=Unit:GetVec2()
self.LastVec2=vec2
elseif self.ZoneUNIT and self.ZoneUNIT:IsAlive()then
local ZoneVec2=self.ZoneUNIT:GetVec2()
self.LastVec2=ZoneVec2
end
return self
end
function ZONE_UNIT:GetVec2()
local ZoneVec2=self.ZoneUNIT:GetVec2()
if ZoneVec2 then
@ -9980,6 +10097,17 @@ ZoneVec2=self._.ZoneVec2Cache
end
return ZoneVec2
end
function ZONE_GROUP:UpdateFromGroup(Group)
if Group and Group:IsAlive()then
local vec2=Group:GetVec2()
self.Vec2=vec2
elseif self._.ZoneGROUP and self._.ZoneGROUP:IsAlive()then
local ZoneVec2=self._.ZoneGROUP:GetVec2()
self.Vec2=ZoneVec2
self._.ZoneVec2Cache=ZoneVec2
end
return self
end
function ZONE_GROUP:GetRandomVec2()
local Point={}
local Vec2=self._.ZoneGROUP:GetVec2()
@ -21723,7 +21851,7 @@ self:ScheduleOnce(0.3,self.SpawnFunctionHook,mystatic,unpack(self.SpawnFunctionA
end
if self.StaticCopyFrom~=nil then
mystatic.StaticCopyFrom=self.StaticCopyFrom
if not _DATABASE.Templates.Statics[Template.name]then
end
local TemplateGroup={}
TemplateGroup.units={}
TemplateGroup.units[1]=Template
@ -21731,8 +21859,6 @@ TemplateGroup.x=Template.x
TemplateGroup.y=Template.y
TemplateGroup.name=Template.name
_DATABASE:_RegisterStaticTemplate(TemplateGroup,self.CoalitionID,self.CategoryID,CountryID)
end
end
return mystatic
end
TIMER={
@ -26216,6 +26342,46 @@ return self
end
return nil
end
function CONTROLLABLE:OptionAAAMinFiringHeightMeters(meters)
self:F2({self.ControllableName})
local meters=meters or 20
local DCSControllable=self:GetDCSObject()
if DCSControllable then
local Controller=self:_GetController()
if Controller then
if self:IsGround()then
self:SetOption(27,meters)
end
end
return self
end
return nil
end
function CONTROLLABLE:OptionAAAMaxFiringHeightMeters(meters)
self:F2({self.ControllableName})
local meters=meters or 1000
local DCSControllable=self:GetDCSObject()
if DCSControllable then
local Controller=self:_GetController()
if Controller then
if self:IsGround()then
self:SetOption(29,meters)
end
end
return self
end
return nil
end
function CONTROLLABLE:OptionAAAMinFiringHeightFeet(feet)
self:F2({self.ControllableName})
local feet=feet or 60
return self:OptionAAAMinFiringHeightMeters(UTILS.FeetToMeters(feet))
end
function CONTROLLABLE:OptionAAAMaxFiringHeightfeet(feet)
self:F2({self.ControllableName})
local feet=feet or 3000
return self:OptionAAAMaxFiringHeightMeters(UTILS.FeetToMeters(feet))
end
function CONTROLLABLE:OptionEngageRange(EngageRange)
self:F2({self.ControllableName})
EngageRange=EngageRange or 100
@ -31125,6 +31291,13 @@ end
self:T2(string.format("Registered airbase %s",tostring(self.AirbaseName)))
return self
end
function AIRBASE:GetVec2()
local runways=self:GetRunways()
if runways and#runways>0 then
return runways[1].center:GetVec2()
end
return self:GetCoordinate():GetVec2()
end
function AIRBASE:_GetCategory()
local name=self.AirbaseName
local static=StaticObject.getByName(name)
@ -33652,6 +33825,8 @@ REMOVED="REMOVED",
}
DYNAMICCARGO.AircraftTypes={
["CH-47Fbl1"]="CH-47Fbl1",
["Mi-8MTV2"]="CH-47Fbl1",
["Mi-8MT"]="CH-47Fbl1",
}
DYNAMICCARGO.AircraftDimensions={
["CH-47Fbl1"]={
@ -33660,8 +33835,20 @@ DYNAMICCARGO.AircraftDimensions={
["length"]=11,
["ropelength"]=30,
},
["Mi-8MTV2"]={
["width"]=6,
["height"]=6,
["length"]=15,
["ropelength"]=30,
},
["Mi-8MT"]={
["width"]=6,
["height"]=6,
["length"]=15,
["ropelength"]=30,
},
}
DYNAMICCARGO.version="0.0.7"
DYNAMICCARGO.version="0.0.9"
function DYNAMICCARGO:Register(CargoName)
local self=BASE:Inherit(self,POSITIONABLE:New(CargoName))
self.StaticName=CargoName
@ -35274,7 +35461,9 @@ end
end)
self.AutoSavePath=SavePath
self.AutoSave=AutoSave or true
if self.AutoSave==true then
self:OpenCSV(GameName)
end
return self
end
function SCORING:SetDisplayMessagePrefix(DisplayMessagePrefix)
@ -36298,7 +36487,7 @@ TargetUnitCoalition=TargetUnitCoalition or""
TargetUnitCategory=TargetUnitCategory or""
TargetUnitType=TargetUnitType or""
TargetUnitName=TargetUnitName or""
if lfs and io and os and self.AutoSave then
if lfs and io and os and self.AutoSave==true and self.CSVFile~=nil then
self.CSVFile:write(
'"'..self.GameName..'"'..','..
'"'..self.RunTime..'"'..','..
@ -58882,8 +59071,8 @@ end
end
TIRESIAS={
ClassName="TIRESIAS",
debug=true,
version=" 0.0.7-OPT",
debug=false,
version=" 0.0.8",
Interval=20,
GroundSet=nil,
VehicleSet=nil,
@ -58903,7 +59092,7 @@ self:SetStartState("Stopped")
self:AddTransition("Stopped","Start","Running")
self:AddTransition("*","Status","*")
self:AddTransition("*","Stop","Stopped")
self.ExceptionSet=nil
self.ExceptionSet=SET_GROUP:New()
self._cached_zones={}
self:HandleEvent(EVENTS.PlayerEnterAircraft,self._EventHandler)
self.lid="TIRESIAS "..self.version.." | "
@ -58934,10 +59123,7 @@ exception=true,
}
Set:ForEachGroupAlive(
function(grp)
local inAAASet=self.AAASet:IsIncludeObject(grp)
local inVehSet=self.VehicleSet:IsIncludeObject(grp)
local inSAMSet=self.SAMSet:IsIncludeObject(grp)
if grp:IsGround()and(not grp.Tiresias)and(not inAAASet)and(not inVehSet)and(not inSAMSet)then
if grp:IsGround()and(not grp.Tiresias)then
grp.Tiresias=exception_data
exceptions:AddGroup(grp,true)
BASE:T(" TIRESIAS: Added exception group: "..grp:GetName())
@ -59078,19 +59264,14 @@ self:T(self.lid.." _SwitchOnGroups "..group:GetName().." Radius "..radius.." N
local group_name=group:GetName()
local cache_key=group_name.." _"..radius
local zone=self._cached_zones[cache_key]
local ground=self._cached_groupsets[cache_key]
if not zone then
zone=ZONE_GROUP:New(" Zone-"..group_name,group,UTILS.NMToMeters(radius))
self._cached_zones[cache_key]=zone
else
zone:UpdateFromGroup(group)
end
if not ground then
ground=SET_GROUP:New():FilterCategoryGround():FilterZones({zone}):FilterOnce()
self._cached_groupsets[cache_key]=ground
else
ground:FilterZones({zone},true):FilterOnce()
end
zone:Scan({Object.Category.UNIT},{Unit.Category.GROUND_UNIT})
local ground=zone:GetScannedSetGroup()
local count=ground:CountAlive()
if self.debug then
self:I(string.format(" There are %d groups around this plane or helo!",count))
@ -63489,6 +63670,7 @@ brc=self:GetBRCintoWind(self.recoverywindow.SPEED)
end
flight.Tcharlie=self:_GetCharlieTime(flight)
local Ccharlie=UTILS.SecondsToClock(flight.Tcharlie)
brc=brc%360
self:_MarshalCallArrived(flight.onboard,flight.case,brc,alt,Ccharlie,P)
if self.TACANon and(not flight.ai)and flight.difficulty==AIRBOSS.Difficulty.EASY then
local radial=self:GetRadial(flight.case,true,true,true)
@ -63852,7 +64034,7 @@ playerData.stable=false
playerData.landed=false
playerData.Tlso=timer.getTime()
playerData.Tgroove=nil
playerData.TIG0=nil
playerData.TIG0=0
playerData.wire=nil
playerData.flag=-100
playerData.debriefschedulerID=nil
@ -64882,77 +65064,9 @@ if self:_CheckAbort(X,Z,self.BreakEntry)then
self:_AbortPattern(playerData,X,Z,self.BreakEntry,true)
return
end
local stern=self:_GetSternCoord()
local coord=playerData.unit:GetCoordinate()
local dist=coord:Get2DDistance(stern)
local playerCallsign=playerData.unit:GetCallsign()
local playerName=playerData.name
local unit=playerData.unit
local unitClient=Unit.getByName(unit:GetName())
local hookArgument=unitClient:getDrawArgumentValue(25)
local hookArgument_Tomcat=unitClient:getDrawArgumentValue(1305)
local speedMPS=playerData.unit:GetVelocityMPS()
local speedKTS=UTILS.MpsToKnots(speedMPS)
local player_alt=playerData.unit:GetAltitude()
player_alt_feet=player_alt*3.28
player_alt_feet=player_alt_feet/10
player_alt_feet=math.floor(player_alt_feet)*10
local player_velocity_round=speedKTS*1.00
player_velocity_round=player_velocity_round/10
player_velocity_round=math.floor(player_velocity_round)*10
local player_alt_feet=player_alt*3.28
player_alt_feet=player_alt_feet/10
player_alt_feet=math.floor(player_alt_feet)*10
local Play_SH_Sound=USERSOUND:New("Airboss Soundfiles/GreatBallsOfFire.ogg")
local Play_666SH_Sound=USERSOUND:New("Airboss Soundfiles/Runninwiththedevil.ogg")
local playerType=playerData.actype
if dist<1000 and clientSHBFlag==false then
if speedKTS>450 and speedKTS<590 then
if player_alt_feet<1500 then
if hookArgument>0 or hookArgument_Tomcat>0 then
playerData.shb=true
trigger.action.outText(playerName..' performing a Sierra Hotel Break in a '..playerType,10)
local sh_message_to_discord=('**'..playerName..' is performing a Sierra Hotel Break in a '..playerType..' at '..player_velocity_round..' knots and '..player_alt_feet..' feet!**')
HypeMan.sendBotMessage(sh_message_to_discord)
Play_SH_Sound:ToAll()
clientSHBFlag=true
else
playerData.shb=false
end
else
end
elseif speedKTS>589 then
if player_alt_feet<625 and player_alt_feet>575 then
if hookArgument>0 or hookArgument_Tomcat>0 then
playerData.shb=true
trigger.action.outText(playerName..' performing a 666 Sierra Hotel Break in a '..playerType,10)
local sh_message_to_discord=('**'..playerName..' is performing a 666 Sierra Hotel Break in a '..playerType..' at '..player_velocity_round..' knots and '..player_alt_feet..' feet!**')
HypeMan.sendBotMessage(sh_message_to_discord)
Play_666SH_Sound:ToAll()
clientSHBFlag=true
else
playerData.shb=false
end
else
if hookArgument>0 or hookArgument_Tomcat>0 then
playerData.shb=true
trigger.action.outText(playerName..' performing a Sierra Hotel Break in a '..playerType,10)
local sh_message_to_discord=('**'..playerName..' is performing a Sierra Hotel Break in a '..playerType..' at '..player_velocity_round..' knots and '..player_alt_feet..' feet!**')
HypeMan.sendBotMessage(sh_message_to_discord)
Play_SH_Sound:ToAll()
clientSHBFlag=true
else
playerData.shb=false
end
end
else
end
else
end
if self:_CheckLimits(X,Z,self.BreakEntry)then
self:_PlayerHint(playerData)
self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.EARLYBREAK)
clientSHBFlag=false
end
end
function AIRBOSS:_Break(playerData,part)
@ -65240,19 +65354,19 @@ end
if rho>=RAR and rho<=RIM then
if gd.LUE>0.22 and lineupError<-0.22 then
env.info" Drift Right across centre ==> DR-"
gd.Drift=" DR"
gd.Drift="DR"
self:T(self.lid..string.format("Got Drift Right across centre step %s, d=%.3f: Max LUE=%.3f, lower LUE=%.3f",gs,d,gd.LUE,lineupError))
elseif gd.LUE<-0.22 and lineupError>0.22 then
env.info" Drift Left ==> DL-"
gd.Drift=" DL"
gd.Drift="DL"
self:T(self.lid..string.format("Got Drift Left across centre at step %s, d=%.3f: Min LUE=%.3f, lower LUE=%.3f",gs,d,gd.LUE,lineupError))
elseif gd.LUE>0.13 and lineupError<-0.14 then
env.info" Little Drift Right across centre ==> (DR-)"
gd.Drift=" (DR)"
gd.Drift="(DR)"
self:T(self.lid..string.format("Got Little Drift Right across centre at step %s, d=%.3f: Max LUE=%.3f, lower LUE=%.3f",gs,d,gd.LUE,lineupError))
elseif gd.LUE<-0.13 and lineupError>0.14 then
env.info" Little Drift Left across centre ==> (DL-)"
gd.Drift=" (DL)"
gd.Drift="(DL)"
self:E(self.lid..string.format("Got Little Drift Left across centre at step %s, d=%.3f: Min LUE=%.3f, lower LUE=%.3f",gs,d,gd.LUE,lineupError))
end
end
@ -66051,9 +66165,7 @@ local hdg=self.carrier:GetHeading()
if magnetic then
hdg=hdg-self.magvar
end
if hdg<0 then
hdg=hdg+360
end
hdg=hdg%360
return hdg
end
function AIRBOSS:GetBRC()
@ -66152,7 +66264,7 @@ theta=math.asin(vdeck*math.sin(alpha)/vwind)
v=vdeck*math.cos(alpha)-vwind*math.cos(theta)
end
local magvar=magnetic and self.magvar or 0
local intowind=self:GetHeadingIntoWind_old(vdeck)
local intowind=(540+(windto-magvar+math.deg(theta)))%360
return intowind,v
end
function AIRBOSS:GetBRCintoWind(vdeck)
@ -66343,7 +66455,7 @@ return select(2,string.gsub(base,pattern,""))
end
local TIG=""
if playerData.Tgroove and playerData.Tgroove<=360 and playerData.case<3 then
TIG=self:_EvalGrooveTime(playerData)
TIG=self:_EvalGrooveTime(playerData)or"N/A"
end
local GXX,nXX=self:_Flightdata2Text(playerData,AIRBOSS.GroovePos.XX)
local GIM,nIM=self:_Flightdata2Text(playerData,AIRBOSS.GroovePos.IM)
@ -66358,6 +66470,7 @@ local N=nXX+nIM+nIC+nAR+nIW
local nL=count(G,'_')/2
local nS=count(G,'%(')
local nN=N-nS-nL
if TIG=="_OK_"then nL=nL-1 end
local Tgroove=playerData.Tgroove
local TgrooveUnicorn=Tgroove and(Tgroove>=16.49 and Tgroove<=16.59)or false
local TgrooveVstolUnicorn=Tgroove and(Tgroove>=60.0 and Tgroove<=65.0)and playerData.actype==AIRBOSS.AircraftCarrier.AV8B or false
@ -66465,14 +66578,8 @@ grade="CUT"
points=0.0
end
end
if playerData.wire==1 and points>1 then
if points==4 then
points=3
grade="(OK)"
elseif points==3 then
points=2
grade="--"
end
if playerData.wire==1 and points>=3 and N>4 then
points=points-1
end
env.info("Returning: "..grade.." "..points.." "..G)
return grade,points,G
@ -66520,6 +66627,7 @@ O=little("OS")
end
end
local S=nil
local A=nil
if step~=AIRBOSS.PatternStep.GROOVE_IW then
if AIRBOSS.PatternStep.GROOVE_AR and playerData.waveoff==true and playerData.owo==true then
else
@ -66536,7 +66644,6 @@ S="F"
elseif AOA<acaoa.OnSpeedMin then
S=little("F")
end
local A=nil
if GSE>self.gle.HIGH then
A=underline("H")
elseif GSE>self.gle.High then
@ -72835,6 +72942,7 @@ if type(Location)=="string"then
Location=ZONE:New(Location)
end
self.Location=Location
self.NoMoveToZone=false
return self
end
function CTLD_CARGO:SetStaticTypeAndShape(Category,TypeName,ShapeName)
@ -73326,6 +73434,7 @@ self.dropAsCargoCrate=false
self.smokedistance=2000
self.movetroopstowpzone=true
self.movetroopsdistance=5000
self.returntroopstobase=true
self.troopdropzoneradius=100
self.VehicleMoveFormation=AI.Task.VehicleFormation.VEE
self.enableHercules=false
@ -74698,7 +74807,7 @@ if not inzone then
inzone,zonename,zone,distance=self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP)
end
if inzone then
droppingatbase=true
droppingatbase=self.returntroopstobase
end
local hoverunload=self:IsCorrectHover(Unit)
local IsHerc=self:IsFixedWing(Unit)
@ -75384,7 +75493,8 @@ subcatmenus[catName]=MENU_GROUP:New(_group,catName,cratesmenu)
end
for _,cargoObj in pairs(self.Cargo_Crates)do
if not cargoObj.DontShowInMenu then
local txt=string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0)
local needed=cargoObj:GetCratesNeeded()or 1
local txt=string.format("%d crate%s %s (%dkg)",needed,needed==1 and""or"s",cargoObj.Name,cargoObj.PerCrateMass or 0)
if cargoObj.Location then txt=txt.."[R]"end
local stock=cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt=txt.."["..stock.."]"end
@ -75395,7 +75505,8 @@ end
end
for _,cargoObj in pairs(self.Cargo_Statics)do
if not cargoObj.DontShowInMenu then
local txt=string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0)
local needed=cargoObj:GetCratesNeeded()or 1
local txt=string.format("%d crate%s %s (%dkg)",needed,needed==1 and""or"s",cargoObj.Name,cargoObj.PerCrateMass or 0)
if cargoObj.Location then txt=txt.."[R]"end
local stock=cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt=txt.."["..stock.."]"end
@ -75407,7 +75518,8 @@ end
else
for _,cargoObj in pairs(self.Cargo_Crates)do
if not cargoObj.DontShowInMenu then
local txt=string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0)
local needed=cargoObj:GetCratesNeeded()or 1
local txt=string.format("%d crate%s %s (%dkg)",needed,needed==1 and""or"s",cargoObj.Name,cargoObj.PerCrateMass or 0)
if cargoObj.Location then txt=txt.."[R]"end
local stock=cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt=txt.."["..stock.."]"end
@ -75418,7 +75530,8 @@ end
end
for _,cargoObj in pairs(self.Cargo_Statics)do
if not cargoObj.DontShowInMenu then
local txt=string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0)
local needed=cargoObj:GetCratesNeeded()or 1
local txt=string.format("%d crate%s %s (%dkg)",needed,needed==1 and""or"s",cargoObj.Name,cargoObj.PerCrateMass or 0)
if cargoObj.Location then txt=txt.."[R]"end
local stock=cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt=txt.."["..stock.."]"end
@ -75429,14 +75542,15 @@ end
end
end
else
if self.usesubcats then
if self.usesubcats==true then
local subcatmenus={}
for catName,_ in pairs(self.subcats)do
subcatmenus[catName]=MENU_GROUP:New(_group,catName,cratesmenu)
end
for _,cargoObj in pairs(self.Cargo_Crates)do
if not cargoObj.DontShowInMenu then
local txt=string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0)
local needed=cargoObj:GetCratesNeeded()or 1
local txt=string.format("%d crate%s %s (%dkg)",needed,needed==1 and""or"s",cargoObj.Name,cargoObj.PerCrateMass or 0)
if cargoObj.Location then txt=txt.."[R]"end
local stock=cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt=txt.."["..stock.."]"end
@ -75445,7 +75559,8 @@ end
end
for _,cargoObj in pairs(self.Cargo_Statics)do
if not cargoObj.DontShowInMenu then
local txt=string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0)
local needed=cargoObj:GetCratesNeeded()or 1
local txt=string.format("%d crate%s %s (%dkg)",needed,needed==1 and""or"s",cargoObj.Name,cargoObj.PerCrateMass or 0)
if cargoObj.Location then txt=txt.."[R]"end
local stock=cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt=txt.."["..stock.."]"end
@ -75455,7 +75570,8 @@ end
else
for _,cargoObj in pairs(self.Cargo_Crates)do
if not cargoObj.DontShowInMenu then
local txt=string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0)
local needed=cargoObj:GetCratesNeeded()or 1
local txt=string.format("%d crate%s %s (%dkg)",needed,needed==1 and""or"s",cargoObj.Name,cargoObj.PerCrateMass or 0)
if cargoObj.Location then txt=txt.."[R]"end
local stock=cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt=txt.."["..stock.."]"end
@ -75464,7 +75580,8 @@ end
end
for _,cargoObj in pairs(self.Cargo_Statics)do
if not cargoObj.DontShowInMenu then
local txt=string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0)
local needed=cargoObj:GetCratesNeeded()or 1
local txt=string.format("%d crate%s %s (%dkg)",needed,needed==1 and""or"s",cargoObj.Name,cargoObj.PerCrateMass or 0)
if cargoObj.Location then txt=txt.."[R]"end
local stock=cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt=txt.."["..stock.."]"end
@ -75888,7 +76005,7 @@ if not inzone then
inzone,zonename,zone,distance=self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP)
end
if inzone then
droppingatbase=true
droppingatbase=self.returntroopstobase
end
if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName())then
self:_SendMessage("You need to open the door(s) to unload troops!",10,false,Group)
@ -76117,6 +76234,34 @@ table.insert(self.Cargo_Crates,cargo)
if SubCategory and self.usesubcats~=true then self.usesubcats=true end
return self
end
function CTLD:AddCratesCargoNoMove(Name,Templates,Type,NoCrates,PerCrateMass,Stock,SubCategory,DontShowInMenu,Location,UnitTypes,Category,TypeName,ShapeName)
self:T(self.lid.." AddCratesCargoNoMove")
if not self:_CheckTemplates(Templates)then
self:E(self.lid.."Crates Cargo for "..Name.." has missing template(s)!")
return self
end
self.CargoCounter=self.CargoCounter+1
local cargo=CTLD_CARGO:New(self.CargoCounter,Name,Templates,Type,false,false,NoCrates,nil,nil,PerCrateMass,Stock,SubCategory,DontShowInMenu,Location)
cargo.NoMoveToZone=true
if UnitTypes then
cargo:AddUnitTypeName(UnitTypes)
end
cargo:SetStaticTypeAndShape("Cargos",self.basetype)
if TypeName then
cargo:SetStaticTypeAndShape(Category,TypeName,ShapeName)
end
table.insert(self.Cargo_Crates,cargo)
self.templateToCargoName=self.templateToCargoName or{}
if type(Templates)=="table"then
for _,t in pairs(Templates)do self.templateToCargoName[t]=Name end
else
self.templateToCargoName[Templates]=Name
end
self.nomovetozone_names=self.nomovetozone_names or{}
self.nomovetozone_names[Name]=true
if SubCategory and self.usesubcats~=true then self.usesubcats=true end
return self
end
function CTLD:AddStaticsCargo(Name,Mass,Stock,SubCategory,DontShowInMenu,Location)
self:T(self.lid.." AddStaticsCargo")
self.CargoCounter=self.CargoCounter+1
@ -76269,7 +76414,12 @@ self:E(self.lid.."**** Ship does not exist: "..Name)
return self
end
end
local ctldzone={}
local exists=true
local ctldzone=self:GetCTLDZone(Name,Type)
if not ctldzone then
exists=false
ctldzone={}
end
ctldzone.active=Active or false
ctldzone.color=Color or SMOKECOLOR.Red
ctldzone.name=Name or"NONE"
@ -76292,9 +76442,45 @@ if Type==CTLD.CargoZoneType.SHIP then
ctldzone.shiplength=Shiplength or 100
ctldzone.shipwidth=Shipwidth or 10
end
if not exists then
self:AddZone(ctldzone)
end
return self
end
function CTLD:GetCTLDZone(Name,Type)
if Type==CTLD.CargoZoneType.LOAD then
for _,z in pairs(self.pickupZones)do
if z.name==Name then
return z
end
end
elseif Type==CTLD.CargoZoneType.DROP then
for _,z in pairs(self.dropOffZones)do
if z.name==Name then
return z
end
end
elseif Type==CTLD.CargoZoneType.SHIP then
for _,z in pairs(self.shipZones)do
if z.name==Name then
return z
end
end
elseif Type==CTLD.CargoZoneType.BEACON then
for _,z in pairs(self.droppedBeacons)do
if z.name==Name then
return z
end
end
else
for _,z in pairs(self.wpZones)do
if z.name==Name then
return z
end
end
end
return nil
end
function CTLD:AddCTLDZoneFromAirbase(AirbaseName,Type,Color,Active,HasBeacon)
self:T(self.lid.." AddCTLDZoneFromAirbase")
local AFB=AIRBASE:FindByName(AirbaseName)
@ -77540,9 +77726,12 @@ return self
end
function CTLD:onafterCratesBuild(From,Event,To,Group,Unit,Vehicle)
self:T({From,Event,To})
if self.movetroopstowpzone then
if self.movetroopstowpzone and Vehicle then
local cg=self:GetGenericCargoObjectFromGroupName(Vehicle:GetName())
if not(cg and(cg.NoMoveToZone or(self.nomovetozone_names and self.nomovetozone_names[cg:GetName()])))then
self:_MoveGroupToZone(Vehicle)
end
end
return self
end
function CTLD:onbeforeTroopsRTB(From,Event,To,Group,Unit,ZoneName,ZoneObject)
@ -78295,7 +78484,7 @@ CSAR.AircraftType["MH-60R"]=10
CSAR.AircraftType["OH-6A"]=2
CSAR.AircraftType["OH58D"]=2
CSAR.AircraftType["CH-47Fbl1"]=31
CSAR.version="1.0.33"
CSAR.version="1.0.34"
function CSAR:New(Coalition,Template,Alias)
local self=BASE:Inherit(self,FSM:New())
BASE:T({Coalition,Template,Alias})
@ -78741,11 +78930,11 @@ return self
end
local initdcscoord=nil
local initcoord=nil
if _event.id==EVENTS.Ejection then
if _event.id==EVENTS.Ejection and _event.TgtDCSUnit then
initdcscoord=_event.TgtDCSUnit:getPoint()
initcoord=COORDINATE:NewFromVec3(initdcscoord)
self:T({initdcscoord})
else
elseif _event.IniDCSUnit then
initdcscoord=_event.IniDCSUnit:getPoint()
initcoord=COORDINATE:NewFromVec3(initdcscoord)
self:T({initdcscoord})
@ -78808,7 +78997,10 @@ return self
end
if _place:GetCoalition()==self.coalition or _place:GetCoalition()==coalition.side.NEUTRAL then
self:__Landed(2,_event.IniUnitName,_place)
self:_ScheduledSARFlight(_event.IniUnitName,_event.IniGroupName,true,true)
local IsHeloBase=false
local ABName=_place:GetName()
if ABName and string.find(ABName,"^H")then IsHeloBase=true end
self:_ScheduledSARFlight(_event.IniUnitName,_event.IniGroupName,true,true,IsHeloBase)
else
self:T(string.format("Airfield %d, Unit %d",_place:GetCoalition(),_unit:GetCoalition()))
end
@ -79163,7 +79355,7 @@ else
return false
end
end
function CSAR:_ScheduledSARFlight(heliname,groupname,isairport,noreschedule)
function CSAR:_ScheduledSARFlight(heliname,groupname,isairport,noreschedule,IsHeloBase)
self:T(self.lid.." _ScheduledSARFlight")
self:T({heliname,groupname})
local _heliUnit=self:_GetSARHeli(heliname)
@ -79181,7 +79373,7 @@ self:T(self.lid.."[Drop off debug] Check distance to MASH for "..heliname.." Dis
return
end
self:T(self.lid.."[Drop off debug] Check distance to MASH for "..heliname.." Distance km: "..math.floor(_dist/1000))
if(_dist<self.FARPRescueDistance or isairport)and _heliUnit:InAir()==false then
if(_dist<self.FARPRescueDistance or isairport)and((_heliUnit:InAir()==false)or(IsHeloBase==true))then
self:T(self.lid.."[Drop off debug] Distance ok, door check")
if self.pilotmustopendoors and self:_IsLoadingDoorOpen(heliname)==false then
self:_DisplayMessageToSAR(_heliUnit,"Open the door to let me out!",self.messageTime,true,true)
@ -79194,7 +79386,7 @@ end
end
if not noreschedule then
self:__Returning(5,heliname,_woundedGroupName,isairport)
self:ScheduleOnce(5,self._ScheduledSARFlight,self,heliname,groupname,isairport,noreschedule)
self:ScheduleOnce(5,self._ScheduledSARFlight,self,heliname,groupname,isairport,noreschedule,IsHeloBase)
end
return self
end
@ -82463,7 +82655,7 @@ mission.DCStask=mission:GetDCSMissionTask()
mission.DCStask.params.formation=Formation or"Off Road"
return mission
end
function AUFTRAG:NewCAPTUREZONE(OpsZone,Coalition,Speed,Altitude,Formation)
function AUFTRAG:NewCAPTUREZONE(OpsZone,Coalition,Speed,Altitude,Formation,StayInZoneTime)
local mission=AUFTRAG:New(AUFTRAG.Type.CAPTUREZONE)
mission:_TargetFromObject(OpsZone)
mission.coalition=Coalition
@ -82471,6 +82663,7 @@ mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.CAPTUREZON
mission.optionROE=ENUMS.ROE.ReturnFire
mission.optionROT=ENUMS.ROT.PassiveDefense
mission.optionAlarm=ENUMS.AlarmState.Auto
mission.StayInZoneTime=StayInZoneTime
mission.missionFraction=0.1
mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed)or nil
mission.missionAltitude=Altitude and UTILS.FeetToMeters(Altitude)or nil
@ -83290,6 +83483,15 @@ function AUFTRAG:IsOver()
local over=self.status==AUFTRAG.Status.DONE or self.status==AUFTRAG.Status.CANCELLED or self.status==AUFTRAG.Status.SUCCESS or self.status==AUFTRAG.Status.FAILED
return over
end
function AUFTRAG:IsRepeatable()
local repeatmeS=self.repeatedSuccess<self.NrepeatSuccess or self.repeated<self.Nrepeat
local repeatmeF=self.repeatedFailure<self.NrepeatFailure or self.repeated<self.Nrepeat
if repeatmeS==true or repeatmeF==true then return true else return false end
return false
end
function AUFTRAG:IsNotRepeatable()
return not self:IsRepeatable()
end
function AUFTRAG:IsNotOver()
return not self:IsOver()
end
@ -84812,7 +85014,7 @@ end
do
AWACS={
ClassName="AWACS",
version="0.2.72",
version="0.2.73",
lid="",
coalition=coalition.side.BLUE,
coalitiontxt="blue",
@ -85429,6 +85631,11 @@ self:T(self.lid.."SetLocale")
self.locale=Locale or"en"
return self
end
function AWACS:SetBullsCoordinate(Coordinate)
self:T(self.lid.."SetBullsCoordinate")
self.AOCoordinate=Coordinate
return self
end
function AWACS:SetMaxMissionRange(NM)
self.MaxMissionRange=NM or 125
return self
@ -97028,7 +97235,7 @@ if self.verbose>=2 then
local text=string.format("Updating MENU: State=%s, ATC=%s [%s]",self:GetState(),
self.flightcontrol and self.flightcontrol.airbasename or"None",self.flightcontrol and self.flightcontrol:GetFlightStatus(self)or"Unknown")
MESSAGE:New(text,5):ToGroup(self.group)
self:I(self.lid..text)
self:T(self.lid..text)
end
local position=self:GetCoordinate(nil,player.name)
local fc={}
@ -98621,6 +98828,13 @@ local mission=_mission
if mission:IsNotOver()and mission:IsReadyToCancel()then
mission:Cancel()
end
local TNow=timer.getTime()
if mission:IsOver()and mission:IsNotRepeatable()and mission.DeletionTimstamp==nil then
mission.DeletionTimstamp=TNow
end
if mission.DeletionTimstamp~=nil and TNow-mission.DeletionTimstamp>1800 then
mission=nil
end
end
if self:IsAirwing()then
if self:IsRunwayOperational()==false then
@ -98676,7 +98890,7 @@ if EscortAvail and TransportAvail then
self:MissionRequest(mission,assets)
if reinforce then
mission.reinforce=mission.reinforce-#assets
self:I(self.lid..string.format("Reinforced with N=%d Nreinforce=%d",#assets,mission.reinforce))
self:T(self.lid..string.format("Reinforced with N=%d Nreinforce=%d",#assets,mission.reinforce))
end
return true
else
@ -103616,7 +103830,12 @@ if zoneCurr then
self:T(self.lid..string.format("Current target zone=%s owner=%s",zoneCurr:GetName(),zoneCurr:GetOwnerName()))
if zoneCurr:GetOwner()==self:GetCoalition()then
self:T(self.lid..string.format("Zone %s captured ==> Task DONE!",zoneCurr:GetName()))
if Task.StayInZoneTime then
local stay=Task.StayInZoneTime
self:__TaskDone(stay,Task)
else
self:TaskDone(Task)
end
else
self:T(self.lid..string.format("Zone %s NOT captured!",zoneCurr:GetName()))
if Mission:GetGroupStatus(self)==AUFTRAG.GroupStatus.EXECUTING then
@ -104983,7 +105202,7 @@ self.Ndestroyed=self.Ndestroyed+1
self:ElementDead(Element)
end
function OPSGROUP:onafterElementDead(From,Event,To,Element)
self:I(self.lid..string.format("Element dead %s at t=%.3f",Element.name,timer.getTime()))
self:T(self.lid..string.format("Element dead %s at t=%.3f",Element.name,timer.getTime()))
self:_UpdateStatus(Element,OPSGROUP.ElementStatus.DEAD)
if self.spot.On and self.spot.element.name==Element.name then
self:LaserOff()
@ -105256,7 +105475,7 @@ local text=string.format("WARNING: Group is still alive! Current state=%s. Life
self:T(self.lid..text)
end
_DATABASE.FLIGHTGROUPS[self.groupname]=nil
self:I(self.lid.."STOPPED! Unhandled events, cleared scheduler and removed from _DATABASE")
self:T(self.lid.."STOPPED! Unhandled events, cleared scheduler and removed from _DATABASE")
end
function OPSGROUP:onafterOutOfAmmo(From,Event,To)
self:T(self.lid..string.format("Group is out of ammo at t=%.3f",timer.getTime()))

View File

@ -0,0 +1,269 @@
<#
.SYNOPSIS
Patches DCS mission files (.miz) with updated Lua scripts.
.DESCRIPTION
This script extracts a DCS mission file (which is a ZIP archive), replaces or adds
a Lua script file, and repackages the mission. This allows you to update scripts
like Moose_.lua across multiple missions without opening the DCS Mission Editor.
.PARAMETER MissionPath
Path to the .miz mission file to patch. Can be a single file or multiple files.
.PARAMETER LuaScriptPath
Path to the Lua script file to insert/replace in the mission.
.PARAMETER ScriptName
Optional. The name the script should have inside the mission file.
If not specified, uses the filename from LuaScriptPath.
.PARAMETER OutputPath
Optional. Directory where patched missions should be saved.
If not specified, saves to the same directory as the input mission.
.PARAMETER NoVersionIncrement
If specified, does not increment the version number in the filename.
By default, the script automatically increments the patch version (e.g., 1.1.2 -> 1.1.3).
WARNING: Using this flag will OVERWRITE the original mission file!
.EXAMPLE
.\Patch-MooseMissions.ps1 -MissionPath "C:\Missions\MyMission-1.2.3.miz" -LuaScriptPath "C:\Scripts\Moose_.lua"
Creates: MyMission-1.2.4.miz (original 1.2.3 remains untouched)
.EXAMPLE
Get-ChildItem "C:\Missions\*.miz" | .\Patch-MooseMissions.ps1 -LuaScriptPath "C:\Scripts\Moose_.lua"
.EXAMPLE
.\Patch-MooseMissions.ps1 -MissionPath "Mission-2.1.miz" -LuaScriptPath "MyScript.lua" -NoVersionIncrement
WARNING: Overwrites Mission-2.1.miz
.NOTES
Author: F99th-TracerFacer
Version: 2.0
DCS mission files are ZIP archives containing a 'l10n' folder with a 'DEFAULT' subfolder
where Lua scripts are stored.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[Alias("FullName", "Path")]
[string[]]$MissionPath,
[Parameter(Mandatory=$true)]
[string]$LuaScriptPath,
[Parameter(Mandatory=$false)]
[string]$ScriptName,
[Parameter(Mandatory=$false)]
[string]$OutputPath,
[Parameter(Mandatory=$false)]
[switch]$NoVersionIncrement
)
begin {
# Verify Lua script exists
if (-not (Test-Path $LuaScriptPath)) {
throw "Lua script not found: $LuaScriptPath"
}
# Determine script name to use inside mission
if ([string]::IsNullOrWhiteSpace($ScriptName)) {
$ScriptName = Split-Path $LuaScriptPath -Leaf
}
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "DCS Mission Patcher" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Lua Script: $ScriptName" -ForegroundColor Yellow
Write-Host "Source: $LuaScriptPath" -ForegroundColor Gray
Write-Host "Version Increment: $(if ($NoVersionIncrement) { 'Disabled (OVERWRITES ORIGINAL!)' } else { 'Enabled' })" -ForegroundColor $(if ($NoVersionIncrement) { 'Red' } else { 'Green' })
Write-Host ""
# Validate output path if specified
if ($OutputPath -and -not (Test-Path $OutputPath)) {
Write-Host "Creating output directory: $OutputPath" -ForegroundColor Yellow
New-Item -Path $OutputPath -ItemType Directory -Force | Out-Null
}
$successCount = 0
$failCount = 0
# Function to increment version number in filename
function Get-IncrementedFilename {
param(
[string]$FileName
)
# Remove extension
$nameWithoutExt = [System.IO.Path]::GetFileNameWithoutExtension($FileName)
$extension = [System.IO.Path]::GetExtension($FileName)
# Try to find version patterns: X.Y.Z or X.Y or just X at the end
# Patterns to match (in order of specificity):
# 1. Major.Minor.Patch (e.g., 1.2.3)
# 2. Major.Minor (e.g., 1.2)
# 3. Just version number at end (e.g., v5 or -5)
# Pattern 1: X.Y.Z (most common)
if ($nameWithoutExt -match '^(.+?)[-_\s]?(\d+)\.(\d+)\.(\d+)$') {
$baseName = $matches[1]
$major = $matches[2]
$minor = $matches[3]
$patch = [int]$matches[4]
$newPatch = $patch + 1
$separator = if ($nameWithoutExt -match '[-_\s](\d+\.\d+\.\d+)$') { $matches[0][0] } else { '' }
return "$baseName$separator$major.$minor.$newPatch$extension"
}
# Pattern 2: X.Y
elseif ($nameWithoutExt -match '^(.+?)[-_\s]?(\d+)\.(\d+)$') {
$baseName = $matches[1]
$major = $matches[2]
$minor = [int]$matches[3]
$newMinor = $minor + 1
$separator = if ($nameWithoutExt -match '[-_\s](\d+\.\d+)$') { $matches[0][0] } else { '' }
return "$baseName$separator$major.$newMinor$extension"
}
# Pattern 3: Just a number at the end
elseif ($nameWithoutExt -match '^(.+?)[-_\s]?(\d+)$') {
$baseName = $matches[1]
$version = [int]$matches[2]
$newVersion = $version + 1
$separator = if ($nameWithoutExt -match '[-_\s]\d+$') { $matches[0][0] } else { '' }
return "$baseName$separator$newVersion$extension"
}
# No version found - append .1
else {
return "$nameWithoutExt-1.0.1$extension"
}
}
}
process {
foreach ($mission in $MissionPath) {
try {
# Resolve full path
$missionFile = Resolve-Path $mission -ErrorAction Stop
Write-Host "Processing: " -NoNewline -ForegroundColor White
Write-Host "$missionFile" -ForegroundColor Cyan
# Verify mission file exists and is a .miz file
if (-not (Test-Path $missionFile)) {
throw "Mission file not found: $missionFile"
}
if ([System.IO.Path]::GetExtension($missionFile) -ne ".miz") {
throw "File is not a .miz mission file: $missionFile"
}
# Create temporary extraction directory
$tempDir = Join-Path $env:TEMP ("DCS_Mission_Patch_" + [System.Guid]::NewGuid().ToString())
New-Item -Path $tempDir -ItemType Directory -Force | Out-Null
try {
# Extract mission file (it's a ZIP archive)
# Use .NET classes instead of Expand-Archive for better .miz support
Write-Host " Extracting mission..." -ForegroundColor Gray
Add-Type -Assembly System.IO.Compression.FileSystem
[System.IO.Compression.ZipFile]::ExtractToDirectory($missionFile, $tempDir)
# Determine Lua script destination in mission structure
# DCS stores Lua scripts in: l10n/DEFAULT/ folder
$luaDestDir = Join-Path $tempDir "l10n\DEFAULT"
# Create directory if it doesn't exist
if (-not (Test-Path $luaDestDir)) {
Write-Host " Creating l10n/DEFAULT directory..." -ForegroundColor Yellow
New-Item -Path $luaDestDir -ItemType Directory -Force | Out-Null
}
$luaDestPath = Join-Path $luaDestDir $ScriptName
# Check if script already exists
if (Test-Path $luaDestPath) {
Write-Host " Replacing existing script: $ScriptName" -ForegroundColor Yellow
} else {
Write-Host " Adding new script: $ScriptName" -ForegroundColor Green
}
# Copy Lua script to mission
Copy-Item $LuaScriptPath $luaDestPath -Force
# Determine output filename and location
$originalFileName = Split-Path $missionFile -Leaf
if ($NoVersionIncrement) {
# Keep original filename
$outputFileName = $originalFileName
} else {
# Increment version number
$outputFileName = Get-IncrementedFilename -FileName $originalFileName
Write-Host " Version increment: $originalFileName -> $outputFileName" -ForegroundColor Cyan
}
# Determine output directory
if ($OutputPath) {
$outputDir = $OutputPath
} else {
$outputDir = Split-Path $missionFile -Parent
}
$outputMission = Join-Path $outputDir $outputFileName
# Remove existing mission file if it exists
if (Test-Path $outputMission) {
Remove-Item $outputMission -Force
}
# Repackage mission file
Write-Host " Repackaging mission..." -ForegroundColor Gray
# Use .NET classes for better compatibility
Add-Type -Assembly System.IO.Compression.FileSystem
$compressionLevel = [System.IO.Compression.CompressionLevel]::Optimal
# Create ZIP from directory (will become .miz)
[System.IO.Compression.ZipFile]::CreateFromDirectory($tempDir, $outputMission, $compressionLevel, $false)
Write-Host " SUCCESS: Mission patched successfully!" -ForegroundColor Green
Write-Host " Output: $outputMission" -ForegroundColor Gray
Write-Host ""
$successCount++
}
finally {
# Clean up temporary directory
if (Test-Path $tempDir) {
Remove-Item $tempDir -Recurse -Force
}
}
}
catch {
Write-Host " ERROR: $_" -ForegroundColor Red
Write-Host ""
$failCount++
}
}
}
end {
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Patching Complete" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Successful: $successCount" -ForegroundColor Green
Write-Host "Failed: $failCount" -ForegroundColor $(if ($failCount -gt 0) { "Red" } else { "Gray" })
Write-Host ""
if ($successCount -gt 0) {
if ($NoVersionIncrement) {
Write-Host "WARNING: Original mission files were OVERWRITTEN!" -ForegroundColor Red
} else {
Write-Host "INFO: Original mission files remain untouched. New versioned files created." -ForegroundColor Green
}
Write-Host "TIP: Test your patched missions in DCS before using them!" -ForegroundColor Yellow
}
}

View File

@ -0,0 +1,283 @@
<#
.SYNOPSIS
Batch updates all DCS missions with the latest Moose framework.
.DESCRIPTION
This script scans through the DCS mission development folder structure,
finds the latest version of each mission (.miz files with version numbers),
and patches them with the updated Moose_.lua script using Patch-MooseMissions.ps1.
It processes missions in the structure: C:\DCS_MissionDev\DCS_[Terrain]\[MissionFolder]\
For each mission folder, it identifies the latest version (by version number or file date)
and creates a new patched version.
.PARAMETER RootPath
Root directory containing DCS terrain folders. Defaults to C:\DCS_MissionDev
.PARAMETER MooseLuaPath
Path to the Moose_.lua file to patch into missions. Defaults to C:\DCS_MissionDev\Moose_.lua
.PARAMETER WhatIf
Shows what would be processed without actually patching any files.
.PARAMETER ExcludeTerrain
Array of terrain folder names to exclude from processing (e.g., @("DCS_Normandy", "DCS_Falklands"))
.EXAMPLE
.\Patch-MyMooseMissions.ps1
Processes all missions in C:\DCS_MissionDev using the default Moose_.lua
.EXAMPLE
.\Patch-MyMooseMissions.ps1 -WhatIf
Shows what missions would be processed without making any changes
.EXAMPLE
.\Patch-MyMooseMissions.ps1 -ExcludeTerrain @("DCS_Normandy", "DCS_Falklands")
Processes all missions except those in Normandy and Falklands terrains
.NOTES
Author: F99th-TracerFacer
Version: 1.0
Requires: Patch-MooseMissions.ps1 in the same directory
#>
[CmdletBinding(SupportsShouldProcess)]
param(
[Parameter(Mandatory=$false)]
[string]$RootPath = "C:\DCS_MissionDev",
[Parameter(Mandatory=$false)]
[string]$MooseLuaPath = "C:\DCS_MissionDev\Moose_.lua",
[Parameter(Mandatory=$false)]
[string[]]$ExcludeTerrain = @()
)
# Verify paths exist
if (-not (Test-Path $RootPath)) {
Write-Error "Root path not found: $RootPath"
exit 1
}
if (-not (Test-Path $MooseLuaPath)) {
Write-Error "Moose_.lua file not found: $MooseLuaPath"
exit 1
}
# Verify the Patch-MooseMissions.ps1 script exists
$patchScriptPath = Join-Path $PSScriptRoot "Patch-MooseMissions.ps1"
if (-not (Test-Path $patchScriptPath)) {
Write-Error "Patch-MooseMissions.ps1 not found in: $PSScriptRoot"
exit 1
}
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Batch Moose Mission Patcher" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Root Path: $RootPath" -ForegroundColor Yellow
Write-Host "Moose Script: $MooseLuaPath" -ForegroundColor Yellow
Write-Host "Patch Script: $patchScriptPath" -ForegroundColor Yellow
if ($ExcludeTerrain.Count -gt 0) {
Write-Host "Excluded Terrains: $($ExcludeTerrain -join ', ')" -ForegroundColor Yellow
}
Write-Host ""
# Function to parse version number from filename
function Get-VersionNumber {
param([string]$FileName)
$nameWithoutExt = [System.IO.Path]::GetFileNameWithoutExtension($FileName)
# Try X.Y.Z pattern
if ($nameWithoutExt -match '(\d+)\.(\d+)\.(\d+)') {
$major = [int]$matches[1]
$minor = [int]$matches[2]
$patch = [int]$matches[3]
return [PSCustomObject]@{
Major = $major
Minor = $minor
Patch = $patch
Value = ($major * 1000000) + ($minor * 1000) + $patch
HasVersion = $true
}
}
# Try X.Y pattern
elseif ($nameWithoutExt -match '(\d+)\.(\d+)') {
$major = [int]$matches[1]
$minor = [int]$matches[2]
return [PSCustomObject]@{
Major = $major
Minor = $minor
Patch = 0
Value = ($major * 1000000) + ($minor * 1000)
HasVersion = $true
}
}
# Try single version number
elseif ($nameWithoutExt -match '(\d+)$') {
$version = [int]$matches[1]
return [PSCustomObject]@{
Major = $version
Minor = 0
Patch = 0
Value = $version * 1000000
HasVersion = $true
}
}
# No version found
return [PSCustomObject]@{
Major = 0
Minor = 0
Patch = 0
Value = 0
HasVersion = $false
}
}
# Function to get the latest mission file from a directory
function Get-LatestMission {
param([string]$DirectoryPath)
# Get all .miz files in the directory (not subdirectories)
$mizFiles = Get-ChildItem -Path $DirectoryPath -Filter "*.miz" -File -ErrorAction SilentlyContinue
if ($mizFiles.Count -eq 0) {
return $null
}
# Separate files with version numbers from those without
$versionedFiles = @()
$nonVersionedFiles = @()
foreach ($file in $mizFiles) {
$version = Get-VersionNumber -FileName $file.Name
if ($version.HasVersion) {
$versionedFiles += [PSCustomObject]@{
File = $file
Version = $version
}
} else {
$nonVersionedFiles += $file
}
}
# If we have versioned files, return the one with the highest version
if ($versionedFiles.Count -gt 0) {
$latest = $versionedFiles | Sort-Object -Property { $_.Version.Value } -Descending | Select-Object -First 1
return $latest.File
}
# If no versioned files, return the most recently modified file
if ($nonVersionedFiles.Count -gt 0) {
return $nonVersionedFiles | Sort-Object -Property LastWriteTime -Descending | Select-Object -First 1
}
return $null
}
# Get all DCS terrain folders (folders starting with "DCS_")
$terrainFolders = Get-ChildItem -Path $RootPath -Directory | Where-Object { $_.Name -match '^DCS_' }
if ($terrainFolders.Count -eq 0) {
Write-Warning "No DCS terrain folders found in: $RootPath"
exit 0
}
Write-Host "Found $($terrainFolders.Count) terrain folder(s)" -ForegroundColor Green
Write-Host ""
# Track statistics
$totalMissionsFound = 0
$totalMissionsPatched = 0
$totalMissionsFailed = 0
$skippedTerrains = 0
# Process each terrain folder
foreach ($terrainFolder in $terrainFolders) {
$terrainName = $terrainFolder.Name
# Check if this terrain should be excluded
if ($ExcludeTerrain -contains $terrainName) {
Write-Host "SKIPPING TERRAIN: $terrainName (excluded)" -ForegroundColor DarkGray
$skippedTerrains++
continue
}
Write-Host "TERRAIN: $terrainName" -ForegroundColor Cyan
# Get all subdirectories (mission folders)
$missionFolders = Get-ChildItem -Path $terrainFolder.FullName -Directory -ErrorAction SilentlyContinue
if ($missionFolders.Count -eq 0) {
Write-Host " No mission folders found" -ForegroundColor DarkGray
Write-Host ""
continue
}
Write-Host " Found $($missionFolders.Count) mission folder(s)" -ForegroundColor Gray
# Process each mission folder
foreach ($missionFolder in $missionFolders) {
$missionFolderName = $missionFolder.Name
# Get the latest mission file
$latestMission = Get-LatestMission -DirectoryPath $missionFolder.FullName
if ($null -eq $latestMission) {
Write-Host " [$missionFolderName] No .miz files found" -ForegroundColor DarkGray
continue
}
$totalMissionsFound++
Write-Host " [$missionFolderName] Latest: " -NoNewline -ForegroundColor White
Write-Host "$($latestMission.Name)" -ForegroundColor Yellow
# Execute the patch script
if ($PSCmdlet.ShouldProcess($latestMission.FullName, "Patch with Moose_.lua")) {
try {
# Call Patch-MooseMissions.ps1
& $patchScriptPath -MissionPath $latestMission.FullName -LuaScriptPath $MooseLuaPath -ErrorAction Stop
# Check if it succeeded (the script will output its own messages)
if ($LASTEXITCODE -eq 0 -or $null -eq $LASTEXITCODE) {
$totalMissionsPatched++
} else {
$totalMissionsFailed++
}
}
catch {
Write-Host " ERROR: Failed to patch mission - $_" -ForegroundColor Red
$totalMissionsFailed++
}
}
else {
Write-Host " WHATIF: Would patch this mission" -ForegroundColor Magenta
}
}
Write-Host ""
}
# Final summary
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Batch Processing Complete" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Terrains Processed: $($terrainFolders.Count - $skippedTerrains)" -ForegroundColor White
if ($skippedTerrains -gt 0) {
Write-Host "Terrains Skipped: $skippedTerrains" -ForegroundColor DarkGray
}
Write-Host "Missions Found: $totalMissionsFound" -ForegroundColor White
Write-Host "Missions Patched: $totalMissionsPatched" -ForegroundColor Green
if ($totalMissionsFailed -gt 0) {
Write-Host "Missions Failed: $totalMissionsFailed" -ForegroundColor Red
}
Write-Host ""
if ($WhatIfPreference) {
Write-Host "INFO: This was a WhatIf run - no files were modified" -ForegroundColor Magenta
} elseif ($totalMissionsPatched -gt 0) {
Write-Host "SUCCESS: All missions have been patched with the latest Moose framework!" -ForegroundColor Green
Write-Host "TIP: Test your patched missions in DCS before deployment!" -ForegroundColor Yellow
}

View File

@ -0,0 +1,197 @@
# DCS Mission Patcher
PowerShell script to automatically patch DCS mission files (.miz) with updated Lua scripts and increment version numbers.
## Features
- ✅ Updates Lua scripts in .miz files without opening DCS Mission Editor
- ✅ **Automatically increments version numbers** in mission filenames (e.g., 1.2.3 → 1.2.4)
- ✅ **Preserves original missions** - creates new versioned files instead of overwriting
- ✅ Supports single or batch processing of multiple missions
- ✅ Can output to a different directory
- ✅ Handles both new script insertion and existing script replacement
- ✅ Pipeline support for processing multiple missions
- ✅ Smart version detection (supports X.Y.Z, X.Y, and X formats)
## Version Increment Examples
| Input Filename | Output Filename |
|----------------|-----------------|
| `Mission-1.2.3.miz` | `Mission-1.2.4.miz` |
| `Operation Black Gold 2.8.miz` | `Operation Black Gold 2.9.miz` |
| `F99th-Battle of Gori 1.3.miz` | `F99th-Battle of Gori 1.4.miz` |
| `MyMission-5.miz` | `MyMission-6.miz` |
| `Test.miz` | `Test-1.0.1.miz` |
## Usage
### Basic Usage
Update a mission with automatic version increment:
```powershell
.\Patch-MooseMissions.ps1 -MissionPath "MyMission-1.2.3.miz" -LuaScriptPath "Moose_.lua"
# Original: MyMission-1.2.3.miz (untouched)
# Creates: MyMission-1.2.4.miz
```
### Batch Processing
Update all .miz files in a directory:
```powershell
Get-ChildItem "C:\DCS_Missions\*.miz" | .\Patch-MooseMissions.ps1 -LuaScriptPath "C:\Scripts\Moose_.lua"
# Each mission gets a new version
```
### Output to Different Directory
Patch missions and save versioned outputs to a different folder:
```powershell
.\Patch-MooseMissions.ps1 -MissionPath "Mission-1.5.miz" -LuaScriptPath "Moose_.lua" -OutputPath "C:\PatchedMissions"
# Creates: C:\PatchedMissions\Mission-1.6.miz
```
### Disable Version Increment (CAUTION!)
⚠️ Overwrite the original mission file without creating a new version:
```powershell
.\Patch-MooseMissions.ps1 -MissionPath "Mission-1.2.3.miz" -LuaScriptPath "Moose_.lua" -NoVersionIncrement
# WARNING: Overwrites Mission-1.2.3.miz (original is lost!)
```
### Custom Script Name
Insert a script with a different name than the source file:
```powershell
.\Patch-MooseMissions.ps1 -MissionPath "Mission.miz" -LuaScriptPath "MyScript.lua" -ScriptName "CustomName.lua"
```
## Parameters
| Parameter | Required | Description |
|-----------|----------|-------------|
| `-MissionPath` | Yes | Path to .miz mission file(s) to patch |
| `-LuaScriptPath` | Yes | Path to the Lua script to insert/replace |
| `-ScriptName` | No | Name for the script inside the mission (defaults to source filename) |
| `-OutputPath` | No | Directory for patched missions (defaults to source directory) |
| `-NoVersionIncrement` | No | **CAUTION:** Overwrites original instead of creating new version |
## Examples
### Example 1: Update All Caucasus Missions with Version Increment
```powershell
Get-ChildItem "C:\DCS_MissionDev\DCS_Caucasus\*.miz" | .\Patch-MooseMissions.ps1 -LuaScriptPath "C:\Moose\Moose_.lua"
# Each mission gets a new version: 1.2.miz -> 1.3.miz
# Originals remain untouched
```
### Example 2: Update Specific Missions to Output Folder
```powershell
$missions = @(
"F99th-Operation Black Gold 2.8.miz",
"F99th-Operation Ronin 1.4.miz",
"F99th-Battle of Gori 1.3.miz"
)
$missions | .\Patch-MooseMissions.ps1 -LuaScriptPath "Moose_.lua" -OutputPath "C:\UpdatedMissions"
# Creates: Operation Black Gold 2.9.miz, Operation Ronin 1.5.miz, Battle of Gori 1.4.miz in C:\UpdatedMissions
```
### Example 3: Overwrite Original (Use with Caution)
```powershell
.\Patch-MooseMissions.ps1 -MissionPath "TestMission-1.0.miz" -LuaScriptPath "Moose_.lua" -NoVersionIncrement
# WARNING: Overwrites TestMission-1.0.miz (original is lost)
```
## How It Works
1. **Extraction**: The script treats .miz files as ZIP archives and extracts them to a temporary directory
2. **Script Replacement**: Locates the Lua script in `l10n/DEFAULT/` folder and replaces/adds it
3. **Version Increment**: Automatically detects version pattern and increments the patch number
4. **Repackaging**: Compresses the modified mission into a new .miz file with incremented version
5. **Cleanup**: Removes temporary files
## Version Detection Logic
The script intelligently detects and increments version numbers:
- **X.Y.Z format** (e.g., `Mission-1.2.3.miz`) → Increments Z: `Mission-1.2.4.miz`
- **X.Y format** (e.g., `Mission-2.8.miz`) → Increments Y: `Mission-2.9.miz`
- **X format** (e.g., `Mission-5.miz`) → Increments X: `Mission-6.miz`
- **No version** (e.g., `Mission.miz`) → Adds version: `Mission-1.0.1.miz`
Supports various separators: `-`, `_`, or space
## Mission File Structure
DCS mission files (.miz) are ZIP archives with this structure:
```
Mission.miz
├── mission
├── options
├── warehouses
├── l10n/
│ └── DEFAULT/
│ ├── dictionary
│ ├── mapResource
│ └── *.lua ← Scripts are stored here
```
## Important Notes
⚠️ **Always test patched missions** before using them in production or multiplayer!
⚠️ **Originals are safe**: By default, the script creates NEW versioned files and never modifies originals
⚠️ **Script order matters**: If your mission has script load order dependencies, this tool only replaces the file content, not the trigger order
⚠️ **Version increment default**: By default, versions are incremented. Use `-NoVersionIncrement` to overwrite originals (NOT recommended)
**Safe for**: Updating framework files like Moose_.lua, mist.lua, or any embedded Lua script
**Preserves originals**: Original missions remain untouched (new versioned files are created)
**No backups needed**: Since originals aren't modified, you always have your previous version
## Troubleshooting
### "File is not a .miz mission file"
- Ensure you're pointing to a valid .miz file, not a .lua or other file type
### "Lua script not found"
- Verify the path to your Lua script is correct and the file exists
### Mission doesn't work after patching
- Restore from backup
- Verify the Lua script is valid
- Check DCS.log for script errors
- Ensure you updated the correct script name
### Permission errors
- Run PowerShell as Administrator if modifying files in protected directories
- Ensure mission files are not read-only
## Requirements
- Windows PowerShell 5.1 or later (or PowerShell Core 7+)
- Write permissions to mission file locations
- Valid .miz mission files
- Valid Lua script to insert
## Author
**F99th-TracerFacer**
Discord: https://discord.gg/7wBVWKK3
## License
Free to use and modify for the DCS community.