Skip to content

Commit 2effc78

Browse files
committed
DeathTracker 0.1.8
1 parent aeeb97e commit 2effc78

File tree

4 files changed

+388
-10
lines changed

4 files changed

+388
-10
lines changed

DeathTracker/0.1.8/DeathTracker.js

+366
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,366 @@
1+
/*
2+
* Version 0.1.8
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_player_tint && isPlayer || state[state_name].config.change_npc_tint && !isPlayer){
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_player_tintButton = makeButton(state[state_name].config.change_player_tint, '!' + state[state_name].config.command + ' config change_player_tint|' + !state[state_name].config.change_player_tint, styles.button + styles.float.right),
194+
change_npc_tintButton = makeButton(state[state_name].config.change_npc_tint, '!' + state[state_name].config.command + ' config change_npc_tint|' + !state[state_name].config.change_npc_tint, styles.button + styles.float.right),
195+
fxButton = makeButton(state[state_name].config.fx, '!' + state[state_name].config.command + ' config fx|' + !state[state_name].config.fx, styles.button + styles.float.right),
196+
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),
197+
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),
198+
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),
199+
200+
listItems = [
201+
'<span style="'+styles.float.left+'">Command:</span> ' + commandButton,
202+
'<span style="'+styles.float.left+'">HP Bar:</span> ' + barButton,
203+
'<span style="'+styles.float.left+'">Dead Statusmarker:</span> ' + death_markerButton,
204+
'<span style="'+styles.float.left+'">Uncon. Statusmarker:<div style="font-size: 8pt">Unconscious marker if PC.</div></span> ' + pc_unconscious_markerButton,
205+
'<span style="'+styles.float.left+'">Half Dead Statusmarker:</span> ' + half_markerButton,
206+
'<span style="'+styles.float.left+'">Change Player Tint Color:</span> ' + change_player_tintButton,
207+
'<span style="'+styles.float.left+'">Change NPC Tint Color:</span> ' + change_npc_tintButton,
208+
'<span style="'+styles.float.left+'">Use FX:<div style="font-size: 8pt">When getting damage.</div></span> ' + fxButton,
209+
]
210+
211+
if(state[state_name].config.fx){
212+
listItems.push('<span style="'+styles.float.left+'">FX Type:</span> ' + fx_typeButton);
213+
}
214+
215+
listItems.push('<span style="'+styles.float.left+'">Use Heal FX:</span> ' + heal_fxButton);
216+
217+
if(state[state_name].config.heal_fx){
218+
listItems.push('<span style="'+styles.float.left+'">Heal FX Type:</span> ' + heal_fx_typeButton);
219+
}
220+
221+
let resetButton = makeButton('Reset', '!' + state[state_name].config.command + ' reset', styles.button + styles.fullWidth);
222+
223+
let title_text = (first) ? script_name + ' First Time Setup' : script_name + ' Config';
224+
message = (message) ? '<p>' + message + '</p>' : '';
225+
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;
226+
makeAndSendMenu(contents, title_text, 'gm');
227+
},
228+
229+
makeAndSendMenu = (contents, title, whisper) => {
230+
title = (title && title != '') && makeTitle(title)
231+
whisper = (whisper && whisper !== '') && '/w ' + whisper + ' ';
232+
sendChat(script_name, whisper + '<div style="' + styles.menu + styles.overflow + '">' + title + contents + '</div>', null, {
233+
noarchive: true
234+
});
235+
},
236+
237+
makeTitle = (title) => {
238+
return '<h3 style="margin-bottom: 10px;">' + title + '</h3>';
239+
},
240+
241+
makeButton = (title, href, style) => {
242+
return '<a style="' + style + '" href="' + href + '">' + title + '</a>';
243+
},
244+
245+
makeList = (items, listStyle, itemStyle) => {
246+
let list = '<ul style="' + listStyle + '">';
247+
items.forEach((item) => {
248+
list += '<li style="' + itemStyle + '">' + item + '</li>';
249+
});
250+
list += '</ul>';
251+
return list;
252+
},
253+
254+
pre_log = (message) => {
255+
log('---------------------------------------------------------------------------------------------');
256+
log(message);
257+
log('---------------------------------------------------------------------------------------------');
258+
},
259+
260+
checkInstall = () => {
261+
if (!_.has(state, state_name)) {
262+
state[state_name] = state[state_name] || {};
263+
}
264+
setDefaults();
265+
266+
log(script_name + ' Ready! Command: !' + state[state_name].config.command);
267+
if (state[state_name].config.debug) {
268+
makeAndSendMenu(script_name + ' Ready! Debug On.', '', 'gm')
269+
}
270+
},
271+
272+
observeTokenChange = function (handler) {
273+
if (handler && _.isFunction(handler)) {
274+
observers.tokenChange.push(handler);
275+
}
276+
},
277+
278+
notifyObservers = function (event, obj, prev) {
279+
_.each(observers[event], function (handler) {
280+
handler(obj, prev);
281+
});
282+
},
283+
284+
registerEventHandlers = () => {
285+
on('chat:message', handleInput);
286+
on('change:graphic', handleBarValueChange);
287+
if('undefined' !== typeof TokenMod && TokenMod.ObserveTokenChange){
288+
TokenMod.ObserveTokenChange(handleBarValueChange);
289+
}
290+
},
291+
292+
setDefaults = (reset) => {
293+
const defaults = {
294+
config: {
295+
command: 'dead',
296+
death_statusmarker: 'dead',
297+
half_statusmarker: 'red',
298+
bar: 1,
299+
firsttime: (reset) ? false : true,
300+
pc_unconscious_statusmarker: 'sleepy',
301+
change_player_tint: true,
302+
change_npc_tint: true,
303+
fx: true,
304+
fx_type: 'splatter-blood',
305+
heal_fx: false,
306+
heal_fx_type: 'glow-holy'
307+
}
308+
};
309+
310+
if (!state[state_name].config) {
311+
state[state_name].config = defaults.config;
312+
} else {
313+
if (!state[state_name].config.hasOwnProperty('command')) {
314+
state[state_name].config.command = defaults.config.command;
315+
}
316+
if (!state[state_name].config.hasOwnProperty('death_statusmarker')) {
317+
state[state_name].config.death_statusmarker = defaults.config.death_statusmarker;
318+
}
319+
if (!state[state_name].config.hasOwnProperty('half_statusmarker')) {
320+
state[state_name].config.half_statusmarker = defaults.config.half_statusmarker;
321+
}
322+
if (!state[state_name].config.hasOwnProperty('bar')) {
323+
state[state_name].config.bar = defaults.config.bar;
324+
}
325+
if (!state[state_name].config.hasOwnProperty('pc_unconscious_statusmarker')) {
326+
state[state_name].config.pc_unconscious_statusmarker = defaults.config.pc_unconscious_statusmarker;
327+
}
328+
if (!state[state_name].config.hasOwnProperty('change_player_tint')) {
329+
state[state_name].config.change_player_tint = defaults.config.change_player_tint;
330+
}
331+
if (!state[state_name].config.hasOwnProperty('change_npc_tint')) {
332+
state[state_name].config.change_npc_tint = defaults.config.change_npc_tint;
333+
}
334+
if (!state[state_name].config.hasOwnProperty('fx')) {
335+
state[state_name].config.fx = defaults.config.fx;
336+
}
337+
if (!state[state_name].config.hasOwnProperty('fx_type')) {
338+
state[state_name].config.fx_type = defaults.config.fx_type;
339+
}
340+
if (!state[state_name].config.hasOwnProperty('heal_fx')) {
341+
state[state_name].config.heal_fx = defaults.config.heal_fx;
342+
}
343+
if (!state[state_name].config.hasOwnProperty('heal_fx_type')) {
344+
state[state_name].config.heal_fx_type = defaults.config.heal_fx_type;
345+
}
346+
}
347+
348+
if (state[state_name].config.firsttime) {
349+
sendConfigMenu(true);
350+
state[state_name].config.firsttime = false;
351+
}
352+
};
353+
354+
return {
355+
CheckInstall: checkInstall,
356+
ObserveTokenChange: observeTokenChange,
357+
RegisterEventHandlers: registerEventHandlers
358+
}
359+
})();
360+
361+
on('ready', function () {
362+
'use strict';
363+
364+
DeathTracker.CheckInstall();
365+
DeathTracker.RegisterEventHandlers();
366+
});

0 commit comments

Comments
 (0)