Jump to content

MOOSE - Mission Object Oriented Scripting Framework


Recommended Posts

1. You changed the resource plan of an airbase. Only one airplane left.

2. You spawn that airplane, so the resources of the airbase are empty now.

3. You want to destroy that airplane when the airbase is captured...

 

I did not think that it was possible to access the resources of the airbases from within Lua. Is that a thing now?

Fridge

----------

Things which do you no good in aviation:

1) Altitude above you;

2) Runway behind you;

3) Fuel in the truck;

4) The airspeed you don't have.

Link to comment
Share on other sites

DCS 1.5.6;

 

I am running into a weird issue with regard to MENU_CLIENT_COMMAND. I realize that the client code is only partially working until the final few related DCS bugs are addressed but I was using the example code in MOOSE as a baseline.

 

So, here is what I was doing. I was creating a training mission with 4 client player aircraft and I was choosing one of those (by name) as the training master. I wanted this client to have access to menu commands to spawn adversary aircraft on command while the other 3 client player positions were for the trainees.

 

I used the same method in the MOOSE test missions: create a scheduled function call to generate the menu items every 30 seconds. The function would create/re-create the top level Flight Lead Commands menu item with a Spawn Active and Spawn Passive menu under that. In each of the Spawn Active and Spawn Passive menu options would be the actual spawn commands. In this case there were 8 of those.

 

Going through the menu would be like this:

F10 -> F1. Flight Lead Commands -> F1. Spawn Active -> F1. Spawn Su-17 Defensive;

F10 -> F1. Flight Lead Commands -> F1. Spawn Active -> F2. Spawn Su-17 Offensive;

F10 -> F1. Flight Lead Commands -> F1. Spawn Active -> F3. Spawn F-5E-3 Defensive;

F10 -> F1. Flight Lead Commands -> F1. Spawn Active -> F4. Spawn F-5E-3 Offensive;

etc

 

The menu items were created correctly with the correct names.

 

The problem was that, randomly, the list would be reversed but not reversed in the F10 menu.

 

Normally, following this chain would spawn the first item (F10 -> F1 -> F1 -> F1) and this would spawn the last item (F10 -> F1 -> F1 -> F8) but every now and then the items were switched even though the menu text was not switched. Meaning that the following sequence (F10 -> F1. Flight Lead Commands -> F1. Spawn Active -> F1. Spawn Su-17) would actually execute the expected result from this sequence (F10 -> F1. Flight Lead Commands -> F1. Spawn Active -> F8. Spawn Mig 29);

 

This is weird and I could not figure out if it was a MOOSE issue or if it was related to DCS.

 

As I understand it, the pattern portrayed in the MOOSE Test code creates a scheduled task to create the menu hierarchy and this hierarchy is recreated each time that the scheduled function is called. Sometimes it seemed that although it was creating the list in the correct manor, DCS was interpreting the underlying calls to be in the reverse order. DCS would display the menu in the correct sequence but when the sequence was called it would be, in effect, reversed.

 

I was wondering if that seemed plausible from the MOOSE framework point of view. I was thinking that maybe I was hitting a spot where I was accessing the menu items at the same time that it was being re-written so I extended the time between the scheduled function calls and could reliably get the right spawns about 70% of the time.

 

Weird?

 

In the end I removed the scheduled function calls to create the MENU_CLIENT_COMMAND and replaced it with a static global set of MENU_MISSION_COMMAND calls.

 

I will attach my code but I would guess a mission would be better? The commented code at the bottom replaces the code just above it.

 

I do not think it is the MOOSE calls as I am using them sequentially and unless MOOSE is caching them (?). Instead I think that it may be related to the nature of having a scheduled function replace the menu structure every 20 seconds or so - ie: replacing the menu structure repeatedly is causing DCS, from time to time, to get confused.

 

Side note: I was considering using the scheduled task once to establish the menu structure and then relying on events (ON_BIRTH, ON_DEATH) to detect the client 'logging on/off to the server since this is targeted at a multiplayer environment.

 

For the mission I have 2 sets of route groups/units (ie: groups with the name Passive Route #001 and the unit name of the single unit in the group set to the same name (note the comments below where I need to change this!)) and a second set of template groups named as Passive_TargetRoute.

 

PS: My earlier problem with DCS World 2.0 with 'late activation' units moving before they are activated is back again :-)

 

do

-- TargetRoutes are single unit groups with the group name matching the unit (pilot) name. THIS NEEDS TO CHANGE!
Passive_TargetRoutes = { "Passive Route #001", "Passive Route #002", "Passive Route #003", "Passive Route #004" } -- Unit/Group names

Passive_TargetGroups = {
 { spawn = SPAWN:New("P-Su-17M4 01" ):Limit( 10, 10 ), txt = "Su-17M4 Single" },
 { spawn = SPAWN:New("P-Su-17M4 02" ):Limit( 10, 10 ), txt = "Su-17M4 Pair" },
 { spawn = SPAWN:New("P-Su-17M4 01A"):Limit( 10, 10 ), txt = "Su-17M4 Single AA" },
 { spawn = SPAWN:New("P-Su-17M4 02A"):Limit( 10, 10 ), txt = "Su-17M4 Pair AA" },
 { spawn = SPAWN:New("P-F-5E-3 01"  ):Limit( 10, 10 ), txt = "F-5E-3 Single" },
 { spawn = SPAWN:New("P-F-5E-3 02"  ):Limit( 10, 10 ), txt = "F-5E-3 Pair" },
 { spawn = SPAWN:New("P-F-5E-3 01A" ):Limit( 10, 10 ), txt = "F-5E-3 Single AA" },
 { spawn = SPAWN:New("P-F-5E-3 02A" ):Limit( 10, 10 ), txt = "F-5E-3 Pair AA" },
}

