Jump to content
ROGER_BALL

getPlayerScores -- has me totally baffled.

Recommended Posts

Posted (edited)

This is my first posting of a "stump-the-dummy" question. 3 hours on this one thing is no exaggeration.  I can usually find the answer if I look long and hard enough and do enough trial & error coding.  Not this time. I've tried everything I know to get a simple hint to announce the running total of deaths of my guys in the squad as we play along.

 

I have a "addMissionHandler (entityKilled) and inside it, I have lots of things that work great. But at the end of those, I tried to add the following. The hint always produces "0" deaths results. I use to be pretty good at coding C++ but at 73 years old now, I'm losing my edge -- especially since this scripting SQF is so much different.  Sorry for the newbie disclosure blah blah..    Could someone please help me see where I'm going wrong with this approach?  Thanks.

 

I couldn't really couldn't find a good example of the "getPlayerScores" usage -- Wiki says its output is an array and I know that the 6th element is #deaths of "player1" but I'll be darn if I can get that out to a format[] statement to display it.   Thank you for your time to look this over.

 

 

 


  my_deaths = str (getPlayerScores player1 select 5);

  hint format ["Player 1 killed %1 times. ", my_deaths];    // elements 0,1,2,3,4,5,6   5 = #deaths

 

  I also tried many, many other ways including:

 

  hint format ["Player 1 killed %1 times. ", getPlayerScores player 1 (select 5)];  // etc.

 

Cheers.

Edited by ROGER_BALL
Added information

Share this post


Link to post
Share on other sites

getPlayerScores returns an empty array in singleplayer. Are you testing in a multiplayer environment? 

  • Like 1

Share this post


Link to post
Share on other sites

Hello ROGER_BALL and welcome to the forums.

 

As Harzach mentioned per the BIKI, when used in single player getPlayerScores returns an empty array, which, whether it is turned to '0' or '[]' when converted to string, it is definitely not what you want. I haven't tested it but I believe your code should work when used in multiplayer.

 

In case it doesn't, an alternative would be to create a global (whether you will make it public or not depends on your needs) variable to hold the number of deaths. This could possibly look like (I present the event handler with just this part of the code as I do not know what else you have included in yours).

// Create the global variable to hold number of deaths
numDeath = 0;

addMissionEventHandler ["EntityKilled", {
	// Get the killed unit
	private _killed = _this select 0;

	// Check if it is a player who died
	if (isPlayer [_killed]) then {
		numDeath = numDeath + 1; // Increament the number of deaths
	};
}];

Some comments on the code presented above...

 

1) The mission event handler will run locally where it was added. This means that you will have to add it to every player, making it a very good candidate for the initPlayerLocal.sqf. I assume you already know that.

 

2) Please note the syntax of isPlayer. I have placed the killed unit in an array. This is per the alternative syntax presented in the BIKI of the isPlayer command. When this syntax is used, it doesn't matter whether the unit is alive or not. I have used it because I haven't tested the code and I am not sure whether in the "original" syntax the state (alive or dead) of the unit matters.

 

3) Finally, as per the BIKI again, please note that

Quote

In some cases, the identity of certain player units might fail to propagate to other clients and the server, which causes isPlayer and getPlayerUID to incorrectly return false and "", respectively, where the affected units are not local.[1] Therefore, beware of false negatives.

 

[1] https://github.com/michail-nikolaev/task-force-arma-3-radio/issues/1096

This means that if you intend to call the command from another client (or the server) you may have to do some extra checks.

 

Some final notes. The code presented here is not tested, thus it should be treated with care and run some tests with it. Not sure this provides a solution to your problem as I don't have enough information on how you intend to use the commands and what exactly you are trying to achieve (present the total deaths to everyone, present each player with their own deaths, something else?).

 

I do hope this helps somehow. Please don't hesitate to ask for more help, clarification or throw some more info and "re-initiate" the conversation.

 

Best...

  • Like 2

Share this post


