Jump to content
🛡️FORUMS ARE IN READ-ONLY MODE Read more... ×
HolyLight56

Setting values in nested array help/debugging

Recommended Posts

I'm trying to modify an array whenever a player respawns to lower their individual respawn tickets. The current issue I am facing is changing the second value of each pair to the desired number. (Note: some code may be placeholders, full code below)

 

The array will look something like this:
playerArray = [["Player1UID", X], ["Player2UID", X], ["Player3UID", X]]

Code I'm trying to use:
playerArray set [((playerArray select 0) select 1), X-1];                                 // Lowers the old value by 1 in the array

Desired Result:

playerArray = [["Player1UID", X-1], ["Player2UID", X], ["Player3UID", X]]

Actual Result
playerArray = [["Player1UID", X], ["Player2UID", X], ["Player3UID", X], X-1]


Full script:

Spoiler

(_this select 0) addMPEventHandler ["MPRespawn", {
playerUID = getPlayerUID (_this select 0);                                                                   // Grabs the player's UID
_index = [playerArray, _playerUID] call BIS_fnc_findNestedElement;                      // Finds the position of the player's UID in the array
_oldTickets = ((playerArray select (_index select 0)) select 1);                               // Determines the value of the player's ticket count before death
_Newtickets = _oldTickets - 1;                                                                                      // Lowers their ticket count by one
playerArray set [((playerArray select (_index select 0)) select 1), _NewTickets]; // Changes the old ticket value to the new value
if (_Newtickets < 1) then {if (Spectator_After_Final_Death == 1) then ["Initialize", [_this, [], true ]] call BIS_fnc_EGSpectator;}; 
// starts spectator cam if they run out of lives
}];

 

Any help, comments, or suggestions would be amazing. Thank you and have a great day.

Edited by HolyLight56
added insight/resources

Share this post


Link to post
Share on other sites

playerArray = [["Player1UID", X], ["Player2UID", X], ["Player3UID", X]];

 