Active_TargetRoutes = { "Active Route #001", "Active Route #002", "Active Route #003", "Active Route #004" } -- Unit/Group names

Active_TargetGroups = {
 { spawn = SPAWN:New("A-Su-17M4 01" ):Limit( 10, 10 ), txt = "Su-17M4 Single" },
 { spawn = SPAWN:New("A-Su-17M4 02" ):Limit( 10, 10 ), txt = "Su-17M4 Pair" },
 { spawn = SPAWN:New("A-Su-17M4 01A"):Limit( 10, 10 ), txt = "Su-17M4 Single AA" },
 { spawn = SPAWN:New("A-Su-17M4 02A"):Limit( 10, 10 ), txt = "Su-17M4 Pair AA" },
 { spawn = SPAWN:New("A-F-5E-3 01"  ):Limit( 10, 10 ), txt = "F-5E-3 Single" },
 { spawn = SPAWN:New("A-F-5E-3 02"  ):Limit( 10, 10 ), txt = "F-5E-3 Pair" },
 { spawn = SPAWN:New("A-F-5E-3 01A" ):Limit( 10, 10 ), txt = "F-5E-3 Single AA" },
 { spawn = SPAWN:New("A-F-5E-3 02A" ):Limit( 10, 10 ), txt = "F-5E-3 Pair AA" },
}


