From 93d5a798fe62f9dc018b1d94a6f6c8e3062ad221 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sun, 29 Oct 2017 19:57:06 +0100 Subject: [PATCH] Static 2.2.5 --- Moose Mission Setup/Moose.lua | 2 +- Moose Mission Setup/Moose_.lua | 1292 +++++++++++++++++++++++++------- 2 files changed, 1003 insertions(+), 291 deletions(-) diff --git a/Moose Mission Setup/Moose.lua b/Moose Mission Setup/Moose.lua index 40fb6a33e..97f014c1c 100644 --- a/Moose Mission Setup/Moose.lua +++ b/Moose Mission Setup/Moose.lua @@ -1,5 +1,5 @@ env.info( '*** MOOSE STATIC INCLUDE START *** ' ) -env.info( 'Moose Generation Timestamp: 20171029_1717' ) +env.info( 'Moose Generation Timestamp: 20171029_1956' ) --- Various routines -- @module routines diff --git a/Moose Mission Setup/Moose_.lua b/Moose Mission Setup/Moose_.lua index 454da58a0..a7ee8d103 100644 --- a/Moose Mission Setup/Moose_.lua +++ b/Moose Mission Setup/Moose_.lua @@ -1,5 +1,5 @@ env.info('*** MOOSE STATIC INCLUDE START *** ') -env.info('Moose Generation Timestamp: 20171026_1856') +env.info('Moose Generation Timestamp: 20171029_1956') env.setErrorMessageBoxEnabled(false) routines={} routines.majorVersion=3 @@ -18358,7 +18358,9 @@ end RAT={ ClassName="RAT", debug=false, +templategroup=nil, alias=nil, +spawninitialized=false, spawndelay=5, spawninterval=5, coalition=nil, @@ -18373,21 +18375,33 @@ AlphaDescent=3.6, roe="hold", rot="noreaction", takeoff=0, +landing=9, mindist=5000, -maxdist=500000, +maxdist=5000000, airports_map={}, airports={}, random_departure=true, random_destination=true, -departure_zones={}, departure_ports={}, destination_ports={}, +Ndestination_Airports=0, +Ndestination_Zones=0, +Ndeparture_Airports=0, +Ndeparture_Zones=0, +destinationzone=false, +return_zones={}, +returnzone=false, excluded_ports={}, +departure_Azone=nil, +destination_Azone=nil, +addfriendlydepartures=false, +addfriendlydestinations=false, ratcraft={}, -Tinactive=300, +Tinactive=600, reportstatus=false, statusinterval=30, placemarkers=false, +FLcruise=nil, FLminuser=nil, FLmaxuser=nil, FLuser=nil, @@ -18399,11 +18413,23 @@ f10menu=true, Menu={}, SubMenuName=nil, respawn_at_landing=false, +norespawn=false, +respawn_after_takeoff=false, respawn_delay=nil, markerids={}, +waypointdescriptions={}, +waypointstatus={}, livery=nil, skill="High", ATCswitch=true, +parking_id=nil, +wp_final=nil, +wp_holding=nil, +radio=nil, +frequency=nil, +modulation=nil, +actype=nil, +uncontrolled=false, } RAT.cat={ plane="plane", @@ -18420,6 +18446,26 @@ cruise=6, descent=7, holding=8, landing=9, +finalwp=10, +} +RAT.status={ +Departure="At departure point", +Climb="Climbing", +Cruise="Cruising", +Uturn="Flying back home", +Descent="Descending", +DescentHolding="Descend to holding point", +Holding="Holding", +Destination="Arrived at destination", +EventBirthAir="Born in air", +EventBirth="Ready and starting engines", +EventEngineStartAir="On journey", +EventEngineStart="Started engines and taxiing", +EventTakeoff="Airborne after take-off", +EventLand="Landed and taxiing", +EventEngineShutdown="Engines off", +EventDead="Dead", +EventCrash="Crashed", } RAT.coal={ same="same", @@ -18449,26 +18495,37 @@ flight={}, airport={}, unregistered=-1, onfinal=-100, +Nclearance=2, +delay=240, } RAT.markerid=0 RAT.MenuF10=nil RAT.id="RAT | " +RAT.version="2.0.0" function RAT:New(groupname,alias) +env.info(RAT.id.."Version "..RAT.version) env.info(RAT.id.."Creating new RAT object from template: "..groupname) alias=alias or groupname local self=BASE:Inherit(self,SPAWN:NewWithAlias(groupname,alias)) self.alias=alias local DCSgroup=Group.getByName(groupname) if DCSgroup==nil then -env.error("Group with name "..groupname.." does not exist in the mission editor!") +env.error(RAT.id.."Group with name "..groupname.." does not exist in the mission editor!") return nil end +self.templategroup=GROUP:FindByName(groupname) self.coalition=DCSgroup:getCoalition() self:_InitAircraft(DCSgroup) self:_GetAirportsOfMap() return self end function RAT:Spawn(naircraft) +if self.spawninitialized==true then +env.error("Spawn function should only be called once per RAT object! Exiting and returning nil.") +return nil +else +self.spawninitialized=true +end self.ngroups=naircraft or 1 if self.ATCswitch and not RAT.ATC.init then RAT:_ATCInit(self.airports_map) @@ -18481,6 +18538,26 @@ self:_GetAirportsOfCoalition() if not self.SubMenuName then self.SubMenuName=self.alias end +if self.departure_Azone~=nil then +self.departure_ports=self:_GetAirportsInZone(self.departure_Azone) +end +if self.destination_Azone~=nil then +self.destination_ports=self:_GetAirportsInZone(self.destination_Azone) +end +if self.addfriendlydepartures then +self:_AddFriendlyAirports(self.departure_ports) +end +if self.addfriendlydestinations then +self:_AddFriendlyAirports(self.destination_ports) +end +if self.FLcruise==nil then +if self.category==RAT.cat.plane then +self.FLcruise=200*RAT.unit.FL2m +else +self.FLcruise=005*RAT.unit.FL2m +end +end +self:_CheckConsistency() local text=string.format("\n******************************************************\n") text=text..string.format("Spawning %i aircraft from template %s of type %s.\n",self.ngroups,self.SpawnTemplatePrefix,self.aircraft.type) text=text..string.format("Alias: %s\n",self.alias) @@ -18488,23 +18565,40 @@ text=text..string.format("Category: %s\n",self.category) text=text..string.format("Friendly coalitions: %s\n",self.friendly) text=text..string.format("Number of airports on map : %i\n",#self.airports_map) text=text..string.format("Number of friendly airports: %i\n",#self.airports) +text=text..string.format("Totally random departure: %s\n",tostring(self.random_departure)) +if not self.random_departure then +text=text..string.format("Number of departure airports: %d\n",self.Ndeparture_Airports) +text=text..string.format("Number of departure zones : %d\n",self.Ndeparture_Zones) +end +text=text..string.format("Totally random destination: %s\n",tostring(self.random_destination)) +if not self.random_destination then +text=text..string.format("Number of destination airports: %d\n",self.Ndestination_Airports) +text=text..string.format("Number of destination zones : %d\n",self.Ndestination_Zones) +end text=text..string.format("Min dist to destination: %4.1f\n",self.mindist) text=text..string.format("Max dist to destination: %4.1f\n",self.maxdist) text=text..string.format("Takeoff type: %i\n",self.takeoff) +text=text..string.format("Landing type: %i\n",self.landing) text=text..string.format("Commute: %s\n",tostring(self.commute)) text=text..string.format("Journey: %s\n",tostring(self.continuejourney)) +text=text..string.format("Destination Zone: %s\n",tostring(self.destinationzone)) +text=text..string.format("Return Zone: %s\n",tostring(self.returnzone)) +text=text..string.format("Uncontrolled: %s\n",tostring(self.uncontrolled)) text=text..string.format("Spawn delay: %4.1f\n",self.spawndelay) text=text..string.format("Spawn interval: %4.1f\n",self.spawninterval) text=text..string.format("Respawn after landing: %s\n",tostring(self.respawn_at_landing)) +text=text..string.format("Respawning off: %s\n",tostring(self.norespawn)) +text=text..string.format("Respawn after take-off: %s\n",tostring(self.respawn_after_takeoff)) text=text..string.format("Respawn delay: %s\n",tostring(self.respawn_delay)) text=text..string.format("ROE: %s\n",tostring(self.roe)) text=text..string.format("ROT: %s\n",tostring(self.rot)) text=text..string.format("Vclimb: %4.1f\n",self.Vclimb) text=text..string.format("AlphaDescent: %4.2f\n",self.AlphaDescent) text=text..string.format("Vcruisemax: %s\n",tostring(self.Vcruisemax)) +text=text..string.format("FLcruise = %6.1f km = FL%3.0f\n",self.FLcruise/1000,self.FLcruise/RAT.unit.FL2m) text=text..string.format("FLuser: %s\n",tostring(self.Fluser)) -text=text..string.format("FLminuser: %s\n",tostring(self.Flminuser)) -text=text..string.format("FLmaxuser: %s\n",tostring(self.Flmaxuser)) +text=text..string.format("FLminuser: %s\n",tostring(self.FLminuser)) +text=text..string.format("FLmaxuser: %s\n",tostring(self.FLmaxuser)) text=text..string.format("Place markers: %s\n",tostring(self.placemarkers)) text=text..string.format("Report status: %s\n",tostring(self.reportstatus)) text=text..string.format("Status interval: %4.1f\n",self.statusinterval) @@ -18512,6 +18606,15 @@ text=text..string.format("Time inactive: %4.1f\n",self.Tinactive) text=text..string.format("Create F10 menu : %s\n",tostring(self.f10menu)) text=text..string.format("F10 submenu name: %s\n",self.SubMenuName) text=text..string.format("ATC enabled : %s\n",tostring(self.ATCswitch)) +text=text..string.format("Radio comms : %s\n",tostring(self.radio)) +text=text..string.format("Radio frequency : %s\n",tostring(self.frequency)) +text=text..string.format("Radio modulation : %s\n",tostring(self.frequency)) +if self.livery then +text=text..string.format("Available liveries:\n") +for _,livery in pairs(self.livery)do +text=text..string.format("- %s\n",livery) +end +end text=text..string.format("******************************************************\n") env.info(RAT.id..text) if self.f10menu then @@ -18537,6 +18640,80 @@ self:HandleEvent(EVENTS.EngineShutdown,self._OnEngineShutdown) self:HandleEvent(EVENTS.Dead,self._OnDead) self:HandleEvent(EVENTS.Crash,self._OnCrash) end +function RAT:_CheckConsistency() +if not self.random_departure then +for _,name in pairs(self.departure_ports)do +if self:_AirportExists(name)then +self.Ndeparture_Airports=self.Ndeparture_Airports+1 +elseif self:_ZoneExists(name)then +self.Ndeparture_Zones=self.Ndeparture_Zones+1 +end +end +for _,name in pairs(self.destination_ports)do +if self:_AirportExists(name)then +self.Ndestination_Airports=self.Ndestination_Airports+1 +elseif self:_ZoneExists(name)then +self.Ndestination_Zones=self.Ndestination_Zones+1 +end +end +if self.Ndeparture_Zones>0 and self.takeoff~=RAT.wp.air then +self.takeoff=RAT.wp.air +env.error(RAT.id.."At least one zone defined as departure and takeoff is NOT set to air. Enabling air start!") +end +if self.Ndeparture_Airports==0 and self.Ndeparture_Zone==0 then +self.random_departure=true +local text="No airports or zones found given in SetDeparture(). Enabling random departure airports!" +env.error(RAT.id..text) +MESSAGE:New(text,30):ToAll() +end +end +if not self.random_destination then +if self.Ndestination_Zones>0 and self.landing~=RAT.wp.air and not self.returnzone then +self.landing=RAT.wp.air +self.destinationzone=true +env.error(RAT.id.."At least one zone defined as destination and landing is NOT set to air. Enabling destination zone!") +end +if self.Ndestination_Airports==0 and self.Ndestination_Zones==0 then +self.random_destination=true +local text="No airports or zones found given in SetDestination(). Enabling random destination airports!" +env.error(RAT.id..text) +MESSAGE:New(text,30):ToAll() +end +end +if self.destinationzone and self.returnzone then +env.error(RAT.id.."Destination zone _and_ return to zone not possible! Disabling return to zone.") +self.returnzone=false +end +if self.returnzone and self.takeoff==RAT.wp.air then +self.landing=RAT.wp.air +end +if self.FLminuser then +self.FLminuser=math.min(self.FLminuser,self.aircraft.ceiling) +end +if self.FLmaxuser then +self.FLmaxuser=math.min(self.FLmaxuser,self.aircraft.ceiling) +end +if self.FLcruise then +self.FLcruise=math.min(self.FLcruise,self.aircraft.ceiling) +end +if self.FLminuser and self.FLmaxuser then +if self.FLminuser>self.FLmaxuser then +local min=self.FLminuser +local max=self.FLmaxuser +self.FLminuser=max +self.FLmaxuser=min +end +end +if self.FLminuser and self.FLcruiseself.FLmaxuser then +self.FLcruise=self.FLmaxuser +end +if self.uncontrolled then +self.takeoff=RAT.wp.hot +end +end function RAT:SetCoalition(friendly) if friendly:lower()=="sameonly"then self.friendly=RAT.coal.sameonly @@ -18561,7 +18738,7 @@ elseif color:lower()=="neutral"then self.coalition=coalition.side.NEUTRAL end end -function RAT:SetCoalition2(id) +function RAT:SetCountry(id) self.country=id end function RAT:SetTakeoff(type) @@ -18579,50 +18756,66 @@ _Type=RAT.wp.coldorhot end self.takeoff=_Type end -function RAT:SetDeparture(names) +function RAT:SetDeparture(departurenames) self.random_departure=false -if type(names)=="table"then +local names +if type(departurenames)=="table"then +names=departurenames +elseif type(departurenames)=="string"then +names={departurenames} +else +env.error(RAT.id.."Input parameter must be a string or a table in SetDeparture()!") +end for _,name in pairs(names)do if self:_AirportExists(name)then table.insert(self.departure_ports,name) +elseif self:_ZoneExists(name)then +table.insert(self.departure_ports,name) else -local z=trigger.misc.getZone(name) -if z then -table.insert(self.departure_zones,name) +env.error(RAT.id.."ERROR! No departure airport or zone found with name "..name) end end end -elseif type(names)=="string"then -if self:_AirportExists(names)then -table.insert(self.departure_ports,names) -else -table.insert(self.departure_zones,names) -end -else -env.error("Input parameter must be a string or a table!") -end -end -function RAT:SetDestination(names) +function RAT:SetDestination(destinationnames) self.random_destination=false -if type(names)=="table"then +local names +if type(destinationnames)=="table"then +names=destinationnames +elseif type(destinationnames)=="string"then +names={destinationnames} +else +env.error(RAT.id.."Input parameter must be a string or a table in SetDestination()!") +end for _,name in pairs(names)do if self:_AirportExists(name)then table.insert(self.destination_ports,name) +elseif self:_ZoneExists(name)then +table.insert(self.destination_ports,name) else -local text=string.format("Airport %s does not exist on map!",name) -env.error(text) +env.error(RAT.id.."ERROR! No destination airport or zone found with name "..name) end end -elseif type(names)=="string"then -if self:_AirportExists(names)then -self.destination_ports={names} -else -local text=string.format("Airport %s does not exist on map!",names) -env.error(text) end -else -env.error("Input parameter must be a string or a table!") +function RAT:DestinationZone() +self.destinationzone=true +self.landing=RAT.wp.air end +function RAT:ReturnZone() +self.returnzone=true +end +function RAT:SetDestinationsFromZone(zone) +self.random_destination=false +self.destination_Azone=zone +end +function RAT:SetDeparturesFromZone(zone) +self.random_departure=false +self.departure_Azone=zone +end +function RAT:AddFriendlyAirportsToDepartures() +self.addfriendlydepartures=true +end +function RAT:AddFriendlyAirportsToDestinations() +self.addfriendlydestinations=true end function RAT:ExcludedAirports(ports) if type(ports)=="string"then @@ -18631,6 +18824,19 @@ else self.excluded_ports=ports end end +function RAT:SetAISkill(skill) +if skill:lower()=="average"then +self.skill="Average" +elseif skill:lower()=="good"then +self.skill="Good" +elseif skill:lower()=="excellent"then +self.skill="Excellent" +elseif skill:lower()=="random"then +self.skill="Random" +else +self.skill="High" +end +end function RAT:Livery(skins) if type(skins)=="string"then self.livery={skins} @@ -18638,35 +18844,80 @@ else self.livery=skins end end -function RAT:ContinueJourney(switch) -switch=switch or true -self.continuejourney=switch +function RAT:ChangeAircraft(actype) +self.actype=actype end -function RAT:Commute(switch) -switch=switch or true -self.commute=switch +function RAT:ContinueJourney() +self.continuejourney=true +self.commute=false +end +function RAT:Commute() +self.commute=true +self.continuejourney=false end function RAT:SetSpawnDelay(delay) +delay=delay or 5 self.spawndelay=math.max(0.5,delay) end function RAT:SetSpawnInterval(interval) +interval=interval or 5 self.spawninterval=math.max(0.5,interval) end function RAT:RespawnAfterLanding(delay) delay=delay or 180 self.respawn_at_landing=true +delay=math.max(0.5,delay) self.respawn_delay=delay end +function RAT:NoRespawn() +self.norespawn=true +end +function RAT:RespawnAfterTakeoff() +self.respawn_after_takeoff=true +end +function RAT:SetParkingID(id) +self.parking_id=id +env.info(RAT.id.."Setting parking ID to "..self.parking_id) +end +function RAT:RadioON() +self.radio=true +end +function RAT:RadioOFF() +self.radio=false +end +function RAT:RadioFrequency(frequency) +self.frequency=frequency +end +function RAT:Uncontrolled() +self.uncontrolled=true +end +function RAT:RadioModulation(modulation) +if modulation=="AM"then +self.modulation=radio.modulation.AM +elseif modulation=="FM"then +self.modulation=radio.modulation.FM +else +self.modulation=radio.modulation.AM +end +end function RAT:TimeDestroyInactive(time) +time=time or self.Tinactive +time=math.max(time,60) self.Tinactive=time end function RAT:SetMaxCruiseSpeed(speed) self.Vcruisemax=speed/3.6 end function RAT:SetClimbRate(rate) +rate=rate or self.Vclimb +rate=math.max(rate,100) +rate=math.min(rate,15000) self.Vclimb=rate end function RAT:SetDescentAngle(angle) +angle=angle or self.AlphaDescent +angle=math.max(angle,0.5) +angle=math.min(angle,50) self.AlphaDescent=angle end function RAT:SetROE(roe) @@ -18691,8 +18942,17 @@ function RAT:MenuName(name) self.SubMenuName=tostring(name) end function RAT:EnableATC(switch) +if switch==nil then +switch=true +end self.ATCswitch=switch end +function RAT:ATC_Clearance(n) +RAT.ATC.Nclearance=n or 2 +end +function RAT:ATC_Delay(time) +RAT.ATC.delay=time or 240 +end function RAT:SetMinDistance(dist) self.mindist=math.max(500,dist*1000) end @@ -18700,37 +18960,45 @@ function RAT:SetMaxDistance(dist) self.maxdist=dist*1000 end function RAT:_Debug(switch) -switch=switch or true +if switch==nil then +switch=true +end self.debug=switch end function RAT:StatusReports(switch) -switch=switch or true +if switch==nil then +switch=true +end self.reportstatus=switch end function RAT:PlaceMarkers(switch) -switch=switch or true +if switch==nil then +switch=true +end self.placemarkers=switch end -function RAT:SetFL(height) -self.FLuser=height*RAT.unit.FL2m +function RAT:SetFL(FL) +FL=FL or self.FLcruise +FL=math.max(FL,0) +self.FLuser=FL*RAT.unit.FL2m end -function RAT:SetFLmax(height) -self.FLmaxuser=height*RAT.unit.FL2m +function RAT:SetFLmax(FL) +self.FLmaxuser=FL*RAT.unit.FL2m end function RAT:SetMaxCruiseAltitude(alt) self.FLmaxuser=alt end -function RAT:SetFLmin(height) -self.FLminuser=height*RAT.unit.FL2m +function RAT:SetFLmin(FL) +self.FLminuser=FL*RAT.unit.FL2m end function RAT:SetMinCruiseAltitude(alt) self.FLminuser=alt end -function RAT:SetFLcruise(height) -self.aircraft.FLcruise=height*RAT.unit.FL2m +function RAT:SetFLcruise(FL) +self.FLcruise=FL*RAT.unit.FL2m end function RAT:SetCruiseAltitude(alt) -self.aircraft.FLcruise=alt +self.FLcruise=alt end function RAT:_InitAircraft(DCSgroup) local DCSunit=DCSgroup:getUnit(1) @@ -18751,15 +19019,10 @@ end self.aircraft.type=DCStype self.aircraft.fuel=DCSunit:getFuel() self.aircraft.Rmax=DCSdesc.range*RAT.unit.nm2m -self.aircraft.Reff=self.aircraft.Rmax*self.aircraft.fuel*0.9 +self.aircraft.Reff=self.aircraft.Rmax*self.aircraft.fuel*0.95 self.aircraft.Vmax=DCSdesc.speedMax self.aircraft.Vymax=DCSdesc.VyMax self.aircraft.ceiling=DCSdesc.Hmax -if self.category==RAT.cat.plane then -self.aircraft.FLcruise=200*RAT.unit.FL2m -else -self.aircraft.FLcruise=005*RAT.unit.FL2m -end local text=string.format("\n******************************************************\n") text=text..string.format("Aircraft parameters:\n") text=text..string.format("Template group = %s\n",self.SpawnTemplatePrefix) @@ -18770,28 +19033,51 @@ text=text..string.format("Max air speed = %6.1f m/s\n",self.aircraft.Vmax) text=text..string.format("Max climb speed = %6.1f m/s\n",self.aircraft.Vymax) text=text..string.format("Initial Fuel = %6.1f\n",self.aircraft.fuel*100) text=text..string.format("Max range = %6.1f km\n",self.aircraft.Rmax/1000) -text=text..string.format("Eff range = %6.1f km\n",self.aircraft.Reff/1000) +text=text..string.format("Eff range = %6.1f km (with 95 percent initial fuel amount)\n",self.aircraft.Reff/1000) text=text..string.format("Ceiling = %6.1f km = FL%3.0f\n",self.aircraft.ceiling/1000,self.aircraft.ceiling/RAT.unit.FL2m) -text=text..string.format("FL cruise = %6.1f km = FL%3.0f\n",self.aircraft.FLcruise/1000,self.aircraft.FLcruise/RAT.unit.FL2m) text=text..string.format("******************************************************\n") env.info(RAT.id..text) end -function RAT:_SpawnWithRoute(_departure,_destination) -local _takeoff=self.takeoff +function RAT:_SpawnWithRoute(_departure,_destination,_takeoff,_landing,_livery,_waypoint) +local takeoff=self.takeoff +local landing=self.landing if self.takeoff==RAT.wp.coldorhot then local temp={RAT.wp.cold,RAT.wp.hot} -_takeoff=temp[math.random(2)] +takeoff=temp[math.random(2)] end -local departure,destination,waypoints=self:_SetRoute(_takeoff,_departure,_destination) +if _takeoff then +takeoff=_takeoff +end +if _landing then +landing=_landing +end +local departure,destination,waypoints=self:_SetRoute(takeoff,landing,_departure,_destination,_waypoint) if not(departure and destination and waypoints)then return nil end -self:_ModifySpawnTemplate(waypoints) +local livery +if _livery then +livery=_livery +elseif self.livery then +livery=self.livery[math.random(#self.livery)] +local text=string.format("Chosen livery for group %s: %s",self:_AnticipatedGroupName(),livery) +env.info(RAT.id..text) +else +livery=nil +end +self:_ModifySpawnTemplate(waypoints,livery) local group=self:SpawnWithIndex(self.SpawnIndex) self.alive=self.alive+1 -if self.ATCswitch then +if self.ATCswitch and landing==RAT.wp.landing then +if self.returnzone then +RAT:_ATCAddFlight(group:GetName(),departure:GetName()) +else RAT:_ATCAddFlight(group:GetName(),destination:GetName()) end +end +if self.placemarkers then +self:_PlaceMarkers(waypoints,self.SpawnIndex) +end self:_SetROE(group,self.roe) self:_SetROT(group,self.rot) self.ratcraft[self.SpawnIndex]={} @@ -18813,7 +19099,10 @@ end self.ratcraft[self.SpawnIndex]["P0"]=group:GetCoordinate() self.ratcraft[self.SpawnIndex]["Pnow"]=group:GetCoordinate() self.ratcraft[self.SpawnIndex]["Distance"]=0 -self.ratcraft[self.SpawnIndex]["takeoff"]=_takeoff +self.ratcraft[self.SpawnIndex].takeoff=takeoff +self.ratcraft[self.SpawnIndex].landing=landing +self.ratcraft[self.SpawnIndex].livery=livery +self.ratcraft[self.SpawnIndex].despawnme=false if self.f10menu then local name=self.aircraft.type.." ID "..tostring(self.SpawnIndex) self.Menu[self.SubMenuName].groups[self.SpawnIndex]=MENU_MISSION:New(name,self.Menu[self.SubMenuName].groups) @@ -18826,37 +19115,86 @@ MENU_MISSION_COMMAND:New("No reaction",self.Menu[self.SubMenuName].groups[self.S MENU_MISSION_COMMAND:New("Passive defense",self.Menu[self.SubMenuName].groups[self.SpawnIndex]["rot"],self._SetROT,self,group,RAT.ROT.passive) MENU_MISSION_COMMAND:New("Evade on fire",self.Menu[self.SubMenuName].groups[self.SpawnIndex]["rot"],self._SetROT,self,group,RAT.ROT.evade) MENU_MISSION_COMMAND:New("Despawn group",self.Menu[self.SubMenuName].groups[self.SpawnIndex],self._Despawn,self,group) -MENU_MISSION_COMMAND:New("Clear for landing",self.Menu[self.SubMenuName].groups[self.SpawnIndex],self.ClearForLanding,self,group:GetName()) -MENU_MISSION_COMMAND:New("Place markers",self.Menu[self.SubMenuName].groups[self.SpawnIndex],self._PlaceMarkers,self,waypoints) +MENU_MISSION_COMMAND:New("Place markers",self.Menu[self.SubMenuName].groups[self.SpawnIndex],self._PlaceMarkers,self,waypoints,self.SpawnIndex) MENU_MISSION_COMMAND:New("Status report",self.Menu[self.SubMenuName].groups[self.SpawnIndex],self.Status,self,true,self.SpawnIndex) end return self.SpawnIndex end function RAT:ClearForLanding(name) -env.info("ATC: setting user flag "..name.." to 1.") trigger.action.setUserFlag(name,1) local flagvalue=trigger.misc.getUserFlag(name) -env.info("ATC: user flag "..name.." ="..flagvalue) +env.info(RAT.id.."ATC: User flag value (landing) for "..name.." set to "..flagvalue) end function RAT:_Respawn(group) local index=self:GetSpawnIndexFromGroup(group) local departure=self.ratcraft[index].departure local destination=self.ratcraft[index].destination +local takeoff=self.ratcraft[index].takeoff +local landing=self.ratcraft[index].landing +local livery=self.ratcraft[index].livery +local lastwp=self.ratcraft[index].waypoints[#self.ratcraft[index].waypoints] local _departure=nil local _destination=nil +local _takeoff=nil +local _landing=nil +local _livery=nil +local _lastwp=nil if self.continuejourney then _departure=destination:GetName() +_livery=livery +if self.destinationzone then +_takeoff=RAT.wp.air +_landing=RAT.wp.air +elseif self.returnzone then +_takeoff=self.takeoff +if self.takeoff==RAT.wp.air then +_landing=RAT.wp.air +else +_landing=RAT.wp.landing +end +_departure=departure:GetName() +else +_takeoff=self.takeoff +_landing=self.landing +end elseif self.commute then _departure=destination:GetName() _destination=departure:GetName() +_livery=livery +if self.destinationzone then +if self.takeoff==RAT.wp.air then +_takeoff=RAT.wp.air +_landing=RAT.wp.air +else +if takeoff==RAT.wp.air then +_takeoff=self.takeoff +_landing=RAT.wp.air +else +_takeoff=RAT.wp.air +_landing=RAT.wp.landing +end +end +elseif self.returnzone then +_departure=departure:GetName() +_destination=destination:GetName() +_takeoff=self.takeoff +_landing=self.landing +end +end +if _takeoff==RAT.wp.air and(self.continuejourney or self.commute)then +_lastwp=lastwp +end +if self.debug then +env.info(RAT.id..string.format("self.takeoff, takeoff, _takeoff = %s, %s, %s",tostring(self.takeoff),tostring(takeoff),tostring(_takeoff))) +env.info(RAT.id..string.format("self.landing, landing, _landing = %s, %s, %s",tostring(self.landing),tostring(landing),tostring(_landing))) end if self.respawn_delay then -SCHEDULER:New(nil,self._SpawnWithRoute,{self,_departure,_destination},self.respawn_delay) +SCHEDULER:New(nil,self._SpawnWithRoute,{self,_departure,_destination,_takeoff,_landing,_livery,_lastwp},self.respawn_delay) else -self:_SpawnWithRoute(_departure,_destination) +self:_SpawnWithRoute(_departure,_destination,_takeoff,_landing,_livery,_lastwp) end end -function RAT:_SetRoute(takeoff,_departure,_destination) +function RAT:_SetRoute(takeoff,landing,_departure,_destination,_waypoint) local VxCruiseMax if self.Vcruisemax then VxCruiseMax=min(self.Vcruisemax,self.aircraft.Vmax) @@ -18872,10 +19210,16 @@ local VxFinal=VxHolding*0.9 local VyClimb=math.min(self.Vclimb*RAT.unit.ft2meter/60,self.aircraft.Vymax) local AlphaClimb=math.asin(VyClimb/VxClimb) local AlphaDescent=math.rad(self.AlphaDescent) +local FLcruise_expect=self.FLcruise local departure=nil if _departure then if self:_AirportExists(_departure)then departure=AIRBASE:FindByName(_departure) +if takeoff==RAT.wp.air then +departure=departure:GetZone() +end +elseif self:_ZoneExists(_departure)then +departure=ZONE:New(_departure) else local text=string.format("ERROR: Specified departure airport %s does not exist for %s!",_departure,self.alias) env.error(RAT.id..text) @@ -18891,8 +19235,12 @@ return nil end local Pdeparture if takeoff==RAT.wp.air then +if _waypoint then +Pdeparture=COORDINATE:New(_waypoint.x,_waypoint.alt,_waypoint.y) +else local vec2=departure:GetRandomVec2() Pdeparture=COORDINATE:NewFromVec2(vec2) +end else Pdeparture=departure:GetCoordinate() end @@ -18904,29 +19252,59 @@ Hmin=1000 else Hmin=50 end -H_departure=self:_Randomize(self.aircraft.FLcruise*0.7,0.3,Pdeparture.y+Hmin,self.aircraft.FLcruise) +H_departure=self:_Randomize(FLcruise_expect*0.7,0.3,Pdeparture.y+Hmin,FLcruise_expect) +if self.FLminuser then +H_departure=math.max(H_departure,self.FLminuser) +end +if _waypoint then +H_departure=_waypoint.alt +end else H_departure=Pdeparture.y end +local mindist=self.mindist if self.FLminuser then -self.mindist=self:_MinDistance(AlphaClimb,AlphaDescent,self.FLminuser-H_departure) -local text=string.format("Adjusting min distance to %d km (for given min FL%03d)",self.mindist/1000,self.FLminuser/RAT.unit.FL2m) +local hclimb=self.FLminuser-H_departure +local hdescent=self.FLminuser-H_departure +local Dclimb,Ddescent,Dtot=self:_MinDistance(AlphaClimb,AlphaDescent,hclimb,hdescent) +if takeoff==RAT.wp.air and landing==RAT.wpair then +mindist=0 +elseif takeoff==RAT.wp.air then +mindist=Ddescent +elseif landing==RAT.wp.air then +mindist=Dclimb +else +mindist=Dtot +end +mindist=math.max(self.mindist,mindist) +local text=string.format("Adjusting min distance to %d km (for given min FL%03d)",mindist/1000,self.FLminuser/RAT.unit.FL2m) env.info(RAT.id..text) end local destination=nil if _destination then if self:_AirportExists(_destination)then destination=AIRBASE:FindByName(_destination) +if landing==RAT.wp.air or self.returnzone then +destination=destination:GetZone() +end +elseif self:_ZoneExists(_destination)then +destination=ZONE:New(_destination) else -local text=string.format("ERROR: Specified destination airport %s does not exist for %s!",_destination,self.alias) +local text=string.format("ERROR: Specified destination airport/zone %s does not exist for %s!",_destination,self.alias) env.error(RAT.id..text) end else +local random=self.random_destination if self.continuejourney and _departure and#self.destination_ports<3 then -self.random_destination=true +random=true end -local destinations=self:_GetDestinations(departure,Pdeparture,self.mindist,math.min(self.aircraft.Reff,self.maxdist)) -destination=self:_PickDestination(destinations) +local mylanding=landing +local acrange=self.aircraft.Reff +if self.returnzone then +mylanding=RAT.wp.air +acrange=self.aircraft.Reff/2 +end +destination=self:_PickDestination(departure,Pdeparture,mindist,math.min(acrange,self.maxdist),random,mylanding) end if not destination then local text=string.format("No valid destination airport could be found for %s!",self.alias) @@ -18935,18 +19313,33 @@ env.error(RAT.id..text) return nil end if destination:GetName()==departure:GetName()then -local text=string.format("%s: Destination and departure airport are identical. Airport %s (ID %d).",self.alias,destination:GetName(),destination:GetID()) -MESSAGE:New(text,120):ToAll() +local text=string.format("%s: Destination and departure are identical. Airport/zone %s.",self.alias,destination:GetName()) +MESSAGE:New(text,30):ToAll() env.error(RAT.id..text) end -local Pdestination=destination:GetCoordinate() -local H_destination=Pdestination.y -local Vholding -if self.category==RAT.cat.plane then -Vholding=destination:GetCoordinate():GetRandomVec2InRadius(10000,5000) -else -Vholding=destination:GetCoordinate():GetRandomVec2InRadius(1000,500) +local Preturn +local destination_returnzone +if self.returnzone then +local vec2=destination:GetRandomVec2() +Preturn=COORDINATE:NewFromVec2(vec2) +destination_returnzone=destination +destination=departure end +local Pdestination +if landing==RAT.wp.air then +local vec2=destination:GetRandomVec2() +Pdestination=COORDINATE:NewFromVec2(vec2) +else +Pdestination=destination:GetCoordinate() +end +local H_destination=Pdestination.y +local Rhmin=8000 +local Rhmax=20000 +if self.category==RAT.cat.heli then +Rhmin=500 +Rhmax=1000 +end +local Vholding=Pdestination:GetRandomVec2InRadius(Rhmax,Rhmin) local Pholding=COORDINATE:NewFromVec2(Vholding) local H_holding=Pholding.y local h_holding @@ -18956,46 +19349,96 @@ else h_holding=150 end h_holding=self:_Randomize(h_holding,0.2) -local d_holding=Pholding:Get2DDistance(Pdestination) -local deltaH=math.abs(h_holding+H_holding-H_departure) -local heading=self:_Course(Pdeparture,Pholding) -local d_total=Pdeparture:Get2DDistance(Pholding) -local phi=math.atan(deltaH/d_total) -local PhiClimb=AlphaClimb+phi -local PhiDescent=AlphaDescent-phi -local FLmax=self:_FLmax(AlphaClimb,AlphaDescent,d_total,phi,H_departure) -local FLmin=math.max(H_departure,H_holding+h_holding) -if FLmin>FLmax then -FLmin=FLmax*0.75 +local Hh_holding=H_holding+h_holding +if landing==RAT.wp.air then +Hh_holding=H_departure end +local d_holding=Pholding:Get2DDistance(Pdestination) +local heading +local d_total +if self.returnzone then +heading=self:_Course(Pdeparture,Preturn) +d_total=Pdeparture:Get2DDistance(Preturn)+Preturn:Get2DDistance(Pholding) +else +heading=self:_Course(Pdeparture,Pholding) +d_total=Pdeparture:Get2DDistance(Pholding) +end +if takeoff==RAT.wp.air then +local H_departure_max +if landing==RAT.wp.air then +H_departure_max=H_departure +else +H_departure_max=d_total*math.tan(AlphaDescent)+Hh_holding +end +H_departure=math.min(H_departure,H_departure_max) +end +local deltaH=math.abs(H_departure-Hh_holding) +local phi=math.atan(deltaH/d_total) +local phi_climb +local phi_descent +if(H_departure>Hh_holding)then +phi_climb=AlphaClimb+phi +phi_descent=AlphaDescent-phi +else +phi_climb=AlphaClimb-phi +phi_descent=AlphaDescent+phi +end +local D_total +if self.returnzone then +D_total=math.sqrt(deltaH*deltaH+d_total/2*d_total/2) +else +D_total=math.sqrt(deltaH*deltaH+d_total*d_total) +end +local gamma=math.rad(180)-phi_climb-phi_descent +local a=D_total*math.sin(phi_climb)/math.sin(gamma) +local b=D_total*math.sin(phi_descent)/math.sin(gamma) +local hphi_max=b*math.sin(phi_climb) +local hphi_max2=a*math.sin(phi_descent) +local h_max1=b*math.sin(AlphaClimb) +local h_max2=a*math.sin(AlphaDescent) +local h_max +if(H_departure>Hh_holding)then +h_max=math.min(h_max1,h_max2) +else +h_max=math.max(h_max1,h_max2) +end +local FLmax=h_max+H_departure +local FLmin=math.max(H_departure,Hh_holding) if self.category==RAT.cat.heli then FLmin=math.max(H_departure,H_destination)+50 FLmax=math.max(H_departure,H_destination)+1000 end -FLmax=math.min(FLmax,self.aircraft.ceiling*0.9) +FLmax=math.min(FLmax,self.aircraft.ceiling) if self.FLminuser then -FLmin=self.FLminuser +FLmin=math.max(self.FLminuser,FLmin) end if self.FLmaxuser then -FLmax=self.FLmaxuser +FLmax=math.min(self.FLmaxuser,FLmax) end -if self.aircraft.FLcruiseFLmax then +FLmin=FLmax end -if self.aircraft.FLcruise>FLmax then -self.aircraft.FLcruise=FLmax +if FLcruise_expectFLmax then +FLcruise_expect=FLmax +end +local FLcruise=self:_Random_Gaussian(FLcruise_expect,math.abs(FLmax-FLmin)/4,FLmin,FLmax) if self.FLuser then FLcruise=self.FLuser +FLcruise=math.max(FLcruise,FLmin) +FLcruise=math.min(FLcruise,FLmax) end local h_climb=FLcruise-H_departure -local d_climb=h_climb/math.tan(PhiClimb) -local h_descent=FLcruise-(H_holding+h_holding) -local d_descent=h_descent/math.tan(PhiDescent) +local h_descent=FLcruise-Hh_holding +local d_climb=h_climb/math.tan(AlphaClimb) +local d_descent=h_descent/math.tan(AlphaDescent) local d_cruise=d_total-d_climb-d_descent local text=string.format("\n******************************************************\n") -text=text..string.format("Template = %s\n\n",self.SpawnTemplatePrefix) +text=text..string.format("Template = %s\n",self.SpawnTemplatePrefix) +text=text..string.format("Alias = %s\n",self.alias) +text=text..string.format("Group name = %s\n\n",self:_AnticipatedGroupName()) text=text..string.format("Speeds:\n") text=text..string.format("VxCruiseMin = %6.1f m/s = %5.1f km/h\n",VxCruiseMin,VxCruiseMin*3.6) text=text..string.format("VxCruiseMax = %6.1f m/s = %5.1f km/h\n",VxCruiseMax,VxCruiseMax*3.6) @@ -19023,74 +19466,160 @@ text=text..string.format("FLmin = %6.1f m ASL = FL%03d\n",FLmin,FLmin/RA text=text..string.format("FLcruise = %6.1f m ASL = FL%03d\n",FLcruise,FLcruise/RAT.unit.FL2m) text=text..string.format("FLmax = %6.1f m ASL = FL%03d\n",FLmax,FLmax/RAT.unit.FL2m) text=text..string.format("\nAngles:\n") -text=text..string.format("Alpha climb = %6.1f Deg\n",math.deg(AlphaClimb)) -text=text..string.format("Alpha descent = %6.1f Deg\n",math.deg(AlphaDescent)) -text=text..string.format("Phi (slope) = %6.1f Deg\n",math.deg(phi)) -text=text..string.format("Phi climb = %6.1f Deg\n",math.deg(PhiClimb)) -text=text..string.format("Phi descent = %6.1f Deg\n",math.deg(PhiDescent)) +text=text..string.format("Alpha climb = %6.2f Deg\n",math.deg(AlphaClimb)) +text=text..string.format("Alpha descent = %6.2f Deg\n",math.deg(AlphaDescent)) +text=text..string.format("Phi (slope) = %6.2f Deg\n",math.deg(phi)) +text=text..string.format("Phi climb = %6.2f Deg\n",math.deg(phi_climb)) +text=text..string.format("Phi descent = %6.2f Deg\n",math.deg(phi_descent)) +if self.debug then +local h_climb_max=FLmax-H_departure +local h_descent_max=FLmax-Hh_holding +local d_climb_max=h_climb_max/math.tan(AlphaClimb) +local d_descent_max=h_descent_max/math.tan(AlphaDescent) +local d_cruise_max=d_total-d_climb_max-d_descent_max text=text..string.format("Heading = %6.1f Deg\n",heading) +text=text..string.format("\nSSA triangle:\n") +text=text..string.format("D_total = %6.1f km\n",D_total/1000) +text=text..string.format("gamma = %6.1f Deg\n",math.deg(gamma)) +text=text..string.format("a = %6.1f m\n",a) +text=text..string.format("b = %6.1f m\n",b) +text=text..string.format("hphi_max = %6.1f m\n",hphi_max) +text=text..string.format("hphi_max2 = %6.1f m\n",hphi_max2) +text=text..string.format("h_max1 = %6.1f m\n",h_max1) +text=text..string.format("h_max2 = %6.1f m\n",h_max2) +text=text..string.format("h_max = %6.1f m\n",h_max) +text=text..string.format("\nMax heights and distances:\n") +text=text..string.format("d_climb_max = %6.1f km\n",d_climb_max/1000) +text=text..string.format("d_cruise_max = %6.1f km\n",d_cruise_max/1000) +text=text..string.format("d_descent_max = %6.1f km\n",d_descent_max/1000) +text=text..string.format("h_climb_max = %6.1f m\n",h_climb_max) +text=text..string.format("h_descent_max = %6.1f m\n",h_descent_max) +end text=text..string.format("******************************************************\n") env.info(RAT.id..text) if d_cruise<0 then d_cruise=100 end -local c0=Pdeparture -local c1=c0:Translate(d_climb/2,heading) -local c2=c1:Translate(d_climb/2,heading) -local c3=c2:Translate(d_cruise,heading) -local c4=c3:Translate(d_descent/2,heading) -local c5=Pholding -local c6=Pdestination -local wp0=self:_Waypoint(takeoff,c0,VxClimb,H_departure,departure) -local wp1=self:_Waypoint(RAT.wp.climb,c1,VxClimb,H_departure+(FLcruise-H_departure)/2) -local wp2=self:_Waypoint(RAT.wp.cruise,c2,VxCruise,FLcruise) -local wp3=self:_Waypoint(RAT.wp.cruise,c3,VxCruise,FLcruise) -local wp4=self:_Waypoint(RAT.wp.descent,c4,VxDescent,FLcruise-(FLcruise-(h_holding+H_holding))/2) -local wp5=self:_Waypoint(RAT.wp.holding,c5,VxHolding,H_holding+h_holding) -local wp6=self:_Waypoint(RAT.wp.landing,c6,VxFinal,H_destination,destination) -local waypoints={wp0,wp1,wp2,wp3,wp4,wp5,wp6} -if self.placemarkers then -self:_PlaceMarkers(waypoints) +local wp={} +local c={} +c[#c+1]=Pdeparture +wp[#wp+1]=self:_Waypoint(#wp+1,takeoff,c[#wp+1],VxClimb,H_departure,departure) +self.waypointdescriptions[#wp]="Departure" +self.waypointstatus[#wp]=RAT.status.Departure +if takeoff==RAT.wp.air then +if d_climb<5000 or d_cruise<5000 then +d_cruise=d_cruise+d_climb +else +c[#c+1]=c[#c]:Translate(d_climb,heading) +wp[#wp+1]=self:_Waypoint(#wp+1,RAT.wp.cruise,c[#wp+1],VxCruise,FLcruise) +self.waypointdescriptions[#wp]="Begin of Cruise" +self.waypointstatus[#wp]=RAT.status.Cruise +end +else +c[#c+1]=c[#c]:Translate(d_climb/2,heading) +c[#c+1]=c[#c]:Translate(d_climb/2,heading) +wp[#wp+1]=self:_Waypoint(#wp+1,RAT.wp.climb,c[#wp+1],VxClimb,H_departure+(FLcruise-H_departure)/2) +self.waypointdescriptions[#wp]="Climb" +self.waypointstatus[#wp]=RAT.status.Climb +wp[#wp+1]=self:_Waypoint(#wp+1,RAT.wp.cruise,c[#wp+1],VxCruise,FLcruise) +self.waypointdescriptions[#wp]="Begin of Cruise" +self.waypointstatus[#wp]=RAT.status.Cruise +end +if self.returnzone then +c[#c+1]=Preturn +wp[#wp+1]=self:_Waypoint(#wp+1,RAT.wp.cruise,c[#wp+1],VxCruise,FLcruise) +self.waypointdescriptions[#wp]="Return Zone" +self.waypointstatus[#wp]=RAT.status.Uturn +end +if landing==RAT.wp.air then +c[#c+1]=Pdestination +wp[#wp+1]=self:_Waypoint(#wp+1,RAT.wp.finalwp,c[#wp+1],VxCruise,FLcruise) +self.waypointdescriptions[#wp]="Final Destination" +self.waypointstatus[#wp]=RAT.status.Destination +elseif self.returnzone then +c[#c+1]=c[#c]:Translate(d_cruise/2,heading-180) +wp[#wp+1]=self:_Waypoint(#wp+1,RAT.wp.cruise,c[#wp+1],VxCruise,FLcruise) +self.waypointdescriptions[#wp]="End of Cruise" +self.waypointstatus[#wp]=RAT.status.Descent +else +c[#c+1]=c[#c]:Translate(d_cruise,heading) +wp[#wp+1]=self:_Waypoint(#wp+1,RAT.wp.cruise,c[#wp+1],VxCruise,FLcruise) +self.waypointdescriptions[#wp]="End of Cruise" +self.waypointstatus[#wp]=RAT.status.Descent +end +if landing==RAT.wp.landing then +if self.returnzone then +c[#c+1]=c[#c]:Translate(d_descent/2,heading-180) +wp[#wp+1]=self:_Waypoint(#wp+1,RAT.wp.descent,c[#wp+1],VxDescent,FLcruise-(FLcruise-(h_holding+H_holding))/2) +self.waypointdescriptions[#wp]="Descent" +self.waypointstatus[#wp]=RAT.status.DescentHolding +else +c[#c+1]=c[#c]:Translate(d_descent/2,heading) +wp[#wp+1]=self:_Waypoint(#wp+1,RAT.wp.descent,c[#wp+1],VxDescent,FLcruise-(FLcruise-(h_holding+H_holding))/2) +self.waypointdescriptions[#wp]="Descent" +self.waypointstatus[#wp]=RAT.status.DescentHolding +end +end +if landing==RAT.wp.landing then +c[#c+1]=Pholding +wp[#wp+1]=self:_Waypoint(#wp+1,RAT.wp.holding,c[#wp+1],VxHolding,H_holding+h_holding) +self.waypointdescriptions[#wp]="Holding Point" +self.waypointstatus[#wp]=RAT.status.Holding +self.wp_holding=#wp +c[#c+1]=Pdestination +wp[#wp+1]=self:_Waypoint(#wp+1,landing,c[#wp+1],VxFinal,H_destination,destination) +self.waypointdescriptions[#wp]="Destination" +self.waypointstatus[#wp]=RAT.status.Destination +end +self.wp_final=#wp +local waypoints={} +for _,p in ipairs(wp)do +table.insert(waypoints,p) end self:_Routeinfo(waypoints,"Waypoint info in set_route:") +if self.returnzone then +return departure,destination_returnzone,waypoints +else return departure,destination,waypoints end +end function RAT:_PickDeparture(takeoff) local departures={} +if self.random_departure then +for _,airport in pairs(self.airports)do +local name=airport:GetName() +if not self:_Excluded(name)then if takeoff==RAT.wp.air then -if self.random_departure then -for _,airport in pairs(self.airports)do -if not self:_Excluded(airport:GetName())then table.insert(departures,airport:GetZone()) -end -end else -for _,name in pairs(self.departure_zones)do -if not self:_Excluded(name)then -table.insert(departures,ZONE:New(name)) -end -end -for _,name in pairs(self.departure_ports)do -if not self:_Excluded(name)then -table.insert(departures,AIRBASE:FindByName(name):GetZone()) -end -end -end -else -if self.random_departure then -for _,airport in pairs(self.airports)do -if not self:_Excluded(airport:GetName())then table.insert(departures,airport) end end +end else for _,name in pairs(self.departure_ports)do -if not self:_Excluded(name)then -table.insert(departures,AIRBASE:FindByName(name)) -end +local dep=nil +if self:_AirportExists(name)then +if takeoff==RAT.wp.air then +dep=AIRBASE:FindByName(name):GetZone() +else +dep=AIRBASE:FindByName(name) +end +elseif self:_ZoneExists(name)then +if takeoff==RAT.wp.air then +dep=ZONE:New(name) +else +env.error(RAT.id.."Takeoff is not in air. Cannot use "..name.." as departure!") +end +else +env.error(RAT.id.."No airport or zone found with name "..name) +end +if dep then +table.insert(departures,dep) end end end +env.info(RAT.id.."Number of possible departures = "..#departures) local departure=departures[math.random(#departures)] local text if departure and departure:GetName()then @@ -19104,59 +19633,100 @@ if self.debug then MESSAGE:New(text,30):ToAll() end else +env.error(RAT.id.."No departure airport or zone found.") departure=nil end return departure end -function RAT:_PickDestination(destinations,_random) -local destination=nil -if destinations and#destinations>0 then -destination=destinations[math.random(#destinations)] -local text="Chosen destination airport: "..destination:GetName().." (ID "..destination:GetID()..")" -env.info(RAT.id..text) -if self.debug then -MESSAGE:New(text,30):ToAll() -end -else -env.error(RAT.id.."No destination airport found.") -end -return destination -end -function RAT:_GetDestinations(departure,q,minrange,maxrange) +function RAT:_PickDestination(departure,q,minrange,maxrange,random,landing) minrange=minrange or self.mindist maxrange=maxrange or self.maxdist -local possible_destinations={} -if self.random_destination then +local destinations={} +if random then for _,airport in pairs(self.airports)do local name=airport:GetName() if self:_IsFriendly(name)and not self:_Excluded(name)and name~=departure:GetName()then local distance=q:Get2DDistance(airport:GetCoordinate()) if distance>=minrange and distance<=maxrange then -table.insert(possible_destinations,airport) +if landing==RAT.wp.air then +table.insert(destinations,airport:GetZone()) +else +table.insert(destinations,airport) +end end end end else for _,name in pairs(self.destination_ports)do if name~=departure:GetName()then -local airport=AIRBASE:FindByName(name) -table.insert(possible_destinations,airport) +local dest=nil +if self:_AirportExists(name)then +if landing==RAT.wp.air then +dest=AIRBASE:FindByName(name):GetZone() +else +dest=AIRBASE:FindByName(name) +end +elseif self:_ZoneExists(name)then +if landing==RAT.wp.air then +dest=ZONE:New(name) +else +env.error(RAT.id.."Landing is not in air. Cannot use zone "..name.." as destination!") +end +else +env.error(RAT.id.."No airport or zone found with name "..name) +end +if dest then +local distance=q:Get2DDistance(dest:GetCoordinate()) +if distance>=minrange and distance<=maxrange then +table.insert(destinations,dest) +else +local text=string.format("Destination %s is ouside range. Distance = %5.1f km, min = %5.1f km, max = %5.1f km.",name,distance,minrange,maxrange) +env.info(RAT.id..text) end end end -env.info(RAT.id.."Number of possible destination airports = "..#possible_destinations) -if#possible_destinations>0 then +end +end +env.info(RAT.id.."Number of possible destinations = "..#destinations) +if#destinations>0 then local function compare(a,b) local qa=q:Get2DDistance(a:GetCoordinate()) local qb=q:Get2DDistance(b:GetCoordinate()) return qa0 then +destination=destinations[math.random(#destinations)] +local text +if landing==RAT.wp.air then +text=string.format("Chosen destination zone: %s.",destination:GetName()) +else +text=string.format("Chosen destination airport: %s (ID %d).",destination:GetName(),destination:GetID()) +end +env.info(RAT.id..text) +if self.debug then +MESSAGE:New(text,30):ToAll() +end +else +env.error(RAT.id.."No destination airport or zone found.") +destination=nil +end +return destination +end +function RAT:_GetAirportsInZone(zone) +local airports={} +for _,airport in pairs(self.airports)do +local name=airport:GetName() +local coord=airport:GetCoordinate() +if zone:IsPointVec3InZone(coord)then +table.insert(airports,name) +end +end +return airports end function RAT:_Excluded(port) for _,name in pairs(self.excluded_ports)do @@ -19194,9 +19764,7 @@ local _myab=AIRBASE:FindByName(_name) table.insert(self.airports_map,_myab) if self.debug then local text1="MOOSE: Airport ID = ".._myab:GetID().." and Name = ".._myab:GetName()..", Category = ".._myab:GetCategory()..", TypeName = ".._myab:GetTypeName() -local text2="DCS : Airport ID = "..airbase:getID().." and Name = "..airbase:getName()..", Category = "..airbase:getCategory()..", TypeName = "..airbase:getTypeName() env.info(RAT.id..text1) -env.info(RAT.id..text2) end end end @@ -19223,11 +19791,6 @@ function RAT:Status(message,forID) message=message or false forID=forID or false local ngroups=#self.ratcraft -if(message and not forID)or self.reportstatus then -local text=string.format("Alive groups of template %s: %d",self.SpawnTemplatePrefix,self.alive) -env.info(RAT.id..text) -MESSAGE:New(text,20):ToAll() -end local Tnow=timer.getTime() for i=1,ngroups do if self.ratcraft[i].group then @@ -19256,7 +19819,7 @@ Tg=Tnow-self.ratcraft[i]["Tground"] Dg=coords:Get2DDistance(self.ratcraft[i]["Pground"]) dTlast=Tnow-self.ratcraft[i]["Tlastcheck"] if dTlast>self.Tinactive then -if Dg<50 then +if Dg<50 and not self.uncontrolled then stationary=true end self.ratcraft[i]["Tlastcheck"]=Tnow @@ -19273,18 +19836,9 @@ local Dtravel=Pn:Get2DDistance(self.ratcraft[i]["Pnow"]) self.ratcraft[i]["Pnow"]=Pn self.ratcraft[i]["Distance"]=self.ratcraft[i]["Distance"]+Dtravel local Ddestination=Pn:Get2DDistance(self.ratcraft[i].destination:GetCoordinate()) -local Hp=COORDINATE:New(self.ratcraft[i].waypoints[6].x,self.ratcraft[i].waypoints[6].alt,self.ratcraft[i].waypoints[6].y) -local Dholding=Pn:Get2DDistance(Hp) local status=self.ratcraft[i].status -local DRholding -if self.category==RAT.cat.plane then -DRholding=8000 -else -DRholding=2000 -end -if self.ATCswitch and Dholding<=DRholding and string.match(status,"On journey")then -RAT:_ATCRegisterFlight(group:GetName(),Tnow) -self.ratcraft[i].status="Holding" +if self.uncontrolled then +status="Uncontrolled" end if(forID and i==forID)or(not forID)then local text=string.format("ID %i of group %s\n",i,prefix) @@ -19295,7 +19849,7 @@ text=text..string.format("%s travelling from %s to %s (and continueing form ther else text=text..string.format("%s travelling from %s to %s\n",type,departure,destination) end -text=text..string.format("Status: %s",self.ratcraft[i].status) +text=text..string.format("Status: %s",status) if airborne then text=text.." [airborne]\n" else @@ -19303,9 +19857,9 @@ text=text.." [on ground]\n" end text=text..string.format("Fuel = %3.0f %%\n",fuel) text=text..string.format("Life = %3.0f %%\n",life) -text=text..string.format("FL%03d = %i m\n",alt/RAT.unit.FL2m,alt) +text=text..string.format("FL%03d = %i m ASL\n",alt/RAT.unit.FL2m,alt) text=text..string.format("Distance travelled = %6.1f km\n",self.ratcraft[i]["Distance"]/1000) -text=text..string.format("Distance to destination = %6.1f km",Dholding/1000) +text=text..string.format("Distance to destination = %6.1f km",Ddestination/1000) if not airborne then text=text..string.format("\nTime on ground = %6.0f seconds\n",Tg) text=text..string.format("Position change = %8.1f m since %3.0f seconds.",Dg,dTlast) @@ -19313,21 +19867,27 @@ end if self.debug then env.info(RAT.id..text) end -if self.reportstatus or message then +if message then MESSAGE:New(text,20):ToAll() end end if not airborne then if stationary then -local text=string.format("Group %s is despawned after being %4.0f seconds inaktive on ground.",self.SpawnTemplatePrefix,dTlast) +local text=string.format("Group %s is despawned after being %4.0f seconds inaktive on ground.",self.alias,dTlast) env.info(RAT.id..text) self:_Despawn(group) end if life<10 and Dtravel<100 then -local text=string.format("Damaged group %s is despawned. Life = %3.0f",self.SpawnTemplatePrefix,life) +local text=string.format("Damaged group %s is despawned. Life = %3.0f",self.alias,life) self:_Despawn(group) end end +if self.ratcraft[i].despawnme then +local text=string.format("Flight %s will be despawned NOW!",self.alias) +env.info(RAT.id..text) +self:_Respawn(self.ratcraft[i].group) +self:_Despawn(self.ratcraft[i].group) +end end else if self.debug then @@ -19336,6 +19896,11 @@ env.info(RAT.id..text) end end end +if(message and not forID)then +local text=string.format("Alive groups of %s: %d",self.alias,self.alive) +env.info(RAT.id..text) +MESSAGE:New(text,20):ToAll() +end end function RAT:_GetLife(group) local life=0.0 @@ -19357,8 +19922,15 @@ return life end function RAT:_SetStatus(group,status) local index=self:GetSpawnIndexFromGroup(group) -env.info(RAT.id.."Status for group "..group:GetName()..": "..status) self.ratcraft[index].status=status +local no1=status==RAT.status.Departure +local no2=status==RAT.status.EventBirthAir +local no3=status==RAT.status.Holding +local text=string.format("Flight %s: %s.",group:GetName(),status) +env.info(RAT.id..text) +if(not(no1 or no2 or no3))then +MESSAGE:New(text,10):ToAllIf(self.reportstatus) +end end function RAT:_OnBirth(EventData) local SpawnGroup=EventData.IniGroup @@ -19371,8 +19943,10 @@ env.info(RAT.id..text) local status if SpawnGroup:InAir()then status="Just born (after air start)" +status=RAT.status.EventBirthAir else status="Starting engines (after birth)" +status=RAT.status.EventBirth end self:_SetStatus(SpawnGroup,status) end @@ -19394,8 +19968,10 @@ env.info(RAT.id..text) local status if SpawnGroup:InAir()then status="On journey (after air start)" +status=RAT.status.EventEngineStartAir else status="Taxiing (after engines started)" +status=RAT.status.EventEngineStart end self:_SetStatus(SpawnGroup,status) end @@ -19414,7 +19990,13 @@ if EventPrefix then if EventPrefix==self.alias then local text="Event: Group "..SpawnGroup:GetName().." is airborne." env.info(RAT.id..text) -self:_SetStatus(SpawnGroup,"On journey (after takeoff)") +local status=RAT.status.EventTakeoff +self:_SetStatus(SpawnGroup,status) +if self.respawn_after_takeoff then +text="Event: Group "..SpawnGroup:GetName().." will be respawned." +env.info(RAT.id..text) +self:_Respawn(SpawnGroup) +end end end else @@ -19431,11 +20013,12 @@ if EventPrefix then if EventPrefix==self.alias then local text="Event: Group "..SpawnGroup:GetName().." landed." env.info(RAT.id..text) -self:_SetStatus(SpawnGroup,"Taxiing (after landing)") +local status=RAT.status.EventLand +self:_SetStatus(SpawnGroup,status) if self.ATCswitch then RAT:_ATCFlightLanded(SpawnGroup:GetName()) end -if self.respawn_at_landing then +if self.respawn_at_landing and not self.norespawn then text="Event: Group "..SpawnGroup:GetName().." will be respawned." env.info(RAT.id..text) self:_Respawn(SpawnGroup) @@ -19456,8 +20039,9 @@ if EventPrefix then if EventPrefix==self.alias then local text="Event: Group "..SpawnGroup:GetName().." shut down its engines." env.info(RAT.id..text) -self:_SetStatus(SpawnGroup,"Parking (shutting down engines)") -if not self.respawn_at_landing then +local status=RAT.status.EventEngineShutdown +self:_SetStatus(SpawnGroup,status) +if not self.respawn_at_landing and not self.norespawn then text="Event: Group "..SpawnGroup:GetName().." will be respawned." env.info(RAT.id..text) self:_Respawn(SpawnGroup) @@ -19481,7 +20065,8 @@ if EventPrefix then if EventPrefix==self.alias then local text="Event: Group "..SpawnGroup:GetName().." died." env.info(RAT.id..text) -self:_SetStatus(SpawnGroup,"Destroyed (after dead)") +local status=RAT.status.EventDead +self:_SetStatus(SpawnGroup,status) end end else @@ -19492,14 +20077,15 @@ end end function RAT:_OnCrash(EventData) local SpawnGroup=EventData.IniGroup -env.info(string.format("%sGroup %s crashed!",RAT.id,SpawnGroup:GetName())) if SpawnGroup then +env.info(string.format("%sGroup %s crashed!",RAT.id,SpawnGroup:GetName())) local EventPrefix=self:_GetPrefixFromGroup(SpawnGroup) if EventPrefix then if EventPrefix==self.alias then local text="Event: Group "..SpawnGroup:GetName().." crashed." env.info(RAT.id..text) -self:_SetStatus(SpawnGroup,"Crashed") +local status=RAT.status.EventCrash +self:_SetStatus(SpawnGroup,status) end end else @@ -19510,38 +20096,34 @@ end end function RAT:_Despawn(group) local index=self:GetSpawnIndexFromGroup(group) -self.ratcraft[index].group:Destroy() self.ratcraft[index].group=nil +group:Destroy() self.alive=self.alive-1 if self.f10menu then self.Menu[self.SubMenuName]["groups"][index]:Remove() end end -function RAT:_Waypoint(Type,Coord,Speed,Altitude,Airport) +function RAT:_Waypoint(index,Type,Coord,Speed,Altitude,Airport) local _Altitude=Altitude or Coord.y local Hland=Coord:GetLandHeight() local _Type=nil local _Action=nil local _alttype="RADIO" -local _AID=nil if Type==RAT.wp.cold then _Type="TakeOffParking" _Action="From Parking Area" _Altitude=0 _alttype="RADIO" -_AID=Airport:GetID() elseif Type==RAT.wp.hot then _Type="TakeOffParkingHot" _Action="From Parking Area Hot" _Altitude=0 _alttype="RADIO" -_AID=Airport:GetID() elseif Type==RAT.wp.runway then _Type="TakeOff" _Action="From Parking Area" _Altitude=0 _alttype="RADIO" -_AID=Airport:GetID() elseif Type==RAT.wp.air then _Type="Turning Point" _Action="Turning Point" @@ -19567,7 +20149,10 @@ _Type="Land" _Action="Landing" _Altitude=0 _alttype="RADIO" -_AID=Airport:GetID() +elseif Type==RAT.wp.finalwp then +_Type="Turning Point" +_Action="Turning Point" +_alttype="BARO" else env.error("Unknown waypoint type in RAT:Waypoint() function!") _Type="Turning Point" @@ -19575,7 +20160,9 @@ _Action="Turning Point" _alttype="RADIO" end local text=string.format("\n******************************************************\n") +text=text..string.format("Waypoint = %d\n",index) text=text..string.format("Template = %s\n",self.SpawnTemplatePrefix) +text=text..string.format("Alias = %s\n",self.alias) text=text..string.format("Type: %i - %s\n",Type,_Type) text=text..string.format("Action: %s\n",_Action) text=text..string.format("Coord: x = %6.1f km, y = %6.1f km, alt = %6.1f m\n",Coord.x/1000,Coord.z/1000,Coord.y) @@ -19586,7 +20173,7 @@ if Airport then if Type==RAT.wp.air then text=text..string.format("Zone = %s\n",Airport:GetName()) else -text=text..string.format("Airport = %s with ID %i\n",Airport:GetName(),Airport:GetID()) +text=text..string.format("Airport = %s\n",Airport:GetName()) end else text=text..string.format("No airport/zone specified\n") @@ -19607,7 +20194,7 @@ RoutePoint.speed_locked=true RoutePoint.ETA=nil RoutePoint.ETA_locked=false RoutePoint.name="RAT waypoint" -if(Airport~=nil)and Type~=RAT.wp.air then +if(Airport~=nil)and(Type~=RAT.wp.air)then local AirbaseID=Airport:GetID() local AirbaseCategory=Airport:GetDesc().category if AirbaseCategory==Airbase.Category.SHIP then @@ -19628,15 +20215,17 @@ RoutePoint.properties={ ["vangle"]=0, ["steer"]=2, } -if Type==RAT.wp.holding then -local Duration=self:_Randomize(90,0.9) -RoutePoint.task=self:_TaskHolding({x=Coord.x,y=Coord.z},Altitude,Speed,Duration) -else +local TaskCombo={} +local TaskHolding=self:_TaskHolding({x=Coord.x,y=Coord.z},Altitude,Speed,self:_Randomize(90,0.9)) +local TaskWaypoint=self:_TaskFunction("RAT._WaypointFunction",self,index) RoutePoint.task={} RoutePoint.task.id="ComboTask" RoutePoint.task.params={} -RoutePoint.task.params.tasks={} +TaskCombo[#TaskCombo+1]=TaskWaypoint +if Type==RAT.wp.holding then +TaskCombo[#TaskCombo+1]=TaskHolding end +RoutePoint.task.params.tasks=TaskCombo return RoutePoint end function RAT:_Routeinfo(waypoints,comment) @@ -19648,7 +20237,7 @@ end text=text..string.format("Number of waypoints = %i\n",#waypoints) for i=1,#waypoints do local p=waypoints[i] -text=text..string.format("WP #%i: x = %6.1f km, y = %6.1f km, alt = %6.1f m\n",i-1,p.x/1000,p.y/1000,p.alt) +text=text..string.format("WP #%i: x = %6.1f km, y = %6.1f km, alt = %6.1f m %s\n",i-1,p.x/1000,p.y/1000,p.alt,self.waypointdescriptions[i]) end local total=0.0 for i=1,#waypoints-1 do @@ -19661,13 +20250,13 @@ local y2=point2.y local d=math.sqrt((x1-x2)^2+(y1-y2)^2) local heading=self:_Course(point1,point2) total=total+d -text=text..string.format("Distance from WP %i-->%i = %6.1f km. Heading = %i.\n",i-1,i,d/1000,heading) +text=text..string.format("Distance from WP %i-->%i = %6.1f km. Heading = %03d : %s - %s\n",i-1,i,d/1000,heading,self.waypointdescriptions[i],self.waypointdescriptions[i+1]) end text=text..string.format("Total distance = %6.1f km\n",total/1000) -local text=string.format("******************************************************\n") +text=text..string.format("******************************************************\n") if self.debug then -env.info(RAT.id..text) end +env.info(RAT.id..text) return total end function RAT:_TaskHolding(P1,Altitude,Speed,Duration) @@ -19696,12 +20285,68 @@ DCSTask.params={} DCSTask.params.task=Task if self.ATCswitch then local userflagname=string.format("%s#%03d",self.alias,self.SpawnIndex+1) -DCSTask.params.stopCondition={userFlag=userflagname,userFlagValue=1,duration=1800} +local maxholdingduration=60*120 +DCSTask.params.stopCondition={userFlag=userflagname,userFlagValue=1,duration=maxholdingduration} else DCSTask.params.stopCondition={duration=Duration} end return DCSTask end +function RAT._WaypointFunction(group,rat,wp) +local Tnow=timer.getTime() +local sdx=rat:GetSpawnIndexFromGroup(group) +local departure=rat.ratcraft[sdx].departure:GetName() +local destination=rat.ratcraft[sdx].destination:GetName() +local landing=rat.ratcraft[sdx].landing +local text +text=string.format("Flight %s passing waypoint #%d %s.",group:GetName(),wp,rat.waypointdescriptions[wp]) +env.info(RAT.id..text) +local status=rat.waypointstatus[wp] +rat:_SetStatus(group,status) +if wp==rat.wp_holding then +text=string.format("Flight %s to %s ATC: Holding and awaiting landing clearance.",group:GetName(),destination) +MESSAGE:New(text,10):ToAllIf(rat.reportstatus) +if rat.ATCswitch then +MENU_MISSION_COMMAND:New("Clear for landing",rat.Menu[rat.SubMenuName].groups[sdx],rat.ClearForLanding,rat,group:GetName()) +rat:_ATCRegisterFlight(group:GetName(),Tnow) +end +end +if wp==rat.wp_final then +text=string.format("Flight %s arrived at final destination %s.",group:GetName(),destination) +MESSAGE:New(text,10):ToAllIf(rat.reportstatus) +env.info(RAT.id..text) +if landing==RAT.wp.air then +text=string.format("Activating despawn switch for flight %s! Group will be detroyed soon.",group:GetName()) +MESSAGE:New(text,30):ToAllIf(rat.debug) +env.info(RAT.id..text) +rat.ratcraft[sdx].despawnme=true +end +end +end +function RAT:_TaskFunction(FunctionString,...) +self:F2({FunctionString,arg}) +local DCSTask +local ArgumentKey +local templatename=self.templategroup:GetName() +local groupname=self:_AnticipatedGroupName() +local DCSScript={} +DCSScript[#DCSScript+1]="local MissionControllable = GROUP:FindByName(\""..groupname.."\") " +DCSScript[#DCSScript+1]="local RATtemplateControllable = GROUP:FindByName(\""..templatename.."\") " +if arg and arg.n>0 then +ArgumentKey='_'..tostring(arg):match("table: (.*)") +self.templategroup:SetState(self.templategroup,ArgumentKey,arg) +DCSScript[#DCSScript+1]="local Arguments = RATtemplateControllable:GetState(RATtemplateControllable, '"..ArgumentKey.."' ) " +DCSScript[#DCSScript+1]=FunctionString.."( MissionControllable, unpack( Arguments ) )" +else +DCSScript[#DCSScript+1]=FunctionString.."( MissionControllable )" +end +DCSTask=self.templategroup:TaskWrappedAction(self.templategroup:CommandDoScript(table.concat(DCSScript))) +return DCSTask +end +function RAT:_AnticipatedGroupName(index) +local index=index or self.SpawnIndex+1 +return string.format("%s#%03d",self.alias,index) +end function RAT:_FLmax(alpha,beta,d,phi,h0) local gamma=math.rad(180)-alpha-beta local a=d*math.sin(alpha)/math.sin(gamma) @@ -19717,10 +20362,25 @@ env.info(RAT.id..text) end return h3+h0 end -function RAT:_MinDistance(alpha,beta,h) -local d1=h/math.tan(alpha) -local d2=h/math.tan(beta) -return d1+d2 +function RAT:_MinDistance(alpha,beta,ha,hb) +local d1=ha/math.tan(alpha) +local d2=hb/math.tan(beta) +return d1,d2,d1+d2 +end +function RAT:_AddFriendlyAirports(ports) +for _,airport in pairs(self.airports)do +if not self:_NameInList(ports,airport:GetName())then +table.insert(ports,airport:GetName()) +end +end +end +function RAT:_NameInList(liste,name) +for _,item in pairs(liste)do +if item==name then +return true +end +end +return false end function RAT:_AirportExists(name) for _,airport in pairs(self.airports_map)do @@ -19730,6 +20390,13 @@ end end return false end +function RAT:_ZoneExists(name) +local z=trigger.misc.getZone(name) +if z then +return true +end +return false +end function RAT:_SetROE(group,roe) env.info(RAT.id.."Setting ROE to "..roe.." for group "..group:GetName()) if self.roe==RAT.ROE.returnfire then @@ -19783,6 +20450,15 @@ angle=360+angle end return angle end +function RAT:_Heading(course) +local h +if course<=180 then +h=math.rad(course) +else +h=-math.rad(360-course) +end +return h +end function RAT:_Randomize(value,fac,lower,upper) local min if lower then @@ -19819,23 +20495,24 @@ end end return r end -function RAT:_PlaceMarkers(waypoints) -self:_SetMarker("Takeoff",waypoints[1]) -self:_SetMarker("Climb",waypoints[2]) -self:_SetMarker("Begin of Cruise",waypoints[3]) -self:_SetMarker("End of Cruise",waypoints[4]) -self:_SetMarker("Descent",waypoints[5]) -self:_SetMarker("Holding Point",waypoints[6]) -self:_SetMarker("Destination",waypoints[7]) +function RAT:_PlaceMarkers(waypoints,index) +for i=1,#waypoints do +self:_SetMarker(self.waypointdescriptions[i],waypoints[i],index) +if self.debug then +local text=string.format("Marker at waypoint #%d: %s for flight #%d",i,self.waypointdescriptions[i],index) +env.info(RAT.id..text) end -function RAT:_SetMarker(text,wp) +end +end +function RAT:_SetMarker(text,wp,index) RAT.markerid=RAT.markerid+1 self.markerids[#self.markerids+1]=RAT.markerid if self.debug then env.info(RAT.id..self.SpawnTemplatePrefix..": placing marker with ID "..RAT.markerid..": "..text) end local vec={x=wp.x,y=wp.alt,z=wp.y} -local text1=string.format("%s:\n%s",self.alias,text) +local flight=self:GetGroupFromIndex(index):GetName() +local text1=string.format("%s:\n%s",flight,text) trigger.action.markToAll(RAT.markerid,text1,vec) end function RAT:_DeleteMarkers() @@ -19846,13 +20523,17 @@ for k,v in ipairs(self.markerids)do self.markerids[k]=nil end end -function RAT:_ModifySpawnTemplate(waypoints) +function RAT:_ModifySpawnTemplate(waypoints,livery) local PointVec3={x=waypoints[1].x,y=waypoints[1].alt,z=waypoints[1].y} -local heading=self:_Course(waypoints[1],waypoints[2]) +local course=self:_Course(waypoints[1],waypoints[2]) +local heading=self:_Heading(course) if self:_GetSpawnIndex(self.SpawnIndex+1)then local SpawnTemplate=self.SpawnGroups[self.SpawnIndex].SpawnTemplate if SpawnTemplate then self:T(SpawnTemplate) +if self.uncontrolled then +SpawnTemplate.uncontrolled=true +end for UnitID=1,#SpawnTemplate.units do self:T('Before Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) local UnitTemplate=SpawnTemplate.units[UnitID] @@ -19865,9 +20546,13 @@ local TY=PointVec3.z+(SY-BY) SpawnTemplate.units[UnitID].x=TX SpawnTemplate.units[UnitID].y=TY SpawnTemplate.units[UnitID].alt=PointVec3.y -SpawnTemplate.units[UnitID].heading=math.rad(heading) -if self.livery then -SpawnTemplate.units[UnitID].livery_id=self.livery[math.random(#self.livery)] +SpawnTemplate.units[UnitID].heading=heading +SpawnTemplate.units[UnitID].psi=-heading +if livery then +SpawnTemplate.units[UnitID].livery_id=livery +end +if self.actype then +SpawnTemplate.units[UnitID]["type"]=self.actype end SpawnTemplate.units[UnitID]["skill"]=self.skill SpawnTemplate.units[UnitID]["onboard_num"]=self.SpawnIndex @@ -19876,7 +20561,7 @@ if self.country then SpawnTemplate.CountryID=self.country end UnitTemplate.parking=nil -UnitTemplate.parking_id=nil +UnitTemplate.parking_id=self.parking_id UnitTemplate.alt=PointVec3.y self:T('After Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) end @@ -19885,7 +20570,15 @@ SpawnTemplate.route.points[i]=wp end SpawnTemplate.x=PointVec3.x SpawnTemplate.y=PointVec3.z -self.SpawnGroups[self.SpawnIndex].SpawnTemplate=SpawnTemplate +if self.radio then +SpawnTemplate.communication=self.radio +end +if self.frequency then +SpawnTemplate.frequency=self.frequency +end +if self.modulation then +SpawnTemplate.modulation=self.modulation +end self:T(SpawnTemplate) end end @@ -19893,14 +20586,18 @@ end function RAT:_ATCInit(airports_map) if not RAT.ATC.init then env.info(RAT.id.."Starting RAT ATC.") +env.info(RAT.id.."Simultanious = "..RAT.ATC.Nclearance) +env.info(RAT.id.."Delay = "..RAT.ATC.delay) RAT.ATC.init=true for _,ap in pairs(airports_map)do local name=ap:GetName() RAT.ATC.airport[name]={} RAT.ATC.airport[name].queue={} RAT.ATC.airport[name].busy=false -RAT.ATC.airport[name].onfinal=nil +RAT.ATC.airport[name].onfinal={} +RAT.ATC.airport[name].Nonfinal=0 RAT.ATC.airport[name].traffic=0 +RAT.ATC.airport[name].Tlastclearance=nil end SCHEDULER:New(nil,RAT._ATCCheck,{self},5,15) SCHEDULER:New(nil,RAT._ATCStatus,{self},5,60) @@ -19908,7 +20605,7 @@ RAT.ATC.T0=timer.getTime() end end function RAT:_ATCAddFlight(name,dest) -env.info(string.format("%s%s ATC: Adding flight %s with destination %s.",RAT.id,dest,name,dest)) +env.info(string.format("%sATC %s: Adding flight %s with destination %s.",RAT.id,dest,name,dest)) RAT.ATC.flight[name]={} RAT.ATC.flight[name].destination=dest RAT.ATC.flight[name].Tarrive=-1 @@ -19923,6 +20620,7 @@ end end end function RAT:_ATCRegisterFlight(name,time) +env.info(RAT.id.."Flight "..name.." registered at ATC for landing clearance.") RAT.ATC.flight[name].Tarrive=time RAT.ATC.flight[name].holding=0 end @@ -19932,20 +20630,18 @@ for name,_ in pairs(RAT.ATC.flight)do local hold=RAT.ATC.flight[name].holding local dest=RAT.ATC.flight[name].destination if hold>=0 then -local busy="Runway is currently clear" -if RAT.ATC.airport[dest].busy then -if RAT.ATC.airport[dest].onfinal then -busy="Runway is occupied by "..RAT.ATC.airport[dest].onfinal +local busy="Runway state is unknown" +if RAT.ATC.airport[dest].Nonfinal>0 then +busy="Runway is occupied by "..RAT.ATC.airport[dest].Nonfinal else -busy="Runway is occupied" +busy="Runway is currently clear" end -end -env.info(string.format("%s%s ATC: Flight %s is holding for %i:%02d. %s.",RAT.id,dest,name,hold/60,hold%60,busy)) +local text=string.format("ATC %s: Flight %s is holding for %i:%02d. %s.",dest,name,hold/60,hold%60,busy) +env.info(RAT.id..text) elseif hold==RAT.ATC.onfinal then local Tfinal=Tnow-RAT.ATC.flight[name].Tonfinal -env.info(string.format("%s%s ATC: Flight %s was cleared for landing. Waiting %i:%02d for landing event.",RAT.id,dest,name,Tfinal/60,Tfinal%60)) -if Tfinal>300 then -end +local text=string.format("ATC %s: Flight %s is on final. Waiting %i:%02d for landing event.",dest,name,Tfinal/60,Tfinal%60) +env.info(RAT.id..text) elseif hold==RAT.ATC.unregistered then else env.error(RAT.id.."Unknown holding time in RAT:_ATCStatus().") @@ -19956,33 +20652,40 @@ function RAT:_ATCCheck() RAT:_ATCQueue() local Tnow=timer.getTime() for name,_ in pairs(RAT.ATC.airport)do -local qw={} for qID,flight in ipairs(RAT.ATC.airport[name].queue)do local nqueue=#RAT.ATC.airport[name].queue -if RAT.ATC.airport[name].busy then -RAT.ATC.flight[flight].holding=Tnow-RAT.ATC.flight[flight].Tarrive -local text=string.format("%s ATC: Flight %s runway is busy. You are #%d of %d in landing queue. Your holding time is %i:%02d.",name,flight,qID,nqueue,RAT.ATC.flight[flight].holding/60,RAT.ATC.flight[flight].holding%60) -env.info(text) +local landing1 +if RAT.ATC.airport[name].Tlastclearance then +landing1=(Tnow-RAT.ATC.airport[name].Tlastclearance>RAT.ATC.delay)and RAT.ATC.airport[name].Nonfinal=0 then +RAT.ATC.flight[name].holding=Tnow-RAT.ATC.flight[name].Tarrive +end local hold=RAT.ATC.flight[name].holding local dest=RAT.ATC.flight[name].destination if hold>=0 and airport==dest then