Character Vault
Any Concept / Any System
Compendium
Your System Come To Life
Roll20 for Android
Streamlined for your Tablet
Roll20 for iPad
Streamlined for your Tablet

Personal tools

Difference between revisions of "Script:Conditions and Status Tracker"

From Roll20 Wiki

Jump to: navigation, search
m
m (discont. API)
(4 intermediate revisions by one user not shown)
Line 5: Line 5:
 
|lastmodified=2014-01-15}}
 
|lastmodified=2014-01-15}}
  
Tracks arbitrary statuses associated with tokens. Statuses may have a turn-limited duration, or may be permanent. Only the GM may modify what statuses exist on a given token, but depending on settings the status may be broadcast to all players.
+
'''Conditions and Status Tracker''' tracks arbitrary statuses associated with tokens. Statuses may have a turn-limited duration, or may be permanent. Only the GM may modify what statuses exist on a given token, but depending on settings the status may be broadcast to all players.
  
 
The API commands <code>!StatusAdd</code>, <code>!StatusDel</code>, <code>!StatusAll</code>, and <code>!StatusClearAll</code> are used to manipulate what statuses are present.
 
The API commands <code>!StatusAdd</code>, <code>!StatusDel</code>, <code>!StatusAll</code>, and <code>!StatusClearAll</code> are used to manipulate what statuses are present.
Line 11: Line 11:
  
 
=== Syntax ===
 
=== Syntax ===
{{syntaxbox top|formal=true}}
+
{{syntaxbox top|formal=true|Conditions and Status Tracker}}
 
