2022-05-31 06:31:37 +00:00
const ci _save _order = [ "name" , "lore" , "tier" , "set" , "slots" , "type" , "material" , "drop" , "quest" , "nDam" , "fDam" , "wDam" , "aDam" , "tDam" , "eDam" , "atkSpd" , "hp" , "fDef" , "wDef" , "aDef" , "tDef" , "eDef" , "lvl" , "classReq" , "strReq" , "dexReq" , "intReq" , "defReq" , "agiReq" , "str" , "dex" , "int" , "agi" , "def" , "id" , "skillpoints" , "reqs" , "nDam_" , "fDam_" , "wDam_" , "aDam_" , "tDam_" , "eDam_" , "majorIds" , "hprPct" , "mr" , "sdPct" , "mdPct" , "ls" , "ms" , "xpb" , "lb" , "ref" , "thorns" , "expd" , "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" , "durability" , "duration" , "charges" ] ;
const nonRolled _strings = [ "name" , "lore" , "tier" , "set" , "type" , "material" , "drop" , "quest" , "majorIds" , "classReq" , "atkSpd" , "displayName" , "nDam" , "fDam" , "wDam" , "aDam" , "tDam" , "eDam" , "nDam_" , "fDam_" , "wDam_" , "aDam_" , "tDam_" , "eDam_" , "durability" , "duration" ] ;
2021-03-08 22:50:47 +00:00
//omitted restrict - it's always "Custom Item"
//omitted displayName - either it's the same as name (repetitive) or it's "Custom Item"
//omitted category - can always get this from type
//omitted fixId - we will denote this early in the string.
2021-04-30 03:10:29 +00:00
//omitted "nDam_", "fDam_", "wDam_", "aDam_", "tDam_", "eDam_" - will be calculated on display
2021-07-06 01:46:09 +00:00
// NOTE: DO NOT DELETE ENTRIES FROM ARRAYS FOR BACKWARDS COMPAT REASONS!!!
2021-03-05 16:28:00 +00:00
2021-07-06 04:51:20 +00:00
// TODO: Add an exclude list
2021-04-30 10:36:25 +00:00
/ * *
* @ param { Map } custom - the statMap of the CI
* @ param { boolean } verbose - if we want lore and majorIds to display
* /
function encodeCustom ( custom , verbose ) {
if ( custom ) {
if ( custom . statMap ) {
2022-05-31 06:31:37 +00:00
custom = custom . statMap ;
2021-04-30 10:36:25 +00:00
}
let hash = "1" ;
//version 1
if ( custom . has ( "fixID" ) && custom . get ( "fixID" ) ) {
hash += "1" ;
} else {
hash += "0" ;
}
for ( const i in ci _save _order ) {
let id = ci _save _order [ i ] ;
if ( rolledIDs . includes ( id ) ) {
let val _min = custom . get ( "minRolls" ) . has ( id ) ? custom . get ( "minRolls" ) . get ( id ) : 0 ;
let val _max = custom . get ( "maxRolls" ) . has ( id ) ? custom . get ( "maxRolls" ) . get ( id ) : 0 ;
2022-01-05 19:35:14 +00:00
// 0 - both pos
// 1 - min neg max pos
// 2 - min pos max neg (how?)
// 3 - min neg max neg
2022-05-31 06:31:37 +00:00
let sign = ( Boolean ( val _min / Math . abs ( val _min ) < 0 ) | 0 ) + 2 * ( Boolean ( val _max / Math . abs ( val _max ) < 0 ) | 0 ) ;
2021-04-30 10:36:25 +00:00
//console.log(id + ": " + sign);
2022-05-31 06:31:37 +00:00
let min _len = Math . max ( 1 , Math . ceil ( log ( 64 , Math . abs ( val _min ) + 1 ) ) ) ;
let max _len = Math . max ( 1 , Math . ceil ( log ( 64 , Math . abs ( val _max ) + 1 ) ) ) ;
let len = Math . max ( min _len , max _len ) ;
2021-04-30 10:36:25 +00:00
val _min = Math . abs ( val _min ) ;
val _max = Math . abs ( val _max ) ;
2022-05-31 06:31:37 +00:00
if ( val _min != 0 || val _max != 0 ) {
2021-04-30 10:36:25 +00:00
if ( custom . get ( "fixID" ) ) {
2022-05-31 06:31:37 +00:00
hash += Base64 . fromIntN ( i , 2 ) + Base64 . fromIntN ( len , 2 ) + sign + Base64 . fromIntN ( val _min , len ) ;
2021-04-30 10:36:25 +00:00
} else {
2022-05-31 06:31:37 +00:00
hash += Base64 . fromIntN ( i , 2 ) + Base64 . fromIntN ( len , 2 ) + sign + Base64 . fromIntN ( val _min , len ) + Base64 . fromIntN ( val _max , len ) ;
2021-04-30 10:36:25 +00:00
}
}
} else {
let damages = [ "nDam" , "eDam" , "tDam" , "wDam" , "fDam" , "aDam" ] ; //"nDam_", "eDam_", "tDam_", "wDam_", "fDam_", "aDam_"
let val = custom . get ( id ) ;
2021-07-23 07:30:26 +00:00
if ( id == "majorIds" ) {
if ( val . length > 0 ) {
val = val [ 0 ] ;
}
else {
val = "" ;
}
}
2021-04-30 10:36:25 +00:00
2022-05-31 06:31:37 +00:00
if ( typeof ( val ) === "string" && val !== "" ) {
if ( ( damages . includes ( id ) && val === "0-0" ) || ( ! verbose && [ "lore" , "majorIds" , "quest" , "materials" , "drop" , "set" ] . includes ( id ) ) ) { continue ; }
2021-04-30 10:36:25 +00:00
if ( id === "type" ) {
2022-07-24 06:10:19 +00:00
hash += Base64 . fromIntN ( i , 2 ) + Base64 . fromIntN ( all _types . indexOf ( val . substring ( 0 , 1 ) . toUpperCase ( ) + val . slice ( 1 ) ) , 1 ) ;
2021-04-30 10:36:25 +00:00
} else if ( id === "tier" ) {
2022-05-31 06:31:37 +00:00
hash += Base64 . fromIntN ( i , 2 ) + Base64 . fromIntN ( tiers . indexOf ( val ) , 1 ) ;
2021-04-30 10:36:25 +00:00
} else if ( id === "atkSpd" ) {
2022-05-31 06:31:37 +00:00
hash += Base64 . fromIntN ( i , 2 ) + Base64 . fromIntN ( attackSpeeds . indexOf ( val ) , 1 ) ;
2021-04-30 10:36:25 +00:00
} else if ( id === "classReq" ) {
2022-05-31 06:31:37 +00:00
hash += Base64 . fromIntN ( i , 2 ) + Base64 . fromIntN ( classes . indexOf ( val ) , 1 ) ;
2021-04-30 10:36:25 +00:00
} else {
2022-05-31 06:31:37 +00:00
hash += Base64 . fromIntN ( i , 2 ) + Base64 . fromIntN ( val . replaceAll ( " " , "%20" ) . length , 2 ) + val . replaceAll ( " " , "%20" ) ; //values cannot go above 4096 chars!!!! Is this ok?
2021-04-30 10:36:25 +00:00
}
2022-05-31 06:31:37 +00:00
} else if ( typeof ( val ) === "number" && val != 0 ) {
2023-01-03 01:31:55 +00:00
let len = Math . max ( 1 , Math . ceil ( log ( 64 , Math . abs ( val ) + 1 ) ) ) ;
2021-04-30 10:36:25 +00:00
let sign = Boolean ( val / Math . abs ( val ) < 0 ) | 0 ;
//console.log(sign);
//hash += Base64.fromIntN(i,2) + Base64.fromIntN(val,Math.max(1,Math.ceil(log(64,Math.abs(val))))) + "_";
2022-05-31 06:31:37 +00:00
hash += Base64 . fromIntN ( i , 2 ) + Base64 . fromIntN ( len , 2 ) + sign + Base64 . fromIntN ( Math . abs ( val ) , len ) ;
}
2021-04-30 10:36:25 +00:00
}
}
return hash ;
}
return "" ;
}
2021-03-14 07:55:08 +00:00
function getCustomFromHash ( hash ) {
let name = hash . slice ( ) ;
let statMap ;
2022-05-31 06:31:37 +00:00
console . log ( "decoding" ) ;
2021-03-14 07:55:08 +00:00
try {
2022-05-31 06:31:37 +00:00
if ( name . slice ( 0 , 3 ) === "CI-" ) {
2021-03-14 07:55:08 +00:00
name = name . substring ( 3 ) ;
} else {
throw new Error ( "Not a custom item!" ) ;
}
2022-05-31 06:31:37 +00:00
//probably change vers and fixID to be encoded and decoded to/from B64 in the future
2021-03-14 07:55:08 +00:00
let version = name . charAt ( 0 ) ;
2022-05-31 06:31:37 +00:00
let fixID = Boolean ( parseInt ( name . charAt ( 1 ) , 10 ) ) ;
2021-03-14 07:55:08 +00:00
let tag = name . substring ( 2 ) ;
statMap = new Map ( ) ;
statMap . set ( "minRolls" , new Map ( ) ) ;
statMap . set ( "maxRolls" , new Map ( ) ) ;
2022-05-31 06:31:37 +00:00
2021-03-14 07:55:08 +00:00
if ( version === "1" ) {
//do the things
if ( fixID ) {
2021-03-22 21:33:10 +00:00
statMap . set ( "fixID" , true ) ;
2022-05-31 06:31:37 +00:00
}
2021-03-14 07:55:08 +00:00
while ( tag !== "" ) {
2022-05-31 06:31:37 +00:00
let id = ci _save _order [ Base64 . toInt ( tag . slice ( 0 , 2 ) ) ] ;
let len = Base64 . toInt ( tag . slice ( 2 , 4 ) ) ;
2021-03-14 07:55:08 +00:00
if ( rolledIDs . includes ( id ) ) {
2022-05-31 06:31:37 +00:00
let sign = parseInt ( tag . slice ( 4 , 5 ) , 10 ) ;
let minRoll = Base64 . toInt ( tag . slice ( 5 , 5 + len ) ) ;
2021-03-14 07:55:08 +00:00
if ( ! fixID ) {
2022-05-31 06:31:37 +00:00
let maxRoll = Base64 . toInt ( tag . slice ( 5 + len , 5 + 2 * len ) ) ;
2021-03-14 07:55:08 +00:00
if ( sign > 1 ) {
maxRoll *= - 1 ;
}
if ( sign % 2 == 1 ) {
minRoll *= - 1 ;
}
2022-05-31 06:31:37 +00:00
statMap . get ( "minRolls" ) . set ( id , minRoll ) ;
statMap . get ( "maxRolls" ) . set ( id , maxRoll ) ;
tag = tag . slice ( 5 + 2 * len ) ;
2021-03-14 07:55:08 +00:00
} else {
if ( sign != 0 ) {
minRoll *= - 1 ;
}
2022-05-31 06:31:37 +00:00
statMap . get ( "minRolls" ) . set ( id , minRoll ) ;
statMap . get ( "maxRolls" ) . set ( id , minRoll ) ;
tag = tag . slice ( 5 + len ) ;
2021-03-14 07:55:08 +00:00
}
} else {
let val ;
if ( nonRolled _strings . includes ( id ) ) {
if ( id === "tier" ) {
val = tiers [ Base64 . toInt ( tag . charAt ( 2 ) ) ] ;
len = - 1 ;
} else if ( id === "type" ) {
2022-07-24 06:10:19 +00:00
val = all _types [ Base64 . toInt ( tag . charAt ( 2 ) ) ] ;
2021-03-14 07:55:08 +00:00
len = - 1 ;
} else if ( id === "atkSpd" ) {
val = attackSpeeds [ Base64 . toInt ( tag . charAt ( 2 ) ) ] ;
len = - 1 ;
} else if ( id === "classReq" ) {
val = classes [ Base64 . toInt ( tag . charAt ( 2 ) ) ] ;
len = - 1 ;
} else { //general case
2022-05-31 06:31:37 +00:00
val = tag . slice ( 4 , 4 + len ) . replaceAll ( "%20" , " " ) ;
2021-03-14 07:55:08 +00:00
}
2022-05-31 06:31:37 +00:00
tag = tag . slice ( 4 + len ) ;
2021-03-14 07:55:08 +00:00
} else {
2022-05-31 06:31:37 +00:00
let sign = parseInt ( tag . slice ( 4 , 5 ) , 10 ) ;
val = Base64 . toInt ( tag . slice ( 5 , 5 + len ) ) ;
2021-03-14 07:55:08 +00:00
if ( sign == 1 ) {
val *= - 1 ;
}
2022-05-31 06:31:37 +00:00
tag = tag . slice ( 5 + len ) ;
}
2021-07-23 07:20:21 +00:00
if ( id === "majorIds" ) {
val = [ val ] ;
console . log ( val ) ;
}
2021-03-14 07:55:08 +00:00
statMap . set ( id , val ) ;
}
}
2022-05-31 06:31:37 +00:00
statMap . set ( "hash" , "CI-" + name ) ;
2022-06-20 13:12:22 +00:00
statMap . set ( "custom" , true ) ;
2021-03-14 07:55:08 +00:00
return new Custom ( statMap ) ;
}
} catch ( error ) {
2022-07-24 06:10:19 +00:00
console . log ( error ) ;
console . log ( statMap ) ;
2021-03-14 07:55:08 +00:00
return undefined ;
}
2022-05-31 06:31:37 +00:00
2021-03-14 07:55:08 +00:00
}
2021-03-05 16:28:00 +00:00
/ * * A n o b j e c t r e p r e s e n t i n g a C u s t o m I t e m . M o s t l y f o r v a n i t y p u r p o s e s .
2021-09-25 18:57:57 +00:00
* @ dep Requires the use of nonRolledIDs and rolledIDs from display _constants . js .
2022-08-16 05:32:49 +00:00
* @ dep Requires the use of attackSpeeds from ` builder/build.js ` .
2021-03-05 16:28:00 +00:00
* /
2022-05-31 06:31:37 +00:00
class Custom {
2021-03-05 16:28:00 +00:00
/ * *
* @ description Construct a custom item ( CI ) from a statMap .
* @ param { statMap } : A map with keys from rolledIDs or nonRolledIDs or minRolls / maxRolls and values befitting the keys . minRolls and maxRolls are their own maps and have the same keys , but with minimum and maximum values ( for rolls ) .
*
* /
2022-05-31 06:31:37 +00:00
constructor ( statMap ) {
2021-03-05 16:28:00 +00:00
this . statMap = statMap ;
2021-07-23 07:20:21 +00:00
// TODO patch
2021-07-23 07:47:13 +00:00
// this.statMap.set("majorIds", [this.statMap.get("majorIds")]);
2021-03-05 16:28:00 +00:00
this . initCustomStats ( ) ;
}
setHash ( hash ) {
2021-03-14 07:55:08 +00:00
let ihash = hash . slice ( ) ;
2022-05-31 06:31:37 +00:00
if ( ihash . slice ( 0 , 3 ) !== "CI-" ) {
2021-03-14 07:55:08 +00:00
ihash = "CI-" + hash ;
}
this . hash = ihash ;
2022-05-31 06:31:37 +00:00
this . statMap . set ( "hash" , ihash ) ;
2021-03-05 16:28:00 +00:00
}
updateName ( name ) {
this . name = name ;
2022-05-31 06:31:37 +00:00
this . displayName = name ;
2021-03-05 16:28:00 +00:00
}
/ * G e t a l l s t a t s f o r t h i s C I .
* Stores in this . statMap .
* Follows the expandedItem item structure , similar to a crafted item .
* TODO : Check if this is even useful
* /
2022-05-31 06:31:37 +00:00
initCustomStats ( ) {
2021-03-08 22:50:47 +00:00
//this.setHashVerbose(); //do NOT move sethash from here please
2021-08-07 23:15:44 +00:00
console . log ( this . statMap ) ;
2021-03-14 07:55:08 +00:00
for ( const id of ci _save _order ) {
if ( rolledIDs . includes ( id ) ) {
if ( ! ( this . statMap . get ( "minRolls" ) . has ( id ) && this . statMap . get ( "minRolls" ) . get ( id ) ) ) {
2022-05-31 06:31:37 +00:00
this . statMap . get ( "minRolls" ) . set ( id , 0 ) ;
this . statMap . get ( "maxRolls" ) . set ( id , 0 ) ;
2021-03-14 07:55:08 +00:00
}
} else {
if ( nonRolled _strings . includes ( id ) ) {
2022-05-31 06:31:37 +00:00
if ( ! ( this . statMap . has ( id ) && this . statMap . get ( id ) ) ) {
this . statMap . set ( id , "" ) ;
2021-03-14 07:55:08 +00:00
}
} else {
2022-05-31 06:31:37 +00:00
if ( ! ( this . statMap . has ( id ) && this . statMap . get ( id ) ) ) {
this . statMap . set ( id , 0 ) ;
2021-03-14 07:55:08 +00:00
}
}
2021-03-05 16:28:00 +00:00
}
}
2021-06-19 10:40:47 +00:00
let type = this . statMap . get ( "type" ) . toLowerCase ( ) ;
if ( weaponTypes . includes ( type ) ) {
2022-05-31 06:31:37 +00:00
for ( const n of [ "nDam" , "eDam" , "tDam" , "wDam" , "fDam" , "aDam" ] ) {
2021-06-19 10:40:47 +00:00
if ( ! ( this . statMap . has ( n ) && this . statMap . get ( n ) ) ) {
2022-05-31 06:31:37 +00:00
this . statMap . set ( n , "0-0" ) ;
2021-06-19 10:40:47 +00:00
}
}
}
else {
2022-05-31 06:31:37 +00:00
for ( const n of [ "nDam" , "eDam" , "tDam" , "wDam" , "fDam" , "aDam" ] ) {
2021-06-19 10:40:47 +00:00
if ( this . statMap . has ( n ) ) {
this . statMap . delete ( n ) ;
}
}
}
2022-05-31 06:31:37 +00:00
2021-03-05 16:28:00 +00:00
if ( this . statMap . get ( "type" ) ) {
2022-05-31 06:31:37 +00:00
this . statMap . set ( "type" , this . statMap . get ( "type" ) . toLowerCase ( ) ) ;
2021-03-05 16:28:00 +00:00
if ( armorTypes . includes ( this . statMap . get ( "type" ) ) ) {
2022-05-31 06:31:37 +00:00
this . statMap . set ( "category" , "armor" ) ;
2021-03-05 16:28:00 +00:00
} else if ( accessoryTypes . includes ( this . statMap . get ( "type" ) ) ) {
2022-05-31 06:31:37 +00:00
this . statMap . set ( "category" , "accessory" ) ;
2021-03-05 16:28:00 +00:00
} else if ( weaponTypes . includes ( this . statMap . get ( "type" ) ) ) {
2022-05-31 06:31:37 +00:00
this . statMap . set ( "category" , "weapon" ) ;
2021-03-05 16:28:00 +00:00
} else if ( consumableTypes . includes ( this . statMap . get ( "type" ) ) ) {
2022-05-31 06:31:37 +00:00
this . statMap . set ( "category" , "consumable" ) ;
2022-01-05 19:44:59 +00:00
} else if ( tomeTypes . includes ( this . statMap . get ( "type" ) ) ) {
this . statMap . set ( "category" , "tome" ) ;
2021-03-05 16:28:00 +00:00
}
}
2021-03-14 07:55:08 +00:00
if ( this . statMap . get ( "tier" ) === "Crafted" ) {
this . statMap . set ( "crafted" , true ) ;
for ( const e of skp _elements ) {
2022-05-31 06:31:37 +00:00
this . statMap . set ( e + "DamLow" , this . statMap . get ( e + "Dam" ) ) ;
2021-03-14 07:55:08 +00:00
}
this . statMap . set ( "nDamLow" , this . statMap . get ( "nDam" ) ) ;
this . statMap . set ( "hpLow" , this . statMap . get ( "hp" ) ) ;
for ( const e of skp _order ) {
2022-05-31 06:31:37 +00:00
this . statMap . get ( "minRolls" ) . set ( e , this . statMap . get ( e ) ) ;
this . statMap . get ( "maxRolls" ) . set ( e , this . statMap . get ( e ) ) ;
2021-03-14 07:55:08 +00:00
}
// for (const e of ["durability", "duration"]) {
// if (this.statMap.get(e) === "") {
// this.statMap.set(e, [0,0]);
// } else {
// this.statMap.set(e, [this.statMap.get(e).split("-")[0],this.statMap.get(e).split("-")[1]])
// }
// }
2022-05-31 06:31:37 +00:00
this . statMap . set ( "lvlLow" , this . statMap . get ( "lvl" ) ) ;
2021-03-14 07:55:08 +00:00
if ( this . statMap . get ( "category" ) === "weapon" ) {
//this is for powder purposes.
//users will likely not stick to the 0.9,1.1 rule because custom item. We will get around this by breaking everything and rewarding users for sticking to 0.9,1.1.
2022-05-31 06:31:37 +00:00
this . statMap . set ( "nDamBaseLow" , Math . floor ( ( parseFloat ( this . statMap . get ( "nDamLow" ) ) + parseFloat ( this . statMap . get ( "nDam" ) ) ) / 2 ) ) ;
this . statMap . set ( "nDamBaseHigh" , Math . floor ( ( parseFloat ( this . statMap . get ( "nDamLow" ) ) + parseFloat ( this . statMap . get ( "nDam" ) ) ) / 2 ) ) ;
2021-03-14 07:55:08 +00:00
for ( const e in skp _elements ) {
2022-05-31 06:31:37 +00:00
this . statMap . set ( skp _elements [ e ] + "DamBaseLow" , Math . floor ( ( parseFloat ( this . statMap . get ( skp _elements [ e ] + "DamLow" ) ) + parseFloat ( this . statMap . get ( skp _elements [ e ] + "Dam" ) ) ) / 2 ) ) ;
this . statMap . set ( skp _elements [ e ] + "DamBaseHigh" , Math . floor ( ( parseFloat ( this . statMap . get ( skp _elements [ e ] + "DamLow" ) ) + parseFloat ( this . statMap . get ( skp _elements [ e ] + "Dam" ) ) ) / 2 ) ) ;
2021-03-14 07:55:08 +00:00
}
2021-04-07 06:01:05 +00:00
this . statMap . set ( "ingredPowders" , [ ] ) ;
2021-03-14 07:55:08 +00:00
}
}
if ( this . statMap . get ( "category" ) !== "weapon" ) {
2021-03-05 16:28:00 +00:00
this . statMap . set ( "atkSpd" , "" ) ;
2022-05-31 06:31:37 +00:00
for ( const n in [ "nDam" , "eDam" , "tDam" , "wDam" , "fDam" , "aDam" ] ) {
2021-03-14 07:55:08 +00:00
//this.statMap.set(n,"");
}
} else {
2021-03-05 16:28:00 +00:00
}
2021-03-08 22:50:47 +00:00
2021-03-05 16:28:00 +00:00
if ( this . statMap . get ( "name" ) && this . statMap . get ( "name" ) !== "" ) {
this . statMap . set ( "displayName" , this . statMap . get ( "name" ) ) ;
} else {
2021-03-08 22:50:47 +00:00
this . statMap . set ( "displayName" , "Custom Item" ) ;
2021-03-05 16:28:00 +00:00
}
2022-05-31 06:31:37 +00:00
this . statMap . set ( "powders" , [ ] ) ;
2021-03-14 07:55:08 +00:00
2022-05-31 06:31:37 +00:00
this . statMap . set ( "reqs" , [ this . statMap . get ( "strReq" ) , this . statMap . get ( "dexReq" ) , this . statMap . get ( "intReq" ) , this . statMap . get ( "defReq" ) , this . statMap . get ( "agiReq" ) ] ) ;
this . statMap . set ( "skillpoints" , [ this . statMap . get ( "str" ) , this . statMap . get ( "dex" ) , this . statMap . get ( "int" ) , this . statMap . get ( "def" ) , this . statMap . get ( "agi" ) ] ) ;
2021-03-05 16:28:00 +00:00
2021-03-14 07:55:08 +00:00
2021-03-05 16:28:00 +00:00
this . statMap . set ( "restrict" , "Custom Item" )
}
2022-07-29 05:48:01 +00:00
copy ( ) {
return new Custom ( new Map ( this . statMap ) ) ;
}
2021-03-05 16:28:00 +00:00
}