Link to post
Share on other sites
Posted (edited)

Sorry, Harzach, I should have mentioned "MP".

 

ZaellixA, I'm excited to give that a try. You explained it so that I understand it. Thank you. I have 5 guys who play on my local host so with the code you kindly wrote, I can add to include everyone. I will try putting the code in the sqf and "calling" it from the missionEventHandler[] -- at least that's what I think is suggested.  If I can't get that to work, I will just put it within the missionEventHandlerandler and try it there. The advantage/disadvantage of that choice is over my head.

 

Generally, I have my init.sqf with my missionEventHandler "EntityKilled" to include this sort of additional code within it.

 

 I learned alot from your code example. Thank you. I especially enjoyed this part:

 

// Check if it is a player who died if (isPlayer [_killed]) then { numDeath = numDeath + 1; // Increment the number of deaths

 

 

I will report back for the benefit of others who may be following along on this thread. Thanks a lot fellas.

Edited by ROGER_BALL
Spelling

Share this post


Link to post
Share on other sites

@ROGER_BALL 

Arrays are indexed from "0", not from "1", so the while the "deaths" element of getPlayerScores is the fifth element, it is referenced as element 4.

my_deaths = str (getPlayerScores player1 select 4); 
hint format ["Player 1 killed %1 times. ", my_deaths];

So your code works fine.

 

Your comment:

// elements 0,1,2,3,4,5,6   5 = #deaths

shows seven elements where there are only six, which is where you tripped yourself up.

 

Good luck, have fun!

  • Like 2

Share this post


Link to post
Share on other sites

Hehe, Harzach is absolutely right, didn't spot that 8|...!!!

Additionally, when I mentioned the initPlayerLocal.sqf I meant the one that is mentioned in this page. initPlayerLocal.sqf is, along with init.sqf and a whole bunch other scripts (you can find most of them at the provided link), is a script that is automatically called by the engine locally (in each player's machine and not on the server) when a player joins the game and the "player" is initialised.

 

All you have to do is create such a script and place it in the same folder your mission .sqm is and it will be automatically called. Thus, if you place the provided code in this script it will be automatically called for each player that joins the game, saving you the burden to manually add it to all the playable units' init box in the editor. Not sure you already know that but from your last post I understood that it is not very clear to you. Apologies if I am wrong there...

 

Finally, if you want to put the code anywhere but the event handler you could do something like

// Keep score of deaths
numDeaths = 0;

// Put the code into a variable
incDeath = {
	// Get the passed parameter (normally the unit)
	_unit = _this select 0;

	// Check if the unit is a player
	if (isPlayer [_unit]) then {
		numDeath = numDeath + 1; // Increament the number of deaths
	};
};


// Call the code inside the even handler
addMissionEventHandler ["EntityKilled", {
	params["_killed"];

	[_killed] call incDeath;
}];

Hope this helps somehow and I haven't complicated things unnecessarily...!!!

  • Like 1

Share this post


Link to post
Share on other sites

You guys are terrific help.  I definitely understood all points made and I can't wait to implement and try them over my weekend. My men in the squad will benefit from this too.

Have a good weekend and thank you both again. Cheers.

  • Like 1

Share this post


Link to post
Share on other sites

In MP, the MEH "entitykilled" runs where  the MEH is scripted... so for any player in initPlayerLocal.sqf..., + server if  init.sqf, or even, on server only if running on initserver.sqf

That's the first point. The second one is, the inner code will run locally, whatever your first option. That means:

in initServer.sqf, the code runs on server only. Difficult to make such code appropriate for a count based on players.

in initPlayerLocal.sqf or init.sqf,  all players (+ dedicated server if init.sqf) will run this code independently, whatever the killed/killer could be. So, it's ok for local score but a little bit more complex for counting your own kill. The score is not discriminant until you add a condition on _killer or _instigator

Something like, in initPlayerLocal.sqf like:

numDeaths = 0;
addMissionEventHandler ["EntityKilled", {
  params ["_unit", "_killer", "_instigator"];
  if (isNull _instigator) then {_instigator = UAVControl vehicle _killer select 0};
  if (isNull _instigator) then {_instigator = _killer};
  if (_instigator == player && _instigator isNotEqualTo _unit) then {
    numDeaths = numDeaths + 1;
    hint str numDeaths; 
  };
}];

 

This score is local.Suicide (or self crash) doesn't count. You can add conditions for enemy kill only... or even just other killed players only (ZaellixA's code) but I'm not sure it was your intention.

 