local function SpawnControl_spawn ( _data )
		-- Expects :
		_data._client    = _data._client or nil    -- Reference to the client, if it exists. Will be nil if it call is from 'global' message command pool
		_data._spawnobj  = _data._spawnobj or nil  -- Reference to the MOOSE SPAWN pool object
		_data._routelist = _data._routelist or nil -- Reference to the list of routes (list of unit names that map to groups)
		_data._txt       = _data._txt or nil       -- Basically the Menu Command text
		_data._grp       = _data._grp or nil       -- Basically the index of the menu parent entry
		_data._idx       = _data._idx or nil       -- Basically the index of the menu entry

	if _data ~= nil then
			-- Get the Route Unit (unit whose route we will clone)
			-- TargetRoutes are single unit groups with the group name matching the unit (pilot) name. THIS NEEDS TO CHANGE!
		local idx = math.random( #_data._routelist )
			-- OLD
		local UnitName = _data._routelist[ idx ]
     		local RouteUnit = UNIT:FindByName( UnitName )
		local RouteGroup = GROUP:FindByName( UnitName )
			-- NEW
		-- RouteGroup = Get Group by Name from Route List (So Route List is a Group List)
		-- RouteUnit  = Get First Unit in Group as the spawn unit (position where the cloned group will spawn)

			-- Check to see if we have a _client
		client_txt = "<MISSION cmd>"
		if _data._client ~= nil then
			client_txt = "" .. _data._client:GetName()
		end

		env.info( 
			"SpawnControl_spawn: Call from client " .. client_txt .. 
			" requesting spawn of " .. _data._txt .. "[" .. _data._grp .. ":"  .. _data._idx .. "]" ..
			" to route unit " .. UnitName .. "[" .. idx .."]" ..
			" - spawn obj ref: " .. _data._spawnobj.SpawnTemplatePrefix)

		if RouteUnit ~= nil and RouteGroup ~= nil then
			local RouteRef = RouteGroup:GetTaskRoute()
			if RouteRef ~= nil then
				if _data._spawnobj ~= nil then
						-- Spawn From Unit (clone from unit ref) and assign the route from the Group that contains the Route Unit
     					local spawn_ref = _data._spawnobj:SpawnFromUnit( RouteUnit ):Route( RouteRef )

					if spawn_ref ~= nil then
						env.info( "SpawnControl_spawn: Call completed with no errors." )
						if _data._client ~= nil then
								-- If there is a client, send a client message
  								_data._client:Message( 
								"Group Spawned via call from: " .. client_txt .. 
								" - Spawned Group " .. _data._txt .. " (" .. _data._spawnobj.SpawnTemplatePrefix .. ")" ..
								" to route " .. _data._routelist[idx], 15 )
						else
								-- Else send a general message
  								MESSAGE:New( 
								"Group Spawned via call from: " .. client_txt .. 
								" - Spawned Group " .. _data._txt .. " (" .. _data._spawnobj.SpawnTemplatePrefix .. ")" ..
								" to route " .. _data._routelist[idx], 15 ):ToAll()
						end
					else
						MESSAGE:New( "Failed to Spawn Group; spawn_ref empty after spawn req. Request from: " .. client_txt, 15 ):ToAll()
						env.warning( "Failed to Spawn Group; spawn_ref empty after spawn req. Request from: " .. client_txt )
					end
				else
					MESSAGE:New( "Failed to access spawn obj (from menu call data). Request from: " .. client_txt, 15 ):ToAll()
					env.warning( "Failed to access spawn obj (from menu call data). Request from: " .. client_txt )
				end
			else
				MESSAGE:New( "Failed to get ref to route: " .. RouteUnit .. ". Request from: " .. client_txt, 15 ):ToAll()
				env.warning( "Failed to get ref to route: " .. RouteUnit .. ". Request from: " .. client_txt )
			end
		else
			MESSAGE:New( "Failed to get ref to route unit or route group: " .. _data._routelist[ idx ] .. ". Request from: " .. client_txt, 15 ):ToAll()
			env.warning( "Failed to get ref to route unit or route group: " .. _data._routelist[ idx ] .. ". Request from: " .. client_txt )
		end
	else
		MESSAGE:New( "Failed to Spawn Group; no _data from menu call.  Request from: " .. client_txt, 15 ):ToAll()
		env.warning( "Failed to Spawn Group; no _data from menu call.  Request from: " .. client_txt )
	end
 	end


local function SpawnControl_check ()

	local msg = "Active Spawns:\n"

	for idx,val in pairs (Passive_TargetGroups) do
		local GroupPlane, Index = val.spawn:GetFirstAliveGroup()
		while GroupPlane ~= nil do
			msg = msg .. "P:" .. idx .. ":" .. Index .. "-" .. GroupPlane.GroupName .. "\n"
 				GroupPlane, Index = val.spawn:GetNextAliveGroup( Index )
		end
	end

	for idx,val in pairs (Active_TargetGroups) do
		local GroupPlane, Index = val.spawn:GetFirstAliveGroup()
		while GroupPlane ~= nil do
			msg = msg .. "A:" .. idx .. ":" .. Index .. "-" .. GroupPlane.GroupName .. "\n"
 				GroupPlane, Index = val.spawn:GetNextAliveGroup( Index )
		end
	end
	MESSAGE:New( msg, 15 ):ToAll()
end

--[[
--]]
local grp = "B1"
env.info( "SpawnControl: Adding MENU_MISSION commands" )
   local MenuBSpawn          = MENU_MISSION:New( "Flight Lead Commands" )
   local MenuBSpawnPassive   = MENU_MISSION:New( "Spawn Passive Group", MenuBSpawn)
   local MenuBSpawnActive    = MENU_MISSION:New( "Spawn Active Group", MenuBSpawn)
local MenuBSpawnPositions = MENU_MISSION_COMMAND:New( "Debug: dump route unit positions", MenuBSpawn, SpawnControl_GetRoutePos, nil )
local MenuBSpawnPositions = MENU_MISSION_COMMAND:New( "Debug: dump spawned list", MenuBSpawn, SpawnControl_check, nil )
if MenuBSpawnPassive then
	for idx,val in pairs (Passive_TargetGroups) do
		MENU_MISSION_COMMAND:New( 
				idx .. ": " .. val.txt,  
				MenuBSpawnPassive,  
				SpawnControl_spawn, 
				{ _client = nil, _spawnobj =  val.spawn,  _routelist = Passive_TargetRoutes,  _txt = idx .. ": " .. val.txt,  _grp = grp,  _idx = idx } 
		)
	end
end

grp ="B2" 
if MenuBSpawnActive then
	for idx,val in pairs (Active_TargetGroups) do
		MENU_MISSION_COMMAND:New( 
				idx .. ": " .. val.txt,  
				MenuBSpawnActive,  
				SpawnControl_spawn, 
				{ _client = nil, _spawnobj =  val.spawn,  _routelist = Active_TargetRoutes,  _txt = idx .. ": " .. val.txt,  _grp = grp,  _idx = idx } 
		)
	end
end
--[[
--]]

--[[
SCHEDULER:New( nil,
   function()
   	local PlaneClient = CLIENT:FindByName( "Trainer #001" )
	local grp = "C1"
	env.info( "SpawnControl: Refreshing Comms Menu for " .. tostring(PlaneClient:GetName()))
     	if PlaneClient and PlaneClient:IsAlive() then
       	local MenuCSpawn          = MENU_CLIENT:New( PlaneClient, "Flight Lead Commands" )
       	local MenuCSpawnPassive   = MENU_CLIENT:New( PlaneClient, "Spawn Passive Group", MenuCSpawn)
       	local MenuCSpawnActive    = MENU_CLIENT:New( PlaneClient, "Spawn Active Group", MenuCSpawn)
		local MenuCSpawnPositions = MENU_CLIENT_COMMAND:New( PlaneClient, "Debug: dump route unit positions", MenuCSpawn, SpawnControl_GetRoutePos, nil )
		local MenuCSpawnPositions = MENU_CLIENT_COMMAND:New( PlaneClient, "Debug: dump spawned list", MenuCSpawn, SpawnControl_check, nil )
		if MenuCSpawnPassive then
			for idx,val in pairs (Passive_TargetGroups) do
				MENU_CLIENT_COMMAND:New( 
						PlaneClient,  
						idx .. ": " .. val.txt,  
						MenuCSpawnPassive,  
						SpawnControl_spawn, 
						{ _client = PlaneClient, _spawnobj =  val.spawn,  _routelist = Passive_TargetRoutes,  _txt = idx .. ": " .. val.txt,  _grp = grp,  _idx = idx } 
				)
			end
		else
  	  			PlaneClient:Message( "Unable to add Passive Comms Menu for Client: " .. tostring(PlaneClient:GetName()), 15 )
		end

		grp ="C2" 
		if MenuCSpawnActive then
			for idx,val in pairs (Active_TargetGroups) do
				MENU_CLIENT_COMMAND:New( 
						PlaneClient,  
						idx .. ": " .. val.txt,  
						MenuCSpawnActive,  
						SpawnControl_spawn, 
						{ _client = PlaneClient, _spawnobj =  val.spawn,  _routelist = Active_TargetRoutes,  _txt = idx .. ": " .. val.txt,  _grp = grp,  _idx = idx } 
				)
			end
		else
  	  			PlaneClient:Message( "Unable to add Active Comms Menu for Client: " .. tostring(PlaneClient:GetName()), 15 )
		end
	end
   end, {}, 10, 20 )
--]]

end

Fridge

----------

Things which do you no good in aviation:

1) Altitude above you;

