Skip to content

Commit 103f1ed

Browse files
committed
Hunters Mark
updated the version folder
1 parent e08ed90 commit 103f1ed

File tree

1 file changed

+253
-0
lines changed

1 file changed

+253
-0
lines changed

HuntersMark/0.4.0/huntersmark.js

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
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

Comments
 (0)