Jump to content
_Clockwerk

[SOLUTION] Create player markers and update location every 10 seconds

Recommended Posts

So I've been working on my first hand-written script to place markers on the map where players are every 10 seconds, with a line drawn between the squad members and the squad leader.

 

I was having some trouble with undefined variables but just making the variables private within the script got rid of that error. Now I'm stuck with a perpetual missing semi-colon error which tells me that my script is just wrong somewhere.

 

I'm very new to this and I've been scouring the examples on the biki, but just can't seem to get this to work. I can't tell if I'm just doing things out of order or defining variables in the wrong place etc., etc. ... Bottom line: I'm lost.

 

If anyone could just point out where I might be going wrong that would help.

 

And yes I realize that the game already has map markers, just humor my ridiculousness if you will.

 

//////////////Definitions//////////////
private p1 = zCurator;
private p2 = ar;
private p3 = gren;
private p4 = at;
_p1POS = getpos p1;
_p2POS = getpos p2;
_p3POS = getpos p3;
_p4POS = getpos p4;
createMarker ["mkrPos1", _p1POS];
createMarker ["mkrPos2", _p2POS];
createMarker ["mkrPos3", _p3POS];
createMarker ["mkrPos4", _p4POS];
_p1Array = [p1, "mkrPos1", _p1POS];
_p2Array = [p2, "mkrPos2", _p2POS];
_p3Array = [p3, "mkrPos3", _p3POS];
_p4Array = [p4, "mkrPos4", _p4POS];
_delta = [p1,p2,p3,p4];
_deltaUnder = [p2,p3,p4];

//////////////Draw black lines between squad members and squad leader//////////////

{
while {alive p1} do {
(_this select 0) drawLine [_x,p1,[0,0,0,1]];
		sleep 10;
};
} forEach _deltaUnder;

//////////////Create and set approximate locations of players//////////////

_posMarkers = ["mkrPos1","mkrPos2","mkrPos3","mkrPos4"];

{
	_x setMarkerType "mil_dot_noShadow";
	_x setMarkerColor "#(0,0,1,1)";
} forEach _posMarkers;

//////////////Update player location markers every 10 seconds//////////////
{
while {alive _x select 0} do {
	(_x select 1) setMarkerPos (_x select 2);
	sleep 10;
};
} forEach [_p1Array, _p2Array, _p3Array, _p4Array];

 

EDIT: Spent the 6 hours between this post and now trying to diagnose this. I managed to clear all errors after about an hour, but the script still wasn't working. I found drawLine to be a lost cause to I just settled for map icons that update every 10 seconds. Here's the code if anyone is interested. p1-p4 assignments are just the variable names I gave to each of my squad members (Squad Lead, Auto Rifleman, Grenadier, Anti-Tank).

 

_p1 = zCurator;
_p2 = ar;
_p3 = gren;
_p4 = at;
_p1POS = getPos _p1;
_p2POS = getPos _p2;
_p3POS = getPos _p3;
_p4POS = getPos _p4;
_mkr1 = createMarker ["mkrPos1", _p1POS];
_mkr2 = createMarker ["mkrPos2", _p2POS];
_mkr3 = createMarker ["mkrPos3", _p3POS];
_mkr4 = createMarker ["mkrPos4", _p4POS];
_posMarkers = [_mkr1,_mkr2,_mkr3,_mkr4];

	{
	_x setMarkerType "mil_dot_noShadow";
	_x setMarkerShape "ICON";
	_x setMarkerColor "ColorBlue";
	} forEach _posMarkers;

sleep 2;
	
	while {isServer} do {
	_mkr1 setMarkerPos (getPos _p1);
	_mkr2 setMarkerPos (getPos _p2);
	_mkr3 setMarkerPos (getPos _p3);
	_mkr4 setMarkerPos (getPos _p4);
	sleep 10;
	};

 

 

EDIT 2: Noticed that the script was stopping after respawn or death, referencing the players' positions as an array seems to have fixed that.

 

_p1 = zCurator;
_p2 = ar;
_p3 = gren;
_p4 = at;
_p1POS = getPos _p1;
_p2POS = getPos _p2;
_p3POS = getPos _p3;
_p4POS = getPos _p4;
_mkr1 = createMarker ["mkrPos1", _p1POS];
_mkr2 = createMarker ["mkrPos2", _p2POS];
_mkr3 = createMarker ["mkrPos3", _p3POS];
_mkr4 = createMarker ["mkrPos4", _p4POS];
_posMarkers = [_mkr1,_mkr2,_mkr3,_mkr4];

	{
	_x setMarkerType "mil_dot_noShadow";
	_x setMarkerShape "ICON";
	_x setMarkerColor "ColorBlue";
	} forEach _posMarkers;

