· 6 years ago · Dec 28, 2019, 03:08 AM
1// ==UserScript==
2// @name TORN: CT2019
3// @namespace dekleinekobini.christmastown
4// @version 2019.4
5// @author DeKleineKobini
6// @description Christmas Town utilities
7// @match https://www.torn.com/christmas_town.php*
8// @require https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js
9// @require https://greasyfork.org/scripts/390917-dkk-torn-utilities/code/DKK%20Torn%20Utilities.js?version=759441
10// @run-at document-start
11// @connect api.torn.com
12// @grant GM_xmlhttpRequest
13// @grant GM_setValue
14// @grant GM_getValue
15// ==/UserScript==
16
17"use strict";
18
19initScript({
20 name: "Christmas Town",
21 abbr: "XMASS",
22 logging: "all"
23});
24
25class WordFixer {
26
27 constructor() {
28 this.started = false;
29 }
30
31 get started() {
32 return this._started;
33 }
34
35 set started(started) {
36 this._started = started;
37 }
38
39 openGame() {
40 dkklog.trace("WordFixer::openGame", $("#minigame-panel"));
41 this.started = true;
42 $("#minigame-information").html("WordFixer solution: <span id='ct-minigame'><span>");
43 $("#minigame-panel").show();
44 }
45
46 updateGame(message) {
47 $("#ct-minigame").html(message);
48 }
49
50 closeGame() {
51 this.started = false;
52 $('#minigame-panel').hide();
53 }
54
55 gameLogic(text) {
56 let ordered = LIBRARY_WORDFIXER.map(e => {return { orig: e, sort: e.replace(/[^ a-zA-Z]/g, "").split('').sort().join('') }});
57 let results = ordered.filter(e => e.sort == text.toLowerCase().split('').sort().join('')).map(e => e.orig);
58 //if (results.length == 0)
59 // return `Good luck with figuring this out :(. Contact me <a href="${contact}">here</a> to update this 2018 script <input value="${text}" readonly/>` + ping(`report=${payload.result.word}&user=${getID()}`);
60 return results;
61 }
62
63}
64
65const STORAGE = new Storage("christmastown", "localStorage");
66
67const dataVersion = getCTVersion();
68dkklog.info(`Running version '${dataVersion}' for Christmas Town!`);
69const christmas = STORAGE.get({ settings: {} });
70const data = { lastMap: {}};
71const API = new TornAPI(api => {
72 dkklog.trace("API object created.");
73 getCache("tornapi_items", false).then(cache => {
74 dkklog.trace("Possible caching found.");
75 if (cache) {
76 dkklog.debug("Loaded items from the cache!");
77 for (let id in cache) {
78 let store = cache[id];
79 itemValues[store.name] = {
80 value: store.market_value,
81 image: store.image
82 }
83 }
84 } else {
85 dkklog.debug("Attempting to load from the api.");
86 api.sendRequest("torn", null, "items").then(json => {
87 dkklog.debug("Loaded items from the api!");
88
89 for (let id in json.items) {
90 let store = json.items[id];
91 itemValues[store.name] = {
92 value: store.market_value,
93 image: store.image
94 }
95 }
96
97 setCache("tornapi_items", json.items, getMillisUntilNewDay());
98 }, reject => {
99 dkklog.warn("Rejected api response! " + reject);
100 });
101 }
102 }, reject => {
103 dkklog.warn("Rejected cache response!");
104 });
105});
106
107/*
108 * Provided by Helcostr [1934501]
109 */
110const LIBRARY_WORDFIXER = ["elf","eve","fir","ham","icy","ivy","joy","pie","toy","gift","gold","list","love","nice","sled","star","wish","wrap","xmas","yule","angel","bells","cider","elves","goose","holly","jesus","merry","myrrh","party","skate","visit","candle","creche","cookie","eggnog","family","frosty","icicle","joyful","manger","season","spirit","tinsel","turkey","unwrap","wonder","winter","wreath","charity","chimney","festive","holiday","krampus","mittens","naughty","package","pageant","rejoice","rudolph","scrooge","snowman","sweater","tidings","firewood","nativity","reindeer","shopping","snowball","stocking","toboggan","trimming","vacation","wise men","workshop","yuletide","chestnuts","christmas","fruitcake","greetings","mince pie","mistletoe","ornaments","snowflake","tradition","candy cane","decoration","ice skates","jack frost","north pole","nutcracker","saint nick","yule log","card","jolly","hope","scarf","candy","sleigh","parade","snowy","wassail","blizzard","noel","partridge","give","carols","tree","fireplace","socks","lights","kings","goodwill","sugarplum","bonus","coal","snow","happy","presents","pinecone"];
111
112var fixer = new WordFixer();
113
114/*
115 * CSS provided by Jox [1714547] and added on request.
116 */
117if (getData("settings", {}).hideCompass) addCSS("ct", ".d #ct-wrap .user-map-container .user-map:before {z-index: 0 !important}");
118
119interceptFetch('christmas_town.php', (response, url) => {
120 dkklog.trace("Got a FETCH response.", response, url)
121
122 if (response.settings) data.lastMap.id = response.settings.userActiveMapID;
123 if (response.myMaps) $("#ctwidget").hide();
124
125 //
126 if (response.prizes) {
127 response.prizes.forEach(element => receiveItem(response.board ? "minigame" : "chest", element.name, element.quantity));
128 }
129
130 if (!url.includes("q=miniGameAction") && fixer.started) fixer.closeGame();
131
132 if (response.miniGameType) {
133 let game = response.miniGameType;
134 dkklog.debug("Minigame", game);
135 if (game == "WordFixer") {
136 fixer.openGame();
137 fixer.updateGame(fixer.gameLogic(response.progress.word));
138 }
139 } else if (fixer.started && !response.settings) {
140 if (response.finished) {
141 fixer.updateGame('<font color="gray">What was my purpose?... Oh my god.</font>');
142 } else if (typeof response.message != "undefined" && response.message != "game")
143 fixer.updateGame(`<font color="gray">I hope you enjoy my existance. Thanks to <a href='https://www.torn.com/profiles.php?XID=1934501#/'>Helcostr</a> for the original code and permission.</font>`);
144 else if (response.success) fixer.updateGame(fixer.gameLogic(response.progress.word));
145 else if (response.finished === false || (response.mapData && !response.mapData.cellEvent)) fixer.closeGame();
146 }
147
148 if (response.mapData) {
149 $("#ctwidget").show();
150
151 // has just loaded the map
152 if (response.mapData.map) data.lastMap.name = response.mapData.map.name;
153
154 // current tile is minigames or chest
155 if (response.mapData.cellEvent) {
156 let game = response.mapData.cellEvent.miniGameType;
157 let event = response.mapData.cellEvent.type;
158
159 let area = $(".status-area-container").get(0)
160
161 switch (event) {
162 case "gameChristmasWreath":
163 if (!getData("settings", {}).imageWreath) observeMutations(area, ".status-area-container > div[class*='christmas-wreath']", true, (mutations, observer) => {
164 $(".status-area-container > div[class*='christmas-wreath'] > div:eq(1) > img:eq(0)").remove();
165 }, { childList: true, attributes: true, subtree: true});
166 break;
167 case "gameItemMatch": // wtf is this?
168 case "gameSnowballShooter": // maybe only show grinch ?
169 case "gameGetTheGrinch": // not enabled yet
170 case "gamePickStocking": // not enabled yet
171 case "gamePYPresent":
172 case "gameHangman":
173 case "":
174 break;
175 // Eternity
176
177 // Three Hats
178 // Icicle Assassins
179 // Garland Assamble
180 // Sping the Weel
181 // Gift Wrap
182 // Lucky Figurine
183 }
184 } // 1*2
185
186 // view has items
187 if (response.mapData.items) {
188 let items = response.mapData.items;
189
190 let ctspawn = "";
191 let spawnChests = "";
192 data.position = response.mapData.position;
193
194 let countItems = 0;
195 let countChests = 0;
196 $.each(items, (index, item) => {
197 let image = item.image.url;
198 let name, type;
199
200 if (image.includes("/keys/")) {
201 type = "key";
202 if (image.includes("/bronze/")){
203 name = "Bronze key";
204 image = "https://www.torn.com/images/items/christmas_town/keys/keys-91.png"
205 } else if (image.includes("/silver/")) { // TODO - confirm
206 name = "Silver key";
207 image = "https://www.torn.com/images/items/christmas_town/keys/keys-92.png"
208 } else if (image.includes("/gold/")) {
209 name = "Golden key";
210 image = "https://www.torn.com/images/items/christmas_town/keys/keys-93.png"
211 }
212 } else if (image.includes("/chests/")) {
213 type = "chest";
214 // TODO - possibly improve images
215 if (image.includes("/3.gif")){
216 name = "Bronze chest";
217 } else if (image.includes("/2.gif")){
218 name = "Silver chest";
219 } else if (image.includes("/1.gif")){
220 name = "Golden chest";
221 }
222 } else if (image.includes("/combinationChest/")) {
223 type = "combinationChest";
224 name = "Combination chest";
225 } else {
226 type = "item";
227 name = "Mysterious gift";
228 image = "http://iconshow.me/media/images/xmas/merry-christmas-icon/7/gift-16.png"
229 }
230
231 switch (type) {
232 case "item":
233 countItems++;
234 break;
235 case "chest":
236 countChests++;
237 break;
238 }
239
240 let paddingL = "0px";
241 let paddingR = "6px";
242
243 let html = `<li><img src="${image}" style="padding-left: ${paddingL}; padding-right: ${paddingR};">[${item.position.x}, ${item.position.y}] ${name}</li>`;
244
245 if (type == "chest") spawnChests += html;
246 else ctspawn += html;
247 });
248 $("#ct19-items").html(ctspawn || "No items nearby.");
249 $("#ct19-itemshead").html(`Nearby Items (${countItems})`);
250 $("#ct19-chests").html(spawnChests || "No chests nearby.");
251 $("#ct19-chestshead").html(`Nearby Chests (${countChests})`);
252 }
253
254 // current tile has trigger
255 if (response.mapData.trigger) {
256 let triggerMessage = response.mapData.trigger.message;
257
258 // current tile had an item
259 if (triggerMessage.includes(" find ")) {
260 // TODO - fix item with 'an' instead of 'a'
261 let indexA = triggerMessage.indexOf(" a ");
262 if (indexA < 0) indexA = triggerMessage.indexOf(" an ") + 4;
263 else indexA += 3;
264
265 let item = triggerMessage.substring(indexA).trim();
266
267 switch (item) {
268 case "pair of Festive Socks":
269 item = "Festive Socks";
270 }
271
272 receiveItem("map", item, 1, response.mapData.position);
273 }
274 }
275 }
276});
277
278$(document).ready(() => {
279 loadPanel()
280
281 runOnEvent(() => {
282 let hash = window.location.hash;
283 if (hash.includes("/mymaps")) {
284 $("#ctwidget").hide();
285 $("#ctsettings").show();
286 } else if (hash.includes("/mapeditor")) {
287 $("#ctwidget").hide();
288 $("#ctsettings").hide();
289 } else if (hash.includes("/parametereditor")) {
290 $("#ctwidget").hide();
291 $("#ctsettings").hide();
292 } else if (hash === undefined || hash == "#/" || hash == "#" || hash == ""){
293 $("#ctwidget").show();
294 $("#ctsettings").hide();
295 }
296 }, "hashchange", true)
297});
298
299function inRadius(item) {
300 let difference = {
301 x: (data.position.x - parseInt(item.x)),
302 y: (data.position.y - parseInt(item.y))
303 };
304 return (difference.x > 8 && difference.x < 8) && (difference.y > 8 && difference.y < 8);
305}
306
307function getData(key, def) {
308 if (!christmas[dataVersion]) christmas[dataVersion] = {};
309
310 return christmas[dataVersion][key] || def;
311}
312
313function setData(key, value) {
314 if (!christmas[dataVersion]) christmas[dataVersion] = {};
315
316 christmas[dataVersion][key] = value;
317}
318
319function saveData() {
320 STORAGE.set(christmas);
321}
322
323var itemValues = {};
324
325function loadPanel() {
326 dkklog.trace("Adding panel to the view.");
327 let name = "CT2019 by DeKleineKobini";
328
329 addCSS("ct",
330 "@keyframes pulse { 0% { opacity: 1; box-shadow: 0 0 0 0px #ce81dd; bottom: 200%; right: 200%; height: 500%; width: 500%; } 50% { bottom: 250%; right: 250%; height: 600%; width: 600%; } 100% { opacity: 0.5; box-shadow: 0 0 0 100px rgba(0, 0, 0, 0); bottom: 200%; right: 200%; height: 500%; width: 500%; } }"
331 + "div.items-layer div.ct-item::after { background-image: radial-gradient(rgba(0, 0, 0, 0), #BD6FCC); border-radius: 100%; content: ''; display: block; position: relative; bottom: 200%; right: 200%; height: 500%; width: 500%; animation: pulse 5s ease-out; animation-iteration-count: infinite; }"
332 + ".ct-information-table { width: 100%; height: 100%; border-collapse: separate; text-align: left; }"
333 + ".ct-information-table > tbody > tr > th { height: 16px; white-space: nowrap; text-overflow: ellipsis; font-weight: 700; padding: 2px 10px; border-top: 1px solid rgb(255, 255, 255); border-bottom: 1px solid rgb(204, 204, 204); background: linear-gradient(rgb(255, 255, 255), rgb(215, 205, 220)); }"
334 + ".ct-information-table, .ct-information-table > tbody > tr > tr, .ct-information-table > tbody > tr > td { border: none !important;; }"
335 + ".ct-information-table > tbody > tr > td { padding: 2px 10px; }"
336 + "#ct19-items, #ct19-chests { padding: 4px 0px; }"
337 + "#ct19-items li, #ct19-chests li { padding: 4px 0px; }"
338 + "#ct19-items img, #ct19-chests img { vertical-align: middle; float: left; max-height: 16px; }"
339 )
340 $("#christmastownroot div:eq(1)").after("<hr class='page-head-delimiter m-top10 m-bottom10'>");
341 $("#christmastownroot div:eq(1)").after(
342 `<div><article class='dkk-widget' style='display: none;' id='ctsettings'><header class='dkk-widget_green'><span class='dkk-widget_title'>Settings for ${name}</span></header>
343<div class='dkk-widget_body dkk-round-bottom'>
344<div class='dkk-panel-left'><table class='dkk-data-table'><tr><td>Hide Compass</td><td style='text-align: center;'><input type="checkbox" id='showCompass' name='showCompass' value='Compass'></td></tr><tr><td>Show Wreath image</td><td style='text-align: center;'><input type="checkbox" id='imageWreath' name='imageWreath' value='Wreath'></td></tr></table></div>
345<div class='dkk-panel-middle'></div>
346<div class='dkk-panel-right'></div>
347</div>
348</article></div><div class="clear"></div>`
349 );
350 $("#showCompass").change(() => {
351 let settings = getData("settings", {});
352
353 settings.hideCompass = $("#showCompass").is(":checked");
354
355 setData("settings", settings);
356 saveData();
357
358 if (settings.hideCompass) addCSS("ct", ".d #ct-wrap .user-map-container .user-map:before {z-index: 0 !important}");
359 else removeCSS("ct");
360 });
361 if (getData("settings", {}).hideCompass) $("#showCompass").prop("checked", true);
362
363 $("#imageWreath").change(() => {
364 let settings = getData("settings", {});
365
366 settings.imageWreath = $("#imageWreath").is(":checked");
367
368 setData("settings", settings);
369 saveData();
370 });
371 if (getData("settings", {}).imageWreath) $("#imageWreath").prop("checked", true);
372
373 $("#christmastownroot div:eq(1)").after(
374 `<div><article class='dkk-widget' style='display: none;' id='ctwidget'><header class='dkk-widget_green'><span class='dkk-widget_title'>${name}</span></header>
375<div class='dkk-widget_body'>
376<div class='dkk-panel-left'><table class='dkk-data-table'><tr><th>Item Information</th></tr><tr><td><table class='ct-information-table'><tr><td>Items Found</td><td id='ct19-itemcount'>loading...</td></tr><tr><td>Total Value</td><td id='ct19-itemvalue'>loading...</td></tr><tr><td >Average Value</td><td id='ct19-itemvalueavg'>loading...</td></tr></table></td></tr></table></div>
377<div class='dkk-panel-middle'></div>
378<div class='dkk-panel-right'><table class='dkk-data-table' style='display: none;' id='minigame-panel'><tr><th>Minigame Helper</th></tr><tr><td id='minigame-information'></td></tr></table></div>
379</div>
380<div class='dkk-widget_body dkk-round-bottom'>
381<div class='dkk-panel-left'><table class='dkk-data-table'><tr><th id='ct19-itemshead'>Nearby Items (0)</th></tr><tr><td><ul id='ct19-items'></ul></td></tr></table></div>
382<div class='dkk-panel-middle'><table class='dkk-data-table'><tr><th id='ct19-chestshead'>Nearby Chests (0)</th></tr><tr><td><ul id='ct19-chests'></ul></td></tr></table></div>
383<div class='dkk-panel-right'></div>
384</div>
385</article></div><div class="clear"></div>`
386 );
387
388 let itemcount = getData("itemcount", 0);
389 let itemvalue = getData("itemvalue", 0);
390
391 $("#ct19-itemcount").html(itemcount);
392 $("#ct19-itemvalue").html(`$${itemvalue.format()}`);
393 $("#ct19-itemvalueavg").html(itemcount > 0 ? "$" + (itemvalue / itemcount).format() : "N/A");
394}
395
396function receiveItem(source, item, amount, position) {
397 if (!itemValues[item]) {
398 dkklog.debug("You received something that is not an item!", {
399 source: source,
400 item: item,
401 amount: amount
402 });
403 return false;
404 }
405
406 if (!amount) amount = 1;
407
408 let itemcount = getData("itemcount", 0) + amount;
409 let itemvalue = getData("itemvalue", 0) + (itemValues[item].value * amount);
410
411 setData("itemcount", itemcount);
412 setData("itemvalue", itemvalue);
413 saveData();
414
415 $("#ct19-itemcount").html(itemcount);
416 $("#ct19-itemvalue").html(`$${itemvalue.format()}`);
417 $("#ct19-itemvalueavg").html(`$${(itemvalue / itemcount).format()}`);
418
419 dkklog.debug("You received an item!", {
420 source: source,
421 item: item,
422 amount: amount,
423 value: {
424 single: itemValues[item].value,
425 total: itemValues[item].value * amount
426 }
427 });
428 return true;
429}
430
431function getCTVersion(date) {
432 if (!date) date = new Date();
433
434 let prefix = "";
435 let hour = date.getUTCHours();
436 let day = date.getUTCDate();
437 let month = date.getUTCMonth() + 1;
438 let year = date.getUTCFullYear();
439
440 if (month <= 6) year--;
441
442 if (month != 12 || day < 17) prefix = "BETA ";
443
444 return `${prefix}${year}`;
445}