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
 
(9 intermediate revisions by 2 users not shown)
Line 1: Line 1:
{{script author|84478}}
 
  
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.
+
{{orange| API discontinued }}
 +
 
 +
Try this instead:
 +
* '''{{forum|permalink/9441420/ CombatMaster}}''' (by [[Victor B.]]) -- API for improving various aspects of combat. Automating condition duration in combat, Initiative improvement, and more.
 +
* [[API:Script Index]]
 +
 
 +
<!--
 +
{{script overview
 +
|name=Conditions and Status Tracker
 +
|author={{user profile|84478|Josh}}
 +
|version=1.8
 +
|lastmodified=2014-01-15}}
 +
 
 +
'''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.
 +
<br clear="both">
  
 
=== 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 48: Line 61:
 
* '''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  *********************************************
+
*  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>
+
  
[[Category:User API Scripts]]
+
[[Category:Discontinued API Scripts]]-->

Latest revision as of 21:14, 20 September 2021


Try this instead: