Jump to content
Sign in to follow this  
cerberusjin

Multiplayer kill counter

Recommended Posts

I'm trying to use the script I learned from this thread with a little modification to my multiplayer game(arma3).

Instead of Civilians, i wanted to count the number of killed EAST players(or AI) and WEST player(or AI) then multiply it by pointsperkill.

First team to get the needed points win the game.

The codes work but when I added loadout, i started to have some problem. (Script for computing killed EAST player)

Problem(s):

If a player(opfor) joined when the game is already in progress. if he dies, his death is not being counted. (same with blufor)

Also players respawn at the start is being counted as death and being added to score(sometimes 1 respawn adds 2 deaths).

init.sqf

//-----declaration of variables
DeadRedCount = 0;
PointsNeeded = 40;
Pointsperkill = 20;
bluescore = 0;

execVM "scoring.sqf";

//==============LOADOUT
//-----WEST
[west, "WEST1"] call BIS_fnc_addRespawnInventory;
[west, "WEST2"] call BIS_fnc_addRespawnInventory;
[west, "WEST3"] call BIS_fnc_addRespawnInventory;
[west, "WEST4"] call BIS_fnc_addRespawnInventory;
[west, "WEST5"] call BIS_fnc_addRespawnInventory;
[west, "WEST6"] call BIS_fnc_addRespawnInventory;

//-----EAST
[east, "EAST1"] call BIS_fnc_addRespawnInventory;
[east, "EAST2"] call BIS_fnc_addRespawnInventory;
[east, "EAST3"] call BIS_fnc_addRespawnInventory;
[east, "EAST4"] call BIS_fnc_addRespawnInventory;
[east, "EAST5"] call BIS_fnc_addRespawnInventory;
[east, "EAST6"] call BIS_fnc_addRespawnInventory;

scoring.sqf


if isserver then {
//--------dead east players counting

//------counting dead red players/points
func_deadRed = {
	//computing total score of blue team
	bluescore = Pointsperkill * _this;
	publicvariable "bluescore";
	hintsilent format ["Blue: %1",bluescore];

//--------END GAME
if (bluescore >= PointsNeeded) then {
execVM "bluforwin.sqf";
};
	/*
	if (bluescore >= PointsNeeded) then {
   		"End1" call BIS_fnc_endMission; // end mission
	};
	*/
};

 {
   if ((side _x == east) or (side player == east)) then {
     _x addMPEventHandler ["mpkilled", {
       DeadRedCount = DeadRedCount + 1;
       publicvariable "DeadRedCount";
       DeadRedCount call func_deadRed;
     }];
   };
 } foreach allunits;
}
else
{
 // Function to display the death count.
 fnc_showRedDeathCount = {
   hintsilent format ["Blue: %1",bluescore];
 };
 "DeadRedCount" addpublicvariableeventhandler {call func_deadRed };
};

Edited by cerberusjin
added additional problem(s)

Share this post


Link to post
Share on other sites

Firstly, "if isserver then" has to be "if (isServer) then" as an if-statement demands parantheses - always. If it works anyway, you're lucky. ;) But you may want to check your RPT log, client- and serverside. (EDIT: I was taking that assumption as fact as it is that way in Java, C, C++, PHP ...)

To your actual question: Am I blind or do you have no eventhandler for side "opfor"?

Edited by waltenberg

Share this post


Link to post
Share on other sites

yes, i still don't have eventhandler for the opfor.

I don't want to add more codes if there is still an existing problem but i will add it later on when the problem is fixed.

Share this post


Link to post
Share on other sites

Alright I made a mistake. I didn't realize "east" and "opfor" are the same in Arma 3.

Anyway, I needed to backup on the "mpkilled" handler again. But first, you should realize that your if-statement is ALWAYS true! That means that it doesn't matter what side the _x unit is. If the player is of side "east", the eventhandler will be added to every unit.

