diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index a0cebabb4..314b6460c 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -1505,15 +1505,24 @@ do -- COORDINATE --- Creates an explosion at the point of a certain intensity. -- @param #COORDINATE self - -- @param #number ExplosionIntensity Intensity of the explosion in kg TNT. - function COORDINATE:Explosion( ExplosionIntensity ) + -- @param #number ExplosionIntensity Intensity of the explosion in kg TNT. Default 100 kg. + -- @param #number Delay Delay before explosion in seconds. + -- @return #COORDINATE self + function COORDINATE:Explosion( ExplosionIntensity, Delay ) self:F2( { ExplosionIntensity } ) - trigger.action.explosion( self:GetVec3(), ExplosionIntensity ) + ExplosionIntensity=ExplosionIntensity or 100 + if Delay and Delay>0 then + SCHEDULER:New(nil, self.Explosion, {self,ExplosionIntensity}, Delay) + else + trigger.action.explosion( self:GetVec3(), ExplosionIntensity ) + end + return self end --- Creates an illumination bomb at the point. -- @param #COORDINATE self - -- @param #number power + -- @param #number power Power of illumination bomb in Candela. + -- @return #COORDINATE self function COORDINATE:IlluminationBomb(power) self:F2() trigger.action.illuminationBomb( self:GetVec3(), power ) diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index 65c591676..c3f9ee12e 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -264,6 +264,14 @@ RANGE.Defaults={ foulline=610, } +--- Bomb target. self.bombingTargets, {name=name, target=unit, goodhitrange=goodhitrange, move=randommove, speed=speed} +-- @type RANGE.BombTarget +-- @field #string name Name of unit? +-- @field Wrapper.Unit#UNIT target Target unit. +-- @field #number goodhitrange Range in meters for a good hit. +-- @field #boolean move If true, unit move randomly. +-- @field #number speed Speed of unit. + --- Global list of all defined range names. -- @field #table Names RANGE.Names={} @@ -1177,7 +1185,7 @@ function RANGE:OnEventShot(EventData) -- Weapon data. local _weapon = EventData.Weapon:getTypeName() -- should be the same as Event.WeaponTypeName - local _weaponStrArray = self:_split(_weapon,"%.") + local _weaponStrArray = UTILS.Split(_weapon,"%.") local _weaponName = _weaponStrArray[#_weaponStrArray] -- Debug info. diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 3ce11bb07..3537c42d5 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -3675,6 +3675,8 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay, local SpeedMax=group:GetSpeedMax() local RangeMin=group:GetRange() local smax,sx,sy,sz=_GetObjectSize(Descriptors) + + --self:E(Descriptors) -- Get weight and cargo bay size in kg. local weight=0 @@ -6379,6 +6381,7 @@ function WAREHOUSE:_CheckRequestValid(request) if inwater then self:E("ERROR: Incorrect request. Ground asset requested but at least one spawn zone is in water!") + --valid=false valid=false end diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index 5fe985d33..1aa0b1f6f 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -226,6 +226,7 @@ -- @field #boolean trapsheet If true, players can save their trap sheets. -- @field #string trappath Path where to save the trap sheets. -- @field #string trapprefix File prefix for trap sheet files. +-- @field #number initialmaxalt Max altitude in meters to register in the inital zone. -- @extends Core.Fsm#FSM --- Be the boss! @@ -267,7 +268,9 @@ -- their current stack to the next lower stack. -- -- The flight that transitions form the holding pattern to the landing approach, it should leave the Marshal stack at the 3 position and make a left hand turn to the *Initial* --- position, which is 3 NM astern of the boat. +-- position, which is 3 NM astern of the boat. Note that you need to be below 1300 feet to be registered in the initial zone. +-- The altitude can be set via the function @{AIRBOSS.SetInitialMaxAlt}(*altitude*) function. +-- As described belwo, the initial zone can be smoked or flared via the AIRBOSS F10 Help radio menu. -- -- ### Landing Pattern -- @@ -1193,7 +1196,7 @@ AIRBOSS = { marshalradius = nil, airbossnice = nil, staticweather = nil, - windowcount = 0, + windowcount = 0, LSOdT = nil, senderac = nil, radiorelayLSO = nil, @@ -1221,6 +1224,7 @@ AIRBOSS = { trapsheet = nil, trappath = nil, trapprefix = nil, + initialmaxalt = nil, } --- Aircraft types capable of landing on carrier (human+AI). @@ -1667,7 +1671,7 @@ AIRBOSS.MenuF10Root=nil --- Airboss class version. -- @field #string version -AIRBOSS.version="0.9.9.7" +AIRBOSS.version="0.9.9.8" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -1850,7 +1854,10 @@ function AIRBOSS:New(carriername, alias) self:SetHoldingOffsetAngle() -- Set Marshal stack radius. Default 2.75 NM, which gives a diameter of 5.5 NM. - self:SetMarshalRadius() + self:SetMarshalRadius() + + -- Set max alt at initial. Default 1300 ft. + self:SetInitialMaxAlt() -- Default player skill EASY. self:SetDefaultPlayerSkill(AIRBOSS.Difficulty.EASY) @@ -2531,6 +2538,15 @@ function AIRBOSS:SetRefuelAI(lowfuelthreshold) return self end +--- Set max alitude to register flights in the initial zone. Aircraft above this altitude will not be registerered. +-- @param #AIRBOSS self +-- @param #number altitude Max alitude in feet. Default 1300 ft. +-- @return #AIRBOSS self +function AIRBOSS:SetInitialMaxAlt(altitude) + self.initialmaxalt=UTILS.FeetToMeters(altitude or 1300) + return self +end + --- Set folder where the airboss sound files are located **within you mission (miz) file**. -- The default path is "l10n/DEFAULT/" but sound files simply copied there will be removed by DCS the next time you save the mission. @@ -6047,7 +6063,13 @@ function AIRBOSS:_MarshalAI(flight, nstack, respawn) -- Nil check. if flight==nil or flight.group==nil then - self:E(self.lid.."ERROR: flight or flight.group is nil") + self:E(self.lid.."ERROR: flight or flight.group is nil.") + return + end + + -- Nil check. + if flight.group:GetCoordinate()==nil then + self:E(self.lid.."ERROR: cannot get coordinate of flight group.") return end @@ -8855,8 +8877,11 @@ function AIRBOSS:_Initial(playerData) -- Relative heading to carrier direction. local relheading=self:_GetRelativeHeading(playerData.unit, false) + -- Alitude of player in feet. + local altitude=playerData.unit:GetAltitude() + -- Check if player is in zone and flying roughly in the right direction. - if inzone and math.abs(relheading)<60 then + if inzone and math.abs(relheading)<60 and altitude<=self.initialmaxalt then -- Send message for normal and easy difficulty. if playerData.showhints then diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index a068d6a72..7cfb56abb 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -830,6 +830,7 @@ function CONTROLLABLE:TaskEPLRS(SwitchOnOff, idx) end + -- TASKS FOR AIR CONTROLLABLES --- (AIR) Attack a Controllable. -- @param #CONTROLLABLE self @@ -890,17 +891,16 @@ end --- (AIR) Attack the Unit. -- @param #CONTROLLABLE self --- @param Wrapper.Unit#UNIT AttackUnit The UNIT. --- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found. --- @param DCS#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. --- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. --- @param DCS#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- @param #number Altitude (optional) The altitude from where to attack. --- @param #boolean Visible (optional) not a clue. --- @param #number WeaponType (optional) The WeaponType. +-- @param Wrapper.Unit#UNIT AttackUnit The UNIT to be attacked +-- @param #boolean GroupAttack (Optional) If true, all units in the group will attack the Unit when found. Default false. +-- @param DCS#AI.Task.WeaponExpend WeaponExpend (Optional) Determines how many weapons will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. +-- @param #number AttackQty (Optional) Limits maximal quantity of attack. The aicraft/controllable will not make more attacks than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. +-- @param DCS#Azimuth Direction (Optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. +-- @param #number Altitude (Optional) The (minimum) altitude in meters from where to attack. Default 2000 m. +-- @param #number WeaponType (optional) The WeaponType. See [DCS Enumerator Weapon Type](https://wiki.hoggitworld.com/view/DCS_enum_weapon_flag) on Hoggit. -- @return DCS#Task The DCS task structure. -function CONTROLLABLE:TaskAttackUnit( AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, WeaponType ) - self:F2( { self.ControllableName, AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, WeaponType } ) +function CONTROLLABLE:TaskAttackUnit(AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType) + self:F2({self.ControllableName, AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType}) local DCSTask DCSTask = { @@ -908,12 +908,11 @@ function CONTROLLABLE:TaskAttackUnit( AttackUnit, GroupAttack, WeaponExpend, Att params = { unitId = AttackUnit:GetID(), groupAttack = GroupAttack or false, - visible = Visible or false, expend = WeaponExpend or "Auto", directionEnabled = Direction and true or false, - direction = Direction, + direction = math.rad(Direction or 0), altitudeEnabled = Altitude and true or false, - altitude = Altitude or 30, + altitude = Altitude or 2000, attackQtyLimit = AttackQty and true or false, attackQty = AttackQty, weaponType = WeaponType @@ -1798,6 +1797,100 @@ end +--[[ +--- Used in conjunction with the embarking task for a transport helicopter group. The Ground units will move to the specified location and wait to be picked up by a helicopter. +-- The helicopter will then fly them to their dropoff point defined by another task for the ground forces; DisembarkFromTransport task. +-- The controllable has to be an infantry group! +-- @param #CONTROLLABLE self +-- @param Core.Point#COORDINATE Coordinate Coordinates where AI is expecting to be picked up. +-- @param #number Radius Radius in meters. +-- @return #table Embark to transport task. +function CONTROLLABLE:TaskEmbarkToTransport(Coordinate, Radius) + + local EmbarkToTransport = { + id="EmbarkToTransport", + params={ + x=Coordinate.x, + y=Coordinate.z, + zoneRadius=Radius, + --selectedType="UH-1H", + } + } + + self:E(EmbarkToTransport) + return EmbarkToTransport +end + +--- Used in conjunction with the EmbarkToTransport task for a ground infantry group, the controlled helicopter flight will land at the specified coordinates, +-- pick up boarding troops and transport them to that groups DisembarkFromTransport task. +-- The CONTROLLABLE has to be a helicopter group! +-- @param #CONTROLLABLE self +-- @param Core.Set#SET_GROUP GroupSet Set of groups to be embarked by the controllable. +-- @param Core.Point#COORDINATE Coordinate Coordinate of embarking. +-- @param #number Duration Duration of embarking in seconds. +-- @return #table Embarking task. +function CONTROLLABLE:TaskEmbarking(GroupSet, Coordinate, Duration) + + -- Create table of group IDs. + local gids={} + for _,_group in pairs(GroupSet:GetAliveSet()) do + local group=_group --Wrapper.Group#GROUP + table.insert(gids, group:GetID()) + end + + -- Group ID of controllable. + local id=self:GetID() + + -- Distribution + local distribution={} + distribution[id]=gids + + local durationFlag=false + if Duration then + durationFlag=true + else + Duration=300 + end + + local DCStask={ + id="Embarking", + params={ + selectedTransport=self:GetID(), + distributionFlag=true, + distribution=distribution, + groupsForEmbarking=gids, + durationFlag=durationFlag, + distribution=distribution, + duration=Duration, + x=Coordinate.x, + y=Coordinate.z, + } + } + + self:E(DCStask) + + return DCStask +end + +--- Specifies the location an infantry group that is being transported by helicopters will be unloaded at. Used in conjunction with the EmbarkToTransport task. +-- The CONTROLLABLE has to be an infantry group! +-- @param #CONTROLLABLE self +-- @param Core.Point#COORDINATE Coordinate Coordinates where AI is expecting to be picked up. +-- @param #number Radius Radius in meters. +-- @return #table Embark to transport task. +function CONTROLLABLE:TaskDisembarkFromTransport(Coordinate, Radius) + + local DisembarkFromTransport={ + id="DisembarkFromTransport", + params = { + x=Coordinate.x, + y=Coordinate.y, + zoneRadius=Radius, + }} + + return DisembarkFromTransport +end +]] --- (AIR) Move the controllable to a Vec2 Point, wait for a defined duration and embark a controllable. -- @param #CONTROLLABLE self @@ -1825,8 +1918,7 @@ function CONTROLLABLE:TaskEmbarking( Point, Duration, EmbarkingControllable ) end --- (GROUND) Embark to a Transport landed at a location. - ---- Move to a defined Vec2 Point, and embark to a controllable when arrived within a defined Radius. +-- Move to a defined Vec2 Point, and embark to a controllable when arrived within a defined Radius. -- @param #CONTROLLABLE self -- @param DCS#Vec2 Point The point where to wait. -- @param #number Radius The radius of the embarking zone around the Point. @@ -1846,6 +1938,7 @@ function CONTROLLABLE:TaskEmbarkToTransport( Point, Radius ) return DCSTask end + --- This creates a Task element, with an action to call a function as part of a Wrapped Task. -- This Task can then be embedded at a Waypoint by calling the method @{#CONTROLLABLE.SetTaskWaypoint}. -- @param #CONTROLLABLE self diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 25b6a9f91..4b6e77bea 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -653,6 +653,49 @@ function GROUP:GetSize() return nil end +--- Count number of alive units in the group. +-- @param #GROUP self +-- @return #number Number of alive units +function GROUP:CountAliveUnits() + self:F3( { self.GroupName } ) + local DCSGroup = self:GetDCSObject() + + if DCSGroup then + local units=self:GetUnits() + local n=0 + for _,_unit in pairs(units) do + local unit=_unit --Wrapper.Unit#UNIT + if unit and unit:IsAlive() then + n=n+1 + end + end + return n + end + + return nil +end + +--- Get the first unit of the group which is alive. +-- @param #GROUP self +-- @return Wrapper.Unit#UNIT First unit alive. +function GROUP:GetFirstUnitAlive() + self:F3({self.GroupName}) + local DCSGroup = self:GetDCSObject() + + if DCSGroup then + local units=self:GetUnits() + for _,_unit in pairs(units) do + local unit=_unit --Wrapper.Unit#UNIT + if unit and unit:IsAlive() then + return unit + end + end + end + + return nil +end + + --- Returns the average velocity Vec3 vector. -- @param Wrapper.Group#GROUP self @@ -1463,6 +1506,16 @@ function GROUP:InitRandomizePositionRadius( OuterRadius, InnerRadius ) return self end +--- Set respawn coordinate. +-- @param #GROUP self +-- @param Core.Point#COORDINATE coordinate Coordinate where the group should be respawned. +-- @return #GROUP self +function GROUP:InitCoordinate(coordinate) + self:F({coordinate=coordinate}) + self.InitCoord=coordinate + return self +end + --- Sets the radio comms on or off when the group is respawned. Same as checking/unchecking the COMM box in the mission editor. -- @param #GROUP self -- @param #boolean switch If true (or nil), enables the radio comms. If false, disables the radio for the spawned group. @@ -1603,6 +1656,11 @@ function GROUP:Respawn( Template, Reset ) end end + -- Coordinate where the group should be respawned. + if self.InitCoord then + GroupUnitVec3=self.InitCoord:GetVec3() + end + -- Altitude Template.units[UnitID].alt = self.InitRespawnHeight and self.InitRespawnHeight or GroupUnitVec3.y @@ -1647,6 +1705,11 @@ function GROUP:Respawn( Template, Reset ) end end + -- Coordinate where the group should be respawned. + if self.InitCoord then + GroupUnitVec3=self.InitCoord:GetVec3() + end + -- Set altitude. Template.units[UnitID].alt = self.InitRespawnHeight and self.InitRespawnHeight or GroupUnitVec3.y diff --git a/Moose Development/Moose/Wrapper/Static.lua b/Moose Development/Moose/Wrapper/Static.lua index 68c224922..1f2568a73 100644 --- a/Moose Development/Moose/Wrapper/Static.lua +++ b/Moose Development/Moose/Wrapper/Static.lua @@ -142,7 +142,9 @@ function STATIC:Destroy( GenerateEvent ) end - +--- Get DCS object of static of static. +-- @param #STATIC self +-- @return DCS static object function STATIC:GetDCSObject() local DCSStatic = StaticObject.getByName( self.StaticName ) @@ -172,45 +174,72 @@ function STATIC:GetUnits() end - - +--- Get threat level of static. +-- @param #STATIC self +-- @return #number Threat level 1. +-- @return #string "Static" function STATIC:GetThreatLevel() - return 1, "Static" end ---- Respawn the @{Wrapper.Unit} using a (tweaked) template of the parent Group. +--- Spawn the @{Wrapper.Static} at a specific coordinate and heading. -- @param #STATIC self -- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static. --- @param #number Heading The heading of the unit respawn. -function STATIC:SpawnAt( Coordinate, Heading ) +-- @param #number Heading The heading of the static respawn in degrees. Default is 0 deg. +-- @param #number Delay Delay in seconds before the static is spawned. +function STATIC:SpawnAt( Coordinate, Heading, Delay ) - local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName ) + Heading=Heading or 0 + + if Delay and Delay>0 then + SCHEDULER:New(nil, self.SpawnAt, {self, Coordinate, Heading}, Delay) + else + + local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName ) - SpawnStatic:SpawnFromPointVec2( Coordinate, Heading, self.StaticName ) + SpawnStatic:SpawnFromPointVec2( Coordinate, Heading, self.StaticName ) + + end end --- Respawn the @{Wrapper.Unit} at the same location with the same properties. -- This is useful to respawn a cargo after it has been destroyed. -- @param #STATIC self --- @param DCS#country.id countryid The country ID used for spawning the new static. -function STATIC:ReSpawn(countryid) +-- @param DCS#country.id countryid The country ID used for spawning the new static. Default is same as currently. +-- @param #number Delay Delay in seconds before static is respawned. +function STATIC:ReSpawn(countryid, Delay) - local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName, countryid ) + countryid=countryid or self:GetCountry() + + if Delay and Delay>0 then + SCHEDULER:New(nil, self.ReSpawn, {self, countryid}, Delay) + else + + local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName, countryid ) - SpawnStatic:ReSpawn() + SpawnStatic:ReSpawn() + + end end --- Respawn the @{Wrapper.Unit} at a defined Coordinate with an optional heading. -- @param #STATIC self -- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static. --- @param #number Heading The heading of the unit respawn. -function STATIC:ReSpawnAt( Coordinate, Heading ) +-- @param #number Heading The heading of the static respawn in degrees. Default is 0 deg. +-- @param #number Delay Delay in seconds before static is respawned. +function STATIC:ReSpawnAt( Coordinate, Heading, Delay ) - local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName ) + Heading=Heading or 0 + + if Delay and Delay>0 then + SCHEDULER:New(nil, self.ReSpawnAt, {self, Coordinate, Heading}, Delay) + else - SpawnStatic:ReSpawnAt( Coordinate, Heading ) + local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName ) + + SpawnStatic:ReSpawnAt( Coordinate, Heading ) + end end diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 100f27206..1ef55065d 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -817,16 +817,22 @@ function UNIT:Explode(power, delay) -- Default. power=power or 100 - -- Check if delay or not. - if delay and delay>0 then - -- Delayed call. - SCHEDULER:New(nil, self.Explode, {self, power}, delay) - else - -- Create an explotion at the coordinate of the unit. - self:GetCoordinate():Explosion(power) + local DCSUnit = self:GetDCSObject() + if DCSUnit then + + -- Check if delay or not. + if delay and delay>0 then + -- Delayed call. + SCHEDULER:New(nil, self.Explode, {self, power}, delay) + else + -- Create an explotion at the coordinate of the unit. + self:GetCoordinate():Explosion(power) + end + + return self end - - return self + + return nil end -- Is functions