{{API command|StatusAdd}} {{API parameter|name=status}} {{API parameter|name=duration}} {{API parameter|name=description}} {{API parameter|name=GM only flag|optional=true}} {{API parameter|name=</em>// <em>marker|optional=true}}<br>
 
{{API command|StatusAdd}} {{API parameter|name=status}} {{API parameter|name=duration}} {{API parameter|name=description}} {{API parameter|name=GM only flag|optional=true}} {{API parameter|name=</em>// <em>marker|optional=true}}<br>
 
{{API command|StatusDel}} {{API parameter|name=status}}<br>
 
{{API command|StatusDel}} {{API parameter|name=status}}<br>
Line 53: Line 53:
 
* '''StatusTracker.statusIndicatorFontColor''' &ndash; Foreground style color for status chat messages.
 
* '''StatusTracker.statusIndicatorFontColor''' &ndash; Foreground style color for status chat messages.
  
=== Code ===
+
=== Changelog ===
<pre data-language="javascript">// Gist: https://gist.github.com/DarokinB/5776368
+
{{changelog version|1.8|2014-01-15|* Release}}
  
/* ***********  STATUS TRACKER  *********************************************
+
[[Category:Discontinued API Scripts]]
*  The purpose of this script is to track character and token status
+
*  as well as durations of the status, with a reminder given on each turn.
+
*  It allows the GM to manimpulate the controls, while players can only test
+
*  their characters current status, if the GM allows it. It will use basic
+
*  API commands.
+
*  Please Note: This script will lock up one of the indicators on a token
+
*  to show a character is under a specific effect. You can change which
+
*  indicator to your preference.
+
+
*  Commands:
+
*  !StatusAdd <status name> <duration, -1 for perm> <description> <GM Only Flag> // <marker name>
+
*  !StatusDel <status name>
+
*  !StatusAll
+
*  !StatusClearAll
+
+
*  For ease of use, add the following to a macro and assign it to your macro bar:
+
+
*  !StatusAdd ?{Status name} ?{Status Duration, -1 for perm} ?{Status description} ?{Type GM for GM only} // ?{Status Indicator [default is purple]}
+
+
*  NOTE: If you end the command with GM, only the GM will be able to see the
+
*  reminder.
+
+
*  NOTE: Names cannot contain spaces, but it searches for containing so for
+
*  'Hank the Dwarf' you can put !Status Hank Bless 10
+
+
*****************************************************************************/
+
//Storage location
+
state.activeStatus = state.activeStatus || new Array();
+
+
var StatusTracker = StatusTracker || {};
+
+
//Personal Settings
+
StatusTracker.statusIndicator = "purple"; //default marker if DM does not designate one
+
StatusTracker.Chat = true;  //Uses chat window to give indicators
+
StatusTracker.ChatDescriptions = true; //displays descriptions
+
StatusTracker.ChatOnlyGM = false; //All chat only goes to the gm
+
StatusTracker.currentTurn = ""; //The current tokens turn
+
StatusTracker.bloodiedTintColor = "#FF0000"; //If you want a creature's token to be tinted when below 1/2 hp (bar1_value), 'transparent' for none.
+
StatusTracker.bloodiedMarker = "none"; //If you want a creature's token to get marked when below 1/2 hp (bar1_value), 'none' for none.
+
StatusTracker.deadMarker = "dead"; //Marker to show if the token is dead (0 bar1_value)
+
StatusTracker.statusIncidatorBG = 'blue';
+
StatusTracker.statusIndicatorFontColor = '#ffffff';
+
 
+
//Adding a status to the tracker
+
StatusTracker.AddStatus = function(CharID, statusName, statusDescript, Duration, GMOnly, Marker) {
+
    if (CharID == "") return; //Don't add empty statuses
+
   
+
    state.activeStatus.push({
+
        'CharID': CharID,
+
        'statusName': statusName,
+
        'statusDescript': statusDescript,
+
        'Duration': Duration,
+
        'GMOnly': GMOnly,
+
        'Marker': Marker,
+
    });
+
   
+
    StatusTracker.addMarker(CharID, Marker);
+
}
+
+
StatusTracker.addMarker = function(CharID, Marker) {
+
    //Find the token with the ID: non-linked tokens
+
    var currChar = findObjs({
+
        _type: "graphic",
+
        _id: CharID,
+
        _pageid: Campaign().get("playerpageid"),
+
    });   
+
    _.each(currChar, function(obj) {
+
        obj.set("statusmarkers", StatusTracker.addMarkerToString(obj.get("statusmarkers"), Marker));
+
    });
+
}
+
+
//Removes a marker of the listed type if it is present
+
StatusTracker.delMarker = function(CharID, Marker) {
+
    //Find the token with the ID: non-linked tokens
+
    var currChar = findObjs({
+
        _type: "graphic",
+
        _id: CharID,
+
        _pageid: Campaign().get("playerpageid"),
+
    });   
+
    _.each(currChar, function(obj){
+
        var returnString = StatusTracker.delMarkerFromString(obj.get("statusmarkers"), Marker);
+
        obj.set("statusmarkers", returnString);
+
    });
+
}
+
+
StatusTracker.addMarkerToString = function(Original, AddMarker){
+
    var splitOriginal = Original.split(",");
+
    var newString = new Array();
+
    var maxLoops = splitOriginal.length;
+
    var loop = 0;
+
    var Added = false;
+
   
+
    while (loop < maxLoops && Added == false) {
+
        if (splitOriginal[loop].indexOf(AddMarker) !== -1){
+
            //already has more than one
+
            if(splitOriginal[loop].indexOf("@") !== -1) {
+
                var num = Number(splitOriginal[loop].substr(splitOriginal[loop].length - 1));
+
                num += 1;
+
                splitOriginal[loop] = splitOriginal[loop].substr(0,splitOriginal[loop].length - 1) + num;
+
                Added = true;
+
            }
+
           
+
            //already has it but not at multiple, first check to make sure not added
+
            //above, then add 1 more. Forced addition of @
+
            if(Added == false) {
+
                splitOriginal[loop] = splitOriginal[loop] + "@2";
+
                Added = true;
+
            }
+
        }
+
        loop += 1;
+
    }
+
   
+
    if (Added == false ) {
+
        return Original + "," + AddMarker;
+
    } else {
+
        return splitOriginal.join();
+
    }
+
}
+
+
StatusTracker.delMarkerFromString = function(Original, delMarker) {
+
    var splitOriginal = Original.split(",");
+
    var maxLoops = splitOriginal.length;
+
    var loop = 0;
+
    var Deleted = false;
+
   
+
    while (loop < maxLoops && Deleted == false) {
+
        if (splitOriginal[loop].indexOf(delMarker) !== -1) {
+
+
            //already has more than one
+
            if(splitOriginal[loop].indexOf("@") !== -1) {
+
                var num = Number(splitOriginal[loop].substr(splitOriginal[loop].length - 1));
+
                //Lowers the count down
+
                num -= 1;
+
               
+
                //tests for solitary remaining
+
                if (num == 1 ) {
+
                    splitOriginal[loop] = splitOriginal[loop].substr(0,splitOriginal[loop].length - 2);
+
                } else {
+
                    splitOriginal[loop] = splitOriginal[loop].substr(0,splitOriginal[loop].length - 1) + num;
+
                }
+
                Deleted = true;
+
            }
+
           
+
            //Matches but there are not multiple, removes instance
+
            if (Deleted == false) {
+
                splitOriginal.splice(loop,1);
+
                Deleted = true;
+
            }
+
        }
+
        loop += 1;
+
    }
+
    return splitOriginal.join();
+
}
+
+
StatusTracker.getTokenName = function(CharID) {
+
    if (CharID == "") return "";
+
       
+
    //if CharID is a token, turn on the status indicator for the token
+
    var Chars = findObjs({
+
      _type: "graphic",
+
      _id: CharID,
+
    });
+
    if (Chars.length > 0 ) {
+
        var name = Chars[0].get("name");
+
    }
+
   
+
    return name;
+
}
+
+
StatusTracker.DelStatus = function(CharID, statusName){
+
    var loop = 0;
+
    var maxLoops = state.activeStatus.length;
+
   
+
    while (loop < maxLoops) {
+
        if (state.activeStatus[loop].statusName == statusName && state.activeStatus[loop].CharID == CharID) {
+
            //Message
+
            message = state.activeStatus[loop].statusName + " ends.";
+
            StatusTracker.sendMessage(message, state.activeStatus.GMOnly, state.activeStatus[loop].statusDescript);
+
           
+
            //Remove the relevant marker
+
            StatusTracker.delMarker(state.activeStatus[loop].CharID, state.activeStatus[loop].Marker);
+
           
+
            state.activeStatus.splice(loop,1);
+
            loop = loop - 1;
+
            maxLoops = maxLoops - 1;
+
        }
+
        loop = loop + 1;
+
    }
+
}
+
+
StatusTracker.sendMessage = function(message, GMOnly, description) {
+
    if (StatusTracker.Chat == false) return;
+
   
+
    //if not a direct message, assign chat limiters
+
    if (message.indexOf("/direct") > 0 ) {
+
        if (GMOnly == true || StatusTracker.ChatOnlyGM == true) {
+
            message = "/w gm " + message;
+
        }
+
        else
+
        {
+
            message = "/desc " + message;
+
        }
+
    }
+
   
+
    if (StatusTracker.ChatDescriptions == true && description != "") {
+
        message = message + " (" + description + ")";
+
    }
+
    sendChat("", message);
+
   
+
}
+
+
StatusTracker.newTurn = function(CharID) {   
+
    //loops through all durations and effects ones on the current character/token
+
    var loop = 0;
+
    var maxLoops = state.activeStatus.length;
+
    var count = 0;
+
+
    if (maxLoops == 0) return;
+
   
+
    //If the current token does not have any statues, exit
+
    while (loop < maxLoops) {
+
        if (state.activeStatus[loop].CharID == CharID) {count += 1}
+
        loop = loop + 1
+
    }
+
    if (count == 0) return;
+
   
+
    //reset loop
+
    loop = 0;
+
   
+
    //Extract the current tokens name
+
    var charName = StatusTracker.getTokenName(CharID)
+
   
+
    if (StatusTracker.Chat) {
+
        StatusTracker.sendMessage("/direct <div align='center'> <p style='color:" +
+
          StatusTracker.statusIndicatorFontColor + "; background-color:" +
+
          StatusTracker.statusIncidatorBG + "'>  ****  " + charName +
+
          " Status Effects  ****</p></div>", StatusTracker.ChatOnlyGM, "")
+
    }
+
 
+
    while (loop < maxLoops) {
+
       
+
        //Decrement Duration
+
        var Duration = Number(state.activeStatus[loop].Duration || 1);
+
+
        // A -1 Duration is permanent, the current token/character's statuses are
+
        // increments for the next round.
+
        if (Duration > 0 && state.activeStatus[loop].CharID == CharID) {
+
            state.activeStatus[loop].Duration = Duration - 1;
+
        }
+
       
+
        //Still active, announced
+
        if (Duration > 1 && state.activeStatus[loop].CharID == CharID) {
+
            StatusTracker.sendMessage(StatusTracker.getTokenName(state.activeStatus[loop].CharID) + " " + state.activeStatus[loop].statusName + " for " + (Duration - 1) +
+
                " more rounds.", state.activeStatus[loop].GMOnly, state.activeStatus[loop].statusDescript);
+
        }
+
       
+
        //Permanent announced
+
        if (Duration == -1 && state.activeStatus[loop].CharID == CharID) {
+
            StatusTracker.sendMessage(StatusTracker.getTokenName(state.activeStatus[loop].CharID) + " " + state.activeStatus[loop].statusName + " still.", state.activeStatus[loop].GMOnly, state.activeStatus[loop].statusDescript);
+
        }
+
       
+
        //Ending effects
+
        if (Duration <= 1 && Duration !== -1 && state.activeStatus[loop].CharID == CharID) {
+
           
+
            StatusTracker.sendMessage( StatusTracker.getTokenName(state.activeStatus[loop].CharID) + " " + state.activeStatus[loop].statusName + " ends.", state.activeStatus[loop].GMOnly,  state.activeStatus[loop].statusDescript);
+
            StatusTracker.DelStatus(state.activeStatus[loop].CharID, state.activeStatus[loop].statusName);
+
           
+
            //drop loop by 1 now that there is 1 less object in the array.
+
            loop = 0;
+
            maxLoops = state.activeStatus.length;
+
        }
+
       
+
        loop = loop + 1;
+
    }
+
    if (StatusTracker.Chat) {
+
        StatusTracker.sendMessage("/direct <div align='center'> <p style='color:" +
+
          StatusTracker.statusIndicatorFontColor + "; background-color:" +
+
          StatusTracker.statusIncidatorBG + "'>  ****  End " + charName +
+
          " Status Effects  ****</p></div>", StatusTracker.ChatOnlyGM, "")
+
    }
+
    return;
+
}
+
+
StatusTracker.getCurrentToken = function() {
+
    var turn_order = JSON.parse(Campaign().get('turnorder'));
+
   
+
    if (!turn_order.length) {
+
        return "";
+
    }
+
   
+
    var turn = turn_order.shift();
+
    return getObj('graphic', turn.id) || "";
+
};
+
+
on("change:campaign:turnorder", function() {
+
    var status_current_token = StatusTracker.getCurrentToken();
+
   
+
    //Handler for non-token items in initiative
+
    if (status_current_token == "") return;
+
   
+
    //If turn order was changed but it is still the same persons turn, exit
+
    if (status_current_token.id == StatusTracker.currentTurn) return;
+
   
+
    StatusTracker.currentTurn = status_current_token.id;
+
    StatusTracker.newTurn(status_current_token.id);
+
});
+
+
on("chat:message", function(msg) { 
+
    var cmd = "!StatusAll";
+
   
+
    if (msg.type == "api" && msg.content.indexOf(cmd) !== -1 && msg.who.indexOf("(GM)") !== -1) {
+
        var loop = 0;
+
        var maxLoops = state.activeStatus.length;
+
        StatusTracker.sendMessage("/direct <div align='center'> <p style='color:" +
+
          StatusTracker.statusIndicatorFontColor + "; background-color:" +
+
          StatusTracker.statusIncidatorBG + "'>  ****  All Status Effects  ****</p></div>", StatusTracker.ChatOnlyGM, "")
+
       
+
        while (loop < maxLoops) {
+
            sendChat("", "/w gm " + StatusTracker.getTokenName(state.activeStatus[loop].CharID) + " " + state.activeStatus[loop].statusName + " for " + state.activeStatus[loop].Duration + " rounds. [" + state.activeStatus[loop].Marker + " marker].")
+
            loop = loop + 1;
+
        }
+
       
+
        StatusTracker.sendMessage("/direct <div align='center'> <p style='color:" +
+
          StatusTracker.statusIndicatorFontColor + "; background-color:" +
+
          StatusTracker.statusIncidatorBG + "'>  ****  End All Status Effects  ****</p></div>", StatusTracker.ChatOnlyGM, "")
+
    }
+
});
+
+
on("chat:message", function(msg) { 
+
    var cmd = "!StatusClearAll";
+
   
+
    if (msg.type == "api" && msg.content.indexOf(cmd) !== -1 && msg.who.indexOf("(GM)") !== -1) {
+
        state.activeStatus = new Array();
+
    }
+
});
+
+
on("chat:message", function(msg) { 
+
    var cmd = "!StatusAdd ";
+
   
+
    if (msg.type == "api" && msg.content.indexOf(cmd) !== -1 && msg.who.indexOf("(GM)") !== -1) {
+
        var cleanedMsg = msg.content.replace(cmd, "");
+
+
        //Pulls any marker first
+
        var marker = cleanedMsg.split("// ")[1] || StatusTracker.statusIndicator;
+
        cleanedMsg = cleanedMsg.split("//")[0];
+
       
+
        //Pulls the effect name
+
        var statusName = cleanedMsg.split(" ")[0];
+
        cleanedMsg = cleanedMsg.substr(statusName.length + 1) //Removes the target from the array
+
+
        //Pulls the duration
+
        var Duration = cleanedMsg.split(" ")[0];
+
        cleanedMsg = cleanedMsg.substr(Duration.length + 1) //Removes the target from the array
+
+
        var GMOnly = false;
+
        if (cleanedMsg.substr(cleanedMsg.length - 2) == "GM") {
+
            GMOnly = true;
+
            cleanedMsg = cleanedMsg.substr(0, cleanedMsg.length - 2);
+
        }
+
+
        //The remainder is the description
+
        var statusDescription = cleanedMsg;
+
       
+
        //Adds the status to each of the selected tokens
+
        _.each(msg.selected, function (obj){
+
            //Runs through each selected token and adds the status
+
            if (obj._type == "graphic") { //only if the selected is a token
+
                StatusTracker.AddStatus(obj._id, statusName, statusDescription, Duration, GMOnly, marker);
+
                sendChat("","/desc " + statusName + " added.")
+
            }
+
        })
+
    }
+
}); 
+
+
on("chat:message", function(msg) { 
+
    var cmd = "!StatusDel ";
+
   
+
    if (msg.type == "api" && msg.content.indexOf(cmd) !== -1 && msg.who.indexOf("(GM)" !== -1)) {
+
       
+
        var CharID = "";
+
       
+
        var cleanedMsg = msg.content.replace(cmd, "");
+
       
+
        //Pulls the effect name
+
        var statusName = cleanedMsg.split(" ")[0];
+
        cleanedMsg = cleanedMsg.substr(statusName.length + 1) //Removes the target from the array
+
       
+
        _.each(msg.selected, function (obj){
+
            //Deletes the statud from each selected ID           
+
            if (obj._type == "graphic") { //only if the selected is a token
+
                StatusTracker.DelStatus(obj._id, statusName);
+
            }
+
        })
+
    }
+
});
+
 
+
on("change:graphic:bar1_value", function(obj, prev) {
+
    if(obj.get("bar1_max") === "") return;
+
     
+
    if(obj.get("bar1_value") <= 0) {
+
        obj.set({
+
            status_dead: true,
+
            tint_color: "transparent"
+
        });
+
    }
+
    else if(obj.get("bar1_value") <= obj.get("bar1_max") / 2) {
+
        obj.set({
+
            tint_color: "#FF0000",
+
            status_dead: false
+
        });
+
    }
+
    else{
+
        obj.set({
+
            tint_color: "transparent",
+
            status_dead: false
+
        });
+
    }
+
});</pre>
+

Revision as of 11:52, 17 June 2020

API ScriptAuthor: Josh
Version: 1.8
Last Modified: 2014-01-15
Code: Conditions and Status Tracker
Dependencies: None
Conflicts: None

Conditions and Status Tracker tracks arbitrary statuses associated with tokens. Statuses may have a turn-limited duration, or may be permanent. Only the GM may modify what statuses exist on a given token, but depending on settings the status may be broadcast to all players.

The API commands !StatusAdd, !StatusDel, !StatusAll, and !StatusClearAll are used to manipulate what statuses are present.

Syntax

!StatusAdd <status> <duration> <description> [GM only flag] [// marker]
!StatusDel <status>
!StatusAll
!StatusClearAll
Formally:

S

→ !StatusAdd status
duration
description
GMFlag
marker


S

→ !StatusDel status


S

→ !StatusAll

S

→ !StatusClearAll

status

string

duration

integer

description

string

GMFlag

→ ε

GMFlag

→ GM

marker

→ ε

marker

→ // string
Parameter Values
status Name of the status to add. No spaces are allowed.
duration Number of turns the status lasts. Use -1 for permanent duration.
description Description of the status.
GM only flag Optional. GM is the only accepted value. If this flag is set, only the GM will be notified about this status.
marker Optional. Defines the statusmarker to set on the token representing this status.

Installation

There are several configuration variables near the top of the script. You may alter them to customize the script functionality:

  • StatusTracker.statusIndicator – Then default marker to use if marker is not supplied.
  • StatusTracker.Chat – Set to true to indicate the status in the chat window, or false otherwise.
  • StatusTracker.ChatDescriptions – Set to true to display the status descriptions, or false otherwise.
  • StatusTracker.ChatOnlyGM – If set to true, all chat messages will be sent to the GM regardless of the GM flag. Set to false to control statuses individually.
  • StatusTracker.currentTurn – Not customizable.
  • StatusTracker.bloodiedTintColor – Set to a hexadecimal color value in order to tint the token to the set color when bar1 is below half of its maximum. Set to "transparent" to disable this behavior.
  • StatusTracker.bloodiedMarker – Set to a statusmarker name in order to mark the token when bar1 is below half of its maximum. Set to "none" to disable this behavior.
  • StatusTracker.deadMarker – Set to a statusmarker name which indicates that the token is dead (bar1 is 0).
  • StatusTracker.statusIncidatorBG – Background style color for status chat messages.
  • StatusTracker.statusIndicatorFontColor – Foreground style color for status chat messages.

Changelog

v1.8 (2014-01-15)

  • Release