2024-03-06 11:18:00 +00:00
|
|
|
// This code is licensed under GNU GPLv3.
|
|
|
|
const BASE_URL = "/api/appinfo";
|
|
|
|
var TS4_APP_ID = 1222670;
|
|
|
|
const APPID_REGEX = /#(\d{5,10})/
|
|
|
|
if(APPID_REGEX.test(window.location.hash)===true) {
|
|
|
|
let matches = APPID_REGEX.exec(window.location.hash);
|
|
|
|
TS4_APP_ID = parseInt(matches[1])
|
|
|
|
console.debug("Set app ID to " + TS4_APP_ID);
|
|
|
|
} else {
|
|
|
|
console.debug("No custom ID set or does not match regex.");
|
|
|
|
}
|
|
|
|
const PRICE_ELEMENT = document.getElementById("price");
|
|
|
|
const NAME_ELEMENT = document.getElementById("name");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* These prices are gathered from a range of sources, such as Tesco, Amazon, and CeX.
|
|
|
|
* Some prices may no longer be correct. They do not include discounts.
|
|
|
|
*
|
|
|
|
* Last updated: 10/12/2023
|
|
|
|
*/
|
|
|
|
const PRICES = {
|
|
|
|
"Freddo (18g)": 0.25,
|
|
|
|
"Skittles (136G)": 1.25,
|
|
|
|
"Cadbury Fingers (114G)": 1.70,
|
|
|
|
"howmuchdoesthesims4cost.lol": 1.78,
|
|
|
|
"Pepsi Max (2L)": 2.00,
|
|
|
|
"Pringles (Salt & Vinegar, 185g)": 2.25,
|
2024-03-12 11:44:02 +00:00
|
|
|
"BLÅHAJ Soft toy, shark (40 cm)": 22.00,
|
|
|
|
"Dell Optiplex 7040 USFF (6th generation Intel i5)": 25.00,
|
2024-03-06 11:18:00 +00:00
|
|
|
"Tenda AC10 V3.0 AC1200 Dual Band Gigabit Wireless Cable Router": 27.99,
|
2024-03-12 11:44:02 +00:00
|
|
|
"Seagate SkyHawk 4TB 3.5\" 7200RPM HDD": 30.00,
|
|
|
|
"Dell Optiplex 3020 USFF (4th generation Intel i5)": 39.99,
|
|
|
|
"TP-Link AX1800 WiFi 6 Router": 39.99,
|
2024-03-06 11:18:00 +00:00
|
|
|
"KIOXIA EXERIA NVMe M.2 SSD (1TB)": 43.20,
|
|
|
|
"Seagate BarraCuda,Internal Hard Drive 2.5 Inch (1TB)": 49.99,
|
|
|
|
"Kingston NV2 NVMe PCIe 4.0 Internal SSD (1TB)": 59.99,
|
|
|
|
"NVIDIA GeForce GTX 1650": 117.96,
|
|
|
|
"AMD Radeon RX 5700 8GB (Second hand from CeX)": 150.00,
|
|
|
|
"TP-Link AX5400 WiFi 6 Router": 159.99,
|
|
|
|
"Intel Core i5-13400F": 191.99,
|
2024-03-12 11:46:58 +00:00
|
|
|
"Apple Watch SE (1st generation)": 219.00,
|
2024-03-06 11:18:00 +00:00
|
|
|
"iPhone SE (3rd generation/5G/2022)": 499.00,
|
2024-03-12 11:44:02 +00:00
|
|
|
"My custom PC (https://uk.pcpartpicker.com/user/nexy7574/saved/#view=cKYV4D)": 800.00,
|
2024-03-06 11:18:00 +00:00
|
|
|
"NVIDIA GeForce RTX 4090": 1519.00,
|
|
|
|
}
|
|
|
|
|
|
|
|
function compare(price, item) {
|
|
|
|
let n = Math.floor((price / PRICES[item]) * 100) / 100;
|
|
|
|
if(n>=1) {
|
|
|
|
n = Math.floor(n);
|
|
|
|
};
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
|
|
|
function update(n, unit="$") {
|
|
|
|
PRICE_ELEMENT.textContent = unit + n;
|
|
|
|
console.debug("Updated price to: " + n);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function ids_to_body(ids) {
|
|
|
|
return JSON.stringify({"app_ids": ids})
|
|
|
|
}
|
|
|
|
|
|
|
|
function ratelimited(response) {
|
|
|
|
let retry_after = response.headers.get("retry-after");
|
|
|
|
console.warn("Rate-limited for " + retry_after + " seconds.");
|
|
|
|
setTimeout(() => {window.location.reload()}, retry_after * 1000);
|
|
|
|
setInterval(
|
|
|
|
() => {
|
|
|
|
let element = document.getElementById("retry-after");
|
|
|
|
let value = parseFloat(element.textContent);
|
|
|
|
value -= 0.1;
|
|
|
|
element.textContent = value.toFixed(1);
|
|
|
|
},
|
|
|
|
100
|
|
|
|
)
|
|
|
|
|
|
|
|
let element = document.getElementById("retrying");
|
|
|
|
element.hidden = false;
|
|
|
|
document.getElementById("retry-after").textContent = (retry_after * 1).toFixed(1);
|
|
|
|
document.querySelector("main").hidden = true;
|
|
|
|
document.querySelector("main").style.display = "none";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function get_app_info(id) {
|
|
|
|
const response = await fetch(BASE_URL, {method: "POST", body: ids_to_body([id]), headers: {"Content-Type": "application/json"}});
|
|
|
|
if(response.status === 429) {
|
|
|
|
ratelimited(response);
|
|
|
|
return {id: {"name": "<rate limited>"}}
|
|
|
|
}
|
|
|
|
const data = await response.json();
|
|
|
|
console.debug(data);
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function calculate_price(ids) {
|
|
|
|
console.debug("Calculating price for: " + ids);
|
|
|
|
const response = await fetch(BASE_URL, {method: "POST", body: ids_to_body(ids), headers: {"Content-Type": "application/json"}});
|
|
|
|
if(response.status === 429) {
|
|
|
|
ratelimited(response);
|
|
|
|
let o = {}
|
|
|
|
for (let id of ids) {
|
|
|
|
o[id] = {"price_overview": {"final": 0.0}}
|
|
|
|
}
|
|
|
|
return o
|
|
|
|
}
|
|
|
|
const data = await response.json();
|
|
|
|
console.debug("mass price data:", data);
|
|
|
|
|
|
|
|
let _price = 0.0;
|
|
|
|
let unit = "£";
|
|
|
|
for (let value of Object.values(data)) {
|
|
|
|
console.debug("Value:", value)
|
|
|
|
if (value.price_overview == null) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
let new_unit = value.price_overview.final_formatted.slice(0, 1);
|
|
|
|
if((/\d/).test(new_unit)) {
|
|
|
|
//new_unit = value.price_overview.final_formatted.slice(-2, -1);
|
|
|
|
new_unit = value.price_overview.currency || '£'
|
|
|
|
}
|
|
|
|
unit = new_unit
|
|
|
|
_price += parseFloat(value.price_overview.final / 100);
|
|
|
|
}
|
|
|
|
return [_price, unit];
|
|
|
|
}
|
|
|
|
|
|
|
|
function find_unit(formatted) {
|
|
|
|
let unit = "£";
|
|
|
|
if((/\d/).test(formatted.slice(0, 1))) {
|
|
|
|
unit = formatted.slice(-2, -1);
|
|
|
|
}
|
|
|
|
return unit;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function main() {
|
|
|
|
console.debug("Fetching app info for base game");
|
|
|
|
let root;
|
|
|
|
try {
|
|
|
|
root = await get_app_info(TS4_APP_ID);
|
|
|
|
} catch (e) {
|
|
|
|
alert(`Error fetching information from Steam: ${e}`);
|
|
|
|
window.location.reload();
|
|
|
|
};
|
|
|
|
|
|
|
|
console.debug("Fetching app info for DLCs");
|
|
|
|
let key = Object.keys(root)[0];
|
|
|
|
NAME_ELEMENT.textContent = root[key].name;
|
|
|
|
console.debug("Checking key: " + key);
|
|
|
|
let dlc_price = (root[key].price_overview || {final: 0.0}).final / 100;
|
|
|
|
let unit = find_unit((root[key].price_overview || {final_formatted: "£0.00"}).final_formatted);
|
|
|
|
if(root[key].dlc && root[key].dlc.length >= 0) {
|
|
|
|
document.getElementById("dlcs").hidden = false;
|
|
|
|
document.getElementById("dlc-count").textContent = root[key].dlc.length + " ";
|
|
|
|
let _result= await calculate_price(root[key].dlc)
|
|
|
|
dlc_price += _result[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
if(root[key].header_image && root[key].capsule_image) {
|
|
|
|
let element2 = document.createElement("img");
|
|
|
|
element2.src = root[key].header_image;
|
|
|
|
element2.alt = root[key].name;
|
|
|
|
element2.style.height = "2em"
|
|
|
|
NAME_ELEMENT.textContent = null;
|
|
|
|
NAME_ELEMENT.appendChild(element2);
|
|
|
|
}
|
|
|
|
|
|
|
|
update(dlc_price.toFixed(2), unit);
|
|
|
|
|
|
|
|
let element = document.getElementById("comparison");
|
|
|
|
element.hidden = false;
|
|
|
|
for(let item of Object.keys(PRICES)) {
|
|
|
|
let price = compare(dlc_price, item);
|
|
|
|
if(price >= 0.95) {
|
|
|
|
let li = document.createElement("li");
|
|
|
|
li.textContent = `${price}x ${item} (${unit}${PRICES[item]}/pc)`;
|
|
|
|
element.children[1].appendChild(li);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
main();
|