private _index = playerArray apply {_x#0} find _playerUID;

_oldTickets = playerArray #_index #1;

(playerArray #_index) set [1, _oldTickets -1];

  • Like 2

Share this post


Link to post
Share on other sites

Thank you for the reply pierremgi. I did some more testing with your code and I ended up with something that seems to work.

Working Testing Code:

Spoiler

_playerUID = getPlayerUID _this;                                                                                  // grabs the player's UID
private _index = [playerArray, _playerUID] call BIS_fnc_findNestedElement;        // Finds it's position in the array
_oldTickets = (playerArray #(_index #0)) #1;                                                            // Determines how many tickets they had
(playerArray #(_index #0)) set [1, _oldTickets -1];                                                    //  Reduces the number of tickets by one

 

I'll be posting the full code once it's been thoroughly tested. Again, thank you for the help.

(Note: I was using ZEN, so '_this' worked rather than 'this'

Share this post


Link to post
Share on other sites

Unless you have a specific reason to use arrays this would be a perfect use case for a Hashmap.

 

Same example using a Hashmap: 

playerMap = createHashmapFromArray [["Player1UID", X], ["Player2UID", X], ["Player3UID", X]];

 

_playerUID = getPlayerUID _this; // grabs the player's UID

_oldTickets = playerMap get _playerUID; // Determines how many tickets they had

playerMap set [ _playerUID, _oldTickets -1]; // Reduces the number of tickets by one

 

Less code, easier to read and lightning fast even with thousands of players in the playerMap.

  • Like 3

Share this post


Link to post
Share on other sites
8 hours ago, mrcurry said:

createHashmapFromArray

 

Man, I really need to read the SPOTREPs. Thanks for bringing this one to my attention, @mrcurry!

Share this post


Link to post
Share on other sites

I'll be needing to add player's UIDs and set their ticket amounts throughout the mission. If it were a set list of players, i'd absoultely do as you said and use the hashmap. I'll keep looking into how to properly implement the code. 
@mrcurry

Currently I'm having a hard time understanding how to get arrays and variables from global space, and then execute a function to change global variables and arrays. (updating the player array with a correct ticket count for each player).
I'm sure there is an easy solution, the only problem is finding it.

Thank you for pointing me towards hashmaps, they seem perfect this and i'll see if i might be able to implement them.

Share this post


Link to post
Share on other sites

@HolyLight56

To make an empty Hashmap use command:

variable = createHashmap;

To add new elements to the map:

variable set [ key, value ]

In your case the key would of course be the PlayerUID string and value be the number of respawn tickets remaining.

 

The BIKI page on hashmaps is a great place to start to get to grips with them:

https://community.bistudio.com/wiki/HashMap

 

As for your respawn tickets system my tip would be:

  1. Let the server initialize the hashmap on init and set tickets for all present players.
  2. On server add a mission eventhandler to handle PlayerConnected events which add new players to the hashmap.
  3. Use remoteExec in client-code when there is need to read or update the Hashmap.

Share this post


Link to post
Share on other sites

Yep!

Don't forget that keys can't be object variables. You can (need to) use strings of playerUIDs but not p1, p2, p3... as variables of playable units.

Share this post


Link to post
Share on other sites

So i've done some more work on the code, and after changing it to hashmaps and their corresponding codes, i've gotten approximately what @mrcurry posted previously.

On 12/10/2021 at 4:49 AM, mrcurry said:

_playerUID = getPlayerUID _this; // grabs the player's UID

_oldTickets = playerMap get _playerUID; // Determines how many tickets they had

playerMap set [ _playerUID, _oldTickets -1]; // Reduces the number of tickets by one



Unfortunately, remoteExec can't target hashmaps so i've defaulted to using the above code. Which, while working on singleplayer perfectly fine, appears to not be working in multiplayer. I'm still tryin to find some way to use remoteExec with hashmaps, but again, i've hit a bit of a brick wall.

The initServer file looks like this right now:

Spoiler

addMissionEventHandler ["PlayerConnected", {
    params ["_id", "_uid"];
    _respawn_Tickets = ((missionNamespace getVariable "Respawn_Tickets") + 1);
    if (isNil {playerHashMap get _uid}) then {playerHashMap set [_uid, _respawn_Tickets];};
}];


I've added an if statement to only add the player's UID to the hashMap if it doesn't already exist there. Without being able to use remoteExec, i'm not sure how to go forward on making this work in a multiplayer environment.

Share this post


Link to post
Share on other sites
On 12/9/2021 at 5:54 AM, HolyLight56 said:

I'm trying to modify an array whenever a player respawns to lower their individual respawn tickets. The current issue I am facing is changing the second value of each pair to the desired number. (Note: some code may be placeholders, full code below)

 

Any help, comments, or suggestions would be amazing. Thank you and have a great day.

 

 @HolyLight56  The previous answers focused on your nested array then hashmaps... but, as far as respawn tickets are concerned, why not trying: Bis_fnc_respawnTickets ? you can run it on player(s) and effect is already global.

Share this post


Link to post
Share on other sites
On 12/16/2021 at 11:35 PM, HolyLight56 said:

Unfortunately, remoteExec can't target hashmaps

Oh well technically it can but for this its a bit stupid to transmit the entire map across the network. Besides we don't need to...

 

Spoiler

initServer.sqf


// =========== initServer.sqf ===========
initial_respawn_tickes = 3;
respawnTicketsMap = createHashMap;

// Function to send respawn tickets back to client
TAG_fnc_playerRespawnedServer = {
	params ["_player"];

	private _uid = getPlayerUID _player;
	private _respawnTickets = respawnTicketsMap get _uid;
	respawnTicketsMap set [_uid, (_respawnTickets - 1) max 0];

	[_respawnTickets] remoteExec ["TAG_fnc_playerRespawnedClient", remoteExecutedOwner];
};

// Set initial player respawn tickets
{
	respawnTicketsMap set [ getPlayerUID _x, initial_respawn_tickes ];
} forEach allPlayers;

// Add event handler to handle connecting players
addMissionEventHandler [
	"PlayerConnected", 
	{
		params ["_id", "_uid"];
		if !( _uid in respawnTicketsMap ) then {
			respawnTicketsMap set [_uid, initial_respawn_tickes];
		};
	}
];

initPlayerLocal.sqf


// =========== initPlayerLocal.sqf =============
// Function to handle respawn on tickets received
TAG_fnc_playerRespawnedClient = {
	params ["_tickets"];
  	respawnTicketsRemaining = _tickets;
	if( _tickets > 0 ) then {
		// Code to run on normal respawn goes here
	} else {
		// Code to run when tickets has run out goes here
	};
};

// Eventhandler to handle player respawn
player addEventHandler [
	"Respawn",
	{
		[player] remoteExec ["TAG_fnc_playerRespawnedServer", 2];
	}
];

 

 

  • Like 1

Share this post


Link to post
Share on other sites

Thank you for the help mrcurry and pierremgi, and sorry for the long wait. I've finalized the script (included below) and now it's working properly.

initServer.sqf

Spoiler

 

// =========== initServer.sqf ===========
initial_respawn_tickets = 4;         // NOTE: Set one higher than desired if players initially respawn
Spectator_After_Final_Death = 1;
respawnTicketsMap = createHashMap;
 

// Function to send respawn tickets back to client
TAG_fnc_playerRespawnedServer = {
    params ["_player"];

    private _uid = getPlayerUID _player;
    private _respawnTickets = respawnTicketsMap get _uid;
    respawnTicketsMap set [_uid, (_respawnTickets - 1) max 0];
    private _newrespawnTickets = respawnTicketsMap get _uid;
    
    [_newrespawnTickets, Spectator_After_Final_Death] remoteExec ["TAG_fnc_playerRespawnedClient", remoteExecutedOwner];
};
 

// Set initial player respawn tickets
{
    respawnTicketsMap set [ getPlayerUID _x, initial_respawn_tickets ];
} forEach allPlayers;
 

// Add event handler to handle connecting players
addMissionEventHandler [
    "PlayerConnected", 
    {
        params ["_id", "_uid"];
        if !(_uid in respawnTicketsMap) then {
            respawnTicketsMap set [_uid, initial_respawn_tickets];
        };
    }
];
 

// Extra scripts and functions for your pleasure
 

// Function: to set a player's number of tickets
// Example: [_this, 3] call TAG_fnc_SetPlayerRespawnTickets;
TAG_fnc_SetPlayerRespawnTickets = {
    params ["_player", "_setTickets"];

    private _networkId = owner _player;
    private _uid = getPlayerUID _player;
    private _respawnTickets = respawnTicketsMap get _uid;
    respawnTicketsMap set [_uid, _setTickets max 0];

    [_setTickets, Spectator_After_Final_Death] remoteExec ["TAG_fnc_playerRespawnedClient", _networkId];
};
 

// Function: to add a number of tickets to a player (negative numbers subtract that amount, mimimum 0)
// Example: [_this, 3] call TAG_fnc_AddPlayerRespawnTickets;
TAG_fnc_AddPlayerRespawnTickets = {
    params ["_player", "_AddedTickets"];

    private _networkId = owner _player;
    private _uid = getPlayerUID _player;
    private _respawnTickets = respawnTicketsMap get _uid;
    respawnTicketsMap set [_uid, (_AddedTickets + _respawnTickets) max 0];
    private _newrespawnTickets = respawnTicketsMap get _uid;

    [_newrespawnTickets, Spectator_After_Final_Death] remoteExec ["TAG_fnc_playerRespawnedClient", _networkId];
};
 

// Script to reset all players tickets to their initial amount
// {[_x, (initial_respawn_tickets - 1)] call TAG_fnc_SetPlayerRespawnTickets;} forEach allPlayers;


initPlayerLocal.sqf

Spoiler

// =========== initPlayerLocal.sqf =============
// Function to handle respawn on tickets received
TAG_fnc_playerRespawnedClient = {
    params ["_tickets", "_Spectator_After_Final_Death"];
    
    private _networkId = clientOwner;
    if (_tickets > 0) then {
        ["Terminate", [player]] remoteExec ["BIS_fnc_EGSpectator", _networkId]; [player, false] remoteExec ["hideObjectGlobal", 2];
    } else {
        if (_Spectator_After_Final_Death == 1) then {["Initialize", [player]] remoteExec ["BIS_fnc_EGSpectator",_networkId]; [player] remoteExec ["hideObjectGlobal", 2];};
    };
};


// Eventhandler to handle player respawn
player addEventHandler [
    "Respawn",
    {
        [player] remoteExec ["TAG_fnc_playerRespawnedServer", 2];
    }
];

 

 

Edited by HolyLight56
finalized script

Share this post


Link to post
Share on other sites

×