Add IDs to builds, save state starting

This commit is contained in:
b 2021-01-07 04:23:54 -06:00
parent 77e9d77742
commit b51b3c6482
5 changed files with 3666 additions and 29 deletions

3547
id_map_sav.json Normal file

File diff suppressed because it is too large Load diff

View file

@ -14,7 +14,7 @@
<div class="center" style="grid-column:1;grid-row:1">
<div>
<label for="helmet-choice">Helmet:</label>
<input list="helmet-items" id="helmet-choice" name="helmet-choice" value=""/>
<input list="helmet-items" id="helmet-choice" name="helmet-choice" value="No Helmet"/>
<datalist id="helmet-items">
</datalist>
</div>
@ -29,7 +29,7 @@
<div class="center" style="grid-column:2;grid-row:1">
<div>
<label for="chestplate-choice">Chestplate:</label>
<input list="chestplate-items" id="chestplate-choice" name="chestplate-choice" value=""/>
<input list="chestplate-items" id="chestplate-choice" name="chestplate-choice" value="No Chestplate"/>
<datalist id="chestplate-items">
</datalist>
</div>
@ -44,7 +44,7 @@
<div class="center" style="grid-column:3;grid-row:1">
<div>
<label for="leggings-choice">Leggings:</label>
<input list="leggings-items" id="leggings-choice" name="leggings-choice" value=""/>
<input list="leggings-items" id="leggings-choice" name="leggings-choice" value="No Leggings"/>
<datalist id="leggings-items">
</datalist>
</div>
@ -59,7 +59,7 @@
<div class="center" style="grid-column:4;grid-row:1">
<div id="boots">
<label for="boots-choice">Boots:</label>
<input list="boots-items" id="boots-choice" name="boots-choice" value=""/>
<input list="boots-items" id="boots-choice" name="boots-choice" value="No Boots"/>
<datalist id="boots-items">
</datalist>
</div>
@ -73,25 +73,25 @@
</div>
<div class="center" style="grid-column:1;grid-row:2">
<label for="ring1-choice">Ring 1:</label>
<input list="ring1-items" id="ring1-choice" name="ring1-choice" value=""/>
<input list="ring1-items" id="ring1-choice" name="ring1-choice" value="No Ring 1"/>
<datalist id="ring1-items">
</datalist>
</div>
<div class="center" style="grid-column:2;grid-row:2">
<label for="ring2-choice">Ring 2:</label>
<input list="ring2-items" id="ring2-choice" name="ring2-choice" value=""/>
<input list="ring2-items" id="ring2-choice" name="ring2-choice" value="No Ring 2"/>
<datalist id="ring2-items">
</datalist>
</div>
<div class="center" style="grid-column:3;grid-row:2">
<label for="bracelet-choice">Bracelet:</label>
<input list="bracelet-items" id="bracelet-choice" name="bracelet-choice" value=""/>
<input list="bracelet-items" id="bracelet-choice" name="bracelet-choice" value="No Bracelet"/>
<datalist id="bracelet-items">
</datalist>
</div>
<div class="center" style="grid-column:4;grid-row:2">
<label for="necklace-choice">Necklace:</label>
<input list="necklace-items" id="necklace-choice" name="necklace-choice" value="" />
<input list="necklace-items" id="necklace-choice" name="necklace-choice" value="No Necklace"/>
<datalist id="necklace-items">
</datalist>
</div>

View file

@ -1,4 +1,4 @@
const DB_VERSION = 3;
const DB_VERSION = 4;
// @See https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/video-store/index.js
let db;

113
test.js
View file

