Jump to content
froggyluv

[Question] Changing Text Color by Side

Recommended Posts

So this script is from SaOk's great WLA missions and its a function to highlight various text over AI to enliven the experience. Ive tried to modify (badly) to change the texts color depending on Faction type

 


FU = {  
private ["_unit","_line","_someId","_t","_ar","_k"];  
_unit = _this select 0;  
_line = _this select 1; 

_side = side _unit;

hint str _side;

_F_Color = [];
NUMM =0;   
 
NUMM = NUMM + 1;  
_someId = format ["IDUSAY%1",NUMM];  
_k = 1;  
if (player distance _unit > 10) then {_k = 100/((player distance _unit)*10);};  
[_someId, "onEachFrame", {  
   
 _cCiv =  [0.44,0.44,0.905,1];
 _cInd =  [0.2,0.9,0.505,1];
 _cWest = [0.2,0.2,0.805,1];
 _cEast = [0.44,0.44,0.905,1];
 
 switch _side do
 
 {
     case EAST:
     {
     if (_side == EAST) then {_F_Color = [0.44,0.44,0.905,1]};        ////sometimes im using the color code directly other times using the _cEast type variable which is redundant...   :/
     
     };
 
     case WEST:
     {
    if (_side == WEST) then {_F_Color = [0.2,0.2,0.805,1]};         
     
     };
      
     case INDEPENDENT:
     {
     if (_side == INDEPENDENT) then {_F_Color = _cInd};         
     
     };
      
     case CIVILIAN:
     {
     if (_side == CIVILIAN) then {_F_Color = _cCiv};         
     
     };
 };



 _start = getposATL (_this select 0);  
 _start set [2,(_start select 2)+1.9];  
 drawIcon3D ["" ,_F_Color, _start, 0, 0, 0, _this select 1, 1, _this select 2, "TahomaB"];   ////////////////// HERE is where the variable _F_Color should be picking up the variable from _side (which displays properly in hint) but for some reason always comes up as empty brackets
 
}, [_unit, _line,(.05 * _k)]] call BIS_fnc_addStackedEventHandler;  
_t = 3;   
if (count _this > 2) then {_t = _this select 2;};  
sleep _t;  
[_someId, "onEachFrame"] call BIS_fnc_removeStackedEventHandler;  
sleep 18;  
if (isNull _unit || {!alive _unit}) exitWith {}; };  
[cursortarget, "Fcckkk you!!!"] spawn FU;

systemchat str _F_Color

Share this post


Link to post
Share on other sites

The main thing I found is that unit was "unknown" because it only considers the unit at the start, and then after 3 seconds it's done and not fired again even though you may be pointing at a target. I modded it a bit, put cursorTarget inside the even handler. Probably not what you want, but maybe this will get you going. This does work, just not sure if it's what you want :)

 


FU = {  
    private ["_unit","_line","_someId","_t","_ar","_k"];  
    params ["_line","_timeout"]; 
    NUMM = random(50000);  
    _someId = format ["IDUSAY%1",NUMM];  
    
    [_someId, "onEachFrame", {
     _cCiv =  [0.44,0.44,0.905,1];
     _cInd =  [0.2,0.9,0.505,1];
     _cWest = [0.2,0.2,0.805,1];
     _cEast = [0.44,0.44,0.905,1];
    _F_Color = [];
    _unit = cursorTarget;
    _side = side _unit;
    _k = 1;
    _line = _this select 0;
    if (player distance _unit > 10) then {_k = 100/((player distance _unit)*10);};  
    
    switch _side do
     {
         case EAST:
         {
            _F_Color = [0.44,0.44,0.905,1];
         };
     
         case WEST:
         {
            _F_Color = [0.2,0.2,0.805,1];  
         };
          
         case INDEPENDENT:
         {
            _F_Color = _cInd;
         };
          
         case civilian:
         {
            _F_Color = _cCiv;
         };
     };

     if (_unit isKindOf "Man") then
     {
         _start = getposATL _unit;  
         _start set [2,(_start select 2)+1.9];
         systemChat format["%3: color: %1, side: %2, %4, %5",_F_Color,_side,time,_line,_start];
         drawIcon3D ["" , _F_Color, _start, 0, 0, 0, _line, 1, 0.05, "TahomaB"];
     };
     //drawIcon3D ["", [1,0,0,1], position cursorTarget, 0, 0, 0, "Target", 1, 0.05, "PuristaMedium"];
     ////////////////// HERE is where the variable _F_Color should be picking up the variable from _side (which displays properly in hint) but for some reason always comes up as empty brackets
     
    }, [_line]] call BIS_fnc_addStackedEventHandler;

    // sleep _timeout;
    // [_someId, "onEachFrame"] call BIS_fnc_removeStackedEventHandler;
    // sleep 18;
    //if (isNull _unit || {!alive _unit}) exitWith {};
};