Then, your counter eventHandler does not distinguish sides. The way it currently is, sets "DeadRedCount = DeadRedCount + 1;", no matter if you kill an east or blufor. So even if an east kills a blufor, it adds +1 to "DeadRedCount".

So you should at first remove the "or (side player == east)" from the currently existing forEach loop and then it should only add +1 if an east kills somebody.

BUT if I understand correctly, that's still not as you want it as it counts "dead red" if a red kills a blue... sort of the wrong way round, so you know what to do. ^_^

Then make a second loop for "west" where you do the same thing, only correct this time. ;)

---------- Post added at 11:56 ---------- Previous post was at 11:45 ----------

Ad lib, I'd consider this to fit your needs. Pretty redundant, but I was a bit lazy. ;)

{
   if(side _x == east) then {
       _x addMPEventHandler [
           "MPKilled",
           {
               DeadBlueCount = DeadBlueCount + 1;
               publicVariable "DeadBlueCount";
               DeadBlueCount call func_deadRed;
           }
       ];
   } else {    //west and others if more side exist
       _x addMPEventHandler [
           "MPKilled",
           {
               DeadRedCount = DeadRedCount + 1;
               publicVariable "DeadRedCount";
               DeadRedCount call func_deadRed;
           }
       ];
   };
} forEach allUnits;

Share this post


Link to post
Share on other sites

You might want to consider using playerSide rather than just east. i.e.

if(side _x != playerSide) then { blah blah balh

In other words

if _x is not on the same side as player then.....

Share this post


Link to post
Share on other sites
Alright I made a mistake. I didn't realize "east" and "opfor" are the same in Arma 3.

Anyway, I needed to backup on the "mpkilled" handler again. But first, you should realize that your if-statement is ALWAYS true! That means that it doesn't matter what side the _x unit is. If the player is of side "east", the eventhandler will be added to every unit.

Then, your counter eventHandler does not distinguish sides. The way it currently is, sets "DeadRedCount = DeadRedCount + 1;", no matter if you kill an east or blufor. So even if an east kills a blufor, it adds +1 to "DeadRedCount".

So you should at first remove the "or (side player == east)" from the currently existing forEach loop and then it should only add +1 if an east kills somebody.

BUT if I understand correctly, that's still not as you want it as it counts "dead red" if a red kills a blue... sort of the wrong way round, so you know what to do. ^_^

Then make a second loop for "west" where you do the same thing, only correct this time. ;)

---------- Post added at 11:56 ---------- Previous post was at 11:45 ----------

Ad lib, I'd consider this to fit your needs. Pretty redundant, but I was a bit lazy. ;)

{
   if(side _x == east) then {
       _x addMPEventHandler [
           "MPKilled",
           {
               DeadBlueCount = DeadBlueCount + 1;
               publicVariable "DeadBlueCount";
               DeadBlueCount call func_deadRed;
           }
       ];
   } else {    //west and others if more side exist
       _x addMPEventHandler [
           "MPKilled",
           {
               DeadRedCount = DeadRedCount + 1;
               publicVariable "DeadRedCount";
               DeadRedCount call func_deadRed;
           }
       ];
   };
} forEach allUnits;

thanks for helping but that didn't solved the problem. and you got it the other way around.

i believed _x is the victim not the killer. which means he is with the EAST/Red Team.

{
   if ((side _x == east) or (side player == east)) then {
     _x addMPEventHandler ["mpkilled", {
       DeadRedCount = DeadRedCount + 1;
       publicvariable "DeadRedCount";
       DeadRedCount call func_deadRed;
     }];
   };
 } foreach allunits;