@ -2,7 +2,10 @@
* TESTING SECTION
*/
console.log(location.href);
const url_base = location.href.split("#")[0];
const url_tag = location.hash.slice(1);
console.log(url_base);
console.log(url_tag);
/*
* END testing section
@ -13,8 +16,8 @@ let player_build;
let armorTypes = [ "helmet", "chestplate", "leggings", "boots" ];
let accessoryTypes = [ "ring", "bracelet", "necklace" ];
let weaponTypes = [ "wand", "spear", "bow", "dagger", "relik" ];
let item_fields = [ "name", "displayName", "tier", "set", "slots", "type", "material", "drop", "quest", "restrict", "nDam", "fDam", "wDam", "aDam", "tDam", "eDam", "atkSpd", "hp", "fDef", "wDef", "aDef", "tDef", "eDef", "lvl", "classReq", "strReq", "dexReq", "intReq", "agiReq", "defReq", "hprPct", "mr", "sdPct", "mdPct", "ls", "ms", "xpb", "lb", "ref", "str", "dex", "int", "agi", "def", "thorns", "exploding", "spd", "atkTier", "poison", "hpBonus", "spRegen", "eSteal", "hprRaw", "sdRaw", "mdRaw", "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", "fixID", "category", "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4", "rainbowRaw", "sprint", "sprintReg", "jh", "lq", "gXp", "gSpd" ];
let nonRolledIDs = ["name", "displayName", "tier", "set", "slots", "type", "material", "drop", "quest", "restrict", "nDam", "fDam", "wDam", "aDam", "tDam", "eDam", "atkSpd", "hp", "fDef", "wDef", "aDef", "tDef", "eDef", "lvl", "classReq", "strReq", "dexReq", "intReq", "agiReq", "defReq","str", "dex", "int", "agi", "def", "fixID", "category"];
let item_fields = [ "name", "displayName", "tier", "set", "slots", "type", "material", "drop", "quest", "restrict", "nDam", "fDam", "wDam", "aDam", "tDam", "eDam", "atkSpd", "hp", "fDef", "wDef", "aDef", "tDef", "eDef", "lvl", "classReq", "strReq", "dexReq", "intReq", "agiReq", "defReq", "hprPct", "mr", "sdPct", "mdPct", "ls", "ms", "xpb", "lb", "ref", "str", "dex", "int", "agi", "def", "thorns", "exploding", "spd", "atkTier", "poison", "hpBonus", "spRegen", "eSteal", "hprRaw", "sdRaw", "mdRaw", "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", "fixID", "category", "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4", "rainbowRaw", "sprint", "sprintReg", "jh", "lq", "gXp", "gSpd", "id" ];
let nonRolledIDs = ["name", "displayName", "tier", "set", "slots", "type", "material", "drop", "quest", "restrict", "nDam", "fDam", "wDam", "aDam", "tDam", "eDam", "atkSpd", "hp", "fDef", "wDef", "aDef", "tDef", "eDef", "lvl", "classReq", "strReq", "dexReq", "intReq", "agiReq", "defReq","str", "dex", "int", "agi", "def", "fixID", "category", "id"];
let rolledIDs = ["hprPct", "mr", "sdPct", "mdPct", "ls", "ms", "xpb", "lb", "ref", "thorns", "exploding", "spd", "atkTier", "poison", "hpBonus", "spRegen", "eSteal", "hprRaw", "sdRaw", "mdRaw", "fDamPct", "wDamPct", "aDamPct", "tDamPct", "eDamPct", "fDefPct", "wDefPct", "aDefPct", "tDefPct", "eDefPct", "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4", "rainbowRaw", "sprint", "sprintReg", "jh", "lq", "gXp", "gSpd"];
let itemTypes = armorTypes.concat(accessoryTypes).concat(weaponTypes);
let itemLists = new Map();
@ -22,6 +25,7 @@ for (const it of itemTypes) {
itemLists.set(it, []);
}
let itemMap = new Map();
let idMap = new Map();
/*
* Function that takes an item list and populates its corresponding dropdown.
@ -64,6 +68,7 @@ function init() {
item.skillpoints = [0, 0, 0, 0, 0];
item.has_negstat = false;
item.reqs = [0, 0, 0, 0, 0];
item.id = 10000 + i;
noneItems[i] = item;
}
@ -72,6 +77,7 @@ function init() {
for (const item of items) {
itemLists.get(item.type).push(item.displayName);
itemMap.set(item.displayName, item);
idMap.set(item.id, item.displayName);
}
/*for (const item of noneItems){
itemLists.get(item.type).push(item.name);
@ -125,6 +131,77 @@ function init() {
document.getElementById("weapon-slots").textContent = "X slots";
}
});
populateFromURL();
}
/*
* Populate fields based on url, and calculate build.
*/
function populateFromURL() {
if (url_tag) {
let helmet;
let chestplate;
let leggings;
let boots;
let ring1;
let ring2;
let bracelet;
let necklace;
let weapon;
let info = url_tag.split("|");
let version = info[0];
if (version === "0") {
let equipments = info[1];
helmet = idMap.get(Base64.toInt(equipments.slice(0,3)));
chestplate = idMap.get(Base64.toInt(equipments.slice(3,6)));
leggings = idMap.get(Base64.toInt(equipments.slice(6,9)));
boots = idMap.get(Base64.toInt(equipments.slice(9,12)));
ring1 = idMap.get(Base64.toInt(equipments.slice(12,15)));
ring2 = idMap.get(Base64.toInt(equipments.slice(15,18)));
bracelet = idMap.get(Base64.toInt(equipments.slice(18,21)));
necklace = idMap.get(Base64.toInt(equipments.slice(21,24)));
weapon = idMap.get(Base64.toInt(equipments.slice(24,27)));
}
setValue("helmet-choice", helmet);
setValue("helmet-powder", "");
setValue("chestplate-choice", chestplate);
setValue("chestplate-powder", "");
setValue("leggings-choice", leggings);
setValue("leggings-powder", "");
setValue("boots-choice", boots);
setValue("boots-powder", "");
setValue("ring1-choice", ring1);
setValue("ring2-choice", ring2);
setValue("bracelet-choice", bracelet);
setValue("necklace-choice", necklace);
setValue("weapon-choice", weapon);
setValue("weapon-powder", "");
setValue("str-skp", "0");
setValue("dex-skp", "0");
setValue("int-skp", "0");
setValue("def-skp", "0");
setValue("agi-skp", "0");
calculateBuild();
}
}
function encodeBuild() {
if (player_build) {
let build_string = "0|" + Base64.fromIntN(player_build.helmet.id, 3) +
Base64.fromIntN(player_build.chestplate.id, 3) +
Base64.fromIntN(player_build.leggings.id, 3) +
Base64.fromIntN(player_build.boots.id, 3) +
Base64.fromIntN(player_build.ring1.id, 3) +
Base64.fromIntN(player_build.ring2.id, 3) +
Base64.fromIntN(player_build.bracelet.id, 3) +
Base64.fromIntN(player_build.necklace.id, 3) +
Base64.fromIntN(player_build.weapon.id, 3);
return build_string;
}
return "";
}
function calculateBuild(){
@ -201,13 +278,15 @@ function calculateBuild(){
setHTML("build-helmet", expandedItemToString(expandItem(player_build.helmet)));
setHTML("build-chestplate", expandedItemToString(expandItem(player_build.chestplate)));
setHTML("build-leggings", expandedItemToString(expandItem(player_build.helmet)));
setHTML("build-boots", expandedItemToString(expandItem(player_build.helmet)));
setHTML("build-leggings", expandedItemToString(expandItem(player_build.leggings)));
setHTML("build-boots", expandedItemToString(expandItem(player_build.boots)));
setHTML("build-ring1", expandedItemToString(expandItem(player_build.ring1)));
setHTML("build-ring2", expandedItemToString(expandItem(player_build.ring2)));
setHTML("build-bracelet", expandedItemToString(expandItem(player_build.bracelet)));
setHTML("build-necklace", expandedItemToString(expandItem(player_build.necklace)));
setHTML("build-weapon", expandedItemToString(expandItem(player_build.weapon)));
location.hash = encodeBuild();
}
/* Helper function that gets stats ranges for wearable items.
@param item - an item in Object format.
@ -234,11 +313,11 @@ function expandItem(item){
console.log(id);
if(item[rolledIDs[id]]){
if(item[rolledIDs[id]] > 0){ // positive rolled IDs
minRolls.set(rolledIDs[id],item[rolledIDs[id]]*0.3);
maxRolls.set(rolledIDs[id],item[rolledIDs[id]]*1.3);
minRolls.set(rolledIDs[id],idRound(item[rolledIDs[id]]*0.3));
maxRolls.set(rolledIDs[id],idRound(item[rolledIDs[id]]*1.3));
}else if(item[rolledIDs[id]] < 0){ //negative rolled IDs
minRolls.set(rolledIDs[id],item[rolledIDs[id]]*1.3);
maxRolls.set(rolledIDs[id],item[rolledIDs[id]]*0.7);
minRolls.set(rolledIDs[id],idRound(item[rolledIDs[id]]*1.3));
maxRolls.set(rolledIDs[id],idRound(item[rolledIDs[id]]*0.7));
}else{//Id = 0
minRolls.set(rolledIDs[id],0);
maxRolls.set(rolledIDs[id],0);
@ -263,7 +342,7 @@ function expandItem(item){
function expandedItemToString(item){
console.log(item);
let ids = ["lvl", "classReq","strReq", "dexReq", "intReq", "defReq","agiReq", "nDam", "eDam", "tDam", "wDam", "tDam", "aDam", "atkSpd", "hp", "eDef", "tDef", "wDef", "fDef", "aDef", "str", "dex", "int", "agi", "def", "hpBonus", "hprRaw", "hprPct", "sdRaw", "sdPct", "mdRaw", "mdPct", "mr", "ms", "ref", "ls", "poison", "thorns", "exploding", "spd", "atkTier", "eDamPct", "tDamPct", "wDamPct", "fDamPct", "aDamPct", "eDefPct", "tDefPct", "wDefPct", "fDefPct", "aDefPct", "spPct1", "spRaw1", "spPct2", "spRaw2", "spPct3", "spRaw3", "spPct4", "spRaw4", "rainbowRaw", "sprint", "sprintReg", "jh", "xpb", "lb", "lq", "spRegen", "eSteal", "gXp", "gSpd", "slots", "set", "quest", "restrict"];
let idPrefixes = {"lvl":"Combat Level Min: ", "classReq":"Class Req: ","strReq":"Strength Min: ","dexReq":"Dexterity Min: ","intReq":"Intelligence Min: ","defReq":"Defense Min: ","agiReq":"Agility Min: ", "nDam":"Neutral Damage: ", "eDam":"Earth Damage: ", "tDam":"Thunder Damage: ", "wDam":"Water Damage: ", "fDam":"Fire Damage: ", "aDam":"Air Damage: ", "atkSpd":"Attack Speed: ", "hp":"Health: ", "eDef":"Earth Defense: ", "tDef":"Thunder Defense: ", "wDef":"Water Defense: ", "fDef":"Fire Defense: ", "aDef":"Air Defense: ", "str":"Strength: ", "dex":"Dexterity: ", "int":"Intelligence: ", "def":"Defense: ","agi":"Agility: ", "hpBonus":"Health Bonus: ", "hprRaw":"Health Regen Raw: ", "hprPct":"Health Regen %: ", "sdRaw":"Raw Spell Damage: ", "sdPct":"Spell Damage %: ", "mdRaw":"Main Attack Neutral Damage: ", "mdPct":"Main Attack Damage %: ", "mr":"Mana Regen: ", "ms":"Mana Steal: ", "ref":"Reflection: ", "ls":"Life Steal: ", "poison":"Poison: ", "thorns":"Thorns: ", "exploding":"Expoding: ", "spd":"Walk Speed Bonus: ", "atkTier":"Attack Speed Bonus: ", "eDamPct":"Earth Damage %: ", "tDamPct":"Thunder Damage %: ", "wDamPct":"Water Damage %: ", "fDamPct":"Fire Damage %: ", "aDamPct":"Air Damage %: ", "eDefPct":"Earth Defense %: ", "tDefPct":"Thunder Defense %: ", "wDefPct":"Water Defense %: ", "fDefPct":"Fire Defense %: ", "aDefPct":"Air Defense %: ", "spPct1":"1st Spell Cost %: ", "spRaw1":"1st Spell Cost Raw: ", "spPct2":"2nd Spell Cost %: ", "spRaw2":"2nd Spell Cost Raw: ", "spPct3":"3rd Spell Cost %: ", "spRaw3":"3rd Spell Cost Raw: ", "spPct4":"4th Spell Cost %: ", "spRaw4":"4th Spell Cost Raw: ", "rainbowRaw":"Rainbow Spell Damage Raw: ", "sprint":"Sprint Bonus: ", "sprintReg":"Sprint Regen Bonus: ", "jh":"Jump Height: ", "xpb":"Combat XP Bonus: ", "lb":"Loot Bonus: ", "lq":"Loot Quality: ", "spRegen":"Soul Point Regen: ", "eSteal":"Stealing: ", "gXp":"Gathering XP Bonus: ", "gSpd":"Gathering Speed Bonus: ", "slots":"Powder Slots: ", "set":"This item belongs to the ", "quest":"This item is from the quest", "restrict":""};
let idPrefixes = {"lvl":"Combat Level Min: ", "classReq":"Class Req: ","strReq":"Strength Min: ","dexReq":"Dexterity Min: ","intReq":"Intelligence Min: ","defReq":"Defense Min: ","agiReq":"Agility Min: ", "nDam":"Neutral Damage: ", "eDam":"Earth Damage: ", "tDam":"Thunder Damage: ", "wDam":"Water Damage: ", "fDam":"Fire Damage: ", "aDam":"Air Damage: ", "atkSpd":"Attack Speed: ", "hp":"Health: ", "eDef":"Earth Defense: ", "tDef":"Thunder Defense: ", "wDef":"Water Defense: ", "fDef":"Fire Defense: ", "aDef":"Air Defense: ", "str":"Strength: ", "dex":"Dexterity: ", "int":"Intelligence: ", "def":"Defense: ","agi":"Agility: ", "hpBonus":"Health Bonus: ", "hprRaw":"Health Regen Raw: ", "hprPct":"Health Regen %: ", "sdRaw":"Raw Spell Damage: ", "sdPct":"Spell Damage %: ", "mdRaw":"Main Attack Neutral Damage: ", "mdPct":"Main Attack Damage %: ", "mr":"Mana Regen: ", "ms":"Mana Steal: ", "ref":"Reflection: ", "ls":"Life Steal: ", "poison":"Poison: ", "thorns":"Thorns: ", "exploding":"Expoding: ", "spd":"Walk Speed Bonus: ", "atkTier":"Attack Speed Bonus: ", "eDamPct":"Earth Damage %: ", "tDamPct":"Thunder Damage %: ", "wDamPct":"Water Damage %: ", "fDamPct":"Fire Damage %: ", "aDamPct":"Air Damage %: ", "eDefPct":"Earth Defense %: ", "tDefPct":"Thunder Defense %: ", "wDefPct":"Water Defense %: ", "fDefPct":"Fire Defense %: ", "aDefPct":"Air Defense %: ", "spPct1":"1st Spell Cost %: ", "spRaw1":"1st Spell Cost Raw: ", "spPct2":"2nd Spell Cost %: ", "spRaw2":"2nd Spell Cost Raw: ", "spPct3":"3rd Spell Cost %: ", "spRaw3":"3rd Spell Cost Raw: ", "spPct4":"4th Spell Cost %: ", "spRaw4":"4th Spell Cost Raw: ", "rainbowRaw":"Rainbow Spell Damage Raw: ", "sprint":"Sprint Bonus: ", "sprintReg":"Sprint Regen Bonus: ", "jh":"Jump Height: ", "xpb":"Combat XP Bonus: ", "lb":"Loot Bonus: ", "lq":"Loot Quality: ", "spRegen":"Soul Point Regen: ", "eSteal":"Stealing: ", "gXp":"Gathering XP Bonus: ", "gSpd":"Gathering Speed Bonus: ", "slots":"Powder Slots: ", "set":"This item belongs to the ", "quest":"This item is from the quest<br>", "restrict":""};
let idSuffixes = {"lvl":"", "classReq":"","strReq":"","dexReq":"","intReq":"","defReq":"","agiReq":"", "nDam":"", "eDam":"", "tDam":"", "wDam":"", "fDam":"", "aDam":"", "atkSpd":"", "hp":"", "eDef":"", "tDef":"", "wDef":"", "fDef":"", "aDef":"", "str":"", "dex":"", "int":"", "def":"","agi":"", "hpBonus":"", "hprRaw":"", "hprPct":"%", "sdRaw":"", "sdPct":"%", "mdRaw":"", "mdPct":"%", "mr":"/4s", "ms":"/4s", "ref":"%", "ls":"/4s", "poison":"/3s", "thorns":"%", "exploding":"%", "spd":"%", "atkTier":" tier", "eDamPct":"%", "tDamPct":"%", "wDamPct":"%", "fDamPct":"%", "aDamPct":"%", "eDefPct":"%", "tDefPct":"%", "wDefPct":"%", "fDefPct":"%", "aDefPct":"%", "spPct1":"%", "spRaw1":"", "spPct2":"%", "spRaw2":"", "spPct3":"%", "spRaw3":"", "spPct4":"%", "spRaw4":"", "rainbowRaw":"", "sprint":"%", "sprintReg":"%", "jh":"", "xpb":"%", "lb":"%", "lq":"%", "spRegen":"%", "eSteal":"%", "gXp":"%", "gSpd":"%", "slots":"", "set":" set.", "quest":".", "restrict":""};
let itemString = "";
itemString = itemString.concat(item.get("name"),"<br><br>");
@ -276,7 +355,7 @@ function expandedItemToString(item){
if(rolledIDs.includes(ids[i])&& item.get("minRolls").get(ids[i]) && item.get("maxRolls").get(ids[i]) ){//rolled ID & non-0/non-null/non-und ID
console.log("hi");
itemString = itemString.concat(idPrefixes[ids[i]]);
itemString = itemString.concat(idRound(item.get("minRolls").get(ids[i])), idSuffixes[ids[i]],"<br>");
itemString = itemString.concat(item.get("minRolls").get(ids[i]), idSuffixes[ids[i]],"<br>");
}//Just don't do anything if else
}
}else{//non-fixed IDs
@ -288,7 +367,7 @@ function expandedItemToString(item){
if(rolledIDs.includes(ids[i])&& item.get("minRolls").get(ids[i]) && item.get("maxRolls").get(ids[i]) ){//rolled ID & non-0/non-null/non-und ID
console.log("hi");
itemString = itemString.concat(idPrefixes[ids[i]]);
itemString = itemString.concat(idRound(item.get("minRolls").get(ids[i])), idSuffixes[ids[i]], " -> ", idRound(item.get("maxRolls").get(ids[i])),idSuffixes[ids[i]],"<br>");
itemString = itemString.concat(item.get("minRolls").get(ids[i]), idSuffixes[ids[i]], " -> ", idRound(item.get("maxRolls").get(ids[i])),idSuffixes[ids[i]],"<br>");
}//Just don't do anything if else
}
}
@ -322,11 +401,11 @@ function resetFields(){
setValue("necklace-choice", "");
setValue("weapon-choice", "");
setValue("weapon-powder", "");
setValue("str-skp", "");
setValue("dex-skp", "");
setValue("int-skp", "");
setValue("def-skp", "");
setValue("agi-skp", "");
setValue("str-skp", "0");
setValue("dex-skp", "0");
setValue("int-skp", "0");
setValue("def-skp", "0");
setValue("agi-skp", "0");
}
load_init(init);

View file

@ -27,11 +27,14 @@ function setHTML(id, html) {
}
function setValue(id, value) {
document.getElementById(id).value = value;
let el = document.getElementById(id);
el.value = value;
el.dispatchEvent(new Event("change"));
}
// Base 64 encoding tools
// https://stackoverflow.com/a/27696695
// Modified for fixed precision
Base64 = (function () {
var digitsStr =
@ -44,7 +47,7 @@ Base64 = (function () {
digitsMap[digits[i]] = i;
}
return {
fromInt: function(int32) {
fromIntV: function(int32) {
var result = '';
while (true) {
result = digits[int32 & 0x3f] + result;
@ -54,6 +57,14 @@ Base64 = (function () {
}
return result;
},
fromIntN: function(int32, n) {
var result = '';
for (let i = 0; i < n; ++i) {
result = digits[int32 & 0x3f] + result;
int32 >>>= 6;
}
return result;
},
toInt: function(digitsStr) {
var result = 0;
var digits = digitsStr.split('');
@ -61,7 +72,7 @@ Base64 = (function () {
result = (result << 6) + digitsMap[digits[i]];
}
return result;
}
},
};
})();