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 if not MOOSE_DEVELOPMENT_FOLDER then
MOOSE_DEVELOPMENT_FOLDER='Scripts' MOOSE_DEVELOPMENT_FOLDER='Scripts'
end end
@ -469,6 +469,7 @@ CH47={},
OH58={}, OH58={},
UH1H={}, UH1H={},
AH64D={}, AH64D={},
UH60L={},
} }
} }
ENUMS.Storage.weapons.nurs.SNEB_TYPE253_F1B="weapons.nurs.SNEB_TYPE253_F1B" 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.M60_MG_Right_Door={4,15,46,177}
ENUMS.Storage.weapons.UH1H.M134_MiniGun_Left_Door={4,15,46,174} 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.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.FIM92={4,4,7,449}
ENUMS.Storage.weapons.OH58.MG_M3P100={4,15,46,2611} ENUMS.Storage.weapons.OH58.MG_M3P100={4,15,46,2611}
ENUMS.Storage.weapons.OH58.MG_M3P200={4,15,46,2610} 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") BASE:T(unit_name.." front door(s) are open")
return true return true
end 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 if type_name=="AH-64D_BLK_II"then
BASE:T(unit_name.." front door(s) are open") BASE:T(unit_name.." front door(s) are open")
return true return true
@ -3860,7 +3887,55 @@ end
UTILS.lcg.seed=(UTILS.lcg.a*UTILS.lcg.seed+UTILS.lcg.c)%UTILS.lcg.m UTILS.lcg.seed=(UTILS.lcg.a*UTILS.lcg.seed+UTILS.lcg.c)%UTILS.lcg.m
return UTILS.lcg.seed/UTILS.lcg.m return UTILS.lcg.seed/UTILS.lcg.m
end 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 farplocation=Coordinate
local farptype=FARPType or ENUMS.FARPType.FARP local farptype=FARPType or ENUMS.FARPType.FARP
local Coalition=Coalition or coalition.side.BLUE local Coalition=Coalition or coalition.side.BLUE
@ -3878,11 +3953,62 @@ local STypeName=statictypes.TypeName
local SShapeName=statictypes.ShapeName local SShapeName=statictypes.ShapeName
local Country=Country or(Coalition==coalition.side.BLUE and country.id.USA or country.id.RUSSIA) local Country=Country or(Coalition==coalition.side.BLUE and country.id.USA or country.id.RUSSIA)
local ReturnObjects={} 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) local newfarp=SPAWNSTATIC:NewFromType(STypeName,"Heliports",Country)
newfarp:InitShape(SShapeName) newfarp:InitShape(SShapeName)
newfarp:InitFARP(callsign,freq,mod,DynamicSpawns,HotStart) newfarp:InitFARP(callsign,freq,mod,DynamicSpawns,HotStart)
local spawnedfarp=newfarp:SpawnFromCoordinate(farplocation,0,Name) local spawnedfarp=newfarp:SpawnFromCoordinate(farplocation,0,Name)
table.insert(ReturnObjects,spawnedfarp) table.insert(ReturnObjects,spawnedfarp)
PopulateStorage(Name,liquids,equip,airframes)
end
local FARPStaticObjectsNato={ local FARPStaticObjectsNato={
["FUEL"]={TypeName="FARP Fuel Depot",ShapeName="GSM Rus",Category="Fortifications"}, ["FUEL"]={TypeName="FARP Fuel Depot",ShapeName="GSM Rus",Category="Fortifications"},
["AMMO"]={TypeName="FARP Ammo Dump Coating",ShapeName="SetkaKP",Category="Fortifications"}, ["AMMO"]={TypeName="FARP Ammo Dump Coating",ShapeName="SetkaKP",Category="Fortifications"},
@ -3911,25 +4037,6 @@ vehicles:InitDelayOff()
local spawnedvehicle=vehicles:SpawnFromCoordinate(vcoordinate) local spawnedvehicle=vehicles:SpawnFromCoordinate(vcoordinate)
table.insert(ReturnObjects,spawnedvehicle) table.insert(ReturnObjects,spawnedvehicle)
end 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 local ADFName
if ADF and type(ADF)=="number"then if ADF and type(ADF)=="number"then
local ADFFreq=ADF*1000 local ADFFreq=ADF*1000
@ -9919,6 +10026,16 @@ self.LastVec2=ZoneUNIT:GetVec2()
_EVENTDISPATCHER:CreateEventNewZone(self) _EVENTDISPATCHER:CreateEventNewZone(self)
return self return self
end 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() function ZONE_UNIT:GetVec2()
local ZoneVec2=self.ZoneUNIT:GetVec2() local ZoneVec2=self.ZoneUNIT:GetVec2()
if ZoneVec2 then if ZoneVec2 then
@ -9980,6 +10097,17 @@ ZoneVec2=self._.ZoneVec2Cache
end end
return ZoneVec2 return ZoneVec2
end 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() function ZONE_GROUP:GetRandomVec2()
local Point={} local Point={}
local Vec2=self._.ZoneGROUP:GetVec2() local Vec2=self._.ZoneGROUP:GetVec2()
@ -21723,7 +21851,7 @@ self:ScheduleOnce(0.3,self.SpawnFunctionHook,mystatic,unpack(self.SpawnFunctionA
end end
if self.StaticCopyFrom~=nil then if self.StaticCopyFrom~=nil then
mystatic.StaticCopyFrom=self.StaticCopyFrom mystatic.StaticCopyFrom=self.StaticCopyFrom
if not _DATABASE.Templates.Statics[Template.name]then end
local TemplateGroup={} local TemplateGroup={}
TemplateGroup.units={} TemplateGroup.units={}
TemplateGroup.units[1]=Template TemplateGroup.units[1]=Template
@ -21731,8 +21859,6 @@ TemplateGroup.x=Template.x
TemplateGroup.y=Template.y TemplateGroup.y=Template.y
TemplateGroup.name=Template.name TemplateGroup.name=Template.name
_DATABASE:_RegisterStaticTemplate(TemplateGroup,self.CoalitionID,self.CategoryID,CountryID) _DATABASE:_RegisterStaticTemplate(TemplateGroup,self.CoalitionID,self.CategoryID,CountryID)
end
end
return mystatic return mystatic
end end
TIMER={ TIMER={
@ -26216,6 +26342,46 @@ return self
end end
return nil return nil
end 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) function CONTROLLABLE:OptionEngageRange(EngageRange)
self:F2({self.ControllableName}) self:F2({self.ControllableName})
EngageRange=EngageRange or 100 EngageRange=EngageRange or 100
@ -31125,6 +31291,13 @@ end
self:T2(string.format("Registered airbase %s",tostring(self.AirbaseName))) self:T2(string.format("Registered airbase %s",tostring(self.AirbaseName)))
return self return self
end 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() function AIRBASE:_GetCategory()
local name=self.AirbaseName local name=self.AirbaseName
local static=StaticObject.getByName(name) local static=StaticObject.getByName(name)
@ -33652,6 +33825,8 @@ REMOVED="REMOVED",
} }
DYNAMICCARGO.AircraftTypes={ DYNAMICCARGO.AircraftTypes={
["CH-47Fbl1"]="CH-47Fbl1", ["CH-47Fbl1"]="CH-47Fbl1",
["Mi-8MTV2"]="CH-47Fbl1",
["Mi-8MT"]="CH-47Fbl1",
} }
DYNAMICCARGO.AircraftDimensions={ DYNAMICCARGO.AircraftDimensions={
["CH-47Fbl1"]={ ["CH-47Fbl1"]={
@ -33660,8 +33835,20 @@ DYNAMICCARGO.AircraftDimensions={
["length"]=11, ["length"]=11,
["ropelength"]=30, ["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) function DYNAMICCARGO:Register(CargoName)
local self=BASE:Inherit(self,POSITIONABLE:New(CargoName)) local self=BASE:Inherit(self,POSITIONABLE:New(CargoName))
self.StaticName=CargoName self.StaticName=CargoName
@ -35274,7 +35461,9 @@ end
end) end)
self.AutoSavePath=SavePath self.AutoSavePath=SavePath
self.AutoSave=AutoSave or true self.AutoSave=AutoSave or true
if self.AutoSave==true then
self:OpenCSV(GameName) self:OpenCSV(GameName)
end
return self return self
end end
function SCORING:SetDisplayMessagePrefix(DisplayMessagePrefix) function SCORING:SetDisplayMessagePrefix(DisplayMessagePrefix)
@ -36298,7 +36487,7 @@ TargetUnitCoalition=TargetUnitCoalition or""
TargetUnitCategory=TargetUnitCategory or"" TargetUnitCategory=TargetUnitCategory or""
TargetUnitType=TargetUnitType or"" TargetUnitType=TargetUnitType or""
TargetUnitName=TargetUnitName 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.CSVFile:write(
'"'..self.GameName..'"'..','.. '"'..self.GameName..'"'..','..
'"'..self.RunTime..'"'..','.. '"'..self.RunTime..'"'..','..
@ -58882,8 +59071,8 @@ end
end end
TIRESIAS={ TIRESIAS={
ClassName="TIRESIAS", ClassName="TIRESIAS",
debug=true, debug=false,
version=" 0.0.7-OPT", version=" 0.0.8",
Interval=20, Interval=20,
GroundSet=nil, GroundSet=nil,
VehicleSet=nil, VehicleSet=nil,
@ -58903,7 +59092,7 @@ self:SetStartState("Stopped")
self:AddTransition("Stopped","Start","Running") self:AddTransition("Stopped","Start","Running")
self:AddTransition("*","Status","*") self:AddTransition("*","Status","*")
self:AddTransition("*","Stop","Stopped") self:AddTransition("*","Stop","Stopped")
self.ExceptionSet=nil self.ExceptionSet=SET_GROUP:New()
self._cached_zones={} self._cached_zones={}
self:HandleEvent(EVENTS.PlayerEnterAircraft,self._EventHandler) self:HandleEvent(EVENTS.PlayerEnterAircraft,self._EventHandler)
self.lid="TIRESIAS "..self.version.." | " self.lid="TIRESIAS "..self.version.." | "
@ -58934,10 +59123,7 @@ exception=true,
} }
Set:ForEachGroupAlive( Set:ForEachGroupAlive(
function(grp) function(grp)
local inAAASet=self.AAASet:IsIncludeObject(grp) if grp:IsGround()and(not grp.Tiresias)then
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
grp.Tiresias=exception_data grp.Tiresias=exception_data
exceptions:AddGroup(grp,true) exceptions:AddGroup(grp,true)
BASE:T(" TIRESIAS: Added exception group: "..grp:GetName()) 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 group_name=group:GetName()
local cache_key=group_name.." _"..radius local cache_key=group_name.." _"..radius
local zone=self._cached_zones[cache_key] local zone=self._cached_zones[cache_key]
local ground=self._cached_groupsets[cache_key]
if not zone then if not zone then
zone=ZONE_GROUP:New(" Zone-"..group_name,group,UTILS.NMToMeters(radius)) zone=ZONE_GROUP:New(" Zone-"..group_name,group,UTILS.NMToMeters(radius))
self._cached_zones[cache_key]=zone self._cached_zones[cache_key]=zone
else else
zone:UpdateFromGroup(group) zone:UpdateFromGroup(group)
end end
if not ground then zone:Scan({Object.Category.UNIT},{Unit.Category.GROUND_UNIT})
ground=SET_GROUP:New():FilterCategoryGround():FilterZones({zone}):FilterOnce() local ground=zone:GetScannedSetGroup()
self._cached_groupsets[cache_key]=ground
else
ground:FilterZones({zone},true):FilterOnce()
end
local count=ground:CountAlive() local count=ground:CountAlive()
if self.debug then if self.debug then
self:I(string.format(" There are %d groups around this plane or helo!",count)) 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 end
flight.Tcharlie=self:_GetCharlieTime(flight) flight.Tcharlie=self:_GetCharlieTime(flight)
local Ccharlie=UTILS.SecondsToClock(flight.Tcharlie) local Ccharlie=UTILS.SecondsToClock(flight.Tcharlie)
brc=brc%360
self:_MarshalCallArrived(flight.onboard,flight.case,brc,alt,Ccharlie,P) self:_MarshalCallArrived(flight.onboard,flight.case,brc,alt,Ccharlie,P)
if self.TACANon and(not flight.ai)and flight.difficulty==AIRBOSS.Difficulty.EASY then if self.TACANon and(not flight.ai)and flight.difficulty==AIRBOSS.Difficulty.EASY then
local radial=self:GetRadial(flight.case,true,true,true) local radial=self:GetRadial(flight.case,true,true,true)
@ -63852,7 +64034,7 @@ playerData.stable=false
playerData.landed=false playerData.landed=false
playerData.Tlso=timer.getTime() playerData.Tlso=timer.getTime()
playerData.Tgroove=nil playerData.Tgroove=nil
playerData.TIG0=nil playerData.TIG0=0
playerData.wire=nil playerData.wire=nil
playerData.flag=-100 playerData.flag=-100
playerData.debriefschedulerID=nil playerData.debriefschedulerID=nil
@ -64882,77 +65064,9 @@ if self:_CheckAbort(X,Z,self.BreakEntry)then
self:_AbortPattern(playerData,X,Z,self.BreakEntry,true) self:_AbortPattern(playerData,X,Z,self.BreakEntry,true)
return return
end 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 if self:_CheckLimits(X,Z,self.BreakEntry)then
self:_PlayerHint(playerData) self:_PlayerHint(playerData)
self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.EARLYBREAK) self:_SetPlayerStep(playerData,AIRBOSS.PatternStep.EARLYBREAK)
clientSHBFlag=false
end end
end end
function AIRBOSS:_Break(playerData,part) function AIRBOSS:_Break(playerData,part)
@ -65240,19 +65354,19 @@ end
if rho>=RAR and rho<=RIM then if rho>=RAR and rho<=RIM then
if gd.LUE>0.22 and lineupError<-0.22 then if gd.LUE>0.22 and lineupError<-0.22 then
env.info" Drift Right across centre ==> DR-" 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)) 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 elseif gd.LUE<-0.22 and lineupError>0.22 then
env.info" Drift Left ==> DL-" 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)) 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 elseif gd.LUE>0.13 and lineupError<-0.14 then
env.info" Little Drift Right across centre ==> (DR-)" 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)) 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 elseif gd.LUE<-0.13 and lineupError>0.14 then
env.info" Little Drift Left across centre ==> (DL-)" 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)) 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
end end
@ -66051,9 +66165,7 @@ local hdg=self.carrier:GetHeading()
if magnetic then if magnetic then
hdg=hdg-self.magvar hdg=hdg-self.magvar
end end
if hdg<0 then hdg=hdg%360
hdg=hdg+360
end
return hdg return hdg
end end
function AIRBOSS:GetBRC() function AIRBOSS:GetBRC()
@ -66152,7 +66264,7 @@ theta=math.asin(vdeck*math.sin(alpha)/vwind)
v=vdeck*math.cos(alpha)-vwind*math.cos(theta) v=vdeck*math.cos(alpha)-vwind*math.cos(theta)
end end
local magvar=magnetic and self.magvar or 0 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 return intowind,v
end end
function AIRBOSS:GetBRCintoWind(vdeck) function AIRBOSS:GetBRCintoWind(vdeck)
@ -66343,7 +66455,7 @@ return select(2,string.gsub(base,pattern,""))
end end
local TIG="" local TIG=""
if playerData.Tgroove and playerData.Tgroove<=360 and playerData.case<3 then if playerData.Tgroove and playerData.Tgroove<=360 and playerData.case<3 then
TIG=self:_EvalGrooveTime(playerData) TIG=self:_EvalGrooveTime(playerData)or"N/A"
end end
local GXX,nXX=self:_Flightdata2Text(playerData,AIRBOSS.GroovePos.XX) local GXX,nXX=self:_Flightdata2Text(playerData,AIRBOSS.GroovePos.XX)
local GIM,nIM=self:_Flightdata2Text(playerData,AIRBOSS.GroovePos.IM) 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 nL=count(G,'_')/2
local nS=count(G,'%(') local nS=count(G,'%(')
local nN=N-nS-nL local nN=N-nS-nL
if TIG=="_OK_"then nL=nL-1 end
local Tgroove=playerData.Tgroove local Tgroove=playerData.Tgroove
local TgrooveUnicorn=Tgroove and(Tgroove>=16.49 and Tgroove<=16.59)or false 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 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 points=0.0
end end
end end
if playerData.wire==1 and points>1 then if playerData.wire==1 and points>=3 and N>4 then
if points==4 then points=points-1
points=3
grade="(OK)"
elseif points==3 then
points=2
grade="--"
end
end end
env.info("Returning: "..grade.." "..points.." "..G) env.info("Returning: "..grade.." "..points.." "..G)
return grade,points,G return grade,points,G
@ -66520,6 +66627,7 @@ O=little("OS")
end end
end end
local S=nil local S=nil
local A=nil
if step~=AIRBOSS.PatternStep.GROOVE_IW then if step~=AIRBOSS.PatternStep.GROOVE_IW then
if AIRBOSS.PatternStep.GROOVE_AR and playerData.waveoff==true and playerData.owo==true then if AIRBOSS.PatternStep.GROOVE_AR and playerData.waveoff==true and playerData.owo==true then
else else
@ -66536,7 +66644,6 @@ S="F"
elseif AOA<acaoa.OnSpeedMin then elseif AOA<acaoa.OnSpeedMin then
S=little("F") S=little("F")
end end
local A=nil
if GSE>self.gle.HIGH then if GSE>self.gle.HIGH then
A=underline("H") A=underline("H")
elseif GSE>self.gle.High then elseif GSE>self.gle.High then
@ -72835,6 +72942,7 @@ if type(Location)=="string"then
Location=ZONE:New(Location) Location=ZONE:New(Location)
end end
self.Location=Location self.Location=Location
self.NoMoveToZone=false
return self return self
end end
function CTLD_CARGO:SetStaticTypeAndShape(Category,TypeName,ShapeName) function CTLD_CARGO:SetStaticTypeAndShape(Category,TypeName,ShapeName)
@ -73326,6 +73434,7 @@ self.dropAsCargoCrate=false
self.smokedistance=2000 self.smokedistance=2000
self.movetroopstowpzone=true self.movetroopstowpzone=true
self.movetroopsdistance=5000 self.movetroopsdistance=5000
self.returntroopstobase=true
self.troopdropzoneradius=100 self.troopdropzoneradius=100
self.VehicleMoveFormation=AI.Task.VehicleFormation.VEE self.VehicleMoveFormation=AI.Task.VehicleFormation.VEE
self.enableHercules=false self.enableHercules=false
@ -74698,7 +74807,7 @@ if not inzone then
inzone,zonename,zone,distance=self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP) inzone,zonename,zone,distance=self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP)
end end
if inzone then if inzone then
droppingatbase=true droppingatbase=self.returntroopstobase
end end
local hoverunload=self:IsCorrectHover(Unit) local hoverunload=self:IsCorrectHover(Unit)
local IsHerc=self:IsFixedWing(Unit) local IsHerc=self:IsFixedWing(Unit)
@ -75384,7 +75493,8 @@ subcatmenus[catName]=MENU_GROUP:New(_group,catName,cratesmenu)
end end
for _,cargoObj in pairs(self.Cargo_Crates)do for _,cargoObj in pairs(self.Cargo_Crates)do
if not cargoObj.DontShowInMenu then 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 if cargoObj.Location then txt=txt.."[R]"end
local stock=cargoObj:GetStock() local stock=cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt=txt.."["..stock.."]"end if stock>=0 and self.showstockinmenuitems then txt=txt.."["..stock.."]"end
@ -75395,7 +75505,8 @@ end
end end
for _,cargoObj in pairs(self.Cargo_Statics)do for _,cargoObj in pairs(self.Cargo_Statics)do
if not cargoObj.DontShowInMenu then 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 if cargoObj.Location then txt=txt.."[R]"end
local stock=cargoObj:GetStock() local stock=cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt=txt.."["..stock.."]"end if stock>=0 and self.showstockinmenuitems then txt=txt.."["..stock.."]"end
@ -75407,7 +75518,8 @@ end
else else
for _,cargoObj in pairs(self.Cargo_Crates)do for _,cargoObj in pairs(self.Cargo_Crates)do
if not cargoObj.DontShowInMenu then 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 if cargoObj.Location then txt=txt.."[R]"end
local stock=cargoObj:GetStock() local stock=cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt=txt.."["..stock.."]"end if stock>=0 and self.showstockinmenuitems then txt=txt.."["..stock.."]"end
@ -75418,7 +75530,8 @@ end
end end
for _,cargoObj in pairs(self.Cargo_Statics)do for _,cargoObj in pairs(self.Cargo_Statics)do
if not cargoObj.DontShowInMenu then 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 if cargoObj.Location then txt=txt.."[R]"end
local stock=cargoObj:GetStock() local stock=cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt=txt.."["..stock.."]"end if stock>=0 and self.showstockinmenuitems then txt=txt.."["..stock.."]"end
@ -75429,14 +75542,15 @@ end
end end
end end
else else
if self.usesubcats then if self.usesubcats==true then
local subcatmenus={} local subcatmenus={}
for catName,_ in pairs(self.subcats)do for catName,_ in pairs(self.subcats)do
subcatmenus[catName]=MENU_GROUP:New(_group,catName,cratesmenu) subcatmenus[catName]=MENU_GROUP:New(_group,catName,cratesmenu)
end end
for _,cargoObj in pairs(self.Cargo_Crates)do for _,cargoObj in pairs(self.Cargo_Crates)do
if not cargoObj.DontShowInMenu then 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 if cargoObj.Location then txt=txt.."[R]"end
local stock=cargoObj:GetStock() local stock=cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt=txt.."["..stock.."]"end if stock>=0 and self.showstockinmenuitems then txt=txt.."["..stock.."]"end
@ -75445,7 +75559,8 @@ end
end end
for _,cargoObj in pairs(self.Cargo_Statics)do for _,cargoObj in pairs(self.Cargo_Statics)do
if not cargoObj.DontShowInMenu then 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 if cargoObj.Location then txt=txt.."[R]"end
local stock=cargoObj:GetStock() local stock=cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt=txt.."["..stock.."]"end if stock>=0 and self.showstockinmenuitems then txt=txt.."["..stock.."]"end
@ -75455,7 +75570,8 @@ end
else else
for _,cargoObj in pairs(self.Cargo_Crates)do for _,cargoObj in pairs(self.Cargo_Crates)do
if not cargoObj.DontShowInMenu then 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 if cargoObj.Location then txt=txt.."[R]"end
local stock=cargoObj:GetStock() local stock=cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt=txt.."["..stock.."]"end if stock>=0 and self.showstockinmenuitems then txt=txt.."["..stock.."]"end
@ -75464,7 +75580,8 @@ end
end end
for _,cargoObj in pairs(self.Cargo_Statics)do for _,cargoObj in pairs(self.Cargo_Statics)do
if not cargoObj.DontShowInMenu then 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 if cargoObj.Location then txt=txt.."[R]"end
local stock=cargoObj:GetStock() local stock=cargoObj:GetStock()
if stock>=0 and self.showstockinmenuitems then txt=txt.."["..stock.."]"end 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) inzone,zonename,zone,distance=self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP)
end end
if inzone then if inzone then
droppingatbase=true droppingatbase=self.returntroopstobase
end end
if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName())then 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) 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 if SubCategory and self.usesubcats~=true then self.usesubcats=true end
return self return self
end 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) function CTLD:AddStaticsCargo(Name,Mass,Stock,SubCategory,DontShowInMenu,Location)
self:T(self.lid.." AddStaticsCargo") self:T(self.lid.." AddStaticsCargo")
self.CargoCounter=self.CargoCounter+1 self.CargoCounter=self.CargoCounter+1
@ -76269,7 +76414,12 @@ self:E(self.lid.."**** Ship does not exist: "..Name)
return self return self
end end
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.active=Active or false
ctldzone.color=Color or SMOKECOLOR.Red ctldzone.color=Color or SMOKECOLOR.Red
ctldzone.name=Name or"NONE" ctldzone.name=Name or"NONE"
@ -76292,9 +76442,45 @@ if Type==CTLD.CargoZoneType.SHIP then
ctldzone.shiplength=Shiplength or 100 ctldzone.shiplength=Shiplength or 100
ctldzone.shipwidth=Shipwidth or 10 ctldzone.shipwidth=Shipwidth or 10
end end
if not exists then
self:AddZone(ctldzone) self:AddZone(ctldzone)
end
return self return self
end 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) function CTLD:AddCTLDZoneFromAirbase(AirbaseName,Type,Color,Active,HasBeacon)
self:T(self.lid.." AddCTLDZoneFromAirbase") self:T(self.lid.." AddCTLDZoneFromAirbase")
local AFB=AIRBASE:FindByName(AirbaseName) local AFB=AIRBASE:FindByName(AirbaseName)
@ -77540,9 +77726,12 @@ return self
end end
function CTLD:onafterCratesBuild(From,Event,To,Group,Unit,Vehicle) function CTLD:onafterCratesBuild(From,Event,To,Group,Unit,Vehicle)
self:T({From,Event,To}) 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) self:_MoveGroupToZone(Vehicle)
end end
end
return self return self
end end
function CTLD:onbeforeTroopsRTB(From,Event,To,Group,Unit,ZoneName,ZoneObject) 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["OH-6A"]=2
CSAR.AircraftType["OH58D"]=2 CSAR.AircraftType["OH58D"]=2
CSAR.AircraftType["CH-47Fbl1"]=31 CSAR.AircraftType["CH-47Fbl1"]=31
CSAR.version="1.0.33" CSAR.version="1.0.34"
function CSAR:New(Coalition,Template,Alias) function CSAR:New(Coalition,Template,Alias)
local self=BASE:Inherit(self,FSM:New()) local self=BASE:Inherit(self,FSM:New())
BASE:T({Coalition,Template,Alias}) BASE:T({Coalition,Template,Alias})
@ -78741,11 +78930,11 @@ return self
end end
local initdcscoord=nil local initdcscoord=nil
local initcoord=nil local initcoord=nil
if _event.id==EVENTS.Ejection then if _event.id==EVENTS.Ejection and _event.TgtDCSUnit then
initdcscoord=_event.TgtDCSUnit:getPoint() initdcscoord=_event.TgtDCSUnit:getPoint()
initcoord=COORDINATE:NewFromVec3(initdcscoord) initcoord=COORDINATE:NewFromVec3(initdcscoord)
self:T({initdcscoord}) self:T({initdcscoord})
else elseif _event.IniDCSUnit then
initdcscoord=_event.IniDCSUnit:getPoint() initdcscoord=_event.IniDCSUnit:getPoint()
initcoord=COORDINATE:NewFromVec3(initdcscoord) initcoord=COORDINATE:NewFromVec3(initdcscoord)
self:T({initdcscoord}) self:T({initdcscoord})
@ -78808,7 +78997,10 @@ return self
end end
if _place:GetCoalition()==self.coalition or _place:GetCoalition()==coalition.side.NEUTRAL then if _place:GetCoalition()==self.coalition or _place:GetCoalition()==coalition.side.NEUTRAL then
self:__Landed(2,_event.IniUnitName,_place) 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 else
self:T(string.format("Airfield %d, Unit %d",_place:GetCoalition(),_unit:GetCoalition())) self:T(string.format("Airfield %d, Unit %d",_place:GetCoalition(),_unit:GetCoalition()))
end end
@ -79163,7 +79355,7 @@ else
return false return false
end end
end end
function CSAR:_ScheduledSARFlight(heliname,groupname,isairport,noreschedule) function CSAR:_ScheduledSARFlight(heliname,groupname,isairport,noreschedule,IsHeloBase)
self:T(self.lid.." _ScheduledSARFlight") self:T(self.lid.." _ScheduledSARFlight")
self:T({heliname,groupname}) self:T({heliname,groupname})
local _heliUnit=self:_GetSARHeli(heliname) local _heliUnit=self:_GetSARHeli(heliname)
@ -79181,7 +79373,7 @@ self:T(self.lid.."[Drop off debug] Check distance to MASH for "..heliname.." Dis
return return
end end
self:T(self.lid.."[Drop off debug] Check distance to MASH for "..heliname.." Distance km: "..math.floor(_dist/1000)) 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") self:T(self.lid.."[Drop off debug] Distance ok, door check")
if self.pilotmustopendoors and self:_IsLoadingDoorOpen(heliname)==false then if self.pilotmustopendoors and self:_IsLoadingDoorOpen(heliname)==false then
self:_DisplayMessageToSAR(_heliUnit,"Open the door to let me out!",self.messageTime,true,true) self:_DisplayMessageToSAR(_heliUnit,"Open the door to let me out!",self.messageTime,true,true)
@ -79194,7 +79386,7 @@ end
end end
if not noreschedule then if not noreschedule then
self:__Returning(5,heliname,_woundedGroupName,isairport) 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 end
return self return self
end end
@ -82463,7 +82655,7 @@ mission.DCStask=mission:GetDCSMissionTask()
mission.DCStask.params.formation=Formation or"Off Road" mission.DCStask.params.formation=Formation or"Off Road"
return mission return mission
end 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) local mission=AUFTRAG:New(AUFTRAG.Type.CAPTUREZONE)
mission:_TargetFromObject(OpsZone) mission:_TargetFromObject(OpsZone)
mission.coalition=Coalition mission.coalition=Coalition
@ -82471,6 +82663,7 @@ mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.CAPTUREZON
mission.optionROE=ENUMS.ROE.ReturnFire mission.optionROE=ENUMS.ROE.ReturnFire
mission.optionROT=ENUMS.ROT.PassiveDefense mission.optionROT=ENUMS.ROT.PassiveDefense
mission.optionAlarm=ENUMS.AlarmState.Auto mission.optionAlarm=ENUMS.AlarmState.Auto
mission.StayInZoneTime=StayInZoneTime
mission.missionFraction=0.1 mission.missionFraction=0.1
mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed)or nil mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed)or nil
mission.missionAltitude=Altitude and UTILS.FeetToMeters(Altitude)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 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 return over
end 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() function AUFTRAG:IsNotOver()
return not self:IsOver() return not self:IsOver()
end end
@ -84812,7 +85014,7 @@ end
do do
AWACS={ AWACS={
ClassName="AWACS", ClassName="AWACS",
version="0.2.72", version="0.2.73",
lid="", lid="",
coalition=coalition.side.BLUE, coalition=coalition.side.BLUE,
coalitiontxt="blue", coalitiontxt="blue",
@ -85429,6 +85631,11 @@ self:T(self.lid.."SetLocale")
self.locale=Locale or"en" self.locale=Locale or"en"
return self return self
end end
function AWACS:SetBullsCoordinate(Coordinate)
self:T(self.lid.."SetBullsCoordinate")
self.AOCoordinate=Coordinate
return self
end
function AWACS:SetMaxMissionRange(NM) function AWACS:SetMaxMissionRange(NM)
self.MaxMissionRange=NM or 125 self.MaxMissionRange=NM or 125
return self return self
@ -97028,7 +97235,7 @@ if self.verbose>=2 then
local text=string.format("Updating MENU: State=%s, ATC=%s [%s]",self:GetState(), 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") self.flightcontrol and self.flightcontrol.airbasename or"None",self.flightcontrol and self.flightcontrol:GetFlightStatus(self)or"Unknown")
MESSAGE:New(text,5):ToGroup(self.group) MESSAGE:New(text,5):ToGroup(self.group)
self:I(self.lid..text) self:T(self.lid..text)
end end
local position=self:GetCoordinate(nil,player.name) local position=self:GetCoordinate(nil,player.name)
local fc={} local fc={}
@ -98621,6 +98828,13 @@ local mission=_mission
if mission:IsNotOver()and mission:IsReadyToCancel()then if mission:IsNotOver()and mission:IsReadyToCancel()then
mission:Cancel() mission:Cancel()
end 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 end
if self:IsAirwing()then if self:IsAirwing()then
if self:IsRunwayOperational()==false then if self:IsRunwayOperational()==false then
@ -98676,7 +98890,7 @@ if EscortAvail and TransportAvail then
self:MissionRequest(mission,assets) self:MissionRequest(mission,assets)
if reinforce then if reinforce then
mission.reinforce=mission.reinforce-#assets 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 end
return true return true
else else
@ -103616,7 +103830,12 @@ if zoneCurr then
self:T(self.lid..string.format("Current target zone=%s owner=%s",zoneCurr:GetName(),zoneCurr:GetOwnerName())) self:T(self.lid..string.format("Current target zone=%s owner=%s",zoneCurr:GetName(),zoneCurr:GetOwnerName()))
if zoneCurr:GetOwner()==self:GetCoalition()then if zoneCurr:GetOwner()==self:GetCoalition()then
self:T(self.lid..string.format("Zone %s captured ==> Task DONE!",zoneCurr:GetName())) 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) self:TaskDone(Task)
end
else else
self:T(self.lid..string.format("Zone %s NOT captured!",zoneCurr:GetName())) self:T(self.lid..string.format("Zone %s NOT captured!",zoneCurr:GetName()))
if Mission:GetGroupStatus(self)==AUFTRAG.GroupStatus.EXECUTING then if Mission:GetGroupStatus(self)==AUFTRAG.GroupStatus.EXECUTING then
@ -104983,7 +105202,7 @@ self.Ndestroyed=self.Ndestroyed+1
self:ElementDead(Element) self:ElementDead(Element)
end end
function OPSGROUP:onafterElementDead(From,Event,To,Element) 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) self:_UpdateStatus(Element,OPSGROUP.ElementStatus.DEAD)
if self.spot.On and self.spot.element.name==Element.name then if self.spot.On and self.spot.element.name==Element.name then
self:LaserOff() self:LaserOff()
@ -105256,7 +105475,7 @@ local text=string.format("WARNING: Group is still alive! Current state=%s. Life
self:T(self.lid..text) self:T(self.lid..text)
end end
_DATABASE.FLIGHTGROUPS[self.groupname]=nil _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 end
function OPSGROUP:onafterOutOfAmmo(From,Event,To) function OPSGROUP:onafterOutOfAmmo(From,Event,To)
self:T(self.lid..string.format("Group is out of ammo at t=%.3f",timer.getTime())) 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.