You could be also interested in handleScore EH (on server only)

  • Like 2

Share this post


Link to post
Share on other sites

addMissionEventHandler ["EntityKilled",
{
    params ["_killed", "_killer", "_instigator"];
    if (isNull _instigator) then {_instigator = UAVControl vehicle _killer select 0}; // UAV/UGV player operated road kill
    if (isNull _instigator) then {_instigator = _killer}; // player driven vehicle road kill
    

    if (_killed isEqualTo ROGER_BALL ) then {
        ROGER_BALL setVariable["PATROL_LOADOUT_ROGER_BALL",getUnitLoadout ROGER_BALL]};  
    if (_killed isEqualTo EL_GUAPO) then {
        EL_GUAPO setVariable["PATROL_LOADOUT_EL_GUAPO",getUnitLoadout EL_GUAPO]}; 
    if (_killed isEqualTo CRESTRUCTOR) then {
        CRESTRUCTOR setVariable["PATROL_LOADOUT_CRESTRUCTOR",getUnitLoadout CRESTRUCTOR]};
    if (_killed isEqualTo TYRONE) then {
        TYRONE setVariable["PATROL_LOADOUT_TYRONE",getUnitLoadout TYRONE]};
    if (_killed isEqualTo DOC_1) then {
        DOC_1 setVariable["PATROL_LOADOUT_DOC",getUnitLoadout DOC_1]};

    // Print "hint" to all players as to who killed whom
    if ((_instigator isEqualTo ROGER_BALL) OR (_instigator isEqualTo EL_GUAPO) OR (_instigator isEqualTo CRESTRUCTOR) OR (_instigator isEqualTo TYRONE)) then {

        hint format ["%2 eliminated %1.", name _killed,  _instigator]}; 
    
		//##########################################################
		//##########################################################
		//##################    Bohemia Forum     ##################
		//##########################################################
		//##########################################################

		// finally, track nr kills by calling code routine below.

        [name _killed] call incDeath;
    
        systemChat str numDeaths + " " + name _killed;

		//##########################################################
		//##########################################################
		//##########################################################
		//##########################################################
		//##########################################################

}

];  // end addMissionEventHandler "EntityKilled"

//********************************************************************

//**********************************************
// THIS CODE ALSO ADDED FROM MY FORUM QUESTION ABOUT getPlayerScores
// BUT PLACED AFTER THE MEH
//**********************************************

// Keep score of deaths
numDeaths = 0;  // only runs by here once during init? It must. 


// Put the code into a variable
incDeath = {
	// Get the passed parameter (normally the unit)
	_unit = _this select 0;

	// Check if the unit is a player
	if (isPlayer [_unit]) then {
		numDeath = numDeath + 1; // Increament the number of deaths
	};
};

 

So, I am close I guess and the above code you suggested is pasted directly into my init.sqf as shown above.  As you can see, my MEH now includes the code to call "incDeath".

 

Unfortunately, the output I get in-game when someone dies (via SystemChat) is always "0" - i.e., it never increments. 

 

I'm not too clear on "passing a _unit" parameter.  I'm challenged to get a definitive meaning of "_unit". I confuse "_player"with  "_unit"  therefore I'm not too sure if I placed this code as you all had intended. 

 

In full disclosure, I did not write the other code in my MEH.  I simply adapted it from code suggestions I found, albeit it did take me a couple weeks to get it to do what I wanted. So, I did put in lots of time:  1311.4 hours in the past month or so.  Sorry, I digress. 

 

