diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Ship.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Ship.lua index ed4984381..2de56b9ec 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Ship.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Ship.lua @@ -1,38 +1,194 @@ --- **AI** -- (2.5.1) - Models the intelligent transportation of infantry and other cargo using Ships +-- +-- ## Features: +-- +-- * Transport cargo to various deploy zones using naval vehicles. +-- * Various @{Cargo.Cargo#CARGO} types can be transported, including infantry, vehicles, and crates. +-- * Define a deploy zone of various types to determine the destination of the cargo. +-- * Ships will follow shipping lanes as defined in the Mission Editor. +-- * Multiple ships can transport multiple cargo as a single group. +-- +-- === +-- +-- ## Test Missions: +-- +-- NEED TO DO +-- +-- === +-- +-- ### Author: **acrojason** (derived from AI_Cargo_Dispatcher_APC by FlightControl) +-- +-- === +-- +-- @module AI.AI_Cargo_Dispatcher_Ship +-- @image AI_Cargo_Dispatching_For_Ship.JPG + +--- @type AI_CARGO_DISPATCHER_SHIP +-- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER + + +--- A dynamic cargo transportation capability for AI groups. +-- +-- Naval vessels can be mobilized to semi-intelligently transport cargo within the simulation. +-- +-- The AI_CARGO_DISPATCHER_SHIP module is derived from the AI_CARGO_DISPATCHER module. +-- +-- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_SHIP class, it is recommended that you first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher} module!!! +-- +-- This will be particularly helpful in order to determine how to **Tailor the different cargo handling events**. +-- +-- The AI_CARGO_DISPATCHER_SHIP class uses the @{Cargo.Cargo} capabilities within the MOOSE framwork. +-- Also ensure that you fully understand how to declare and setup Cargo objects within the MOOSE framework before using this class. +-- CARGO derived objects must generally be declared within the mission to make the AI_CARGO_DISPATCHER_SHIP object recognize the cargo. +-- +-- +-- # 1) AI_CARGO_DISPATCHER_SHIP constructor. +-- +-- * @{AI_CARGO_DISPATCHER_SHIP.New}(): Creates a new AI_CARGO_DISPATCHER_SHIP object. +-- +-- --- +-- +-- # 2) AI_CARGO_DISPATCHER_SHIP is a Finite State Machine. +-- +-- This section must be read as follows... Each of the rows indicate a state transition, triggered through an event, and with an ending state of the event was executed. +-- The first column is the **From** state, the second column the **Event**, and the third column the **To** state. +-- +-- So, each of the rows have the following structure. +-- +-- * **From** => **Event** => **To** +-- +-- Important to know is that an event can only be executed if the **current state** is the **From** state. +-- This, when an **Event** that is being triggered has a **From** state that is equal to the **Current** state of the state machine, the event will be executed, +-- and the resulting state will be the **To** state. +-- +-- These are the different possible state transitions of this state machine implementation: +-- +-- * Idle => Start => Monitoring +-- * Monitoring => Monitor => Monitoring +-- * Monitoring => Stop => Idle +-- +-- * Monitoring => Pickup => Monitoring +-- * Monitoring => Load => Monitoring +-- * Monitoring => Loading => Monitoring +-- * Monitoring => Loaded => Monitoring +-- * Monitoring => PickedUp => Monitoring +-- * Monitoring => Deploy => Monitoring +-- * Monitoring => Unload => Monitoring +-- * Monitoring => Unloaded => Monitoring +-- * Monitoring => Deployed => Monitoring +-- * Monitoring => Home => Monitoring +-- +-- +-- ## 2.1) AI_CARGO_DISPATCHER States. +-- +-- * **Monitoring**: The process is dispatching. +-- * **Idle**: The process is idle. +-- +-- ## 2.2) AI_CARGO_DISPATCHER Events. +-- +-- * **Start**: Start the transport process. +-- * **Stop**: Stop the transport process. +-- * **Monitor**: Monitor and take action. +-- +-- * **Pickup**: Pickup cargo. +-- * **Load**: Load the cargo. +-- * **Loading**: The dispatcher is coordinating the loading of a cargo. +-- * **Loaded**: Flag that the cargo is loaded. +-- * **PickedUp**: The dispatcher has loaded all requested cargo into the CarrierGroup. +-- * **Deploy**: Deploy cargo to a location. +-- * **Unload**: Unload the cargo. +-- * **Unloaded**: Flag that the cargo is unloaded. +-- * **Deployed**: All cargo is unloaded from the carriers in the group. +-- * **Home**: A Carrier is going home. +-- +-- ## 2.3) Enhance your mission scripts with **Tailored** Event Handling! +-- +-- Within your mission, you can capture these events when triggered, and tailor the events with your own code! +-- Check out the @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} class at chapter 3 for details on the different event handlers that are available and how to use them. +-- +-- **There are a lot of templates available that allows you to quickly setup an event handler for a specific event type!** +-- +-- --- +-- +-- # 3) Set the pickup parameters. +-- +-- Several parameters can be set to pickup cargo: +-- +-- * @{#AI_CARGO_DISPATCHER_SHIP.SetPickupRadius}(): Sets or randomizes the pickup location for the Ship around the cargo coordinate in a radius defined an outer and optional inner radius. +-- * @{#AI_CARGO_DISPATCHER_SHIP.SetPickupSpeed}(): Set the speed or randomizes the speed in km/h to pickup the cargo. +-- +-- # 4) Set the deploy parameters. +-- +-- Several parameters can be set to deploy cargo: +-- +-- * @{#AI_CARGO_DISPATCHER_SHIP.SetDeployRadius}(): Sets or randomizes the deploy location for the Ship around the cargo coordinate in a radius defined an outer and an optional inner radius. +-- * @{#AI_CARGO_DISPATCHER_SHIP.SetDeploySpeed}(): Set the speed or randomizes the speed in km/h to deploy the cargo. +-- +-- # 5) Set the home zone when there isn't any more cargo to pickup. +-- +-- A home zone can be specified to where the Ship will move when there isn't any cargo left for pickup. +-- Use @{#AI_CARGO_DISPATCHER_SHIP.SetHomeZone}() to specify the home zone. +-- +-- If no home zone is specified, the Ship will wait near the deploy zone for a new pickup command. +-- +-- === +-- -- @field #AI_CARGO_DISPATCHER_SHIP AI_CARGO_DISPATCHER_SHIP = { ClassName = "AI_CARGO_DISPATCHER_SHIP" } - function AI_CARGO_DISPATCHER_SHIP:New( ShipSet, CargoSet, PickupZoneSet, DeployZoneSet, ShippingLane ) - - local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( ShipSet, CargoSet, PickupZoneSet, DeployZoneSet ) ) - - self:SetPickupSpeed( 60, 10 ) - self:SetDeploySpeed( 60, 10 ) - - self:SetPickupRadius( 500, 3000 ) - self:SetDeployRadius( 500, 3000 ) - - self:SetPickupHeight( 0, 0 ) - self:SetDeployHeight( 0, 0 ) - - self:SetShippingLane( ShippingLane ) - - self:SetMonitorTimeInterval( 600 ) - - return self - end - - function AI_CARGO_DISPATCHER_SHIP:SetShippingLane( ShippingLane ) - self.ShippingLane = ShippingLane - - return self - - end - - function AI_CARGO_DISPATCHER_SHIP:AICargo( Ship, CargoSet ) - - return AI_CARGO_SHIP:New( Ship, CargoSet, 0, self.ShippingLane ) - end \ No newline at end of file +--- Creates a new AI_CARGO_DISPATCHER_SHIP object. +-- @param #AI_CARGO_DISPATCHER_SHIP self +-- @param Core.Set#SET_GROUP ShipSet The set of @{Wrapper.Group#GROUP} objects of Ships that will transport the cargo +-- @param Core.Set#SET_CARGO CargoSet The set of @{Cargo.Cargo#CARGO} objects, which can be CARGO_GROUP, CARGO_CRATE, or CARGO_SLINGLOAD objects. +-- @param Core.Set#SET_ZONE PickupZoneSet The set of pickup zones which are used to determine from where the cargo can be picked up by the Ship. +-- @param Core.Set#SET_ZONE DeployZoneSet The set of deploy zones which determine where the cargo will be deployed by the Ship. +-- @param #table ShippingLane Table containing list of Shipping Lanes to be used +-- @return #AI_CARGO_DISPATCHER_SHIP +-- @usage +-- +-- -- An AI dispatcher object for a naval group, moving cargo from pickup zones to deploy zones via a predetermined Shipping Lane +-- +-- local SetCargoInfantry = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- local SetShip = SET_GROUP:New():FilterPrefixes( "Ship" ):FilterStart() +-- local SetPickupZones = SET_ZONE:New():FilterPrefixes( "Pickup" ):FilterStart() +-- local SetDeployZones = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() +-- NEED MORE THOUGHT - ShippingLane is part of Warehouse....... +-- local ShippingLane = GROUP:New():FilterPrefixes( "ShippingLane" ):FilterStart() +-- +-- AICargoDispatcherShip = AI_CARGO_DISPATCHER_SHIP:New( SetShip, SetCargoInfantry, SetPickupZones, SetDeployZones, ShippingLane ) +-- AICargoDispatcherShip:Start() +-- +function AI_CARGO_DISPATCHER_SHIP:New( ShipSet, CargoSet, PickupZoneSet, DeployZoneSet, ShippingLane ) + + local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( ShipSet, CargoSet, PickupZoneSet, DeployZoneSet ) ) + + self:SetPickupSpeed( 60, 10 ) + self:SetDeploySpeed( 60, 10 ) + + self:SetPickupRadius( 500, 6000 ) + self:SetDeployRadius( 500, 6000 ) + + self:SetPickupHeight( 0, 0 ) + self:SetDeployHeight( 0, 0 ) + + self:SetShippingLane( ShippingLane ) + + self:SetMonitorTimeInterval( 600 ) + + return self +end + +function AI_CARGO_DISPATCHER_SHIP:SetShippingLane( ShippingLane ) + self.ShippingLane = ShippingLane + + return self + +end + +function AI_CARGO_DISPATCHER_SHIP:AICargo( Ship, CargoSet ) + + return AI_CARGO_SHIP:New( Ship, CargoSet, 0, self.ShippingLane ) +end \ No newline at end of file diff --git a/Moose Development/Moose/AI/AI_Cargo_Ship.lua b/Moose Development/Moose/AI/AI_Cargo_Ship.lua index 7f3f6f5aa..e28e52aa2 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Ship.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Ship.lua @@ -1,5 +1,60 @@ --- **AI** -- (R2.5.1) - Models the intelligent transportation of infantry and other cargo. +-- +-- === +-- +-- ### Author: **acrojason** (derived from AI_Cargo_APC by FlightControl) +-- +-- === +-- +-- @module AI.AI_Cargo_Ship +-- @image AI_Cargo_Dispatching_For_Ship.JPG +--- @type AI_CARGO_SHIP +-- @extends AI.AI_Cargo#AI_CARGO + +--- Brings a dynamic cargo handling capability for an AI naval group. +-- +-- Naval ships can be utilized to transport cargo around the map following naval shipping lanes. +-- The AI_CARGO_SHIP class uses the @{Cargo.Cargo} capabilities within the MOOSE framework. +-- @{Cargo.Cargo} must be declared within the mission or warehouse to make the AI_CARGO_SHIP recognize the cargo. +-- Please consult the @{Cargo.Cargo} module for more information. +-- +-- ## Cargo loading. +-- +-- The module will automatically load cargo when the Ship is within boarding or loading radius. +-- The boarding or loading radius is specified when the cargo is created in the simulation and depends on the type of +-- cargo and the specified boarding radius. +-- +-- ## Defending the Ship when enemies are nearby +-- This is not supported for naval cargo because most tanks don't float. Protect your transports... +-- +-- ## Infantry or cargo **health**. +-- When cargo is unboarded from the Ship, the cargo is actually respawned into the battlefield. +-- As a result, the unboarding cargo is very _healthy_ every time it unboards. +-- This is due to the limitation of the DCS simulator, which is not able to specify the health of newly spawned units as a parameter. +-- However, cargo that was destroyed when unboarded and following the Ship won't be respawned again (this is likely not a thing for +-- naval cargo due to the lack of support for defending the Ship mentioned above). Destroyed is destroyed. +-- As a result, there is some additional strength that is gained when an unboarding action happens, but in terms of simulation balance +-- this has marginal impact on the overall battlefield simulation. Given the relatively short duration of DCS missions and the somewhat +-- lengthy naval transport times, most units entering the Ship as cargo will be freshly en route to an amphibious landing or transporting +-- between warehouses. +-- +-- ## Control the Ships on the map. +-- +-- Currently, naval transports can only be controlled via scripts due to their reliance upon predefined Shipping Lanes created in the Mission +-- Editor. An interesting future enhancement could leverage new pathfinding functionality for ships in the Ops module. +-- +-- ## Cargo deployment. +-- +-- Using the @{AI_CARGO_SHIP.Deploy}() method, you are able to direct the Ship towards a Deploy zone to unboard/unload the cargo at the +-- specified coordinate. The Ship will follow the Shipping Lane to ensure consistent cargo transportation within the simulation environment. +-- +-- ## Cargo pickup. +-- +-- Using the @{AI_CARGO_SHIP.Pickup}() method, you are able to direct the Ship towards a Pickup zone to board/load the cargo at the specified +-- coordinate. The Ship will follow the Shipping Lane to ensure consistent cargo transportation within the simulation environment. +-- +-- -- @field #AI_CARGO_SHIP AI_CARGO_SHIP = { ClassName = "AI_CARGO_SHIP", @@ -7,16 +62,21 @@ AI_CARGO_SHIP = { } --- Creates a new AI_CARGO_SHIP object. +-- @param #AI_CARGO_SHIP self +-- @param Wrapper.Group#GROUP Ship The carrier Ship group +-- @param Core.Set#SET_CARGO CargoSet The set of cargo to be transported +-- @param #number CombatRadius Provide the combat radius to defend the carrier by unboarding the cargo when enemies are nearby. When CombatRadius is 0, no defense will occur. +-- @param #table ShippingLane Table containing list of Shipping Lanes to be used +-- @return #AI_CARGO_SHIP function AI_CARGO_SHIP:New( Ship, CargoSet, CombatRadius, ShippingLane ) local self = BASE:Inherit( self, AI_CARGO:New( Ship, CargoSet ) ) -- #AI_CARGO_SHIP self:AddTransition( "*", "Monitor", "*" ) - self:AddTransition( "*", "Destroyed", "Destroyed" ) self:AddTransition( "*", "Home", "*" ) - self:SetCombatRadius( CombatRadius ) + self:SetCombatRadius( 0 ) -- Don't want to deploy cargo in middle of water to defend Ship, so set CombatRadius to 0 self:SetShippingLane ( ShippingLane ) self:SetCarrier( Ship ) @@ -45,14 +105,14 @@ function AI_CARGO_SHIP:SetCarrier( CargoCarrier ) AICargoTroops:Destroyed() end end -end + end -self.Zone = ZONE_UNIT:New( self.CargoCarrier:GetName() .. "-Zone", self.CargoCarrier, self.CombatRadius ) -self.Coalition = self.CargoCarrier:GetCoalition() + self.Zone = ZONE_UNIT:New( self.CargoCarrier:GetName() .. "-Zone", self.CargoCarrier, self.CombatRadius ) + self.Coalition = self.CargoCarrier:GetCoalition() -self:SetControllable( CargoCarrier ) + self:SetControllable( CargoCarrier ) -return self + return self end @@ -69,11 +129,11 @@ function AI_CARGO_SHIP:FindCarrier( Coordinate, Radius ) local NearUnit = UNIT:Find( DCSUnit ) self:F({NearUnit=NearUnit}) if not NearUnit:GetState( NearUnit, "AI_CARGO_SHIP" ) then - local Attributes = NearUnit:GetDesc() - self:F({Desc=Attributes}) - if NearUnit:HasAttributes( "Trucks" ) then - return NearUnit:GetGroup() - end + local Attributes = NearUnit:GetDesc() + self:F({Desc=Attributes}) + if NearUnit:HasAttributes( "Trucks" ) then + return NearUnit:GetGroup() + end end end @@ -100,7 +160,7 @@ end -- @param Cargo.CargoGroup#CARGO_GROUP Cargo -- @return #AI_CARGO_SHIP function AI_CARGO_SHIP:FollowToCarrier( Me, ShipUnit, CargoGroup ) - BASE:T("DEBUGGING*** AI_CARGO_SHIP:FollowToCarrier") + local InfantryGroup = CargoGroup:GetGroup() self:F( { self=self:GetClassNameAndID(), InfantryGroup = InfantryGroup:GetName() } ) @@ -147,6 +207,8 @@ function AI_CARGO_SHIP:onafterMonitor( Ship, From, Event, To ) self:F( { Ship, From, Event, To, IsTransporting = self:IsTransporting() } ) if self.CombatRadius > 0 then + -- We really shouldn't find ourselves in here for Ships since the CombatRadius should always be 0. + -- This is to avoid Unloading the Ship in the middle of the sea. if Ship and Ship:IsAlive() then if self.CarrierCoordinate then if self:IsTransporting() == true then @@ -197,8 +259,11 @@ function AI_CARGO_SHIP:onafterMonitor( Ship, From, Event, To ) end end - +--- Check if cargo ship is alive and trigger Load event +-- @param Wrapper.Group#Group Ship +-- @param #AI_CARGO_SHIP self function AI_CARGO_SHIP._Pickup( Ship, self, Coordinate, Speed, PickupZone ) + Ship:F( { "AI_CARGO_Ship._Pickup:", Ship:GetName() } ) if Ship:IsAlive() then @@ -206,9 +271,10 @@ function AI_CARGO_SHIP._Pickup( Ship, self, Coordinate, Speed, PickupZone ) end end - +--- Check if cargo ship is alive and trigger Unload event. Good time to remind people that Lua is case sensitive and Unload != UnLoad +-- @param Wrapper.Group#GROUP Ship +-- @param #AI_CARGO_SHIP self function AI_CARGO_SHIP._Deploy( Ship, self, Coordinate, DeployZone ) - Ship:F( { "AI_CARGO_Ship._Deploy:", Ship } ) if Ship:IsAlive() then @@ -216,6 +282,15 @@ function AI_CARGO_SHIP._Deploy( Ship, self, Coordinate, DeployZone ) end end +--- on after Pickup event. +-- @param AI_CARGO_SHIP Ship +-- @param From +-- @param Event +-- @param To +-- @param Core.Point#COORDINATE Coordinate of the pickup point +-- @param #number Speed Speed in km/h to sail to the pickup coordinate. Default is 50% of max speed for the unit +-- @param #number Height Altitude in meters to move to the pickup coordinate. This parameter is ignored for Ships +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil if there was no PickupZoneSet provided function AI_CARGO_SHIP:onafterPickup( Ship, From, Event, To, Coordinate, Speed, Height, PickupZone ) if Ship and Ship:IsAlive() then @@ -224,16 +299,89 @@ function AI_CARGO_SHIP:onafterPickup( Ship, From, Event, To, Coordinate, Speed, end end -function AI_CARGO_SHIP:onafterPickedUp( Ship, From, Event, To, Coordinate, Speed, Height, PickupZone ) +--- On after Deploy event. +-- @param #AI_CARGO_SHIP self +-- @param Wrapper.Group#GROUP SHIP +-- @param From +-- @param Event +-- @param To +-- @param Core.Point#COORDINATE Coordinate Coordinate of the deploy point +-- @param #number Speed Speed in km/h to sail to the deploy coordinate. Default is 50% of max speed for the unit +-- @param #number Height Altitude in meters to move to the deploy coordinate. This parameter is ignored for Ships +-- @param Core.Zone#ZONE DeployZone The zone where the cargo will be deployed. +function AI_CARGO_SHIP:onafterDeploy( Ship, From, Event, To, Coordinate, Speed, Height, DeployZone ) + if Ship and Ship:IsAlive() then + + Speed = Speed or Ship:GetSpeedMax()*0.8 + local lane = self.ShippingLane + + if lane then + local Waypoints = {} + + for i=1, #lane do + local coord = lane[i] + local Waypoint = coord:WaypointGround(_speed) + table.insert(Waypoints, Waypoint) + end + + local TaskFunction = Ship:TaskFunction( "AI_CARGO_SHIP._Deploy", self, Coordinate, DeployZone ) + local Waypoint = Waypoints[#Waypoints] + Ship:SetTaskWaypoint( Waypoint, TaskFunction ) + Ship:Route(Waypoints, 1) + self:GetParent( self, AI_CARGO_SHIP ).onafterDeploy( self, Ship, From, Event, To, Coordinate, Speed, Height, DeployZone ) + else + self:E(self.lid.."ERROR: No shipping lane defined for Naval Transport!") + end + end +end + +--- On after Unload event. +-- @param #AI_CARGO_SHIP self +-- @param Wrapper.Group#GROUP Ship +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +function AI_CARGO_SHIP:onafterUnload( Ship, From, Event, To, DeployZone, Defend ) + self:F( { Ship, From, Event, To, DeployZone, Defend = Defend } ) + + local UnboardInterval = 5 + local UnboardDelay = 5 + if Ship and Ship:IsAlive() then - Speed = Speed or Ship:GetSpeedMax()*0.8 + for _, ShipUnit in pairs( Ship:GetUnits() ) do + local ShipUnit = ShipUnit -- Wrapper.Unit#UNIT + Ship:RouteStop() + for _, Cargo in pairs( ShipUnit:GetCargo() ) do + self:F( { Cargo = Cargo:GetName(), Isloaded = Cargo:IsLoaded() } ) + if Cargo:IsLoaded() then + local unboardCoord = DeployZone:GetRandomPointVec2() + Cargo:__UnBoard( UnboardDelay, unboardCoord, 1000) + UnboardDelay = UnboardDelay + Cargo:GetCount() * UnboardInterval + self:__Unboard( UnboardDelay, Cargo, ShipUnit, DeployZone, Defend ) + if not Defend == true then + Cargo:SetDeployed( true ) + end + end + end + end + end +end +function AI_CARGO_SHIP:onafterHome( Ship, From, Event, To, Coordinate, Speed, Height, HomeZone ) + if Ship and Ship:IsAlive() then + + self.RouteHome = true + Speed = Speed or Ship:GetSpeedMax()*0.8 local lane = self.ShippingLane + if lane then local Waypoints = {} - for i=1, #lane do + -- Need to find a more generalized way to do this instead of reversing the shipping lane. + -- This only works if the Source/Dest route waypoints are numbered 1..n and not n..1 + for i=#lane, 1, -1 do local coord = lane[i] local Waypoint = coord:WaypointGround(_speed) table.insert(Waypoints, Waypoint) @@ -243,36 +391,7 @@ function AI_CARGO_SHIP:onafterPickedUp( Ship, From, Event, To, Coordinate, Speed Ship:Route(Waypoints, 1) else - BASE:T("ERROR: No shipping lane defined for Naval Transport!") - end - end -end - - -function AI_CARGO_SHIP:onafterHome( Ship, From, Event, To, Coordinate, Speed, Height, HomeZone ) - if Ship and Ship:IsAlive() then - - self.RouteHome = true - - Speed = Speed or Ship:GetSpeedMax()*0.8 - - local lane = self.ShippingLane - if lane then - - local Waypoints = {} - - for i=1, #lane do - local coord = lane[i] - local Waypoint = coord:WaypointGround(_speed) - table.insert(Waypoints, Waypoint) - end - - local Waypoint = Waypoints[#Waypoints] - - Ship:Route(Waypoints, 1) - - else - BASE:T("ERROR: No shipping lane defined for Naval Transport!") + self:E(self.lid.."ERROR: No shipping lane defined for Naval Transport!") end end end diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index 61319476d..88ba8c725 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -100,7 +100,12 @@ do -- CARGO_UNIT -- Respawn the group... if self.CargoObject then - self.CargoObject:ReSpawnAt( FromPointVec2, CargoDeployHeading ) + if CargoCarrier:IsShip() then + -- If CargoCarrier is a ship, we don't want to spawn the units in the water next to the boat. Use destination coord instead. + self.CargoObject:ReSpawnAt( ToPointVec2, CargoDeployHeading ) + else + self.CargoObject:ReSpawnAt( FromPointVec2, CargoDeployHeading ) + end self:F( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } ) self.CargoCarrier = nil diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index d61c33984..2aabf81a7 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -1777,7 +1777,7 @@ WAREHOUSE.version="1.0.2" -- TODO: Make more examples: ARTY, CAP, ... -- TODO: Check also general requests like all ground. Is this a problem for self propelled if immobile units are among the assets? Check if transport. -- TODO: Handle the case when units of a group die during the transfer. --- TODO: Added habours as interface for transport to from warehouses? Could make a rudimentary shipping dispatcher. +-- DONE: Added harbours as interface for transport to/from warehouses. Simplifies process of spawning units near the ship, especially if cargo not self-propelled. -- DONE: Test capturing a neutral warehouse. -- DONE: Add save/load capability of warehouse <==> persistance after mission restart. Difficult in lua! -- DONE: Get cargo bay and weight from CARGO_GROUP and GROUP. No necessary any more! @@ -1830,7 +1830,6 @@ WAREHOUSE.version="1.0.2" -- @param #string alias (Optional) Alias of the warehouse, i.e. the name it will be called when sending messages etc. Default is the name of the static -- @return #WAREHOUSE self function WAREHOUSE:New(warehouse, alias) - BASE:T({warehouse=warehouse}) -- Check if just a string was given and convert to static. if type(warehouse)=="string" then @@ -4496,7 +4495,6 @@ function WAREHOUSE:onafterRequestSpawned(From, Event, To, Request, CargoGroupSet -- Find asset belonging to this group. local asset=self:FindAssetInDB(_group) - BASE:T("DEBUGGING*** load radius: "..asset.loadradius) -- New cargo group object. local cargogroup=CARGO_GROUP:New(_group, _cargotype,_group:GetName(),_boardradius, asset.loadradius) @@ -4556,7 +4554,8 @@ function WAREHOUSE:onafterRequestSpawned(From, Event, To, Request, CargoGroupSet -- Pickup and deploy zones. local PickupZoneSet = SET_ZONE:New():AddZone(self.portzone) PickupZoneSet:AddZone(self.harborzone) - local DeployZoneSet = SET_ZONE:New():AddZone(Request.warehouse.portzone) + local DeployZoneSet = SET_ZONE:New():AddZone(Request.warehouse.harborzone) + -- Get the shipping lane to use and pass it to the Dispatcher local remotename = Request.warehouse.warehouse:GetName() @@ -4583,21 +4582,6 @@ function WAREHOUSE:onafterRequestSpawned(From, Event, To, Request, CargoGroupSet pickupinner=20 deployouter=1000 deployinner=0 - --BASE:T("DEBUGGING*** Let's try to move these units") - --[[for _,_group in pairs(CargoGroupSet:GetSetObjects()) do - local group=GROUP:FindByName( _group:GetName() ) --Wrapper.Group#GROUP - - - --local _speed = group:GetSpeedMax()*0.7 - BASE:T("DEBUGGING*** Group ".._.." coordinate is "..CargoTransport:GetCoordinate()) - --local FromCoord = group:GetCoordinate() - local ToCoord = CargoTransport:GetCoordinate() - - local FromWP = FromCoord:WaypointGround() - local ToWP = ToCoord:WaypointGround( 15, "Vee" ) - - group:Route( { FromWP, ToWP }, 10 ) - end]]-- else pickupouter=200 pickupinner=0 @@ -4639,15 +4623,6 @@ function WAREHOUSE:onafterRequestSpawned(From, Event, To, Request, CargoGroupSet -- Dispatcher Event Functions -- -------------------------------- - --- Function called before carrier loads something - function CargoTransport:OnBeforeMonitor(From, Event, To, Carrier, Cargo, PickupZone) - -- Need to get the cargo over to the portzone - -- But what if the cargo can't move on it's own? - BASE:T("DEBUGGING*** CargoTransport:OnBeforeMonitor") - - end - - --- Function called after carrier picked up something. function CargoTransport:OnAfterPickedUp(From, Event, To, Carrier, PickupZone) diff --git a/Moose Development/Moose/Modules.lua b/Moose Development/Moose/Modules.lua index 22f23bb75..45442da11 100644 --- a/Moose Development/Moose/Modules.lua +++ b/Moose Development/Moose/Modules.lua @@ -95,10 +95,12 @@ __Moose.Include( 'Scripts/Moose/AI/AI_Cargo.lua' ) __Moose.Include( 'Scripts/Moose/AI/AI_Cargo_APC.lua' ) __Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Helicopter.lua' ) __Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Airplane.lua' ) +__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Ship.lua' ) __Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher.lua' ) __Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_APC.lua' ) __Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua' ) __Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua' ) +__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_Ship.lua' ) __Moose.Include( 'Scripts/Moose/Actions/Act_Assign.lua' ) __Moose.Include( 'Scripts/Moose/Actions/Act_Route.lua' ) diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index be85454a3..e3ce4778a 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -1390,6 +1390,19 @@ do -- Cargo } self.__.CargoBayWeightLimit = Weights[Desc.typeName] or ( Desc.massMax - ( Desc.massEmpty + Desc.fuelMassMax ) ) + elseif self:IsShip() then + local Desc = self:GetDesc() + self:F({Desc=Desc}) + + local Weights = { + ["Type_071"] = 245000, + ["LHA_Tarawa"] = 500000, + ["Ropucha-class"] = 150000, + ["Dry-cargo ship-1"] = 70000, + ["Dry-cargo ship-2"] = 70000, + } + self.__.CargoBayWeightLimit = Weights[Desc.typeName] + else local Desc = self:GetDesc() diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index 4852f9346..8a3b67961 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -1,3 +1,4 @@ +Utilities/Enums.lua Utilities/Routines.lua Utilities/Utils.lua @@ -11,6 +12,7 @@ Core/Event.lua Core/Settings.lua Core/Menu.lua Core/Zone.lua +Core/Zone_Detection.lua Core/Database.lua Core/Set.lua Core/Point.lua @@ -18,6 +20,8 @@ Core/Velocity.lua Core/Message.lua Core/Fsm.lua Core/Radio.lua +Core/RadioQueue.lua +Core/RadioSpeech.lua Core/Spawn.lua Core/SpawnStatic.lua Core/Goal.lua @@ -48,6 +52,7 @@ Functional/Escort.lua Functional/MissileTrainer.lua Functional/ATC_Ground.lua Functional/Detection.lua +Functional/DetectionZones.lua Functional/Designate.lua Functional/RAT.lua Functional/Range.lua @@ -67,31 +72,35 @@ Ops/ATIS.lua AI/AI_Balancer.lua AI/AI_Air.lua -AI/AI_A2A.lua +AI/AI_Air_Patrol.lua +AI/AI_Air_Engage.lua AI/AI_A2A_Patrol.lua AI/AI_A2A_Cap.lua AI/AI_A2A_Gci.lua AI/AI_A2A_Dispatcher.lua -AI/AI_A2G.lua -AI/AI_A2G_Engage.lua AI/AI_A2G_BAI.lua AI/AI_A2G_CAS.lua AI/AI_A2G_SEAD.lua -AI/AI_A2G_Patrol.lua AI/AI_A2G_Dispatcher.lua AI/AI_Patrol.lua -AI/AI_Cap.lua -AI/AI_Cas.lua -AI/AI_Bai.lua +AI/AI_CAP.lua +AI/AI_CAS.lua +AI/AI_BAI.lua AI/AI_Formation.lua +AI/AI_Escort.lua +AI/AI_Escort_Request.lua +AI/AI_Escort_Dispatcher.lua +AI/AI_Escort_Dispatcher_Request.lua AI/AI_Cargo.lua AI/AI_Cargo_APC.lua AI/AI_Cargo_Helicopter.lua AI/AI_Cargo_Airplane.lua +AI/AI_Cargo_Ship.lua AI/AI_Cargo_Dispatcher.lua AI/AI_Cargo_Dispatcher_APC.lua AI/AI_Cargo_Dispatcher_Helicopter.lua AI/AI_Cargo_Dispatcher_Airplane.lua +AI/AI_Cargo_Dispatcher_Ship.lua Actions/Act_Assign.lua Actions/Act_Route.lua @@ -108,10 +117,11 @@ Tasking/Task_A2G_Dispatcher.lua Tasking/Task_A2G.lua Tasking/Task_A2A_Dispatcher.lua Tasking/Task_A2A.lua -Tasking/Task_Cargo.lua +Tasking/Task_CARGO.lua Tasking/Task_Cargo_Transport.lua Tasking/Task_Cargo_CSAR.lua Tasking/Task_Cargo_Dispatcher.lua -Tasking/TaskZoneCapture.lua +Tasking/Task_Capture_Zone.lua +Tasking/Task_Capture_Dispatcher.lua Globals.lua