Jump to content
ROGER_BALL

getPlayerScores -- has me totally baffled.

Recommended Posts

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
  • Thanks 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

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

My apologies. I promised to try this over the weekend but "domestic duties" took over my to-do list. Happy wife; happy life.  (as they say).

 

Anyway, I really want to try this and will definitely report back here. Sorry for the delay.

 

Roger_Ball

  • Like 1

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 6

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

UPDATE:  Even with all the good graces and help from you all, I must be operating outside my pay grade and can't get things going like I wanted.  Don't think I'm giving up though nor is your time wasted.  I copied / pasted this thread's contents to this project's folder and saved it. As I learn more and more, there will come a day when I get that pay raise.  🙂

 

When that day comes, I will return here to celebrate and thank you all again.

 

Cheers

Share this post


Link to post
Share on other sites

Next help should come if you have a clear message on what you intend to do. Usually topics fail when we are speaking about code and code subtleties, sometimes about details,... before drawing the context and the final intended result.

  • Like 2

Share this post


Link to post
Share on other sites

SUCCESS ! I wish to thank you:

 

Thank you all for spending your time and sharing your expertise and helpful code. I have taken what I learned from each contributor and experimented with the code using the DEBUG console in the 'Editor' and placed code in my mission that does exactly what I want.  Thanks to you and for what it's worth, each example and comment you all wrote in this thread was given 100% attention. My feeling is that if someone is going to donate their time and expertise to enlighten those of us on the bottom of the coding food chain, then by golly we owe the utmost of our effort to grasp, understand and implement that expertise. In this case, this thread provided me with outstanding examples and code to dissect for learning and use in my mission plan. While the effort took days and days from Oct 2021 until now to get everything working (error free), this whole experience was very enlightening and I have you to thank. Just in the process of Googling and researching your code structures and syntax, the learning curve was a steep climb. A whole lot more learning of peripheral topics naturally occurred. So, not only did I appreciate what you all wrote, the process in and of itself snowballed - gathering all sorts of great stuff along the way. It was easy to go down rabbit holes via Google/Bohemia but the trip was always a learning experience. Multiplicative learning, if you will.

 

Sorry for the poor problem statement from the beginning. From now on,  I will start my post with all the elements to define the goals as suggested by Pierremgi. Thank you. In retrospect, lacking this first step on my part, I'm surprised you all took the time to untangle my question and sift it for the facts. I double-thank you for that and the help here on the forum. I have about 3,500 hours spent on ARMA3, both gaming and mission designing and debugging. Hardly a day goes without a 2 to 3 hour session with ARMA III. I just absolutely love the immersion of it all whether it's playing or mission designing & debugging.  (In present company, I use of the terms very loosely in claiming that I am "mission designing & debugging"! ) .  "Attempting" to do so is more like it.  

 

Again, I can't thank you all enough. How could I ever return the favor.

 

Best regards,

Roger Ball

 

On 10/29/2021 at 4:33 PM, pierremgi said:

Next help should come if you have a clear message on what you intend to do. Usually topics fail when we are speaking about code and code subtleties, sometimes about details,... before drawing the context and the final intended result.

 

  • Like 3

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

×