From 8b1cfe012f5b766fd401135dcec7040b038d394c Mon Sep 17 00:00:00 2001
From: Brandon Fowler
Date: Fri, 3 Feb 2023 21:04:56 -0500
Subject: [PATCH 1/7] feat: add Power & Stat Turning stats
---
public/resources/scss/stats.scss | 20 +-
public/resources/ts/calculate-player-stats.ts | 20 ++
public/resources/ts/development-defer.ts | 6 +
public/resources/ts/globals.d.ts | 24 ++
public/resources/ts/stats-defer.ts | 6 +
src/constants.js | 1 +
src/constants/powers.js | 326 ++++++++++++++++++
src/lib.js | 152 ++++++++
views/stats.ejs | 105 ++++--
9 files changed, 623 insertions(+), 37 deletions(-)
create mode 100644 src/constants/powers.js
diff --git a/public/resources/scss/stats.scss b/public/resources/scss/stats.scss
index 488761444f..017d2f7ab1 100644
--- a/public/resources/scss/stats.scss
+++ b/public/resources/scss/stats.scss
@@ -1524,7 +1524,8 @@ a.additional-player-stat:hover {
}
.missing-pet,
-.missing-accessory {
+.missing-accessory,
+.missing-power {
filter: grayscale(0.8);
transition: filter 0.2s;
@@ -2563,6 +2564,23 @@ bonus-stats {
}
}
+.stats-tuning {
+ bonus-stats p {
+ margin: 0;
+ }
+}
+
+.selected-power {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ font-size: 20px;
+
+ .piece {
+ margin-top: 0;
+ }
+}
+
#techno-support {
text-align: center;
padding: 20px;
diff --git a/public/resources/ts/calculate-player-stats.ts b/public/resources/ts/calculate-player-stats.ts
index c397047dc5..6aa31032e7 100644
--- a/public/resources/ts/calculate-player-stats.ts
+++ b/public/resources/ts/calculate-player-stats.ts
@@ -84,6 +84,26 @@ export function getPlayerStats() {
}
}
+ // Thaumaturgist power
+ if (calculated.selected_power) {
+ for (const [name, value] of Object.entries(calculated.selected_power.stats)) {
+ if (!allowedStats.includes(name)) {
+ continue;
+ }
+
+ stats[name].thaumaturgist_power = value;
+ }
+ }
+
+ // Tuning points
+ for (const [name, value] of Object.entries(calculated.tuning_points.distribution)) {
+ if (!allowedStats.includes(name)) {
+ continue;
+ }
+
+ stats[name].tuning_points = value;
+ }
+
// Skill bonus stats
for (const [skill, data] of Object.entries(calculated.levels)) {
const bonusStats: ItemStats = getBonusStat(data.level, `skill_${skill}` as BonusType, data.maxLevel);
diff --git a/public/resources/ts/development-defer.ts b/public/resources/ts/development-defer.ts
index a41ef35884..37755853ee 100644
--- a/public/resources/ts/development-defer.ts
+++ b/public/resources/ts/development-defer.ts
@@ -37,6 +37,12 @@ document.addEventListener("click", (e) => {
} else if (element.hasAttribute("data-upgrade-accessory-index")) {
item =
calculated.missingAccessories.upgrades[parseInt(element.getAttribute("data-upgrade-accessory-index") as string)];
+ } else if (element.hasAttribute("data-power-selected")) {
+ item = calculated.selected_power;
+ } else if (element.hasAttribute("data-power-index")) {
+ item = calculated.unlocked_powers[parseInt(element.getAttribute("data-power-index") as string)];
+ } else if (element.hasAttribute("data-locked-power-index")) {
+ item = calculated.locked_powers[parseInt(element.getAttribute("data-locked-power-index") as string)];
}
console.log(item);
diff --git a/public/resources/ts/globals.d.ts b/public/resources/ts/globals.d.ts
index 56e682e852..af43b8e4fd 100644
--- a/public/resources/ts/globals.d.ts
+++ b/public/resources/ts/globals.d.ts
@@ -569,6 +569,30 @@ declare const calculated: SkyCryptPlayer & {
amount: number;
}[];
reaper_peppers_eaten: number;
+ selected_power: Item & {
+ stats: {
+ [key in StatName]: number;
+ };
+ };
+ unlocked_powers: (Item & {
+ power_name: string;
+ stats: {
+ [key in StatName]: number;
+ };
+ })[];
+ locked_powers: (Item & {
+ power_name: string;
+ stats: {
+ [key in StatName]: number;
+ };
+ })[];
+ tuning_points: {
+ distribution: {
+ [key in StatName]: number;
+ };
+ total: number;
+ used: number;
+ };
};
interface SkyCryptRelativeTime {
diff --git a/public/resources/ts/stats-defer.ts b/public/resources/ts/stats-defer.ts
index 3bdec3138c..8ef169064f 100644
--- a/public/resources/ts/stats-defer.ts
+++ b/public/resources/ts/stats-defer.ts
@@ -263,6 +263,12 @@ function fillLore(element: HTMLElement) {
} else if (element.hasAttribute("data-upgrade-accessory-index")) {
item =
calculated.missingAccessories.upgrades[parseInt(element.getAttribute("data-upgrade-accessory-index") as string)];
+ } else if (element.hasAttribute("data-power-selected")) {
+ item = calculated.selected_power;
+ } else if (element.hasAttribute("data-power-index")) {
+ item = calculated.unlocked_powers[parseInt(element.getAttribute("data-power-index") as string)];
+ } else if (element.hasAttribute("data-locked-power-index")) {
+ item = calculated.locked_powers[parseInt(element.getAttribute("data-locked-power-index") as string)];
}
if (item == undefined) {
diff --git a/src/constants.js b/src/constants.js
index 33f20d9dcf..2d1803ef65 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -19,3 +19,4 @@ export * from "./constants/skills.js";
export * from "./constants/skins-animations.js";
export * from "./constants/tags.js";
export * from "./constants/accessories.js";
+export * from "./constants/powers.js";
diff --git a/src/constants/powers.js b/src/constants/powers.js
new file mode 100644
index 0000000000..ac24533f85
--- /dev/null
+++ b/src/constants/powers.js
@@ -0,0 +1,326 @@
+export const POWERS = {
+ fortuitous: {
+ type: "Starter",
+ id: 266,
+ stats: {
+ health: 3.35,
+ defense: 1.2,
+ strength: 4.8,
+ crit_chance: 4.35,
+ crit_damage: 4.8,
+ },
+ },
+ pretty: {
+ type: "Starter",
+ id: 38,
+ Damage: 8,
+ stats: {
+ health: 1.65,
+ defense: 1.2,
+ speed: 0.65,
+ strength: 4.8,
+ intelligence: 10.8,
+ crit_chance: 0.475,
+ crit_damage: 1.2,
+ },
+ },
+ protected: {
+ type: "Starter",
+ id: 307,
+ stats: {
+ health: 11.75,
+ defense: 10.8,
+ strength: 2.4,
+ crit_chance: 0.475,
+ crit_damage: 1.2,
+ },
+ },
+ simple: {
+ type: "Starter",
+ id: 1,
+ stats: {
+ health: 5.02,
+ defense: 3.6,
+ speed: 1.2,
+ strength: 3.6,
+ intelligence: 5.4,
+ crit_chance: 1.45,
+ crit_damage: 3.6,
+ },
+ },
+ warrior: {
+ type: "Starter",
+ id: 264,
+ stats: {
+ health: 3.35,
+ defense: 1.2,
+ strength: 8.4,
+ crit_chance: 2.4,
+ crit_damage: 6,
+ },
+ },
+ commando: {
+ type: "Intermediate",
+ id: 280,
+ stats: {
+ health: 5.02,
+ defense: 2.4,
+ strength: 8.4,
+ crit_chance: 0.475,
+ crit_damage: 8.4,
+ },
+ },
+ disciplined: {
+ type: "Intermediate",
+ id: 267,
+ stats: {
+ health: 5.02,
+ defense: 2.4,
+ strength: 7.2,
+ crit_chance: 1.45,
+ crit_damage: 7.2,
+ },
+ },
+ inspired: {
+ type: "Intermediate",
+ id: 351,
+ Damage: 4,
+ stats: {
+ health: 1.65,
+ defense: 1.2,
+ strength: 4.8,
+ intelligence: 16.2,
+ crit_chance: 0.95,
+ crit_damage: 3.6,
+ },
+ },
+ ominous: {
+ type: "Intermediate",
+ id: 410,
+ stats: {
+ health: 5.02,
+ speed: 0.95,
+ strength: 3.6,
+ intelligence: 6.1,
+ crit_chance: 1.45,
+ crit_damage: 3.6,
+ bonus_attack_speed: 0.9,
+ },
+ },
+ prepared: {
+ type: "Intermediate",
+ id: 297,
+ stats: {
+ health: 12.4,
+ speed: 11.3,
+ strength: 1.95,
+ crit_chance: 0.4,
+ crit_damage: 0.95,
+ },
+ },
+ scorching: {
+ type: "Advanced Stone",
+ id: 400,
+ stats: {
+ strength: 8.4,
+ crit_damage: 9.6,
+ bonus_attack_speed: 1.8,
+ },
+ unique: {
+ ferocity: 7,
+ },
+ },
+ healthy: {
+ type: "Advanced Stone",
+ texture_path: "/item/SCORCHED_BOOKS",
+ stats: {
+ health: 33.6,
+ },
+ unique: {
+ health: 200,
+ },
+ },
+ slender: {
+ type: "Advanced Stone",
+ texture_path: "/item/HAZMAT_ENDERMAN",
+ stats: {
+ health: 8.4,
+ defense: 6,
+ speed: 0.6,
+ strength: 6,
+ intelligence: 7.2,
+ crit_damage: 6,
+ bonus_attack_speed: 1.1,
+ },
+ unique: {
+ defense: 100,
+ strength: 50,
+ },
+ },
+ strong: {
+ type: "Advanced Stone",
+ texture_path: "/item/MANDRAA",
+ stats: {
+ strength: 12,
+ crit_damage: 12,
+ },
+ unique: {
+ strength: 25,
+ crit_damage: 25,
+ },
+ },
+ bizarre: {
+ type: "Advanced Stone",
+ texture_path: "/item/ECCENTRIC_PAINTING",
+ stats: {
+ strength: -2.4,
+ intelligence: 43.2,
+ crit_damage: -2.4,
+ },
+ unique: {
+ ability_damage: 5,
+ },
+ },
+ demonic: {
+ type: "Advanced Stone",
+ texture_path: "/item/HORNS_OF_TORMENT",
+ stats: {
+ strength: 5.5,
+ intelligence: 27.725,
+ },
+ unique: {
+ crit_damage: 50,
+ },
+ },
+ hurtful: {
+ type: "Advanced Stone",
+ texture_path: "/item/MAGMA_URCHIN",
+ stats: {
+ strength: 4.8,
+ crit_damage: 19.2,
+ },
+ unique: {
+ bonus_attack_speed: 15,
+ },
+ },
+ pleasant: {
+ type: "Advanced Stone",
+ texture_path: "/item/PRECIOUS_PEARL",
+ stats: {
+ health: 13.45,
+ defense: 14.4,
+ },
+ },
+ adept: {
+ type: "Advanced Stone",
+ texture_path: "/item/END_STONE_SHULKER",
+ stats: {
+ health: 16.8,
+ defense: 9.6,
+ intelligence: 3.6,
+ },
+ unique: {
+ health: 100,
+ defense: 40,
+ },
+ },
+ bloody: {
+ type: "Advanced Stone",
+ texture_path: "/item/BEATING_HEART",
+ stats: {
+ strength: 10.8,
+ intelligence: 3.6,
+ crit_damage: 10.8,
+ },
+ unique: {
+ bonus_attack_speed: 10,
+ },
+ },
+ forceful: {
+ type: "Advanced Stone",
+ texture_path: "/item/ACACIA_BIRDHOUSE",
+ stats: {
+ health: 1.7,
+ strength: 18,
+ crit_damage: 4.8,
+ },
+ unique: {
+ ferocity: 4,
+ },
+ },
+ itchy: {
+ type: "Advanced Stone",
+ texture_path: "/item/FURBALL",
+ stats: {
+ speed: 0.6,
+ strength: 7.2,
+ crit_damage: 8.4,
+ bonus_attack_speed: 2.15,
+ },
+ unique: {
+ strength: 15,
+ crit_damage: 15,
+ },
+ },
+ mythical: {
+ type: "Advanced Stone",
+ texture_path: "/item/OBSIDIAN_TABLET",
+ stats: {
+ health: 5.7,
+ defense: 4.05,
+ speed: 0.95,
+ strength: 4.05,
+ intelligence: 6.1,
+ crit_chance: 1.65,
+ crit_damage: 4.05,
+ },
+ unique: {
+ health: 150,
+ strength: 40,
+ },
+ },
+ shaded: {
+ type: "Advanced Stone",
+ texture_path: "/item/ENDER_MONOCLE",
+ stats: {
+ intelligence: 36,
+ },
+ unique: {
+ ability_damage: 3,
+ },
+ },
+ silky: {
+ type: "Advanced Stone",
+ texture_path: "/item/LUXURIOUS_SPOOL",
+ stats: {
+ speed: 0.6,
+ crit_damage: 22.8,
+ },
+ unique: {
+ bonus_attack_speed: 5,
+ },
+ },
+ sweet: {
+ type: "Advanced Stone",
+ texture_path: "/item/ROCK_CANDY",
+ stats: {
+ health: 15.1,
+ defense: 10.8,
+ speed: 1.2,
+ },
+ unique: {
+ speed: 5,
+ },
+ },
+};
+
+export const POWER_TUNING_MULTIPLIERS = {
+ health: 5,
+ defense: 1,
+ speed: 1.5,
+ strength: 1,
+ crit_chance: 0.2,
+ crit_damage: 1,
+ bonus_attack_speed: 0.3,
+ intelligence: 2,
+};
diff --git a/src/lib.js b/src/lib.js
index 38e4a71ee2..bd36616986 100644
--- a/src/lib.js
+++ b/src/lib.js
@@ -824,6 +824,73 @@ function getMinionSlots(minions) {
return output;
}
+function addPowerLoreStat(lore, stat, value) {
+ const statData = constants.STATS_DATA[stat];
+
+ lore.push(
+ `§${statData.color}${value >= 0 ? "+" : ""}${Math.round(value).toLocaleString()}${statData.symbol} ${
+ statData.nameLore
+ }`
+ );
+}
+
+function getPower(name, magicalPower) {
+ const powerMultiplier = 29.97 * Math.pow(Math.log(0.0019 * magicalPower + 1), 1.2);
+ const displayName = helper.titleCase(name);
+
+ const itemData = {
+ power_name: name,
+ display_name: displayName,
+ tag: {
+ display: {
+ name: displayName,
+ },
+ },
+ stats: {},
+ };
+
+ if (name in constants.POWERS) {
+ const info = constants.POWERS[name];
+
+ const stats = {};
+ const lore = [`§8${info.type} Power`, "", "§7Stats:"];
+
+ for (const [name, value] of Object.entries(info.stats)) {
+ stats[name] = value * powerMultiplier;
+
+ addPowerLoreStat(lore, name, stats[name]);
+ }
+
+ const combined = { ...stats };
+
+ if (info.unique) {
+ lore.push("");
+ lore.push("§7Unique Power Bonus:");
+
+ for (const [name, value] of Object.entries(info.unique)) {
+ if (!combined[name]) {
+ combined[name] = value;
+ } else {
+ combined[name] += value;
+ }
+
+ addPowerLoreStat(lore, name, value);
+ }
+ }
+
+ lore.push("", `You Have: §6${magicalPower} Magical Power`);
+
+ itemData.id = info.id;
+ itemData.rarity = "uncommon";
+ itemData.Damage = info.Damage;
+ itemData.texture_path = info.texture_path;
+ itemData.tag.display.Lore = lore;
+ itemData.stats = combined;
+ }
+
+ return helper.generateItem(itemData);
+}
+
export const getItems = async (
profile,
customTextures = false,
@@ -2087,6 +2154,91 @@ export async function getStats(
active: userProfile.nether_island_player_data?.abiphone?.active_contacts?.length || 0,
};
+ output.magical_power = {
+ total: 0,
+ };
+
+ for (const [rarity, value] of Object.entries(constants.MAGICAL_POWER)) {
+ output.magical_power[rarity] = items.accessory_rarities[rarity] * value || 0;
+ }
+
+ output.magical_power.hegemony = items.accessory_rarities.hegemony
+ ? constants.MAGICAL_POWER[items.accessory_rarities.hegemony.rarity]
+ : 0;
+
+ if (items.accessory_rarities.abicase) {
+ output.magical_power.abicase = Math.floor(output.abiphone.active / 2);
+ }
+
+ for (const value of Object.values(output.magical_power)) {
+ output.magical_power.total += value;
+ }
+
+ output.selected_power = null;
+ output.unlocked_powers = [];
+ output.locked_powers = [];
+
+ output.tuning_points = {
+ distribution: {},
+ total: Math.floor(output.magical_power.total / 10),
+ used: 0,
+ };
+
+ if ("accessory_bag_storage" in userProfile) {
+ const selectedPower = userProfile.accessory_bag_storage.selected_power;
+
+ if (selectedPower) {
+ output.selected_power = getPower(selectedPower, output.magical_power.total);
+ }
+
+ const unlockedPowers = Object.entries(constants.POWERS)
+ .filter(
+ ([_, power]) => power.type === "Starter" || (output.levels.combat.level >= 15 && power.type === "Intermediate")
+ )
+ .map(([name, _]) => name);
+
+ if ("unlocked_powers" in userProfile.accessory_bag_storage) {
+ unlockedPowers.push(...userProfile.accessory_bag_storage.unlocked_powers);
+ }
+
+ for (const [power] of Object.entries(constants.POWERS)) {
+ if (unlockedPowers.includes(power)) {
+ continue;
+ }
+
+ output.locked_powers.push(getPower(power, output.magical_power.total));
+ }
+
+ const selectedIndex = unlockedPowers.indexOf(selectedPower);
+
+ if (selectedIndex !== -1) {
+ unlockedPowers.splice(selectedIndex, 1);
+ }
+
+ for (const power of unlockedPowers) {
+ output.unlocked_powers.push(getPower(power, output.magical_power.total));
+ }
+
+ const tuning = userProfile.accessory_bag_storage.tuning.slot_0;
+
+ for (const [hypixelStat, stat] of Object.entries({
+ walk_speed: "speed",
+ critical_chance: "crit_chance",
+ critical_damage: "crit_damage",
+ })) {
+ tuning[stat] = tuning[hypixelStat];
+
+ delete tuning[hypixelStat];
+ }
+
+ for (const [name, value] of Object.entries(tuning)) {
+ if (!value) continue;
+
+ output.tuning_points.used += value;
+ output.tuning_points.distribution[name] = value * constants.POWER_TUNING_MULTIPLIERS[name];
+ }
+ }
+
// MISC
const misc = {};
diff --git a/views/stats.ejs b/views/stats.ejs
index daa1f86ac9..472ad20bb2 100644
--- a/views/stats.ejs
+++ b/views/stats.ejs
@@ -918,6 +918,7 @@ const metaDescription = getMetaDescription()
Armor
<% if(!items.no_inventory){ %>Weapons<% } %>
<% if(!items.no_inventory){ %>Accessories<% } %>
+ <% if(!items.no_inventory){ %>Power<% } %>
<% if(calculated.pets.length > 0){ %>Pets<% } %>
<% if(!items.no_inventory){ %>Inventory<% } %>
Skills
@@ -935,7 +936,7 @@ const metaDescription = getMetaDescription()
const notAvailable = [];
if(items.no_inventory)
- notAvailable.push('Weapons', 'Accessories', 'Inventory', 'Storage');
+ notAvailable.push('Weapons', 'Accessories', 'Power', 'Inventory', 'Storage');
if(items.no_personal_vault)
notAvailable.push('Personal Vault');
@@ -1095,41 +1096,6 @@ const metaDescription = getMetaDescription()
Recombobulated:
<%= items.accessories.filter(a => a.isUnique && a.extra?.recombobulated).length %> / <%= constants.RECOMBABLE_ACCESSORIES_COUNT %>
- <%
- const rarities = items.accessory_rarities;
- const player_magical_power = {}
-
- for (const rarity in constants.MAGICAL_POWER) {
- player_magical_power[rarity] = 0
- player_magical_power[rarity] += rarities[rarity] * constants.MAGICAL_POWER[rarity];
- }
-
- const mp_hegemony = rarities.hegemony ? constants.MAGICAL_POWER[rarities.hegemony.rarity] : 0
- const mp_total = Object.values(player_magical_power).reduce((a, b) => a + b) + mp_hegemony + Math.floor(calculated.abiphone.active / 2);
- %>
- );' class='grey-text'>Abicase = +<%= Math.floor(calculated.abiphone.active / 2).toLocaleString() %> MP
- <% } %>
-
- Total: <%= mp_total.toLocaleString() %> Magical Power
- ">
- Magical Power: <%= mp_total.toLocaleString() %>
-
<% if(items.accessories.find(a => !a.isInactive) != undefined){ %>
@@ -1193,6 +1159,73 @@ const metaDescription = getMetaDescription()
<% } %>
+
+
+
+
Magical Power: <%= calculated.magical_power.total.toLocaleString() %>
+
+ <% if (calculated.selected_power) { %>
+
+
+ <% itemIcon(calculated.selected_power, ['piece-icon']); %>
+
+
+ <%= calculated.selected_power.display_name %>
+
+
+
+ <% } else { %>
+
+ <%= calculated.display_name %> hasn't selected a power.
+
+ <% } %>
+
+
+ <% for (const [i, power] of calculated.unlocked_powers.entries()) { %>
+
+ <% itemIcon(power, ['piece-icon']); %>
+
+ <% } %>
+
+ <% if (calculated.locked_powers.length) { %>
+
+
+ <% for (const [i, power] of calculated.locked_powers.entries()) { %>
+
+ <% itemIcon(power, ['piece-icon']); %>
+
+ <% } %>
+
+ <% } %>
+
+
+
Used Points:
+
<%= calculated.tuning_points.used %> / <%= calculated.tuning_points.total %>
+
+
+
<% } %>
<% if(calculated.pets.length > 0){ %>
From f3c87f3f4e4ead1b40211747e3ae5dec82b94ee6 Mon Sep 17 00:00:00 2001
From: Brandon Fowler
Date: Sat, 4 Feb 2023 15:47:53 -0500
Subject: [PATCH 2/7] add spaces to power lore stats
---
src/lib.js | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/lib.js b/src/lib.js
index bd36616986..58116d5e16 100644
--- a/src/lib.js
+++ b/src/lib.js
@@ -828,7 +828,7 @@ function addPowerLoreStat(lore, stat, value) {
const statData = constants.STATS_DATA[stat];
lore.push(
- `§${statData.color}${value >= 0 ? "+" : ""}${Math.round(value).toLocaleString()}${statData.symbol} ${
+ `§${statData.color}${value >= 0 ? "+" : ""}${Math.round(value).toLocaleString()} ${statData.symbol} ${
statData.nameLore
}`
);
@@ -864,8 +864,7 @@ function getPower(name, magicalPower) {
const combined = { ...stats };
if (info.unique) {
- lore.push("");
- lore.push("§7Unique Power Bonus:");
+ lore.push("", "§7Unique Power Bonus:");
for (const [name, value] of Object.entries(info.unique)) {
if (!combined[name]) {
From 6ddb8b69253614b809d26597ab52364913c41448 Mon Sep 17 00:00:00 2001
From: Brandon Fowler
Date: Sun, 5 Feb 2023 02:42:21 -0500
Subject: [PATCH 3/7] correct power order and types
---
public/resources/ts/globals.d.ts | 4 +-
src/constants/powers.js | 275 ++++++++++++++++---------------
src/helper.js | 24 +++
src/lib.js | 46 ++----
views/stats.ejs | 21 +--
5 files changed, 184 insertions(+), 186 deletions(-)
diff --git a/public/resources/ts/globals.d.ts b/public/resources/ts/globals.d.ts
index af43b8e4fd..2c0f63a9e7 100644
--- a/public/resources/ts/globals.d.ts
+++ b/public/resources/ts/globals.d.ts
@@ -575,13 +575,13 @@ declare const calculated: SkyCryptPlayer & {
};
};
unlocked_powers: (Item & {
- power_name: string;
+ power_type: string;
stats: {
[key in StatName]: number;
};
})[];
locked_powers: (Item & {
- power_name: string;
+ power_type: string;
stats: {
[key in StatName]: number;
};
diff --git a/src/constants/powers.js b/src/constants/powers.js
index ac24533f85..c63cb4ef2c 100644
--- a/src/constants/powers.js
+++ b/src/constants/powers.js
@@ -1,126 +1,7 @@
export const POWERS = {
- fortuitous: {
- type: "Starter",
- id: 266,
- stats: {
- health: 3.35,
- defense: 1.2,
- strength: 4.8,
- crit_chance: 4.35,
- crit_damage: 4.8,
- },
- },
- pretty: {
- type: "Starter",
- id: 38,
- Damage: 8,
- stats: {
- health: 1.65,
- defense: 1.2,
- speed: 0.65,
- strength: 4.8,
- intelligence: 10.8,
- crit_chance: 0.475,
- crit_damage: 1.2,
- },
- },
- protected: {
- type: "Starter",
- id: 307,
- stats: {
- health: 11.75,
- defense: 10.8,
- strength: 2.4,
- crit_chance: 0.475,
- crit_damage: 1.2,
- },
- },
- simple: {
- type: "Starter",
- id: 1,
- stats: {
- health: 5.02,
- defense: 3.6,
- speed: 1.2,
- strength: 3.6,
- intelligence: 5.4,
- crit_chance: 1.45,
- crit_damage: 3.6,
- },
- },
- warrior: {
- type: "Starter",
- id: 264,
- stats: {
- health: 3.35,
- defense: 1.2,
- strength: 8.4,
- crit_chance: 2.4,
- crit_damage: 6,
- },
- },
- commando: {
- type: "Intermediate",
- id: 280,
- stats: {
- health: 5.02,
- defense: 2.4,
- strength: 8.4,
- crit_chance: 0.475,
- crit_damage: 8.4,
- },
- },
- disciplined: {
- type: "Intermediate",
- id: 267,
- stats: {
- health: 5.02,
- defense: 2.4,
- strength: 7.2,
- crit_chance: 1.45,
- crit_damage: 7.2,
- },
- },
- inspired: {
- type: "Intermediate",
- id: 351,
- Damage: 4,
- stats: {
- health: 1.65,
- defense: 1.2,
- strength: 4.8,
- intelligence: 16.2,
- crit_chance: 0.95,
- crit_damage: 3.6,
- },
- },
- ominous: {
- type: "Intermediate",
- id: 410,
- stats: {
- health: 5.02,
- speed: 0.95,
- strength: 3.6,
- intelligence: 6.1,
- crit_chance: 1.45,
- crit_damage: 3.6,
- bonus_attack_speed: 0.9,
- },
- },
- prepared: {
- type: "Intermediate",
- id: 297,
- stats: {
- health: 12.4,
- speed: 11.3,
- strength: 1.95,
- crit_chance: 0.4,
- crit_damage: 0.95,
- },
- },
scorching: {
- type: "Advanced Stone",
- id: 400,
+ type: "Marvelous Stone",
+ texture_path: "/item/SCORCHED_BOOKS",
stats: {
strength: 8.4,
crit_damage: 9.6,
@@ -131,8 +12,8 @@ export const POWERS = {
},
},
healthy: {
- type: "Advanced Stone",
- texture_path: "/item/SCORCHED_BOOKS",
+ type: "Grandiose Stone",
+ texture_path: "/item/VITAMIN_DEATH",
stats: {
health: 33.6,
},
@@ -141,7 +22,7 @@ export const POWERS = {
},
},
slender: {
- type: "Advanced Stone",
+ type: "Grandiose Stone",
texture_path: "/item/HAZMAT_ENDERMAN",
stats: {
health: 8.4,
@@ -158,7 +39,7 @@ export const POWERS = {
},
},
strong: {
- type: "Advanced Stone",
+ type: "Grandiose Stone",
texture_path: "/item/MANDRAA",
stats: {
strength: 12,
@@ -170,7 +51,7 @@ export const POWERS = {
},
},
bizarre: {
- type: "Advanced Stone",
+ type: "Master Stone",
texture_path: "/item/ECCENTRIC_PAINTING",
stats: {
strength: -2.4,
@@ -182,7 +63,7 @@ export const POWERS = {
},
},
demonic: {
- type: "Advanced Stone",
+ type: "Master Stone",
texture_path: "/item/HORNS_OF_TORMENT",
stats: {
strength: 5.5,
@@ -193,7 +74,7 @@ export const POWERS = {
},
},
hurtful: {
- type: "Advanced Stone",
+ type: "Master Stone",
texture_path: "/item/MAGMA_URCHIN",
stats: {
strength: 4.8,
@@ -204,7 +85,7 @@ export const POWERS = {
},
},
pleasant: {
- type: "Advanced Stone",
+ type: "Master Stone",
texture_path: "/item/PRECIOUS_PEARL",
stats: {
health: 13.45,
@@ -280,6 +161,19 @@ export const POWERS = {
},
},
shaded: {
+ type: "Advanced Stone",
+ texture_path: "/item/DARK_ORB",
+ stats: {
+ speed: 0.6,
+ strength: 4.8,
+ crit_damage: 18,
+ },
+ unique: {
+ bonus_attack_speed: 3,
+ ferocity: 3,
+ },
+ },
+ sighted: {
type: "Advanced Stone",
texture_path: "/item/ENDER_MONOCLE",
stats: {
@@ -290,7 +184,7 @@ export const POWERS = {
},
},
silky: {
- type: "Advanced Stone",
+ type: "Intermediate Stone",
texture_path: "/item/LUXURIOUS_SPOOL",
stats: {
speed: 0.6,
@@ -301,7 +195,7 @@ export const POWERS = {
},
},
sweet: {
- type: "Advanced Stone",
+ type: "Intermediate Stone",
texture_path: "/item/ROCK_CANDY",
stats: {
health: 15.1,
@@ -312,6 +206,125 @@ export const POWERS = {
speed: 5,
},
},
+ commando: {
+ type: "Intermediate",
+ id: 280,
+ stats: {
+ health: 5.02,
+ defense: 2.4,
+ strength: 8.4,
+ crit_chance: 0.475,
+ crit_damage: 8.4,
+ },
+ },
+ disciplined: {
+ type: "Intermediate",
+ id: 267,
+ stats: {
+ health: 5.02,
+ defense: 2.4,
+ strength: 7.2,
+ crit_chance: 1.45,
+ crit_damage: 7.2,
+ },
+ },
+ inspired: {
+ type: "Intermediate",
+ id: 351,
+ Damage: 4,
+ stats: {
+ health: 1.65,
+ defense: 1.2,
+ strength: 4.8,
+ intelligence: 16.2,
+ crit_chance: 0.95,
+ crit_damage: 3.6,
+ },
+ },
+ ominous: {
+ type: "Intermediate",
+ id: 410,
+ stats: {
+ health: 5.02,
+ speed: 0.95,
+ strength: 3.6,
+ intelligence: 6.1,
+ crit_chance: 1.45,
+ crit_damage: 3.6,
+ bonus_attack_speed: 0.9,
+ },
+ },
+ prepared: {
+ type: "Intermediate",
+ id: 297,
+ stats: {
+ health: 12.4,
+ speed: 11.3,
+ strength: 1.95,
+ crit_chance: 0.4,
+ crit_damage: 0.95,
+ },
+ },
+ fortuitous: {
+ type: "Starter",
+ id: 266,
+ stats: {
+ health: 3.35,
+ defense: 1.2,
+ strength: 4.8,
+ crit_chance: 4.35,
+ crit_damage: 4.8,
+ },
+ },
+ pretty: {
+ type: "Starter",
+ id: 38,
+ Damage: 8,
+ stats: {
+ health: 1.65,
+ defense: 1.2,
+ speed: 0.65,
+ strength: 4.8,
+ intelligence: 10.8,
+ crit_chance: 0.475,
+ crit_damage: 1.2,
+ },
+ },
+ protected: {
+ type: "Starter",
+ id: 307,
+ stats: {
+ health: 11.75,
+ defense: 10.8,
+ strength: 2.4,
+ crit_chance: 0.475,
+ crit_damage: 1.2,
+ },
+ },
+ simple: {
+ type: "Starter",
+ id: 1,
+ stats: {
+ health: 5.02,
+ defense: 3.6,
+ speed: 1.2,
+ strength: 3.6,
+ intelligence: 5.4,
+ crit_chance: 1.45,
+ crit_damage: 3.6,
+ },
+ },
+ warrior: {
+ type: "Starter",
+ id: 264,
+ stats: {
+ health: 3.35,
+ defense: 1.2,
+ strength: 8.4,
+ crit_chance: 2.4,
+ crit_damage: 6,
+ },
+ },
};
export const POWER_TUNING_MULTIPLIERS = {
diff --git a/src/helper.js b/src/helper.js
index 0ca19c7b98..0ed0e91d4e 100644
--- a/src/helper.js
+++ b/src/helper.js
@@ -1080,3 +1080,27 @@ export function getAnimatedTexture(item) {
return deepResults[0] ?? false;
}
+
+/**
+ * @param {string} enrichment
+ * @returns string
+ * @description takes an enrichment name and returns the corresponding stat name
+ */
+export function enrichmentToStatName(enrichment) {
+ switch (enrichment) {
+ case "walk_speed":
+ return "speed";
+
+ case "critical_chance":
+ return "crit_chance";
+
+ case "critical_damage":
+ return "crit_damage";
+
+ case "attack_speed":
+ return "bonus_attack_speed";
+
+ default:
+ return enrichment;
+ }
+}
diff --git a/src/lib.js b/src/lib.js
index 58116d5e16..a31b20d917 100644
--- a/src/lib.js
+++ b/src/lib.js
@@ -839,7 +839,6 @@ function getPower(name, magicalPower) {
const displayName = helper.titleCase(name);
const itemData = {
- power_name: name,
display_name: displayName,
tag: {
display: {
@@ -884,6 +883,7 @@ function getPower(name, magicalPower) {
itemData.Damage = info.Damage;
itemData.texture_path = info.texture_path;
itemData.tag.display.Lore = lore;
+ itemData.power_type = info.type;
itemData.stats = combined;
}
@@ -2190,51 +2190,31 @@ export async function getStats(
output.selected_power = getPower(selectedPower, output.magical_power.total);
}
- const unlockedPowers = Object.entries(constants.POWERS)
- .filter(
- ([_, power]) => power.type === "Starter" || (output.levels.combat.level >= 15 && power.type === "Intermediate")
- )
- .map(([name, _]) => name);
+ const unlockedPowers = new Set(userProfile.accessory_bag_storage.unlocked_powers);
- if ("unlocked_powers" in userProfile.accessory_bag_storage) {
- unlockedPowers.push(...userProfile.accessory_bag_storage.unlocked_powers);
- }
-
- for (const [power] of Object.entries(constants.POWERS)) {
- if (unlockedPowers.includes(power)) {
+ for (const name of Object.keys(constants.POWERS)) {
+ if (name === selectedPower) {
continue;
}
- output.locked_powers.push(getPower(power, output.magical_power.total));
- }
-
- const selectedIndex = unlockedPowers.indexOf(selectedPower);
-
- if (selectedIndex !== -1) {
- unlockedPowers.splice(selectedIndex, 1);
- }
+ const power = getPower(name, output.magical_power.total);
+ const unlocked =
+ power.power_type === "Starter" ||
+ (power.power_type === "Intermediate" && output.levels.combat.level >= 15) ||
+ unlockedPowers.has(name);
- for (const power of unlockedPowers) {
- output.unlocked_powers.push(getPower(power, output.magical_power.total));
+ output[unlocked ? "unlocked_powers" : "locked_powers"].push(power);
}
const tuning = userProfile.accessory_bag_storage.tuning.slot_0;
- for (const [hypixelStat, stat] of Object.entries({
- walk_speed: "speed",
- critical_chance: "crit_chance",
- critical_damage: "crit_damage",
- })) {
- tuning[stat] = tuning[hypixelStat];
-
- delete tuning[hypixelStat];
- }
-
for (const [name, value] of Object.entries(tuning)) {
if (!value) continue;
+ const stat = helper.enrichmentToStatName(name);
+
output.tuning_points.used += value;
- output.tuning_points.distribution[name] = value * constants.POWER_TUNING_MULTIPLIERS[name];
+ output.tuning_points.distribution[stat] = value * constants.POWER_TUNING_MULTIPLIERS[stat];
}
}
diff --git a/views/stats.ejs b/views/stats.ejs
index 472ad20bb2..ab607cf930 100644
--- a/views/stats.ejs
+++ b/views/stats.ejs
@@ -234,25 +234,6 @@ function formatEnrichment(string) {
return enrichment
}
-function enrichmentToStatName(enrichment) {
- switch (enrichment) {
- case 'walk_speed':
- return 'speed'
-
- case 'critical_chance':
- return 'crit_chance'
-
- case 'critical_damage':
- return 'crit_damage'
-
- case 'attack_speed':
- return 'bonus_attack_speed'
-
- default:
- return enrichment
- }
-}
-
function getEnrichments(accessories) {
const enrichmentCounts = {}
const filteredAccessories = accessories
@@ -272,7 +253,7 @@ function getEnrichments(accessories) {
Enrichments:
<%
for (const [enrichment, amount] of Object.entries(enrichmentCounts)) {
- const stat = enrichmentToStatName(enrichment)
+ const stat = helper.enrichmentToStatName(enrichment)
%>
">
<%= amount %>× <%= formatEnrichment(enrichment) %>
From d871b3a87415b1fc12ed2b4a922e162a8e38c010 Mon Sep 17 00:00:00 2001
From: Brandon Fowler
Date: Sun, 19 Feb 2023 13:17:10 -0500
Subject: [PATCH 4/7] fix: slot_0 might be undefined
---
src/lib.js | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/src/lib.js b/src/lib.js
index ba7d5594ed..db4d64b31b 100644
--- a/src/lib.js
+++ b/src/lib.js
@@ -2257,15 +2257,17 @@ export async function getStats(
output[unlocked ? "unlocked_powers" : "locked_powers"].push(power);
}
- const tuning = userProfile.accessory_bag_storage.tuning.slot_0;
+ const tuning = userProfile.accessory_bag_storage.tuning?.slot_0;
- for (const [name, value] of Object.entries(tuning)) {
- if (!value) continue;
+ if (tuning) {
+ for (const [name, value] of Object.entries(tuning)) {
+ if (!value) continue;
- const stat = helper.enrichmentToStatName(name);
+ const stat = helper.enrichmentToStatName(name);
- output.tuning_points.used += value;
- output.tuning_points.distribution[stat] = value * constants.POWER_TUNING_MULTIPLIERS[stat];
+ output.tuning_points.used += value;
+ output.tuning_points.distribution[stat] = value * constants.POWER_TUNING_MULTIPLIERS[stat];
+ }
}
}
From 815ef7b885c34e561bb9ca9eb016ac78d57ec1c4 Mon Sep 17 00:00:00 2001
From: Brandon Fowler
Date: Sun, 19 Feb 2023 13:17:46 -0500
Subject: [PATCH 5/7] fix: accessories were counted twice for MP
---
src/lib.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/lib.js b/src/lib.js
index db4d64b31b..f79f47c05e 100644
--- a/src/lib.js
+++ b/src/lib.js
@@ -1158,7 +1158,7 @@ export const getItems = async (
accessories.push(insertAccessory);
accessoryIds.push(id);
- accessoryRarities[insertAccessory.rarity]++;
+
if (insertAccessory.isInactive === false) {
accessoryRarities[insertAccessory.rarity]++;
if (id == "HEGEMONY_ARTIFACT") {
From 7fb8293a7a68e56c8e82093771c06dd0e5cc4743 Mon Sep 17 00:00:00 2001
From: Brandon Fowler
Date: Tue, 11 Jul 2023 01:47:57 -0400
Subject: [PATCH 6/7] add new powers
---
common/constants/stats.js | 9 +++++++
src/constants/powers.js | 53 +++++++++++++++++++++++++++++++++++++++
src/lib.js | 1 +
3 files changed, 63 insertions(+)
diff --git a/common/constants/stats.js b/common/constants/stats.js
index 1c94bd4eb0..a2ef2fad27 100644
--- a/common/constants/stats.js
+++ b/common/constants/stats.js
@@ -237,6 +237,15 @@ export const STATS_DATA = {
suffix: "",
color: "3",
},
+ combat_wisdom: {
+ name: "Combat Wisdom",
+ nameLore: "Combat Wisdom",
+ nameShort: "Combat Wisdom",
+ nameTiny: "CW",
+ symbol: "☯",
+ suffix: "",
+ color: "3",
+ },
mana_regen: {
name: "Mana Regen",
nameLore: "Mana Regen",
diff --git a/src/constants/powers.js b/src/constants/powers.js
index c63cb4ef2c..b2fcf7b083 100644
--- a/src/constants/powers.js
+++ b/src/constants/powers.js
@@ -62,6 +62,22 @@ export const POWERS = {
ability_damage: 5,
},
},
+ bubba: {
+ type: "Master Stone",
+ texture_path: "/item/BUBBA_BLISTER",
+ stats: {
+ strength: 6,
+ crit_damage: 10.8,
+ defense: -9.6,
+ true_defense: 1.2,
+ bonus_attack_speed: 1.8,
+ health: 5.1,
+ crit_chance: 0.9,
+ },
+ unique: {
+ combat_wisdom: 2,
+ },
+ },
demonic: {
type: "Master Stone",
texture_path: "/item/HORNS_OF_TORMENT",
@@ -117,6 +133,20 @@ export const POWERS = {
bonus_attack_speed: 10,
},
},
+ crumbly: {
+ type: "Advanced Stone",
+ texture_path: "/item/CHOCOLATE_CHIP",
+ stats: {
+ mending: 1.8,
+ intelligence: 5.4,
+ true_defense: 0.6,
+ vitality: 2.4,
+ health: 10.1,
+ },
+ unique: {
+ speed: 25,
+ },
+ },
forceful: {
type: "Advanced Stone",
texture_path: "/item/ACACIA_BIRDHOUSE",
@@ -206,6 +236,19 @@ export const POWERS = {
speed: 5,
},
},
+ sanguisuge: {
+ type: "Starter",
+ texture_path: "/item/DISPLACED_LEECH",
+ stats: {
+ strength: 12,
+ vitality: 1.2,
+ crit_damage: 4.8,
+ health: 5.1,
+ },
+ unique: {
+ intelligence: 100,
+ },
+ },
commando: {
type: "Intermediate",
id: 280,
@@ -216,6 +259,7 @@ export const POWERS = {
crit_chance: 0.475,
crit_damage: 8.4,
},
+ unlocked_at: 15,
},
disciplined: {
type: "Intermediate",
@@ -227,6 +271,7 @@ export const POWERS = {
crit_chance: 1.45,
crit_damage: 7.2,
},
+ unlocked_at: 15,
},
inspired: {
type: "Intermediate",
@@ -240,6 +285,7 @@ export const POWERS = {
crit_chance: 0.95,
crit_damage: 3.6,
},
+ unlocked_at: 15,
},
ominous: {
type: "Intermediate",
@@ -253,6 +299,7 @@ export const POWERS = {
crit_damage: 3.6,
bonus_attack_speed: 0.9,
},
+ unlocked_at: 15,
},
prepared: {
type: "Intermediate",
@@ -264,6 +311,7 @@ export const POWERS = {
crit_chance: 0.4,
crit_damage: 0.95,
},
+ unlocked_at: 15,
},
fortuitous: {
type: "Starter",
@@ -275,6 +323,7 @@ export const POWERS = {
crit_chance: 4.35,
crit_damage: 4.8,
},
+ unlocked_at: 0,
},
pretty: {
type: "Starter",
@@ -289,6 +338,7 @@ export const POWERS = {
crit_chance: 0.475,
crit_damage: 1.2,
},
+ unlocked_at: 0,
},
protected: {
type: "Starter",
@@ -300,6 +350,7 @@ export const POWERS = {
crit_chance: 0.475,
crit_damage: 1.2,
},
+ unlocked_at: 0,
},
simple: {
type: "Starter",
@@ -313,6 +364,7 @@ export const POWERS = {
crit_chance: 1.45,
crit_damage: 3.6,
},
+ unlocked_at: 0,
},
warrior: {
type: "Starter",
@@ -324,6 +376,7 @@ export const POWERS = {
crit_chance: 2.4,
crit_damage: 6,
},
+ unlocked_at: 0,
},
};
diff --git a/src/lib.js b/src/lib.js
index 402ef3fcd2..8f12a50694 100644
--- a/src/lib.js
+++ b/src/lib.js
@@ -943,6 +943,7 @@ function getPower(name, magicalPower) {
itemData.tag.display.Lore = lore;
itemData.power_type = info.type;
itemData.stats = combined;
+ itemData.unlocked_at = info.unlocked_at;
}
return helper.generateItem(itemData);
From 8d33a2ba465d5fb635f4e7be418610a9aa3664b9 Mon Sep 17 00:00:00 2001
From: Brandon Fowler
Date: Sun, 16 Jul 2023 23:36:19 -0400
Subject: [PATCH 7/7] use unlocked_at to determine if power is unlocked
---
src/lib.js | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/src/lib.js b/src/lib.js
index 8f12a50694..c6f6dd41f3 100644
--- a/src/lib.js
+++ b/src/lib.js
@@ -2320,9 +2320,7 @@ export async function getStats(
const power = getPower(name, output.magical_power.total);
const unlocked =
- power.power_type === "Starter" ||
- (power.power_type === "Intermediate" && output.levels.combat.level >= 15) ||
- unlockedPowers.has(name);
+ ("unlocked_at" in power && output.levels.combat.level >= power.unlocked_at) || unlockedPowers.has(name);
output[unlocked ? "unlocked_powers" : "locked_powers"].push(power);
}