["Fcckkk you Benny!!!", 3] spawn FU;
//systemchat str _F_Color;

Share this post


Link to post
Share on other sites

Your BIS_fnc_addStackedEventHandler parameters are not correctly defined inside the code, and your local variable _side isn't defined in this scope.

Add something like:

[_someId, "onEachFrame", {

params ["_unit","_line","_someWeirdK"];

then work with that: _side = side _unit; for example.

 

What a scope is: a local code inside a loop (while, for, forEach), or EH, or addAction, or call, or spawn where local (_underscored) variables stay defined. You need to pass them (parameters, <private> command) if you want to change scope. Each time you { } or loop some code.

 

Example:

 in a SQF // scope

params ["_titi","_blabla","_anyFurtherOne"];   // parameters coming from outside (another sqf) say _titi and _blabla + a following local variable (_anyFurtHerOne) to make it work inside this sqf scope. This is equivalent to:

_titi = _this select 0;

_blabla = _this select 1;

private "_anyFurtherOne";

 

_anyFurtherOne = [];   (or concatenate these 2 lines with : private _anyFurtherOne = [];

 

for "_i" from 0 to 10 do {   // I open a scope here. _anyFurtherOne is already defined local variable but none other inside this loop. As you can see _i (the counter) is also a local variable, here defined only for the loop.I.e. you can use another loop with _i outside this loop, but use another on for imbricated loop (_k)

  for "_k" from 0 to 5 do {

   _anyFurtherOne pushBack [_i,kj]

  }

};

hint str _anyFurtherOne;

 

Same with forEach or count, even select {code } ... (when you are using _x, this local variable has no sense outside the loop)

For most of EHs, bis_fnc/commands handlers (stacked, display, control, mission...) but also addAction, you have predetermined, specific parameters explained in BIKI, to pass arguments.

 

 

Share this post


Link to post
Share on other sites

Wow, this code is ancient!

Why is there an additional if statement inside the switch?

 

Also there's a whole lot of redundant calculations going on with _k

_k = 100/((player distance _unit)*10)
//later then _k turns into this
(0.5*_k)

which is the same as

_k = 5/player distance _unit

Not sure about the mathematical reasoning behind that, also the text will be extremely huge and won't fit the screen, heh.

 

NUMM =0;

This will reset the iterator to 0 everytime the function is called, using missioneventhandler there's no need for most of the fuzz if you want to run the code on cursorobject only.

 

The if exitwith after the sleep 18 at the end are making no sense.

 

No idea what your goal is here, but this works in the most basic way utilizing up to date commands:


FU = {

	_ID = addMissionEventHandler ["EachFrame",{

		if (cursorObject isEqualTo objNull) exitWith {false};

		_unit = cursorTarget;
		_F_Color = [side _unit] call BIS_fnc_sideColor;
		_line = name _unit;
		_start = getposATL _unit;
		_start set [2,(_start select 2)+2.2];

		_k = 0.05;

		drawIcon3D ["" ,_F_Color, _start, 0, 0, 0, _line, 1, _k, "TahomaB"];

	}];

};

[] spawn FU;

 

Cheers

Share this post


Link to post
Share on other sites

As @pierremgi says the reason your code does not work ( ignoring the fact that it is a terrible way to do what you want by adding and removing a stacked OEF  ) is that you pass the variables into the stacked eventHandler but never retrieve them until the drawIcon line (_this select #).

As the code passed to the stacked event is deferred to run on each frame it has no knowledge about any variables defined in the code before it was called and is why the variables are passed into the stacked event as parameter 3    }, [_unit, _line,(.05 * _k)]] call BIS_fnc_addStackedEventHandler;

You need to retrieve the variables before you can do anything with them.

[_someId, "onEachFrame", {  
	params[ "_unit", "_line", "_k" ];
	//Do stuff with variables
}, [_unit, _line,(.05 * _k)]] call BIS_fnc_addStackedEventHandler;  

 

1 hour ago, Grumpy Old Man said:

Not sure about the mathematical reasoning behind that, also the text will be extremely huge and won't fit the screen, heh.

Although it starts very large == 1, for every ^10 above a distance of 10 _k becomes ^10 of 0.5 smaller.

player distance < 10 then _k == 1

player distance == 10 then _k == 0.5

player distance == 100 then _k == 0.05

player distance == 1000 then _k == 0.005

In essance the further away a unit is the smaller the name tag. I usually find using some form of linear drop off is better for this kind of effect.

_k = linearConversion[ 0, 100, player distance _unit, 0.5, 0.05, true ];

So at a distance of 0 the text is 0.5 falling to 0.05 at 100 meters, at which point it is clamped to never fall below this.

  • Like 2

Share this post


Link to post
Share on other sites
18 minutes ago, Larrow said:

 

player distance < 10 then _k == 1

player distance == 10 then _k == 0.5

player distance == 100 then _k == 0.05

player distance == 1000 then _k == 0.005

 

 

I'm aware of that and always using linearConversion since it's a godsend,

what I meant was why the value of _k was being recalculated a few times rather than just using 5 / distance

//init of _k
_k = 100/((player distance _unit)*10)
//passing _k to the stacked EH
(0.5 * _k)

//could be easier to read and tweak by simply using
_k = 5 / player distance _unit
//since the result is the same
//so you don't have multiple recalculations all over the place

Cheers

Share this post


Link to post
Share on other sites
14 hours ago, froggyluv said:

FU

You lucky frog, all the big guns are helping you out!   My important contribution here is to endorse the usage of FU for function names, as I'm usually screaming that at them until they finally work.

  • Like 2
  • Haha 1

Share this post


Link to post
Share on other sites
21 minutes ago, johnnyboy said:

You lucky frog, all the big guns are helping you out!   My important contribution here is to endorse the usage of FU for function names, as I'm usually screaming that at them until they finally work.

Don't forget the ST prefix, might come in handy to further refine your naming conventions.

 

Cheers

  • Like 3
  • Haha 1

Share this post


Link to post
Share on other sites

 

 

3 hours ago, johnnyboy said:

You lucky frog, all the big guns are helping you out!   My important contribution here is to endorse the usage of FU for function names, as I'm usually screaming that at them until they finally work.

 

 Yup man seriously appreciated all it was killing me. Still learning proper scope syntax but i understand the variables needed to be inside the scope. Also the Cursortarget was just for testing this function is actually called by all units including Civilians and Grenades(thats why removed "if this is typeof 'man' ") for various ingame banter. Im calling this with [Fred, "Fccckkk You Benny!"] Spawn FU.

 

FU = {
private ["_unit","_line","_someId","_t","_ar","_k"];
params ["_unit","_line","_timeout"];  
 
NUMM =0;   
NUMM = NUMM + 1;  
_someId = format ["IDUSAY%1",NUMM];  
 
 
[_someId, "onEachFrame", {  
 
 _cCiv =  [0.44,0.44,0.905,1];
 _cInd =  [0.2,0.9,0.505,1];
 _cWest = [0.2,0.2,0.805,1];
 _cEast = [0.44,0.44,0.905,1];
 _unit = _this select 0;  
 _line = _this select 1;  
 _side = side _unit;
 _F_Color = [1,1,1,1];
_k = linearConversion[ 0, 150, player distance _unit, 0.03, 0.005, true ];   //// Awesome
 
 switch _side do
 
 {
     case east:
     {
          _F_Color = [0.73,0.24,0.11,1]     
     
     };
 
                    case West:
     {
        _F_Color = [0.2,0.2,0.805,1];           
     
     };
      
     case Independent:
     {
        _F_Color = _cInd;       
     
     };
      
     case Civilian:
     {
        _F_Color = _cCiv;        
     
     };
 };
 
 
 _start = getposATL (_this select 0);  
 _start set [2,(_start select 2)+1.9];  
 drawIcon3D ["" , _F_Color, _start, 0, 0, 0, _line, 1, _k, "TahomaB"];  
 
 
 
},  [_unit, _line]]  call BIS_fnc_addStackedEventHandler;  
_t = 3;   
if (count _this > 2) then {_t = _this select 2;};  
sleep _t;  
[_someId, "onEachFrame"] call BIS_fnc_removeStackedEventHandler;

 };

 

 I know Larrow you said not to use BIS_fnc_addStackedEventHandler and BIS_fnc_removeStackedEventHandler insame code but what do you suggest to turn off the text after alotted time?

 

Share this post


Link to post
Share on other sites

Awww you kept my Total Recall reference :)

I have to admit I'm not sure what the intended result is / the use-case. I removed stacked event handler in my example because the script was only fired for 3 seconds and didn't work after that. However I don't see a problem having it there if you are creating the event handler on a case by case basis. Anyone feel free to correct me about that.

Share this post


Link to post
Share on other sites

Best practice would be to have one single eventhandler and have it iterate through every unit/object you want to handle.

You could have the eventhandler check for a variable on the unit, if found then display the text, this way you can handle individual units with one single eventhandler, like this:

 

addMissionEventhandler ["EachFrame",{

_units = allUnits select {!(_x getVariable ["TAG_fnc_myText",""] isEqualTo "")};

{

	_color = (side _x call BIS_fnc_sideColor);
	_pos = (getposatl _x vectorAdd [0,0,2.2]);	
	_text = _x getVariable ["TAG_fnc_myText",""];

	drawIcon3D["", _color, _pos, 1, 1, 0, _text];

} forEach _units;


}]

Then in parallel you could run a loop that changes the text for individuals at random intervals, again, no clue what your goal is here:

 

_words = {

	TAG_fnc_printWords = true;
	_stuffToPrint = ["Eeny","Meeny","Miny","Moe"];

	while {TAG_fnc_printWords} do {
		{
			if (random 100 < 80) then {
				_spawn = [_x,selectRandom _stuffToPrint] spawn {
					params ["_unit","_words"];
					sleep random 7;
					_unit setVariable ["TAG_fnc_myText",_words];
				};
			};
		} forEach allUnits;
		sleep 10;
	};
	{
		_x setVariable ["TAG_fnc_myText",""];
	} forEach allUnits;
};

[] spawn _words;

You can turn off the text on individual units by setting the TAG_fnc_myText variable to an empty string, as seen in the example above, simple as that.

 

Cheers

  • Like 1

Share this post


Link to post
Share on other sites
On 1/26/2018 at 9:16 PM, froggyluv said:

 I know Larrow you said not to use BIS_fnc_addStackedEventHandler and BIS_fnc_removeStackedEventHandler insame code but what do you suggest to turn off the text after alotted time?

For this type of thing, I would likely set up some type of queue system. So one eventHandler to handle all FUs. Rather than, what could possibly be, multiple stacked events starting and stopping all the time.

 

Think about if you did something like..

[Fred, "Fccckkk You Benny!"] spawn FU;
sleep 1;
[Benny, "Fccckkk You Fred!",5] spawn FU;
sleep 1;
[Bert,"Fccck all of you!",8] spawn FU;

You would now have three entries in the scheduler (spawned threads waiting to cancel a stacked event on timeout) and three stacked events running.

 

But if you did something like..

FU = {
	params ["_unit","_line",[ "_timeout", 3 ]];

	//Set up the variables needed to be added to the queue
	//The unit, the line and its end time
	_queueItem = [ _unit, _line, time + _timeout ];

	//If there is currently no queue
	if ( isNil "FU_Queue" ) then {

		//Initiaite queue with queue item
		FU_Queue = [ _queueItem ];

		//Start up ONE event to manage all items in the queue
		private _nul = addMissionEventHandler [ "EachFrame", {

			//For each queue items
			{
				//Retreive queue items variables
				_x params[ "_unit", "_line", "_endTime" ];

				//If it has not reached its end time
				if ( time <= _endTime ) then {

					//Work out its color
					_cCiv =  [0.44,0.44,0.905,1];
					_cInd =  [0.2,0.9,0.505,1];
					_cWest = [0.2,0.2,0.805,1];
					_cEast = [0.44,0.44,0.905,1];

					_F_Color = switch side _unit do {
					 	case east : {
							[0.73,0.24,0.11,1]
						};
						case west : {
							[0.2,0.2,0.805,1]
						};
						case independent : {
							_cInd;
						};
						case civilian : {
							_cCiv;
						};
						default {
							[1,1,1,1]
						};
					};

					//Work out text size
					_k = linearConversion[ 0, 150, player distance _unit, 0.03, 0.005, true ];   //// Awesome
					//And position
					_start = getPosATLVisual _unit vectorAdd [0,0,1.9];

					//Draw text
					drawIcon3D ["" , _F_Color, _start, 0, 0, 0, _line, 1, _k, "TahomaB"];
				}else{
					//If we have reached the end time then flag queue item for removal
					FU_Queue set[ _forEachIndex, objNull ];
				};

			}forEach +FU_Queue;

			//Remove all finished items from the queue
			FU_Queue = FU_Queue - [ objNull ];

			//If there is nothing left in the queue
			if ( count FU_Queue isEqualTo 0 ) then {
				//Delete entire queue
				FU_Queue = nil;
				//Stop the queue handler
				removeMissionEventHandler[ "EachFrame", _thisEventHandler ];
			};
		}];
	}else{
		//Else the queue is already running
		//Just add new item to the back of the queue
		private _nul = FU_Queue pushBack _queueItem;
	};

};


[Fred, "Fccckkk You Benny!"] call FU;
sleep 1;
[Benny, "Fccckkk You Fred!",5] call FU;
sleep 1;
[Bert,"Fccck all of you!",8] call FU;

You have no scheduler threads (function is called and has no waits/sleeps) and only one event handler running that automatically shuts its self down when there are no more items left in the queue.

Although the stackedEvent is itself a queue system at least this way you have full control over your own queue system dedicated to your FUs.

 

A queue system can be a handy way for handling all types of things like UI effects for example.

Edited by Larrow
fixed _side
  • Like 2

Share this post


Link to post
Share on other sites

I've been struggling with this same issue (multiple units running separate EHs vs. one EV managing them all), and these 2 awesome examples above will really help me overhaul the mess I was making.   Plus they serve as great templates for anyone doing onEachFrame EHs.  You guys rule.

 

@Larrow:  Question:  Is the following a typo, or does  the "+" in "+FU_Queue" do something?

 

9 hours ago, Larrow said:

}forEach +FU_Queue;

 

