This is a complete overhaul of traditional tabletop inventory management, replacing a large blank area on a character sheet with a living, breathing and fully responsive graphic user interface. It's designed to simultaneously add precision and realism, but also make tracking these stats even easier than through traditional methods. No pencils, no paper, no slowing down your game. Before I even get into the nitty gritty of this script, I'm going to start with a disclaimer. This is not a small deal. It will require a good bit of work on your end, from creating your items (defining weights and volumes and creating graphics) to setting up the area your players will use. For best results, it is HIGHLY RECOMMENDED to create this system in a separate campaign altogether, and have your players open it up in a separate window during your playtime. While the set up of this system can be quite the task, the end result can drastically improve the flow and realism of your game. If you're not ready to spend a few hours getting this up and running, turn back now and go back to what you're used to. How it Works Using Roll20's built-in functionality, this script automatically calculates a character's carrying weight, carrying volume, fullness of containers (backpacks, pouches, quivers), liquid weights based on volume (waterskins, potions), warmth, degree of comfort during rests, and how likely a character is to have trouble doing dexterous acts. Yes, ALL of those. Your players are also able to throw their inventories and items onto the ground, restricting access to them until the GM gives them back. This is good for situations where a character would drop his or her backpack to aid in combat movement or other dexterous activities. Installation - The Journey to Realism Begins STEP ONE - POPULATE Create your items. If you're playing Dungeons & Dragons, a great place to start is the player's handbook. Create a journal entry (with a default token) for each item in the book (adventurer's gear, weapons, armor, food, tool kits, etc). The green bar (bar1) of each item's token should be its weight in pounds, and the blue bar (bar2) should be it's volume in cubic feet. Leave the max values of these two bars blank, unless specified in the Special Items section below. (Third bar is optional and up to you; I use it to track item HP or durability). While weight of items is pretty easy to find, volume is a little trickier. Try to be accurate, but don't kill yourself over it. Be flexible, and err to the benefit of your players. For example, a nicely folded set of traveler's clothes might only take up 0.1 cubic feet, but a messy heap of clothing could easily occupy 0.3 or more. Give your players the benefit of the doubt and go with the lower value: you're already adding a challenging level of realism by even installing this, so balance out fun with grit. Try to think in terms of quantity, and visualize the items in your mind. Ask yourself, "how many of these healing potions could I fit in an adventurer's backpack (which, conveniently, holds 1 cubic foot) and divide it out. For example, if you think you could fit 50 healing potions in that backpack, divide 1/50 and there's your volume. Remember, it's your world, and the better you become at improvising, the easier it will be for you to create special items for your characters on the fly (think: keys, treasure items, maps, quest items). Both weight and volume will be tracked in real time on a special status token on the player's sheet. Keep special items in mind: Liquid containers, such as waterskins, bottles, vials, flasks, etc. are handled a little differently. Set the bar1 value at 0, and its max at how many pints the container can hold when its full. (We use pints because 1 pt = 1 lb for most water-like liquids). For example, a 5e waterskin holds 4 pts of liquid, so its green bar should be set to 0/4. For simplicity, volume is still static for these items. Finally, put the following in the item's GM Notes (always without quotes): " weight X liquid ", where X = how much the container weighs (in lbs) when it's empty. When it's all set up, your players will be able to adjust the bar1 value depending on how full the container is, and name the container based on what's inside. For example, give your players an empty bottle token, fill it up to its max weight, color it red and name it "Potion of Healing." When they "drink" it (reduce its green bar to 0), the token will automatically revert to its default state (an empty bottle with no name and no color tint!) and they can then choose what to do with the empty container. There are some items that would be silly to put into containers like backpacks, pockets and pouches. For example, your players are not going to put that 10-foot-ladder they picked up into their backpack, despite the fact that it would technically meet the requirements. I also make sure my players aren't stuffing greatclubs and greatswords into their packs, either. To prevent players from stowing these sorts of items, simply put " unstowable " in its GM notes. This will force them to sling these items on their backs or carry them some other way. Certain items are physical representations of the inventories they provide. Backpacks, quivers, pouches, and pockets on a set of clothing are some examples. These items need to be identified using the " storageItems " array in the script. If your item is a backpack, put " backpack " in the GM Notes. If you want your clothing items to have pockets, put "pocket1". "pocket2" and so on depending on how many pockets the clothing has. The first time your player equips one of these "inventory" items, the script will automatically generate a storage area for you, the GM, to move into a nice spot. This will be explained in greater detail in a later section. The default selection of such items includes: " quiver " " boltcase " " pouch " " bagofholding " " pocket1 " " pocket2 " ... " pocket6 " and a few more, including the Handy Haversack " hhh " and its individual sides. You can modify this list as you please in the script. (OPTIONAL) If you want to use this script to calculate currency, coins are handled a bit differently, and they are the only item that is grouped in a single token (by type). You don't need more than a single graphic, but you will need to change the GM Notes of each monetary graphic to " coins " followed by the type, for example "coins copper". Relevant types are " copper " " silver " " gold " " electrum " " platinum " (I'll probably add some configuration here down the line). Do not modify the bar1 and bar2 values of the coin graphics. Instead, change the bar3 value to the amount of coins that graphic represents. The script will then automatically calculate that amount of coins' weight and volume (based on user defined variables in the config, defaults to 5e rules). (OPTIONAL) Wearable items that provide warmth, such as clothing and most armors, need special text in their GM Notes. Think of clothing and armor as how well they protect you from the elements on a scale of 1 to 4, with 4 being something very warm, like traveler's clothes or plate armor. For these items, put in the GM Notes: " warmth X " with X = the value of warmth. Commoner clothes might be 2, while a hat or a breastplate might be 1 or even 0. Use your judgment and have fun with it. When your players put these items in a "worn" inventory slot, it will increase their protection from cold by 10*F for each digit of warmth (you can change these values to Celsius in the script config) and will reflect on a special status token on their sheets. (OPTIONAL) Hospitality items are items such as tents, mess kits, bed rolls, blankets, pillows and anything else you can think of that would improve quality of life for a player during a short/long rest. This creates a simple numerical value that you can use for stat checks, temp HP or any other awesome benefit you can dream up that gives a player tangible incentive for even having these sorts of items. To enable, put " hosp " in an item's GM Notes. That item, when stored anywhere in that player's inventory, will add +1 to their hospitality rating and add to the party's average, which are both displayed on a convenient status token. You can do as much or as little as you want for your item list. I personally have nice artwork for each item and some even have helpful information about them in the journal. As long as the weights and volumes are filled out, you've done enough to move on to the next step. STEP TWO - LAYOUT Create your work space. Each player needs a large "main" area that will encompass all of the other "sub" inventories, which include wearable slots, backpacks, quivers, and anything else. If you want, you can also have special areas of ground where players can "drop" used items (arrows, empty bottles, used spell scrolls). These "drop" areas are programmed to move the item to the map layer and remove them from calculations until the GM decides what to do with them. For example, a clever rogue can "drop" his or her heavy backpack before invading a barracks to help with their stealth and acrobatics checks, and then pick it up later. If you want to model your "main" areas after mine, create a silhouette shape that you can populate with as many worn spaces as you desire, keeping default grid sizes (70 pixel x 70 pixel squares) in mind. Having some experience with Photoshop or similar can be helpful here. My main areas are available here for use. Simplicity is probably a good idea, as you will be piling A LOT of stuff on these, and size is important, as you need to be prepared for your players' crazy ideas, like carrying three backpacks or a barrel full of gear on a sled behind them. You'll need one " main " area for each player, and I choose to stack mine vertically for their ease of access. You need to number them for each player by putting the player number as the value for bar3, starting with 1. Put " main " in the GM Notes for each, and then put them on the map layer and forget about them. You can then decorate them for identification purposes, like adding a text name tag. Create a lightly tinted square (and ideally semi-transparent) graphic for "worn" inventory slots. These will all go on the map layer along with the "main" area. You're free to do as you please, but keeping it open ended works to everyone's benefit. A slot of each shoulder, one for the back, a couple for clothing an armor, two each for the hands and wrists, ring/bracelet slots, a necklace slot, head and cap slots, boot slots, and whatever else you can dream of. Position is more important than actually naming them. For example, I just put a bunch of squares touching every part of the silhouette on the grid, coloring some of them for ease. You can even just cover the silhouette with one big (transparent) rectangle and call it a day. Your players will likely use their logic (daggers in the belt, backpack between the shoulders, etc) and should be able to easily justify their usage. Each of these should have " worn " in the GM Notes. Go back to the "object" layer and create a token to display carrying weight/volume and put " status " in the GM Notes (and if you're using the warmth/hospitality system, make a second token with " status2 " in the GM Notes). Put the token(s) somewhere prominent (I used their character tokens for the status graphic, and a little campfire for the warmth/hosp graphic). Put their player number as the bar3 value (should match the "main" sheet) and bar1's max as the player's max carry weight. This will create a nice at-a-glance meter for your players so they can see how weighed down they are. The status token will also display a little stat called "Profile," which is how "big" their character is (it's the total volume, in square feet, of everything they have on them). I use this number against various dexterity checks, as a character loaded down with a bunch of crap would find these tasks much more difficult. (OPTIONAL) Grab a graphic that represents the ground, put it somewhere on the map layer and put " drop " in the GM notes. These automatically bring items to the map layer and out of the players' calculations, as explained above. When the GM moves these items out of the "drop" area, they will automatically go back to the object layer. STEP THREE - SCRIPT At this point, you should be ready to configure the script. The user configuration section is close to the top. The Graphic Inventory Manager WILL NOT FUNCTION AT ALL without some minimal configuration. Assign player IDs. The variable logPlayerIds should be set to true by default. If it is not, change it to true and save the script. When it starts, the console will log the IDs of every player who has ever joined your campaign. Copy and paste the relevant ones into the playerList array at the top of the user configuration section. Please pay special attention and note that THE MINUS SIGN IS PART OF THE ID and the players should be ordered according to the number you assigned them in descending order. You can have as many or as few players as you want, just make sure that any unused slots are deleted or commented out, like the Sampleton Testificate example is. Also, make sure to match the syntax of the existing IDs, including the quotes and commas. Change the default inventory image. The script will CRASH if you do not properly set your own default inventory box image for the invImg variable. YOU CANNOT USE THE DEFAULT LINK. This is the image that should make up the defining box of almost every inventory on your sheet (probably a semi-transparent square). Follow the instructions on the wiki to properly find the code for the image you want to use. All other configuration is optional or user discretion. This includes changing the temperature unit, the weight and volume of coins, the types of inventories available to your players and their maximum weights and volumes. STEP FOUR - EQUIP It's finally time to begin! Drag items from your item journal to your players individual sheets and have them equip themselves. When they equip a storage item (like a backpack) for the first time, it is YOUR JOB to move and stretch that item to the standards of your game, and decorate it as you please. You can add custom backgrounds and attachment slots to your items, as well, for visual effect. View the instructional video (COMING SOON) below for more information. Good luck, and enjoy! Above all, remember, this system is designed to make your game run SMOOTHER AND FASTER and allow you and your players to easily and precisely track your inventories. It is NOT designed make things more difficult, especially for your players! The burden of this task should be on you, the DM, and you should strive to make these technical details as easy as possible. This script -> less typing -> more storytelling! THIS IS AN OLD VIDEO, BUT IT MIGHT HELP YOU VISUALIZE THE ABOVE STEPS WHILE I GET A NEW ONE MADE: <a href="https://www.youtube.com/watch?v=sp1KACYziFg&feature=youtu.be" rel="nofollow">https://www.youtube.com/watch?v=sp1KACYziFg&feature=youtu.be</a>
I AM CURRENTLY CONSTRUCTING THIS OP. PLEASE WAIT UNTIL I RELEASE THE INSTRUCTIONAL AND MORE INFORMATION BEFORE BUG REPORTS/COMPLAINTS/HELPME'S. v.0.1 (2015-01-07) Release /////////////////////////////////////////////////
/***********************************************/
var GIM = {
alias: "Graphic Inventory Manager",
author: {
name: "John C." || "Echo" || "SplenectomY",
company: "Team Asshat" || "The Alehounds",
contact: "<a href="mailto:echo@TeamAsshat.com" rel="nofollow">echo@TeamAsshat.com</a>",
},
version: "0.2",
gist: "<a href="https://gist.github.com/SplenectomY/9d1ef745f23927edc930" rel="nofollow">https://gist.github.com/SplenectomY/9d1ef745f23927edc930</a>",
forum: "",
/***********************************************/
/////////////////////////////////////////////////
/////////BEGIN USER CONFIG/////////
playerList: [
"-JeTLkLt_UFt7Boc1DsQ", /* 1: Coille Flitterleaf */
"-JeT529rL-wvu8yT8Q9N", /* 2: Tanion of House Victaria */
"-JeXs7g5ADZiaVjYOdIg", /* 3: Oldstuff McTuggins */
"-Jebn59AIWLWOxW3WMS9", /* 4: Xanneiros Lothammer */
//"-JeIO7XaKP76ZS22z0x3", /* 5: Sampleton Testificate */
],
logPlayerIds: true,
invImg: "<a href="https://s3.amazonaws.com/files.d20.io/images/6927416/y5kI_BBCDJrxGCU8FttwEg/thumb.png?1420069394" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/6927416/y5kI_BBCDJrxGCU8FttwEg/thumb.png?1420069394</a>",
coin: {
wgt: 0.02, /* in lbs. Default is per 5e rules */
vol: 0.0002 /* in cubic feet. (5,000 per ft3) */
},
temperatureUnit: "F", /* Change to C if Celsius is desired */
storageItems: [
backpack = {
name: "backpack",
max_wgt: 30,
max_vol: 1,
},
quiver = {
name: "quiver",
max_wgt: "",
max_vol: 0.1,
},
boltCase = {
name: "boltcase",
max_wgt: "",
max_vol: 0.15,
},
pouch = {
name: "pouch",
max_wgt: 6,
max_vol: .2,
},
bagHolding = {
name: "bagofholding",
max_wgt: 500,
max_vol: 64,
},
handyHaversackSide1 = {
name: "hhhside1",
max_wgt: 20,
max_vol: 2,
},
handyHaversackSide2 = {
name: "hhhside2",
max_wgt: 20,
max_vol: 2,
},
handyHaversack = {
name: "hhh",
max_wgt: 80,
max_vol: 8,
},
// I have 6 pockets, because thats the max
// I would give my characters on any item.
// Add more if you wish.
pocket1 = {
name: "pocket1",
max_wgt: 3,
max_vol: .1,
},
pocket2 = {
name: "pocket2",
max_wgt: 3,
max_vol: .1,
},
pocket3 = {
name: "pocket3",
max_wgt: 3,
max_vol: .1,
},
pocket4 = {
name: "pocket4",
max_wgt: 3,
max_vol: .1,
},
pocket5 = {
name: "pocket5",
max_wgt: 3,
max_vol: .1,
},
pocket6 = {
name: "pocket6",
max_wgt: 3,
max_vol: .1,
},
],
//////////END USER CONFIG//////////
runTimeout: 0,
isNewGraphic: false,
extraRun: false,
objsToBeCreated: [],
runMe: function forGreatJustice() {
if (GIM.runTimeout > 0) {
GIM.runTimeout--;
} else {
processGIM()
for(i1 = 0; i1 < GIM.objsToBeCreated.length; i1++) {
create_inv(
GIM.objsToBeCreated[i1][0],
GIM.objsToBeCreated[i1][1],
GIM.objsToBeCreated[i1][2],
GIM.objsToBeCreated[i1][3],
GIM.objsToBeCreated[i1][4],
GIM.objsToBeCreated[i1][5],
GIM.objsToBeCreated[i1][6]
);
}
GIM.objsToBeCreated = [];
// This handles any extra runs
if (GIM.extraRun == true && GIM.objsToBeCreated.length == 0) {
processGIM();
processGIM();
GIM.extraRun = false;
}
GIM.runTimeout--;
}
},
isObj: function runFlagChecks(obj,type) {
s = obj.get("gmnotes");
var isObjResult,
invFlags = [
"main",
"inv",
"worn",
"drop",
"ignore"
],
statusTokenFlags = [
"status",
"status2"
];
if (type == "item") {
isObjResult = true;
for (i2 = 0; i2 < invFlags.length; i2++) {
if (s.indexOf(invFlags[i2]) !== -1) {
isObjResult = false;
}
}
for (i3 = 0; i3 < statusTokenFlags.length; i3++) {
if (s.indexOf(statusTokenFlags[i3]) !== -1) {
isObjResult = false;
}
}
}
if (type == "inv") {
isObjResult = false;
for (i4 = 0; i4 < invFlags.length; i4++) {
if (s.indexOf(invFlags[i4]) !== -1) {
isObjResult = true;
}
}
}
if (type == "status") {
isObjResult = false;
for (i4 = 0; i4 < statusTokenFlags.length; i4++) {
if (s.indexOf(statusTokenFlags[i4]) !== -1) {
isObjResult = true;
}
}
}
return isObjResult;
},
isInside: function allYourBase(x,y) {
if (x.get("left") > y.left && x.get("top") > y.top && x.get("left") < y.right && x.get("top") < y.bottom) return true;
else return false;
}
};
fixedCreateObj = (function () {
return function () {
var obj = createObj.apply(this, arguments);
if (obj && !obj.fbpath) {
obj.fbpath = obj.changed._fbpath.replace(/([^\/]*\/){4}/, "/");
}
return obj;
};
}());
function processGIM() {
var warmth = [],
hosp = [],
hosp_average = 0,
equipped = [],
inv = [],
items = [],
items_stacked = [],
objInvs = filterObjs(function(obj) {
if(obj.get("type") == "graphic") {
return GIM.isObj(obj,"inv");
}
else return false;
}),
objItems = filterObjs(function(obj) {
if(obj.get("type") == "graphic") {
return GIM.isObj(obj,"item");
}
else return false;
}),
objStatus = filterObjs(function(obj) {
if(obj.get("type") == "graphic") {
return GIM.isObj(obj,"status");
}
else return false;
}),
objects = objInvs.concat(objItems,objStatus);
for (i5 = 0; i5 < GIM.playerList.length; i5++) {
warmth[i5] = 0;
hosp[i5] = 0;
equipped[i5] = [];
}
/***************************/
/***** STEP 1 out of 4 *****/
/***************************/
_.each(objInvs, function(obj) {
s = obj.get("gmnotes");
if (s.indexOf("main") !== -1) {
if (!obj.get("bar1_value")) {obj.set("bar1_value", 0)}
inv[inv.length] = {
type: "main",
id: obj.get("_id"),
owner: obj.get("bar3_value"),
weight: 0,
volume: 0,
inv_gmnotes: obj.get("gmnotes"),
left: Math.floor(obj.get("left") - Math.floor(obj.get("width") / 2)),
top: Math.floor(obj.get("top") - Math.floor(obj.get("height") / 2)),
right: Math.floor(obj.get("left") + Math.floor(obj.get("width") / 2)),
bottom: Math.floor(obj.get("top") + Math.floor(obj.get("height") / 2))
};
}
});
/***************************/
/***** STEP 2 out of 4 *****/
/***************************/
_.each(objInvs, function(obj) {
s = obj.get("gmnotes");
for(i6 = 0; i6 < inv.length; i6++) {
if (inv[i6].type == "main" && s.indexOf("main") == -1 && GIM.isInside(obj, inv[i6]) == true) {
inv[inv.length] = {
type: "sub",
id: obj.get("_id"),
owner: inv[i6].owner,
weight: 0,
volume: 0,
layer: obj.get("layer"),
inv_gmnotes: obj.get("gmnotes"),
left: Math.floor(obj.get("left") - Math.floor(obj.get("width") / 2)),
top: Math.floor(obj.get("top") - Math.floor(obj.get("height") / 2)),
right: Math.floor(obj.get("left") + Math.floor(obj.get("width") / 2)),
bottom: Math.floor(obj.get("top") + Math.floor(obj.get("height") / 2))
};
}
}
});
/***************************/
/***** STEP 3 out of 4 *****/
/***************************/
_.each(objItems, function(obj) {
s = obj.get("gmnotes");
// ... log it into the item array
items[items.length] = {
id: obj.get("_id"),
left: obj.get("left"),
top: obj.get("top")
};
// ... make sure the item has wgt/vol values
if(!obj.get("bar1_value")) {obj.set("bar1_value",0)}
if(!obj.get("bar2_value")) {obj.set("bar2_value",0)}
// If it's an empty potion, remove the name and tint
if (s.indexOf("liquid") !== -1 && obj.get("bar1_value") == 0) {
obj.set("name","");
obj.set("tint_color","transparent");
}
// Settings for newly added items
if(GIM.isNewGraphic == true) {
obj.set("showplayers_name",true);
obj.set("showname",true);
obj.set("playersedit_bar2",false);
obj.set("playersedit_bar3",false);
obj.set("showplayers_bar3",false);
obj.set("showplayers_bar2",false);
if (s.indexOf("liquid") !== -1) {
obj.set("playersedit_bar1",true);
obj.set("showplayers_bar1",true);
} else {
obj.set("playersedit_bar1",false);
obj.set("showplayers_bar1",false);
}
}
// For each inventory ...
for(i7 = 0; i7 < inv.length; i7++) {
var q = inv[i7].owner - 1, r = inv[i7].inv_gmnotes;
// ... that contains this item object ...
if (GIM.isInside(obj, inv[i7]) == true && r.indexOf("ignore") == -1) {
// ... do these things:
// If the item's containing inventory is hidden, hide the item ...
if(inv[i7].layer == "gmlayer" && obj.get("layer") != "gmlayer") {
obj.set("layer","gmlayer")
// ... or unhide it otherwise.
} else if (inv[i7].type == "sub" && inv[i7].inv_gmnotes != "drop" && inv[i7].layer == "objects" && obj.get("layer") != "objects" || inv[i7].layer == "map") {
obj.set("layer","objects");
}
// If item is on a drop inventory, put it on the map layer
if(inv[i7].inv_gmnotes == "drop") {
obj.set("layer","map");
}
// If item is not stowable, prevent stowing as such
if (r.indexOf("inv") !== -1 && r.indexOf("worn") == -1 && s.indexOf("unstowable") !== -1) {
obj.set("top",inv[i7].bottom + 35);
} else {
// This will exclude items that are hidden or dropped.
if(obj.get("layer") == "objects") {
// If the item is a pile of coins ...
if(s.indexOf("coins") !== -1) {
// ... adjust the weight and volume ...
obj.set("bar1_value",Math.round((parseFloat(obj.get("bar3_value") * GIM.coin.wgt) * 1000) / 1000));
obj.set("bar2_value",Math.round((parseFloat(obj.get("bar3_value") * GIM.coin.vol) * 1000) / 1000));
// ... and let its name show the total
if(s.indexOf("copper") !== -1) {
obj.set("name","Cp: " + obj.get("bar3_value"));
}
if(s.indexOf("silver") !== -1) {
obj.set("name","Sp: " + obj.get("bar3_value"));
}
if(s.indexOf("gold") !== -1) {
obj.set("name","Gp: " + obj.get("bar3_value"));
}
if(s.indexOf("electrum") !== -1) {
obj.set("name","Ep: " + obj.get("bar3_value"));
}
if(s.indexOf("platinum") !== -1) {
obj.set("name","Pp: " + obj.get("bar3_value"));
}
}
// If the item has a non-changing base weight (empty bottles) ...
if (s.indexOf("weight") !== -1) {
var weight_array = s.split("%20"), weight_loc = weight_array.indexOf("weight") + 1;
// ... add the weight to its containing inventories
inv[i7].weight += parseFloat(weight_array[weight_loc]);
}
// Add weights and volumes of each item to its containing inventories
inv[i7].weight += parseFloat(obj.get("bar1_value"));
inv[i7].volume += parseFloat(obj.get("bar2_value"));
// Calculate how warm a player is based on what he or she is wearing
if (inv[i7].inv_gmnotes == 'worn') {
if (s.indexOf("warmth") !== -1) {
warmth_array = s.split("%20");
warmth_loc = warmth_array.indexOf("warmth") + 1;
warmth[q] += parseInt(warmth_array[warmth_loc]);
}
// Create new inventory boxes if an inv item is equipped for the first time
for (k = 0; k < GIM.storageItems.length; k++) {
if (s.indexOf(GIM.storageItems[k].name) !== -1) {
GIM.objsToBeCreated[GIM.objsToBeCreated.length] = [
obj.get("_pageid"),
GIM.storageItems[k].name,
obj.get("left"),
obj.get("top"),
obj.get("_id"),
GIM.storageItems[k].max_wgt,
GIM.storageItems[k].max_vol,
];
obj.set("gmnotes", s.replace(GIM.storageItems[k].name,"storage_" + obj.get("_id")));
}
}
// Log any worn "storage_" items into an array.
if (s.indexOf("storage_" + obj.get("_id")) !== -1) {
equipped[q][(equipped[q]).length] = "rep_" + obj.get("_id");
}
}
// Give the proper player control of that item
if(inv[i7].type == "main") {
for(j = 0; j < GIM.playerList.length; j++) {
if (inv[i7].owner == j) {
obj.set("controlledby",GIM.playerList[j - 1])
}
}
// If the item gives hospitality, reflect as such
if (s.indexOf("hosp") !== -1) {
hosp[q]++;
}
}
}
}
}
}
});
// Calculate average hospitality
for(i8 = 0; i8 < hosp.length; i8++) {
hosp_average += hosp[i8];
}
Math.floor(hosp_average /= GIM.playerList.length);
/***************************/
/***** STEP 4 out of 4 *****/
/***************************/
_.each(objects, function(obj) {
s = obj.get("gmnotes");
stacks = 1;
for (i9 = 0; i9 < items.length; i9++) {
if (obj.get("left") == items[i9].left && obj.get("top") == items[i9].top && obj.get("_id") != items[i9].id && GIM.isObj(obj,"item") == true) {
stacks++;
}
}
if (stacks == 1) {
obj.set({
status_blue: false,
status_red: false
});
} else if (stacks < 10) {
obj.set({
status_red: false,
status_blue: stacks
});
} else if (stacks < 100) {
var tens = Math.floor(stacks / 10);
obj.set({
status_blue: stacks - (tens * 10),
status_red: tens
});
}
// Make sure "unequipped" inventories are hidden.
for (i10 = 0; i10 < inv.length; i10++) {
var q = inv[i10].owner - 1;
is_it_equipped = false;
if (inv[i10].id == obj.get("_id")) {
if (s.indexOf("rep") !== -1) {
item_owner = inv[i10].owner - 1;
for (j = 0; j < equipped[item_owner].length; j++){
if (s.indexOf(equipped[item_owner][j]) !== -1) {
is_it_equipped = true;
}
}
if (is_it_equipped == true) {
if (s.indexOf("ignore") !== -1) {
obj.set("layer","map");
} else {
obj.set("layer","objects");
}
if (GIM.extraRun == true) {
} else {
GIM.extraRun = true;
}
} else if (is_it_equipped == false) {
obj.set("layer","gmlayer");
if (GIM.extraRun == true) {
} else {
GIM.extraRun = true;
}
}
}
// Now we apply values to bar1 and bar2 of each inventory.
obj.set("bar1_value", Math.round(inv[i10].weight * 1000) / 1000);
obj.set("bar2_value", Math.round(inv[i10].volume * 1000) / 1000);
if (obj.get("bar1_value") > obj.get("bar1_max") && obj.get("bar1_max") > 0) {
obj.set({
status_dead: true
});
} else if (obj.get("bar2_value") > obj.get("bar2_max") && obj.get("bar2_max") > 0) {
obj.set({
status_dead: true
});
} else {
obj.set({
status_dead: false
});
}
}
// Set status token values
if (inv[i10].type == 'main' && obj.get("bar3_value") == inv[i10].owner) {
if (obj.get("gmnotes") == "status") {
obj.set("bar1_value", Math.round(inv[i10].weight * 10) / 10);
obj.set("bar2_value", Math.round(inv[i10].volume * 10) / 10);
obj.set("name",'Weight: ' + obj.get("bar1_value") + " | Profile: " + obj.get("bar2_value"));
} else if (obj.get("gmnotes") == "status2") {
// Temperature values are changed per user preference
if (GIM.temperatureUnit == "F") {
obj.set("bar1_value", warmth[q] * 10);
} else if (GIM.temperatureUnit == "C") {
obj.set("bar1_value", Math.floor(((warmth[q] * 10 - 32) * (5 / 9))));
}
obj.set("bar2_value", hosp[q]);
obj.set("name",'Warmth: ' + obj.get("bar1_value") + "°" + GIM.temperatureUnit + " | Hosp: " + obj.get("bar2_value") + " (" + hosp_average + ")");
}
}
}
});
GIM.runTimeout = GIM.runTimeout + 3;
};
function create_inv(g_page_id, g_name, g_left, g_top, g_id, g_wgt, g_vol) {
setTimeout(function(){
toBack(fixedCreateObj("graphic",{
name: g_name,
imgsrc: GIM.invImg,
pageid: g_page_id,
left: g_left + 350,
top: g_top,
width: 70,
height: 70,
bar1_value: 0,
bar1_max: g_wgt,
bar2_value: 0,
bar2_max: g_vol,
layer: "objects",
gmnotes: "inv rep_" + g_id
}));
},5);
}
on("ready",function(){
if (GIM.logPlayerIds == true) {
var players = filterObjs(function(obj) {
if (obj.get("type") == "player") return true;
else return false;
});
log('************* PLAYER IDs *************');
_.each(players, function(obj) {
log(obj.get("_displayname") + ": " + obj.get("_id"));
});
log('**************************************');
}
setInterval(function(){GIM.runMe()},500);
on("change:graphic:lastmove",function(obj){
GIM.runTimeout = 2;
obj.set({
statusmarkers: "!"
});
});
on("add:graphic",function(obj){
GIM.isNewGraphic = true;
obj.set("width",70);
obj.set("height",70);
});
});