2) Runway behind you;

3) Fuel in the truck;

4) The airspeed you don't have.

Link to comment
Share on other sites

It's a way around the multiplayer issue of creating a menu for a client that has not logged in (ie: nobody is occupying the slot). If no one is occupying the slot, you can not (I think) set the menu for that particular client. You can use the global menu (ie: game, or coalition) no problem.

 

That means if someone was to join that slot with the mission already in progress their comms menu would not be complete. A hack around this is to rebuild the menu every little while. I am thinking that there may be a way to set the menu once at the mission start and then detect players joining with the ON_BIRTH event to set the menu at that point - to get around both issues but I have not had time to try this in/with MOOSE.

Fridge

----------

Things which do you no good in aviation:

1) Altitude above you;

2) Runway behind you;

3) Fuel in the truck;

4) The airspeed you don't have.

Link to comment
Share on other sites

I see but I think FC has added events to Moose recently so you can likely hook into when a client connects and then build the menu once then. Ask FC or look at the latest Event demo missions.

 

It's a way around the multiplayer issue of creating a menu for a client that has not logged in (ie: nobody is occupying the slot). If no one is occupying the slot, you can not (I think) set the menu for that particular client. You can use the global menu (ie: game, or coalition) no problem.

 

That means if someone was to join that slot with the mission already in progress their comms menu would not be complete. A hack around this is to rebuild the menu every little while. I am thinking that there may be a way to set the menu once at the mission start and then detect players joining with the ON_BIRTH event to set the menu at that point - to get around both issues but I have not had time to try this in/with MOOSE.

Link to comment
Share on other sites

Mission explained by Gunterlund

 

Would like to share with you a very nice video of Gunterlund.

 

 

He has taken (a lot of) his time to explain you one of his missions, wherein he applies lua mission scripting using the MOOSE framework.

 

His mission has become a dynamic Ground Offensive, where he focuses on the CA module utilization of DCS World.

 

If you're into mission design, I really think you should have a careful look at his work; please take your time watching this video. He walks you through the mission design, explains his lua code that he applied.

 

As a result, you get a comprehensive understanding how to approach a larger mission using MOOSE.

 

Let's thank Gunterlund for his work done here, and hope you enjoy the explanation.

 

FC

[TABLE][sIGPIC][/sIGPIC]|

[/TABLE]

Link to comment
Share on other sites

Excellent video:

 

Would like to share with you a very nice video of Gunterlund.

 

 

He has taken (a lot of) his time to explain you one of his missions, wherein he applies lua mission scripting using the MOOSE framework.

 

His mission has become a dynamic Ground Offensive, where he focuses on the CA module utilization of DCS World.

 

If you're into mission design, I really think you should have a careful look at his work; please take your time watching this video. He walks you through the mission design, explains his lua code that he applied.

 

As a result, you get a comprehensive understanding how to approach a larger mission using MOOSE.

 

Let's thank Gunterlund for his work done here, and hope you enjoy the explanation.

 

FC

Link to comment
Share on other sites

I'm discovering MOOSE, and I got to say I'm quite impressed by the automation that it seems to bring, leaving the micromanaging of units to the code.

 

Although the documentation is very well done, some details still aren't clear to me.

1. When is my lua script executed ? At the beginning of the mission ? Or is it running in parallel with the mission ? Is it executed every frame ? Every second ?

2. Let's say I want the player aircraft to explode when he reaches 3000ft, using trigger.action.explosion() on his coordinates. As I understand it there is no way to do it. I would need to use a Mission Editor Triggers. Correct ?

3. Is there a way to wait ? Let's say I want a smoke to spawn at the crash of the player (EVT-104 - OnEventCrash Example), but 10 seconds after the crash. Is there a way to do that ?

4. Is there a way to interact with FLAGS set up in the Mission Editor ? Do they generate an event that may be caught by the event handler, for example ?

5. Am I blind or trigger.action.explosion() isn't wrapped in MOOSE, while trigger.action.smoke() and trigger.action.signalFlare() are ? Is there a specific reason or is it just an overlook / WIP ?

 

Thank you for taking the time to read me and for your answer !

Intel i5-2500k - Gigabyte P67X-UD3-B3 - AMD HD7950 - RAM 12Go - SSD 250Go - Acer FHD Screen - Logitech G940 - TIR5 - FC3 - M-2000C - A-10C

[sIGPIC][/sIGPIC]

 

Link to comment
Share on other sites

@GreyEcho, sorry for my late answer.

 

I'm discovering MOOSE, and I got to say I'm quite impressed by the automation that it seems to bring, leaving the micromanaging of units to the code.

 

Thanks to all and those who have helped making and testing it.

 

 

Although the documentation is very well done, some details still aren't clear to me.

 

That is normal. I suggest you have a look at the links at my signature or send me a PM with your email, so I can add you to the moose community on slack.com. It is easier to interact on slack than through this forum.

 

1. When is my lua script executed ? At the beginning of the mission ? Or is it running in parallel with the mission ? Is it executed every frame ? Every second ?

 

You can use MOOSE anywhere within your DCS mission.

 

Forget the trigger system in DCS. MOOSE works differently. You will hardly need the trigger system in DCS if you use the moose framework. You won't need flags, switches and other means to build logic. You can use lua to code your mission logic directly in the script. That makes it easier for you to follow how your mission works and what happens when ...

 

Create one big lua file and include that lua file into your mission .miz file, by creating a trigger in the Mission Editor that will run at Mission Start in a DO SCRIPT FILE ...

 