If there is something obvious that I did wrong in this "numDeath" implementation recommended, please point it out and I will fix it.  On the other hand, I don't wish to be a drag on you fellas' time. I promised to report back my results and this is about the size of it. 

 

Thank you for what you have suggested here. I will keep doing some trial & error and reading more.  Cheers to all!!

 

 

 

 

Share this post


Link to post
Share on other sites

Your code is false. You increment suicides + players killed!   That's really what you need???

Share this post


Link to post
Share on other sites
12 hours ago, pierremgi said:

Your code is false. You increment suicides + players killed!   That's really what you need???

 

Thank you for this information. I will fix that later but right now, actually, I'd be happy if ANYTHING was being displayed that indicated to me that any type of increments are happening.  As I mentioned "0" is the only value being displayed. So, I'm actually not even getting "suicides + player killed" --  I get "0" no matter the manner of death, so to speak. That is, whether I sit on a grenade or get shot the chat output is still, always "0".   Any ideas pierremgi?

 

Thank you.

Share this post


Link to post
Share on other sites

Clearly, I'm operating over my head. Dead in the water.  Getting old sucks. LOL.

 

Share this post


Link to post
Share on other sites

I believe the issue here is that the part of the code that increments the numDeaths parameter never runs. Most probably this happens because instead of passing the object to the function you call you pass the name of the object. Thus, the commad isPlayer does not return true and the part of the code that increments the numDeaths parameter never runs. See the BIKI pages of isPlayer and name commands and you will see that one returns a string (the name command that is) while the other expects an object (the isPlayer command). There is a high probability that you haven't enabled the option to be shown the errors that occur during gameplay and if this is the case you wouldn't be able to "catch" that.

 

Now, apart from that I will show you one more way to perform the checks which I personally prefer. This being said, I don't claim that this is neither cleaner nor more efficient code than what you have presented here. I just prefer to do it in the way I present below.

 

Instead of making all those checks I prefer to put the names of the objects/units in an array and use findIf to directly find the position of the object in the array. This is good only for the checks you do for the players. So, copying and adapting your code, I would do like that (showing only the event handler part)

addMissionEventHandler ["EntityKilled", {
	params ["_killed", "_killer", "_instigator"];
	if (isNull _instigator) then {_instigator = UAVControl vehicle _killer select 0}; // UAV/UGV player operated road kill
	if (isNull _instigator) then {_instigator = _killer}; // player driven vehicle road kill
    

	/*
	 * BELOW IS THE PART I CHANGED
	 */
	// Create an array with the names of the players. This could also be a global variable

	private _playas = [ROGER_BALL, EL_GUAPO, CRESTRUCTOR, TYRONE, DOC_1];
	private _check = _playas findIf {_x isEqualTo _killed};

	// Check if the dead was in the array and get the loadout
	if (!(_check isEqualTo -1)) then {
		_killed setVariable ["PATROL_LOADOUT_" + (str _killed), getUnitLoadout, _killed];
	};

	// Check if the killer was one of those that we care about (reuse variable)
	_check = _playas resize ((count _playas) - 2); // Discard DOC_1
	_check = _check findIf {_x isEqualTo _instigator}; // Check for instigator

	/*
	 * The above could be one-lined like this
	 * _check = (_playas resize ((count _playas) - 2)) findIf {_x isEqualTo _instigator};
	 */

	// Print "hint" to all players as to who killed whom
	if (!(_instigator isEqualTo -1)) then {
		hint format ["%2 eliminated %1.", name _killed,  _instigator]}; 
    
		//##########################################################
		//##########################################################
		//##################    Bohemia Forum     ##################
		//##########################################################
		//##########################################################

		// finally, track nr kills by calling code routine below.

		[_killed] call incDeath; // REMOVED THE "NAME" COMMAND HERE IN ORDER TO PASS THE OBJECT AND NOT ITS NAME TO THE CODE
    
		systemChat str numDeaths + " " + name _killed;

		//##########################################################
		//##########################################################
		//##########################################################
		//##########################################################
		//##########################################################
	};
];  // end addMissionEventHandler "EntityKilled"

