// VC:MP TeamSystem
// ------------------------------------------------------------------------------------------------
// Global table used to scope script commands
_CMD <- {}
// ------------------------------------------------------------------------------------------------
// Global enumeration to hold the command syntax
enum CmdSyn
{
TeamUp = "teamup <partener-name>",
SplitUp = "splitup",
DenyReq = "denyreq <partener-name>",
DenyAll = "denyall",
AcceptReq = "acceptreq <partener-name>",
AcceptAny = "acceptany",
FlushReq = "flushreq",
PlaySolo = "playsolo",
Partner = "partner"
}
// ------------------------------------------------------------------------------------------------
// Global enumeration to hold the message style and colours
enum MsgTag {
Dbg = "[#11455D][DEBUG][#F4F3EE]",
Msg = "[#64A5BB][MESSAGE][#F4F3EE]",
Scs = "[#698433][SUCCESS][#F4F3EE]",
Inf = "[#FFD35C][INFO][#F4F3EE]",
Wrn = "[#FF8202][WARNING][#F4F3EE]",
Err = "[#EA3E70][ERROR][#F4F3EE]",
Ftl = "[#C82C3A][FATAL][#F4F3EE]"
}
// ------------------------------------------------------------------------------------------------
// Scoped functions used to send formatted messages to players
_Msg <- {
// --------------------------------------------------------------------------------------------
function Client(...)
{
local i_player = vargv[0], r = vargv[1], g = vargv[2], b = vargv[3];
vargv = vargv.slice(3);
vargv[0] = this;
local str = ::format.acall(vargv);
ClientMessage(str, i_player, r, g, b);
}
// --------------------------------------------------------------------------------------------
function ClientAlpha(...)
{
local i_player = vargv[0], r = vargv[1], g = vargv[2], b = vargv[3], a = vargv[4];
vargv = vargv.slice(4);
vargv[0] = this;
local str = ::format.acall(vargv);
ClientMessage(str, i_player, r, g, b, a);
}
// --------------------------------------------------------------------------------------------
function ClientAll(...)
{
local r = vargv[0], g = vargv[1], b = vargv[2];
vargv = vargv.slice(2);
vargv[0] = this;
local str = ::format.acall(vargv);
ClientMessageToAll(str, r, g, b);
}
// --------------------------------------------------------------------------------------------
function ClientAllAlpha(...)
{
local r = vargv[0], g = vargv[1], b = vargv[2], a = vargv[3];
vargv = vargv.slice(3);
vargv[0] = this;
local str = ::format.acall(vargv);
ClientMessageToAll(str, r, g, b, a);
}
// --------------------------------------------------------------------------------------------
function Game(...)
{
local i_player = vargv[0], r = vargv[1], g = vargv[2], b = vargv[3];
vargv = vargv.slice(3);
vargv[0] = this;
local str = ::format.acall(vargv);
Announce(str, i_player, r, g, b);
}
// --------------------------------------------------------------------------------------------
function GameAlpha(...)
{
local i_player = vargv[0], r = vargv[1], g = vargv[2], b = vargv[3], a = vargv[4];
vargv = vargv.slice(4);
vargv[0] = this;
local str = ::format.acall(vargv);
Announce(str, i_player, r, g, b, a);
}
// --------------------------------------------------------------------------------------------
function GameAll(...)
{
local r = vargv[0], g = vargv[1], b = vargv[2];
vargv = vargv.slice(2);
vargv[0] = this;
local str = ::format.acall(vargv);
AnnounceAll(str, r, g, b);
}
// --------------------------------------------------------------------------------------------
function GameAllAlpha(...)
{
local r = vargv[0], g = vargv[1], b = vargv[2], a = vargv[3];
vargv = vargv.slice(3);
vargv[0] = this;
local str = ::format.acall(vargv);
AnnounceAll(str, r, g, b, a);
}
// --------------------------------------------------------------------------------------------
function Player(...)
{
local i_player = vargv[0];
vargv[0] = this;
local str = ::format.acall(vargv);
MessagePlayer(str, i_player);
}
}
// ------------------------------------------------------------------------------------------------
_ClientPool <- []
// ------------------------------------------------------------------------------------------------
_RequestPool <- []
// ------------------------------------------------------------------------------------------------
// Class responsible for management and identification of player instances
class _Client
{
// --------------------------------------------------------------------------------------------
function constructor(i_player)
{
// Verify the player instance
if (typeof i_player != "instance") return null;
// Store the player instance
else Instance = i_player
}
// --------------------------------------------------------------------------------------------
function Connect()
{
// Ignored....
}
// --------------------------------------------------------------------------------------------
function Disconnect()
{
// See if the player had any partner
if (Partner != null) {
// Notify the partner about the split-up
_Msg.Player(Partner, @"%s %s has disconnected and you no longer have a partner.", MsgTag.Inf, Instance.Name);
// Free the partner from used
_ClientPool[Partner.ID].Partner = null;
// Clear any stored instances
Partner = null;
}
// See if there's any requests made by this instance and clear them
if (_RequestPool[Instance.ID] != null) {
// Notify the partner about the result
_Msg.Player(_RequestPool[Instance.ID].Target, @"%s %s has disconnected and his team-up request was flushed.", MsgTag.Inf, Instance.Name);
// Remove the request from the pool/list
_RequestPool[Instance.ID] = null;
}
// See if there's any requests made to this instance and clear them
foreach (idx, req in _RequestPool) {
// See if this is a valid request
if (req != null && req.Target.ID == Instance.ID) {
// Notify the partner about the rejected request
_Msg.Player(req.Invoker, @"%s %s has disconnected and cannot accept team-up request.", MsgTag.Inf, i_player.Name);
// Clear the request from the pool/list
_RequestPool[idx] = null;
}
}
// Clear any stored instances
Instance = null;
}
// --------------------------------------------------------------------------------------------
Instance = null
// --------------------------------------------------------------------------------------------
Partner = null
// --------------------------------------------------------------------------------------------
PlaySolo = false
}
// ------------------------------------------------------------------------------------------------
_CMD.TeamUp <- function(i_player, args)
{
// See if there isn't already a request in the pool and send a notice to the invoker to flush his previous request
if (_RequestPool[i_player.ID] != null) return _Msg.Player(i_player, @"%s Please flush your previous request to: %s", MsgTag.Scs, _RequestPool[i_player.ID].Target.Name);
// See if we are dealing with a valid command
if (typeof args != "string" || args.len() <= 0) return _Msg.Player(i_player, @"%s Syntax: %s", MsgTag.Inf, CmdSyn.TeamUp);
// Strip the command from unnecessary space characters
args = strip(args);
// Check one more time if we still have a valid command
if (typeof args != "string" || args.len() <= 0) return _Msg.Player(i_player, @"%s Syntax: %s", MsgTag.Inf, CmdSyn.TeamUp);
// Try to find the target instance by name first
local i_partner = FindPlayer(args);
// See if we found a valid player instance for our partner
if (typeof i_partner != "instance") return _Msg.Player(i_player, @"%s There's no player on the server identified by: %s", MsgTag.Err, args);
// See if the invoker tries to team up with him self
else if (i_player.ID == i_partner.ID) return _Msg.Player(i_player, @"%s You cannot team up with your self.", MsgTag.Wrn);
// See if the invoker has the option to play solo enabled
else if (_ClientPool[i_player.ID].PlaySolo) return _Msg.Player(i_player, @"%s You already chose to play solo.", MsgTag.Wrn);
// See if the partner has the option to play solo enabled
else if (_ClientPool[i_partner.ID].PlaySolo) return _Msg.Player(i_player, @"%s %s chose to play solo.", MsgTag.Wrn, i_partner.Name);
// See if the invoker is already in a team
else if (_ClientPool[i_player.ID].Partner != null) return _Msg.Player(i_player, @"%s You are already teamed-up with: %s", MsgTag.Wrn, _ClientPool[i_player.ID].Partner.Name);
// See if the requested partner is already in a team
else if (_ClientPool[i_partner.ID].Partner != null) return _Msg.Player(i_player, @"%s %s is already teamed-up with: %s", MsgTag.Wrn, i_partner.Name, _ClientPool[i_partner.ID].Partner.Name);
// Submit the request to the pool
_RequestPool[i_player.ID] = {Invoker = i_player, Target = i_partner, Timeout = (time()+30)};
// Notify the requested partner
_Msg.Player(i_partner, @"%s %s would like to team-up with you.", MsgTag.Inf, i_player.Name);
// Notify the invoker about his request
_Msg.Player(i_player, @"%s %s successfully received your team-up request.", MsgTag.Scs, i_partner.Name);
}
// ------------------------------------------------------------------------------------------------
_CMD.SplitUp <- function(i_player, args)
{
// See if the invoker is in a team
if (_ClientPool[i_player.ID].Partner != null) {
// Inform the invoker about his action
_Msg.Player(i_player, @"%s You split-up from: %s", MsgTag.Scs, _ClientPool[i_player.ID].Partner.Name);
// Send a notice to the partner
_Msg.Player(_ClientPool[i_player.ID].Partner, @"%s %s split-up from you.", MsgTag.Inf, i_player.Name);
// Remove the link from the partner
_ClientPool[_ClientPool[i_player.ID].Partner.ID].Partner = null;
// Remove the link from the invoker
_ClientPool[i_player.ID].Partner = null;
// Notify the invoker that he doesn't have a team to split-up
} else _Msg.Player(i_player, @"%s You don't have a team to split-up.", MsgTag.Wrn);
}
// ------------------------------------------------------------------------------------------------
_CMD.DenyReq <- function(i_player, args)
{
// See if we are dealing with a valid command
if (typeof args != "string" || args.len() <= 0) return _Msg.Player(i_player, @"%s Syntax: %s", MsgTag.Inf, CmdSyn.DenyReq);
// Strip the command from unnecessary space characters
args = strip(args);
// Check one more time if we still have a valid command
if (typeof args != "string" || args.len() <= 0) return _Msg.Player(i_player, @"%s Syntax: %s", MsgTag.Inf, CmdSyn.DenyReq);
// Try to find the target instance by name first
local i_partner = FindPlayer(args);
// See if we found a valid player instance for our partner
if (typeof i_partner != "instance") return _Msg.Player(i_player, @"%s There's no player on the server identified by: %s", MsgTag.Err, args);
// Notify the partner about the rejected request
_Msg.Player(i_partner, @"%s %s rejected your team-up request.", MsgTag.Inf, i_player.Name);
// Notify the invoker about the rejected request
_Msg.Player(i_player, @"%s You successfully rejected the team-up request from %s", MsgTag.Scs, i_partner.Name);
// Flush the request from the pool/list
_RequestPool[i_partner.ID] = null;
}
// ------------------------------------------------------------------------------------------------
_CMD.DenyAll <- function(i_player, args)
{
// Go through each element in the request pool and clear it if it's addressed to the invoker
foreach (idx, req in _RequestPool) {
// See if this is a valid request
if (req != null && req.Target.ID == i_player.ID) {
// Notify the partner about the rejected request
_Msg.Player(req.Invoker, @"%s %s rejected your team-up request.", MsgTag.Inf, i_player.Name);
// Notify the invoker about the rejected request
_Msg.Player(i_player, @"%s You successfully rejected the team-up request from %s", MsgTag.Scs, req.Invoker.Name);
// Clear the request from the pool/list
_RequestPool[idx] = null;
}
}
}
// ------------------------------------------------------------------------------------------------
_CMD.AcceptReq <- function(i_player, args)
{
// See if we are dealing with a valid command
if (typeof args != "string" || args.len() <= 0) return _Msg.Player(i_player, @"%s Syntax: %s", MsgTag.Inf, CmdSyn.AcceptReq);
// Strip the command from unnecessary space characters
args = strip(args);
// Check one more time if we still have a valid command
if (typeof args != "string" || args.len() <= 0) return _Msg.Player(i_player, @"%s Syntax: %s", MsgTag.Inf, CmdSyn.AcceptReq);
// Try to find the target instance by name first
local i_partner = FindPlayer(args);
// See if we found a valid player instance for our partner
if (typeof i_partner != "instance") return _Msg.Player(i_player, @"%s There's no player on the server identified by: %s", MsgTag.Err, args);
// Notify the invoker about the result
_Msg.Player(i_partner, @"%s %s accepted your team up request.", MsgTag.Inf, i_player.Name);
// Notify the partner about the result
_Msg.Player(i_player, @"%s You accepted the team up request from %s", MsgTag.Scs, i_partner.Name);
// Remove this request from the pool/list
_RequestPool[i_partner.ID] = null;
// Link the players together
_ClientPool[i_player.ID].Partner = i_partner;
_ClientPool[i_partner.ID].Partner = i_player;
// A variable to count the rest of the requests
local req_count = 0;
// Go through each element in the request pool and clear it if it's addressed to the invoker
foreach (idx, req in _RequestPool) {
// See if this is a valid request
if (req != null && req.Target.ID == i_player.ID) {
// Notify the partner about the rejected request
_Msg.Player(req.Invoker, @"%s %s rejected your team-up request.", MsgTag.Inf, i_player.Name);
// Increase the request counter
req_count++;
// Clear the request from the pool/list
_RequestPool[idx] = null;
}
}
// Notify the invoker about the rest of the requests
_Msg.Player(i_player, @"%s You successfully rejected the rest of the requests (%d).", MsgTag.Inf, req_count);
}
// ------------------------------------------------------------------------------------------------
_CMD.AcceptAny <- function(i_player, args)
{
// An array to hold the indexes of the available requests
local requests = array(0);
// Find all the requests made to the invoker
foreach (idx, req in _RequestPool) {
// See if this is a valid request
if (req != null && req.Target.ID == i_player.ID) {
// Add the request index to the list
requests.push(idx);
}
}
// See if we have any requests made to this player
if (requests.len() <= 0) return _Msg.Player(i_player, @"%s, You don't have any team-up requests.", MsgTag.Inf);
// See if we have only one request
else if (requests.len() == 1) return _CMD.AcceptReq(i_player, _RequestPool[requests[0]].Invoker.Name);
// Set the seed for the random number generator
srand(time());
// Make the seed even more random for small ranges
srand(time() % ((time() / rand()) % rand()));
// Get a random index from the list
local rnum = rand() % requests.len();
// Accept the request of the partner located at the generated index
_CMD.AcceptReq(i_player, _RequestPool[requests[rnum]].Invoker.Name);
}
// ------------------------------------------------------------------------------------------------
_CMD.FlushReq <- function(i_player, args)
{
// See if there's a request in the pool/list
if (_RequestPool[i_player.ID] != null) {
// Inform the invoker about his action
_Msg.Player(i_player, @"%s You revoked your team-up request to: %s", MsgTag.Scs, _RequestPool[i_player.ID].Target.Name);
// Send a notice to the target
_Msg.Player(_RequestPool[i_player.ID].Target, @"%s %s revoked his team-up request.", MsgTag.Inf, i_player.Name);
// Flush the request
_RequestPool[i_player.ID] = null;
// Notify the invoker that there is no previous request
} else _Msg.Player(i_player, @"%s You don't have any team-up request.", MsgTag.Wrn);
}
// ------------------------------------------------------------------------------------------------
_CMD.PlaySolo <- function(i_player, args)
{
// See if the invoker isn't in a team already
if (_ClientPool[i_player.ID].Partner != null) return _Msg.Player(i_player, @"%s You need to split-up from %x", MsgTag.Wrn, _ClientPool[i_player.ID].Partner.Name);
// See if the invoker has this option enabled or not
if (_ClientPool[i_player.ID].PlaySolo) {
// Deactivate the play solo option
_ClientPool[i_player.ID].PlaySolo = false;
// Notify the invoker about his action
_Msg.Player(i_player, @"%s You successfully deactivated the play solo option.", MsgTag.Scs);
} else {
// Activate the play solo option
_ClientPool[i_player.ID].PlaySolo = true;
// Notify the invoker about his action
_Msg.Player(i_player, @"%s You successfully activated the play solo option.", MsgTag.Scs);
}
}
// ------------------------------------------------------------------------------------------------
_CMD.Partner <- function(i_player, args)
{
// See if the invoker has a partner or not
if (_ClientPool[i_player.ID].Partner != null) {
// Inform the invoker about his partner
_Msg.Player(i_player, @"%s Your partner is %s", MsgTag.Inf, _ClientPool[i_player.ID].Partner.Name);
// Notify the invoker that he doesn't have a partner
} else _Msg.Player(i_player, @"%s You don't have a partner.", MsgTag.Wrn);
}
// ------------------------------------------------------------------------------------------------
// Processes all the active team-up requests
function _RequestProcess()
{
// Get the current time
local cur_time = time();
// Loop through all the queued requests
foreach (idx, req in _RequestPool) {
// See if this is a valid request
if (req != null && req.Timeout <= cur_time) {
// Notify the invoker about the expired request
_Msg.Player(req.Invoker, @"%s Your team-up request to %s has expired.", MsgTag.Inf, req.Target.Name);
// Clear the request from the pool/list
_RequestPool[idx] = null;
}
}
}
// ------------------------------------------------------------------------------------------------
function onServerStart()
{
// Reserve enough space to hold the maximum number of Clients that the server can hold
_ClientPool = array(GetMaxPlayers());
// Reserve enough space to hold the maximum number of Clients that the server can hold
_RequestPool = array(GetMaxPlayers());
// Attempt to find any previously connected Clients in case of a script reload
foreach (idx, inst in _ClientPool) {
// Attempt to find the player by it's unique identifier
local res = FindPlayer(idx);
// See if a valid player instance was found at this ID
if (res != null) {
// Create a Client instance for this player instance
inst = _Client(res);
// Allow the Client instance to initialize
inst.Connect();
}
}
// Start the request cleaning timer
NewTimer("_RequestProcess", 5000, 0);
}
// ------------------------------------------------------------------------------------------------
function onServerStop()
{
// Attempt to de-initialize all connected players from the server
foreach (inst in _ClientPool) {
// See if there was any Client instance in this slot
if (inst != null) {
// Allow the Client instance to de-initialize
inst.Disconnect();
// Clear the slot
_ClientPool[i_player.ID] = null;
}
}
// Clear all the team-up requests
foreach (idx, req in _RequestPool) _RequestPool[idx] = null;
}
// ------------------------------------------------------------------------------------------------
function onPlayerJoin(i_player)
{
// Create a Client instance for this player instance
_ClientPool[i_player.ID] = _Client(i_player);
// Allow the newly created Client instance to initialize
_ClientPool[i_player.ID].Connect();
}
// ------------------------------------------------------------------------------------------------
function onPlayerPart(i_player, reason)
{
// Allow the Client instance to de-initialize
_ClientPool[i_player.ID].Disconnect();
// Clear the slot for other Clients
_ClientPool[i_player.ID] = null;
}
// ------------------------------------------------------------------------------------------------
function onPlayerSpawn(i_player)
{
// See if the player has a partner
if (_ClientPool[i_player.ID].Partner != null) {
// Teleport the player to his partner
i_player.Pos = _ClientPool[i_player.ID].Partner.Pos;
}
}
// ------------------------------------------------------------------------------------------------
function onPlayerCommand(i_player, cmd, args)
{
switch (cmd)
{
// ----------------------------------------------------------------------------------------
case "teamup": _CMD.TeamUp(i_player, args); break;
case "splitup": _CMD.SplitUp(i_player, args); break;
// ----------------------------------------------------------------------------------------
case "denyreq": _CMD.DenyReq(i_player, args); break;
case "denyall": _CMD.DenyAll(i_player, args); break;
// ----------------------------------------------------------------------------------------
case "acceptreq": _CMD.AcceptReq(i_player, args); break;
case "acceptany": _CMD.AcceptAny(i_player, args); break;
// ----------------------------------------------------------------------------------------
case "flushreq": _CMD.FlushReq(i_player, args); break;
case "playsolo": _CMD.PlaySolo(i_player, args); break;
case "partner": _CMD.Partner(i_player, args); break;
// ----------------------------------------------------------------------------------------
default: _Msg.Player(i_player, "%s Unknown command: %s %s", MsgTag.Err, cmd, args);
}
}