That lua script file contains all the declarations of the local and global objects, variables, event handlers, schedulers and other mechanisms that the moose framework provides...

 

Then there are a couple of options that you have:

 

1. You can try to build all your mission logic into that one big lua file. You keep the mission defined in the mission editor "clean" from any scripting.

 

2. You can use the global objects declared in the lua file at certain scripting points within the mission editor. For example, at a waypoint or at trigger DO SCRIPT logic.

 

The goal of moose is to achieve point #1, but for the moment it is still sometimes required to also do point #2 :-)

 

So think of moose as a toolbox that you can use to:

- Spawn stuff

- Handle DCS events

- Schedule events within time

- Score your achievements

- Define and use zone logic

- Create menus and embed commands at menus

- Send text messages to various players with various conditions

- Build finite state machines

- Detect targets

- Build logical missions for players

- Create tasks in missions for players

- Perform AI "processes" like CAP, PATROL, CAS that are much richer than the DCS default role tasks.

- Execute various tasks using WRAPPER classes of the DCS object API.

- Build dynamic SETs of objects that you can use to do batch asynchronous processing on these objects.

- ... and build a mission "flow".

 

Note that you'll recognise the same functionality available using the DCS api, but MOOSE has built around this API a much more rich functional set of objects.

 

2. Let's say I want the player aircraft to explode when he reaches 3000ft, using trigger.action.explosion() on his coordinates. As I understand it there is no way to do it. I would need to use a Mission Editor Triggers. Correct ?

 

Just added, the POINT_VEC3 and derived POINT_VEC2 classes now also contain Smoke, Flare and Explosion and IlluminationBomb methods.

Heres an example how to code an explosion right near your plane:

 

local PlaneUnit = UNIT:FindByName( "Plane" ) -- This can be also other logic, i just need the plane UNIT object!
local PointVec3 = PlaneUnit:GetPointVec3() -- This creates a POINT_VEC3 object at the location of the PlaneUnit
PointVec3:Explosion( 100 ) -- Explode near the plane with an intensity of 100.

 

3. Is there a way to wait ? Let's say I want a smoke to spawn at the crash of the player (EVT-104 - OnEventCrash Example), but 10 seconds after the crash. Is there a way to do that ?

 

Yes, sure, the trick here is to schedule a logic to be executed using the SCHEDULER class.

 

Heres an example, I just copied the code from the test mission and added stuff in red:

 

-- Create a variable PlaneHuman that holds a reference to UNIT object (created by moose at the beginning of the mission) with the name "PlaneHuman".
local PlaneHuman = UNIT:FindByName( "PlaneHuman" )

-- Subscribe to the event Crash. The Crash event occurs when a plane crashes into the ground (or into something else).
PlaneHuman:HandleEvent( EVENTS.Crash )

-- Because the PlaneHuman object is subscribed to the Crash event, the following method will be automatically
-- called when the Crash event is happening FOR THE PlaneHuman UNIT only!

--- @param self
-- @param Core.Event#EVENTDATA EventData
function PlaneHuman:OnEventCrash( EventData )

-- But delay with 10 seconds!
[color=Red]local PointVec3 = EventData.IniUnit:GetPointVec3()[/color]
[color=Red]SCHEDULER:New( self, 
 function()

   BASE:E( "Smoking at the position" )
   EventData.IniUnit:SmokeOrange()
 end, {}, [b]10[/b]
)
[/color]

 

4. Is there a way to interact with FLAGS set up in the Mission Editor ? Do they generate an event that may be caught by the event handler, for example ?

 

Can you give me an example what you want to do or achieve?

 

5. Am I blind or trigger.action.explosion() isn't wrapped in MOOSE, while trigger.action.smoke() and trigger.action.signalFlare() are ? Is there a specific reason or is it just an overlook / WIP ?

 

I just added the Explosion and IlluminationBomb logic at the MOOSE framework, under POINT_VEC3. Sync in github and you should have it.

 

Thank you for taking the time to read me and for your answer !

 

My pleasure, and feel free to ask further questions if any.

FC


Edited by FlightControl
  • Like 1

[TABLE][sIGPIC][/sIGPIC]|

[/TABLE]

Link to comment
Share on other sites

Thank you for the detailed answer that clears up quite a bit! I sent you a PM.

Intel i5-2500k - Gigabyte P67X-UD3-B3 - AMD HD7950 - RAM 12Go - SSD 250Go - Acer FHD Screen - Logitech G940 - TIR5 - FC3 - M-2000C - A-10C

[sIGPIC][/sIGPIC]

 

Link to comment
Share on other sites

Hey FlightControl! Great work BTW!

 

Now the questions :-)

 

I am trying to task an air group to go to a given zone with the CONTROLLABLE:TaskRouteToZone() function. But ... it does not seem to work for air groups, only ground groups.

 

Is this as intended? Are many more of the CONTROLLABLE class functions only usable by ground groups?

 

In my case, this is what I am getting when I use the following code:

 

do

-- Set up the pool of spawnables based on the RED #001 group
PatrolSpawnPool = SPAWN:New("RED #001"):InitRandomizeRoute( 1, 0, 50 )
--PatrolSpawnPool = SPAWN:New("RED #002"):InitRandomizeRoute( 1, 0, 50 )

if PatrolSpawnPool == nil then
	env.error ("DVD: PatrolSpawn is nil - the pool of groups we are spawning from.")
end

-- Grab the group whose path represents the 'polygon'
PatrolZoneGroupRef = GROUP:FindByName( "PolygonZone #001" )

