|
|
(8 intermediate revisions by 2 users not shown) |
Line 1: |
Line 1: |
| + | |
| + | {{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 | | {{script overview |
− | |author=84478 | + | |name=Conditions and Status Tracker |
| + | |author={{user profile|84478|Josh}} |
| |version=1.8 | | |version=1.8 |
| |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 10: |
Line 19: |
| | | |
| === 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 52: |
Line 61: |
| * '''StatusTracker.statusIndicatorFontColor''' – Foreground style color for status chat messages. | | * '''StatusTracker.statusIndicatorFontColor''' – 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]]--> |