
This script is intended to help handle HackMaster's Count Up system. HackMaster uses a unique way to manage players turns by counting in seconds, and allowing players to act on each second. This script manipulates the Turn Tracker to accommodate for the unique system. Updates : 5/19/14 - Added highlight to tokens when it is their turn Added permission restrictions to !eot command to players that control the token Added permission restrictions to !clearTracker, !next and !resetTint to GM only. Fixed some sorting issues. Added !resetTint function Usage: !clearTracker - Clears the tracker of all entries, and creates a second counter with a value of 0, and clears tint of all tokens previously in tracker. !next - Increments the second when no creature tokens match the current second. If a token does match the current second, it will move to that token. !eot [number] - Increments the current highlighted creature or player token by amount. !resetTint - Resets the tint on all tokens in the tracker to default. All functions re-sort the list after each command. !clearTracker, !resetTint and !next commands are intended to be for GM use only, while !eot command is for both players and GMs. Future Updates: No new features planned, message me if you would like something added. Credits: Some ideas were taken from other scripts including Chris N and HoneyBadger, thanks! Script: /*
Script to handle HackMaster count up system
This script is intended to help GMs handle large battles and initiatives more easily.
The !next function is for the GM to simulate counting up the seconds until a token with that initiative is reached, in which is will now wait for either the player or GM to modify the currently highlighted tokens second.
Note that this is not intented to control typical actions like movement, re-adjustments of weapon speeds due to new engagements, etc.
!clearTracker : Clear the tracker and add a Second Tracker with value of 0 and resets highlight tint.
!next : Handles second increments and turn switching.
!eot number : To be used by players to end their turn while in combat. It will take an input which will re-calculate the next second their weapon can attack on.
This can be combined with the HackMaster 5e character sheet and/or macro to allow easy second tracking based on multiple combat profiles.
Much of this script was re-used from both Chris N and HoneyBadger scripts.
-FeltZ
*/
var AnnounceNewSecond = true;
var GmIdList = [455563]; // List of GMs, get GM id by doing !getmyid
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 = true; // Make GM Obey RestrictEOT (Setting true allows GM to override)
/// 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);
// Increment Second and/or player turn
if (command == "!next" && IsGM(msg.playerid)) Next();
// End Turn
if (command == "!eot"){
// Get input to determine how much to increase initiative by
var speed = parseFloat(msg.content.replace("!eot ", ""));
EndTurn(msg.playerid,false,speed);
}
// Reset tint of all tokens in turn order
if(command == "!resetTint" && IsGM(msg.playerid)) ResetTint();
// Resets tint then clears tracker, leaving Second Counter at 0.
if(command == "!clearTracker" && IsGM(msg.playerid)) ClearTracker();
});
// Clear the tracker and add a Second Tracker with value of 0.
function ClearTracker(){
ResetTint();
var turnorder = [];
turnorder.push({
id: "-1",
pr: "0",
custom: "Second Counter"
});
Campaign().set("turnorder", JSON.stringify(turnorder));
}
function Next(){
var turnorder;
if(Campaign().get("turnorder") == "") turnorder = [];
else turnorder = JSON.parse(Campaign().get("turnorder"));
// Exit if there is only 1 item in tracker
if (turnorder.length < 2) return;
var turnOrderSorted = [];
// Sort items in tracker ascending
turnOrderSorted = sortJSON(turnorder,'pr');
// check if top item is NOT a token (second tracker)
if (turnOrderSorted[0].id == -1){
// check if token after second tracker has same time, if so, move second tracker to bottom and re-sort
if (parseFloat(turnOrderSorted[0].pr) == parseFloat(turnOrderSorted[1].pr)){
var turn = turnOrderSorted.shift();
turnOrderSorted.push({
id: turn.id,
pr: turn.pr,
custom: turn.custom
});
turnOrderSorted = sortJSON(turnOrderSorted,'pr');
}
// otherwise, increment second, and announce in chat window when enabled
else{
var CurrentSecond = parseFloat(turnOrderSorted[0].pr);
CurrentSecond = CurrentSecond+1;
turnOrderSorted[0].pr = CurrentSecond;
if(AnnounceNewSecond == true) {
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;'>Second " + turnorder[0].pr + "</div>");
}
// Remove highlights when top item is second counter.
ResetTint();
}
}
Campaign().set("turnorder", JSON.stringify(turnOrderSorted));
// Change highlight color if top item is not the second counter
if (turnOrderSorted[0].id != -1){
var current_token = getObj("graphic", turnOrderSorted[0].id);
ResetTint();
current_token.set({'tint_color' : tint_color_turn});
}
}
function EndTurn(playerid, force, speed){
var turnorder;
// Exit if turn order is empty
if(Campaign().get("turnorder") == "") return;
else turnorder = JSON.parse(Campaign().get("turnorder"));
// Exit if there is only 1 item in tracker
if (turnorder.length < 2) return;
// Exit if not a token at the top.
if (turnorder[0].id == -1) return;
// Grab the info for the top of initiative
var turn = turnorder[0];
// get the graphic obj for the current token
var graphic = getObj("graphic", turn.id);
// if request is not valid send error
if(force || EotRequestValid(RestrictEOT,RestrictGmEOT,playerid,graphic)){
// Get input to determine how much to increase initiative by
var CurrentSecond = parseFloat(turnorder[0].pr);
CurrentSecond = CurrentSecond+speed;
turnorder[0].pr = CurrentSecond;
var turnOrderSorted = [];
// Sort items in tracker ascending
turnOrderSorted = sortJSON(turnorder,'pr');
Campaign().set("turnorder", JSON.stringify(turnorder));
// Remove highlight from current player and add to next if there is one
ResetTint();
if (turnorder[0].id != -1){
var current_token = getObj("graphic", turnorder[0].id);
current_token.set({'tint_color' : tint_color_turn});
}
}
else{
SendChatTo(playerid, "TurnAgent", "It's not your turn silly");
return;
}
}
// 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 turnorder = JSON.parse(Campaign().get('turnorder')); // Parse the turn order information into JSON
if (!turnorder.length) return; // Exit if there are no tokens on the tracker
turnorder.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
}
}catch(e)
{
log(e);
sendChat("GM", "/w GM Error occured in TurnHighlighter2 script func(ResetTint)");
sendChat("GM", "/w GM " + e);
return;
}
});
};
// Sort function
function sortJSON(turnorder, pr) {
return turnorder.sort(function(a, b) {
var x = parseFloat(a['pr']); var y = parseFloat(b['pr']);
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
});
}
// 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"));
});
// 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
};
// 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;
};