From 7ef69208d4919b28cbc4eb341c8cbbba802ea730 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 30 Aug 2022 14:36:18 +0200 Subject: [PATCH] #ZONE_RADIUS * Added `ZONE_RADIUS:GetRandomCoordinateWithoutBuildings(...)` #MARKEROPS_BASE * Added Class --- .../Moose/Core/MarkerOps_Base.lua | 251 ++++++++++++++++++ Moose Development/Moose/Core/Zone.lua | 81 +++++- Moose Development/Moose/Modules.lua | 1 + 3 files changed, 332 insertions(+), 1 deletion(-) create mode 100644 Moose Development/Moose/Core/MarkerOps_Base.lua diff --git a/Moose Development/Moose/Core/MarkerOps_Base.lua b/Moose Development/Moose/Core/MarkerOps_Base.lua new file mode 100644 index 000000000..8e318a7a7 --- /dev/null +++ b/Moose Development/Moose/Core/MarkerOps_Base.lua @@ -0,0 +1,251 @@ +--- **Core** - MarkerOps_Base. +-- +-- **Main Features:** +-- +-- * Create an easy way to tap into markers added to the F10 map by users. +-- * Recognize own tag and list of keywords. +-- * Matched keywords are handed down to functions. +-- +-- === +-- +-- ### Author: **Applevangelist** +-- +-- Date: 5 May 2021 +-- +-- === +--- +-- @module Core.MarkerOps_Base +-- @image MOOSE_Core.JPG + +-------------------------------------------------------------------------- +-- MARKEROPS_BASE Class Definition. +-------------------------------------------------------------------------- + +--- MARKEROPS_BASE class. +-- @type MARKEROPS_BASE +-- @field #string ClassName Name of the class. +-- @field #string Tag Tag to identify commands. +-- @field #table Keywords Table of keywords to recognize. +-- @field #string version Version of #MARKEROPS_BASE. +-- @extends Core.Fsm#FSM + +--- *Fiat lux.* -- Latin proverb. +-- +-- === +-- +-- # The MARKEROPS_BASE Concept +-- +-- This class enable scripting text-based actions from markers. +-- +-- @field #MARKEROPS_BASE +MARKEROPS_BASE = { + ClassName = "MARKEROPS", + Tag = "mytag", + Keywords = {}, + version = "0.0.1", + debug = false, +} + +--- Function to instantiate a new #MARKEROPS_BASE object. +-- @param #MARKEROPS_BASE self +-- @param #string Tagname Name to identify us from the event text. +-- @param #table Keywords Table of keywords recognized from the event text. +-- @return #MARKEROPS_BASE self +function MARKEROPS_BASE:New(Tagname,Keywords) + -- Inherit FSM + local self=BASE:Inherit(self, FSM:New()) -- #MARKEROPS_BASE + + -- Set some string id for output to DCS.log file. + self.lid=string.format("MARKEROPS_BASE %s | ", tostring(self.version)) + + self.Tag = Tagname or "mytag"-- #string + self.Keywords = Keywords or {} -- #table - might want to use lua regex here, too + self.debug = false + + ----------------------- + --- FSM Transitions --- + ----------------------- + + -- Start State. + self:SetStartState("Stopped") + + -- Add FSM transitions. + -- From State --> Event --> To State + self:AddTransition("Stopped", "Start", "Running") -- Start the FSM. + self:AddTransition("*", "MarkAdded", "*") -- Start the FSM. + self:AddTransition("*", "MarkChanged", "*") -- Start the FSM. + self:AddTransition("*", "MarkDeleted", "*") -- Start the FSM. + self:AddTransition("Running", "Stop", "Stopped") -- Stop the FSM. + + self:HandleEvent(EVENTS.MarkAdded, self.OnEventMark) + self:HandleEvent(EVENTS.MarkChange, self.OnEventMark) + self:HandleEvent(EVENTS.MarkRemoved, self.OnEventMark) + + -- start + self:I(self.lid..string.format("started for %s",self.Tag)) + self:__Start(1) + return self + + ------------------- + -- PSEUDO Functions + ------------------- + + --- On after "MarkAdded" event. Triggered when a Marker is added to the F10 map. + -- @function [parent=#MARKEROPS_BASE] OnAfterMarkAdded + -- @param #MARKEROPS_BASE self + -- @param #string From The From state + -- @param #string Event The Event called + -- @param #string To The To state + -- @param #string Text The text on the marker + -- @param #table Keywords Table of matching keywords found in the Event text + -- @param Core.Point#COORDINATE Coord Coordinate of the marker. + + --- On after "MarkChanged" event. Triggered when a Marker is changed on the F10 map. + -- @function [parent=#MARKEROPS_BASE] OnAfterMarkChanged + -- @param #MARKEROPS_BASE self + -- @param #string From The From state + -- @param #string Event The Event called + -- @param #string To The To state + -- @param #string Text The text on the marker + -- @param #table Keywords Table of matching keywords found in the Event text + -- @param Core.Point#COORDINATE Coord Coordinate of the marker. + + --- On after "MarkDeleted" event. Triggered when a Marker is deleted from the F10 map. + -- @function [parent=#MARKEROPS_BASE] OnAfterMarkDeleted + -- @param #MARKEROPS_BASE self + -- @param #string From The From state + -- @param #string Event The Event called + -- @param #string To The To state + + --- "Stop" trigger. Used to stop the function an unhandle events + -- @function [parent=#MARKEROPS_BASE] Stop + +end + +--- (internal) Handle events. +-- @param #MARKEROPS_BASE self +-- @param Core.Event#EVENTDATA Event +function MARKEROPS_BASE:OnEventMark(Event) + self:T({Event}) + if Event == nil or Event.idx == nil then + self:E("Skipping onEvent. Event or Event.idx unknown.") + return true + end + --position + local vec3={y=Event.pos.y, x=Event.pos.x, z=Event.pos.z} + local coord=COORDINATE:NewFromVec3(vec3) + if self.debug then + local coordtext = coord:ToStringLLDDM() + local text = tostring(Event.text) + local m = MESSAGE:New(string.format("Mark added at %s with text: %s",coordtext,text),10,"Info",false):ToAll() + end + -- decision + if Event.id==world.event.S_EVENT_MARK_ADDED then + self:T({event="S_EVENT_MARK_ADDED", carrier=self.groupname, vec3=Event.pos}) + -- Handle event + local Eventtext = tostring(Event.text) + if Eventtext~=nil then + if self:_MatchTag(Eventtext) then + local matchtable = self:_MatchKeywords(Eventtext) + self:MarkAdded(Eventtext,matchtable,coord) + end + end + elseif Event.id==world.event.S_EVENT_MARK_CHANGE then + self:T({event="S_EVENT_MARK_CHANGE", carrier=self.groupname, vec3=Event.pos}) + -- Handle event. + local Eventtext = tostring(Event.text) + if Eventtext~=nil then + if self:_MatchTag(Eventtext) then + local matchtable = self:_MatchKeywords(Eventtext) + self:MarkChanged(Eventtext,matchtable,coord) + end + end + elseif Event.id==world.event.S_EVENT_MARK_REMOVED then + self:T({event="S_EVENT_MARK_REMOVED", carrier=self.groupname, vec3=Event.pos}) + -- Hande event. + local Eventtext = tostring(Event.text) + if Eventtext~=nil then + if self:_MatchTag(Eventtext) then + self:MarkDeleted() + end + end + end +end + +--- (internal) Match tag. +-- @param #MARKEROPS_BASE self +-- @param #string Eventtext Text added to the marker. +-- @return #boolean +function MARKEROPS_BASE:_MatchTag(Eventtext) + local matches = false + local type = string.lower(self.Tag) -- #string + if string.find(string.lower(Eventtext),type) then + matches = true --event text contains tag + end + return matches +end + +--- (internal) Match keywords table. +-- @param #MARKEROPS_BASE self +-- @param #string Eventtext Text added to the marker. +-- @return #table +function MARKEROPS_BASE:_MatchKeywords(Eventtext) + local matchtable = {} + local keytable = self.Keywords + for _index,_word in pairs (keytable) do + if string.find(string.lower(Eventtext),string.lower(_word))then + table.insert(matchtable,_word) + end + end + return matchtable +end + +--- On before "MarkAdded" event. Triggered when a Marker is added to the F10 map. + -- @param #MARKEROPS_BASE self + -- @param #string From The From state + -- @param #string Event The Event called + -- @param #string To The To state + -- @param #string Text The text on the marker + -- @param #table Keywords Table of matching keywords found in the Event text + -- @param Core.Point#COORDINATE Coord Coordinate of the marker. +function MARKEROPS_BASE:onbeforeMarkAdded(From,Event,To,Text,Keywords,Coord) + self:T({self.lid,From,Event,To,Text,Keywords,Coord:ToStringLLDDM()}) +end + +--- On before "MarkChanged" event. Triggered when a Marker is changed on the F10 map. + -- @param #MARKEROPS_BASE self + -- @param #string From The From state + -- @param #string Event The Event called + -- @param #string To The To state + -- @param #string Text The text on the marker + -- @param #table Keywords Table of matching keywords found in the Event text + -- @param Core.Point#COORDINATE Coord Coordinate of the marker. +function MARKEROPS_BASE:onbeforeMarkChanged(From,Event,To,Text,Keywords,Coord) + self:T({self.lid,From,Event,To,Text,Keywords,Coord:ToStringLLDDM()}) +end + +--- On before "MarkDeleted" event. Triggered when a Marker is removed from the F10 map. + -- @param #MARKEROPS_BASE self + -- @param #string From The From state + -- @param #string Event The Event called + -- @param #string To The To state +function MARKEROPS_BASE:onbeforeMarkDeleted(From,Event,To) + self:T({self.lid,From,Event,To}) +end + +--- On enter "Stopped" event. Unsubscribe events. + -- @param #MARKEROPS_BASE self + -- @param #string From The From state + -- @param #string Event The Event called + -- @param #string To The To state +function MARKEROPS_BASE:onenterStopped(From,Event,To) + self:T({self.lid,From,Event,To}) + -- unsubscribe from events + self:UnHandleEvent(EVENTS.MarkAdded) + self:UnHandleEvent(EVENTS.MarkChange) + self:UnHandleEvent(EVENTS.MarkRemoved) +end + +-------------------------------------------------------------------------- +-- MARKEROPS_BASE Class Definition End. +-------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 58d03cf31..2f9d3ee39 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -1295,7 +1295,86 @@ function ZONE_RADIUS:GetRandomCoordinate(inner, outer, surfacetypes) return Coordinate end - +--- Returns a @{Core.Point#COORDINATE} object reflecting a random location within the zone where there are no **map objects** of type "Building". +-- Does not find statics you might have placed there. **Note** This might be quite CPU intensive, use with care. +-- @param #ZONE_RADIUS self +-- @param #number inner (Optional) Minimal distance from the center of the zone in meters. Default is 0m. +-- @param #number outer (Optional) Maximal distance from the outer edge of the zone in meters. Default is the radius of the zone. +-- @param #number distance (Optional) Minumum distance from any building coordinate. Defaults to 100m. +-- @param #boolean markbuildings (Optional) Place markers on found buildings (if any). +-- @param #boolean markfinal (Optional) Place marker on the final coordinate (if any). +-- @return Core.Point#COORDINATE The random coordinate or `nil` if cannot be found in 1000 iterations. +function ZONE_RADIUS:GetRandomCoordinateWithoutBuildings(inner,outer,distance,markbuildings,markfinal) + + local dist = distance or 100 + + local objects = {} + + if self.ScanData and self.ScanData.Scenery then + objects = self:GetScannedScenery() + else + self:Scan({Object.Category.SCENERY}) + objects = self:GetScannedScenery() + end + + local T0 = timer.getTime() + local T1 = timer.getTime() + + + local buildings = {} + if self.ScanData and self.ScanData.BuildingCoordinates then + buildings = self.ScanData.BuildingCoordinates + else + -- build table of buildings coordinates + for _,_object in pairs (objects) do + for _,_scen in pairs (_object) do + local scenery = _scen -- Wrapper.Scenery#SCENERY + local description=scenery:GetDesc() + if description and description.attributes and description.attributes.Buildings then + if markbuildings then + MARKER:New(scenery:GetCoordinate(),"Building"):ToAll() + end + buildings[#buildings+1] = scenery:GetCoordinate() + end + end + end + self.ScanData.BuildingCoordinates = buildings + end + + -- max 1000 tries + local rcoord = nil + local found = false + local iterations = 0 + + for i=1,1000 do + iterations = iterations + 1 + rcoord = self:GetRandomCoordinate(inner,outer) + found = false + for _,_coord in pairs (buildings) do + local coord = _coord -- Core.Point#COORDINATE + -- keep >50m dist from buildings + if coord:Get2DDistance(rcoord) > dist then + found = true + else + found = false + end + end + if found then + -- we have a winner! + if markfinal then + MARKER:New(rcoord,"FREE"):ToAll() + end + break + end + end + + T1=timer.getTime() + + self:T(string.format("Found a coordinate: %s | Iterations: %d | Time: %d",tostring(found),iterations,T1-T0)) + + if found then return rcoord else return nil end + +end --- @type ZONE -- @extends #ZONE_RADIUS diff --git a/Moose Development/Moose/Modules.lua b/Moose Development/Moose/Modules.lua index 29c10ad0c..f56995e1b 100644 --- a/Moose Development/Moose/Modules.lua +++ b/Moose Development/Moose/Modules.lua @@ -28,6 +28,7 @@ __Moose.Include( 'Scripts/Moose/Core/SpawnStatic.lua' ) __Moose.Include( 'Scripts/Moose/Core/Timer.lua' ) __Moose.Include( 'Scripts/Moose/Core/Goal.lua' ) __Moose.Include( 'Scripts/Moose/Core/Spot.lua' ) +__Moose.Include( 'Scripts/Moose/Core/MarkerOps_Base.lua' ) __Moose.Include( 'Scripts/Moose/Wrapper/Object.lua' ) __Moose.Include( 'Scripts/Moose/Wrapper/Identifiable.lua' )