Not sure this seems better or worse to you, but I thought I should just show you a different way to achieve what you had already achieved. To me, it's good to see how other people do things in order to "broaden my view" and learn new things and commands. If it seems weird and/or you don't want to see another way just ignore it.

 

Hope this whole thing helps somehow. If there's anything else you would like clarified or you want further help please don't hesitate to ask. Finally, please keep in mind that this code snippet is untested so please treat it with extra caution.

  • Like 2

Share this post


Link to post
Share on other sites

 

The very first step is, as usual, clarify in plain language the aim of the script and effect(s) to obtain. Example:

- in MP environment, (dedicated or hosted server, say dedicated), all / specific players can see/display a personal/global score (players, side, group..), for all / AIs / other players killed.... permanent / on any kill / on any player kill / on player kills only . That's the first difficulty, at least for me. Result can be as simple as the code I gave you and tested above, or an elaborated display with multiple entries.

  • Like 2

Share this post


Link to post
Share on other sites
1 hour ago, ROGER_BALL said:

Getting old sucks. LOL.

May we all be blessed with such suckiness.

"Of all the things I've lost over time, I miss my mind the most."  :down: - I can't recall who said that . . . .

 

To reiterate some basic debugging techniques, in case they be forgotten:

 

- 1)  The Debug Console is a tremendous help, but it doesn't always provide an answer by itself.

 

- 2)  When debugging a problematic script, use the startup parameter -showScriptErrors.  When an error occurs, a black box appears which displays the error  (on the machine which encounters the error; sometimes client has an error but server doesn't, and vice versa).  The appearance of the black box helps one to observe exactly when an error occurs as the mission goes on.

 

- 3)  Use the .rpt log to your advantage.  It logs what the error was that occurred.  This means when debugging a problem script, you do not want to use the startup parameter -noLogs, as that prevent errors from getting logged to the .rpt file.  The .rpt file logs the errors in sequence as they occur, and one who is debugging can review them in depth and at leisure to determine what's going wrong.  A casual gamer would want to use the -noLogs param to maybe prevent a huge spam .rpt log getting generated, if perhaps it affects gameplay, but when debugging a problem script, one should not use -noLogs, they would want the log generated.

 

-4)  When no errors are getting thrown, but the results are still unexpected, use diag_log and diag_log format in your code to send messages and variable values to the .rpt log for review.  I could not possibly list the number of times I've had to resort to this method in the end, when all else failed, and achieved success.  A simple nonsense Example:

Spoiler

MySimpleFunct = {
	diag_log "MySimpleFunct running....";
	params ["_unit","_number","_array"];
	diag_log format ["MySimpleFunct - params: _unit: %1; _number: %2; _array: %3",_unit,_number,_array];

	if (isNull _unit) exitWith {diag_log "_unit is Null, exiting MySimpleFunct";};
	if (Count (Units (Group _unit)) > 0) then {
		diaglog format ["MySimpleFunct - group count: %1",Count (Units (Group _unit))];
	} else {
		diag_log "MySimpleFunct - _unit has no group members";
	};

	// . . . a bunch of other code
};

 

And when the problem gets resolved, you go back to remove all the diag_logs and diag_log formats from the code which are no longer needed.

 

Hope this helps.

  • Like 4

Share this post


Link to post
Share on other sites

Holy scripts!  Thanks for these new ideas and especially your time donated to help. I REALLY value your time and effort.

 

Now, I must really read and study the last three posts carefully and do some testing.  As before, I promise to get back here and affirm any successes so others following along will also benefit from your good graces. The "pyramid" of learning, if you will.

 

Back soon & thank you again.

Roger_Ball

 

  • 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

×