if PatrolZoneGroupRef == nil then
	env.error ("DVD: PatrolZoneGroup is nil - Could not find the PolygoneZone #001 group.")
end

-- Create the ZONE_POLYGON based on that group
--[[
PatrolZoneRef = ZONE_POLYGON:New( "Zone #001", PatrolZoneGroupRef )

if PatrolZoneRef == nil then
	env.error ("DVD: PatrolZone is nil - Did not create zone from the Patrol Zone Group.")
end
--]]

PatrolZoneRef = ZONE:New( "PatrolZone #001" )

if PatrolZoneRef == nil then
	env.error ("DVD: PatrolZone is nil - Did not create zone from the Trigger Zone.")
end

-- Spawn the instance from the spawn pool
PatrolGroupRef = PatrolSpawnPool:Spawn()

if PatrolGroupRef == nil then
	env.error ("DVD: PatrolGroup is nil - The group was not spawned.")
end

-- Assign the route
PatrolGroupRef:TaskRouteToZone (PatrolZoneRef, true, 500, "Cone")

env.info ("DVD: Patrol Group has been sent to the Patrol Zone Group zone.")
end

 

This is using the code from GitHub as of last week.

 

The error I am getting is the following:

 

03876.854 ERROR   DCS: Mission script error: : [string "C:\Users\penney\AppData\Local\Temp\DCS\/~mis00002DCF"]:15733: attempt to index local 'UnitPoint' (a nil value)
stack traceback:
[C]: ?
[]:15733: in function 'GetVec2'
[]:14611: in function 'TaskRouteToZone'
[]:40: in main chunk

 

In my test mission, RED #001 is an aircraft and RED#002 is a ground unit. If I use RED #002 it works and if I used RED #001 it does not.

 

Also ... a general call to all MOOSE developers. Is anyone using those CONTROLLABLE functions to create routes for air units because I would love to see some example code that implements those calls :-)

 

Thanks for working on this framework FlightControl! The potential is huge! I have been working on making some dynamic missions for helicopters and fixed-wing aircraft and as I am learning the framework, a lot of the tediousness of working in Lua has been taken away!

Fridge

----------

Things which do you no good in aviation:

1) Altitude above you;

2) Runway behind you;

3) Fuel in the truck;

4) The airspeed you don't have.

Link to comment
Share on other sites

Hey FlightControl! Great work BTW!

 

My pleasure! I enjoy making this framework, but just know that it is not a one man show.

Currently other people are helping out with the code too! And sharing ideas etc.

Join the club I would say!

 

Now the questions :-)

 

Let me see if I can help answering them.

 

I am trying to task an air group to go to a given zone with the CONTROLLABLE:TaskRouteToZone() function. But ... it does not seem to work for air groups, only ground groups.

 

Is this as intended? Are many more of the CONTROLLABLE class functions only usable by ground groups?

 

In my case, this is what I am getting when I use the following code:

 

Okay, let me give some context to all this.

