Skip to content

Commit 0a9505d

Browse files
committed
DeathTracker: Adding Observation of TokenMod
1 parent 3d5b541 commit 0a9505d

File tree

3 files changed

+369
-5
lines changed

3 files changed

+369
-5
lines changed

DeathTracker/0.1.7/DeathTracker.js

+360
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,360 @@
1+
/*
2+
* Version 0.1.7
3+
* Made By Robin Kuiper
4+
* Minor changes by The Aaron
5+
* Skype: RobinKuiper.eu
6+
* Discord: Atheos#1095
7+
* Roll20: https://app.roll20.net/users/1226016/robin
8+
* Github: https://github.com/RobinKuiper/Roll20APIScripts
9+
* Reddit: https://www.reddit.com/user/robinkuiper/
10+
* Patreon: https://patreon.com/robinkuiper
11+
* Paypal.me: https://www.paypal.me/robinkuiper
12+
*/
13+
14+
var DeathTracker = DeathTracker || (function () {
15+
'use strict';
16+
17+
let observers = {
18+
tokenChange: []
19+
}
20+
21+
// Styling for the chat responses.
22+
const styles = {
23+
reset: 'padding: 0; margin: 0;',
24+
menu: 'background-color: #fff; border: 1px solid #000; padding: 5px; border-radius: 5px;',
25+
button: 'background-color: #000; border: 1px solid #292929; border-radius: 3px; padding: 5px; color: #fff; text-align: center;',
26+
list: 'list-style: none;',
27+
float: {
28+
right: 'float: right;',
29+
left: 'float: left;'
30+
},
31+
overflow: 'overflow: hidden;',
32+
fullWidth: 'width: 100%;',
33+
underline: 'text-decoration: underline;',
34+
strikethrough: 'text-decoration: strikethrough'
35+
},
36+
script_name = 'DeathTracker',
37+
state_name = 'DEATHTRACKER',
38+
markers = ['none', 'blue', 'brown', 'green', 'pink', 'purple', 'red', 'yellow', '-', 'all-for-one', 'angel-outfit', 'archery-target', 'arrowed', 'aura', 'back-pain', 'black-flag', 'bleeding-eye', 'bolt-shield', 'broken-heart', 'broken-shield', 'broken-skull', 'chained-heart', 'chemical-bolt', 'cobweb', 'dead', 'death-zone', 'drink-me', 'edge-crack', 'fishing-net', 'fist', 'fluffy-wing', 'flying-flag', 'frozen-orb', 'grab', 'grenade', 'half-haze', 'half-heart', 'interdiction', 'lightning-helix', 'ninja-mask', 'overdrive', 'padlock', 'pummeled', 'radioactive', 'rolling-tomb', 'screaming', 'sentry-gun', 'skull', 'sleepy', 'snail', 'spanner', 'stopwatch', 'strong', 'three-leaves', 'tread', 'trophy', 'white-tower'],
39+
40+
handleInput = (msg) => {
41+
if (msg.type != 'api' || !playerIsGM(msg.playerid)) return;
42+
43+
// Split the message into command and argument(s)
44+
let args = msg.content.split(' ');
45+
let command = args.shift().substring(1);
46+
let extracommand = args.shift();
47+
48+
if (command == state[state_name].config.command) {
49+
switch (extracommand) {
50+
case 'reset':
51+
state[state_name] = {};
52+
setDefaults(true);
53+
sendConfigMenu();
54+
break;
55+
56+
case 'config':
57+
let message;
58+
if (args.length > 0) {
59+
let setting = args.shift().split('|');
60+
let key = setting.shift();
61+
let value = (setting[0] === 'true') ? true : (setting[0] === 'false') ? false : setting[0];
62+
63+
if (key === 'death_statusmarker') {
64+
if (value !== state[state_name].config.half_statusmarker) {
65+
state[state_name].config[key] = value;
66+
} else {
67+
message = '<span style="color: red">Statusmakers can\'t be the same.</span>';
68+
}
69+
} else if (key === 'half_statusmarker') {
70+
if (value !== state[state_name].config.death_statusmarker) {
71+
state[state_name].config[key] = value;
72+
} else {
73+
message = '<span style="color: red">Statusmakers can\'t be the same.</span>';
74+
}
75+
} else {
76+
state[state_name].config[key] = value;
77+
}
78+
79+
if (key === 'bar') {
80+
//registerEventHandlers();
81+
message = '<span style="color: red">The API Sandbox needs to be restarted for this to take effect.</span>';
82+
}
83+
}
84+
85+
sendConfigMenu(false, message);
86+
break;
87+
88+
default:
89+
sendConfigMenu();
90+
break;
91+
}
92+
}
93+
},
94+
95+
handleBarValueChange = (obj, prev) => {
96+
let bar = 'bar' + state[state_name].config.bar;
97+
98+
if (!obj || !prev || !obj.get('represents') || obj.get(bar + '_value') === prev[bar + '_value']) {
99+
return;
100+
}
101+
102+
let attributes = {};
103+
104+
let deathMarker = state[state_name].config.death_statusmarker;
105+
let halfMarker = state[state_name].config.half_statusmarker;
106+
let unconsciousMarker = state[state_name].config.pc_unconscious_statusmarker;
107+
108+
let playerid = (obj.get('controlledby') && obj.get('controlledby') !== '') ? obj.get('controlledby') : (getObj('character', obj.get('represents'))) ? getObj('character', obj.get('represents')).get('controlledby') : false;
109+
let isPlayer = (playerid && !playerIsGM(playerid));
110+
111+
if (deathMarker !== 'none' && obj.get(bar + '_value') <= 0) {
112+
let marker = (unconsciousMarker !== 'none' && isPlayer) ? unconsciousMarker : deathMarker;
113+
attributes['status_' + marker] = true;
114+
attributes['status_' + halfMarker] = false;
115+
} else {
116+
attributes['status_' + deathMarker] = false;
117+
attributes['status_' + unconsciousMarker] = false;
118+
attributes['status_' + halfMarker] = (halfMarker !== 'none' && obj.get(bar + '_max') !== '' && obj.get(bar + '_value') <= obj.get(bar + '_max') / 2);
119+
}
120+
121+
if(state[state_name].config.change_tint){
122+
let color = getColor(1 - (obj.get(bar + '_value') / obj.get(bar + '_max')));
123+
attributes.tint_color = (obj.get(bar + '_max') == obj.get(bar + '_value')) ? 'transparent' : color;
124+
}
125+
126+
if(state[state_name].config.fx && parseInt(obj.get(bar + '_value')) < parseInt(prev[bar + '_value'])){
127+
let x = parseInt(obj.get('left')),
128+
y = parseInt(obj.get('top'));
129+
130+
spawnFxBetweenPoints({ x, y }, { x, y }, state[state_name].config.fx_type, obj.get('pageid'))
131+
}
132+
133+
if(state[state_name].config.heal_fx && parseInt(obj.get(bar + '_value')) > parseInt(prev[bar + '_value'])){
134+
let x = parseInt(obj.get('left')),
135+
y = parseInt(obj.get('top'));
136+
137+
spawnFxBetweenPoints({ x, y }, { x, y }, state[state_name].config.heal_fx_type, obj.get('pageid'))
138+
}
139+
140+
obj.set(attributes);
141+
notifyObservers('tokenChange', obj, prev);
142+
},
143+
144+
getColor = (value) => {
145+
return hslToHex(((1-value)*120), 75, 50);
146+
},
147+
148+
hslToHex = (h, s, l) => {
149+
h /= 360;
150+
s /= 100;
151+
l /= 100;
152+
let r, g, b;
153+
if (s === 0) {
154+
r = g = b = l; // achromatic
155+
} else {
156+
const hue2rgb = (p, q, t) => {
157+
if (t < 0) t += 1;
158+
if (t > 1) t -= 1;
159+
if (t < 1 / 6) return p + (q - p) * 6 * t;
160+
if (t < 1 / 2) return q;
161+
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
162+
return p;
163+
};
164+
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
165+
const p = 2 * l - q;
166+
r = hue2rgb(p, q, h + 1 / 3);
167+
g = hue2rgb(p, q, h);
168+
b = hue2rgb(p, q, h - 1 / 3);
169+
}
170+
const toHex = x => {
171+
const hex = Math.round(x * 255).toString(16);
172+
return hex.length === 1 ? '0' + hex : hex;
173+
};
174+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
175+
},
176+
177+
ucFirst = (string) => {
178+
return string.charAt(0).toUpperCase() + string.slice(1);
179+
},
180+
181+
sendConfigMenu = (first, message) => {
182+
let markerDropdown = '?{Marker';
183+
markers.forEach((marker) => {
184+
markerDropdown += '|' + ucFirst(marker).replace('-', ' ') + ',' + marker
185+
})
186+
markerDropdown += '}';
187+
188+
let death_markerButton = makeButton(state[state_name].config.death_statusmarker, '!' + state[state_name].config.command + ' config death_statusmarker|' + markerDropdown, styles.button + styles.float.right),
189+
half_markerButton = makeButton(state[state_name].config.half_statusmarker, '!' + state[state_name].config.command + ' config half_statusmarker|' + markerDropdown, styles.button + styles.float.right),
190+
commandButton = makeButton('!' + state[state_name].config.command, '!' + state[state_name].config.command + ' config command|?{Command (without !)}', styles.button + styles.float.right),
191+
barButton = makeButton('bar ' + state[state_name].config.bar, '!' + state[state_name].config.command + ' config bar|?{Bar|Bar 1 (green),1|Bar 2 (blue),2|Bar 3 (red),3}', styles.button + styles.float.right),
192+
pc_unconscious_markerButton = makeButton(state[state_name].config.pc_unconscious_statusmarker, '!' + state[state_name].config.command + ' config pc_unconscious_statusmarker|' + markerDropdown, styles.button + styles.float.right),
193+
change_tintButton = makeButton(state[state_name].config.change_tint, '!' + state[state_name].config.command + ' config change_tint|' + !state[state_name].config.change_tint, styles.button + styles.float.right),
194+
fxButton = makeButton(state[state_name].config.fx, '!' + state[state_name].config.command + ' config fx|' + !state[state_name].config.fx, styles.button + styles.float.right),
195+
fx_typeButton = makeButton(state[state_name].config.fx_type, '!' + state[state_name].config.command + ' config fx_type|?{FX Type|'+state[state_name].config.fx_type+'}', styles.button + styles.float.right),
196+
heal_fxButton = makeButton(state[state_name].config.heal_fx, '!' + state[state_name].config.command + ' config heal_fx|' + !state[state_name].config.heal_fx, styles.button + styles.float.right),
197+
heal_fx_typeButton = makeButton(state[state_name].config.heal_fx_type, '!' + state[state_name].config.command + ' config heal_fx_type|?{Heal FX Type|'+state[state_name].config.heal_fx_type+'}', styles.button + styles.float.right),
198+
199+
listItems = [
200+
'<span style="'+styles.float.left+'">Command:</span> ' + commandButton,
201+
'<span style="'+styles.float.left+'">HP Bar:</span> ' + barButton,
202+
'<span style="'+styles.float.left+'">Dead Statusmarker:</span> ' + death_markerButton,
203+
'<span style="'+styles.float.left+'">Uncon. Statusmarker:<div style="font-size: 8pt">Unconscious marker if PC.</div></span> ' + pc_unconscious_markerButton,
204+
'<span style="'+styles.float.left+'">Half Dead Statusmarker:</span> ' + half_markerButton,
205+
'<span style="'+styles.float.left+'">Change Tint Color:</span> ' + change_tintButton,
206+
'<span style="'+styles.float.left+'">Use FX:<div style="font-size: 8pt">When getting damage.</div></span> ' + fxButton,
207+
]
208+
209+
if(state[state_name].config.fx){
210+
listItems.push('<span style="'+styles.float.left+'">FX Type:</span> ' + fx_typeButton);
211+
}
212+
213+
listItems.push('<span style="'+styles.float.left+'">Use Heal FX:</span> ' + heal_fxButton);
214+
215+
if(state[state_name].config.heal_fx){
216+
listItems.push('<span style="'+styles.float.left+'">Heal FX Type:</span> ' + heal_fx_typeButton);
217+
}
218+
219+
let resetButton = makeButton('Reset', '!' + state[state_name].config.command + ' reset', styles.button + styles.fullWidth);
220+
221+
let title_text = (first) ? script_name + ' First Time Setup' : script_name + ' Config';
222+
message = (message) ? '<p>' + message + '</p>' : '';
223+
let contents = message + makeList(listItems, styles.reset + styles.list + styles.overflow, styles.overflow) + '<hr><p style="font-size: 80%">You can always come back to this config by typing `!' + state[state_name].config.command + ' config`.</p><hr>' + resetButton;
224+
makeAndSendMenu(contents, title_text, 'gm');
225+
},
226+
227+
makeAndSendMenu = (contents, title, whisper) => {
228+
title = (title && title != '') && makeTitle(title)
229+
whisper = (whisper && whisper !== '') && '/w ' + whisper + ' ';
230+
sendChat(script_name, whisper + '<div style="' + styles.menu + styles.overflow + '">' + title + contents + '</div>', null, {
231+
noarchive: true
232+
});
233+
},
234+
235+
makeTitle = (title) => {
236+
return '<h3 style="margin-bottom: 10px;">' + title + '</h3>';
237+
},
238+
239+
makeButton = (title, href, style) => {
240+
return '<a style="' + style + '" href="' + href + '">' + title + '</a>';
241+
},
242+
243+
makeList = (items, listStyle, itemStyle) => {
244+
let list = '<ul style="' + listStyle + '">';
245+
items.forEach((item) => {
246+
list += '<li style="' + itemStyle + '">' + item + '</li>';
247+
});
248+
list += '</ul>';
249+
return list;
250+
},
251+
252+
pre_log = (message) => {
253+
log('---------------------------------------------------------------------------------------------');
254+
log(message);
255+
log('---------------------------------------------------------------------------------------------');
256+
},
257+
258+
checkInstall = () => {
259+
if (!_.has(state, state_name)) {
260+
state[state_name] = state[state_name] || {};
261+
}
262+
setDefaults();
263+
264+
log(script_name + ' Ready! Command: !' + state[state_name].config.command);
265+
if (state[state_name].config.debug) {
266+
makeAndSendMenu(script_name + ' Ready! Debug On.', '', 'gm')
267+
}
268+
},
269+
270+
observeTokenChange = function (handler) {
271+
if (handler && _.isFunction(handler)) {
272+
observers.tokenChange.push(handler);
273+
}
274+
},
275+
276+
notifyObservers = function (event, obj, prev) {
277+
_.each(observers[event], function (handler) {
278+
handler(obj, prev);
279+
});
280+
},
281+
282+
registerEventHandlers = () => {
283+
on('chat:message', handleInput);
284+
on('change:graphic', handleBarValueChange);
285+
if('undefined' !== typeof TokenMod && TokenMod.ObserveTokenChange){
286+
TokenMod.ObserveTokenChange(handleBarValueChange);
287+
}
288+
},
289+
290+
setDefaults = (reset) => {
291+
const defaults = {
292+
config: {
293+
command: 'dead',
294+
death_statusmarker: 'dead',
295+
half_statusmarker: 'red',
296+
bar: 1,
297+
firsttime: (reset) ? false : true,
298+
pc_unconscious_statusmarker: 'sleepy',
299+
change_tint: true,
300+
fx: true,
301+
fx_type: 'splatter-blood',
302+
heal_fx: false,
303+
heal_fx_type: 'glow-holy'
304+
}
305+
};
306+
307+
if (!state[state_name].config) {
308+
state[state_name].config = defaults.config;
309+
} else {
310+
if (!state[state_name].config.hasOwnProperty('command')) {
311+
state[state_name].config.command = defaults.config.command;
312+
}
313+
if (!state[state_name].config.hasOwnProperty('death_statusmarker')) {
314+
state[state_name].config.death_statusmarker = defaults.config.death_statusmarker;
315+
}
316+
if (!state[state_name].config.hasOwnProperty('half_statusmarker')) {
317+
state[state_name].config.half_statusmarker = defaults.config.half_statusmarker;
318+
}
319+
if (!state[state_name].config.hasOwnProperty('bar')) {
320+
state[state_name].config.bar = defaults.config.bar;
321+
}
322+
if (!state[state_name].config.hasOwnProperty('pc_unconscious_statusmarker')) {
323+
state[state_name].config.pc_unconscious_statusmarker = defaults.config.pc_unconscious_statusmarker;
324+
}
325+
if (!state[state_name].config.hasOwnProperty('change_tint')) {
326+
state[state_name].config.change_tint = defaults.config.change_tint;
327+
}
328+
if (!state[state_name].config.hasOwnProperty('fx')) {
329+
state[state_name].config.fx = defaults.config.fx;
330+
}
331+
if (!state[state_name].config.hasOwnProperty('fx_type')) {
332+
state[state_name].config.fx_type = defaults.config.fx_type;
333+
}
334+
if (!state[state_name].config.hasOwnProperty('heal_fx')) {
335+
state[state_name].config.heal_fx = defaults.config.heal_fx;
336+
}
337+
if (!state[state_name].config.hasOwnProperty('heal_fx_type')) {
338+
state[state_name].config.heal_fx_type = defaults.config.heal_fx_type;
339+
}
340+
}
341+
342+
if (state[state_name].config.firsttime) {
343+
sendConfigMenu(true);
344+
state[state_name].config.firsttime = false;
345+
}
346+
};
347+
348+
return {
349+
CheckInstall: checkInstall,
350+
ObserveTokenChange: observeTokenChange,
351+
RegisterEventHandlers: registerEventHandlers
352+
}
353+
})();
354+
355+
on('ready', function () {
356+
'use strict';
357+
358+
DeathTracker.CheckInstall();
359+
DeathTracker.RegisterEventHandlers();
360+
});

DeathTracker/DeathTracker.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
2-
* Version 0.1.6
2+
* Version 0.1.7
33
* Made By Robin Kuiper
4+
* Minor changes by The Aaron
45
* Skype: RobinKuiper.eu
56
* Discord: Atheos#1095
67
* Roll20: https://app.roll20.net/users/1226016/robin
@@ -281,6 +282,9 @@ var DeathTracker = DeathTracker || (function () {
281282
registerEventHandlers = () => {
282283
on('chat:message', handleInput);
283284
on('change:graphic', handleBarValueChange);
285+
if('undefined' !== typeof TokenMod && TokenMod.ObserveTokenChange){
286+
TokenMod.ObserveTokenChange(handleBarValueChange);
287+
}
284288
},
285289

286290
setDefaults = (reset) => {
@@ -353,4 +357,4 @@ on('ready', function () {
353357

354358
DeathTracker.CheckInstall();
355359
DeathTracker.RegisterEventHandlers();
356-
});
360+
});

0 commit comments

Comments
 (0)