diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 9fd532dbd..c4b0fd348 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -1728,26 +1728,28 @@ end -- @param #ZONE_POLYGON_BASE self -- @return DCS#Vec2 The Vec2 coordinate. function ZONE_POLYGON_BASE:GetRandomVec2() - self:F2() - --- It is a bit tricky to find a random point within a polygon. Right now i am doing it the dirty and inefficient way... - local Vec2Found = false - local Vec2 + -- It is a bit tricky to find a random point within a polygon. Right now i am doing it the dirty and inefficient way... + + -- Get the bounding square. local BS = self:GetBoundingSquare() - self:T2( BS ) + local Nmax=1000 ; local n=0 + while n0 then @@ -1451,13 +1451,136 @@ function OPSGROUP:SelfDestruction(Delay, ExplosionPower) local unit=element.unit if unit and unit:IsAlive() then - unit:Explode(ExplosionPower) + unit:Explode(ExplosionPower or 100) end end end + return self end +--- Set that this carrier is an all aspect loader. +-- @param #OPSGROUP self +-- @param #number Length Length of loading zone in meters. Default 50 m. +-- @param #number Width Width of loading zone in meters. Default 20 m. +-- @return #OPSGROUP self +function OPSGROUP:SetCarrierLoaderAllAspect(Length, Width) + self.carrierLoader.type="front" + self.carrierLoader.length=Length or 50 + self.carrierLoader.width=Width or 20 + return self +end + +--- Set that this carrier is a front loader. +-- @param #OPSGROUP self +-- @param #number Length Length of loading zone in meters. Default 50 m. +-- @param #number Width Width of loading zone in meters. Default 20 m. +-- @return #OPSGROUP self +function OPSGROUP:SetCarrierLoaderFront(Length, Width) + self.carrierLoader.type="front" + self.carrierLoader.length=Length or 50 + self.carrierLoader.width=Width or 20 + return self +end + +--- Set that this carrier is a back loader. +-- @param #OPSGROUP self +-- @param #number Length Length of loading zone in meters. Default 50 m. +-- @param #number Width Width of loading zone in meters. Default 20 m. +-- @return #OPSGROUP self +function OPSGROUP:SetCarrierLoaderBack(Length, Width) + self.carrierLoader.type="back" + self.carrierLoader.length=Length or 50 + self.carrierLoader.width=Width or 20 + return self +end + +--- Set that this carrier is a starboard (right side) loader. +-- @param #OPSGROUP self +-- @param #number Length Length of loading zone in meters. Default 50 m. +-- @param #number Width Width of loading zone in meters. Default 20 m. +-- @return #OPSGROUP self +function OPSGROUP:SetCarrierLoaderStarboard(Length, Width) + self.carrierLoader.type="right" + self.carrierLoader.length=Length or 50 + self.carrierLoader.width=Width or 20 + return self +end + +--- Set that this carrier is a port (left side) loader. +-- @param #OPSGROUP self +-- @param #number Length Length of loading zone in meters. Default 50 m. +-- @param #number Width Width of loading zone in meters. Default 20 m. +-- @return #OPSGROUP self +function OPSGROUP:SetCarrierLoaderPort(Length, Width) + self.carrierLoader.type="left" + self.carrierLoader.length=Length or 50 + self.carrierLoader.width=Width or 20 + return self +end + + +--- Set that this carrier is an all aspect unloader. +-- @param #OPSGROUP self +-- @param #number Length Length of loading zone in meters. Default 50 m. +-- @param #number Width Width of loading zone in meters. Default 20 m. +-- @return #OPSGROUP self +function OPSGROUP:SetCarrierUnloaderAllAspect(Length, Width) + self.carrierUnloader.type="front" + self.carrierUnloader.length=Length or 50 + self.carrierUnloader.width=Width or 20 + return self +end + +--- Set that this carrier is a front unloader. +-- @param #OPSGROUP self +-- @param #number Length Length of loading zone in meters. Default 50 m. +-- @param #number Width Width of loading zone in meters. Default 20 m. +-- @return #OPSGROUP self +function OPSGROUP:SetCarrierUnloaderFront(Length, Width) + self.carrierUnloader.type="front" + self.carrierUnloader.length=Length or 50 + self.carrierUnloader.width=Width or 20 + return self +end + +--- Set that this carrier is a back unloader. +-- @param #OPSGROUP self +-- @param #number Length Length of loading zone in meters. Default 50 m. +-- @param #number Width Width of loading zone in meters. Default 20 m. +-- @return #OPSGROUP self +function OPSGROUP:SetCarrierUnloaderBack(Length, Width) + self.carrierUnloader.type="back" + self.carrierUnloader.length=Length or 50 + self.carrierUnloader.width=Width or 20 + return self +end + +--- Set that this carrier is a starboard (right side) unloader. +-- @param #OPSGROUP self +-- @param #number Length Length of loading zone in meters. Default 50 m. +-- @param #number Width Width of loading zone in meters. Default 20 m. +-- @return #OPSGROUP self +function OPSGROUP:SetCarrierUnloaderStarboard(Length, Width) + self.carrierUnloader.type="right" + self.carrierUnloader.length=Length or 50 + self.carrierUnloader.width=Width or 20 + return self +end + +--- Set that this carrier is a port (left side) unloader. +-- @param #OPSGROUP self +-- @param #number Length Length of loading zone in meters. Default 50 m. +-- @param #number Width Width of loading zone in meters. Default 20 m. +-- @return #OPSGROUP self +function OPSGROUP:SetCarrierUnloaderPort(Length, Width) + self.carrierUnloader.type="left" + self.carrierUnloader.length=Length or 50 + self.carrierUnloader.width=Width or 20 + return self +end + + --- Check if this is a FLIGHTGROUP. -- @param #OPSGROUP self -- @return #boolean If true, this is an airplane or helo group. @@ -5767,27 +5890,12 @@ function OPSGROUP:onafterUnloading(From, Event, To) Coordinate=self.cargoTransport.disembarkzone:GetRandomCoordinate() else - - -- TODO: Optimize with random Vec2 - - -- Create a zone around the carrier. - local zoneCarrier=ZONE_RADIUS:New("Carrier", self:GetVec2(), 100) - - local d={} - d.p1={x=vec2.x-l/2, y=vec2.y-w/2} --DCS#Vec2 - d.p2={x=vec2.x-l/2, y=vec2.y+w/2} --DCS#Vec2 - d.p3={x=d2.x+20, y=d2.y+20} - d.p4={x=d1.x+20, y=d1.y+20} - - for _,_p in pairs(d) do - local p=_p --#DCSVec2 - end - - local zoneCarrier=ZONE_POLYGON_BASE:New("Carrier", {d1, d2, d3, d4}) + -- Get random point in disembark zone. + local zoneCarrier=self:GetElementZoneUnload(cargo.opsgroup:_GetMyCarrierElement().name) -- Random coordinate/heading in the zone. - Coordinate=zoneCarrier:GetRandomCoordinate(50) + Coordinate=zoneCarrier:GetRandomCoordinate() end @@ -5800,8 +5908,7 @@ function OPSGROUP:onafterUnloading(From, Event, To) end -- Trigger "Unloaded" event for current cargo transport - self.cargoTransport:Unloaded(cargo.opsgroup) - + self.cargoTransport:Unloaded(cargo.opsgroup) end @@ -5898,13 +6005,16 @@ function OPSGROUP:onafterUnload(From, Event, To, OpsGroup, Coordinate, Activated -- Respawn group. OpsGroup:_Respawn(0, Template) - if self:IsNavygroup() then - self.currentwp=1 - NAVYGROUP.AddWaypoint(self, Coordinate, nil, nil, nil, false) - elseif self:IsArmygroup() then - self.currentwp=1 - ARMYGROUP.AddWaypoint(self, Coordinate, nil, nil, nil, false) - end + -- Add current waypoint. These have been cleard on loading. + if OpsGroup:IsNavygroup() then + OpsGroup.currentwp=1 + OpsGroup.passedfinalwp=true + NAVYGROUP.AddWaypoint(OpsGroup, Coordinate, nil, nil, nil, false) + elseif OpsGroup:IsArmygroup() then + OpsGroup.currentwp=1 + OpsGroup.passedfinalwp=true + ARMYGROUP.AddWaypoint(OpsGroup, Coordinate, nil, nil, nil, false) + end else @@ -5919,11 +6029,10 @@ function OPSGROUP:onafterUnload(From, Event, To, OpsGroup, Coordinate, Activated end -- Trigger "Disembarked" event. - OpsGroup:Disembarked(OpsGroup.carrierGroup, OpsGroup.carrier) + OpsGroup:Disembarked(OpsGroup:_GetMyCarrierGroup(), OpsGroup:_GetMyCarrierElement()) - -- No carrier any more. - OpsGroup.carrier=nil - OpsGroup.carrierGroup=nil + -- Remove my carrier. + OpsGroup:_RemoveMyCarrier() end @@ -5966,11 +6075,8 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param Ops.OpsTransport#OPSTRANSPORT CargoTransport +-- @param Ops.OpsTransport#OPSTRANSPORT CargoTransport The cargo transport assignment. function OPSGROUP:onafterDelivered(From, Event, To, CargoTransport) - - -- Set cargo status. - CargoTransport.status=OPSGROUP.TransportStatus.DELIVERED -- Check if this was the current transport. if self.cargoTransport and self.cargoTransport.uid==CargoTransport.uid then @@ -7995,44 +8101,173 @@ end -- @param #OPSGROUP self -- @param #string UnitName Name of unit. -- @return Core.Zone#ZONE_POLYGON Bounding box polygon zone. -function OPSGROUP:GetElementBoundingBox(UnitName) +function OPSGROUP:GetElementZoneBoundingBox(UnitName) local element=self:GetElementByName(UnitName) if element and element.status~=OPSGROUP.ElementStatus.DEAD then + + -- Create a new zone if necessary. + element.zoneBoundingbox=element.zoneBoundingbox or ZONE_POLYGON_BASE:New(element.name.." Zone Bounding Box", {}) local l=element.length local w=element.width - local heading=element.unit:GetHeading() + local X=self:GetOrientationX(element.name) + local heading=math.deg(math.atan2(X.z, X.x)) env.info(string.format("FF l=%d w=%d h=%d", l, w, heading)) - - local vec2=self:GetVec2(element.name) - - -- Set of + + -- Set of edges facing "North" at the origin of the map. local b={} - b[1]={y=l/2, x=-w/2} --DCS#Vec2 - b[2]={y=l/2, x=w/2} --DCS#Vec2 - b[3]={y=-l/2, x=w/2} --DCS#Vec2 - b[4]={y=-l/2, x=-w/2} --DCS#Vec2 + b[1]={x=l/2, y=-w/2} --DCS#Vec2 + b[2]={x=l/2, y=w/2} --DCS#Vec2 + b[3]={x=-l/2, y=w/2} --DCS#Vec2 + b[4]={x=-l/2, y=-w/2} --DCS#Vec2 + -- Rotate box to match current heading of the unit. for i,p in pairs(b) do b[i]=UTILS.Vec2Rotate2D(p, heading) end - + + -- Translate the zone to the positon of the unit. + local vec2=self:GetVec2(element.name) local d=UTILS.Vec2Norm(vec2) local h=UTILS.Vec2Hdg(vec2) for i,p in pairs(b) do - --b[i]=UTILS.Vec2Translate(p, d, h) + b[i]=UTILS.Vec2Translate(p, d, h) end + + -- Update existing zone. + element.zoneBoundingbox:UpdateFromVec2(b) - return ZONE_POLYGON_BASE:New(element.name, b) + return element.zoneBoundingbox end return nil end +--- Get the loading zone of the element. +-- @param #OPSGROUP self +-- @param #string UnitName Name of unit. +-- @return Core.Zone#ZONE_POLYGON Bounding box polygon zone. +function OPSGROUP:GetElementZoneLoad(UnitName) + + local element=self:GetElementByName(UnitName) + + if element and element.status~=OPSGROUP.ElementStatus.DEAD then + + element.zoneLoad=element.zoneLoad or ZONE_POLYGON_BASE:New(element.name.." Zone Load", {}) + + self:_GetElementZoneLoader(element, element.zoneLoad, self.carrierLoader) + + return element.zoneLoad + end + + return nil +end + +--- Get the unloading zone of the element. +-- @param #OPSGROUP self +-- @param #string UnitName Name of unit. +-- @return Core.Zone#ZONE_POLYGON Bounding box polygon zone. +function OPSGROUP:GetElementZoneUnload(UnitName) + + local element=self:GetElementByName(UnitName) + + if element and element.status~=OPSGROUP.ElementStatus.DEAD then + + element.zoneUnload=element.zoneUnload or ZONE_POLYGON_BASE:New(element.name.." Zone Unload", {}) + + self:_GetElementZoneLoader(element, element.zoneUnload, self.carrierUnloader) + + return element.zoneUnload + end + + return nil +end + +--- Get/update the (un-)loading zone of the element. +-- @param #OPSGROUP self +-- @param #OPSGROUP.Element Element Element. +-- @param Core.Zone#ZONE_POLYGON Zone The zone. +-- @param #OPSGROUP.CarrierLoader Loader Loader parameters. +-- @return Core.Zone#ZONE_POLYGON Bounding box polygon zone. +function OPSGROUP:_GetElementZoneLoader(Element, Zone, Loader) + + if Element.status~=OPSGROUP.ElementStatus.DEAD then + + local l=Element.length + local w=Element.width + + -- Orientation 3D vector where the "nose" is pointing. + local X=self:GetOrientationX(Element.name) + + -- Heading in deg. + local heading=math.deg(math.atan2(X.z, X.x)) + + env.info(string.format("FF l=%d w=%d h=%d", l, w, heading)) + + -- Bounding box at the origin of the map facing "North". + local b={} + + -- Create polygon rectangles. + if Loader.type:lower()=="front" then + table.insert(b, {x= l/2, y=-Loader.width/2}) -- left, low + table.insert(b, {x= l/2+Loader.length, y=-Loader.width/2}) -- left, up + table.insert(b, {x= l/2+Loader.length, y= Loader.width/2}) -- right, up + table.insert(b, {x= l/2, y= Loader.width/2}) -- right, low + elseif Loader.type:lower()=="back" then + table.insert(b, {x=-l/2, y=-Loader.width/2}) -- left, low + table.insert(b, {x=-l/2-Loader.length, y=-Loader.width/2}) -- left, up + table.insert(b, {x=-l/2-Loader.length, y= Loader.width/2}) -- right, up + table.insert(b, {x=-l/2, y= Loader.width/2}) -- right, low + elseif Loader.type:lower()=="left" then + table.insert(b, {x= Loader.length/2, y= -w/2}) -- right, up + table.insert(b, {x= Loader.length/2, y= -w/2-Loader.width}) -- left, up + table.insert(b, {x=-Loader.length/2, y= -w/2-Loader.width}) -- left, down + table.insert(b, {x=-Loader.length/2, y= -w/2}) -- right, down + elseif Loader.type:lower()=="right" then + table.insert(b, {x= Loader.length/2, y= w/2}) -- right, up + table.insert(b, {x= Loader.length/2, y= w/2+Loader.width}) -- left, up + table.insert(b, {x=-Loader.length/2, y= w/2+Loader.width}) -- left, down + table.insert(b, {x=-Loader.length/2, y= w/2}) -- right, down + else + -- All aspect. Rectangle around the unit but need to cut out the area of the unit itself. + b[1]={x= l/2, y=-w/2} --DCS#Vec2 + b[2]={x= l/2, y= w/2} --DCS#Vec2 + b[3]={x=-l/2, y= w/2} --DCS#Vec2 + b[4]={x=-l/2, y=-w/2} --DCS#Vec2 + table.insert(b, {x=b[1].x+Loader.length, y=b[1].y-Loader.width}) + table.insert(b, {x=b[2].x+Loader.length, y=b[2].y+Loader.width}) + table.insert(b, {x=b[3].x-Loader.length, y=b[3].y+Loader.width}) + table.insert(b, {x=b[4].x-Loader.length, y=b[4].y-Loader.width}) + end + + -- Rotate edges to match the current heading of the unit. + for i,p in pairs(b) do + b[i]=UTILS.Vec2Rotate2D(p, heading) + end + + -- Translate box to the current position of the unit. + local vec2=self:GetVec2(Element.name) + local d=UTILS.Vec2Norm(vec2) + local h=UTILS.Vec2Hdg(vec2) + + for i,p in pairs(b) do + b[i]=UTILS.Vec2Translate(p, d, h) + end + + -- Update existing zone. + Zone:UpdateFromVec2(b) + + return Zone + end + + return nil +end + + --- Get the first element of a group, which is alive. -- @param #OPSGROUP self -- @return #OPSGROUP.Element The element or `#nil` if no element is alive any more. @@ -8399,9 +8634,7 @@ function OPSGROUP:_AddElementByName(unitname) end - -- Max cargo weight - --element.weightMaxCargo=math.max(element.weightMaxTotal-element.weightEmpty, 0) - + -- Max cargo weight: unit:SetCargoBayWeightLimit() element.weightMaxCargo=unit.__.CargoBayWeightLimit diff --git a/Moose Development/Moose/Ops/OpsTransport.lua b/Moose Development/Moose/Ops/OpsTransport.lua index ef21c495a..2c03b672f 100644 --- a/Moose Development/Moose/Ops/OpsTransport.lua +++ b/Moose Development/Moose/Ops/OpsTransport.lua @@ -97,7 +97,7 @@ _OPSTRANSPORTID=0 --- Army Group version. -- @field #string version -OPSTRANSPORT.version="0.0.3" +OPSTRANSPORT.version="0.0.5" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index bc5a1a352..25f832ee5 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -1127,13 +1127,13 @@ function UTILS.Vec2Rotate2D(a, angle) local phi=math.rad(angle) - local x=a.y - local y=a.x + local x=a.x + local y=a.y - local Z=x*math.cos(phi)-y*math.sin(phi) - local X=x*math.sin(phi)+y*math.cos(phi) + local X=x*math.cos(phi)-y*math.sin(phi) + local Y=x*math.sin(phi)+y*math.cos(phi) - local A={x=X, y=Z} + local A={x=X, y=Y} return A end diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 67f892052..338417123 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -1394,18 +1394,6 @@ do -- Cargo return ItemCount end --- --- Get Cargo Bay Free Volume in m3. --- -- @param #POSITIONABLE self --- -- @return #number CargoBayFreeVolume --- function POSITIONABLE:GetCargoBayFreeVolume() --- local CargoVolume = 0 --- for CargoName, Cargo in pairs( self.__.Cargo ) do --- CargoVolume = CargoVolume + Cargo:GetVolume() --- end --- return self.__.CargoBayVolumeLimit - CargoVolume --- end --- - --- Get Cargo Bay Free Weight in kg. -- @param #POSITIONABLE self -- @return #number CargoBayFreeWeight @@ -1423,13 +1411,6 @@ do -- Cargo return self.__.CargoBayWeightLimit - CargoWeight end --- --- Get Cargo Bay Volume Limit in m3. --- -- @param #POSITIONABLE self --- -- @param #number VolumeLimit --- function POSITIONABLE:SetCargoBayVolumeLimit( VolumeLimit ) --- self.__.CargoBayVolumeLimit = VolumeLimit --- end - --- Set Cargo Bay Weight Limit in kg. -- @param #POSITIONABLE self -- @param #number WeightLimit @@ -1458,11 +1439,13 @@ do -- Cargo 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, + ["Type_071"] = 245000, + ["LHA_Tarawa"] = 500000, + ["Ropucha-class"] = 150000, + ["Dry-cargo ship-1"] = 70000, + ["Dry-cargo ship-2"] = 70000, + ["Higgins_boat"] = 3700, -- Higgins Boat can load 3700 kg of general cargo or 36 men (source wikipedia). + ["USS_Samuel_Chase"] = 25000, -- Let's say 25 tons for now. Wiki says 33 Higgins boats, which would be 264 tons (can't be right!) and/or 578 troops. } self.__.CargoBayWeightLimit = ( Weights[Desc.typeName] or 50000 ) @@ -1507,9 +1490,11 @@ do -- Cargo } local CargoBayWeightLimit = ( Weights[Desc.typeName] or 0 ) * 95 + self.__.CargoBayWeightLimit = CargoBayWeightLimit end end + self:F({CargoBayWeightLimit = self.__.CargoBayWeightLimit}) end end --- Cargo