sleep 2;
	
	while {isServer} do {
	_mkr1 setMarkerPos (getPos (units group player select 0));
	_mkr2 setMarkerPos (getPos (units group player select 1));
	_mkr3 setMarkerPos (getPos (units group player select 2));
	_mkr4 setMarkerPos (getPos (units group player select 3));
	sleep 5;
	};

 

  • Like 1

Share this post


Link to post
Share on other sites

The code could be optimized. Could you send the "ideal" picture of what you want to update every 10 seconds? i.e. multiplayer? all played sides? For each sides? with different colors? with texts? ....

  • Thanks 1

Share this post


Link to post
Share on other sites
On 11/15/2024 at 11:42 AM, pierremgi said:

The code could be optimized. Could you send the "ideal" picture of what you want to update every 10 seconds? i.e. multiplayer? all played sides? For each sides? with different colors? with texts? ....

 

I would definitely like to optimize the code and potentially add some more information to the markers, but I was struggling just with creating them and updating their position so I didn't bother.

 

As of right now the only information being updated every 10 seconds (I ended up setting it for 5, but that's not really relevant) is the location of the markers created within the script. My mission is 4 player coop (all slots on NATO) with no civs. I considered adding text to the markers via:

 

{

_x setMarkerText str _x;

}

 

But the text crowded the screen and become unreadable when players were close to each other. Which segues into a related problem: I was also really struggling to find a way to "consolidate" the markers when all players are close to each other. As in, when the players are spaced out, they'll each have their own individual marker, but when they get close to each other their individual markers will be deleted in place of one group marker.

 

Maybe you could give some advice?

 

EDIT: I worked a little bit more on it and got it into a state that I'm happy with. The script now occludes individual markers when the players are grouped up near the squad leader and shows when a player is incapacitated. Probably still far from optimized, but it works.

 

private _nOfPlayers = count units group player;
_p1POS = getPos zCurator;
_p2POS = getPos ar;
_p3POS = getPos gren;
_p4POS = getPos at;
_mkr1 = createMarker ["mkrPos1", _p1POS];
_mkr2 = createMarker ["mkrPos2", _p2POS];
_mkr3 = createMarker ["mkrPos3", _p3POS];
_mkr4 = createMarker ["mkrPos4", _p4POS];
_mkr2Array = [ar, _mkr2];
_mkr3Array = [gren, _mkr3];
_mkr4Array = [at, _mkr4];
_mkrArrays = [_mkr2Array, _mkr3Array, _mkr4Array];
_posMarkers = [_mkr1,_mkr2,_mkr3,_mkr4];

	{
	_x setMarkerType "mil_dot_noShadow";
	_x setMarkerShape "ICON";
	_x setMarkerColor "ColorWEST";
	} forEach _posMarkers;

sleep 2;
	
	while {isServer && _nOfPlayers == 4} do {
	{
		if ((getMarkerPos _mkr1) distance2D (getMarkerPos _x) < 30) then {
			_x setMarkerAlpha 0.25;
		} else {
			_x setMarkerAlpha 1;
		};
	} forEach [_mkr2, _mkr3, _mkr4];

	if (markerAlpha _mkr2 == 0.25 || markerAlpha _mkr3 == 0.25 || markerAlpha _mkr4 == 0.25) then {
		_mkr1 setMarkerText "Delta Squad";
		_mkr1 setMarkerType "b_inf";
	} else {
		_mkr1 setMarkerText "";
		_mkr1 setMarkerType "mil_dot_noShadow";
	};

	if ([zCurator] call ace_common_fnc_isAwake == false) then {
		_mkr1 setMarkerColor "ColorRed";
		_mkr1 setMarkerType "mil_dot_noShadow";
		_mkr1 setMarkerText "Man Down!";
		{
			_x setMarkerAlpha 1;
		} forEach [_mkr2, _mkr3, _mkr4];
	} else {
		_mkr1 setMarkerColor "ColorWEST";
	};

	{
		if ([_x select 0] call ace_common_fnc_isAwake == false) then {
			_x select 1 setMarkerColor "ColorRed";
			_x select 1 setMarkerType "mil_dot_noShadow";
			_x select 1 setMarkerAlpha 1;
			_x select 1 setMarkerText "Man Down!";
		} else {
			_x select 1 setMarkerColor "ColorWEST";
		};
	} forEach _mkrArrays;

	_mkr1 setMarkerPos zCurator;
	_mkr2 setMarkerPos ar;
	_mkr3 setMarkerPos gren;
	_mkr4 setMarkerPos at;
	
	sleep 0.01;
	};

 

Share this post


Link to post
Share on other sites

Run this code on all clients:

 

// creating markers locally. Replace playableUnits (MP) by allPlayers or even [p1,p2,p3,p4] or else  (switchableUnits is for SP (then preview) as far as playableUnits doesn't work in SP)
// creating markers locally is resource saving
{
  private _mk = createMarkerLocal [str _x, getPos _x];
  _mk setMarkerTypeLocal "mil_dot_noShadow";
  _mk setMarkerColorLocal "colorWest";
  _x setVariable ["mk",_mk];
} forEach playableUnits + switchableUnits;

// as any line on map must be drawn on each frame, you need to use the draw event handler. So use it also for refreshing the positions of the markers (marker is paired to player by a variable).

(findDisplay 12 displayCtrl 51) ctrlAddEventHandler ["Draw", {
    {
        (_this select 0) drawLine [leader _x,_x,[0,0,0,1]];
        _x getVariable ["mk",""] setMarkerPosLocal getPos _x;
    } forEach playableUnits + switchableUnits;
}];

 

 

 

  • Thanks 1

Share this post


Link to post
Share on other sites
On 11/23/2024 at 1:33 AM, pierremgi said:

Run this code on all clients:

 


// creating markers locally. Replace playableUnits (MP) by allPlayers or even [p1,p2,p3,p4] or else  (switchableUnits is for SP (then preview) as far as playableUnits doesn't work in SP)
// creating markers locally is resource saving
{
  private _mk = createMarkerLocal [str _x, getPos _x];
  _mk setMarkerTypeLocal "mil_dot_noShadow";
  _mk setMarkerColorLocal "colorWest";
  _x setVariable ["mk",_mk];
} forEach playableUnits + switchableUnits;

// as any line on map must be drawn on each frame, you need to use the draw event handler. So use it also for refreshing the positions of the markers (marker is paired to player by a variable).

(findDisplay 12 displayCtrl 51) ctrlAddEventHandler ["Draw", {
    {
        (_this select 0) drawLine [leader _x,_x,[0,0,0,1]];
        _x getVariable ["mk",""] setMarkerPosLocal getPos _x;
    } forEach playableUnits + switchableUnits;
}];

 

 

 

 

Thanks! I made some small tweaks to it (and tried to get fancy with it, to no avail) and it's working great. This is the code I ended up going with: it doesn't group the players under a single marker when nearby anymore, but I think the line drawn between units serves the same purpose. I managed to work in functionality for displaying incapacitated players as well. Definitely learned a lot from this.

 

	{
		private _mk = createMarkerLocal [str _x, getpos _x];
		_mk setMarkerTypeLocal "mil_dot_noShadow";
		_mk setMarkerShapeLocal "ICON";
		_mk setMarkerColorLocal "ColorWEST";
		_mk setMarkerTextLocal roleDescription _x;
		_x setVariable ["mk",_mk];
	} forEach playableUnits + switchableUnits;

	sleep 2;

	while {isServer} do {

	{
		if ([_x] call ace_common_fnc_isAwake) then {
			str _x setMarkerColorLocal "ColorWEST";
			str _x setMarkerTextLocal roleDescription _x;
		} else {
			str _x setMarkerColorLocal "ColorRed";
			str _x setMarkerTextLocal "Man Down!";
		};
	} forEach playableUnits + switchableUnits;

	};

 

Share this post


Link to post
Share on other sites

In one of my modules (Terrain & Map interactive), I added the possibility for showing on map, traits of player's units (medic, engineer, explosive specialist) + the squad number of the units (not in code below).

This feature appears only when you scroll in, reducing bad effect of superimposed icon traits (simplified here for clarity  and no interference with my addon).

Roughly, something like that:


 

findDisplay 12 displayCtrl 51 ctrlAddEventHandler ["Draw", {
  _map = _this select 0;
  if (ctrlMapScale _map <0.03) then {
	{
      if (isNull objectParent _x) then {
        _map drawIcon [
          ["","\a3\ui_f\data\IGUI\Cfg\simpleTasks\types\heal_ca.paa"] select (_x getUnitTrait "medic"),
          [1,1,1,1],
          getPosVisual _x vectorAdd [850*ctrlMapScale _map,300 *ctrlMapScale _map,0],
          12,12,0,"",0,0,"TahomaB","right"
        ];
        _map drawIcon [
          ["","\a3\ui_f\data\IGUI\Cfg\Cursors\iconRepairVehicle_ca.paa"] select (_x getUnitTrait "engineer"),
          [0,1,0,1],
          getPosVisual _x vectorAdd [850*ctrlMapScale _map,0,0],
          12,12,0,"",2,0,"TahomaB","right"
        ];
        _map drawIcon [
          ["","\a3\ui_f\data\IGUI\Cfg\simpleTasks\types\destroy_ca.paa"] select (_x getUnitTrait "explosiveSpecialist"),
          [1,0,0,1],
          getPosVisual _x vectorAdd [850*ctrlMapScale _map,-300 *ctrlMapScale _map,0],
          12,12,0,"",0,0,"TahomaB","right"
        ];
      };
    } forEach (units player select {_x == effectiveCommander vehicle _x});
  };
}];

 

In addon (see picture):

Zoomed out (units are too close):

ArmA-3-Screenshot-2024.11.24-10.05.20.84

 

 

Zoomed in (traits and numbers displayed):

 

ArmA-3-Screenshot-2024.11.24-10.05.45.65

  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites

That looks great! I wish I had better use for it, but my mission doesn't even have any specialists since I'm using simplified ACE medical settings. I will however be chopping out a section of this to have marker texts appear when zoomed in. Thanks for all the info. Please feel free to share any other related scripts you may have, I've already learned a lot from what you posted here.

Share this post


Link to post
Share on other sites

I believe this will be my final iteration of this script as I've pretty much eliminated all direct references to objects making it a painless copy & paste for anyone else wanting to use it. I got lazy towards the end and didn't want to bother finding the class names for all the vehicles so I just referenced the objects in my mission, but the (typeOf objectParent _x == typeOf hum1) condition can be easily swapped with (typeOf objectParent _x == "VehicleClassName"). Unfortunately depending on the vehicles present in the mission, this part needs to be tweaked no matter what.

 

(findDisplay 12 displayCtrl 51) ctrlAddEventHandler ["Draw", {
	{
		if (isNull objectParent _x) then {
		{
			(_this select 0) drawLine [leader _x,_x,[0,0,0,0.5]];
			_x getVariable ["mk",""] setMarkerPosLocal getPos _x;
			
		} forEach playableUnits + switchableUnits;

		if ([_x] call ace_common_fnc_isAwake == false) then {
		(_this select 0) drawIcon ["a3\characters_f\data\ui\icon_medic_ca.paa", [1,0,0,1], getPos _x, 20, 20, getDirVisual _x, "Man Down!", 0, 0.05, "puristaSemiBold", "right"];
		} else {
			if ([_x] call ace_common_fnc_isAwake && _x == leader _x) then {
			(_this select 0) drawIcon ["a3\ui_f\data\map\vehicleicons\iconmanleader_ca.paa", [0, 0.3, 0.6, 1], getPos _x, 20, 20, getDirVisual _x, name _x, 0, 0.04, "puristaMedium", "right"];
			} else {
				if ([_x] call ace_common_fnc_isAwake && [_x] call ace_common_fnc_isEngineer && _x != leader _x) then {
				(_this select 0) drawIcon ["a3\ui_f\data\map\vehicleicons\iconmanengineer_ca.paa", [0, 0.3, 0.6, 1], getPos _x, 20, 20, getDirVisual _x, name _x, 0, 0.04, "puristaMedium", "right"];
				} else {
					if ([_x] call ace_common_fnc_isAwake && [_x] call ace_common_fnc_isMedic && _x != leader _x) then {
					(_this select 0) drawIcon ["a3\ui_f\data\map\vehicleicons\iconmanmedic_ca.paa", [0, 0.3, 0.6, 1], getPos _x, 20, 20, getDirVisual _x, name _x, 0, 0.04, "puristaMedium", "right"];
					} else {
						(_this select 0) drawIcon ["a3\ui_f\data\map\vehicleicons\iconman_ca.paa", [0, 0.3, 0.6, 1], getPos _x, 20, 20, getDirVisual _x, name _x, 0, 0.04, "puristaMedium", "right"];
					};
				};
			};
		};
		} else {
			if (typeOf objectParent _x == typeOf hum1) then {
			(_this select 0) drawIcon ["CUP\WheeledVehicles\CUP_WheeledVehicles_HMMWV\data\ui\icon_hmmwv_m2gpk_ca.paa", [0, 0.3, 0.6, 1], getPos objectParent _x, 30, 30, getDirVisual objectParent _x, getText (configFile >> "cfgVehicles" >> typeOf objectParent _x >> "displayName"), 0, 0.04, "puristaMedium", "right"];
			};

			if (typeOf objectParent _x == typeOf mrap) then {
				(_this select 0) drawIcon ["CUP\WheeledVehicles\CUP_WheeledVehicles_RG31\data\ico\icomap_rg31_m2_ca.paa", [0, 0.3, 0.6, 1], getPos objectParent _x, 30, 30, getDirVisual objectParent _x, getText (configFile >> "cfgVehicles" >> typeOf objectParent _x >> "displayName"), 0, 0.04, "puristaMedium", "right"];
			};

			if (typeOf objectParent _x == typeOf lb) then {
				(_this select 0) drawIcon ["CUP\AirVehicles\CUP_AirVehicles_MH60S\data\ui\icomap_mh60mg_ca.paa", [0, 0.3, 0.6, 1], getPos objectParent _x, 30, 30, getDirVisual objectParent _x, getText (configFile >> "cfgVehicles" >> typeOf objectParent _x >> "displayName"), 0, 0.04, "puristaMedium", "right"];
			};

			if (typeOf objectParent _x == typeOf bradley) then {
				(_this select 0) drawIcon ["CUP\TrackedVehicles\CUP_TrackedVehicles_Bradley\data\ui\icon_m2a2_ca.paa", [0, 0.3, 0.6, 1], getPos objectParent _x, 30, 30, getDirVisual objectParent _x, getText (configFile >> "cfgVehicles" >> typeOf objectParent _x >> "displayName"), 0, 0.04, "puristaMedium", "right"];
			};

			if (typeOf objectParent _x == typeOf apache) then {
				(_this select 0) drawIcon ["CUP\AirVehicles\CUP_AirVehicles_AH64\data\ui\icon_ah64d_ca.paa", [0, 0.3, 0.6, 1], getPos objectParent _x, 30, 30, getDirVisual objectParent _x, getText (configFile >> "cfgVehicles" >> typeOf objectParent _x >> "displayName"), 0, 0.04, "puristaMedium", "right"];
			};

			if (typeOf objectParent _x == typeOf qb1) then {
				(_this select 0) drawIcon ["a3\soft_f\quadbike_01\data\ui\map_quad_ca.paa", [0, 0.3, 0.6, 1], getPos objectParent _x, 24, 24, getDirVisual objectParent _x, name _x, 0, 0.04, "puristaMedium", "right"];
			};
		};		
	} forEach playableUnits + switchableUnits;
	}];

	addMissionEventHandler ["Draw3D", {
		{
		if ([_x] call ace_common_fnc_isAwake == false) then {
		drawIcon3D ["a3\characters_f\data\ui\icon_medic_ca.paa", [1,0,0,1], getPos _x, 1, 1, getDirVisual _x, "Man Down!", 0, 0.05, "puristaSemiBold", "right", true];
		};
		} forEach playableUnits + switchableUnits;
	}];

 

All units inside vehicle

?imw=5000&imh=5000&ima=fit&impolicy=Lett

 

Squad Leader outside of vehicle

?imw=5000&imh=5000&ima=fit&impolicy=Lett

 

All units on foot, {name _x} is used here, but {roleDescription _x} is a good option too (script DOES display if unit is medic or engineer)

?imw=5000&imh=5000&ima=fit&impolicy=Lett

 

Units incapacitated

?imw=5000&imh=5000&ima=fit&impolicy=Lett

 

Units incapacitated (3D)

?imw=5000&imh=5000&ima=fit&impolicy=Lett

 

Note: screenshots captured before leader icon was implemented.

  • Like 1

Share this post


Link to post
Share on other sites

Nice.

Just a point for optimization:

 

instead of :

if... then...

if... then...

if... then...

where all lines are checked whatever the conditions are, prefer:

call {

  if... exitWith...

  if... exitWith...

  if... exitWith...

};

This way, further lines are skipped when a condition is true. Even better if you can place first the best occurrence to end by the less.

Important for such each framed script.

See also all tips in the link above.

 

 

  • Thanks 1

Share this post


Link to post
Share on other sites

Awesome!

 

I changed my if/then/else statements with if/exitWith and I can actually feel a difference in game. There was the tiniest of microstutters when a player was incapacitated (that I admittedly didn't notice at the time) but now it's buttery smooth.

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×