A little script I modified from HoneyBadger's (much thanks) TurnHighlighter script, it has been modified except for the main turn processing logic. Change Log: v1.0 03/29/2014: Initial Release v1.1 3/29/2014: Adding command !turninit [ordering] Adds/Reinitializes Round tracker automatically when initiated v1.2 3/30/2014: Fixed a couple bugs regarding object types v1.3 4/04/2014 Fixed the Round 0 issue. Fixed a crash bug that occurs when attempting to !turninit an empty turn order list Added a command !eoc - this signals end of combat and clears all tint values as well as clears the turn order list v1.4 4/05/2014 Fixed my last fix - now prevents multiple Round markers from adding to the Turn Order. v1.5 10/26/2014 Added a NotifyEndOfTurn and NotifyStartOfTurn option that sends a chat message when a turn ends and a turn begins. (See post below for screen shots) Known Bugs: [Fixed]: Sometimes have to send !turninit command twice to initiate Round 1 Functions: Tints the token that is at the top of the turn list, Color settable. Provides an end of turn command (!eot) to all players with restrictions (more later) Provides a tint reset command to reset the tint of all tokens currently in the turn order list. Other utility functions. Options: AnnounceNewRound = true; // Turns on round announcement (legacy from HoneyBadgers original script, I really like it so I left it) tint_color_turn = '#FFFF00'; // Tint color for the current tokens turn (currently yellow) tint_color_default = 'transparent'; // Default tint color (when it's not a tokens turn) GmIdList = []; A list of d20 user id's that should be considered a GM in regards to sending commands. (use !getmyid to obtain the id needed) Commands: - Init Tracker : !turninit [ordering] ordering : 321 : (default) numeric descending; e.g. !turninit 321 or !turninit 123 : numeric ascending; e.g. !turninit 123 abc : A to Z ordering; e.g. !turninit abc cba : Z to A ordering; e.g. !turninit cba !turninit is a GM only command. - End of Turn: !eot This can be added to a macro along with other normal end of turn actions (such as saving throws for you D&D 4ers). This helps provide a way to remember making those pesky saving throws. This command can only be performed by a player that can control the token whose turn it is (at the top of the list). If a player uses !eot when it's not his/her turn, a message will be sent to them via chat letting them know. Options: Two options for !eot RestrictEOT = true ; Allows only players that can control a token to end the tokens turn. false ; Allows anyone to use !eot cmd on any token. RestrictGmEOT = false; Allows the GM to use the !eot command on any token; true; forces GM to obey RestrictEOT rule. - GetMyId: !getmyid Responds via a chat whisper with the players _d20userid (e.g. getObj("player",playerid).get("_d20userid")); - Reset End of Turn: !reot Resets the Tint value of all tokens in the turn order. The tinting will resume once the turn order changes after issuing this command. GM Only command - End of Combat: !eoc Ends the combat by resetting all tint values and clearing the turn order list. GM only command The Script // VARIABLE & FUNCTION DECLARATION
var GmIdList = ["415342"]; // List of GM's (Used with RestrictGmEOT)
var TurnOrderAgent = TurnOrderAgent || {};
// AnnounceNewRound - Set to TRUE if you want the script to announce
// the beginning of each new round.
var AnnounceNewRound = true;
var tint_color_turn = '#FFFF00'; // Tint color for the current tokens turn
var tint_color_default = 'transparent'; // Default tint color
var RestrictEOT = true; // Restricts !eot to only controllable players
var RestrictGmEOT = false; // Make GM Obey RestrictEOT (Setting true allows GM to override)
var alternate = false;
var NotifyEndOfTurn = false; // Enable Notification in chat window the end of a turn
var NotifyStartOfTurn = false; // Enable Notification in chat window the start of a turn
var Version = 1.5
// Turn Ordering functions
var tosorts = { "abc": function(a,b){return a.pr.toString().localeCompare(b.pr.toString())},
"cba":function(a,b){return b.pr.toString().localeCompare(a.pr.toString())},
"123":function(a,b){return a.pr-b.pr},
"321":function(a,b){return b.pr-a.pr}};
var TOSortFunc = function(turnOrder,ordering){
turnOrder.sort(function(a,b){
try{
if (a.pr.toString().substring(0, 5) == "Round") return -1;
else if(b.pr.toString().substring(0,5) == "Round") return 1;
else return tosorts[ordering](a,b);
}
catch(e)
{
log(e);
sendChat("TOSortFunc", "/w GM " + e);
}
});
return turnOrder;
};
/// Process Chat Messages
on("chat:message", function(msg) {
// Exit if not an api command
if (msg.type != "api") return;
// Get the API Chat Command
msg.who = msg.who.split(" ",1)[0];
var command = msg.content.split(" ", 1);
// End Turn
if (command == "!eot") EndTurn(msg.playerid,false);
// Reset tint of all tokens in turn order
if(command == "!reot" && IsGM(msg.playerid)) ResetTint();
if(command == "!eoc" && IsGM(msg.playerid)) EndCombat();
// Testing
if(command == "!turninit" && IsGM(msg.playerid)) InitRounds(msg);
});
/// Process Turn Change callback
on("change:campaign:turnorder", function(obj) {
TurnOrderAgent();
});
/// Initialize the Turn Order using AutoRound Tracker with ordering if turned on.
function InitRounds(msg){
var cmd = msg.content.split(" "); // tokenize command
var ordering = cmd.length > 1 ? cmd[1] : "321" // default descending sort
var to = Campaign().get("turnorder");
var turn_order = JSON.parse(to); // Parse the turn order information into JSON
if(turn_order.length == 0) return
turn_order = TOSortFunc(turn_order,ordering); // Sort turn order
if(AnnounceNewRound) // only add round tracker if announce is turned on
{
if(turn_order[0].pr.toString().substring(0, 5) == "Round") // initialize Round #
turn_order[0].pr = "Round 0";
else{
turn_order.unshift({
id: "-1",
pr: "Round 0",
custom: "--Round--",
});
}
}
Campaign().set("turnorder", JSON.stringify(turn_order)); // Send the turn order back to the tracker
TurnOrderAgent(); // Process the tracker now
};
/*
function TurnOrderSortByPr(turnOrder){
return;
turnOrder.sort(function(a,b){
if (a.pr.substring(0, 5) == "Round") return -1;
else if(b.pr.substring(0,5) == "Round") return 1;
else return b.pr-a.pr;
});
return turnOrder;
};
*/
/// Processes a Turn Order callback
function TurnOrderAgent () {
if (!Campaign().get("turnorder")) return;
var turn_order = JSON.parse(Campaign().get("turnorder"));
if (!turn_order.length) return;
if (typeof turn_order[0].pr == "string") {
if (turn_order[0].pr.toString().substring(0, 5) == "Round") {
var RoundTracker = turn_order[0].pr;
var CurrentRound = parseInt(RoundTracker.toString().substring(5));
turn_order[0].pr = "Round " + (CurrentRound + 1);
Campaign().set({turnorder: JSON.stringify(turn_order)});
if(AnnounceNewRound == true) {
log("Sending announce");
sendChat("", "/desc ");
sendChat("", "/direct <div style='width: 100%; color: #C8DE84; border: 1px solid #91bd09; background-color: #749a02; box-shadow: 0 0 15px #91bd09; display: block; text-align: center; font-size: 20px; padding: 5px 0; margin-bottom: 0.25em; font-family: Garamond;'>" + turn_order[0].pr + "</div>");
EndTurn(-1,true);
}
}
}
// Exit script if custom item on turn order tracker instead of a token...
if (turn_order[0].id == -1) return;
var current_token = getObj("graphic", turn_order[0].id);
var currentTint = current_token.get('tint_color');
ResetTint();
current_token.set({'tint_color' : tint_color_turn});
};
function EndTurn(playerid, force){
var to = Campaign().get('turnorder');
if (!to) return; // Exit if the turn order tracker is not open
var turn_order = JSON.parse(to); // Parse the turn order information into JSON
if (!turn_order.length) return; // Exit if there are no tokens on the tracker
var turn = turn_order.shift(); // Grab the info for the top of initiative
var graphic = getObj("graphic", turn.id); // get the graphic obj for the current token
if(force || EotRequestValid(RestrictEOT,RestrictGmEOT,playerid,graphic)){ // if request is not valid send error
// Request valid... Process
if(NotifyEndOfTurn) NotifyEOT(graphic); // Notify End of turn if enabled
turn_order.push({ // Add the info to the bottom of initiative
id: turn.id,
pr: turn.pr,
custom: turn.custom
});
if(NotifyStartOfTurn) NotifySOT(getObj("graphic", turn_order[0].id)); // Notify Start of turn if enabled
Campaign().set("turnorder", JSON.stringify(turn_order)); // Send the turn order back to the tracker
log(turn_order);
TurnOrderAgent();
}
else{
SendChatTo(playerid, "TurnAgent", "It's not your turn silly");
return;
}
};
function NotifyEOT(graphic){
var char = getObj("character", graphic.get("represents"));
if(char){
sendChat("", "/desc ");
sendChat("", "/direct <div style='width: 100%; color: #C8DE84; border: 1px solid #91bd09; background-color: #A00000; box-shadow: 0 0 15px #91bd09; display: block; text-align: center; font-size: 20px; padding: 5px 0; margin-bottom: 0.25em; font-family: Garamond;'>" + "Ends Turn - " + char.get("name") + "</div>");
}
}
function NotifySOT(graphic){
var char = getObj("character", graphic.get("represents"));
if(char){
sendChat("", "/desc ");
sendChat("", "/direct <div style='width: 100%; color: #C8DE84; border: 1px solid #91bd09; background-color: #749a02; box-shadow: 0 0 15px #91bd09; display: block; text-align: center; font-size: 20px; padding: 5px 0; margin-bottom: 0.25em; font-family: Garamond;'>" + "Starts Turn - " + char.get("name") + "</div>");
}
}
/// Resets the Tint value of all tokens in the Turn Order to tint_color_default
function ResetTint(){
if (!Campaign().get('turnorder')) return; // Exit if the turn order tracker is not open
var turn_order = JSON.parse(Campaign().get('turnorder')); // Parse the turn order information into JSON
if (!turn_order.length) return; // Exit if there are no tokens on the tracker
turn_order.forEach(function(entry){ // Reset all tint colors for current turn order list
try{
if(entry.id != "-1"){
var token = getObj("graphic", entry.id);
if(token) token.set({'tint_color' : tint_color_default}); // Reset tint value to transparent
}
//Campaign().set("turnorder", {}); // Reset Tracker
}catch(e)
{
log(e);
sendChat("GM", "/w GM Error occured in TurnHighlighter2 script func(ResetTint)");
sendChat("GM", "/w GM " + e);
return;
}
});
};
/// Ends combat clearing all tint values and removing all tokens from the turn order
function EndCombat(){
ResetTint();
Campaign().set("turnorder", "[]"); // Reset Tracker
};
/// Returns true if the EOT Request is valid given the inputs.
/// restrictEot: true: only controlling players can request, false: allows all
/// restrictGmEOT: true: GMs must follow restrictEot rule, otherwise they are always valid
/// playerid: playerid of the requester
/// graphic: graphic object
function EotRequestValid(restrictEot, restrictGmEOT, playerid, graphic){
return !restrictEot || // No Restrictions
(!restrictGmEOT && IsGM(playerid)) || // GM Override
(IsControlledBy(graphic, playerid)); // controlling player
};
/********************************************************************
Utility Functions
********************************************************************/
/// Returns the requesters player._d20userid value via chat
on("chat:message", function(msg) {
// Exit if not an api command
if (msg.type != "api") return;
// Get the API Chat Command
var command = msg.content.split(" ", 1);
if (command == "!getmyid") SendChatTo(msg.playerid, "GetMyId", "UserId: " + getObj("player", msg.playerid).get("_d20userid"));
});
/// Send a whisper to a player using playerid
function SendChatTo(playerid, chatMsg){
SendChatTo(playerid,"Script",chatMsg);
};
/// Send a whisper to a player using a playerid with a custom source
function SendChatTo(playerid, sendAs, chatMsg){
var player = getObj("player", playerid);
if(player) sendChat(sendAs, "/w " + player.get("_displayname").split(" ",1) + " " + chatMsg);
};
/// Returns -1 if the value is not in the array, otherwise the array index of the value
function ArraySearch(array, value){
if(array.length <= 0) return -1;
for(var i = 0; i < array.length; i++) {
if(array[i] == value) return i;
}
return -1;
}
/// Returns an array of playerids that control the graphic
/// First checks if token is a character
/// true: list of playerids that control the character
/// false: list of playerids that control the graphic e.g. graphic.controlledby
function GetControlledBy(graphic){
var charId = graphic.get("represents");
if(charId) return getObj("character", charId).get("controlledby").split(",");
return graphic.get("controlledby").split(",");
};
/// Returns true if "all" or playerid is a controller for the specified graphic or
/// note: while the GM can control all, this does not return true for GM's playerid
/// unless GM is explicitly set or "all" is set
function IsControlledBy(graphic, playerid){
var controllerIds = GetControlledBy(graphic);
if(controllerIds.length <= 0) return false;
for(var i = 0; i < controllerIds.length; i++) {
if(controllerIds[i] == playerid || controllerIds[i] == "all") return true;
}
return false;
};
/// Returns true if the given playerid is a GM
/// Uses the GmIdList global to determine if player is a GM
function IsGM(playerid){
if(!GmIdList) return false;
return ArraySearch(GmIdList, getObj("player", playerid).get("_d20userid")) >= 0;
};