include "cj_types.j"
library PlayerManager initializer Ini {
/*===================================================================
Name: C Player Manager
Version: 3.4
Author: Nestharus
Settings:
*///===================================================================
//remove all units owned by players not in map at ini
define REMOVE_UNITS_ON_INI_FOR_INACTIVE = false
//remove all units owned by player when that player leaves, done after event fire
define REMOVE_UNITS_ON_LEAVE = false
define TRACK_HUMAN = true
define TRACK_COMPUTER = false
define TRACK_ACTIVE = false
define TRACK_INACTIVE = false
define KICK_PLAYER_ON_REMOVE = true
//fire events when a human leaves/removed
define HUMAN_OUT_EVENT = true
//fire events when a computer is removed
define COMPUTER_OUT_EVENT = false
//for use with GetHiddenPlayer
//player 14 is suggested as it has bounty etc enabled
define private HIDDEN_PLAYER_ID = 14
/*//===================================================================
Player Manager keeps track of players in linked lists for easier traversal, faster loops, and less operations.
It has 5 collections of players-
HumanPlayer
Has all active human players in the game in order
ComputerPlayer
Has all active computer players in the game in order
ActivePlayer
Has all active players in the game in order
InactivePlayer
Has all inactive players in the game in order
Players
Has all players in the game in order
All collections are kept updated through out the game except for Players as it will always contain
all players 0-15 within the game. All collections always maintain initial order.
Example- 1, 3, 5, and 8 are active human players
5 leaves
1, 3, 8 would now be in human players in that order
InactivePlayers are stored in the order of leaving players, so it's possible to see who
left first and who left last.
All functions and collections relating to specific features are only in the code if specified in settings.
For this reason, all setting definitions that relates to code that might be removed are not private.
To keep code from breaking and keep code efficient, use #if with the settings.
API-
Structs:
HumanPlayer, ComputerPlayer, ActivePlayer, InactivePlayer
Propertiers:
thistype first
First player (id)
thistype last
Last player (id)
thistype next
Next player (id)
thistype previous
Previous player (id)
player get
Get player (id). If get is null, player is not in the collection.
static int count
Gets the total player count
Methods:
HumanPlayer and ComputerPlayer only-
void remove()
Removes player from the game
static triggercondition addOnOut(boolexpr c)
Adds boolexpr to player out trigger and returns the triggercondition
static void removeOnOut(triggercondition c)
Removes triggercondition from player out trigger
Players
player get
Gets the player handle
bool active
Is player active?
static player lastRemoved
Stores last removed player for event response
static int lastRemovedId
Stores the id of last removed player for event response
static player getLocal
GetLocalPlayer() but faster
static player hidden
HIDDEN_PLAYER_ID
===================================================================*/
private player localPlayer
private bool isPlayerActive[]
private player lastRemovedPlayer = null
private int lastRemovedPlayerId = 0
private player players[]
private trigger humanLeaves;
define private REMOVE_PLAYER(PLAYER_TYPE, PLAYER_OUT_EVENT) = {
public void remove() {
#if KICK_PLAYER_ON_REMOVE
RemovePlayer(.get, PLAYER_GAME_RESULT_DEFEAT)
if (localPlayer == .get) {
EndGame(false)
}
#endif
REMOVE_PLAYER(PLAYER_TYPE, .get, PLAYER_OUT_EVENT)
}
}
define private PLAYER_STRUCT(PLAYER_TYPE) = {
private struct P_##PLAYER_TYPE extends array {
public static thistype first = 0
public static thistype last = 0
public static int count = 0
public player get;
public thistype next;
public thistype previous;
}//end struct P_##PLAYER_TYPE
struct PLAYER_TYPE extends array {
public player operator get() {
return P_##PLAYER_TYPE[this].get;
}//end operator getPlayer
public static thistype operator first() {
return P_##PLAYER_TYPE[this].first;
}//end static operator first
public static thistype operator last() {
return P_##PLAYER_TYPE[this].last;
}//end static operator last
public static int operator count() {
return P_##PLAYER_TYPE[this].count;
}//end static operator count
public thistype operator next() {
return P_##PLAYER_TYPE[this].next;
}//end operator next
public thistype operator previous() {
return P_##PLAYER_TYPE[this].previous;
}//end operator previous
#if PLAYER_TYPE == HumanPlayer
REMOVE_PLAYER(PLAYER_TYPE, HUMAN_OUT_EVENT)
#if HUMAN_OUT_EVENT
public static trigger out = CreateTrigger();
public static triggercondition addOnOut(boolexpr c) {return TriggerAddCondition(out, c);}
public static void removeOnOut(triggercondition c) {TriggerRemoveCondition(out, c);}
#endif
#endif
#if PLAYER_TYPE == ComputerPlayer
REMOVE_PLAYER(PLAYER_TYPE, COMPUTER_OUT_EVENT)
#if COMPUTER_OUT_EVENT
public static trigger out = CreateTrigger();
public static triggercondition addOnOut(boolexpr c) {return TriggerAddCondition(out, c);}
public static void removeOnOut(triggercondition c) {TriggerRemoveCondition(out, c);}
#endif
#endif
}//end struct Players
}//end define PLAYER_STRUCT
#if TRACK_HUMAN
PLAYER_STRUCT(HumanPlayer);
#endif
#if TRACK_COMPUTER
PLAYER_STRUCT(ComputerPlayer);
#endif
#if TRACK_ACTIVE
PLAYER_STRUCT(ActivePlayer);
#endif
#if TRACK_INACTIVE
PLAYER_STRUCT(InactivePlayer);
#endif
struct Players extends array {
public player operator get() {
return players[this]
}//end player operator get()
public bool operator active() {
return isPlayerActive[this]
}//end bool operator active
public static player operator lastRemoved() {
return lastRemovedPlayer
}//end player operator lastRemoved
public static int operator lastRemovedId() {
return lastRemovedPlayerId
}//end player operator lastRemovedId
public static player operator getLocal() {
return localPlayer
}//end player operator local
public static player operator hidden() {
return players[HIDDEN_PLAYER_ID];
}//end player operator hidden
}//end struct Players
#if REMOVE_UNITS_ON_INI_FOR_INACTIVE || REMOVE_UNITS_ON_LEAVE
private group removeUnitGroup = CreateGroup();
private boolexpr removeUnits
#endif
define private REMOVE_FROM_PLAYER_STRUCT(PLAYER_TYPE, WHICH_PLAYER_ID) = {
//decrease count
P_##PLAYER_TYPE.count--
//remove from PLAYER_TYPE struct
if (WHICH_PLAYER_ID == P_##PLAYER_TYPE.last) {
P_##PLAYER_TYPE.last = P_##PLAYER_TYPE[WHICH_PLAYER_ID].previous
}//end if
if (WHICH_PLAYER_ID == P_##PLAYER_TYPE.first) {
P_##PLAYER_TYPE.first = P_##PLAYER_TYPE[WHICH_PLAYER_ID].next
}//end elseif
P_##PLAYER_TYPE[WHICH_PLAYER_ID].next.previous = P_##PLAYER_TYPE[WHICH_PLAYER_ID].previous
P_##PLAYER_TYPE[WHICH_PLAYER_ID].previous.next = P_##PLAYER_TYPE[WHICH_PLAYER_ID].next
//set to null
P_##PLAYER_TYPE[WHICH_PLAYER_ID].get = null
}//end define REMOVE_FROM_PLAYER_STRUCT(PLAYER_TYPE, WHICH_PLAYER)
define private ADD_TO_PLAYER_STRUCT(PLAYER_TYPE, WHICH_PLAYER_ID) = {
//increase count
P_##PLAYER_TYPE.count++
//add to PLAYER_TYPE struct
P_##PLAYER_TYPE.last.next = WHICH_PLAYER_ID
P_##PLAYER_TYPE[WHICH_PLAYER_ID].previous = P_##PLAYER_TYPE.last
P_##PLAYER_TYPE.last = WHICH_PLAYER_ID
P_##PLAYER_TYPE.last.next = bj_MAX_PLAYER_SLOTS
if (P_##PLAYER_TYPE.first.get == null) {//if empty
P_##PLAYER_TYPE.first = WHICH_PLAYER_ID
P_##PLAYER_TYPE.first.previous = bj_MAX_PLAYER_SLOTS
}//if
//set the get
P_##PLAYER_TYPE[WHICH_PLAYER_ID].get = players[WHICH_PLAYER_ID]
}//end define ADD_TO_PLAYER_STRUCT
define private REMOVE_PLAYER(PLAYER_TYPE, WHICH_PLAYER, TRIGGER_CHECK) = {
//update event variables
lastRemovedPlayer = WHICH_PLAYER
lastRemovedPlayerId = GetPlayerId(lastRemovedPlayer)
if (isPlayerActive[lastRemovedPlayerId]) {//if the player is active
//remove player from specific player struct
#if PLAYER_TYPE == HumanPlayer //human
#if TRACK_HUMAN
REMOVE_FROM_PLAYER_STRUCT(PLAYER_TYPE, lastRemovedPlayerId)
#endif
#else //computer
#if TRACK_COMPUTER
REMOVE_FROM_PLAYER_STRUCT(PLAYER_TYPE, lastRemovedPlayerId)
#endif
#endif
//remove from actives
#if TRACK_ACTIVE
REMOVE_FROM_PLAYER_STRUCT(ActivePlayer, lastRemovedPlayerId)
#endif
//add to inactives
#if TRACK_INACTIVE
ADD_TO_PLAYER_STRUCT(InactivePlayer, lastRemovedPlayerId)
#endif
//set active to false
isPlayerActive[lastRemovedPlayerId] = false
//fire trigger
#if TRIGGER_CHECK
TriggerEvaluate(PLAYER_TYPE.out)
#endif
//remove units on leave
#if REMOVE_UNITS_ON_LEAVE
GroupEnumUnitsOfPlayer(removeUnitGroup, lastRemovedPlayer, removeUnits)
#endif
}//end if
}//end define REMOVE_PLAYER
private void Ini() {
int i = 0
humanLeaves = CreateTrigger()
//do ini if either REMOVE_UNITS
#if REMOVE_UNITS_ON_INI_FOR_INACTIVE || REMOVE_UNITS_ON_LEAVE
removeUnits = Condition(lambda bool() {
RemoveUnit(GetFilterUnit())
return false
})
#endif
//ini 0 through bj_MAX_PLAYERS - 1
do {
players[i] = Player(i)
if (GetPlayerSlotState(players[i]) == PLAYER_SLOT_STATE_PLAYING) then
#if TRACK_ACTIVE
ADD_TO_PLAYER_STRUCT(ActivePlayer, i)
#endif
if (GetPlayerController(players[i]) == MAP_CONTROL_USER) then
#if TRACK_HUMAN
ADD_TO_PLAYER_STRUCT(HumanPlayer, i)
if (HumanPlayer.count > 1) {
TriggerRegisterPlayerEvent(humanLeaves, players[i], EVENT_PLAYER_LEAVE)
}//end if
#endif
#if TRACK_COMPUTER
else
ADD_TO_PLAYER_STRUCT(ComputerPlayer, i)
#endif
endif
isPlayerActive[i] = true
#if REMOVE_UNITS_ON_INI_FOR_INACTIVE || TRACK_INACTIVE
else
#if REMOVE_UNITS_ON_INI_FOR_INACTIVE
GroupEnumUnitsOfPlayer(removeUnitGroup, players[i], removeUnits)
#endif
#if TRACK_INACTIVE
ADD_TO_PLAYER_STRUCT(InactivePlayer, i)
#endif
#endif
endif
} whilenot ++i == bj_MAX_PLAYERS
//ini bj_MAX_PLAYERS through bj_MAX_PLAYER_SLOTS-1
do {
players[i] = Player(i)
} whilenot ++i == bj_MAX_PLAYER_SLOTS
#if TRACK_HUMAN
if (HumanPlayer.count > 1) {
TriggerRegisterPlayerEvent(humanLeaves, HumanPlayer.first.get, EVENT_PLAYER_LEAVE)
TriggerAddCondition(humanLeaves, Condition(lambda bool() {
REMOVE_PLAYER(HumanPlayer, GetTriggerPlayer(), HUMAN_OUT_EVENT)
return false
}))
}//end if
else {
DestroyTrigger(humanLeaves)
humanLeaves = null
}//else
#endif
localPlayer = GetLocalPlayer()
}//end function Ini()
}