CONTROLLABLE is an abstract class that implements methods to control anything that is "controllable" in DCS World. Currently, there are two kinds of objects "controllable" in DCS world: Units and Groups. Controllable provides APIs to make these controllable objects move around and perform tasks within your mission. It hides the complexity for MDs having to use a low-level "Controllable" class as documented in the DCS API, and having to make these "tables" to set tasks. Instead CONTROLLABLE provides you (as you've seen) with methods that are intellisensed to created tasks etc.

 

CONTROLLABLE is a base class. That means, it is not meant to be used directly by mission designers, but it is inherited by the GROUP and UNIT wrapper classes in MOOSE. Instead of using the CONTROLLABLE class directly, you should use the GROUP wrapper class or the UNIT wrapper class to control a group or unit respectively.

 

 

do

   -- Set up the pool of spawnables based on the RED #001 group
   PatrolSpawnPool = SPAWN:New("RED #001"):InitRandomizeRoute( 1, 0, 50 )
   --PatrolSpawnPool = SPAWN:New("RED #002"):InitRandomizeRoute( 1, 0, 50 )

   if PatrolSpawnPool == nil then
       env.error ("DVD: PatrolSpawn is nil - the pool of groups we are spawning from.")
   end

   -- Grab the group whose path represents the 'polygon'
   PatrolZoneGroupRef = GROUP:FindByName( "PolygonZone #001" )

   if PatrolZoneGroupRef == nil then
       env.error ("DVD: PatrolZoneGroup is nil - Could not find the PolygoneZone #001 group.")
   end

   -- Create the ZONE_POLYGON based on that group
   --[[
   PatrolZoneRef = ZONE_POLYGON:New( "Zone #001", PatrolZoneGroupRef )

   if PatrolZoneRef == nil then
       env.error ("DVD: PatrolZone is nil - Did not create zone from the Patrol Zone Group.")
   end
   --]]

   PatrolZoneRef = ZONE:New( "PatrolZone #001" )

   if PatrolZoneRef == nil then
       env.error ("DVD: PatrolZone is nil - Did not create zone from the Trigger Zone.")
   end

   -- Spawn the instance from the spawn pool
   PatrolGroupRef = PatrolSpawnPool:Spawn()

   if PatrolGroupRef == nil then
       env.error ("DVD: PatrolGroup is nil - The group was not spawned.")
   end

   -- Assign the route
   PatrolGroupRef:TaskRouteToZone (PatrolZoneRef, true, 500, "Cone")

   env.info ("DVD: Patrol Group has been sent to the Patrol Zone Group zone.")
end

 

I see that you've learned a lot from reading the documentation and looking at the example missions. However, a couple of remarks to your code, if you allow me:

 

Suggestion #1: I think you don't need to do the error handling. That is done in the MOOSE classes for you (it should). If not, I need to ensure that this is properly done.

Also, you can then embed or nest these calls...

 

So the code that you've written:

 

    -- Grab the group whose path represents the 'polygon'
   PatrolZoneGroupRef = GROUP:FindByName( "PolygonZone #001" )

   if PatrolZoneGroupRef == nil then
       env.error ("DVD: PatrolZoneGroup is nil - Could not find the PolygoneZone #001 group.")
   end

   -- Create the ZONE_POLYGON based on that group
   --[[
   PatrolZoneRef = ZONE_POLYGON:New( "Zone #001", PatrolZoneGroupRef )

   if PatrolZoneRef == nil then
       env.error ("DVD: PatrolZone is nil - Did not create zone from the Patrol Zone Group.")
   end
   --]]

 

could simply be rewritten as:

 

PatrolZoneRef = ZONE_POLYGON:New( "Zone #001", GROUP:FindByName( "PolygonZone #001" ) )

 

Much shorter, isn't it?

 

Suggestion #2: The POINT_VEC3 and POINT_VEC2 classes implement methods to create AIR and GROUND waypoints...

 

The method you used PatrolGroupRef:TaskRouteToZone (PatrolZoneRef, true, 500, "Cone") is a something quirky that I wrote about a year ago.

 

Instead, a much better design has emerged that I should ask you to use from now on:

 

POINT_VEC3 and POINT_VEC2 are classes that manage 3D points in space. These classes provide you with a multitude of methods that allow you to "stuff" with a point in 3D space.

One of the things you can do is to create waypoints that you can then embed in a route of a CONTROLLABLE (GROUP or UNIT).

 

There is a method declared to manage AIR waypoints:

--- Build an air type route point.
-- @param #POINT_VEC3 self
-- @param #POINT_VEC3.RoutePointAltType AltType The altitude type.
-- @param #POINT_VEC3.RoutePointType Type The route point type.
-- @param #POINT_VEC3.RoutePointAction Action The route point action.
-- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h.
-- @param #boolean SpeedLocked true means the speed is locked.
-- @return #table The route point.
function POINT_VEC3:RoutePointAir( AltType, Type, Action, Speed, SpeedLocked )

 

And there is a method declared to manage GROUND waypoints:

--- Build an ground type route point.
-- @param #POINT_VEC3 self
-- @param Dcs.DCSTypes#Speed Speed Speed in km/h.
-- @param #POINT_VEC3.RoutePointAction Formation The route point Formation.
-- @return #table The route point.
function POINT_VEC3:RoutePointGround( Speed, Formation )

 

So, your code

PatrolGroupRef:TaskRouteToZone (PatrolZoneRef, true, 500, "Cone")

could be rewritten as:

   local PointVec2 = POINT_VEC2:NewFromVec2( PatrolZoneRef:GetVec2(), 2000 ) -- Returns a POINT_VEC2 object with X, Y coordinates and a height of 2000 meters.
   local ToAirWayPoint = PointVec2:RoutePointAir( POINT_VEC3.RoutePointAltType.BARO, POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, 2000, true )
   
   local CurrentAirWayPoint = PatrolGroupRef:GetPointVec3():RoutePointAir( POINT_VEC3.RoutePointAltType.BARO, POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, 2000, true )
   
   PatrolGroupRef:Route( {CurrentAirWayPoint, ToAirWayPoint } )

 

The seems a bit larger, yes. However, the code can still be optimized and the MOOSE API can still be optimized. F.e., there should be a method that just takes an AIR waypoint and routes to that AIR waypoint from the Current AIR waypoint. But that requires some effort in MOOSe and I currently have other priorities.

 

Hope it helps and thanks for your PM. I will take care of it.

[TABLE][sIGPIC][/sIGPIC]|

[/TABLE]

Link to comment
Share on other sites

Hello,

 

I can try to join in slack channel, but i need that the administrator sign up my email, can you help me pls! ty

 

Great work with moose!!

 

Please send me a private message here on this ED forum. Once received, I can send you a slack.com invitation to join the moose community on slack.

 

thanks for your interest!

FC

[TABLE][sIGPIC][/sIGPIC]|

[/TABLE]

Link to comment
Share on other sites

On a regular basis I get hints that people are having difficulties setting up the LDT environment correctly. This is understandable, as LDT is a bit intimidating in the beginning. If such is the case, just contact us for help. We can sort it out easily within the team.

 

Fc

[TABLE][sIGPIC][/sIGPIC]|

[/TABLE]

Link to comment
Share on other sites

Task Dispatching

 

Find this new video providing a demonstration prologue of the Task Dispatching mechanism done by a command center, that allows for dynamic task creation upon detected targets by FACs and RECCEs.

 

The demo explains the code and mission outlines, and then a demo flying a ka-50 executing one of these tasks assigned ...

 

Hope you enjoy it ... This functionality will be released very soon within the framework for mission designers.

 

 

cheers,

FC

[TABLE][sIGPIC][/sIGPIC]|

[/TABLE]

Link to comment
Share on other sites

Release 1.1.0

 

Release 1.1.0 has been published here:

 

https://github.com/FlightControl-Master/MOOSE/releases/tag/v1.1.0

 

Please check. It contains the following additions:

 

Implements the NEW improved TASKING SYSTEM within MOOSE

 

  • Enhanced detection with various detection grouping methods. Per Type, Distance, Area, Unit, ...
  • Improved Tasking. Tasking for targets destruction has only one mandatory parameter: A set of targets.
  • Task classes have option methods, to vary the process of the task.
  • Dispatcher can now dispatch tasks based on various detection methods.
  • Escort is fixed.
  • This framework now provides the baseline for an improved GCI/CAP tasking system and other systems developed within MOOSE ...

Important classes to check out in this release:

 

  1. A command center coordinates missions:
    COMMANDCENTER
  2. A mission groups tasks and has a goal.
    MISSION
  3. Plan tasks for humans:
    TASK (base class)
    TASK_A2G (base class for A2G)
    TASK_SEAD
    TASK_BAI
    TASK_CAS
  4. Handle advanced detection using:
    DETECTION_UNITS
    DETECTION_TYPES
    DETECTION_AREAS
  5. Automatically dispatch tasks based on detected enemy targets by recces:
    TASK_A2G_DISPATCHER
  6. Score your achievements:
    SCORING
  7. Other changes:
    EVENT is improved
    SCHEDULER is improved

The importance of the release is not the classes itself, but the underlying structures that allow these classes to function. More classes can be created now in MOOSE to do advanced stuff.

Test missions have been added in the following categories:

 

  • ESC - Escorting
  • SCO - Scoring
  • TAD - Task Dispatching

More test missions will be added in the future.

 

 

Enjoy.

FC

  • Like 1

[TABLE][sIGPIC][/sIGPIC]|

[/TABLE]

Link to comment
Share on other sites

Hi! I have two Moose related questions and was hoping one of you veterans could help.

 

1. How do I run a funcion when a group object dies? I've come so far as to understand that I need to use GROUP:IsAlive() in a dead event, but I can't figure out how to put it together.

 

2. Can I use UNIT:GetAmmo() te get a text message to the player of a unit, or even better groups current ammo state?

 

Bonus question: I realize both of my questions are related to me not understanding what IsAlive() and GetAmmo() returns, is it possible to get what they return printed in the log? If so, how?

 

Thanks alot in advance!

Link to comment
Share on other sites

Hi, Im testing the TASK_A2G_DISPATCHER and it has the debugguing messages active.

The debug messages are automatically appearing if you have tracing activated.

Issue a BASE:TraceOnOff( false ) at the start of your mission, and the messages should not display.

 

Hope this is okay for you. Otherwise, I'll have to build something to set these messages on and off separately (which I can do).

 

FC

[TABLE][sIGPIC][/sIGPIC]|

[/TABLE]

Link to comment
Share on other sites

Hi! I have two Moose related questions and was hoping one of you veterans could help.

 

1. How do I run a funcion when a group object dies? I've come so far as to understand that I need to use GROUP:IsAlive() in a dead event, but I can't figure out how to put it together.

 

2. Can I use UNIT:GetAmmo() te get a text message to the player of a unit, or even better groups current ammo state?

 

Bonus question: I realize both of my questions are related to me not understanding what IsAlive() and GetAmmo() returns, is it possible to get what they return printed in the log? If so, how?

 

Thanks alot in advance!

 

When you process a dead event (like this):

 

GroupObject = GROUP:FindByName(...)
GroupObject:HandleEvent( EVENTS.Dead )

function GroupObject:OnEventDead( EventData )
 <logic>
end

then you'll see that the OnEventDead is fired for each unit in that group.

I've designed it like this as dead events are triggered on units, not on groups. But, only those units belonging to the group, will trigger this event handler. Other units, not belonging to this group, will not trigger this event handler.

 

Now if you want to test if the group is dead or not, you can use the IsAlive() method. The GROUP:IsAlive() method is an improved version from the Group:isExist() method of the DCS API (which is bugged for about 2 years now). So the GROUP:IsAlive() method contains a workaround for a bug.

The IsAlive() method of the GROUP wrapper object checks if the underlying Group exists, and if it has one unit. If it has one unit, the group is alive, if it has zere units, the group is not alive. Thus the IsAlive() method does what the Group:isExist() method should be doing of DCS.

 

You can use the GROUP:IsAlive() method in the OnEventDead event handler like this:

 

function GroupObject:OnEventDead( EventData )
 if not EventData.IniGroup:IsAlive() then
   <logic>
 end
end

Hope it helps... (also for others).

 

I'll check on slack your problem with GetAmmo() because that may need another discussion :-)

 

FC

[TABLE][sIGPIC][/sIGPIC]|

[/TABLE]

Link to comment
Share on other sites

Release 1.1.0 has been published here:

 

https://github.com/FlightControl-Master/MOOSE/releases/tag/v1.1.0

 

 

Great efford!

 

Sadly, I have to find the time to dive into MOOSE, yet, but it is looking really sophisticated. So this leaves me with a more general question: how customizable are all the communication messages - i.e. from the command center or the task dispatchers? Two points I have in mind here in particular: 1. does the comms utilize the aircraft's radios or is it all done by MESSAGE TO xxx? I'd love to see missions with "realistic" comms reqirements - like, if you have dialled in the wrong frequency, you won't hear the task dispatcher and thus you will be of no help to the war ... ;-)

 

The other point is, aside from the debugging output, the tasking messages look somewhat "technical". Efficient, but maybe not too realistic in terms of how the real communication would go. Can the wording of those messages be customized in some way?

Link to comment
Share on other sites

The debug messages are automatically appearing if you have tracing activated.

Issue a BASE:TraceOnOff( false ) at the start of your mission, and the messages should not display.

 

Hope this is okay for you. Otherwise, I'll have to build something to set these messages on and off separately (which I can do).

 

FC

 

Oh! I didn't know sorry. I deactivated the trace and now its working fine!!

 

Thanks.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...