Share this post


Link to post
Share on other sites
2 hours ago, johnnyboy said:

@Larrow:  Question:  Is the following a typo, or does  the "+" in "+FU_Queue" do something?

No, not a typo. Arrays in sqf are a reference type. ie

_a = [1,2,3];
_b = _a;
_a set[1,5];

//_a == [1,5,3]
//_b == [1,5,3]
//as _b is a copy of _a by reference, changing _a after assigning to _b also changes _b

_a = [1,2,3];
_b = +_a; //make a copy of _a
_a set[1,5];

//_a == [1,5,3]
//_b == [1,2,3]
//as _b is a copy of _a by value, changing _a afterwards does not effect _b

As the code is forEach-ing through the queue and the queue could possibly change (maybe not so much in this example), instead forEach through a copy (snapshot) of the queue at that moment. Just think of it more of a safety feature if the array could possibly change whilst your processing it, instead process a copy.

  • Like 1

Share this post


Link to post
Share on other sites
7 minutes ago, Larrow said:

As the code is forEach-ing through the queue and the queue could possibly change (maybe not so much in this example), instead forEach through a copy (snapshot) of the queue at that moment. Just think of it more of a safety feature if the array could possibly change whilst your processing it, instead process a copy.

Awesome.  Thanks for the explanation.  I was unaware you could use the + operator to make a copy.  I also see that I could get into trouble not remembering that arrays are a reference type!

Share this post


Link to post
Share on other sites

...and then Larrow shows up..

_m30rM.gif

 

Thanks abunch man - it needed _side = side _unit added but it works out  really well.

  • Haha 1

Share this post


Link to post
Share on other sites
On 1/26/2018 at 4:28 PM, AZCoder said:

Awww you kept my Total Recall reference :)

I have to admit I'm not sure what the intended result is / the use-case.

 

 Its for added ingame text banter which scope is pretty limitless -my goal has always been to just feel like im playing in a more alive, dynamic world even when just firing up the editor. Heres a quickly made footage though you can see i havent yet controled the spammy nature of it yet

 

 

  • Like 1

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

×