• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Snippet] PlayerUtils

Level 7
Joined
Apr 27, 2011
Messages
272
I've seen a lot of kids spam the "Player" function, in an event where in you can just do this and speed things up a little. :D

JASS:
library PlayerUtils initializer PlayerUtils_ini
//===========================================================================
// "PlayerUtils" by Alain.Mark
//
// -Info-
//	-The main purpose of this lib is to get rid of using the Player(...) function
//	and replace it with a faster array search instead. (also w/ added utilities)
//
// -API-
//	-function GetPlayerById takes integer id returns nothing
//	-function GetPlayersCount takes nothing returns integer
//	-function GetHumanPlayersCount takes nothing returns integer
//	-function GetComputerPlayersCount takes nothing returns integer
//
//===========================================================================

	//=======================================================================
		globals
			// The value of "AVAILABLE_SLOTS" is in counting numbers. (starts from 1)
			private constant integer AVAILABLE_SLOTS = 12 
		endglobals
		
	//=======================================================================
		globals
			private constant trigger LEAVER_SENTINEL = CreateTrigger()
			
			private player array IndexedPlayers
			private integer      ACTIVE_PLAYERS_COUNT          = 0
			private integer      ACTIVE_HUMAN_PLAYERS_COUNT    = 0
			private integer      ACTIVE_COMPUTER_PLAYERS_COUNT = 0
		endglobals
		
	//=======================================================================
		function GetPlayerById takes integer id returns player
			return IndexedPlayers[id]
		endfunction
		
	//=======================================================================
		function GetPlayersCount takes nothing returns integer
			return ACTIVE_PLAYERS_COUNT
		endfunction
		
	//=======================================================================
		function GetHumanPlayersCount takes nothing returns integer
			return ACTIVE_HUMAN_PLAYERS_COUNT
		endfunction
		
	//=======================================================================
		function GetComputerPlayersCount takes nothing returns integer
			return ACTIVE_COMPUTER_PLAYERS_COUNT
		endfunction
		
	//=======================================================================
		private function DetectLeavers takes nothing returns nothing
			set ACTIVE_PLAYERS_COUNT=ACTIVE_PLAYERS_COUNT-1
			set ACTIVE_HUMAN_PLAYERS_COUNT=ACTIVE_HUMAN_PLAYERS_COUNT-1
		endfunction
		
	//=======================================================================
		private function IndexPlayers takes nothing returns nothing
			local player  pl
			local integer n = 0
			local integer nMax = AVAILABLE_SLOTS
			local code    c = function DetectLeavers
			loop
				exitwhen n>nMax
				set pl=Player(n)
				if GetPlayerSlotState(pl)==PLAYER_SLOT_STATE_PLAYING then
					if GetPlayerController(pl)==MAP_CONTROL_USER then
						set ACTIVE_HUMAN_PLAYERS_COUNT=ACTIVE_HUMAN_PLAYERS_COUNT+1
						call TriggerRegisterPlayerEvent(LEAVER_SENTINEL,pl,EVENT_PLAYER_LEAVE)
					endif
					set IndexedPlayers[n]=pl
					set ACTIVE_PLAYERS_COUNT=ACTIVE_PLAYERS_COUNT+1
				endif
				set n=n+1
			endloop
			set ACTIVE_COMPUTER_PLAYERS_COUNT=ACTIVE_PLAYERS_COUNT-ACTIVE_HUMAN_PLAYERS_COUNT
			call TriggerAddCondition(LEAVER_SENTINEL,Filter(c))
		endfunction
		
	//=======================================================================
		private function PlayerUtils_ini takes nothing returns nothing
			call IndexPlayers()
		endfunction
		
//===========================================================================
endlibrary

changes:
-now takes computer players into account by default
 
Last edited:
I wrote something similar to this in my map.
The API was something like:

JASS:
struct Player extends array
    static method operator [] takes integer returns player
    static method operator local takes nothing returns player

function IsPlayerPlaying takes player returns boolean
function IsPlayerUser takes player returns boolean
function IsPlayerComputer takes player returns boolean
function IsPlayerLeaver takes player returns boolean

function IsPlayerPlayingById takes integer returns boolean
function IsPlayerUserById takes integer returns boolean
function IsPlayerComputerById takes integer returns boolean
function IsPlayerLeaverById takes integer returns boolean

I was going to submit it, but then I realized that everyone would rage because I'm "using an invalid name for the struct".
 