You might want to consider using playerSide rather than just east. i.e.
if(side _x != playerSide) then { blah blah balh

In other words

if _x is not on the same side as player then.....

I still don't know how to use it. Can you teach me more because my problem is I have 2 teams, East and West.

Edited by cerberusjin

Share this post


Link to post
Share on other sites

Well, to clearify:

_x is the current object in a forEach loop. Say you've got an array of words like this:

_wordArray = ["This", "is", "a", "template", "sentence"];

and you do something like this:

{
  hint format ["%1", _x];
} forEach _wordArray;

then _x is "This" in the first iteration. In the second it's "is", in the third "a" and so on. That's what _x means. This has nothing to do with victim or killer.

Who victim and who killer is, depends on what the "MPKilled" event handler returns. But if you don't use the return values of this very event handler, but simply react if someone gets killed, you need to distinguish the units you add this event handler to.

As your loop is right now, you add the very same event handler to every unit on the map if the playerSide is "east". That means, whenever somebody dies - no matter what side he or his killer is - "DeadRedCount" will be incremented and I don't believe that this is your intention, is it?

(If playerSide was not "east", this applies to every killed unit of side "east".)

Why don't you try out the loop I've posted before. Maybe you add a hint line to see if it's right, like this:

{
   if(side _x == east) then {
       _x addMPEventHandler [
           "MPKilled",
           {
               hint format ["Unit of side '%1' was killed.", side _this]; //the mentioned hint
               DeadBlueCount = DeadBlueCount + 1;
               publicVariable "DeadBlueCount";
               DeadBlueCount call func_deadRed;
           }
       ];
   } else {    //west and others if more side exist
       _x addMPEventHandler [
           "MPKilled",
           {
               hint format ["Unit of side '%1' was killed.", side _this]; //the mentioned hint
               DeadRedCount = DeadRedCount + 1;
               publicVariable "DeadRedCount";
               DeadRedCount call func_deadRed;
           }
       ];
   };
} forEach allUnits;

Edited by waltenberg

Share this post


Link to post
Share on other sites

@waltenberg

I tried your code.

result(s):

I'm getting 2 deaths for east/Red team at the start of the game(not because of your code, I don't know why this is happening. Before I'm getting 1 because of player respawn, but now 2.)

When I killed an East player, it adds two deaths to Red Team. When I killed a West player, it adds zero death to Blue Team.

Share this post


Link to post
Share on other sites
Firstly, "if isserver then" has to be "if (isServer) then" as an if-statement demands parantheses - always. If it works anyway, you're lucky. ;) But you may want to check your RPT log, client- and serverside.

Pretty sure this is entirely incorrect...an if statement demands a boolean value. Parentheses is for the parser's benefit. (Sorry to derail momentarily, was just reading through).

Share this post


Link to post
Share on other sites

@SilentSpike

It's fine. As long as we learned something, it is ok.

Edited by cerberusjin
double post bec of internet problem

Share this post


Link to post
Share on other sites

@SilentSpike

Oops, my bad. In my Job I'm writing Java and as I started learning SQF, I took the parantheses around the condition as "must" and didn't question that as it is that way in almost every programming language.

So nevermind then.

Share this post


Link to post
Share on other sites
i believed _x is the victim not the killer. which means he is with the EAST/Red Team.
You are correct cerberusjin as the EH is being added to say an EAST player then when he dies the red count should go up. _x here as you state is basically the victim.

As an MP eventHandler is added to every connected machine, on a unit dieing the EH will fire on all machines basically multiplying your count by the number of machines.

You need to limit, with in the event were you want to process the counting, the server would be a good idea.

Not properly tested

//==============Declaration of Variables
//[east, west]
sides = [east, west];
DeadCount = [0,0];
scores = [0,0];
PointsNeeded = 80;
Pointsperkill = 20;
gameOver = false;


//==============LOADOUT
{
[west, format ["WEST%1", _x] ] call BIS_fnc_addRespawnInventory;
[east, format ["EAST%1", _x] ] call BIS_fnc_addRespawnInventory;
}forEach [1,2,3,4,5,6];


//==============Client Side
if (!isDedicated) then {

scoreDisplayRefresh = 120;

//A function to show scores - also called via server when someone is killed
fnc_showScores = {
	hintSilent format["EAST vs WEST\n\nDeaths per Side\n%1  /  %2\n\nScore\n%3  /  %4",
	DeadCount select 0, DeadCount select 1, scores select 0, scores select 1];
	lastScoreTime = time + scoreDisplayRefresh;
};

//Display scores every 2 mins
_refreshScoresThread = [] spawn {
	while {!gameOver} do {
		[] call fnc_showScores;
		waitUntil { time > lastScoreTime };
	};
};

};


//==============Server Side
if (isServer) then {

//Funtion to calculate scores
func_addScore = {
	//Passed side from EH
	_side = _this;
	//Get an index for retrieving scores
	_index = sides find _side;
	//Get the sides score and add points
	scores set [_index, (scores select _index) + Pointsperkill];
	//Share new scores across the network
	publicVariable "scores";
	//Show scores on clients
	[[],"fnc_showScores",true,false] call BIS_fnc_MP;
};

//Add evenHandlers to all units
{
	switch (side _x) do {
		case east : {
			_x addMPEventHandler ["MPKilled", {
				//Only on the server
				if (isServer) then {
	     			DeadCount set [0, (DeadCount select 0) + 1];
					publicVariable "DeadCount";
					//add score to WEST
					west spawn func_addScore;
				};
			}];
		};
		case west : {
			_x addMPEventHandler ["MPKilled", {
				//Only on the server
				if (isServer) then {
	         	DeadCount set [1, (DeadCount select 1) + 1];
					publicVariable "DeadCount";
					//Add score to EAST
					east spawn func_addScore;
				};
			}];
		};
	};
} forEach allUnits;

//Spawn a thread on the server to monitor for game over
_gameOverThread = [] spawn {

	//Has one of the teams reached score limit
	waitUntil { {_x >= PointsNeeded }count scores > 0 };
	gameOver = true;
	publicVariable "gameOver";

	//Who reached point limit
	_index = scores find PointsNeeded;
	//Pick winner and loser
	_winner = sides select _index;
	_loser = (sides - [_winner]) select 0;

	//Send end mission to clients - based on if they won or lost
	[["SideLost",false,5],"BIS_fnc_endMission", _loser, false] call BIS_fnc_MP;
	[["SideWon",true,true],"BIS_fnc_endMission", _winner, false] call BIS_fnc_MP;
};
};

TEST

Share this post


Link to post
Share on other sites

Hello everyone,

I believe your problem here is that you are using the "killed" event handler incorrectly. It seems you are checking for a death, and then incrementing the player's score based on a made up variable. There are scripting commands already implemented to increment score.

Important: The "killed" and "MPkilled" event handler can actually check which player killed another player. (_this select 1)

Try adding this to your units, whenever you add event handlers to them:

this addEventHandler ["MPkilled",
{
_this select 1 addScore 1;
side _this select 1 addScoreSide 1;
chat sideChat format["%1 has killed an enemy", name _this select 1]; //place a unit in editor named "chat"
//	westDead = westDead + [_this select 0]; //make sure you define these at the beginning of the script. westDead = []; for example
//	eastDead = eastDead + ]_this select 0];
//	guerDead = guerDead + [_this select 0];
//	civDead = civDead + [_this select 0]; //Uncomment whichever side you want to check for. The array returns OBJECTS (for ease, you can think of them as the names of the units that died. To access a total you need to use "count westDead" for example).
}];
//I purposefully did not add parenthesis in this snippet to show places where they are not needed. This code should still work just fine. In this example, they are for the coder ONLY.

Players can then press "I" in game (default key) to show the scorebox (I believe this is multiplayer only). If you really need to check the number of players on each side that died it's a lot more complicated and I still haven't come up with a way to do this yet. The reason for that is because when a unit dies, it's side changes to civ, yet you can't check which side it was originally on when you use the "killed" event handler (it only returns 2 arguments, and neither of them are the killed unit's original side).

Also, "if" statements absolutely require parenthesis. Tested by removing a few parenthesis from my simple patrol script:

if floor random 2 == 0 then
	{
		_posInTrigger set [0, (random (_params select 0))];
	} else {
		_posInTrigger set [0, -(random (_params select 0))];
	};

Error is "Type Number, Expected Bool". Off topic, but I saw it mentioned in the thread, so I thought I'd throw my 2 cents.

Edited by DreadedEntity

Share this post


Link to post
Share on other sites

@Larrow

i tried to use your code. It works perfectly if only for these two

(1) at the start the of the game when players spawned, it is being counted as death

(2) if a player joins a game in progress his/her death is not being counted.

btw, thanks for the help. I'll just find a solution for these problems.

@DreadedEntity

I tried the EH but i can't find a way to make it work or because i'm doing it wrong.

I'm suppose to put it in player's init right then name him "chat"?

then at init.sqf, define westDead = [];

and remove //(comment) for //westDead = westDead + [_this select 0];

and also I have questions about these 2 lines

_this select 1 addScore 1; //select 1 is for? what will happen for team kill ?
side _this select 1 addScoreSide 1; //select 1 is for?

DreadedEntity gave me an idea so I tried this.

init.sqf


//==============LOADOUT
//-----WEST
[west, "WEST1"] call BIS_fnc_addRespawnInventory;
[west, "WEST2"] call BIS_fnc_addRespawnInventory;
[west, "WEST3"] call BIS_fnc_addRespawnInventory;
[west, "WEST4"] call BIS_fnc_addRespawnInventory;
[west, "WEST5"] call BIS_fnc_addRespawnInventory;
[west, "WEST6"] call BIS_fnc_addRespawnInventory;

//-----EAST
[east, "EAST1"] call BIS_fnc_addRespawnInventory;
[east, "EAST2"] call BIS_fnc_addRespawnInventory;
[east, "EAST3"] call BIS_fnc_addRespawnInventory;
[east, "EAST4"] call BIS_fnc_addRespawnInventory;
[east, "EAST5"] call BIS_fnc_addRespawnInventory;
[east, "EAST6"] call BIS_fnc_addRespawnInventory;

//======declaration of variables
DeadRedCount = 0;
DeadBlueCount = 0;
PointsNeeded = 60;
Pointsperkill = 20;
bluescore = 0;
redscore = 0;

//publicvariable "DeadRedCount";
//publicvariable "DeadBlueCount";
//publicvariable "PointsNeeded";
//publicvariable "Pointsperkill";
//publicvariable "bluescore";
//publicvariable "redscore";


//=======Score Display
while {true} do
{
SCORE_01_Load = {((_this select 0) displayCtrl 1001) ctrlSetText format ["%1", bluescore]};
1 cutRsc ["score_test","PLAIN"];

SCORE_02_Load = {((_this select 0) displayCtrl 1002) ctrlSetText format ["%1", redscore]};
2 cutRsc ["score_test2","PLAIN"];
sleep 0.125;
};

every EAST playable/player's init

if(isserver) then {this addMPEventHandler ["MPkilled",{redscore = redscore + Pointsperkill; publicVariable "redscore";}];};

every WEST playable/player's init

if(isserver) then {this addMPEventHandler ["MPkilled",{bluescore = bluescore + Pointsperkill; publicvariable "bluescore";}]; };

it solved the problem of player's death who joined game in progress. They are now being counted.

Problem: The score being displayed is out of sync for the players who join game in progress and will be updated if a score was added. (I guess I will be using addScoreSide and scoreSide instead)

Edited by cerberusjin

Share this post


Link to post
Share on other sites
(1) at the start the of the game when players spawned, it is being counted as death

I think this is most likely to be due to the BIS respawn on start. Try only applying the EH's after

player getVariable ["bis_fnc_selectRespawnTemplate_respawned",false]

is true.

(2) if a player joins a game in progress his/her death is not being counted.

Use the initPlayerServer to call your code that adds the EH's. As currently the EH's are applied in the init.sqf by the server, once a mission has started this has already been done, When the JIP joins the EH's are not being added.

See this thread for more info, not exactly your problem but, is the same effect, as in your EH code needs to be applied on the server once the respawnOnStart has finished (so as not to count it as a death) and also for JIP but instead of using initPlayerLocal.sqf use initPlayerServer instead.

---------- Post added at 13:16 ---------- Previous post was at 12:43 ----------

If you really need to check the number of players on each side that died it's a lot more complicated and I still haven't come up with a way to do this yet. The reason for that is because when a unit dies, it's side changes to civ, yet you can't check which side it was originally on when you use the "killed" event handler (it only returns 2 arguments, and neither of them are the killed unit's original side).
If the unit in question is a player then you can use playerSide, this will return their actual side rather than the CIV that they get put on when dead.

Other possibilities of the top of my head would be :-

In the init store a variable on the unit of his side

{ _x setvariable ["side", side _x]; } forEach allUnits

then just reference this from the EH to get its proper side.

or

Get a reference to the entities config value of what side it belongs to

_side = (getNumber (configFile >> "CfgVehicles" >> typeOf _unit >> "side")) call BIS_fnc_sideType

Although this one only works if your using units on their predetermined side. e.g if you have grouped a B_Soldier_F to a CSAT soldier to make him EAST then the return result is going to be wrong.

Share this post


Link to post
Share on other sites

Also, "if" statements absolutely require parenthesis. Tested by removing a few parenthesis from my simple patrol script:

if floor random 2 == 0 then
       {
           _posInTrigger set [0, (random (_params select 0))];
       } else {
           _posInTrigger set [0, -(random (_params select 0))];
       };

Error is "Type Number, Expected Bool". Off topic, but I saw it mentioned in the thread, so I thought I'd throw my 2 cents.

They absolutely don't require parenthesis. As you can see right there in the error message it expected a boolean value. However, because of the lack of parenthesis the parser did not return a boolean value after the if statement (which matches precisely with my earlier statement). In you example, the parser's thought process basically went:

If 1 -> I don't know what that means -> Have an error saying I don't know what that means

If you were to script the following then I guarantee it would work:

if isServer then {hint "true"} else {hint "false"};

And it works with no parenthesis because isServer returns a boolean value directly after the if statement.

Edit: And to clarify, it will execute whatever is after the if statement until a value is returned. So in your example it does floor(random 2) and that's the first value returned so it ignores the rest. Whereas if you use brackets, obviously those first two commands only return 0/1 within the brackets so it executes anything else in there and the brackets then return the boolean returned by the comparison. (I don't know all the correct terminology as I have no extensive background in programming, hopefully the order of operation makes sense in this way).

Edited by SilentSpike

Share this post


Link to post
Share on other sites
@DreadedEntity

I tried the EH but i can't find a way to make it work or because i'm doing it wrong.

I'm suppose to put it in player's init right then name him "chat"?

then at init.sqf, define westDead = [];

and remove //(comment) for //westDead = westDead + [_this select 0];

and also I have questions about these 2 lines

_this select 1 addScore 1; //select 1 is for? what will happen for team kill ?
side _this select 1 addScoreSide 1; //select 1 is for?

When I look back at it, sideChat wasn't really the best way to do this, it says it doesn't broadcast but I'm pretty sure it does send a sidechat message to all players on that team. Anyway, sideChat requires a real unit to actually chat so you need to spawn a unit with the name you are using or put one down in the editor and name it.

If you were going to use my event handler code, you would replace your event handler code with that. (simply copy+pasting that into the INIT field of each unit will work, make sure you uncomment the dead counter for whichever side the unit is on.

To answer your 2 questions:

_this select 1 refers to the unit that killed the unit that the "killed" event handler is attached to (the killer, if my explanation was confusing). If a player teamkills, that player's score and his team will still be incremented by 1, you'll need to add your own teamkill checking and result.

Click this. On the right side you'll see unit and killer. Unit is _this select 0 and killer is _this select 1

Hope that helps clear up my last post.

Edited by DreadedEntity

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
Sign in to follow this  

×