|
| 1 | +/* |
| 2 | + !hunters-mark |
| 3 | + !hunters-mark add |
| 4 | + !hunters-mark delete |
| 5 | + !hunters-mark help |
| 6 | + !hunters-mark show |
| 7 | + !hunters-mark menu |
| 8 | + !hunters-mark @{selected|character_id} @{target|token_id} |
| 9 | +
|
| 10 | + add adds selected character as a new hunter. |
| 11 | + must have exactly one marker |
| 12 | + delete removes currently selected character from hunters list |
| 13 | + help shows help menu, and description of each feature |
| 14 | + show shows current hunters in state |
| 15 | + menu shows the menu of buttons |
| 16 | + |
| 17 | + if none of above |
| 18 | + assumes arg[1] is a hunter character_id, and arg[2] is target token id. |
| 19 | + if not, will send a warning and end script. |
| 20 | + |
| 21 | +*/ |
| 22 | +const HUNTERSMARK = (() => { // eslint-disable-line no-unused-vars |
| 23 | + |
| 24 | + const script_name = 'HUNTERSMARK'; |
| 25 | + const version = '0.3.0'; |
| 26 | + const lastUpdate = 1593500895369; |
| 27 | + |
| 28 | + const tokenName = token => token.get('name') ? token.get('name') : (token.get('_id') ? token.get('_id') : 'Unknown'); |
| 29 | + const findHunter = (hunter, hunted = 'hunter') => state.HUNTERSMARK.hunters.findIndex(item => item[hunted] === hunter); |
| 30 | + const getWho = who => who.split(' (GM)')[0]; |
| 31 | + const mark = '@{selected|character_id} @{target|token_id}'; |
| 32 | + const CSS = { |
| 33 | + container: 'border: 1pt solid green; background-color: white; font-size: 0.9em; border-radius: 10px;', |
| 34 | + table: '<table style="border:0;">', |
| 35 | + trow: '<tr style="border-bottom: 1px solid #ddd; border-top: 1px solid #ddd;"><td style="font-weight:bold; padding-left: 5px; padding-right: 5px;">', |
| 36 | + tmiddle: '</td><td>' , |
| 37 | + trowend: '</td></tr>', |
| 38 | + tend: '</table>', |
| 39 | + button: 'border:0; margin-left: 3px; margin-right: 3px; padding-left: 5px; padding-right: 5px; border-radius: 7px; background:green;color:white;font-weight:bold;', |
| 40 | + center: 'text-align: center;', |
| 41 | + leftpad: 'padding-left: 10px;', |
| 42 | + heading: 'text-align: center; text-decoration: underline; font-size: 16px; line-height: 24px;' |
| 43 | + }; |
| 44 | + |
| 45 | + const checkState = () => { |
| 46 | + if(!state.hasOwnProperty('HUNTERSMARK')) { |
| 47 | + state.HUNTERSMARK = { |
| 48 | + schema: 0.0, |
| 49 | + hunters: [] |
| 50 | + }; |
| 51 | + } |
| 52 | + /* hunter: { |
| 53 | + hunter: id of character, |
| 54 | + mark: tag of status marker to assign, |
| 55 | + marked: id of last character marked, or '' |
| 56 | + } |
| 57 | + */ |
| 58 | + }; |
| 59 | + |
| 60 | + const checkInstall = () => { |
| 61 | + log(`-=> ${script_name.toUpperCase()} v${version} <=- [${new Date(lastUpdate)}]`); |
| 62 | + // include state checking here |
| 63 | + checkState(); |
| 64 | + |
| 65 | + }; |
| 66 | + |
| 67 | + const handleInput = (msg) => { |
| 68 | + if (msg.type !== 'api' || !/!hunters-mark\b/.test(msg.content.toLowerCase())) { |
| 69 | + return; |
| 70 | + } |
| 71 | + |
| 72 | + const args = msg.content.split(/\s/); |
| 73 | + const command = args[1] || ''; |
| 74 | + |
| 75 | + if(!command || command.toLowerCase() === 'help') { |
| 76 | + showHelp(getWho(msg.who)); |
| 77 | + } else if(command.toLowerCase() === 'add') { |
| 78 | + hunter(msg, 1); |
| 79 | + } else if(command.toLowerCase() === 'delete') { |
| 80 | + hunter(msg, -1); |
| 81 | + } else if(command.toLowerCase() === 'show') { |
| 82 | + showState(getWho(msg.who)); |
| 83 | + } else if(command.toLowerCase() === 'menu') { |
| 84 | + showMenu(getWho(msg.who)); |
| 85 | + /*} else if(command.toLowerCase() === 'mark') { |
| 86 | + sendChat('player|' + getWho(msg.who),`!hunters-mark ${mark}`);*/ |
| 87 | + } else { |
| 88 | + if(msg.selected > 1) { |
| 89 | + sendChat(script_name,`/w "${getWho(msg.who)}" You must have only one token selected.`); |
| 90 | + return; |
| 91 | + } |
| 92 | + tokenMarker(args[1], args[2], getWho(msg.who)); |
| 93 | + } |
| 94 | + }; |
| 95 | + |
| 96 | + const showHelp = who => { |
| 97 | + const help = { |
| 98 | + show: 'This shows the current list of hunters, and their marks.', |
| 99 | + add: 'To add a new hunter, select a token representing the character and apply the status marker you want to use as their mark. Then click Add.', |
| 100 | + 'delete': 'To remove a character from the list of hunters, select a token representing them and click Delete.', |
| 101 | + help: 'Show this description.', |
| 102 | + menu: "Show a set of buttons to activate the script's features.", |
| 103 | + 'mark a target': `<p>To mark a target, use <code>!hunters-mark [character id of hunter] [token id of target]. </code></p><p>A good way to do this is <code>!hunters-mark ${mark}</code></p>` |
| 104 | + }; |
| 105 | + let output = `<div style="${CSS.container}"><h3 style="${CSS.heading}">Hunter's Mark Instructions</h3><p3>Use <code>!hunters-mark</code> followed by one of the commands below.</p>${CSS.table}`; |
| 106 | + Object.entries(help).forEach(([key, value]) => { |
| 107 | + output += `${CSS.trow}${key}${CSS.tmiddle}${value}${CSS.trowend}`; |
| 108 | + }); |
| 109 | + output += CSS.tend + '</div>'; |
| 110 | + sendChat(script_name, `/w "${who}" ${output}`); |
| 111 | + showMenu(who); |
| 112 | + }; |
| 113 | + |
| 114 | + const showMenu = (who) => { |
| 115 | + const buttons = { |
| 116 | + Show: 'show', |
| 117 | + Add: 'add', |
| 118 | + 'Delete': 'delete', |
| 119 | + Help: 'help' |
| 120 | + |
| 121 | + }; |
| 122 | + const output = `<div style="${CSS.container}"><h3 style="${CSS.heading}">Hunters Mark Menu</h3>` + |
| 123 | + `<p style="${CSS.center}">${makeButton('Mark / Unmark Target', mark, 'width: 192px; font-size: 1.1em; text-align:center;')}</p>` |
| 124 | + + `<p style="${CSS.center}">${Object.entries(buttons).reduce((list, [key, value]) => list + makeButton(key, value), '')}</p></div>`; |
| 125 | + sendChat(script_name, `/w "${who}" ${output}`); |
| 126 | + }; |
| 127 | + |
| 128 | + const makeButton = (label, button, width='') => { |
| 129 | + return `<a style="${CSS.button}${width}" href="!hunters-mark ${button}">${label}</a>`; |
| 130 | + }; |
| 131 | + |
| 132 | + const showState = (who) => { |
| 133 | + const tokenMarkers = JSON.parse(Campaign().get('token_markers')); |
| 134 | + const getIcon = tag => tokenMarkers.find(item => tag === item.tag).url; |
| 135 | + const hunters = state.HUNTERSMARK.hunters.map(hunter => `<tr><td style="${CSS.leftpad}"><img src="${getIcon(hunter.mark)}"></td><td style="${CSS.leftpad}"><p>**${getObj('character', hunter.hunter).get('name')}**${hunter.marked ? ` </p><p>Marked: ${getObj('graphic',hunter.marked).get('name')}` : ''}</p></td></tr>`); |
| 136 | + sendChat(script_name, `/w "${who}" <div style="${CSS.container}"><h3 style="${CSS.heading}">Hunter Details</h3><table>${hunters.join('')}</table> </div>`); |
| 137 | + }; |
| 138 | + |
| 139 | + const hunter = (msg, addordelete) => { |
| 140 | + if (!msg.selected) { |
| 141 | + sendChat(script_name,`/w "${getWho(msg.who)}" You need to select at least one character's token, and each must have a single status marker assigned.`); |
| 142 | + return; |
| 143 | + } |
| 144 | + let showstate = false; |
| 145 | + let excluded = []; |
| 146 | + (msg.selected||[]).forEach((obj) => { |
| 147 | + let token = getObj('graphic', obj._id); |
| 148 | + if (token) { |
| 149 | + let character = getObj('character', token.get('represents')); |
| 150 | + if (character) { |
| 151 | + if(addordelete === -1) { |
| 152 | + // delete selected characters from state |
| 153 | + const found = findHunter(character.get('_id')); |
| 154 | + if(found === -1) { |
| 155 | + excluded.push(tokenName(token)); |
| 156 | + } else { |
| 157 | + state.HUNTERSMARK.hunters.splice(found, 1); |
| 158 | + } |
| 159 | + } else if (addordelete === 1) { |
| 160 | + // only need to check marker if adding. |
| 161 | + const marker = token.get('statusmarkers').split(','); |
| 162 | + if(marker.length === 0 || marker.length > 1 || marker[0] === '') { |
| 163 | + excluded.push(tokenName(token)); |
| 164 | + } else { |
| 165 | + const newHunter = { |
| 166 | + hunter: character.get('_id'), |
| 167 | + marked: '', |
| 168 | + mark: marker[0] |
| 169 | + }; |
| 170 | + const found = findHunter(newHunter.hunter); |
| 171 | + if(found === -1) { |
| 172 | + state.HUNTERSMARK.hunters.push(newHunter); |
| 173 | + } else { |
| 174 | + state.HUNTERSMARK.hunters.splice(found, 1, newHunter); |
| 175 | + } |
| 176 | + } |
| 177 | + } |
| 178 | + showstate = true; |
| 179 | + // report characters in state. |
| 180 | + } else { |
| 181 | + excluded.push(tokenName(token)); |
| 182 | + } |
| 183 | + } |
| 184 | + |
| 185 | + }); |
| 186 | + |
| 187 | + if(showstate) { |
| 188 | + showState(getWho(msg.who)); |
| 189 | + } |
| 190 | + if(excluded.length > 0) { |
| 191 | + sendChat(script_name, `/w "${getWho(msg.who)}" The following tokens were either missing elements or had too many markers, and were not updated: ${excluded.join(', ')}.`); |
| 192 | + } |
| 193 | + }; |
| 194 | + |
| 195 | + const tokenMarker = (hunter_id, target_id, who) => { |
| 196 | + const hunter_index = findHunter(hunter_id); |
| 197 | + if(hunter_index === -1) { |
| 198 | + sendChat(script_name, `/w "${who}" Hunter is not found. Check they are set up properly.`); |
| 199 | + return; |
| 200 | + } |
| 201 | + const token = getObj('graphic', target_id); |
| 202 | + if(!token) { |
| 203 | + sendChat(script_name, `/w "${who}" Target token is not a valid target.`); |
| 204 | + return; |
| 205 | + } |
| 206 | + /* here starts the actual work of the script */ |
| 207 | + if(target_id == state.HUNTERSMARK.hunters[hunter_index].marked) { |
| 208 | + // the target token matches the id stored in owner. |
| 209 | + // This character is already marked, so unmark him and clear mark_id |
| 210 | + state.HUNTERSMARK.hunters[hunter_index].marked = ''; |
| 211 | + changeMarker(target_id, state.HUNTERSMARK.hunters[hunter_index].mark, 'remove'); |
| 212 | + } else { |
| 213 | + // marking a new target so: |
| 214 | + // get old mark, and remove mark from previous character |
| 215 | + // update mark_id and add marker |
| 216 | + const oldmark = state.HUNTERSMARK.hunters[hunter_index].marked; |
| 217 | + if(oldmark !== '') { |
| 218 | + // find old character, remove mark from them, then: |
| 219 | + changeMarker(oldmark, state.HUNTERSMARK.hunters[hunter_index].mark, 'remove'); |
| 220 | + } |
| 221 | + state.HUNTERSMARK.hunters[hunter_index].marked = target_id; |
| 222 | + changeMarker(target_id, state.HUNTERSMARK.hunters[hunter_index].mark, 'add'); |
| 223 | + |
| 224 | + } |
| 225 | + }; |
| 226 | + |
| 227 | + const changeMarker = (tid, marker, addorremove = 'add') => { |
| 228 | + const token = getObj('graphic', tid); |
| 229 | + if(token) { |
| 230 | + let tokenMarkers = token.get('statusmarkers').split(','); |
| 231 | + if(addorremove === 'add') { |
| 232 | + if(!tokenMarkers.includes(marker)) { |
| 233 | + tokenMarkers.push(marker); |
| 234 | + } |
| 235 | + } else if(addorremove === 'remove') { |
| 236 | + tokenMarkers = tokenMarkers.filter(item => item !== marker); |
| 237 | + } else { |
| 238 | + return; |
| 239 | + } |
| 240 | + token.set('statusmarkers', tokenMarkers.join(',')); |
| 241 | + } |
| 242 | + }; |
| 243 | + |
| 244 | + const registerEventHandlers = () => { |
| 245 | + on('chat:message', handleInput); |
| 246 | + }; |
| 247 | + |
| 248 | + on('ready', () => { |
| 249 | + checkInstall(); |
| 250 | + registerEventHandlers(); |
| 251 | + }); |
| 252 | + |
| 253 | +})(); |
0 commit comments