diff --git a/js/builder/build_encode_decode.js b/js/builder/build_encode_decode.js index 2c280b7..d67cf64 100644 --- a/js/builder/build_encode_decode.js +++ b/js/builder/build_encode_decode.js @@ -4,6 +4,195 @@ let build_powders; function getItemNameFromID(id) { return idMap.get(id); } function getTomeNameFromID(id) { return tomeIDMap.get(id); } +/* + * Populate fields based on url, and calculate build. + * TODO: THIS CODE IS GOD AWFUL result of being lazy + * fix all the slice() and break into functions or do something about it... its inefficient, ugly and error prone + */ +async function parse_hash(url_tag) { + const default_load_promises = [ load_atree_data(wynn_version_names[WYNN_VERSION_LATEST]), + load_init(), load_ing_init(), load_tome_init() ]; + if (!url_tag) { + await Promise.all(default_load_promises); + return; + } + //default values + let equipment = [null, null, null, null, null, null, null, null, null]; + let tomes = [null, null, null, null, null, null, null]; + let powdering = ["", "", "", "", ""]; + let info = url_tag.split("_"); + let version = info[0]; + let save_skp = false; + let skillpoints = [0, 0, 0, 0, 0]; + let level = 106; + + let version_number = parseInt(version); + let data_str = info[1]; + if (version_number >= 8) { + // parse query parameters + // https://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript + const url_params = new URLSearchParams(window.location.search); + const version_id = url_params.get('v'); + wynn_version_id = parseInt(version_id); + if (isNaN(wynn_version_id) || wynn_version_id > WYNN_VERSION_LATEST || wynn_version_id < 0) { + // TODO: maybe make the NAN try to use the human readable version? + // NOTE: Failing silently... do we want to raise a loud error? + console.log("Explicit version not found or invalid, using latest version"); + wynn_version_id = WYNN_VERSION_LATEST; + } + else { + console.log(`Build link for wynn version ${wynn_version_id} (${wynn_version_names[wynn_version_id]})`); + } + } + else { + // Change the default to oldest. (A time before v8) + wynn_version_id = 0; + } + + // the deal with this is because old versions should default to 0 (oldest wynn item version), and v8+ defaults to latest. + // its ugly... but i think this is the behavior we want... + if (wynn_version_id != WYNN_VERSION_LATEST) { + // force reload item database and such. + // TODO MUST: display a warning showing older version! + const msg = 'This build was created in an older version of wynncraft ' + + `(${wynn_version_names[wynn_version_id]} < ${wynn_version_names[WYNN_VERSION_LATEST]}). ` + + 'Would you like to update to the latest version? Updating may break the build and ability tree.'; + + if (confirm(msg)) { + wynn_version_id = WYNN_VERSION_LATEST; + } + else { + version_name = wynn_version_names[wynn_version_id]; + const load_promises = [ load_atree_data(version_name), + load_old_version(version_name), + load_ings_old_version(version_name), + load_tome_old_version(version_name) ]; + console.log("Loading old version data...", version_name) + await Promise.all(load_promises); + } + } + if (wynn_version_id == WYNN_VERSION_LATEST) { + await Promise.all(default_load_promises); + } + + //equipment (items) + // TODO: use filters + if (version_number < 4) { + let equipments = info[1]; + for (let i = 0; i < 9; ++i ) { + let equipment_str = equipments.slice(i*3,i*3+3); + equipment[i] = getItemNameFromID(Base64.toInt(equipment_str)); + } + data_str = equipments.slice(27); + } + else if (version_number == 4) { + let info_str = data_str; + let start_idx = 0; + for (let i = 0; i < 9; ++i ) { + if (info_str.charAt(start_idx) === "-") { + equipment[i] = "CR-"+info_str.slice(start_idx+1, start_idx+18); + start_idx += 18; + } + else { + let equipment_str = info_str.slice(start_idx, start_idx+3); + equipment[i] = getItemNameFromID(Base64.toInt(equipment_str)); + start_idx += 3; + } + } + data_str = info_str.slice(start_idx); + } + else if (version_number <= 8) { + let info_str = data_str; + let start_idx = 0; + for (let i = 0; i < 9; ++i ) { + if (info_str.slice(start_idx,start_idx+3) === "CR-") { + equipment[i] = info_str.slice(start_idx, start_idx+20); + start_idx += 20; + } else if (info_str.slice(start_idx+3,start_idx+6) === "CI-") { + let len = Base64.toInt(info_str.slice(start_idx,start_idx+3)); + equipment[i] = info_str.slice(start_idx+3,start_idx+3+len); + start_idx += (3+len); + } else { + let equipment_str = info_str.slice(start_idx, start_idx+3); + equipment[i] = getItemNameFromID(Base64.toInt(equipment_str)); + start_idx += 3; + } + } + data_str = info_str.slice(start_idx); + } + //constant in all versions + for (let i in equipment) { + setValue(equipment_inputs[i], equipment[i]); + } + + //level, skill point assignments, and powdering + if (version_number == 1) { + let powder_info = data_str; + let res = parsePowdering(powder_info); + powdering = res[0]; + } else if (version_number == 2) { + save_skp = true; + let skillpoint_info = data_str.slice(0, 10); + for (let i = 0; i < 5; ++i ) { + skillpoints[i] = Base64.toIntSigned(skillpoint_info.slice(i*2,i*2+2)); + } + + let powder_info = data_str.slice(10); + let res = parsePowdering(powder_info); + powdering = res[0]; + } else if (version_number <= 8){ + level = Base64.toInt(data_str.slice(10,12)); + setValue("level-choice",level); + save_skp = true; + let skillpoint_info = data_str.slice(0, 10); + for (let i = 0; i < 5; ++i ) { + skillpoints[i] = Base64.toIntSigned(skillpoint_info.slice(i*2,i*2+2)); + } + + let powder_info = data_str.slice(12); + + let res = parsePowdering(powder_info); + powdering = res[0]; + data_str = res[1]; + } + // Tomes. + if (version_number >= 6) { + //tome values do not appear in anything before v6. + if (version_number < 8) { + for (let i in tomes) { + let tome_str = data_str.charAt(i); + let tome_name = getTomeNameFromID(Base64.toInt(tome_str)); + setValue(tomeInputs[i], tome_name); + } + data_str = data_str.slice(7); + } + else { + // 2chr tome encoding to allow for more tomes. + for (let i in tomes) { + let tome_str = data_str.slice(2*i, 2*i+2); + let tome_name = getTomeNameFromID(Base64.toInt(tome_str)); + setValue(tomeInputs[i], tome_name); + } + data_str = data_str.slice(14); + } + } + + if (version_number >= 7) { + // ugly af. only works since its the last thing. will be fixed with binary decode + atree_data = new BitVector(data_str); + } + else { + atree_data = null; + } + + for (let i in powder_inputs) { + setValue(powder_inputs[i], powdering[i]); + } + for (let i in skillpoints) { + setValue(skp_order[i] + "-skp", skillpoints[i]); + } +} + function parsePowdering(powder_info) { // TODO: Make this run in linear instead of quadratic time... ew let powdering = []; diff --git a/js/utils.js b/js/utils.js index 7fd8c8b..c432812 100644 --- a/js/utils.js +++ b/js/utils.js @@ -172,7 +172,13 @@ Base64 = (function () { } } } else if (typeof data === "number") { - if (typeof length === "undefined") + if (typeof length === "undefined") { + if (data == 0) { + length = 0; + } else { + length = Math.ceil(Math.log(data) / Math.log(2)); + } + } if (length < 0) { throw new RangeError("BitVector must have nonnegative length."); } @@ -333,13 +339,9 @@ Base64 = (function () { let new_length = this.length + length; if (this.bits.length * this.bits.BYTES_PER_ELEMENT * 8 < new_length) { //resize the internal repr by a factor of 2 before recursive calling - let bit_vec = []; - for (const int of this.bits) { - bit_vec.push(int); - } - //effectively double size - TODO 1-line this - for (const int of this.bits) { - bit_vec.push(0); + let bit_vec = Array(2 * this.bits.length).fill(0); + for (let i = 0; i < this.bits.length; i++) { + bit_vec[i] = this.bits[i]; } this.bits = new Uint32Array(bit_vec); @@ -403,6 +405,18 @@ Base64 = (function () { } }; +function test_bv() { + //empty array + let bv = new BitVector(0); + bv.append(10, 4); + console.log(bv); + + bv = new BitVector(0); + bv.append(10, 5); + console.log(bv); + +} + /* Turns a raw stat and a % stat into a final stat on the basis that - raw and >= 100% becomes 0 and + raw and <=-100% becomes negative. @@ -1039,4 +1053,6 @@ if (screen.width < 992) { } scrollPos = document.documentElement.scrollTop; }); -} \ No newline at end of file +} + +test_bv(); \ No newline at end of file