Dead Frontier - API

Dead Frontier API

当前为 2025-02-08 提交的版本,查看 最新版本

此脚本不应直接安装,它是供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/441829/1533601/Dead%20Frontier%20-%20API.js

  1. // ==UserScript==
  2. // @name Dead Frontier - API
  3. // @namespace Dead Frontier - Shrike00
  4. // @match *://fairview.deadfrontier.com/onlinezombiemmo/index.php?page=24
  5. // @match *://fairview.deadfrontier.com/onlinezombiemmo/index.php?page=25
  6. // @match *://fairview.deadfrontier.com/onlinezombiemmo/index.php?page=35
  7. // @grant none
  8. // @version 0.1.22
  9. // @author Shrike00
  10. // @description Dead Frontier API
  11. // ==/UserScript==
  12.  
  13. // Changelog
  14. // 0.1.22 - February 7, 2025
  15. // - Change: Added implantsToImplants.
  16. // 0.1.21 - February 7, 2025
  17. // - Change: Added backpackToImplants.
  18. // 0.1.20 - February 7, 2025
  19. // - Change: Removed window references and called in implicit scope to account for sandboxing (for non-@grant none scripts)
  20. // - Feature: Added implant mutation.
  21. // 0.1.19 - December 16, 2024
  22. // - Change: Replaced references to webCall with webCall to account for const change.
  23. // 0.1.18 - November 8, 2024
  24. // - Bugfix: Fixed backpack slot count/iterating through backpack when not wearing one.
  25. // - Bugfix: Fixed requesting rename data.
  26. // 0.1.17 - November 4, 2024
  27. // - Feature: Added search by category to MarketCache.
  28. // 0.1.16 - October 27, 2024
  29. // - Feature: Added CollectionBook.
  30. // 0.1.15 - October 25, 2024
  31. // - Bugfix: Added support for private/public selling entries.
  32. // 0.1.14 - October 24, 2024
  33. // - Bugfix: Added credits to stackable items.
  34. // - Feature: Added support for searching renamed items that is completely separate from searching regular items.
  35. // 0.1.13 - May 2, 2024
  36. // - Change: Updated tradezone ids.
  37. // 0.1.12 - April 11, 2024
  38. // - Bugfix: Fixed querying backpack from uservars.
  39. // 0.1.11 - February 25, 2024
  40. // - Bugfix: Storage moves for larger than max stacked ammo should now work properly.
  41. // 0.1.10 - February 22, 2024
  42. // - Change: Added support for backpacks.
  43. // - Change: Added mastercrafting.
  44. // 0.1.9 - February 22, 2024
  45. // - Change: Added generic market search strings that should work for most items.
  46. // 0.1.8 - February 21, 2024
  47. // - Change: Added property to Item for renames, added backpack functions for querying and moving items.
  48. // 0.1.7 - December 15, 2023
  49. // - Change: Added search string for all stockings.
  50. // 0.1.6 - October 24, 2023
  51. // - Bugfix: Item should now correctly handle cooked item names.
  52. // 0.1.5 - August 19, 2023
  53. // - Bugfix: Bank now checks for presence of bank elements.
  54. // 0.1.4 - March 21, 2023
  55. // - Change: Storage moves no longer query the storage before sending the move.
  56. // 0.1.3 - March 19, 2023
  57. // - Change: Added ammo, inventory, and selling entry counts to MarketItems.
  58. // 0.1.2 - March 15, 2023
  59. // - Change: Added additional helper functions.
  60. // - Change: Added parameter to some functions that talk to the backend. Can now choose to not update UI on return.
  61. // 0.1.1 - December 24, 2022
  62. // - Change: Added Christmas Stocking 2022.
  63.  
  64. // base.js
  65. // flshToArr(flashStr, padding, callback) takes a response and puts it to a object or sends it to a callback.
  66. // updateIntoArr(flshArr, baseArr) copies the elements of the first array into the second.
  67. // updateAllFields() updates weapon sidebar, vital stats/boosts sidebar.
  68. // renderAvatarUpdate(elem, customVars) updates character avatar.
  69. // inventory.js
  70. // populateStorage(), populateInventory(), populateImplants(), populateCharacterInventory() all update their elements
  71. // from userVars.
  72. // reloadStorageData() and reloadInventoryData() load from the backend before calling the populate functions.
  73. // TODO: Fix storage no ui updating (need to move response from storage move into local variables)
  74. const DeadFrontier = (function() {
  75. 'use strict';
  76. // TODO: Remove request methods, always re-query. Also remove uservars, global data?
  77. // TODO: Figure out how to request/force update global data.
  78. // Helpers
  79.  
  80. function typeSplit(type) {
  81. // Splits item id into components.
  82. return type.split("_");
  83. }
  84.  
  85. function stringGroups(s, size) {
  86. // Splits string into array of substrings of length size or smaller.
  87. const output = [];
  88. for (let i = 0; i < s.length; i += size) {
  89. output.push(s.substring(i, i + size));
  90. }
  91. return output;
  92. }
  93.  
  94. function responseToMap(response) {
  95. // Converts raw string response to Map.
  96. const map = new Map();
  97. const pairs = response.split("&");
  98. for (let i = 0; i < pairs.length; i++) {
  99. const [key, value] = pairs[i].split("=");
  100. map.set(key, value);
  101. }
  102. map.delete(""); // Removes undefined key, since response leads with an ampersand (&).
  103. return map;
  104. }
  105.  
  106. function updateCashBank(cash_on_hand, cash_in_bank) {
  107. // Updates cash and bank amount elements with provided values.
  108. const cash = "Cash: $"+nf.format(cash_on_hand);
  109. const bank = "Bank: $"+nf.format(cash_in_bank);
  110. $(".heldCash").each(function(cashKey, cashVal) {
  111. $(cashVal).text(cash).attr("data-cash", cash);
  112. });
  113. const bank_element_exists = $("#bankCash").length > 0;
  114. if (bank_element_exists){
  115. $("#bankCash").text(bank).attr("data-cash", bank);
  116. }
  117. }
  118.  
  119. function promiseWait(dt) {
  120. // Returns promise that waits the given number of ms.
  121. const promise = new Promise(function(resolve, reject) {
  122. setTimeout(resolve, dt);
  123. });
  124. return promise;
  125. }
  126.  
  127. function queryObjectByKey(obj, key, dt = 100) {
  128. // Returns promise that resolves with the object once it contains the given key.
  129. if (obj[key] !== undefined) {
  130. return Promise.resolve(obj);
  131. }
  132. const promise = new Promise(function(resolve, reject) {
  133. const check = setInterval(function() {
  134. const key_exists = obj[key] !== undefined;
  135. if (key_exists) {
  136. clearInterval(check);
  137. resolve(obj);
  138. }
  139. }, dt);
  140. });
  141. return promise;
  142. }
  143.  
  144. function queryObjectByKeys(obj, keys, dt) {
  145. // Returns promise that resolves with the object once it contains all the given keys.
  146. const unique_keys = new Set(keys);
  147. const promises = Array.from(unique_keys).map((key) => queryObjectForKey(obj, key, dt));
  148. return Promise.all(promises).then(() => obj);
  149. }
  150.  
  151. function isMastercrafted(item) {
  152. // Returns if item is mastercrafted.
  153. const properties = item.properties;
  154. const category = item.category
  155. const is_mastercrafted = properties.has("mastercrafted") && properties.get("mastercrafted") === true;
  156. const is_enhanceable = isEnhanceable(item);
  157. return is_enhanceable && is_mastercrafted;
  158. }
  159.  
  160. function isNearGodcrafted(item) {
  161. // Returns if item is near-godcrafted.
  162. const properties = item.properties;
  163. const category = item.category;
  164. const is_mastercrafted = properties.has("mastercrafted") && properties.get("mastercrafted") === true;
  165. if (category === ItemCategory.WEAPON) {
  166. const total_stats = properties.get("accuracy") + properties.get("reloading") + properties.get("critical_hit");
  167. return total_stats === 23;
  168. } else if (category === ItemCategory.ARMOUR) {
  169. const total_stats = properties.get("agility") + properties.get("endurance");
  170. return total_stats === 47;
  171. } else if (category === ItemCategory.BACKPACK) {
  172. return properties.get("bonus_slots") === 2;
  173. } else {
  174. return false;
  175. }
  176. }
  177.  
  178. function isGodcrafted(item) {
  179. // Returns if item is godcrafted.
  180. const properties = item.properties;
  181. const category = item.category;
  182. const is_mastercrafted = properties.has("mastercrafted") && properties.get("mastercrafted") === true;
  183. const godcrafted_weapon = is_mastercrafted && category === ItemCategory.WEAPON
  184. && properties.get("accuracy") === 8 && properties.get("reloading") === 8 && properties.get("critical_hit") === 8;
  185. const godcrafted_armour = is_mastercrafted && category === ItemCategory.ARMOUR
  186. && properties.get("agility") === 24 && properties.get("endurance") === 24;
  187. const godcrafted_backpack = is_mastercrafted && category === ItemCategory.BACKPACK
  188. && properties.get("bonus_slots") === 3;
  189. return godcrafted_weapon || godcrafted_armour || godcrafted_backpack;
  190. }
  191.  
  192. function isCooked(item) {
  193. // Returns if item is cooked.
  194. return item.properties.has("cooked") && item.properties.get("cooked") === true;
  195. }
  196.  
  197. function isStackable(item) {
  198. // Returns if item is stackable.
  199. return item.category === ItemCategory.AMMO || item.base_type === "credits";
  200. }
  201.  
  202. function isEnhanceable(item) {
  203. // Returns if an item can be mastercrafted/godcrafted.
  204. const enhanceable = new Set([ItemCategory.WEAPON, ItemCategory.ARMOUR, ItemCategory.BACKPACK]);
  205. return enhanceable.has(item.category);
  206. }
  207.  
  208. // Enums
  209.  
  210. const Tradezone = {
  211. // The integers used are the same as the internal Dead Frontier representation, so they cannot be changed.
  212. OUTPOST: 21,
  213. CAMP_VALCREST: 22,
  214. // NASTYAS_HOLDOUT: 4,
  215. // DOGGS_STOCKAGE: 10,
  216. // PRECINCT_13: 11,
  217. // FORT_PASTOR: 12,
  218. // SECRONOM_BUNKER: 13,
  219. WASTELANDS: 10,
  220. NW: 1,
  221. N: 2,
  222. NE: 3,
  223. W: 4,
  224. CENTRAL: 5,
  225. E: 6,
  226. SW: 7,
  227. S: 8,
  228. SE: 9
  229. };
  230.  
  231. const ItemCategory = {
  232. AMMO: "ammo",
  233. WEAPON: "weapon",
  234. ARMOUR: "armour",
  235. BACKPACK: "backpack",
  236. ITEM: "item",
  237. OTHER: "other"
  238. };
  239.  
  240. const ItemSubcategory = {
  241. FOOD: "food",
  242. MEDICINE: "medicine",
  243. IMPLANT: "implant",
  244. CLOTHING: "clothing",
  245. BARRICADE: "barricade",
  246. OTHER: "other"
  247. };
  248.  
  249. const ServiceType = {
  250. CHEF: "Chef",
  251. DOCTOR: "Doctor",
  252. ENGINEER: "Engineer"
  253. };
  254.  
  255. const ProficiencyType = {
  256. MELEE: "melee",
  257. PISTOL: "pistol",
  258. RIFLE: "rifle",
  259. SHOTGUN: "shotgun",
  260. MACHINEGUN: "machinegun",
  261. EXPLOSIVE: "explosive"
  262. };
  263.  
  264. const UiUpdate = {
  265. YES: true,
  266. NO: false
  267. };
  268.  
  269. // Predicates
  270.  
  271. const ItemFilters = {
  272. Mastercrafted: (item) => isMastercrafted(item),
  273. NearGodcrafted: (item) => isNearGodcrafted(item),
  274. Godcrafted: (item) => isGodcrafted(item),
  275. Cooked: (item) => isCooked(item),
  276. Enhanceable: (item) => isEnhanceable(item)
  277. };
  278.  
  279. const ServiceFilters = {
  280. ServiceLevel: function(level) {
  281. return (service) => service.level === level;
  282. },
  283. ServiceLevels: function(levels) {
  284. return (service) => levels.includes(service.level);
  285. },
  286. ServiceLevelAtLeast: function(level) {
  287. return (service) => service.level >= level;
  288. }
  289. };
  290.  
  291. const MarketFilters = {
  292. ServiceLevel: function(level) {
  293. return (market_entry) => market_entry.service.level === level;
  294. },
  295. ServiceLevels: function(levels) {
  296. return (market_entry) => levels.includes(market_entry.service.level);
  297. },
  298. ServiceLevelAtLeast: function(level) {
  299. return (market_entry) => market_entry.service.level >= level;
  300. },
  301. Mastercrafted: (market_entry) => isMastercrafted(market_entry.item),
  302. NearGodcrafted: (market_entry) => isNearGodcrafted(market_entry.item),
  303. Godcrafted: (market_entry) => isGodcrafted(market_entry.item),
  304. Cooked: (market_entry) => isCooked(market_entry.item),
  305. Enhanceable: (market_entry) => isEnhanceable(market_entry.item)
  306. };
  307.  
  308. // Classes
  309.  
  310. // Item, ItemMarketEntry, Service, ServiceMarketEntry
  311. // These classes are all simple data classes meant to hold information.
  312.  
  313. class Item {
  314. static #global_data = globalData;
  315.  
  316. #makeProperties(full_type) {
  317. const base_type = typeSplit(full_type)[0];
  318. const data = Item.#global_data[base_type];
  319. const is_weapon = data.itemcat === "weapon";
  320. const is_armour = data.itemcat === "armour";
  321. const is_backpack = data.itemcat === "backpack";
  322. // Creates properties Map from full type string.
  323. const components = typeSplit(full_type);
  324. const properties = new Map();
  325. // Iterate through each component.
  326. for (let i = 1; i < components.length; i++) {
  327. const component = components[i];
  328. const is_mastercrafted = component.indexOf("stats") !== -1;
  329. const has_colour = component.indexOf("colour") !== -1;
  330. const is_rename = component.indexOf("name") !== -1;
  331. if (is_mastercrafted) {
  332. properties.set("mastercrafted", true);
  333. const numbers = component.substring("stats".length);
  334. if (is_weapon) {
  335. const stats = stringGroups(numbers, 1);
  336. properties.set("accuracy", parseInt(stats[0]));
  337. properties.set("reloading", parseInt(stats[1]));
  338. properties.set("critical_hit", parseInt(stats[2]));
  339. } else if (is_armour) {
  340. const stats = stringGroups(numbers, 2);
  341. properties.set("agility", parseInt(stats[0]));
  342. properties.set("endurance", parseInt(stats[1]));
  343. } else if (is_backpack) {
  344. const stats = stringGroups(numbers, 1);
  345. properties.set("bonus_slots", parseInt(stats[0]));
  346. }
  347. } else if (has_colour) {
  348. const colour = component.substring("colour".length);
  349. properties.set("colour", colour);
  350. } else if (is_rename) {
  351. const rename = component.substring("name".length);
  352. properties.set("rename", rename);
  353. } else {
  354. properties.set(component, true);
  355. }
  356. }
  357. // Properties from globalData.
  358. properties.set("lootable", !("noloot" in data) || data.noloot !== "1");
  359. properties.set("transferable", !("no_transfer" in data) || data.no_transfer !== "1");
  360. if (is_weapon) {
  361. properties.set("weapon_type", data.type);
  362. properties.set("proficiency_type", data.wepPro);
  363. properties.set("proficiency_level", data.pro_req);
  364. properties.set("required_strength", data.str_req);
  365. } else if (is_armour) {
  366. properties.set("engineer_level", parseInt(data.shop_level) - 5);
  367. properties.set("required_strength", parseInt(data.str_req));
  368. } else if (is_backpack) {
  369. properties.set("base_slots", parseInt(data.slots));
  370. const total_slots = properties.has("bonus_slots") ? properties.get("base_slots") + properties.get("bonus_slots") : properties.get("base_slots");
  371. properties.set("total_slots", total_slots);
  372. }
  373.  
  374. return properties;
  375. }
  376.  
  377. #itemCategory(type) {
  378. // Returns item category given type.
  379. const data = Item.#global_data[type];
  380. const is_ammo = data.itemcat == "ammo";
  381. const is_weapon = data.itemcat == "weapon";
  382. const is_armour = data.itemcat == "armour";
  383. const is_backpack = data.itemcat === "backpack";
  384. const is_item = data.itemcat == "item";
  385. if (is_ammo) {
  386. return ItemCategory.AMMO;
  387. } else if (is_weapon) {
  388. return ItemCategory.WEAPON;
  389. } else if (is_armour) {
  390. return ItemCategory.ARMOUR;
  391. } else if (is_backpack) {
  392. return ItemCategory.BACKPACK;
  393. } else if (is_item) {
  394. return ItemCategory.ITEM;
  395. } else {
  396. return ItemCategory.OTHER;
  397. }
  398. }
  399.  
  400. #itemSubcategory(type) {
  401. // Returns item subcategory given type. Type should have itemcat == "item".
  402. const data = Item.#global_data[type];
  403. const is_food = parseInt(data.foodrestore) > 0;
  404. const is_medicine = parseInt(data.healthrestore) > 0;
  405. const is_implant = "implant" in data && data.implant == "1";
  406. const is_clothing = "clothingtype" in data;
  407. const is_barricade = "barricade" in data && data.barricade == "1";
  408. if (is_food) {
  409. return ItemSubcategory.FOOD;
  410. } else if (is_medicine) {
  411. return ItemSubcategory.MEDICINE;
  412. } else if (is_implant) {
  413. return ItemSubcategory.IMPLANT;
  414. } else if (is_clothing) {
  415. return ItemSubcategory.CLOTHING;
  416. } else if (is_barricade) {
  417. return ItemSubcategory.BARRICADE;
  418. } else {
  419. return ItemSubcategory.OTHER;
  420. }
  421. }
  422.  
  423. constructor(full_type, name, quantity) {
  424. this.full_type = full_type;
  425. this.base_name = name;
  426. this.base_type = typeSplit(full_type)[0];
  427. this.category = this.#itemCategory(this.base_type);
  428. this.quantity = parseInt(quantity);
  429. this.properties = this.#makeProperties(full_type);
  430. if (this.category === ItemCategory.ITEM) {
  431. this.subcategory = this.#itemSubcategory(this.base_type);
  432. if (this.subcategory === ItemSubcategory.CLOTHING) {
  433. this.properties.set("clothing_type", Item.#global_data[this.base_type].clothingtype);
  434. }
  435. }
  436. if (this.properties.has("cooked") && this.properties.get("cooked")) {
  437. this.base_name = name.substring(0, 6) === "Cooked" ? name.substring(7) : name;
  438. this.full_name = "Cooked " + this.base_name;
  439. } else if (this.properties.has("colour")) {
  440. this.full_name = this.properties.get("colour") + " " + this.base_name;
  441. }
  442. }
  443. }
  444.  
  445. class ItemMarketEntry {
  446. constructor(item, price, trade_id, member_id, member_name, tradezone) {
  447. this.item = item;
  448. this.price = parseInt(price);
  449. this.trade_id = parseInt(trade_id)
  450. this.member_id = parseInt(member_id);
  451. this.member_name = member_name;
  452. this.tradezone = parseInt(tradezone);
  453. }
  454. }
  455.  
  456. class ItemSellingEntry {
  457. constructor(market_entry, member_to_id, member_to_name) {
  458. this.market_entry = market_entry;
  459. this.member_to_id = parseInt(member_to_id);
  460. this.member_to_name = member_to_name;
  461. }
  462. }
  463.  
  464. class Service {
  465. constructor(service_type, level) {
  466. this.service_type = service_type;
  467. this.level = parseInt(level);
  468. }
  469. }
  470.  
  471. class ServiceMarketEntry {
  472. constructor(service, price, member_id, member_name, tradezone) {
  473. this.service = service;
  474. this.price = parseInt(price);
  475. this.member_id = parseInt(member_id);
  476. this.member_name = member_name;
  477. this.tradezone = tradezone;
  478. }
  479. }
  480.  
  481. // GlobalData
  482.  
  483. // class GlobalData {
  484. // #setupPlayerValuesWebcallParameters() {
  485. // const parameters = {};
  486. // const uservars = userVars;
  487. // parameters["userID"] = uservars["userID"];
  488. // parameters["password"] = uservars["password"];
  489. // parameters["sc"] = uservars["sc"];
  490. // parameters["template_ID"] = "";
  491. // console.debug(parameters);
  492. // return parameters;
  493. // }
  494. //
  495. // #_data;
  496. // #_requests_out;
  497. // constructor() {
  498. // this.#_requests_out = 0;
  499. // }
  500. //
  501. // request() {
  502. // const instance = this;
  503. // const promise = new Promise(function(resolve, reject) {
  504. // instance.#_requests_out += 1;
  505. // const parameters = instance.#setupPlayerValuesWebcallParameters();
  506. // const data = {};
  507. // webCall("itemspawn", data);
  508. // setTimeout(function() {
  509. // console.debug(data);
  510. // }, 5000);
  511. // // webCall("itemspawn", parameters, function(data) {
  512. // // instance.#_data = responseToMap(data);
  513. // // instance.#_requests_out -= 1;
  514. // // resolve(instance);
  515. // // });
  516. // });
  517. // return promise;
  518. // }
  519. // }
  520.  
  521.  
  522. // PlayerValues
  523.  
  524. class PlayerValues {
  525.  
  526. #setupPlayerValuesWebcallParameters() {
  527. const parameters = {};
  528. const uservars = userVars;
  529. parameters["userID"] = uservars["userID"];
  530. parameters["password"] = uservars["password"];
  531. parameters["sc"] = uservars["sc"];
  532. return parameters;
  533. }
  534.  
  535. #query(str) {
  536. return this.#_data.get(str);
  537. }
  538.  
  539. #_data;
  540. #_requests_out;
  541. constructor() {
  542. this.#_requests_out = 0;
  543. }
  544.  
  545. get data() {
  546. return this.#_data;
  547. }
  548.  
  549. get member_id() {
  550. return this.#query("id_member");
  551. }
  552.  
  553. get name() {
  554. return this.#query("df_name");
  555. }
  556.  
  557. get gender() {
  558. return this.#query("df_gender");
  559. }
  560.  
  561. get rank() {
  562. return this.#query("df_rank");
  563. }
  564.  
  565. get profession() {
  566. return this.#query("df_profession");
  567. }
  568.  
  569. get level() {
  570. return this.#query("df_level");
  571. }
  572.  
  573. get dead() {
  574. return this.#query("df_dead") === "1";
  575. }
  576.  
  577. get cash() {
  578. return parseInt(this.#query("df_cash"));
  579. }
  580.  
  581. get bank() {
  582. return parseInt(this.#query("df_bankcash"));
  583. }
  584.  
  585. get credits() {
  586. return parseInt(this.#query("df_credits"));
  587. }
  588.  
  589. get gold_member() {
  590. return this.#query("df_goldmember") === "1"
  591. }
  592.  
  593. get max_hp() {
  594. return parseInt(this.#query("df_hpmax"));
  595. }
  596.  
  597. get current_hp() {
  598. return parseInt(this.#query("df_hpcurrent"));
  599. }
  600.  
  601. get current_hunger() {
  602. return parseInt(this.#query("df_hungerhp"));
  603. }
  604.  
  605. get x() {
  606. return parseInt(this.#query("df_positionx"));
  607. }
  608.  
  609. get y() {
  610. return parseInt(this.#query("df_positiony"));
  611. }
  612.  
  613. get stats() {
  614. return {
  615. strength: parseInt(this.#query("df_strength")),
  616. accuracy: parseInt(this.#query("df_accuracy")),
  617. agility: parseInt(this.#query("df_agility")),
  618. endurance: parseInt(this.#query("df_endurance")),
  619. critical_hit: parseInt(this.#query("df_criticalhit")),
  620. reloading: parseInt(this.#query("df_reloading"))
  621. };
  622. }
  623.  
  624. get proficiencies() {
  625. return {
  626. melee: parseInt(this.#query("df_promelee")),
  627. pistols: parseInt(this.#query("df_propistol")),
  628. rifles: parseInt(this.#query("df_prorifle")),
  629. shotguns: parseInt(this.#query("df_proshotgun")),
  630. machine_guns: parseInt(this.#query("df_promachinegun")),
  631. explosives: parseInt(this.#query("df_proexplosive"))
  632. };
  633. }
  634.  
  635. get ammo() {
  636. return {
  637. // Shotgun ammo
  638. "10gaugeammo": parseInt(this.#query("df_10gaugeammo")),
  639. "12gaugeammo": parseInt(this.#query("df_12gaugeammo")),
  640. "16gaugeammo": parseInt(this.#query("df_16gaugeammo")),
  641. "20gaugeammo": parseInt(this.#query("df_20gaugeammo")),
  642. // Handgun ammo
  643. "32ammo": parseInt(this.#query("df_32ammo")),
  644. "357ammo": parseInt(this.#query("df_357ammo")),
  645. "35ammo": parseInt(this.#query("df_35ammo")),
  646. "38ammo": parseInt(this.#query("df_38ammo")),
  647. "40ammo": parseInt(this.#query("df_40ammo")),
  648. "45ammo": parseInt(this.#query("df_45ammo")),
  649. "50ammo": parseInt(this.#query("df_50ammo")),
  650. "55ammo": parseInt(this.#query("df_55ammo")),
  651. // Rifle ammo
  652. "55rifleammo": parseInt(this.#query("df_55rifleammo")),
  653. "75rifleammo": parseInt(this.#query("df_75rifleammo")),
  654. "9rifleammo": parseInt(this.#query("df_9rifleammo")),
  655. "127rifleammo": parseInt(this.#query("df_127rifleammo")),
  656. "14rifleammo": parseInt(this.#query("df_14rifleammo")),
  657. // Grenade ammo
  658. "grenadeammo": parseInt(this.#query("df_grenadeammo")),
  659. "heavygrenadeammo": parseInt(this.#query("df_heavygrenadeammo"))
  660. };
  661. }
  662.  
  663. get tradezone() {
  664. return parseInt(this.#query("df_tradezone"));
  665. }
  666.  
  667. get account_name() {
  668. return this.#query("account_name");
  669. }
  670.  
  671. request() {
  672. const instance = this;
  673. const promise = new Promise(function(resolve, reject) {
  674. instance.#_requests_out += 1;
  675. const parameters = instance.#setupPlayerValuesWebcallParameters();
  676. webCall("get_values", parameters, function(data) {
  677. instance.#_data = responseToMap(data);
  678. instance.#_requests_out -= 1;
  679. resolve(instance);
  680. });
  681. });
  682. return promise;
  683. }
  684. }
  685.  
  686.  
  687. // PlayerItems
  688. // The PlayerItems class is responsible for storing item data and item movement (equipment, storage).
  689. // TODO: Add check for global data availability for all items (inventory, equipment).
  690. class PlayerItems {
  691. static #global_data = globalData;
  692.  
  693. #fulltypeQuantityFromInventorySlot(slot) {
  694. // Returns item type and item quantity from slot.
  695. const uservars = this.#_uservars;
  696. const nslots = parseInt(uservars["DFSTATS_df_invslots"]);
  697. if (slot > nslots || slot < 1) {
  698. throw new RangeError("Slot: " + slot.toString() + " out of range of inventory slots.");
  699. }
  700. const type_key = "DFSTATS_df_inv" + slot.toString() + "_type";
  701. const quantity_key = "DFSTATS_df_inv" + slot.toString() + "_quantity";
  702. return [uservars[type_key], parseInt(uservars[quantity_key])];
  703. }
  704.  
  705. #fulltypeQuantityFromStorageSlot(slot) {
  706. const uservars = this.#_uservars;
  707. const storage = this.#_storage_data;
  708. const nslots = parseInt(uservars["DFSTATS_df_storage_slots"]);
  709. if (slot > nslots || slot < 1) {
  710. throw new RangeError("Slot: " + slot.toString() + " out of range of storage slots.");
  711. }
  712. const type_key = "df_store" + slot.toString() + "_type";
  713. const quantity_key = "df_store" + slot.toString() + "_quantity";
  714. return [storage.get(type_key), parseInt(storage.get(quantity_key))];
  715. }
  716.  
  717. #nBackpackSlots() {
  718. // Returns number of backpack slots available.
  719. const uservars = this.#_uservars;
  720. const globaldata = PlayerItems.#global_data;
  721. const full_type = uservars["DFSTATS_df_backpack"];
  722. if (full_type === "") {
  723. return 0;
  724. }
  725. const base_type = typeSplit(uservars["DFSTATS_df_backpack"])[0];
  726. const backpack = new Item(full_type, globaldata[base_type].name, 1);
  727. return backpack.properties.get("total_slots");
  728. }
  729.  
  730. #fulltypeQuantityFromBackpackSlot(slot) {
  731. // Returns item type and item quantity from slot.
  732. const uservars = this.#_uservars;
  733. const nslots = this.#nBackpackSlots();
  734. if (slot > nslots || slot < 1) {
  735. throw new RangeError("Slot: " + slot.toString() + " out of range of backpack slots.");
  736. }
  737. const type_key = "DFSTATS_df_backpack" + slot.toString() + "_type";
  738. const quantity_key = "DFSTATS_df_backpack" + slot.toString() + "_quantity";
  739. return type_key in uservars ? [uservars[type_key], parseInt(uservars[quantity_key])] : ["", ""];
  740. }
  741.  
  742. #setupStorageWebcallParameters() {
  743. const parameters = {};
  744. const uservars = this.#_uservars;
  745. parameters["userID"] = uservars["userID"];
  746. parameters["password"] = uservars["password"];
  747. parameters["sc"] = uservars["sc"];
  748. parameters["pagetime"] = uservars["pagetime"];
  749. return parameters;
  750. }
  751.  
  752. #typeFromImplantSlot(slot) {
  753. const uservars = this.#_uservars;
  754. const nslots = parseInt(uservars["DFSTATS_df_implantslots"]);
  755. if (slot > nslots || slot < 1) {
  756. throw new RangeError("Slot: " + slot.toString() + " out of range of implant slots.");
  757. }
  758. const type_key = "DFSTATS_df_implant" + slot.toString() + "_type";
  759. return uservars[type_key];
  760. }
  761.  
  762. #typeInGlobalData(type) {
  763. return type === "" || type in PlayerItems.#global_data;
  764. }
  765.  
  766. #_uservars;
  767. #_storage_data;
  768. constructor() {
  769. this.#_uservars = userVars;
  770. }
  771.  
  772. // Availability Checks
  773.  
  774. inventoryAvailable() {
  775. // Check for userstats keys.
  776. const inventory_available = this.#_uservars["DFSTATS_df_invslots"] !== undefined;
  777. if (!inventory_available) {
  778. return false;
  779. }
  780. // Check for global data keys.
  781. const nslots = parseInt(this.#_uservars["DFSTATS_df_invslots"]);
  782. for (let i = 1; i <= nslots; i++) {
  783. const [full_type, quantity] = this.#fulltypeQuantityFromInventorySlot(i);
  784. const base_type = typeSplit(full_type)[0];
  785. if (!this.#typeInGlobalData(base_type)) {
  786. return false;
  787. }
  788. }
  789. return true;
  790. }
  791.  
  792. equipmentAvailable() {
  793. // TODO: Check for clothing in global data.
  794. const uservars = this.#_uservars;
  795. const global_data = PlayerItems.#global_data;
  796. const implants_available = uservars["DFSTATS_df_implantslots"] !== undefined;
  797. const weapons_available = uservars["DFSTATS_df_weapon1type"] !== undefined;
  798. const armour_available = uservars["DFSTATS_df_armourtype"] !== undefined;
  799. const implants_nslots = parseInt(this.#_uservars["DFSTATS_df_implantslots"]);
  800. for (let i = 1; i <= implants_nslots; i++) {
  801. const implant_type = typeFromImplantSlot(i);
  802. if (!this.#typeInGlobalData(implant_type)) {
  803. return false;
  804. }
  805. }
  806. const weapons_in_globaldata = this.#typeInGlobalData(uservars["DFSTATS_df_weapon1type"]) && this.#typeInGlobalData(uservars["DFSTATS_df_weapon2type"]) && this.#typeInGlobalData(uservars["DFSTATS_df_weapon3type"]);
  807. if (!weapons_in_globaldata) {
  808. return false;
  809. }
  810. const armour_in_global_data = this.#typeInGlobalData(uservars["DFSTATS_df_armourtype"]);
  811. if (!armour_in_global_data) {
  812. return false;
  813. }
  814. return true;
  815. }
  816.  
  817. storageAvailable() {
  818. const uservars_available = this.#_uservars !== undefined;
  819. return uservars_available;
  820. }
  821.  
  822. // Inventory Queries
  823.  
  824. itemFromInventorySlot(slot) {
  825. const [full_type, quantity] = this.#fulltypeQuantityFromInventorySlot(slot);
  826. if (full_type === "") {
  827. return undefined;
  828. }
  829. const base_type = typeSplit(full_type)[0];
  830. const data = PlayerItems.#global_data[base_type]
  831. const name = data.name;
  832. const item = new Item(full_type, name, quantity);
  833. // Adding in durability properties for armour.
  834. if (item.category === ItemCategory.ARMOUR) {
  835. item.properties.set("durability", quantity);
  836. item.properties.set("max_durability", parseInt(data.hp));
  837. }
  838. // Adding in cooked property for food.
  839. if (item.category === ItemCategory.ITEM && item.subcategory === ItemSubcategory.FOOD) {
  840. const is_cooked = item.properties.has("cooked") && item.properties.get("cooked") === true;
  841. if (!is_cooked) {
  842. item.properties.set("cooked", false);
  843. }
  844. }
  845. // Adding in max quantity property for ammunition.
  846. if (item.category === ItemCategory.AMMO) {
  847. item.properties.set("max_quantity", parseInt(data.max_quantity));
  848. }
  849. return item;
  850. }
  851.  
  852. inventory(slot) {
  853. return this.itemFromInventorySlot(slot);
  854. }
  855.  
  856. *inventorySlots() {
  857. // Generator that iterates through slots and yields [slot, Item].
  858. const uservars = this.#_uservars;
  859. const nslots = parseInt(uservars["DFSTATS_df_invslots"]);
  860. for (let slot = 1; slot <= nslots; slot++) {
  861. const item = this.itemFromInventorySlot(slot);
  862. yield [slot, item];
  863. }
  864. }
  865.  
  866. *inventoryItems() {
  867. // Generator that iterates through filled slots and yields [slot, Item].
  868. for (const [slot, item] of this.inventorySlots()) {
  869. const slot_filled = item !== undefined;
  870. if (slot_filled) {
  871. yield [slot, item];
  872. }
  873. }
  874. }
  875.  
  876. isLockedSlot(slot) {
  877. return lockedSlots.includes(slot.toString());
  878. }
  879.  
  880. totalInventorySlots() {
  881. const uservars = this.#_uservars;
  882. const nslots = parseInt(uservars["DFSTATS_df_invslots"]);
  883. return nslots;
  884. }
  885.  
  886. fullInventorySlots() {
  887. return Array.from(this.inventorySlots()).filter((e) => e[1] !== undefined).length;
  888. }
  889.  
  890. emptyInventorySlots() {
  891. return Array.from(this.inventorySlots()).filter((e) => e[1] === undefined).length;
  892. }
  893.  
  894. // Backpack Queries
  895.  
  896. itemFromBackpackSlot(slot) {
  897. const [full_type, quantity] = this.#fulltypeQuantityFromBackpackSlot(slot);
  898. if (full_type === "") {
  899. return undefined;
  900. }
  901. const base_type = typeSplit(full_type)[0];
  902. const data = PlayerItems.#global_data[base_type]
  903. const name = data.name;
  904. const item = new Item(full_type, name, quantity);
  905. // Adding in durability properties for armour.
  906. if (item.category === ItemCategory.ARMOUR) {
  907. item.properties.set("durability", quantity);
  908. item.properties.set("max_durability", parseInt(data.hp));
  909. }
  910. // Adding in cooked property for food.
  911. if (item.category === ItemCategory.ITEM && item.subcategory === ItemSubcategory.FOOD) {
  912. const is_cooked = item.properties.has("cooked") && item.properties.get("cooked") === true;
  913. if (!is_cooked) {
  914. item.properties.set("cooked", false);
  915. }
  916. }
  917. // Adding in max quantity property for ammunition.
  918. if (item.category === ItemCategory.AMMO) {
  919. item.properties.set("max_quantity", parseInt(data.max_quantity));
  920. }
  921. return item;
  922. }
  923.  
  924. backpack(slot) {
  925. return this.itemFromBackpackSlot(slot);
  926. }
  927.  
  928. *backpackSlots() {
  929. // Generator that iterates through slots and yields [slot, Item].
  930. const uservars = this.#_uservars;
  931. const nslots = this.#nBackpackSlots();
  932. for (let slot = 1; slot <= nslots; slot++) {
  933. const item = this.itemFromBackpackSlot(slot);
  934. yield [slot, item];
  935. }
  936. }
  937.  
  938. *backpackItems() {
  939. // Generator that iterates through filled slots and yields [slot, Item].
  940. for (const [slot, item] of this.backpackSlots()) {
  941. const slot_filled = item !== undefined;
  942. if (slot_filled) {
  943. yield [slot, item];
  944. }
  945. }
  946. }
  947.  
  948. isLockedBackpackSlot(slot) {
  949. return lockedSlots.includes((slot + 1050).toString());
  950. }
  951.  
  952. totalBackpackSlots() {
  953. return this.#nBackpackSlots();
  954. }
  955.  
  956. fullBackpackSlots() {
  957. return Array.from(this.backpackSlots()).filter((e) => e[1] !== undefined).length;
  958. }
  959.  
  960. emptyBackpackSlots() {
  961. return Array.from(this.backpackSlots()).filter((e) => e[1] === undefined).length;
  962. }
  963.  
  964. // Implant Queries
  965.  
  966. itemFromImplantSlot(slot) {
  967. const type = this.#typeFromImplantSlot(slot);
  968. if (type === "") {
  969. return undefined;
  970. }
  971. const base_type = typeSplit(type)[0];
  972. const name = PlayerItems.#global_data[base_type].name;
  973. const quantity = 1;
  974. const item = new Item(type, name, quantity);
  975. return item;
  976. }
  977.  
  978. implant(slot) {
  979. return this.itemFromImplantSlot(slot);
  980. }
  981.  
  982. *implantSlots() {
  983. const uservars = this.#_uservars;
  984. const nslots = parseInt(uservars["DFSTATS_df_implantslots"]);
  985. for (let slot = 1; slot <= nslots; slot++) {
  986. const item = this.itemFromImplantSlot(slot);
  987. yield [slot, item];
  988. }
  989. }
  990.  
  991. *implantItems() {
  992. for (const [slot, item] of this.implantSlots()) {
  993. const slot_filled = item !== undefined;
  994. if (slot_filled) {
  995. yield [slot, item];
  996. }
  997. }
  998. }
  999.  
  1000. // Equipment Queries
  1001.  
  1002. armour() {
  1003. const uservars = this.#_uservars;
  1004. const full_type = uservars["DFSTATS_df_armourtype"];
  1005. if (full_type === "") {
  1006. return undefined;
  1007. }
  1008. const name = uservars["DFSTATS_df_armourname"];
  1009. const durability = parseInt(uservars["DFSTATS_df_armourhp"]);
  1010. const max_durability = parseInt(uservars["DFSTATS_df_armourhpmax"]);
  1011. const item = new Item(full_type, name, durability);
  1012. item.properties.set("durability", durability);
  1013. item.properties.set("max_durability", max_durability);
  1014. return item;
  1015. }
  1016.  
  1017. weapon(index) {
  1018. const uservars = this.#_uservars;
  1019. const i = index.toString();
  1020. const full_type = uservars["DFSTATS_df_weapon" + i + "type"];
  1021. if (full_type === "") {
  1022. return undefined;
  1023. }
  1024. const name = uservars["DFSTATS_df_weapon" + i + "name"];
  1025. const quantity = 1;
  1026. const item = new Item(full_type, name, quantity);
  1027. return item;
  1028. }
  1029.  
  1030. backpack() {
  1031. const uservars = this.#_uservars;
  1032. const full_type = uservars["DFSTATS_df_backpack"];
  1033. if (full_type === "") {
  1034. return undefined;
  1035. }
  1036. const base_type = typeSplit(full_type)[0];
  1037. const name = PlayerItems.#global_data[base_type].name;
  1038. const quantity = 1;
  1039. const item = new Item(full_type, name, quantity);
  1040. return item;
  1041. }
  1042.  
  1043. #cosmetic(key) {
  1044. const uservars = this.#_uservars;
  1045. const full_type = uservars[key];
  1046. if (full_type === "") {
  1047. return undefined;
  1048. }
  1049. const base_type = typeSplit(full_type)[0];
  1050. const name = PlayerItems.#global_data[base_type].name;
  1051. const quantity = 1;
  1052. const item = new Item(full_type, name, quantity);
  1053. return item;
  1054. }
  1055.  
  1056. hat() {
  1057. return this.#cosmetic("DFSTATS_df_avatar_hat");
  1058. }
  1059.  
  1060. mask() {
  1061. return this.#cosmetic("DFSTATS_df_avatar_mask");
  1062. }
  1063.  
  1064. coat() {
  1065. return this.#cosmetic("DFSTATS_df_avatar_coat");
  1066. }
  1067.  
  1068. shirt() {
  1069. return this.#cosmetic("DFSTATS_df_avatar_shirt");
  1070. }
  1071.  
  1072. trousers() {
  1073. return this.#cosmetic("DFSTATS_df_avatar_trousers");
  1074. }
  1075.  
  1076. // Storage Queries
  1077.  
  1078. requestStorage() {
  1079. // Before any storage queries, this function must be called, as well as after any changes are made to storage.
  1080. const instance = this;
  1081. const promise = new Promise(function(resolve, reject) {
  1082. const parameters = instance.#setupStorageWebcallParameters();
  1083. webCall("get_storage", parameters, function(data) {
  1084. storageBox = flshToArr(data); // Update website vars.
  1085. instance.#_storage_data = responseToMap(data);
  1086. resolve(instance);
  1087. }, true);
  1088. });
  1089. return promise;
  1090. }
  1091.  
  1092. itemFromStorageSlot(slot) {
  1093. const [full_type, quantity] = this.#fulltypeQuantityFromStorageSlot(slot);
  1094. if (full_type === undefined) {
  1095. return undefined;
  1096. }
  1097. const base_type = typeSplit(full_type)[0];
  1098. const data = PlayerItems.#global_data[base_type]
  1099. const name = data.name;
  1100. const item = new Item(full_type, name, quantity);
  1101. if (item.category === ItemCategory.ITEM && item.subcategory === ItemSubcategory.FOOD) {
  1102. const is_cooked = item.properties.has("cooked") && item.properties.get("cooked") === true;
  1103. if (!is_cooked) {
  1104. item.properties.set("cooked", false);
  1105. }
  1106. }
  1107. if (item.category === ItemCategory.AMMO) {
  1108. item.properties.set("max_quantity", parseInt(data.max_quantity));
  1109. }
  1110. return item;
  1111. }
  1112.  
  1113. storage(slot) {
  1114. return this.itemFromStorageSlot(slot);
  1115. }
  1116.  
  1117. *storageSlots() {
  1118. const uservars = this.#_uservars;
  1119. const nslots = parseInt(uservars["DFSTATS_df_storage_slots"]);
  1120. for (let slot = 1; slot <= nslots; slot++) {
  1121. const item = this.itemFromStorageSlot(slot);
  1122. yield [slot, item];
  1123. }
  1124. }
  1125.  
  1126. *storageItems() {
  1127. for (const [slot, item] of this.storageSlots()) {
  1128. const slot_filled = item !== undefined;
  1129. if (slot_filled) {
  1130. yield [slot, item];
  1131. }
  1132. }
  1133. }
  1134.  
  1135. // Move Operations
  1136.  
  1137. #setupMoveParameters() {
  1138. const parameters = {};
  1139. const uservars = this.#_uservars;
  1140. parameters["userID"] = uservars["userID"];
  1141. parameters["password"] = uservars["password"];
  1142. parameters["sc"] = uservars["sc"];
  1143. parameters["pagetime"] = uservars["pagetime"];
  1144. parameters["templateID"] = uservars["template_ID"];
  1145. parameters["creditsnum"] = this.#_uservars["DFSTATS_df_credits"];
  1146. return parameters;
  1147. }
  1148.  
  1149. #quantityIsMax(item) {
  1150. return item.quantity === item.properties.get("max_quantity");
  1151. }
  1152.  
  1153. #moveResult(item1, item2_init) {
  1154. // Returns result of moving item1 into item2. item2 can be undefined.
  1155. const item2 = item2_init === undefined ? new Item(item1.full_type, item1.base_name, 0) : item2_init; // Dummy item if item2 is undefined.
  1156. const both_stackable = isStackable(item1) && isStackable(item2);
  1157. const same_type = item1.full_type === item2.full_type;
  1158. if (both_stackable && same_type) {
  1159. const max = item1.properties.get("max_quantity");
  1160. const sum = item1.quantity + item2.quantity;
  1161. const item_stacking = item1.quantity < max && item2.quantity < max;
  1162. if (item_stacking) {
  1163. if (sum > max) {
  1164. // item2 slot becomes fully stacked, item1 slot gets remainder.
  1165. const diff = sum - max;
  1166. const item1_new = new Item(item1.full_type, item1.base_name, diff);
  1167. const item2_new = new Item(item2.full_type, item2.base_name, max);
  1168. return [item1_new, item2_new];
  1169. } else {
  1170. // item2 slot gets full quantity, item1 slot is empty.
  1171. const item1_new = undefined;
  1172. const item2_new = new Item(item2.full_type, item2.base_name, sum);
  1173. return [item1_new, item2_new];
  1174. }
  1175. }
  1176. }
  1177. // Swap items.
  1178. return [item2, item1];
  1179. }
  1180.  
  1181. #storageArrayFromSlotItem(slot, item) {
  1182. const delta = {};
  1183. delta["df_store" + slot.toString() + "_type"] = item.full_type;
  1184. delta["df_store" + slot.toString() + "_quantity"] = item.quantity.toString();
  1185. return delta;
  1186. }
  1187.  
  1188. inventoryToInventory(primary_slot, secondary_slot, update_ui = UiUpdate.YES) {
  1189. const instance = this;
  1190. const promise = new Promise(function(resolve, reject) {
  1191. const parameters = instance.#setupMoveParameters();
  1192. parameters["action"] = "newswap";
  1193. const primary_item = instance.itemFromInventorySlot(primary_slot);
  1194. const secondary_item = instance.itemFromInventorySlot(secondary_slot);
  1195. parameters["itemnum"] = primary_slot.toString();
  1196. parameters["itemnum2"] = secondary_slot.toString();
  1197. parameters["expected_itemtype"] = primary_item !== undefined ? primary_item.full_type : "";
  1198. parameters["expected_itemtype2"] = secondary_item !== undefined ? secondary_item.full_type : "";
  1199. webCall("inventory_new", parameters, function(data)
  1200. {
  1201. updateIntoArr(flshToArr(data, "DFSTATS_"), userVars);
  1202. if (update_ui === UiUpdate.YES) {
  1203. populateInventory();
  1204. updateAllFields();
  1205. }
  1206. resolve(instance);
  1207. }, true);
  1208. });
  1209. return promise;
  1210. }
  1211.  
  1212. inventoryToImplants(inventory_slot, implant_slot, update_ui = UiUpdate.YES) {
  1213. const instance = this;
  1214. const promise = new Promise(function(resolve, reject) {
  1215. const parameters = instance.#setupMoveParameters();
  1216. parameters["action"] = "newswap";
  1217. const inventory_item = instance.itemFromInventorySlot(inventory_slot);
  1218. if (inventory_item !== undefined && (inventory_item.category !== ItemCategory.ITEM || inventory_item.subcategory !== ItemSubcategory.IMPLANT)) {
  1219. throw new TypeError("Inventory item: " + inventory_item.full_type + " is not an implant.");
  1220. }
  1221. const implant_item = instance.itemFromImplantSlot(implant_slot);
  1222. parameters["itemnum"] = inventory_slot.toString();
  1223. parameters["itemnum2"] = (implant_slot + 1000).toString();
  1224. parameters["expected_itemtype"] = inventory_item !== undefined ? inventory_item.full_type : "";
  1225. parameters["expected_itemtype2"] = implant_item !== undefined ? implant_item.full_type : "";
  1226. webCall("inventory_new", parameters, function(data) {
  1227. updateIntoArr(flshToArr(data, "DFSTATS_"), userVars);
  1228. if (update_ui === UiUpdate.YES) {
  1229. populateInventory();
  1230. populateImplants();
  1231. updateAllFields();
  1232. }
  1233. resolve(instance);
  1234. }, true);
  1235. });
  1236. return promise;
  1237. }
  1238.  
  1239. #inventoryToEquipment(inventory_slot, equipment_slot, get_equipment, error_if_false, equipment_slot_name = "given", update_ui = UiUpdate.YES) {
  1240. const instance = this;
  1241. const promise = new Promise(function(resolve, reject) {
  1242. const parameters = instance.#setupMoveParameters();
  1243. parameters["action"] = "newequip";
  1244. const inventory_item = instance.itemFromInventorySlot(inventory_slot);
  1245. if (!error_if_false(inventory_item)) {
  1246. throw new TypeError("Inventory item: " + inventory_item.full_type + " cannot be placed in the " + equipment_slot_name + " slot.");
  1247. }
  1248. const equipment_item = get_equipment();
  1249. parameters["itemnum"] = inventory_slot.toString();
  1250. parameters["itemnum2"] = equipment_slot.toString();
  1251. parameters["expected_itemtype"] = inventory_item !== undefined ? inventory_item.full_type : "";
  1252. parameters["expected_itemtype2"] = equipment_item !== undefined ? equipment_item.full_type : "";
  1253. webCall("inventory_new", parameters, function(data) {
  1254. updateIntoArr(flshToArr(data, "DFSTATS_"), userVars);
  1255. if (update_ui === UiUpdate.YES) {
  1256. $.each($(".characterRender"), function(key, val)
  1257. {
  1258. renderAvatarUpdate(val, userVars);
  1259. });
  1260. populateInventory();
  1261. populateCharacterInventory();
  1262. updateAllFields();
  1263. renderAvatarUpdate();
  1264. }
  1265. resolve(instance)
  1266. }, true);
  1267. });
  1268. return promise;
  1269. }
  1270.  
  1271. inventoryToArmour(inventory_slot, update_ui = UiUpdate.YES) {
  1272. const armour_slot = 34
  1273. const error_if_false = (inventory_item) => inventory_item === undefined || inventory_item.category === ItemCategory.ARMOUR;
  1274. const armour = () => this.armour();
  1275. return this.#inventoryToEquipment(inventory_slot, armour_slot, armour, error_if_false, "armour", update_ui);
  1276. }
  1277.  
  1278. inventoryToWeapon(inventory_slot, weapon_slot, update_ui = UiUpdate.YES) {
  1279. const adjusted_weapon_slot = weapon_slot + 30;
  1280. const error_if_false = (inventory_item) => inventory_item === undefined || inventory_item.category === ItemCategory.WEAPON;
  1281. const weapon = () => this.weapon(weapon_slot);
  1282. return this.#inventoryToEquipment(inventory_slot, adjusted_weapon_slot, weapon, error_if_false, "weapon", update_ui);
  1283. }
  1284.  
  1285. inventoryToHat(inventory_slot, update_ui = UiUpdate.YES) {
  1286. const hat_slot = 40;
  1287. const error_if_false = (inventory_item) => inventory_item === undefined || (inventory_item.category === ItemCategory.ITEM && inventory_item.subcategory === ItemSubcategory.CLOTHING && inventory_item.properties.get("clothing_type") === "hat");
  1288. const hat = () => this.hat();
  1289. return this.#inventoryToEquipment(inventory_slot, hat_slot, hat, error_if_false, "hat", update_ui);
  1290. }
  1291.  
  1292. inventoryToMask(inventory_slot, update_ui = UiUpdate.YES) {
  1293. const mask_slot = 39;
  1294. const error_if_false = (inventory_item) => inventory_item === undefined || (inventory_item.category === ItemCategory.ITEM && inventory_item.subcategory === ItemSubcategory.CLOTHING && inventory_item.properties.get("clothing_type") === "mask");
  1295. const mask = () => this.mask();
  1296. return this.#inventoryToEquipment(inventory_slot, mask_slot, mask, error_if_false, "mask", update_ui);
  1297. }
  1298.  
  1299. inventoryToCoat(inventory_slot, update_ui = UiUpdate.YES) {
  1300. const coat_slot = 38;
  1301. const error_if_false = (inventory_item) => inventory_item === undefined || (inventory_item.category === ItemCategory.ITEM && inventory_item.subcategory === ItemSubcategory.CLOTHING && inventory_item.properties.get("clothing_type") === "coat");
  1302. const coat = () => this.coat();
  1303. return this.#inventoryToEquipment(inventory_slot, coat_slot, coat, error_if_false, "coat", update_ui);
  1304. }
  1305.  
  1306. inventoryToShirt(inventory_slot, update_ui = UiUpdate.YES) {
  1307. const shirt_slot = 36;
  1308. const error_if_false = (inventory_item) => inventory_item === undefined || (inventory_item.category === ItemCategory.ITEM && inventory_item.subcategory === ItemSubcategory.CLOTHING && inventory_item.properties.get("clothing_type") === "shirt");
  1309. const shirt = () => this.shirt();
  1310. return this.#inventoryToEquipment(inventory_slot, shirt_slot, shirt, error_if_false, "shirt", update_ui);
  1311. }
  1312.  
  1313. inventoryToTrousers(inventory_slot, update_ui = UiUpdate.YES) {
  1314. const trousers_slot = 37;
  1315. const error_if_false = (inventory_item) => inventory_item === undefined || (inventory_item.category === ItemCategory.ITEM && inventory_item.subcategory === ItemSubcategory.CLOTHING && inventory_item.properties.get("clothing_type") === "trousers");
  1316. const trousers = () => this.trousers();
  1317. return this.#inventoryToEquipment(inventory_slot, trousers_slot, trousers, error_if_false, "trousers", update_ui);
  1318. }
  1319.  
  1320. inventoryToStorage(inventory_slot, storage_slot, update_ui = UiUpdate.YES) {
  1321. const instance = this;
  1322. const promise = new Promise(function(resolve, reject) {
  1323. const parameters = instance.#setupMoveParameters();
  1324. parameters["action"] = "store";
  1325. const inventory_item = instance.itemFromInventorySlot(inventory_slot);
  1326. const storage_item = instance.itemFromStorageSlot(storage_slot);
  1327. parameters["itemnum"] = inventory_slot.toString();
  1328. parameters["itemnum2"] = (storage_slot + 40).toString();
  1329. parameters["expected_itemtype"] = inventory_item !== undefined ? inventory_item.full_type : "";
  1330. parameters["expected_itemtype2"] = storage_item !== undefined ? storage_item.full_type : "";
  1331. webCall("inventory_new", parameters, function(data)
  1332. {
  1333. updateIntoArr(flshToArr(data, "DFSTATS_"), userVars);
  1334. const [inventory_new, storage_new] = instance.#moveResult(inventory_item, storage_item);
  1335. if (storage_new !== undefined) {
  1336. updateIntoArr(instance.#storageArrayFromSlotItem(storage_slot, storage_new), storageBox);
  1337. } else {
  1338. delete storageBox["df_store" + storage_slot.toString() + "_type"];
  1339. delete storageBox["df_store" + storage_slot.toString() + "_quantity"];
  1340. }
  1341. if (update_ui) {
  1342. populateStorage();
  1343. populateInventory();
  1344. updateAllFields();
  1345. }
  1346. resolve(instance);
  1347. }, true);
  1348. });
  1349. return promise;
  1350. }
  1351.  
  1352. storageToInventory(storage_slot, inventory_slot, update_ui = UiUpdate.YES) {
  1353. // Slightly different because the first item in the inventory_new webCall must be defined, and the final
  1354. // item/stack will end up in inventory. i.e. If there is no item in the inventory slot for inventoryToStorage,
  1355. // it will not do anything. (Also has a different action type.)
  1356. const instance = this;
  1357. const promise = new Promise(function(resolve, reject) {
  1358. const parameters = instance.#setupMoveParameters();
  1359. parameters["action"] = "take";
  1360. const inventory_item = instance.itemFromInventorySlot(inventory_slot);
  1361. const storage_item = instance.itemFromStorageSlot(storage_slot);
  1362. parameters["itemnum"] = (storage_slot + 40).toString();
  1363. parameters["itemnum2"] = inventory_slot.toString();
  1364. parameters["expected_itemtype"] = storage_item !== undefined ? storage_item.full_type : "";
  1365. parameters["expected_itemtype2"] = inventory_item !== undefined ? inventory_item.full_type : "";
  1366. webCall("inventory_new", parameters, function(data)
  1367. {
  1368. updateIntoArr(flshToArr(data, "DFSTATS_"), userVars);
  1369. const [storage_new, inventory_new] = instance.#moveResult(storage_item, inventory_item);
  1370. if (storage_new !== undefined) {
  1371. updateIntoArr(instance.#storageArrayFromSlotItem(storage_slot, storage_new), storageBox);
  1372. } else {
  1373. delete storageBox["df_store" + storage_slot.toString() + "_type"];
  1374. delete storageBox["df_store" + storage_slot.toString() + "_quantity"];
  1375. }
  1376. if (update_ui) {
  1377. populateStorage();
  1378. populateInventory();
  1379. updateAllFields();
  1380. }
  1381. resolve(instance);
  1382. }, true);
  1383. });
  1384. return promise;
  1385. }
  1386.  
  1387. inventoryToBackpack(inventory_slot, backpack_slot, update_ui = UiUpdate.YES) {
  1388. const instance = this;
  1389. const promise = new Promise(function(resolve, reject) {
  1390. const parameters = instance.#setupMoveParameters();
  1391. parameters["action"] = "backpack";
  1392. const inventory_item = instance.itemFromInventorySlot(inventory_slot);
  1393. const backpack_item = instance.itemFromBackpackSlot(backpack_slot);
  1394. parameters["itemnum"] = inventory_slot.toString();
  1395. parameters["itemnum2"] = (backpack_slot + 1050).toString();
  1396. parameters["expected_itemtype"] = inventory_item !== undefined ? inventory_item.full_type : "";
  1397. parameters["expected_itemtype2"] = backpack_item !== undefined ? backpack_item.full_type : "";
  1398. webCall("hotrods/backpack", parameters, function(data)
  1399. {
  1400. updateIntoArr(flshToArr(data, "DFSTATS_"), userVars);
  1401. if (update_ui) {
  1402. populateBackpack();
  1403. populateInventory();
  1404. updateAllFields();
  1405. }
  1406. resolve(instance);
  1407. }, true);
  1408. });
  1409. return promise;
  1410. }
  1411.  
  1412. backpackToInventory(backpack_slot, inventory_slot, update_ui = UiUpdate.YES) {
  1413. const instance = this;
  1414. const promise = new Promise(function(resolve, reject) {
  1415. const parameters = instance.#setupMoveParameters();
  1416. parameters["action"] = "backpack";
  1417. const inventory_item = instance.itemFromInventorySlot(inventory_slot);
  1418. const backpack_item = instance.itemFromBackpackSlot(backpack_slot);
  1419. parameters["itemnum"] = (backpack_slot + 1050).toString();
  1420. parameters["itemnum2"] = inventory_slot.toString();
  1421. parameters["expected_itemtype"] = backpack_item !== undefined ? backpack_item.full_type : "";
  1422. parameters["expected_itemtype2"] = inventory_item !== undefined ? inventory_item.full_type : "";
  1423. webCall("hotrods/backpack", parameters, function(data)
  1424. {
  1425. updateIntoArr(flshToArr(data, "DFSTATS_"), userVars);
  1426. if (update_ui) {
  1427. populateBackpack();
  1428. populateInventory();
  1429. updateAllFields();
  1430. }
  1431. resolve(instance);
  1432. }, true);
  1433. });
  1434. return promise;
  1435. }
  1436.  
  1437. backpackToImplants(backpack_slot, implant_slot, update_ui = UiUpdate.YES) {
  1438. const instance = this;
  1439. const promise = new Promise(function(resolve, reject) {
  1440. const parameters = instance.#setupMoveParameters();
  1441. parameters["action"] = "backpack";
  1442. const implant_item = instance.itemFromImplantSlot(implant_slot);
  1443. const backpack_item = instance.itemFromBackpackSlot(backpack_slot);
  1444. parameters["itemnum"] = (backpack_slot + 1050).toString();
  1445. parameters["itemnum2"] = (implant_slot + 1000).toString();
  1446. parameters["expected_itemtype"] = backpack_item !== undefined ? backpack_item.full_type : "";
  1447. parameters["expected_itemtype2"] = implant_item !== undefined ? implant_item.full_type : "";
  1448. webCall("hotrods/backpack", parameters, function(data)
  1449. {
  1450. updateIntoArr(flshToArr(data, "DFSTATS_"), userVars);
  1451. if (update_ui) {
  1452. populateBackpack();
  1453. populateImplants();
  1454. updateAllFields();
  1455. }
  1456. resolve(instance);
  1457. }, true);
  1458. });
  1459. return promise;
  1460. }
  1461.  
  1462. implantsToImplants(primary_slot, secondary_slot, update_ui = UiUpdate.YES) {
  1463. const instance = this;
  1464. const promise = new Promise(function(resolve, reject) {
  1465. const parameters = instance.#setupMoveParameters();
  1466. parameters["action"] = "newswap";
  1467. const primary_item = instance.itemFromImplantSlot(primary_slot);
  1468. const secondary_item = instance.itemFromImplantSlot(secondary_slot);
  1469. parameters["itemnum"] = (primary_slot + 1000).toString();
  1470. parameters["itemnum2"] = (secondary_slot + 1000).toString();
  1471. parameters["expected_itemtype"] = primary_item !== undefined ? primary_item.full_type : "";
  1472. parameters["expected_itemtype2"] = secondary_item !== undefined ? secondary_item.full_type : "";
  1473. parameters["expected_itemprice"] = "-1";
  1474. parameters["buynum"] = "0";
  1475. parameters["price"] = "2400000";
  1476. parameters["renameto"] = "undefined`undefined";
  1477. webCall("hotrods/inventory_new", parameters, function(data)
  1478. {
  1479. updateIntoArr(flshToArr(data, "DFSTATS_"), userVars);
  1480. if (update_ui) {
  1481. populateImplants();
  1482. updateAllFields();
  1483. }
  1484. resolve(instance);
  1485. }, true);
  1486. });
  1487. return promise;
  1488. }
  1489.  
  1490. // Usage, Discard, Scrap, Mastercraft
  1491.  
  1492. #setupItemScrapRequest() {
  1493. const parameters = {};
  1494. parameters["pagetime"] = this.#_uservars["pagetime"];
  1495. parameters["templateID"] = this.#_uservars["template_ID"];
  1496. parameters["sc"] = this.#_uservars["sc"];
  1497. parameters["creditsnum"] = 0;
  1498. parameters["buynum"] = 0;
  1499. parameters["renameto"] = "";
  1500. parameters["expected_itemprice"] = "-1";
  1501. parameters["expected_itemtype2"] = "";
  1502. parameters["itemnum2"] = "0";
  1503. parameters["userID"] = this.#_uservars["userID"];
  1504. parameters["password"] = this.#_uservars["password"];
  1505. return parameters;
  1506. }
  1507.  
  1508. scrapInventoryItem(slot, inventory_item, update_ui = UiUpdate.YES) {
  1509. const instance = this;
  1510. const promise = new Promise(function(resolve, reject) {
  1511. const scrap_value = scrapValue(inventory_item.full_type, inventory_item.quantity);
  1512. const parameters = instance.#setupItemRemovalRequest();
  1513. parameters["action"] = "scrap";
  1514. parameters["price"] = scrap_value;
  1515. parameters["itemnum"] = slot.toString();
  1516. parameters["expected_itemtype"] = inventory_item.full_type;
  1517. webCall("inventory_new", parameters, function(data) {
  1518. updateIntoArr(flshToArr(data, "DFSTATS_"), userVars);
  1519. if (update_ui === UiUpdate.YES) {
  1520. populateInventory();
  1521. populateCharacterInventory();
  1522. updateAllFields();
  1523. renderAvatarUpdate();
  1524. const cash = instance.#_uservars["DFSTATS_df_cash"];
  1525. const bank = instance.#_uservars["DFSTATS_df_bankcash"];
  1526. updateCashBank(cash, bank);
  1527. }
  1528. resolve(instance);
  1529. }, true);
  1530. });
  1531. return promise;
  1532. }
  1533.  
  1534. #setupItemMastercraftRequest() {
  1535. const parameters = {};
  1536. parameters["pagetime"] = this.#_uservars["pagetime"];
  1537. parameters["templateID"] = this.#_uservars["template_ID"];
  1538. parameters["sc"] = this.#_uservars["sc"];
  1539. parameters["creditsnum"] = 0;
  1540. parameters["buynum"] = 0;
  1541. parameters["renameto"] = "";
  1542. parameters["expected_itemprice"] = "-1";
  1543. parameters["expected_itemtype2"] = "";
  1544. parameters["itemnum2"] = "0";
  1545. parameters["userID"] = this.#_uservars["userID"];
  1546. parameters["password"] = this.#_uservars["password"];
  1547. return parameters;
  1548. }
  1549.  
  1550. mastercraftInventoryItem(slot, inventory_item, update_ui = UiUpdate.YES) {
  1551. const instance = this;
  1552. const promise = new Promise(function(resolve, reject) {
  1553. const enhance_value = enhanceValue(inventory_item.full_type);
  1554. const parameters = instance.#setupItemMastercraftRequest();
  1555. parameters["action"] = "enhance";
  1556. parameters["price"] = enhance_value;
  1557. parameters["itemnum"] = slot.toString();
  1558. parameters["expected_itemtype"] = inventory_item.full_type;
  1559. webCall("inventory_new", parameters, function(data) {
  1560. updateIntoArr(flshToArr(data, "DFSTATS_"), userVars);
  1561. if (update_ui === UiUpdate.YES) {
  1562. populateInventory();
  1563. populateCharacterInventory();
  1564. updateAllFields();
  1565. renderAvatarUpdate();
  1566. const cash = instance.#_uservars["DFSTATS_df_cash"];
  1567. const bank = instance.#_uservars["DFSTATS_df_bankcash"];
  1568. updateCashBank(cash, bank);
  1569. }
  1570. resolve(instance);
  1571. }, true);
  1572. });
  1573. return promise;
  1574. }
  1575.  
  1576. #setupItemRemovalRequest() {
  1577. const parameters = {};
  1578. parameters["pagetime"] = this.#_uservars["pagetime"];
  1579. parameters["templateID"] = this.#_uservars["template_ID"];
  1580. parameters["sc"] = this.#_uservars["sc"];
  1581. parameters["creditsnum"] = this.#_uservars["DFSTATS_df_credits"];
  1582. parameters["buynum"] = "0";
  1583. parameters["renameto"] = "undefined`undefined";
  1584. parameters["expected_itemprice"] = "-1";
  1585. parameters["price"] = getUpgradePrice();
  1586. parameters["userID"] = this.#_uservars["userID"];
  1587. parameters["password"] = this.#_uservars["password"];
  1588. return parameters;
  1589. }
  1590.  
  1591. #useActionType(item) {
  1592. const global_data = PlayerItems.#global_data;
  1593. const medicine = parseInt(global_data[item.base_type]["healthrestore"]) > 0;
  1594. const usable_gm_ticket = global_data[item.base_type]["gm_days"] && global_data[item.base_type]["gm_days"] !== "0";
  1595. const food = parseInt(global_data[item.base_type]["foodrestore"]) > 0;
  1596. const boost = parseInt(global_data[item.base_type]["boostdamagehours"]) > 0 || parseInt(global_data[item.base_type]["boostexphours"]) > 0 || parseInt(global_data[item.base_type]["boostspeedhours"]) > 0;
  1597. const story_item = global_data[item.base_type]["opencontents"] && global_data[item.base_type]["opencontents"].length > 0;
  1598. if (medicine || usable_gm_ticket) {
  1599. return "newuse";
  1600. } else if (food) {
  1601. return "newconsume";
  1602. } else if (boost) {
  1603. return "newboost";
  1604. } else if (story_item) {
  1605. return "newopen";
  1606. } else {
  1607. return false;
  1608. }
  1609. }
  1610.  
  1611. #actionNecessary(item) {
  1612. const global_data = PlayerItems.#global_data;
  1613. const medicine = parseInt(global_data[item.base_type]["healthrestore"]) > 0;
  1614. const usable_gm_ticket = global_data[item.base_type]["gm_days"] && global_data[item.base_type]["gm_days"] !== "0";
  1615. const food = parseInt(global_data[item.base_type]["foodrestore"]) > 0;
  1616. const boost = parseInt(global_data[item.base_type]["boostdamagehours"]) > 0 || parseInt(global_data[item.base_type]["boostexphours"]) > 0 || parseInt(global_data[item.base_type]["boostspeedhours"]) > 0;
  1617. const story_item = global_data[item.base_type]["opencontents"] && global_data[item.base_type]["opencontents"].length > 0;
  1618. const need_health = parseInt(this.#_uservars["DFSTATS_df_hpcurrent"]) < parseInt(this.#_uservars["DFSTATS_df_hpmax"]);
  1619. const need_hunger = parseInt(this.#_uservars["DFSTATS_df_hungerhp"]) < 100;
  1620. if (medicine) {
  1621. return need_health;
  1622. } else if (food) {
  1623. return need_hunger;
  1624. } else {
  1625. return true;
  1626. }
  1627. }
  1628.  
  1629. useInventoryItem(slot, inventory_item, update_ui = UiUpdate.YES) {
  1630. const instance = this;
  1631. const promise = new Promise(function(resolve, reject) {
  1632. const action_type = instance.#useActionType(inventory_item);
  1633. if (!action_type) {
  1634. throw new TypeError("Cannot use Item: " + inventory_item.full_type + ".");
  1635. }
  1636. const action_necessary = instance.#actionNecessary(inventory_item);
  1637. if (!action_necessary) {
  1638. throw new RangeError("Item: " + inventory_item.full_type + " will provide no benefit when used.");
  1639. }
  1640. const parameters = instance.#setupItemRemovalRequest();
  1641. parameters["action"] = action_type;
  1642. parameters["itemnum"] = slot.toString();
  1643. parameters["itemnum2"] = "0";
  1644. parameters["expected_itemtype"] = inventory_item.full_type;
  1645. parameters["expected_itemtype2"] = "";
  1646. webCall("inventory_new", parameters, function(data) {
  1647. updateIntoArr(flshToArr(data, "DFSTATS_"), userVars);
  1648. if (update_ui === UiUpdate.YES) {
  1649. populateInventory();
  1650. populateCharacterInventory();
  1651. updateAllFields();
  1652. }
  1653. resolve(instance);
  1654. }, true);
  1655. });
  1656. return promise;
  1657. }
  1658.  
  1659. discardInventoryItem(slot, inventory_item, update_ui = UiUpdate.YES) {
  1660. const instance = this;
  1661. const promise = new Promise(function(resolve, reject) {
  1662. const parameters = instance.#setupItemRemovalRequest();
  1663. parameters["action"] = "newdiscard";
  1664. parameters["itemnum"] = slot.toString();
  1665. parameters["itemnum2"] = "0";
  1666. parameters["expected_itemtype"] = inventory_item.full_type;
  1667. parameters["expected_itemtype2"] = "";
  1668. webCall("inventory_new", parameters, function(data) {
  1669. updateIntoArr(flshToArr(data, "DFSTATS_"), userVars);
  1670. if (update_ui === UiUpdate.YES) {
  1671. populateInventory();
  1672. populateCharacterInventory();
  1673. updateAllFields();
  1674. renderAvatarUpdate();
  1675. }
  1676. resolve(instance);
  1677. }, true);
  1678. });
  1679. return promise;
  1680. }
  1681.  
  1682. #setupItemMutateRequest() {
  1683. const parameters = {};
  1684. parameters["pagetime"] = this.#_uservars["pagetime"];
  1685. parameters["templateID"] = this.#_uservars["template_ID"];
  1686. parameters["sc"] = this.#_uservars["sc"];
  1687. parameters["creditsnum"] = this.#_uservars["DFSTATS_df_credits"];
  1688. parameters["buynum"] = "0";
  1689. parameters["renameto"] = "undefined`undefined";
  1690. parameters["expected_itemprice"] = "-1";
  1691. parameters["price"] = "0";
  1692. parameters["userID"] = this.#_uservars["userID"];
  1693. parameters["password"] = this.#_uservars["password"];
  1694. return parameters;
  1695. }
  1696.  
  1697. mutateItem(slot, item, new_item_type, update_ui = UiUpdate.YES) {
  1698. const instance = this;
  1699. const promise = new Promise(function(resolve, reject) {
  1700. const parameters = instance.#setupItemRemovalRequest();
  1701. parameters["action"] = "mutate";
  1702. parameters["itemnum"] = slot.toString();
  1703. parameters["itemnum2"] = "0";
  1704. parameters["expected_itemtype"] = item.full_type;
  1705. parameters["expected_itemtype2"] = new_item_type;
  1706. webCall("inventory_new", parameters, function(data) {
  1707. updateIntoArr(flshToArr(data, "DFSTATS_"), userVars);
  1708. if (update_ui === UiUpdate.YES) {
  1709. populateInventory();
  1710. populateCharacterInventory();
  1711. populateImplants();
  1712. updateAllFields();
  1713. renderAvatarUpdate();
  1714. }
  1715. resolve(instance);
  1716. }, true);
  1717. });
  1718. return promise;
  1719. }
  1720. }
  1721.  
  1722. // MarketItems
  1723.  
  1724. class MarketItems {
  1725.  
  1726. static #global_data = globalData;
  1727.  
  1728. #typeInGlobalData(base_type) {
  1729. return base_type in MarketItems.#global_data;
  1730. }
  1731.  
  1732. #_uservars;
  1733. #_data;
  1734. #_nselling;
  1735. #_publicselling;
  1736. constructor() {
  1737. this.#_uservars = userVars;
  1738. }
  1739.  
  1740. // Availability Checks
  1741.  
  1742. sellingEntriesAvailable() {
  1743. // Run after requesting data.
  1744. const data = this.#_data;
  1745. const nselling = this.#_nselling;
  1746. const publicselling = this.#_publicselling;
  1747. for (let i = 0; i < publicselling; i++) {
  1748. const full_type = data.get("tradelist_" + i.toString() + "_item");
  1749. const base_type = typeSplit(full_type)[0];
  1750. if (!this.#typeInGlobalData(base_type)) {
  1751. return false;
  1752. }
  1753. }
  1754. return true;
  1755. }
  1756.  
  1757. // Selling Queries
  1758.  
  1759. #setupSellingWebcallParameters() {
  1760. const parameters = {};
  1761. parameters["pagetime"] = this.#_uservars["pagetime"];
  1762. parameters["tradezone"] = "";
  1763. parameters["searchname"] = "";
  1764. parameters["searchtype"] = "sellinglist";
  1765. parameters["search"] = "trades";
  1766. parameters["memID"] = this.#_uservars["userID"];
  1767. parameters["category"] = "";
  1768. parameters["profession"] = "";
  1769. return parameters;
  1770. }
  1771.  
  1772. requestSelling() {
  1773. const instance = this;
  1774. const promise = new Promise(function(resolve, reject) {
  1775. const parameters = instance.#setupSellingWebcallParameters();
  1776. webCall("trade_search", parameters, function(data) {
  1777. instance.#_data = responseToMap(data);
  1778. // maxresults is the number of results for this type of trade, while totalsales is across all trades (public and private)
  1779. // totalsales is shared between public and private sales (30 is the total sales between public and private)
  1780. instance.#_nselling = parseInt(instance.#_data.get("tradelist_totalsales")); // maxresults vs. totalsales?
  1781. instance.#_publicselling = parseInt(instance.#_data.get("tradelist_maxresults"));
  1782. resolve(instance);
  1783. });
  1784. });
  1785. return promise;
  1786. }
  1787.  
  1788. maxSellingEntries() {
  1789. const uservars = this.#_uservars;
  1790. const nslots = parseInt(uservars["DFSTATS_df_invslots"]);
  1791. return nslots; // Maximum selling entries equal to number of inventory slots.
  1792. }
  1793.  
  1794. nSellingEntries() {
  1795. return this.#_nselling;
  1796. }
  1797.  
  1798. availableSellingEntries() {
  1799. return this.maxSellingEntries() - this.nSellingEntries();
  1800. }
  1801.  
  1802. itemFromSellingIndex(index) {
  1803. const data = this.#_data;
  1804. const full_type = data.get("tradelist_" + index.toString() + "_item");
  1805. const name = data.get("tradelist_" + index.toString() + "_itemname");
  1806. const quantity = data.get("tradelist_" + index.toString() + "_quantity");
  1807. const item = new Item(full_type, name, quantity);
  1808. return item;
  1809. }
  1810.  
  1811. marketEntryFromSellingIndex(index) {
  1812. const item = this.itemFromSellingIndex(index);
  1813. const data = this.#_data;
  1814. const price = data.get("tradelist_" + index.toString() + "_price");
  1815. const trade_id = data.get("tradelist_" + index.toString() + "_trade_id");
  1816. const member_id = data.get("tradelist_" + index.toString() + "_id_member");
  1817. const member_name = data.get("tradelist_" + index.toString() + "_member_name");
  1818. const tradezone = data.get("tradelist_" + index.toString() + "_trade_zone");
  1819. const market_entry = new ItemMarketEntry(item, price, trade_id, member_id, member_name, tradezone);
  1820. return market_entry;
  1821. }
  1822.  
  1823. sellingEntryFromSellingIndex(index) {
  1824. const market_entry = this.marketEntryFromSellingIndex(index);
  1825. const data = this.#_data;
  1826. const member_to_id = data.get("tradelist_" + index.toString() + "_id_member_to");
  1827. const member_to_name = data.get("tradelist_" + index.toString() + "_member_to_name");
  1828. const selling_entry = new ItemSellingEntry(market_entry, member_to_id, member_to_name);
  1829. return selling_entry;
  1830. }
  1831.  
  1832. *sellingEntries() {
  1833. for (let i = 0; i < this.#_publicselling; i++) {
  1834. yield [i, this.sellingEntryFromSellingIndex(i)];
  1835. }
  1836. }
  1837.  
  1838. // Selling Changes
  1839.  
  1840. #setupCancelSellingParameters() {
  1841. const parameters = {};
  1842. parameters["pagetime"] = this.#_uservars["pagetime"];
  1843. parameters["templateID"] = this.#_uservars["template_ID"];
  1844. parameters["sc"] = this.#_uservars["sc"];
  1845. parameters["creditsnum"] = "0";
  1846. parameters["renameto"] = "";
  1847. parameters["expected_itemprice"] = "-1";
  1848. parameters["expected_itemtype2"] = "";
  1849. parameters["expected_itemtype"] = "";
  1850. parameters["itemnum2"] = 0;
  1851. parameters["itemnum"] = 0;
  1852. parameters["price"] = 0;
  1853. parameters["action"] = "newcancelsale";
  1854. parameters["userID"] = this.#_uservars["userID"];
  1855. parameters["password"] = this.#_uservars["password"];
  1856. return parameters
  1857. }
  1858.  
  1859. cancelSellingEntry(selling_entry, update_ui = UiUpdate.YES) {
  1860. const instance = this;
  1861. const promise = new Promise(function(resolve, reject) {
  1862. const inventory_space_available = findFirstEmptyGenericSlot("inv") !== false;
  1863. const is_credits = selling_entry.market_entry.item.full_type === "credits";
  1864. if (!is_credits && !inventory_space_available) {
  1865. throw new RangeError("Cannot cancel selling entry if no inventory space is available.");
  1866. }
  1867. const parameters = instance.#setupCancelSellingParameters();
  1868. parameters["buynum"] = selling_entry.market_entry.trade_id;
  1869. webCall("inventory_new", parameters, function(data) {
  1870. updateIntoArr(flshToArr(data, "DFSTATS_"), userVars);
  1871. if (update_ui === UiUpdate.YES) {
  1872. if (getSellingList !== undefined) {
  1873. getSellingList();
  1874. } else { // These functions are also called by getSellingList, but it is undefined out of the marketplace.
  1875. populateInventory();
  1876. updateAllFields();
  1877. }
  1878. }
  1879. resolve(instance);
  1880. }, true);
  1881. });
  1882. return promise;
  1883. }
  1884.  
  1885. #setupSellInventoryParameters() {
  1886. var parameters = {};
  1887. parameters["pagetime"] = this.#_uservars["pagetime"];
  1888. parameters["templateID"] = this.#_uservars["template_ID"];
  1889. parameters["sc"] = this.#_uservars["sc"];
  1890. parameters["buynum"] = 0;
  1891. parameters["renameto"] = "";
  1892. parameters["expected_itemprice"] = "-1"; // same on all sales
  1893. parameters["expected_itemtype2"] = "";
  1894. parameters["expected_itemtype"] = "";
  1895. parameters["itemnum2"] = "0";
  1896. parameters["userID"] = this.#_uservars["userID"];
  1897. parameters["password"] = this.#_uservars["password"];
  1898. return parameters;
  1899. }
  1900.  
  1901. sellInventoryItem(inventory_slot, inventory_item, price, update_ui = UiUpdate.YES) {
  1902. const instance = this;
  1903. // return instance.requestSelling().then(
  1904. const promise = new Promise(function(resolve, reject) {
  1905. // const selling_list_has_space = instance.#_nselling < parseInt(instance.#_uservars["DFSTATS_df_invslots"]);
  1906. // if (!selling_list_has_space) {
  1907. // throw new RangeError("Cannot sell item when selling list is full.");
  1908. // }
  1909. const parameters = instance.#setupSellInventoryParameters();
  1910. parameters["price"] = price;
  1911. parameters["action"] = "newsell";
  1912. parameters["expected_itemtype"] = inventory_item.full_type;
  1913. parameters["itemnum"] = inventory_slot.toString();
  1914. webCall("inventory_new", parameters, function(data) {
  1915. updateIntoArr(flshToArr(data, "DFSTATS_"), userVars);
  1916. if (update_ui === UiUpdate.YES) {
  1917. if (getSellingList !== undefined) {
  1918. getSellingList();
  1919. } else {
  1920. populateInventory();
  1921. updateAllFields();
  1922. }
  1923. }
  1924. resolve(instance);
  1925. }, true);
  1926. });
  1927. return promise;
  1928. }
  1929.  
  1930. // Buying Items
  1931.  
  1932. #setupBuyItemParameters() {
  1933. const parameters = {};
  1934. parameters["pagetime"] = this.#_uservars["pagetime"];
  1935. parameters["templateID"] = this.#_uservars["template_ID"];
  1936. parameters["sc"] = this.#_uservars["sc"];
  1937. parameters["creditsnum"] = "undefined";
  1938. parameters["renameto"] = "undefined`undefined";
  1939. parameters["expected_itemtype2"] = "";
  1940. parameters["expected_itemtype"] = "";
  1941. parameters["itemnum2"] = 0;
  1942. parameters["itemnum"] = 0;
  1943. parameters["price"] = 0;
  1944. parameters["action"] = "newbuy";
  1945. parameters["userID"] = userVars["userID"];
  1946. parameters["password"] = userVars["password"];
  1947. return parameters;
  1948. }
  1949.  
  1950. buyItemFromMarketEntry(market_entry, update_ui = UiUpdate.YES) {
  1951. const instance = this;
  1952. const promise = new Promise(function(resolve, reject) {
  1953. // Throw if inventory is full or not enough cash on-hand.
  1954. const inventory_space_available = findFirstEmptyGenericSlot("inv") !== false;
  1955. const enough_cash = parseInt(instance.#_uservars["DFSTATS_df_cash"]) >= market_entry.price;
  1956. if (!inventory_space_available) {
  1957. throw new RangeError("Cannot buy item when inventory is full.");
  1958. }
  1959. if (!enough_cash) {
  1960. throw new RangeError("Cannot buy item: " + market_entry.item.full_type + " with price $" + market_entry.price.toLocaleString() + " with $" + instance.#_uservars["DFSTATS_df_cash"].toLocaleString() + " on-hand.");
  1961. }
  1962. // Setup parameters and request purchase.
  1963. const parameters = instance.#setupBuyItemParameters();
  1964. parameters["buynum"] = market_entry.trade_id;
  1965. parameters["expected_itemprice"] = market_entry.price;
  1966. webCall("inventory_new", parameters, function(data) {
  1967. const item_purchase_successful = data !== "";
  1968. if (item_purchase_successful) {
  1969. updateIntoArr(flshToArr(data, "DFSTATS_"), userVars);
  1970. if (update_ui === UiUpdate.YES) {
  1971. populateInventory();
  1972. const cash = instance.#_uservars["DFSTATS_df_cash"];
  1973. const bank = instance.#_uservars["DFSTATS_df_bankcash"];
  1974. updateCashBank(cash, bank);
  1975. updateAllFields();
  1976. }
  1977. resolve(instance);
  1978. } else {
  1979. reject(instance);
  1980. }
  1981. }, true);
  1982. });
  1983. return promise;
  1984. }
  1985.  
  1986. // Buying Services
  1987.  
  1988. #setupBuyServiceParameters() {
  1989. const parameters = {};
  1990. parameters["pagetime"] = this.#_uservars["pagetime"];
  1991. parameters["templateID"] = this.#_uservars["template_ID"];
  1992. parameters["sc"] = this.#_uservars["sc"];
  1993. parameters["creditsnum"] = 0;
  1994. parameters["renameto"] = "undefined`undefined";
  1995. parameters["expected_itemtype2"] = "";
  1996. parameters["expected_itemtype"] = "";
  1997. parameters["itemnum2"] = "0";
  1998. parameters["userID"] = this.#_uservars["userID"];
  1999. parameters["password"] = this.#_uservars["password"];
  2000. return parameters;
  2001. }
  2002.  
  2003. #buyServiceFromMarketEntry(item_valid, service_needed, action_type, market_entry, slot, inventory_item, update_ui = UiUpdate.YES) {
  2004. const instance = this;
  2005. const promise = new Promise(function(resolve, reject) {
  2006. const is_valid_item = item_valid(inventory_item);
  2007. const enough_cash = parseInt(instance.#_uservars["DFSTATS_df_cash"]) >= market_entry.price;
  2008. if (!service_needed()) {
  2009. throw new RangeError("Service not needed.");
  2010. }
  2011. if (!is_valid_item) {
  2012. throw new TypeError("Item: " + inventory_item.full_type + " is invalid for this service.");
  2013. }
  2014. if (!enough_cash) {
  2015. throw new RangeError("Cannot buy service for $" + market_entry.price.toLocaleString() + " with $" + parseInt(instance.#_uservars["DFSTATS_df_cash"]).toLocaleString() + " on-hand.");
  2016. }
  2017. const parameters = instance.#setupBuyServiceParameters();
  2018. parameters["itemnum"] = slot.toString();
  2019. parameters["price"] = scrapAmount(inventory_item.full_type, inventory_item.quantity); // I don't know why this is here.
  2020. parameters["action"] = action_type;
  2021. parameters["buynum"] = market_entry.member_id.toString();
  2022. parameters["expected_itemprice"] = market_entry.price.toString();
  2023. webCall("inventory_new", parameters, function(data) {
  2024. updateIntoArr(flshToArr(data, "DFSTATS_"), userVars);
  2025. if (update_ui === UiUpdate.YES) {
  2026. loadStatusData();
  2027. populateInventory();
  2028. populateCharacterInventory();
  2029. updateAllFields();
  2030. const cash = instance.#_uservars["DFSTATS_df_cash"];
  2031. const bank = instance.#_uservars["DFSTATS_df_bankcash"];
  2032. updateCashBank(cash, bank);
  2033. }
  2034. resolve(instance);
  2035. }, true);
  2036. });
  2037. return promise;
  2038. }
  2039.  
  2040. buyRepairFromMarketEntry(market_entry, slot, inventory_item, update_ui = UiUpdate.YES) {
  2041. const item_valid = (item) => item.category === ItemCategory.ARMOUR && item.properties.get("durability") !== item.properties.get("max_durability");
  2042. const service_needed = () => true;
  2043. const action_type = "buyrepair";
  2044. return this.#buyServiceFromMarketEntry(item_valid, service_needed, action_type, market_entry, slot, inventory_item, update_ui);
  2045. }
  2046.  
  2047. buyAdministerFromMarketEntry(market_entry, slot, inventory_item, update_ui = UiUpdate.YES) {
  2048. const item_valid = (item) => item.category === ItemCategory.ITEM && item.subcategory === ItemSubcategory.MEDICINE && MarketItems.#global_data[item.full_type].needdoctor === "1";
  2049. const service_needed = () => this.#_uservars["DFSTATS_df_hpcurrent"] !== this.#_uservars["DFSTATS_df_hpmax"];
  2050. const action_type = "buyadminister";
  2051. return this.#buyServiceFromMarketEntry(item_valid, service_needed, action_type, market_entry, slot, inventory_item, update_ui);
  2052. }
  2053.  
  2054. buyCookFromMarketEntry(market_entry, slot, inventory_item, update_ui = UiUpdate.YES) {
  2055. const item_valid = (item) => item.category === ItemCategory.ITEM && item.subcategory === ItemSubcategory.FOOD && MarketItems.#global_data[item.full_type].needcook === "1" && (!inventory_item.properties.has("cooked") || inventory_item.properties.get("cooked") === false);
  2056. const service_needed = () => true;
  2057. const action_type = "buycook";
  2058. return this.#buyServiceFromMarketEntry(item_valid, service_needed, action_type, market_entry, slot, inventory_item, update_ui);
  2059. }
  2060. }
  2061.  
  2062. // Bank
  2063.  
  2064. class Bank {
  2065. #_player_values;
  2066. #_uservars;
  2067. constructor() {
  2068. this.#_uservars = userVars;
  2069. this.#_player_values = new PlayerValues();
  2070. }
  2071.  
  2072. #setupBankWebcallParameters() {
  2073. const parameters = {};
  2074. parameters["sc"] = this.#_uservars["sc"];
  2075. parameters["userID"] = this.#_uservars["userID"];
  2076. parameters["password"] = this.#_uservars["password"];
  2077. return parameters;
  2078. }
  2079.  
  2080. deposit(amount, update_ui = UiUpdate.YES) {
  2081. const instance = this;
  2082. const promise = new Promise(function(resolve, reject) {
  2083. instance.#_player_values.request()
  2084. .then(function(values) {
  2085. const parameters = instance.#setupBankWebcallParameters();
  2086. parameters["deposit"] = amount;
  2087. if (amount > values.cash) {
  2088. throw new RangeError("Cannot deposit: $" + amount.toLocaleString() + " is more than is carried on-hand.");
  2089. }
  2090. webCall("bank", parameters, function(data) {
  2091. const map = responseToMap(data);
  2092. if (update_ui === UiUpdate.YES) {
  2093. const cash = parseInt(map.get("df_cash"));
  2094. const bank = parseInt(map.get("df_bankcash"));
  2095. updateCashBank(cash, bank);
  2096. }
  2097. resolve(instance);
  2098. });
  2099. });
  2100. });
  2101. return promise;
  2102. }
  2103.  
  2104. depositAll(update_ui = UiUpdate.YES) {
  2105. const instance = this;
  2106. const promise = new Promise(function(resolve, reject) {
  2107. instance.#_player_values.request()
  2108. .then(function(values) {
  2109. return instance.deposit(values.cash, update_ui);
  2110. });
  2111. });
  2112. return promise;
  2113. }
  2114.  
  2115. withdraw(amount, update_ui = UiUpdate.YES) {
  2116. const instance = this;
  2117. const promise = new Promise(function(resolve, reject) {
  2118. instance.#_player_values.request()
  2119. .then(function(values) {
  2120. const parameters = instance.#setupBankWebcallParameters();
  2121. parameters["withdraw"] = amount;
  2122. if (amount > values.bank) {
  2123. throw new RangeError("Cannot withdraw: $" + amount.toLocaleString() + " is more than is in bank.");
  2124. }
  2125. webCall("bank", parameters, function(data) {
  2126. const map = responseToMap(data);
  2127. if (update_ui === UiUpdate.YES) {
  2128. const cash = parseInt(map.get("df_cash"));
  2129. const bank = parseInt(map.get("df_bankcash"));
  2130. updateCashBank(cash, bank);
  2131. }
  2132. resolve(instance);
  2133. });
  2134. });
  2135. });
  2136. return promise;
  2137. }
  2138. }
  2139.  
  2140. // MarketCache
  2141. // The MarketCache class is responsible for requesting, processing, and storing market data for items and services.
  2142.  
  2143. class MarketCache {
  2144. static #global_data = globalData;
  2145.  
  2146. // Data Processing
  2147.  
  2148. #specialSearchString(type) {
  2149. // NOTE: All custom search strings go here.
  2150. const name = MarketCache.#global_data[type].name;
  2151. if (type == "exterminatorreactivext") {
  2152. // "12345678901234567890" 20 char limit
  2153. return "reactive xt";
  2154. } else if (type == "exterminatorreactive") {
  2155. return "exterminator react";
  2156. } else if (type == "xterminatorreactive") {
  2157. return "x-terminator react";
  2158. } else if (type == "barnellrf31crossbow") {
  2159. return "barnell rf31";
  2160. } else if (type == "goldenrabbitimplant") {
  2161. return "golden rabbit imp";
  2162. } else if (type == "xmannbergblueprints") {
  2163. return "x-mannberg blue";
  2164. } else if (type == "dawnsabreblueprints") {
  2165. return "dawn blade blue";
  2166. } else if (type == "dawnenforcerblueprints") {
  2167. return "dawn enforcer blue";
  2168. } else if (type == "dawncarbineblueprints") {
  2169. return "dawn carbine blue";
  2170. } else if (type == "dawnstrikerblueprints") {
  2171. return "dawn striker blue";
  2172. } else if (type == "dawnlauncherblueprints") {
  2173. return "dawn launcher blue";
  2174. } else if (type == "xreactiveblueprints") {
  2175. return "x-reactive blue";
  2176. } else if (type == "inquisitorblueprints") {
  2177. return "inquisitor blue";
  2178. } else if (type == "sharktoothripperblueprints") {
  2179. return "ripper blue";
  2180. } else if (type == "qr22obsidianblueprints") {
  2181. return "obsidian blue";
  2182. } else if (type == "rusthound37eblueprints") {
  2183. return "37-e blue";
  2184. } else if (type == "heatpit75blueprints") {
  2185. return "pit 75 blue";
  2186. } else if (type == "a10bullsharkblueprints") {
  2187. return "bullshark blue";
  2188. } else if (type == "scorchernk19blueprints") {
  2189. return "nk19 blue";
  2190. } else if (type == "christmasstocking2022"){
  2191. return "stocking 2022";
  2192. } else if (type.indexOf("christmasstocking") !== -1 && !Number.isNaN(type.slice(-4))) {
  2193. return "stocking " + type.slice(-4);
  2194. } else if (type.indexOf("blueprints") !== -1) {
  2195. return name.slice(-20);
  2196. } else if (name.length > 20) {
  2197. return name.slice(0, 20);
  2198. } else {
  2199. return false;
  2200. }
  2201. }
  2202.  
  2203. // Item Requests
  2204.  
  2205. #setupItemRequest(tradezone, search_string) {
  2206. // Sets up POST request for searching up market data for given search string and tradezone.
  2207. const request = new XMLHttpRequest();
  2208. request.open("POST", "https://fairview.deadfrontier.com/onlinezombiemmo/trade_search.php");
  2209. request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  2210. // Setting up request payload.
  2211. const request_parameters = new URLSearchParams();
  2212. request_parameters.set("hash", "");
  2213. request_parameters.set("pagetime", "");
  2214. request_parameters.set("tradezone", tradezone.toString());
  2215. request_parameters.set("searchname", encodeURI(search_string));
  2216. request_parameters.set("category", "");
  2217. request_parameters.set("profession", "");
  2218. request_parameters.set("memID", "");
  2219. request_parameters.set("searchtype", "buyinglistitemname");
  2220. request_parameters.set("search", "trades");
  2221. return [request, request_parameters];
  2222. }
  2223.  
  2224. #setupCategoryRequest(tradezone, category) {
  2225. // Sets up POST request for searching up market data for given category and tradezone.
  2226. const request = new XMLHttpRequest();
  2227. request.open("POST", "https://fairview.deadfrontier.com/onlinezombiemmo/trade_search.php");
  2228. request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  2229. // Setting up request payload.
  2230. const request_parameters = new URLSearchParams();
  2231. request_parameters.set("hash", "");
  2232. request_parameters.set("pagetime", "");
  2233. request_parameters.set("tradezone", tradezone.toString());
  2234. request_parameters.set("searchname", "");
  2235. request_parameters.set("category", category);
  2236. request_parameters.set("profession", "");
  2237. request_parameters.set("memID", "");
  2238. request_parameters.set("searchtype", "buyinglistcategory");
  2239. request_parameters.set("search", "trades");
  2240. return [request, request_parameters];
  2241. }
  2242.  
  2243. #deleteItemData(item_type) {
  2244. this.item_types.delete(item_type);
  2245. this.item_data.delete(item_type);
  2246. }
  2247.  
  2248. #deleteRenameData(rename) {
  2249. this.renames.delete(rename);
  2250. this.rename_data.delete(rename);
  2251. }
  2252.  
  2253. #parseItemData(response) {
  2254. // Parses item data and puts array(s) of MarketEntry into the instance.
  2255. const map = responseToMap(response);
  2256. const results = parseInt(map.get("tradelist_maxresults"));
  2257. const types = new Set(); // Contains types that this worker is operating on. These will all be new types.
  2258. // Delete all types that exist in this request.
  2259. const types_to_delete = new Set();
  2260. for (let i = 0; i < results; i++) {
  2261. const full_type = map.get("tradelist_" + i.toString() + "_item");
  2262. const base_type = typeSplit(full_type)[0];
  2263. types_to_delete.add(base_type);
  2264. }
  2265. for (const type of types_to_delete.values()) {
  2266. if (this.hasItemType(type)) {
  2267. this.#deleteItemData(type);
  2268. }
  2269. }
  2270. // Add in new data.
  2271. for (let i = 0; i < results; i++) {
  2272. // Extracts MarketEntry properties from keys.
  2273. const full_type = map.get("tradelist_" + i.toString() + "_item");
  2274. const base_type = typeSplit(full_type)[0];
  2275. const name = map.get("tradelist_" + i.toString() + "_itemname");
  2276. const quantity = parseInt(map.get("tradelist_" + i.toString() + "_quantity"))
  2277. const price = parseInt(map.get("tradelist_" + i.toString() + "_price"));
  2278. const trade_id = parseInt(map.get("tradelist_" + i.toString() + "_trade_id"));
  2279. const member_id = parseInt(map.get("tradelist_" + i.toString() + "_id_member"));
  2280. const member_name = map.get("tradelist_" + i.toString() + "_member_name");
  2281. const item = new Item(full_type, name, quantity);
  2282. const market_entry = new ItemMarketEntry(item, price, trade_id, member_id, member_name, this.tradezone);
  2283. const is_renamed = item.properties.has("rename");
  2284. if (is_renamed) {
  2285. const rename = item.properties.get("rename");
  2286. }
  2287. // Putting data into item data.
  2288. if (!this.hasItemType(base_type)) { // New type that has not yet been added.
  2289. types.add(base_type);
  2290. this.item_types.add(base_type);
  2291. this.item_data.set(base_type, []);
  2292. this.item_data.get(base_type).push(market_entry);
  2293. } else if (types.has(base_type)) { // Type that this worker has started and has exclusive access to.
  2294. this.item_data.get(base_type).push(market_entry);
  2295. } else { // Some other worker is handling the type.
  2296. continue;
  2297. }
  2298. }
  2299. // Sorts MarketEntry[] of newly added types by price per unit, ascending.
  2300. for (const type of types.values()) {
  2301. const market_entries = this.item_data.get(type);
  2302. const item = market_entries[0].item;
  2303. if (isStackable(item)) {
  2304. market_entries.sort(function(a, b) {return (a.price/a.quantity) - (b.price/b.quantity);});
  2305. } else { // Armour has its durability in its quantity, and I don't want to sort by that.
  2306. market_entries.sort(function(a, b) {return a.price - b.price;})
  2307. }
  2308. }
  2309. }
  2310.  
  2311. #parseRenameData(response) {
  2312. // Parses item data and puts array(s) of MarketEntry into the instance.
  2313. const map = responseToMap(response);
  2314. const results = parseInt(map.get("tradelist_maxresults"));
  2315. const renames = new Set(); // Contains renames that this worker is operating on. These will all be new names.
  2316. // Delete all names that exist in this request.
  2317. const names_to_delete = new Set();
  2318. for (let i = 0; i < results; i++) {
  2319. const name = map.get("tradelist_" + i.toString() + "_itemname");
  2320. names_to_delete.add(name);
  2321. }
  2322. for (const name of names_to_delete.values()) {
  2323. if (this.hasRename(name)) {
  2324. this.#deleteRenameData(name);
  2325. }
  2326. }
  2327. // Add in new data.
  2328. for (let i = 0; i < results; i++) {
  2329. // Extracts MarketEntry properties from keys.
  2330. const full_type = map.get("tradelist_" + i.toString() + "_item");
  2331. const base_type = typeSplit(full_type)[0];
  2332. const name = map.get("tradelist_" + i.toString() + "_itemname");
  2333. const quantity = parseInt(map.get("tradelist_" + i.toString() + "_quantity"))
  2334. const price = parseInt(map.get("tradelist_" + i.toString() + "_price"));
  2335. const trade_id = parseInt(map.get("tradelist_" + i.toString() + "_trade_id"));
  2336. const member_id = parseInt(map.get("tradelist_" + i.toString() + "_id_member"));
  2337. const member_name = map.get("tradelist_" + i.toString() + "_member_name");
  2338. const item = new Item(full_type, name, quantity);
  2339. const market_entry = new ItemMarketEntry(item, price, trade_id, member_id, member_name, this.tradezone);
  2340. const rename = item.properties.get("rename");
  2341. // Putting data into rename data.
  2342. if (!this.hasRename(rename)) { // New name that has not yet been added.
  2343. renames.add(rename);
  2344. this.renames.add(rename);
  2345. this.rename_data.set(rename, []);
  2346. this.rename_data.get(rename).push(market_entry);
  2347. } else if (renames.has(rename)) { // Name that this worker has started and has exclusive access to.
  2348. this.rename_data.get(rename).push(market_entry);
  2349. } else { // Some other worker is handling this name.
  2350. continue;
  2351. }
  2352. }
  2353. // Sorts MarketEntry[] of newly added names by price per unit, ascending.
  2354. for (const rename of renames.values()) {
  2355. const market_entries = this.rename_data.get(rename);
  2356. const item = market_entries[0].item;
  2357. if (isStackable(item)) {
  2358. market_entries.sort(function(a, b) {return (a.price/a.quantity) - (b.price/b.quantity);});
  2359. } else { // Armour has its durability in its quantity, and I don't want to sort by that.
  2360. market_entries.sort(function(a, b) {return a.price - b.price;})
  2361. }
  2362. }
  2363. }
  2364.  
  2365. #parseMixedData(response) {
  2366. // Parses item data and puts array(s) of MarketEntry into the instance.
  2367. const map = responseToMap(response);
  2368. const results = parseInt(map.get("tradelist_maxresults"));
  2369. const types = new Set(); // Contains types that this worker is operating on. These will all be new types.
  2370. const renames = new Set();
  2371. // Add in new data.
  2372. for (let i = 0; i < results; i++) {
  2373. // Extracts MarketEntry properties from keys.
  2374. const full_type = map.get("tradelist_" + i.toString() + "_item");
  2375. const base_type = typeSplit(full_type)[0];
  2376. const name = map.get("tradelist_" + i.toString() + "_itemname");
  2377. const quantity = parseInt(map.get("tradelist_" + i.toString() + "_quantity"))
  2378. const price = parseInt(map.get("tradelist_" + i.toString() + "_price"));
  2379. const trade_id = parseInt(map.get("tradelist_" + i.toString() + "_trade_id"));
  2380. const member_id = parseInt(map.get("tradelist_" + i.toString() + "_id_member"));
  2381. const member_name = map.get("tradelist_" + i.toString() + "_member_name");
  2382. const item = new Item(full_type, name, quantity);
  2383. const market_entry = new ItemMarketEntry(item, price, trade_id, member_id, member_name, this.tradezone);
  2384. const is_renamed = item.properties.has("rename");
  2385. // Putting data into item data.
  2386. if (!is_renamed) {
  2387. if (!this.hasItemType(base_type)) { // New type that has not yet been added.
  2388. types.add(base_type);
  2389. this.item_types.add(base_type);
  2390. this.item_data.set(base_type, []);
  2391. this.item_data.get(base_type).push(market_entry);
  2392. } else if (types.has(base_type)) { // Type that this worker has started and has exclusive access to.
  2393. this.item_data.get(base_type).push(market_entry);
  2394. } else { // Some other worker is handling the type.
  2395. continue;
  2396. }
  2397. } else {
  2398. // Putting data into rename data.
  2399. const rename = item.properties.get("rename");
  2400. if (!this.hasRename(rename)) { // New name that has not yet been added.
  2401. renames.add(rename);
  2402. this.renames.add(rename);
  2403. this.rename_data.set(rename, []);
  2404. this.rename_data.get(rename).push(market_entry);
  2405. } else if (renames.has(rename)) { // Name that this worker has started and has exclusive access to.
  2406. this.rename_data.get(rename).push(market_entry);
  2407. } else { // Some other worker is handling this name.
  2408. continue;
  2409. }
  2410. }
  2411. }
  2412. // Sorts MarketEntry[] of newly added types by price per unit, ascending.
  2413. for (const type of types.values()) {
  2414. const market_entries = this.item_data.get(type);
  2415. const item = market_entries[0].item;
  2416. if (isStackable(item)) {
  2417. market_entries.sort(function(a, b) {return (a.price/a.quantity) - (b.price/b.quantity);});
  2418. } else { // Armour has its durability in its quantity, and I don't want to sort by that.
  2419. market_entries.sort(function(a, b) {return a.price - b.price;})
  2420. }
  2421. }
  2422. // Sorts MarketEntry[] of newly added names by price per unit, ascending.
  2423. for (const rename of renames.values()) {
  2424. const market_entries = this.rename_data.get(rename);
  2425. const item = market_entries[0].item;
  2426. if (isStackable(item)) {
  2427. market_entries.sort(function(a, b) {return (a.price/a.quantity) - (b.price/b.quantity);});
  2428. } else { // Armour has its durability in its quantity, and I don't want to sort by that.
  2429. market_entries.sort(function(a, b) {return a.price - b.price;})
  2430. }
  2431. }
  2432. }
  2433.  
  2434. #requestItemData(tradezone, search_string, callback) {
  2435. const instance = this; // Variable to hold class instance to avoid clashing with the request (inner 'this').
  2436. const promise = new Promise(function(resolve, reject) {
  2437. const [request, parameters] = instance.#setupItemRequest(tradezone, search_string);
  2438. request.onreadystatechange = function() {
  2439. const is_complete = this.readyState == 4;
  2440. const response_ok = this.status == 200;
  2441. const client_error = this.status >= 400 && this.status < 500;
  2442. const server_error = this.status >= 500 && this.status < 600;
  2443. if (is_complete && response_ok) {
  2444. instance.#parseItemData(this.response);
  2445. instance.#_requests_out -= 1;
  2446. resolve(instance);
  2447. } else if (is_complete && (client_error || server_error)) {
  2448. instance.#_requests_out -= 1;
  2449. reject(instance);
  2450. }
  2451. }
  2452. instance.#_requests_out += 1;
  2453. request.send(parameters);
  2454. });
  2455. return promise;
  2456. }
  2457.  
  2458. #requestRenameData(tradezone, search_string) {
  2459. const instance = this; // Variable to hold class instance to avoid clashing with the request (inner 'this').
  2460. const promise = new Promise(function(resolve, reject) {
  2461. const [request, parameters] = instance.#setupItemRequest(tradezone, search_string);
  2462. request.onreadystatechange = function() {
  2463. const is_complete = this.readyState == 4;
  2464. const response_ok = this.status == 200;
  2465. const client_error = this.status >= 400 && this.status < 500;
  2466. const server_error = this.status >= 500 && this.status < 600;
  2467. if (is_complete && response_ok) {
  2468. instance.#parseRenameData(this.response);
  2469. instance.#_requests_out -= 1;
  2470. resolve(instance);
  2471. } else if (is_complete && (client_error || server_error)) {
  2472. instance.#_requests_out -= 1;
  2473. reject(instance);
  2474. }
  2475. }
  2476. instance.#_requests_out += 1;
  2477. request.send(parameters);
  2478. });
  2479. return promise;
  2480. }
  2481.  
  2482. #requestCategoricalData(tradezone, category) {
  2483. const instance = this; // Variable to hold class instance to avoid clashing with the request (inner 'this').
  2484. const promise = new Promise(function(resolve, reject) {
  2485. const [request, parameters] = instance.#setupCategoryRequest(tradezone, category);
  2486. request.onreadystatechange = function() {
  2487. const is_complete = this.readyState == 4;
  2488. const response_ok = this.status == 200;
  2489. const client_error = this.status >= 400 && this.status < 500;
  2490. const server_error = this.status >= 500 && this.status < 600;
  2491. if (is_complete && response_ok) {
  2492. instance.#parseMixedData(this.response);
  2493. instance.#_requests_out -= 1;
  2494. resolve(instance);
  2495. } else if (is_complete && (client_error || server_error)) {
  2496. instance.#_requests_out -= 1;
  2497. reject(instance);
  2498. }
  2499. }
  2500. instance.#_requests_out += 1;
  2501. request.send(parameters);
  2502. });
  2503. return promise;
  2504. }
  2505.  
  2506. // Service Requests
  2507.  
  2508. #setupServiceRequest(tradezone, service) {
  2509. // Sets up POST request for searching up market data for given service and tradezone.
  2510. const request = new XMLHttpRequest();
  2511. request.open("POST", "https://fairview.deadfrontier.com/onlinezombiemmo/trade_search.php");
  2512. request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  2513. // Setting up request payload.
  2514. const request_parameters = new URLSearchParams();
  2515. request_parameters.set("hash", "");
  2516. request_parameters.set("pagetime", "");
  2517. request_parameters.set("tradezone", tradezone.toString());
  2518. request_parameters.set("searchname", "");
  2519. request_parameters.set("category", "");
  2520. request_parameters.set("profession", service.toString());
  2521. request_parameters.set("memID", "");
  2522. request_parameters.set("searchtype", "buyinglist");
  2523. request_parameters.set("search", "services");
  2524. return [request, request_parameters];
  2525. }
  2526.  
  2527. #deleteServiceData(service) {
  2528. this.service_types.delete(service);
  2529. this.service_data.delete(service);
  2530. }
  2531.  
  2532. #parseServiceData(response, service) {
  2533. const map = responseToMap(response);
  2534. map.set("services", true);
  2535. const results = parseInt(map.get("tradelist_maxresults"));
  2536. const types = new Set();
  2537. // Deletes all types that exist in this request.
  2538. const types_to_delete = new Set();
  2539. for (let i = 0; i < results; i++) {
  2540. const service_type = map.get("tradelist_" + i.toString() + "_profession");
  2541. types_to_delete.add(service_type);
  2542. }
  2543. for (const type of types_to_delete.values()) {
  2544. if (this.hasServiceType(type)) {
  2545. this.#deleteServiceData(type);
  2546. }
  2547. }
  2548. // Adding in new data.
  2549. for (let i = 0; i < results; i++) {
  2550. const service_type = map.get("tradelist_" + i.toString() + "_profession");
  2551. const level = parseInt(map.get("tradelist_" + i.toString() + "_level"));
  2552. const price = parseInt(map.get("tradelist_" + i.toString() + "_price"));
  2553. const member_id = parseInt(map.get("tradelist_" + i.toString() + "_id_member"));
  2554. const member_name = map.get("tradelist_" + i.toString() + "_member_name");
  2555. const service = new Service(service_type, level);
  2556. const market_entry = new ServiceMarketEntry(service, price, member_id, member_name, this.tradezone);
  2557. // Putting data into service data.
  2558. if (!this.hasServiceType(service_type)) { // New type that has not yet been added.
  2559. types.add(service_type);
  2560. this.service_types.add(service_type);
  2561. this.service_data.set(service_type, []);
  2562. this.service_data.get(service_type).push(market_entry);
  2563. } else if (types.has(service_type)) { // Type that this worker has started and has exclusive access to.
  2564. this.service_data.get(service_type).push(market_entry);
  2565. } else { // Some other worker is handling the type.
  2566. continue;
  2567. }
  2568. }
  2569. for (const type of types.values()) {
  2570. const market_entries = this.service_data.get(type);
  2571. market_entries.sort(function(a, b) {return a.price - b.price;});
  2572. }
  2573. }
  2574.  
  2575. #requestServiceData(tradezone, service) {
  2576. // Requests market data for given service and tradezone. Calls parseServiceData to parse and store
  2577. // information.
  2578. const instance = this; // Variable to hold class instance to avoid clashing with the request (inner 'this').
  2579. const promise = new Promise(function(resolve, reject) {
  2580. const [request, parameters] = instance.#setupServiceRequest(tradezone, service);
  2581. request.onreadystatechange = function() {
  2582. const is_complete = this.readyState == 4;
  2583. const response_ok = this.status == 200;
  2584. const client_error = this.status >= 400 && this.status < 500;
  2585. const server_error = this.status >= 500 && this.status < 600;
  2586. if (is_complete && response_ok) {
  2587. instance.#_requests_out -= 1;
  2588. instance.#parseServiceData(this.response, service);
  2589. resolve(instance);
  2590. } else if (is_complete && (client_error || server_error)) {
  2591. instance.#_requests_out -= 1;
  2592. reject(instance);
  2593. }
  2594. }
  2595. instance.#_requests_out += 1;
  2596. request.send(parameters);
  2597. });
  2598. return promise;
  2599. }
  2600.  
  2601. // Public Functions
  2602.  
  2603. static itemToCategorical(item) {
  2604. if (item.category === ItemCategory.WEAPON) {
  2605. const weapon_type = item.properties.get("weapon_type");
  2606. if (["submachinegun", "machinegun"].includes(weapon_type)) {
  2607. return "weapon_lightmachinegun";
  2608. } else if (["bigmachinegun", "minigun"].includes(weapon_type)) {
  2609. return "weapon_heavymachinegun";
  2610. } else if (weapon_type === "grenadelauncher") {
  2611. return "weapon_grenadelauncher";
  2612. }
  2613. else {
  2614. return "weapon_" + item.properties.get("proficiency_type");
  2615. }
  2616. } else if (item.category === ItemCategory.ARMOUR) {
  2617. return "armour";
  2618. } else if (item.category === ItemCategory.AMMO) {
  2619. if (item.base_type.indexOf("rifle") !== -1) {
  2620. return "ammo_rifle";
  2621. } else if (item.base_type.indexOf("gauge") !== -1) {
  2622. return "ammo_shotgun";
  2623. } else if (item.base_type.indexOf("grenade") !== -1) {
  2624. return "ammo_grenade";
  2625. } else if (item.base_type.indexOf("fuel") !== -1) {
  2626. return "ammo_fuel";
  2627. } else {
  2628. return "ammo_handgun";
  2629. }
  2630. } else if (item.category === ItemCategory.ITEM && item.subcategory === ItemSubcategory.FOOD) {
  2631. return "food";
  2632. } else if (item.category === ItemCategory.ITEM && item.subcategory === ItemSubcategory.MEDICINE) {
  2633. return "medical";
  2634. } else if (item.category === ItemCategory.ITEM && item.subcategory === ItemSubcategory.CLOTHING) {
  2635. const clothing_type = item.properties.get("clothing_type");
  2636. if (["mask", "hat"].includes(clothing_type)) {
  2637. return "clothing_headwear";
  2638. } else if (clothing_type === "coat") {
  2639. return "clothing_coat";
  2640. } else {
  2641. return "clothing_basic";
  2642. }
  2643. } else if (item.category === ItemCategory.ITEM && item.subcategory === ItemSubcategory.BARRICADE) {
  2644. return "barricading";
  2645. } else if (item.category === ItemCategory.ITEM && item.subcategory === ItemSubcategory.IMPLANT) {
  2646. return "implants";
  2647. } else if (item.category === ItemCategory.BACKPACK) {
  2648. return "backpack";
  2649. } else {
  2650. return "misc";
  2651. }
  2652. }
  2653.  
  2654. #_tradezone;
  2655. #_requests_out;
  2656. constructor(tradezone) {
  2657. this.#_tradezone = tradezone;
  2658. this.item_types = new Set();
  2659. this.item_data = new Map();
  2660. this.renames = new Set();
  2661. this.rename_data = new Map();
  2662. this.service_types = new Set();
  2663. this.service_data = new Map();
  2664. this.#_requests_out = 0;
  2665. }
  2666.  
  2667. get tradezone() {
  2668. return this.#_tradezone;
  2669. }
  2670.  
  2671. get requests_out() {
  2672. return this.#_requests_out;
  2673. }
  2674.  
  2675. hasItemType(type) {
  2676. const base_type = typeSplit(type)[0];
  2677. return this.item_types.has(base_type);
  2678. }
  2679.  
  2680. hasRename(rename) {
  2681. return this.renames.has(rename);
  2682. }
  2683.  
  2684. getItemMarketEntriesByType(type) {
  2685. const base_type = typeSplit(type)[0];
  2686. const data = this.item_data.get(base_type);
  2687. if (data === undefined) {
  2688. throw new ReferenceError("Item: " + base_type + " unavailable.");
  2689. } else {
  2690. return data;
  2691. }
  2692. }
  2693.  
  2694. getItemMarketEntriesByRename(rename) {
  2695. const data = this.rename_data.get(rename);
  2696. if (data === undefined) {
  2697. throw new ReferenceError("Renamed item: " + rename + " unavailable.");
  2698. } else {
  2699. return data;
  2700. }
  2701. }
  2702.  
  2703. requestItemMarketEntriesByType(type) {
  2704. const base_type = typeSplit(type)[0];
  2705. const special_string = this.#specialSearchString(base_type);
  2706. const name = special_string !== false ? special_string : MarketCache.#global_data[base_type].name;
  2707. const promise = this.#requestItemData(this.tradezone, name);
  2708. return promise;
  2709. }
  2710.  
  2711. requestMultipleItemMarketEntriesByType(types) {
  2712. const unique_types = new Set(types); // To avoid repeats.
  2713. // Need to bind to avoid losing track of 'this', becoming undefined.
  2714. const promises = Array.from(unique_types).map(this.requestItemMarketEntriesByType.bind(this));
  2715. // Once all promises (requests) complete, unify them into a single promise that resolves to the instance.
  2716. return Promise.allSettled(promises).then(() => this);
  2717. }
  2718.  
  2719. requestItemMarketEntriesByRename(rename) {
  2720. const promise = this.#requestRenameData(this.tradezone, rename);
  2721. return promise;
  2722. }
  2723.  
  2724. requestMultipleItemMarketEntriesByRename(renames) {
  2725. const unique_renames = new Set(renames); // To avoid repeats.
  2726. // Need to bind to avoid losing track of 'this', becoming undefined.
  2727. const promises = Array.from(unique_renames).map(this.requestItemMarketEntriesByRename.bind(this));
  2728. // Once all promises (requests) complete, unify them into a single promise that resolves to the instance.
  2729. return Promise.allSettled(promises).then(() => this);
  2730. }
  2731.  
  2732. requestItemMarketEntriesByCategory(category) {
  2733. const promise = this.#requestCategoricalData(this.tradezone, category);
  2734. return promise;
  2735. }
  2736.  
  2737. requestMultipleItemMarketEntriesByCategory(categories) {
  2738. const unique_categories = new Set(categories); // To avoid repeats.
  2739. // Need to bind to avoid losing track of 'this', becoming undefined.
  2740. const promises = Array.from(unique_categories).map(this.requestItemMarketEntriesByCategory.bind(this));
  2741. // Once all promises (requests) complete, unify them into a single promise that resolves to the instance.
  2742. return Promise.allSettled(promises).then(() => this);
  2743. }
  2744.  
  2745. hasServiceType(type) {
  2746. return this.service_types.has(type);
  2747. }
  2748.  
  2749. getServiceMarketEntriesByType(type) {
  2750. const data = this.service_data.get(type);
  2751. if (data === undefined) {
  2752. throw new ReferenceError("Service: " + type + " unavailable.");
  2753. } else {
  2754. return data;
  2755. }
  2756. }
  2757.  
  2758. requestServiceMarketEntriesByType(type) {
  2759. const promise = this.#requestServiceData(this.tradezone, type);
  2760. return promise;
  2761. }
  2762.  
  2763. requestMultipleServiceMarketEntriesByType(types) {
  2764. const unique_types = new Set(types); // To avoid repeats.
  2765. // Need to bind to avoid losing track of 'this', becoming undefined.
  2766. const promises = Array.from(unique_types).map(this.requestServiceMarketEntriesByType.bind(this));
  2767. // Once all promises (requests) complete, unify them into a single promise that resolves to the instance.
  2768. return Promise.allSettled(promises).then(() => this);
  2769. }
  2770. }
  2771.  
  2772. // CollectionBook
  2773.  
  2774. class CollectionBook {
  2775. #setupCollectionBookRequest() {
  2776. // Sets up POST request for requesting collection book data.
  2777. const request = new XMLHttpRequest();
  2778. request.open("POST", "https://fairview.deadfrontier.com/onlinezombiemmo/hotrods/collectionbook.php");
  2779. request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  2780. // Setting up request payload.
  2781. const request_parameters = new URLSearchParams();
  2782. request_parameters.set("userID", this.#_uservars["userID"]);
  2783. request_parameters.set("password", this.#_uservars["password"]);
  2784. request_parameters.set("sc", this.#_uservars["sc"]);
  2785. request_parameters.set("pagetime", this.#_uservars["pagetime"]);
  2786. request_parameters.set("memberto", this.#_uservars["userID"]);
  2787. request_parameters.set("action", "getother");
  2788. return [request, request_parameters];
  2789. }
  2790.  
  2791. #parseCollectionBookData(response) {
  2792. const map = responseToMap(response);
  2793. const count = parseInt(map.get("total_items"));
  2794. for (let i = 0; i < count; i++) {
  2795. const base_key = "cb" + i.toString() + "_";
  2796. const base_type = map.get(base_key + "type");
  2797. const quantity = parseInt(map.get(base_key + "quantity"));
  2798. const full_type = map.get(base_key + "inv");
  2799. const pinned = map.get(base_key + "pinned") === "1";
  2800. const priority = parseInt(map.get(base_key + "priority"));
  2801. this.#_ordered_types.push(base_type);
  2802. this.#_base_types.add(base_type);
  2803. this.#_quantities.set(base_type, quantity);
  2804. this.#_full_types.set(base_type, full_type);
  2805. this.#_pinned.set(base_type, pinned);
  2806. this.#_priorities.set(base_type, priority);
  2807. }
  2808. }
  2809.  
  2810. #_uservars;
  2811. #_requests_out;
  2812. #_ordered_types;
  2813. #_base_types;
  2814. #_quantities;
  2815. #_full_types;
  2816. #_pinned;
  2817. #_priorities;
  2818. constructor() {
  2819. this.#_uservars = userVars;
  2820. this.#_requests_out = 0;
  2821. this.#_ordered_types = [];
  2822. this.#_base_types = new Set();
  2823. this.#_quantities = new Map();
  2824. this.#_full_types = new Map();
  2825. this.#_pinned = new Map();
  2826. this.#_priorities = new Map();
  2827. }
  2828.  
  2829. get requests_out() {
  2830. return this.#_requests_out;
  2831. }
  2832.  
  2833. requestCollectionBookData() {
  2834. const instance = this; // Variable to hold class instance to avoid clashing with the request (inner 'this').
  2835. const promise = new Promise(function(resolve, reject) {
  2836. const [request, parameters] = instance.#setupCollectionBookRequest();
  2837. request.onreadystatechange = function() {
  2838. const is_complete = this.readyState == 4;
  2839. const response_ok = this.status == 200;
  2840. const client_error = this.status >= 400 && this.status < 500;
  2841. const server_error = this.status >= 500 && this.status < 600;
  2842. if (is_complete && response_ok) {
  2843. instance.#_requests_out -= 1;
  2844. instance.#parseCollectionBookData(this.response);
  2845. resolve(instance);
  2846. } else if (is_complete && (client_error || server_error)) {
  2847. instance.#_requests_out -= 1;
  2848. reject(instance);
  2849. }
  2850. }
  2851. instance.#_requests_out += 1;
  2852. request.send(parameters);
  2853. });
  2854. return promise;
  2855. }
  2856.  
  2857. hasType(type) {
  2858. return this.#_base_types.has(type);
  2859. }
  2860.  
  2861. quantity(type) {
  2862. return this.#_quantities.get(type);
  2863. }
  2864.  
  2865. fullTypeInCollection(type) {
  2866. return this.#_full_types.get(type);
  2867. }
  2868.  
  2869. pinned(type) {
  2870. return this.#_pinned.get(type);
  2871. }
  2872.  
  2873. priority(type) {
  2874. return this.#_priorities.get(type);
  2875. }
  2876.  
  2877. total() {
  2878. return this.#_base_types.size;
  2879. }
  2880.  
  2881. *types() {
  2882. const n = this.total();
  2883. for (let i = 0; i < n; i++) {
  2884. yield this.#_ordered_types[i];
  2885. }
  2886. }
  2887. }
  2888.  
  2889. // InventoryUI
  2890.  
  2891. class InventoryUI {
  2892. #_player_items;
  2893. constructor() {
  2894. this.#_player_items = new PlayerItems();
  2895. }
  2896.  
  2897. #isValidSlotElementFromMouseOverElement(element) {
  2898. const is_slot_element = element.classList.contains("validSlot");
  2899. const is_item_element = element.classList.contains("item");
  2900. return is_slot_element || is_item_element;
  2901. }
  2902.  
  2903. #slotSlotTypeFromMouseOverElement(element) {
  2904. const is_slot_element = element.classList.contains("validSlot");
  2905. const is_item_element = element.classList.contains("item");
  2906. if (!is_slot_element && !is_item_element) {
  2907. throw new Error("Element: " + element + " is not a slot or item element.");
  2908. }
  2909. const slot_element = is_slot_element ? element : element.parentNode;
  2910. const slot = parseInt(element.dataset.slot);
  2911. const slot_type = "slottype" in element.dataset ? element.dataset.slottype : undefined;
  2912. return [slot, slot_type];
  2913. }
  2914.  
  2915. #elementSlotTypeCheck(element, key) {
  2916. const [slot, slot_type] = instance.#slotSlotTypeFromMouseOverElement(element);
  2917. return slot_type === key;
  2918. }
  2919.  
  2920. #elementIsInventory(element) {
  2921. return this.#elementSlotTypeCheck(element, undefined);
  2922. }
  2923.  
  2924. #elementIsImplant(element) {
  2925. return this.#elementSlotTypeCheck(element, "implant");
  2926. }
  2927.  
  2928. #elementIsWeapon(element) {
  2929. return this.#elementSlotTypeCheck(element, "weapon");
  2930. }
  2931.  
  2932. #elementIsArmour(element) {
  2933. return this.#elementSlotTypeCheck(element, "armour");
  2934. }
  2935.  
  2936. #elementIsHat(element) {
  2937. return this.#elementSlotTypeCheck(element, "hat");
  2938. }
  2939.  
  2940. #elementIsMask(element) {
  2941. return this.#elementSlotTypeCheck(element, "mask");
  2942. }
  2943.  
  2944. #elementIsCoat(element) {
  2945. return this.#elementSlotTypeCheck(element, "coat");
  2946. }
  2947.  
  2948. #elementIsShirt(element) {
  2949. return this.#elementSlotTypeCheck(element, "shirt");
  2950. }
  2951.  
  2952. #elementIsTrousers(element) {
  2953. return this.#elementSlotTypeCheck("trousers");
  2954. }
  2955. }
  2956.  
  2957. function main() {
  2958. // setTimeout(function() {
  2959. // const player_items = new PlayerItems();
  2960. // }, 1000);
  2961. // const market_cache = new MarketCache(21);
  2962. // market_cache.requestItemMarketEntriesByCategory("weapon_melee")
  2963. // .then(function() {
  2964. // console.debug(market_cache);
  2965. // });
  2966. // setTimeout(function() {
  2967. // const global_data = globalData;
  2968. // const map = new Map();
  2969. // for (const [key, value] of Object.entries(global_data)) {
  2970. // const item = new Item(key, value.name, 1);
  2971. // const transferable = item.properties.get("transferable");
  2972. // if (transferable) {
  2973. // map.set(key, MarketCache.itemToCategorical(item));
  2974. // }
  2975. // }
  2976. // console.debug(map);
  2977. // }, 1000);
  2978. // const player_items = new PlayerItems();
  2979. // const target = player_items.implant(16);
  2980. // console.debug(target);
  2981. // player_items.mutateItem("1016", target, "fortuneimplant_statsb3jrb4")
  2982.  
  2983. }
  2984. main();
  2985.  
  2986. return {
  2987. // Enums
  2988. Tradezone: Tradezone,
  2989. ItemCategory: ItemCategory,
  2990. ItemSubcategory: ItemSubcategory,
  2991. ServiceType: ServiceType,
  2992. ProficiencyType: ProficiencyType,
  2993. UiUpdate: UiUpdate,
  2994. // Predicates
  2995. ItemFilters: ItemFilters,
  2996. ServiceFilters: ServiceFilters,
  2997. MarketFilters: MarketFilters,
  2998. // Classes
  2999. PlayerValues: PlayerValues,
  3000. PlayerItems: PlayerItems,
  3001. MarketItems: MarketItems,
  3002. Bank: Bank,
  3003. MarketCache: MarketCache,
  3004. CollectionBook: CollectionBook
  3005. };
  3006.  
  3007. })();