Level 18
Joined
Oct 20, 2007
Messages
353
4 tips:
1) Instead of setting local integer nMax=PLAYER_SLOTS_AVAILABLE-1 and using exitwhen n>nMax you can use exitwhen n==PLAYER_SLOTS_AVAILABLE (or >=)

2) Because you are using "IndexActivePlayers" only once, you can put its code in "PlayerUtils_ini"

3) You should swap set n=n+1 and set indexedPlayers[n]=pl and add one more counter (because your function will always return 12 active players)

4) You don't need those constants. ConvertPlayerSlotState(1) is already defined as PLAYER_SLOT_STATE_PLAYING, and PLAYER_SLOTS_AVAILABLE you are using only once so you can replace it with number.

JASS:
library PlayerUtils initializer PlayerUtils_ini
//===========================================================================
// "PlayerUtils" by Alain.Mark
//
// -Info-
//     -A simple library that replaces the "Player" function with a faster, more
// efficient and inlineable function instead. This library only takes active players
// into account regardless that they are either controlled by a human or not.
//
// -API-
//     -function GetPlayerById takes integer id returns player
//     -function CountActivePlayers takes nothing returns integer
//
//===========================================================================

    //=======================================================================
        globals
            private integer ACTIVE_PLAYERS_COUNT = 0
            private player array indexedPlayers
        endglobals
        
    //=======================================================================
        function GetPlayerById takes integer id returns player
            return indexedPlayers[id]
        endfunction
        
    //=======================================================================
        function CountActivePlayers takes nothing returns integer
            return ACTIVE_PLAYERS_COUNT
        endfunction
        
    //=======================================================================
        private function PlayerUtils_ini takes nothing returns nothing
            local integer i = 0 // counter
            local player p
            loop
                exitwhen i == 12
                set p = Player(i)
                
                if GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING then
                    set ACTIVE_PLAYERS_COUNT = ACTIVE_PLAYERS_COUNT + 1
                endif
                
                set indexedPlayers[i] = p
                set i = i + 1
            endloop
        endfunction
        
    //=======================================================================
    
//===========================================================================
endlibrary
 
Last edited:
Level 7
Joined
Apr 30, 2011
Messages
359
ACTIVE_PLAYERS_COUNT => ActivePlayersCount, indexedPlayers => IndexedPlayers

JASS:
    //=======================================================================
        private function PlayerUtils_ini takes nothing returns nothing
            local integer i = 0 // counter
            local player p
            loop
                exitwhen i == 12
                set p = Player(i)
                
                if GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING then
                    set ACTIVE_PLAYERS_COUNT = ACTIVE_PLAYERS_COUNT + 1
                endif
                
                set indexedPlayers[n] = p
                set i = i + 1
            endloop
        endfunction
        
    //=======================================================================
to:
JASS:
    //=======================================================================
        private function PlayerUtils_ini takes nothing returns nothing
            local integer i = 0 // counter
            local player p
            loop
                exitwhen i == 12
                set p = Player(i)
                
                if GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING then
                    set ACTIVE_PLAYERS_COUNT = ACTIVE_PLAYERS_COUNT + 1
                endif
                
                set indexedPlayers[@i@] = p
                set i = i + 1
            endloop
        endfunction
        
    //=======================================================================
 
Magtheridon said:
I was going to submit it, but then I realized that everyone would rage because I'm "using an invalid name for the struct".

that's a big problem... I was hoping to see that system... XD

He uses PLAYER_SLOT_STATE_PLAYING for number of times equal to the nMax or on your code, 12, and not just once...

maybe you could add the active players into a player group during the ini just in case someone needs it...
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
After realizing that calling a struct Player makes it impossible for me to call the function Player even inside the library itself, I changed my system to use:
function PlayerEx takes integer i returns player
I gave it TESH Highlighting too <:

It's true than you won't be allowed to use the native function Player anymore in "bottom" libraries, and scopes.
But if you really want to spell your struct Player, you can still use a second library used by your player library, where a global player array is filled with the 16 players.
And then, inside your player library you use this global variable.

Or if jasshelper memorize this struct name in the whole vJass scripts, you still can use a custom common.j or blizzard.j for this global array.

And if you use the Player native when there is now a great way to use a global instead, it will even give you a nice senseless error on compilation to prevent this heresy to happen.
 
The reason we null locals is because of a bug where, upon destroying an object, the handle id will not be recycled unless all locals pointing to the object have been nulled. (or assigned to point to something else) However, since we are dealing with players and code variables which will never be destroyed, we don't really have to null them.
 
Top