JustPaste.it

// 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);
    }
}