Skip to content

Instantly share code, notes, and snippets.

@Parmeisan
Created March 24, 2014 15:55
Show Gist options
  • Save Parmeisan/9743076 to your computer and use it in GitHub Desktop.
Save Parmeisan/9743076 to your computer and use it in GitHub Desktop.
"Page id: -JIjGqeZev9HB5rcJjv4"
"Comparing arrays:"
["-JHmkUEVLtCKu6vCFxtT"]
["-JHmkUEVLtCKu6vCFxtT","-JIjGi-23xpbsUXQMkhw"]
"New:-JIjGi-23xpbsUXQMkhw"
"Moving -JIjGi-23xpbsUXQMkhw to hand."
{"name":"Angreal - Well","avatar":"https://s3.amazonaws.com/files.d20.io/images/3499705/HEPFrdn2JGgMwycqu8bTqw/med.png?1395584000","_deckid":"-JIjDXFy-WlTpDfC1Ggu","_type":"card","_id":"-JIjGi-23xpbsUXQMkhw"}
"Creating card-token--JIjGi-23xpbsUXQMkhw-id"
"https://s3.amazonaws.com/files.d20.io/images/3499705/HEPFrdn2JGgMwycqu8bTqw/med.png?1395584000"
"Moving -JIjGi-23xpbsUXQMkhw to table."
"Found card-token--JIjGi-23xpbsUXQMkhw-id"
"last_change to_table"
"Comparing arrays:"
["-JHmkUEVLtCKu6vCFxtT"]
["-JHmkUEVLtCKu6vCFxtT","-JIjGi-23xpbsUXQMkhw"]
"New:-JIjGi-23xpbsUXQMkhw"
"is -JIjGi-23xpbsUXQMkhw?"
"Deleting from hand."
"Loading"
"Copying"
"Completed copy"
{"_id":"-JIog7FNFPMgrV3IrzLN","_pageid":"-JIjGqeZev9HB5rcJjv4","left":657,"top":529,"width":70,"height":70,"rotation":0,"layer":"objects","isdrawing":false,"flipv":false,"fliph":false,"imgsrc":"https://s3.amazonaws.com/files.d20.io/images/3499705/HEPFrdn2JGgMwycqu8bTqw/med.png?1395584000","name":"","gmnotes":"","controlledby":"","bar1_value":"","bar1_max":"","bar1_link":"","bar2_value":"","bar2_max":"","bar2_link":"","bar3_value":"","bar3_max":"","bar3_link":"","represents":"","aura1_radius":"","aura1_color":"#FFFF99","aura1_square":false,"aura2_radius":"","aura2_color":"#59E594","aura2_square":false,"tint_color":"transparent","statusmarkers":"","showname":false,"showplayers_name":false,"showplayers_bar1":false,"showplayers_bar2":false,"showplayers_bar3":false,"showplayers_aura1":false,"showplayers_aura2":false,"playersedit_name":true,"playersedit_bar1":true,"playersedit_bar2":true,"playersedit_bar3":true,"playersedit_aura1":true,"playersedit_aura2":true,"light_radius":"","light_dimradius":"","light_otherplayers":false,"light_hassight":false,"light_angle":"","light_losangle":"","sides":"","currentSide":0,"lastmove":"","_type":"graphic","_subtype":"card","_cardid":"-JIjGi-23xpbsUXQMkhw"}
"Comparing arrays:"
["-JHmkUEVLtCKu6vCFxtT"]
["-JHmkUEVLtCKu6vCFxtT","-JIjGi-23xpbsUXQMkhw"]
"New:-JIjGi-23xpbsUXQMkhw"
"Moving -JIjGi-23xpbsUXQMkhw to hand."
"Found card-token--JIjGi-23xpbsUXQMkhw-id"
"Deleting from table."
"Saving"
"Copying"
"Completed copy"
{"_id":"-JIog0mXaWgC_gQZTBKg","_pageid":"-JIjGqeZev9HB5rcJjv4","left":105,"top":105,"width":70,"height":70,"rotation":0,"layer":"objects","isdrawing":false,"flipv":false,"fliph":false,"imgsrc":"https://s3.amazonaws.com/files.d20.io/images/3499705/HEPFrdn2JGgMwycqu8bTqw/thumb.png?1395584000","name":"card-token--JIjGi-23xpbsUXQMkhw-id","gmnotes":"","controlledby":"","bar1_value":"1","bar1_max":"1","bar1_link":"","bar2_value":"","bar2_max":"","bar2_link":"","bar3_value":"","bar3_max":"","bar3_link":"","represents":"","aura1_radius":"","aura1_color":"#FFFF99","aura1_square":false,"aura2_radius":"","aura2_color":"#59E594","aura2_square":false,"tint_color":"transparent","statusmarkers":"","showname":false,"showplayers_name":false,"showplayers_bar1":false,"showplayers_bar2":false,"showplayers_bar3":false,"showplayers_aura1":false,"showplayers_aura2":false,"playersedit_name":true,"playersedit_bar1":true,"playersedit_bar2":true,"playersedit_bar3":true,"playersedit_aura1":true,"playersedit_aura2":true,"light_radius":"","light_dimradius":"","light_otherplayers":false,"light_hassight":false,"light_angle":"","light_losangle":"","sides":"","currentSide":0,"lastmove":"","_type":"graphic","_subtype":"token","_cardid":""}
"And back to you."
/home/symbly/www/d20-api-server/node_modules/firebase/lib/firebase-node.js:1
orts, require, module, __filename, __dirname) { function f(a){throw a;}var j=v
^
Error: Firebase.child failed: First argument must be a non-empty string and can't contain ".", "#", "$", "[", or "]".
at Error (<anonymous>)
at Ha (/home/symbly/www/d20-api-server/node_modules/firebase/lib/firebase-node.js:12:204)
at G.W.H (/home/symbly/www/d20-api-server/node_modules/firebase/lib/firebase-node.js:126:213)
at TrackedObj._doSave (
var debugging = true;//false;//
var speak_as = "Inventory-Extension";
var pg_id;
// When a card is dragged to the tabletop, it first triggers add:graphic and then change:hand.
// When the card is taken from the tabletop, it frist triggers change:hand and then destroy:graphic.
// (In other words, it's always copied before it's deleted.) However, any of these can be called
// independently of either situation, and I only want to react when both are called.
// Much of the code that follows deals with that... skip ahead to assertTokenInfo for the meat of the extension.
var last_change = new Array();
on("ready", function ()
{
last_change["time"] = 0; // I may want to add a check for how long ago this happened.
last_change["id"] = 0;
last_change["obj"];
last_change["type"] = "";
// Also, various checks to see if this script is viable:
var script_viable = false;
var script_errmsg = "";
var pg_obj = findObjs({ _type: "page", _name: "Inventory" });
if (pg_obj.length == 1)
{
pg_id = pg_obj[0].get("_id");
debug("Page id: " + pg_id);
}
else
{
script_errmsg += " * Requires exactly one page called 'Inventory'.";
}
script_viable = (script_errmsg.length == 0);
if (!script_viable)
{
script_errmsg = "Script requirements not met." + script_errmsg;
debug(script_errmsg);
error(script_errmsg);
}
else
{
on("change:hand", function(obj, prev)
{
var old_hand = prev["_currentHand"].split(",");
var new_hand = obj.get("_currentHand").split(",");
if (old_hand.length > new_hand.length)
{
debug("last_change " + last_change["type"]);
// Do the quick checks first, then compare card id, and *then* we can react.
if (last_change["type"] == "to_table" && isRecent(last_change["time"]))
{
var card_id = whatsNew(new_hand, old_hand);
debug("is " + last_change["id"] + "?");
if (card_id == last_change["id"])
{
debug("Deleting from hand.");
loadTokenInfo();
}
}
}
else if (old_hand.length < new_hand.length)
{
// Save this data so we can check it later
last_change["time"] = new Date();
last_change["id"] = whatsNew(old_hand, new_hand);
last_change["obj"] = getObj("card", last_change["id"]);
last_change["type"] = "to_hand";
debug("Moving " + last_change["id"] + " to hand.");
assertTokenInfo(); // We'll need it later.
}
else { last_change["type"] == "other"; }
});
on("add:graphic", function(obj)
{
var card_id = obj.get("_cardid");
if (!isnull(card_id))
{
// Save this data so we can check it later
last_change["time"] = new Date();
last_change["id"] = card_id;
last_change["obj"] = obj;
last_change["type"] = "to_table";
debug("Moving " + last_change["id"] + " to table.");
assertTokenInfo(); // We'll need it later.
}
});
on("destroy:graphic", function(obj)
{
// Do the quick checks first, then compare card id, and *then* we can react.
var card_id = obj.get("_cardid");
{
if (!isnull(card_id))
if (last_change["type"] == "to_hand" && isRecent(last_change["time"]))
{
if ((card_id == last_change["id"]))
{
debug("Deleting from table.");
// Update "obj" to be the newly created object first, we'll be needing that.
last_change["obj"] = obj;
saveTokenInfo();
}
}
}
debug("And back to you.");
});
}
});
// Takes two arrays, sorts them, and returns the first value in b that doesn't match a.
// If you already know that they are equal except for one added value, then this tells you what's new.
function whatsNew(a, b)
{
debug("Comparing arrays:");
// I don't know why, but sometimes there's a blank elements here and sometimes they don't even align
a = _.without(a.sort(), "");
b = _.without(b.sort(), "");
debug(a);
debug(b);
var found = false;
for (var i = 0; i < b.length; i++)
{
found = (a[i] !== b[i]);
if (found) break;
}
debug("New:" + b[i]);
return b[i];
}
// Checks if a time is "recent". The exact definition is arbitrary, but this is used to
// guess whether two functions were called one after the after.
function isRecent(t)
{
var now = new Date();
var milli = now - t.getTime();
return (milli < 5000); // 5 seconds
}
// -----------------------------------------------------------------------------------------------
// And finally, the meat of this script! Saving and loading all your token info.
// -----------------------------------------------------------------------------------------------
function assertTokenInfo()
{
// Check to see if this object exists yet, and if not, create it.
// This should happen in a separate call from actually doing anything with it,
// to give Roll20 a chance to actually save this data.
var chk_obj = findInventoryToken();
if (isnull(chk_obj))
{
var o = last_change["obj"];
debug(o);
if (!isnull(o))
{
debug("Creating " + getID());
var img_src; // Required, and must be exactly right
if (o.get("_type") == "card") img_src = o.get("avatar");
else img_src = o.get("imgsrc");
debug(img_src);
img_src = img_src.replace("med.png", "thumb.png");
chk_obj = createObj("graphic", {
pageid: pg_id, layer: "objects", name: getID(),
imgsrc: img_src, left: 105, top: 105, width: 70, height: 70
});
}
else
{
error("Could not create the inventory token object.");
}
}
else
{
debug("Found " + chk_obj.get("name"));
}
}
function saveTokenInfo()
{
debug("Saving");
var copy_src = last_change["obj"];
var copy_dest = findInventoryToken();//findOrCreate();
if (isnull(copy_dest))
error("Inventory token object not found.");
else
copyTokenInfo(copy_src, copy_dest);
}
function loadTokenInfo()
{
debug("Loading");
var copy_src = findInventoryToken();//findOrCreate();
var copy_dest = last_change["obj"];
if (isnull(copy_src))
error("Inventory token object not found.");
else
copyTokenInfo(copy_src, copy_dest);
}
function copyTokenInfo(copy_src, copy_dest)
{
debug("Copying");
if (!isnull(copy_src))
{
_.each(copy_src.attributes, function (value, key)
{
// Save everything that's not read-only or specially-flagged
var dont_save = Array("layer", "imgsrc", "left", "top", "name");
if (key.indexOf("_") != 0 && _.indexOf(dont_save, key) == -1)
{
//debug("saving: " + key + " -> " + value);
copy_dest.set(key, value);
}
//if (key == "gmnotes") copy_dest.set(key, saveGMNotes(value));
});
}
else
{
error("Found nothing to copy.");
}
debug("Completed copy");
debug(copy_dest);
}
function getID()
{
return "card-token-" + last_change["id"] + "-id";
}
// The Inventory version is identified the card ID in the name field.
function findInventoryToken()
{
return findObjs({ _type: "graphic", name: getID() })[0];
}
// My function library
function debug(s) { if (debugging) log(s); }
function error(s) { sendChat(speak_as, "Error: " + s); }
function nvl(o, v) { return isnull(o) ? v : o; }
function isnull(o) { return typeof o == 'undefined' || o == null || o.length == 0; }
"Page id: -JIjGqeZev9HB5rcJjv4"
"Comparing arrays:"
["-JHmkUEVLtCKu6vCFxtT"]
["-JHmkUEVLtCKu6vCFxtT","-JIjGi-23xpbsUXQMkhw"]
"New:-JIjGi-23xpbsUXQMkhw"
"Moving -JIjGi-23xpbsUXQMkhw to hand."
{"name":"Angreal - Well","avatar":"https://s3.amazonaws.com/files.d20.io/images/3499705/HEPFrdn2JGgMwycqu8bTqw/med.png?1395584000","_deckid":"-JIjDXFy-WlTpDfC1Ggu","_type":"card","_id":"-JIjGi-23xpbsUXQMkhw"}
"Creating card-token--JIjGi-23xpbsUXQMkhw-id"
"https://s3.amazonaws.com/files.d20.io/images/3499705/HEPFrdn2JGgMwycqu8bTqw/med.png?1395584000"
"Moving -JIjGi-23xpbsUXQMkhw to table."
"Found card-token--JIjGi-23xpbsUXQMkhw-id"
"last_change to_table"
"Comparing arrays:"
["-JHmkUEVLtCKu6vCFxtT"]
["-JHmkUEVLtCKu6vCFxtT","-JIjGi-23xpbsUXQMkhw"]
"New:-JIjGi-23xpbsUXQMkhw"
"is -JIjGi-23xpbsUXQMkhw?"
"Deleting from hand."
"Loading"
"Copying"
"Completed copy"
{"_id":"-JIofxi30qU4hexRSWNG","_pageid":"-JIjGqeZev9HB5rcJjv4","left":657,"top":519,"width":70,"height":70,"rotation":0,"layer":"objects","isdrawing":false,"flipv":false,"fliph":false,"imgsrc":"https://s3.amazonaws.com/files.d20.io/images/3499705/HEPFrdn2JGgMwycqu8bTqw/med.png?1395584000","name":"","gmnotes":"","controlledby":"","bar1_value":"","bar1_max":"","bar1_link":"","bar2_value":"","bar2_max":"","bar2_link":"","bar3_value":"","bar3_max":"","bar3_link":"","represents":"","aura1_radius":"","aura1_color":"#FFFF99","aura1_square":false,"aura2_radius":"","aura2_color":"#59E594","aura2_square":false,"tint_color":"transparent","statusmarkers":"","showname":false,"showplayers_name":false,"showplayers_bar1":false,"showplayers_bar2":false,"showplayers_bar3":false,"showplayers_aura1":false,"showplayers_aura2":false,"playersedit_name":true,"playersedit_bar1":true,"playersedit_bar2":true,"playersedit_bar3":true,"playersedit_aura1":true,"playersedit_aura2":true,"light_radius":"","light_dimradius":"","light_otherplayers":false,"light_hassight":false,"light_angle":"","light_losangle":"","sides":"","currentSide":0,"lastmove":"","_type":"graphic","_subtype":"card","_cardid":"-JIjGi-23xpbsUXQMkhw"}
Restarting sandbox due to script changes...
Spinning up new sandbox...
"Page id: -JIjGqeZev9HB5rcJjv4"
"Comparing arrays:"
["-JHmkUEVLtCKu6vCFxtT"]
["-JHmkUEVLtCKu6vCFxtT","-JIjGi-23xpbsUXQMkhw"]
"New:-JIjGi-23xpbsUXQMkhw"
"Moving -JIjGi-23xpbsUXQMkhw to hand."
"Found card-token--JIjGi-23xpbsUXQMkhw-id"
"Deleting from table."
"Saving"
"Copying"
"Completed copy"
{"_id":"-JIofqMe9lJuu6k3Z9WB","_pageid":"-JIjGqeZev9HB5rcJjv4","left":105,"top":105,"width":70,"height":70,"rotation":0,"layer":"objects","isdrawing":false,"flipv":false,"fliph":false,"imgsrc":"https://s3.amazonaws.com/files.d20.io/images/3499705/HEPFrdn2JGgMwycqu8bTqw/thumb.png?1395584000","name":"card-token--JIjGi-23xpbsUXQMkhw-id","gmnotes":"","controlledby":"","bar1_value":"1","bar1_max":"1","bar1_link":"","bar2_value":"","bar2_max":"","bar2_link":"","bar3_value":"","bar3_max":"","bar3_link":"","represents":"","aura1_radius":"","aura1_color":"#FFFF99","aura1_square":false,"aura2_radius":"","aura2_color":"#59E594","aura2_square":false,"tint_color":"transparent","statusmarkers":"","showname":false,"showplayers_name":false,"showplayers_bar1":false,"showplayers_bar2":false,"showplayers_bar3":false,"showplayers_aura1":false,"showplayers_aura2":false,"playersedit_name":true,"playersedit_bar1":true,"playersedit_bar2":true,"playersedit_bar3":true,"playersedit_aura1":true,"playersedit_aura2":true,"light_radius":"","light_dimradius":"","light_otherplayers":false,"light_hassight":false,"light_angle":"","light_losangle":"","sides":"","currentSide":0,"lastmove":"","_type":"graphic","_subtype":"token","_cardid":""}
"And back to you."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment