· 8 years ago · Apr 09, 2018, 08:06 AM
1/**
2 * Inventory Tracker for 5e OGL character sheet
3 *
4 * Allows for quick access to the equipped items of a character.
5 *
6 * Commands:
7 * !equipShow [--item|<ITEM_NAME> [--charid|<CHARACTER_ID>]]
8 * Shows the equipped item(s) for the character that belongs to the selected token.
9 * If no arguments are given, displays all equipped items of the character representing the selected token.
10 *
11 * If --item|<ITEM_NAME> is given, will display the equipped status for the given item.
12 * If --charid|<CHARACTER_ID> is given, will attempt to find the item on the character with the given ID.
13 *
14 * The name of the item(s) in the chat allow a player who can control that character to toggle the equipment
15 * status for that particular item utilizing the !equipToggle command.
16 *
17 * !equipToggle --charid|<CHARACTER_ID> --item|<ITEM_NAME>
18 * Toggles the equipped status for the given item and outputs the new status. Clicking on the item name in the
19 * output will toggle the equipment status again. Only players who can control the character can use this command.
20 *
21 * !equipPass --item|<ITEM_NAME> --recipientName|<TARGET_CHARACTER_NAME> [--quantity|<QUANTITY>]
22 * !equipPass --i|<ITEM_NAME> --rn|<TARGET_CHARACTER_NAME> [-q|<QUANTITY>]
23 * De-equips and passes ownership of an item to the specified target character.
24 * If QUANTITY is not specified, it assumes 1.
25 */
26
27var InventoryTracker5eOGL = InventoryTracker5eOGL ||(function() {
28 'use strict';
29 var scriptName = '5e OGL Inventory Tracker',
30 version = '0.0.4',
31
32 ITEM = {
33 // ==============================================================================
34 // ITEM_PREFIX + ROW_ID + ATTR_SUFFIX============================================
35
36 // Prefix
37 PREFIX : 'repeating_inventory_',
38
39 // Suffixes
40 COUNT_SUFFIX : '_itemcount',
41 COUNT_INDEX : 0,
42 NAME_SUFFIX : '_itemname',
43 NAME_INDEX : 1,
44 WEIGHT_SUFFIX : '_itemweight',
45 WEIGHT_INDEX : 2,
46 EQUIPPED_SUFFIX : '_equipped',
47 EQUIPPED_INDEX : 3, // Actually determines whether the item is equipped in regards to other attributes (AC, modifiers, etc.)
48 USEASARESOURCE_SUFFIX : '_useasaresource',
49 USEASARESOURCE_INDEX : 4,
50 HASATTACK_SUFFIX : '_hasattack',
51 HASATTACK_INDEX : 5,
52 PROPERTIES_SUFFIX : '_itemproperties',
53 PROPERTIES_INDEX : 6,
54 MODIFIERS_SUFFIX : '_itemmodifiers',
55 MODIFIERS_INDEX : 7,
56 CONTENT_SUFFIX : '_itemcontent',
57 CONTENTS_INDEX : 8,
58 ATTACKID_SUFFIX : '_itemattackid',
59 ATTACKID_INDEX : 9,
60 RESOURCEID_SUFFIX : '_itemresourceid',
61 RESOURCEID_INDEX : 10,
62 INVENTORYSUBFLAG_SUFFIX: '_inventorysubflag',
63 INVENTORYSUBFLAG_INDEX: 11,
64
65 // These have to be the string equivalent, otherwise the sheet worker will not pick up the change
66 CHECKED : '1',
67 UNCHECKED : '0',
68
69 // ==============================================================================
70 // Generatel Utility ============================================================
71
72 // Find the name from a given charId and rowId
73 findNameForCharacterAndRowId : function(charId, rowId) {
74 log(`findNameForCharacterAndRowId{${charId}, ${rowId})`);
75 var nameAttr = this.findForCharacterAndRowIdAndSuffix(charId, rowId, this.NAME_SUFFIX);
76 return nameAttr ? nameAttr.get('current') : '';
77 },
78
79 // Find attribute object for a character at a row id with a given suffix. If none exists, create a new one and return it.
80 findForCharacterAndRowIdAndSuffix : function(charId, rowId, suffix) {
81 log(`findForCharacterAndRowIdAndSuffix{${charId}, ${rowId}, ${suffix})`);
82 var existing = findObjs({
83 _type: 'attribute',
84 characterid: charId,
85 name: this.PREFIX + rowId + suffix
86 })[0];
87
88 return existing ? existing : createObj('attribute', {
89 characterid: charId,
90 name: this.PREFIX + rowId + suffix,
91 current: ''
92 });
93 },
94
95 // Hunt for the _itemname entry and return its row.
96 getRowIdFromAttribute : function(attrName) {
97 log(`getRowIdFromAttribute{${attrName})`);
98 var regex = new RegExp(this.PREFIX + '(.+?)(?:' + this.NAME_SUFFIX + '|' + this.EQUIPPED_SUFFIX + ')');
99 return regex.exec(attrName) ? regex.exec(attrName)[1] : '';
100 },
101
102 // Return all attribute objects for an inventory rowId
103 getAllFromCharAndRowId : function(charId, rowId) {
104 return [
105 this.findForCharacterAndRowIdAndSuffix(charId, rowId, this.COUNT_SUFFIX),
106 this.findForCharacterAndRowIdAndSuffix(charId, rowId, this.NAME_SUFFIX),
107 this.findForCharacterAndRowIdAndSuffix(charId, rowId, this.WEIGHT_SUFFIX),
108 this.findForCharacterAndRowIdAndSuffix(charId, rowId, this.EQUIPPED_SUFFIX),
109 this.findForCharacterAndRowIdAndSuffix(charId, rowId, this.USEASARESOURCE_SUFFIX),
110 this.findForCharacterAndRowIdAndSuffix(charId, rowId, this.HASATTACK_SUFFIX),
111 this.findForCharacterAndRowIdAndSuffix(charId, rowId, this.PROPERTIES_SUFFIX),
112 this.findForCharacterAndRowIdAndSuffix(charId, rowId, this.MODIFIERS_SUFFIX),
113 this.findForCharacterAndRowIdAndSuffix(charId, rowId, this.CONTENT_SUFFIX),
114 this.findForCharacterAndRowIdAndSuffix(charId, rowId, this.ATTACKID_SUFFIX),
115 this.findForCharacterAndRowIdAndSuffix(charId, rowId, this.RESOURCEID_SUFFIX),
116 this.findForCharacterAndRowIdAndSuffix(charId, rowId, this.INVENTORYSUBFLAG_SUFFIX)
117 ];
118 },
119
120 // ==============================================================================
121 // IsEquipped ===================================================================
122
123 isEquipped : function(equippedAttr) {
124 // if the current status is blank, it means that the equipped status of the item has not been updated
125 // since being added to the inventory.
126 log(`isEquipped{${equippedAttr})`);
127 return equippedAttr && (equippedAttr.get('current') === this.CHECKED || equippedAttr.get('current') === '');
128 },
129
130 toggleEquip : function(charId, rowId) {
131 log(`toggleEquip{${charId}, ${rowId})`);
132 var equipped = this.findEquippedForCharacterAndRowId(charId, rowId);
133 equipped.setWithWorker({current: this.isEquipped(equipped) ? this.UNCHECKED : this.CHECKED});
134 return this.getEquippedStatusForAttr(equipped);
135 },
136
137 getEquippedStatusForAttr : function(equipped) {
138 return this.isEquipped(equipped) ? 'equipped' : 'unequipped';
139 },
140
141 findEquippedForCharacterAndRowId : function(charId, rowId) {
142 log(`findEquippedForCharacterAndRowId{${charId}, ${rowId})`);
143 return this.findForCharacterAndRowIdAndSuffix(charId, rowId, this.EQUIPPED_SUFFIX);
144 },
145
146 getEquippedStatusForCharacterAndRow : function(charId, rowId) {
147 log(`getEquippedStatusForCharacterAndRow{${charId}, ${rowId})`);
148 return this.getEquippedStatusForAttr(this.findEquippedForCharacterAndRowId(charId, rowId));
149 },
150
151 // ==============================================================================
152 // Clean for Deletion ===========================================================
153
154 cleanForDeletion : function(equipAttr, attackAttr, resourceAttr, countAttr) {
155 equipAttr.setWithWorker({current: this.UNCHECKED});
156 attackAttr.setWithWorker({current: this.UNCHECKED});
157 resourceAttr.setWithWorker({current: this.UNCHECKED});
158 countAttr.setWithWorker({current: '0'});
159 },
160
161 copyAttrToNewPlayer : function(existingAttr, recipientCharId, newRowId, suffix, overrideMod = null){
162 let newAttrName = this.PREFIX + newRowId + suffix;
163 let current = existingAttr ? existingAttr.get('current') : '';
164 if(overrideMod != null){
165 current = overrideMod + '';
166 }
167 createObj('attribute', {
168 characterid: recipientCharId,
169 name: newAttrName,
170 current: current,
171 max: existingAttr ? existingAttr.get('max') : ''
172 });
173 },
174
175 // Returns count actually transferred
176 transferItemToRecipient : function(rowId, senderCharId, recipientCharId, itemName, quantity, clone) {
177 log(`transferItemToRecipient(rowId: ${rowId}, senderCharId: ${senderCharId}, recipientCharId: ${recipientCharId}, itemName: ${itemName})`);
178 var allItemAttributes = this.getAllFromCharAndRowId(senderCharId, rowId);
179
180 let newRowId = this.generateRowID();
181
182 // Even if the user was not deliberately trying to clone the item, if they are selecting a quantity that
183 // leaves some left over, mark clone = true.
184 let existingCount = allItemAttributes[this.COUNT_INDEX] ? allItemAttributes[this.COUNT_INDEX].get('current') : '0';
185 existingCount = existingCount || 1;
186 log("Existing Count: " + existingCount);
187 let newCount = parseInt(existingCount) - parseInt(quantity);
188 // If the newCount is negative, the user attempted to send more than they had. Set quantity to the existingCount.
189 if(newCount < 0){
190 newCount = newCount < 0 ? 0 : newCount;
191 quantity = existingCount;
192 }
193 log("New Count on Sender: " + newCount);
194 clone = newCount > 0 ? true : clone;
195 if(clone) {
196 allItemAttributes[this.COUNT_INDEX].setWithWorker({current: newCount + ''});
197 }
198 else {
199 this.cleanForDeletion(allItemAttributes[this.EQUIPPED_INDEX],
200 allItemAttributes[this.HASATTACK_INDEX],
201 allItemAttributes[this.USEASARESOURCE_INDEX],
202 allItemAttributes[this.COUNT_INDEX]);
203 }
204
205 // Check to see if the recipient already has an item by that name. If so, just stack it.
206 let existingRecipientItem = findRowIdForCharacterAndItemName(recipientCharId, itemName);
207 if(existingRecipientItem != null) {
208 log("Recipient has item. Stack items.");
209 var recipientRowId = findRowIdForCharacterAndItemName(recipientCharId, itemName);
210 var recipientCountAttr = this.findForCharacterAndRowIdAndSuffix(recipientCharId, recipientRowId, this.COUNT_SUFFIX);
211 let existingRecipientCount = recipientCountAttr ? recipientCountAttr.get('current') : '0';
212 let newRecipientCount = parseInt(existingRecipientCount) + parseInt(quantity);
213 recipientCountAttr.setWithWorker({current: newRecipientCount + ''});
214 }
215 else {
216 // Create a new item in the recipient's inventory
217 log("Recipient does not have existing item. Create new stack.");
218 this.copyAttrToNewPlayer(allItemAttributes[this.COUNT_INDEX], recipientCharId, newRowId, this.COUNT_SUFFIX, quantity);
219 this.copyAttrToNewPlayer(allItemAttributes[this.NAME_INDEX], recipientCharId, newRowId, this.NAME_SUFFIX);
220 this.copyAttrToNewPlayer(allItemAttributes[this.WEIGHT_INDEX], recipientCharId, newRowId, this.WEIGHT_SUFFIX);
221 this.copyAttrToNewPlayer(allItemAttributes[this.EQUIPPED_INDEX], recipientCharId, newRowId, this.EQUIPPED_SUFFIX);
222 this.copyAttrToNewPlayer(allItemAttributes[this.USEASARESOURCE_INDEX], recipientCharId, newRowId, this.USEASARESOURCE_SUFFIX);
223 this.copyAttrToNewPlayer(allItemAttributes[this.HASATTACK_INDEX], recipientCharId, newRowId, this.HASATTACK_SUFFIX);
224 this.copyAttrToNewPlayer(allItemAttributes[this.PROPERTIES_INDEX], recipientCharId, newRowId, this.PROPERTIES_SUFFIX);
225 this.copyAttrToNewPlayer(allItemAttributes[this.MODIFIERS_INDEX], recipientCharId, newRowId, this.MODIFIERS_SUFFIX);
226 this.copyAttrToNewPlayer(allItemAttributes[this.CONTENTS_INDEX], recipientCharId, newRowId, this.CONTENT_SUFFIX);
227 this.copyAttrToNewPlayer(allItemAttributes[this.ATTACKID_INDEX], recipientCharId, newRowId, this.ATTACKID_SUFFIX);
228 this.copyAttrToNewPlayer(allItemAttributes[this.RESOURCEID_INDEX], recipientCharId, newRowId, this.RESOURCEID_SUFFIX);
229 this.copyAttrToNewPlayer(allItemAttributes[this.INVENTORYSUBFLAG_INDEX], recipientCharId, newRowId, this.INVENTORYSUBFLAG_SUFFIX);
230 }
231
232 // If the sender has expended all stacks, remove all attributes
233 if(newCount == 0) {
234 allItemAttributes.forEach(function(element){element.remove();});
235 }
236
237 return parseInt(quantity);
238 },
239
240 // ==============================================================================
241 // These functions based on ChatSetAttr's row generation ========================
242
243 generateUUID : function() {
244 var a = 0;
245 var b = [];
246 return function () {
247 var c = (new Date()).getTime() + 0,
248 d = c === a;
249 a = c;
250 for (var e = new Array(8), f = 7; 0 <= f; f--) {
251 e[f] = "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz".charAt(c % 64);
252 c = Math.floor(c / 64);
253 }
254 c = e.join("");
255 if (d) {
256 for (f = 11; 0 <= f && 63 === b[f]; f--) {
257 b[f] = 0;
258 }
259 b[f]++;
260 }
261 else {
262 for (f = 0; 12 > f; f++) {
263 b[f] = Math.floor(64 * Math.random());
264 }
265 }
266 for (f = 0; 12 > f; f++) {
267 c += "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz".charAt(b[f]);
268 }
269 return c;
270 };
271 },
272
273 generateRowID : function () {
274 return this.generateUUID()().replace(/_/g, "Z");
275 }
276 },
277
278 playerCanControlCharacter = function(charId, playerId) {
279 var character = getObj('character', charId);
280 var ctrl = character ? character.get('controlledby') : '';
281 return playerIsGM(playerId) || ctrl.indexOf('all') !== -1 || ctrl.indexOf(playerId) !== -1;
282 },
283
284 findCharacterIdForToken = function(tokenId) {
285 log(`findCharacterIdForToken{${tokenId})`);
286 var token = getObj('graphic', tokenId);
287 return token.get('represents');
288 },
289
290 filterItemAttrsForCharacterAndSuffixAndValue = function(charId, suffix, value){
291 log(`filterItemAttrsForCharacterAndSuffixAndValue{${charId}, ${suffix}, ${value})`);
292 return filterObjs(function(obj){
293 if(obj.get('type') === 'attribute'
294 && obj.get('characterid') === charId
295 && obj.get('name').indexOf(ITEM.PREFIX) !== -1 && obj.get('name').indexOf(suffix) !== -1
296 && obj.get('current') === value
297 ) {
298 log(JSON.stringify(obj));
299 return obj;
300 }
301 });
302 },
303
304 findEquippedItemsForCharacter = function(charId){
305 log(`findEquippedItemsForCharacter{${charId})`);
306 return filterItemAttrsForCharacterAndSuffixAndValue(charId, ITEM.EQUIPPED_SUFFIX, ITEM.CHECKED)
307 },
308
309 findUnequippedItemsForCharacter = function(charId){
310 log(`findUnequippedItemsForCharacter{${charId})`);
311 return filterItemAttrsForCharacterAndSuffixAndValue(charId, ITEM.EQUIPPED_SUFFIX, ITEM.UNEQUIPPED)
312 },
313
314 findRowIdForCharacterAndItemName = function(charId, itemName) {
315 log(`findRowIdForCharacterAndItemName{${charId}, ${itemName})`);
316 var nameAttr = filterItemAttrsForCharacterAndSuffixAndValue(charId, ITEM.NAME_SUFFIX, itemName)[0];
317 return nameAttr ? ITEM.getRowIdFromAttribute(nameAttr.get('name')) : null;
318 },
319
320 STYLES = {
321 DIV : 'style="width: 189px; border: 1px solid black; background-color: #ffffff; padding: 5px;"',
322 HEAD : 'style="color: rgb(126, 45, 64); font-size: 18px; text-align: left; font-variant: small-caps; font-family: Times, serif;"',
323 SUBHEAD : 'style="font-size: 11px; line-height: 13px; margin-top: -3px; font-style: italic;"',
324 ARROW : 'style="border: none; border-top: 3px solid transparent; border-bottom: 3px solid transparent; border-left: 195px solid rgb(126, 45, 64); margin-bottom: 2px; margin-top: 2px;"',
325 ASTYLE : 'style="font-size: 12px; color: black; border: 0; margin 0px; background: none; padding: 0;"'
326 },
327
328 outputEquipmentStatusForCharacter = function(charId, sender) {
329 log(`outputEquippedItemsForCharacter{${charId}, ${sender})`);
330 var character = getObj('character', charId);
331 if(!character){
332 var errorMessage = charId + ' is not a valid character id.';
333 log(errorMessage);
334 sendChat(scriptName, '/w ' + sender + ' ' + errorMessage);
335 return;
336 }
337
338 var tableRowsStr;
339 var content;
340
341 // Equipped Items
342 tableRowsStr = '';
343 var equippedItems = findEquippedItemsForCharacter(charId);
344 _.each(equippedItems, function(equipAttr){
345 var rowId = ITEM.getRowIdFromAttribute(equipAttr.get('name'));
346 var name = ITEM.findNameForCharacterAndRowId(equipAttr.get('characterid'), rowId);
347 log(`Row[${rowId}] | Name: ${name}`);
348 if(name) {
349 tableRowsStr = tableRowsStr +
350 '<tr><td>'+ getToggleHyperlink(charId, rowId, name) + '</td></tr>';
351 } else {
352 log('Could not find the name of item with character ID [' + charId + '] and row ID [' + rowId + ']');
353 }
354 });
355
356 if(tableRowsStr) {
357 content = '<table>' + tableRowsStr + '</table>';
358 } else {
359 content = '<div>None</div>';
360 }
361 sendChat(scriptName,
362 '/w ' + sender + ' ' +
363 '<div ' + STYLES.DIV + '>' +
364 '<div ' + STYLES.HEAD + '>' + character.get('name') + '</div>' +
365 '<div ' + STYLES.SUBHEAD + '>Equipped Items</div>' +
366 '<div ' + STYLES.ARROW + '></div>' +
367 content + '</div>'
368 );
369
370 // Unequipped Items
371 tableRowsStr = '';
372 var unequippedItems = findUnequippedItemsForCharacter(charId);
373 _.each(unequippedItems, function(equipAttr){
374 var rowId = ITEM.getRowIdFromAttribute(equipAttr.get('name'));
375 var name = ITEM.findNameForCharacterAndRowId(equipAttr.get('characterid'), rowId);
376 log(`Row[${rowId}] | Name: ${name}`);
377 if(name) {
378 tableRowsStr = tableRowsStr +
379 '<tr><td>'+ getToggleHyperlink(charId, rowId, name) + '</td></tr>';
380 } else {
381 log('Could not find the name of item with character ID [' + charId + '] and row ID [' + rowId + ']');
382 }
383 });
384
385 if(tableRowsStr) {
386 content = '<table>' + tableRowsStr + '</table>';
387 } else {
388 content = '<div>None</div>';
389 }
390 sendChat(scriptName,
391 '/w ' + sender + ' ' +
392 '<div ' + STYLES.DIV + '>' +
393 '<div ' + STYLES.HEAD + '>' + character.get('name') + '</div>' +
394 '<div ' + STYLES.SUBHEAD + '>Unequipped Items</div>' +
395 '<div ' + STYLES.ARROW + '></div>' +
396 content + '</div>'
397 );
398 },
399
400 outputSingleItemForCharacter = function(charId, itemName, sender) {
401 var rowId = findRowIdForCharacterAndItemName(charId, itemName);
402 if(!rowId) {
403 sendChat(scriptName, '/w ' + sender + ' No item with name \'' + itemName + '\' found for the character.');
404 return;
405 }
406 var equippedState = ITEM.getEquippedStatusForCharacterAndRow(charId, rowId);
407 sendChat(scriptName,
408 '/w ' + sender + ' ' +
409 '<div ' + STYLES.DIV + '>' +
410 '<div ' + STYLES.HEAD + '>' + getObj('character', charId).get('name') + '</div>' +
411 '<div ' + STYLES.ARROW + '></div>' +
412 '<div>' + getToggleHyperlink(charId, rowId, itemName) + ' -> ' + equippedState + '</div>' +
413 '</div>'
414 );
415 },
416
417 getToggleHyperlink = function(charId, rowId, itemName) {
418 return '<a ' + STYLES.ASTYLE + ' href="!equipToggle ' +
419 '--charid|' + charId + ' ' +
420 '--item|' + itemName +
421 '">' + itemName + '</a>';
422 },
423
424 toggleEquipment = function(charId, itemName, sender) {
425 var rowId = findRowIdForCharacterAndItemName(charId, itemName);
426 if(!rowId) {
427 sendChat(scriptName, '/w ' + sender + ' No item with name \'' + itemName + '\' found for the character.');
428 return;
429 }
430 var equippedState = ITEM.toggleEquip(charId, rowId);
431 sendChat(scriptName,
432 '/w ' + sender + ' ' +
433 '<div ' + STYLES.DIV + '>' +
434 '<div ' + STYLES.HEAD + '>' + getObj('character', charId).get('name') + '</div>' +
435 '<div ' + STYLES.ARROW + '></div>' +
436 '<div>Has ' + equippedState + ' ' + getToggleHyperlink(charId, rowId, itemName) + '.</div>' +
437 '</div>'
438 );
439 },
440
441 passEquipment = function(senderCharId, recipientName, recipientCharId, itemName, sender, quantity, clone) {
442 var rowId = findRowIdForCharacterAndItemName(senderCharId, itemName);
443 if(!rowId) {
444 sendChat(scriptName, '/w ' + sender + ' No item with name \'' + itemName + '\' found for the character.');
445 return;
446 }
447 let countTransferred = ITEM.transferItemToRecipient(rowId, senderCharId, recipientCharId, itemName, quantity, clone);
448 let plural = countTransferred > 1 ? 's' : '';
449
450 if(!recipientName){
451 return;
452 }
453 sendChat(scriptName,
454 '/w gm ' +
455 '<div ' + STYLES.DIV + '>' +
456 '<div ' + STYLES.HEAD + '>' + itemName + '</div>' +
457 '<div ' + STYLES.ARROW + '></div>' +
458 '<div>Transferred ' + countTransferred + 'x item' + plural + ' from ' + sender + ' to ' + recipientName + '.</div>' +
459 '</div>'
460 );
461 sendChat(scriptName,
462 '/w ' + sender + ' ' +
463 '<div ' + STYLES.DIV + '>' +
464 '<div ' + STYLES.HEAD + '>' + itemName + '</div>' +
465 '<div ' + STYLES.ARROW + '></div>' +
466 '<div>Transferred ' + countTransferred + 'x item' + plural + ' to ' + recipientName + '.</div>' +
467 '</div>'
468 );
469 sendChat(scriptName,
470 '/w ' + recipientName + ' ' +
471 '<div ' + STYLES.DIV + '>' +
472 '<div ' + STYLES.HEAD + '>' + itemName + '</div>' +
473 '<div ' + STYLES.ARROW + '></div>' +
474 '<div>' + sender + ' transferred ' + countTransferred + 'x item' + plural + ' to you.</div>' +
475 '</div>'
476 );
477 },
478
479 handleInput = function(msg) {
480 var sender = msg.who;
481 var charId, itemName, recipientId, recipientName;
482 let quantity = 1;
483 let clone = false;
484
485 // Assert for single-character commands
486 var parseCommandsAndAssertValidCharacter = function() {
487 _.each(msg.content.split('--'), function(str){
488 var split = str.split('|');
489 switch(split[0].toLowerCase()) {
490 case 'charid':
491 case 'ci': charId = split[1].trim(); break;
492 case 'item':
493 case 'i' : itemName = split[1].trim(); break;
494 }
495 });
496
497 // We need a valid character id to proceed
498 if(!charId && msg.selected) {
499 charId = findCharacterIdForToken(msg.selected[0]._id);
500 } else if (!charId && !msg.selected){
501 sendChat(scriptName, '/w ' + sender + ' No character ID found. Either selected the token or add --charid|CHARACTER_ID to the command');
502 return false;
503 }
504 return true;
505 };
506
507 // Assert for two-character commands
508 var parseCommandsAndAssertValidSenderAndReceiver = function() {
509 _.each(msg.content.split('--'), function(str){
510 var split = str.split('|');
511 switch(split[0].toLowerCase()) {
512 case 'charid':
513 case 'ci': charId = split[1].trim(); break;
514 case 'item':
515 case 'i' : itemName = split[1].trim(); break;
516 case 'recipientid':
517 case 'ri': recipientId = split[1].trim(); break;
518 case 'recipientname':
519 case 'rn': recipientName = split[1].trim(); break;
520 case 'quantity':
521 case 'q':
522 case 'count': quantity = split[1].trim(); break;
523 case 'clone': clone = true; break;
524 }
525 });
526
527 // Ensure quantity is a positive integer
528 quantity = parseInt(quantity) || 0;
529 if(quantity < 1) {
530 sendChat(scriptName, '/w ' + sender + '**ERROR:** invalid quantity. It must be a positive integer.');
531 return false;
532 }
533
534 // We need a valid character id for the sender in order to proceed
535 if(!charId && msg.selected) {
536 charId = findCharacterIdForToken(msg.selected[0]._id);
537 }
538 else if (!charId && !msg.selected){
539 sendChat(scriptName, '/w ' + sender + ' No character ID found. Either selected the token or add --charid|CHARACTER_ID to the command');
540 return false;
541 }
542
543 // We need a valid character id for the receiver in order to proceed
544 log("Target ID: " + recipientId + "Target Name: " + recipientName);
545 if(!recipientId && recipientName) {
546 let list = findObjs({
547 _type: "character",
548 name: recipientName
549 });
550 log(JSON.stringify(list));
551 if (list.length == 0) {
552 sendChat(scriptName, '/w ' + sender + '**ERROR:** No character exists by the name ' + recipientName + '.');
553 return false;
554 }
555 else if (list.length > 1) {
556 sendChat(scriptName, '/w ' + sender + '**ERROR:** character name ' + recipientName + ' must be unique.');
557 return false;
558 }
559 recipientId = list[0].id;
560 }
561 else if (!recipientId && !recipientName){
562 sendChat(scriptName, '/w ' + sender + ' No target ID found. Either add --recipientId|TARGET_ID or --recipientName|TARGET_NAME to the command');
563 return false;
564 }
565 return true;
566 };
567
568 if(msg.type === 'api' && msg.content.indexOf('!equipShow') !== -1) {
569 if(!parseCommandsAndAssertValidCharacter()) { return; }
570 if(msg.content.indexOf('--item|') !== -1) {
571 // set the item name and charId from the message
572 outputSingleItemForCharacter(charId, itemName, sender);
573 } else {
574 if (!charId && !msg.selected) {
575 sendChat(scriptName, '/w ' + sender + ' must have a character selected to use !equipShow');
576 return;
577 }
578 else if(charId){
579 outputEquipmentStatusForCharacter(charId, sender);
580 }
581 else{
582 _.each(msg.selected, function (sel) {
583 var charId = findCharacterIdForToken(sel._id);
584 outputEquipmentStatusForCharacter(charId, sender);
585 });
586 }
587 }
588 }
589 else if (msg.type === 'api' && msg.content.indexOf('!equipToggle') !== -1) {
590 if(!parseCommandsAndAssertValidCharacter()) { return; }
591
592 // We need the item's name to proceed
593 if(!itemName) {
594 sendChat(scriptName, '/w ' + sender + ' No item provided. Add --item|ITEM_NAME to the command');
595 return;
596 }
597
598 if(!playerCanControlCharacter(charId, msg.playerid)) {
599 sendChat(scriptName, '/w ' + sender + ' You cannot modify the equipment of a character you do not control.');
600 return;
601 }
602
603 toggleEquipment(charId, itemName, sender);
604 }
605 else if (msg.type === 'api' && msg.content.indexOf('!equipPass') !== -1) {
606 if(!parseCommandsAndAssertValidSenderAndReceiver()) { return; }
607
608 // We need the item's name to proceed
609 if(!itemName) {
610 sendChat(scriptName, '/w ' + sender + ' No item provided. Add --item|ITEM_NAME to the command');
611 return;
612 }
613
614 if(!playerCanControlCharacter(charId, msg.playerid)) {
615 sendChat(scriptName, '/w ' + sender + ' You cannot modify the equipment of a character you do not control.');
616 return;
617 }
618
619 if(charId == recipientId) {
620 sendChat(scriptName, '/w ' + sender + ' You cannot transfer an item to yourself.');
621 return;
622 }
623
624 passEquipment(charId, recipientName, recipientId, itemName, sender, quantity, clone);
625 }
626 },
627
628 checkInstall = function(){
629 log(scriptName + ' v' + version + ' -> Ready');
630 },
631
632 registerEventHandlers = function() {
633 on('chat:message', handleInput);
634 };
635
636 return {
637 CheckInstall: checkInstall,
638 RegisterEventHandlers: registerEventHandlers
639 };
640 }());
641
642on('ready', function(){
643 'use strict';
644 InventoryTracker5eOGL.CheckInstall();
645 InventoryTracker5eOGL.RegisterEventHandlers();
646});