>OmegaK2 |
|
Line 1: |
Line 1: |
| <table class="monster_container" style="border: white solid 1px; width: 100%;">
| | <!-- This is not an actual widget. Do not add code. --> |
| <thead>
| |
| <tr>
| |
| <th class="info_header">Fetching data...</th>
| |
| </tr>
| |
| </thead>
| |
| <tbody>
| |
| </tbody>
| |
| </table><script type="text/javascript">{
| |
| const i18n = {
| |
| missing_monster: 'No monster found with <!--{$metadata_id}-->',
| |
| calculating: 'Calculating properties',
| |
| query_error: 'A database query error has occured.',
| |
| invalid_rarity: 'Rarity given is invalid. Only Normal, Magic, Rare and Unique are acceptable values.',
| |
| invalid_difficulty: 'Difficulty given is invalid. Only part1, part2 and endgame are acceptable values.',
| |
| invalid_level: 'Level given is not a number or outside of the valid range (1-100)',
| |
| // (x to y)
| |
| range: 'to',
| |
| };
| |
| | |
| let init_level = 0;
| |
| const init_max = 3;
| |
| const difficulties = {
| |
| part1: 45,
| |
| part2: 67,
| |
| endgame: 100,
| |
| };
| |
| | |
| const difficulty_order = ['part1', 'part2', 'endgame'];
| |
| | |
| const rarities = {
| |
| Normal: 0,
| |
| Magic: 1,
| |
| Rare: 2,
| |
| Unique: 3,
| |
| };
| |
| | |
| const html_infobox = `<tr class="monster"><td>
| |
| <table class="monster_info_container">
| |
| <tr class="monster_name"><th><em class="monster_name tc">?</em></th></tr>
| |
| <tr class="controls"><td><table class="wikitable controls">
| |
| <thead>
| |
| <tr>
| |
| <th colspan="2">Controls</th>
| |
| </tr>
| |
| </thead>
| |
| <tbody>
| |
| <tr>
| |
| <th>Level</th>
| |
| <td><input type="number" name="level" min="1" max="100" value="1"/></td>
| |
| </tr>
| |
| <tr>
| |
| <th>Rarity</th>
| |
| <td><select name="rarity">
| |
| <option value="Normal" selected>Normal</option>
| |
| <option value="Magic">Magic</option>
| |
| <option value="Rare">Rare</option>
| |
| <option value="Unique">Unique</option>
| |
| </select></td>
| |
| </tr>
| |
| <tr>
| |
| <th>Is map monster?</th>
| |
| <td><input type="checkbox" name="is_map_monster" value="1" /></td>
| |
| </tr>
| |
| <tr>
| |
| <th>Is map boss?</th>
| |
| <td><input type="checkbox" name="is_map_boss" value="1" /></td>
| |
| </tr>
| |
| <tr>
| |
| <th>Is minion</th>
| |
| <td><input type="checkbox" name="is_minion" value="1" /></td>
| |
| </tr>
| |
| <tr>
| |
| <th>Difficuty</th>
| |
| <td><select name="difficulty">
| |
| <option value="part1" selected>Part 1</option>
| |
| <option value="part2">Part 2</option>
| |
| <option value="endgame">Endgame</option>
| |
| </select></td>
| |
| </tr>
| |
| </tbody>
| |
| </table></td></tr>
| |
| | |
| <tr class="monster_table"><td><table class="wikitable monster_table">
| |
| <!-- static attributes -->
| |
| <tr>
| |
| <th colspan="2"><em class="monster_name tc">?</em></th>
| |
| </tr>
| |
| <tr>
| |
| <th>Tags</th>
| |
| <td class="monster_tags">?</td>
| |
| </tr>
| |
| <tr>
| |
| <th>Metadata ID</th>
| |
| <td class="monster_metadata_id">?</td>
| |
| </tr>
| |
| <tr>
| |
| <th>Skills</th>
| |
| <td class="monster_skill_ids">?</td>
| |
| </tr>
| |
| <tr>
| |
| <th>Size</th>
| |
| <td class="monster_size">?</td>
| |
| </tr>
| |
| <tr>
| |
| <th>Minimum attack distance</th>
| |
| <td class="monster_minimum_attack_distance">?</td>
| |
| </tr>
| |
| <tr>
| |
| <th>Maximum attack distance</th>
| |
| <td class="monster_maximum_attack_distance">?</td>
| |
| </tr>
| |
|
| |
| <!-- Variable attributes -->
| |
| <tr>
| |
| <th>Experience</th>
| |
| <td class="monster_experience">?</td>
| |
| </tr>
| |
| <tr>
| |
| <th>Life</th>
| |
| <td class="monster_life">?</td>
| |
| </tr>
| |
| <!-- Wiki colours every other line differently, prevent that from happening -->
| |
| <tr style="display:none"></tr>
| |
| <tr>
| |
| <th>Life</th>
| |
| <td class="monster_summon_life">?</td>
| |
| </tr>
| |
| <tr>
| |
| <th>Accuracy</th>
| |
| <td class="monster_accuracy">?</td>
| |
| </tr>
| |
| <tr>
| |
| <th>Armour</th>
| |
| <td class="monster_armour">?</td>
| |
| </tr>
| |
| <tr>
| |
| <th>Evasion</th>
| |
| <td class="monster_evasion">?</td>
| |
| </tr>
| |
| <tr>
| |
| <th>Base Physical Damage</th>
| |
| <td class="monster_damage">?</td>
| |
| </tr>
| |
| <tr>
| |
| <th>Base Attack Speed</th>
| |
| <td class="monster_attack_speed">?</td>
| |
| </tr>
| |
| <tr>
| |
| <th>Critical Strike Chance</th>
| |
| <td class="monster_critical_strike_chance">?</td>
| |
| </tr>
| |
| <tr>
| |
| <th>Critical Strike Multiplier</th>
| |
| <td class="monster_critical_strike_multiplier">?</td>
| |
| </tr>
| |
| <tr>
| |
| <th>Increased Rarity</th>
| |
| <td class="monster_rarity">?</td>
| |
| </tr>
| |
| <tr>
| |
| <th>Increased Quantity</th>
| |
| <td class="monster_quantity">?</td>
| |
| </tr>
| |
| <tr>
| |
| <th>Resistances</th>
| |
| <td><table class="monster_resistance"><tr>
| |
| <td class="tc -fire">?</td>
| |
| <td class="tc -cold">?</td>
| |
| <td class="tc -lightning">?</td>
| |
| <td class="tc -chaos">?</td>
| |
| </tr></table></td>
| |
| </tr>
| |
| <tr>
| |
| <th>Stats</th>
| |
| <td class="monster_stat_text">?</td>
| |
| </tr>
| |
| </table></td></tr>
| |
| | |
| <tr class="monster_stats"><td><table class="wikitable monster_stats">
| |
| <thead>
| |
| <tr>
| |
| <th colspan="3">Stats</th>
| |
| </tr>
| |
| <tr>
| |
| <th>Stat</td>
| |
| <th>Min</td>
| |
| <th>Max</td>
| |
| </tr>
| |
| </thead>
| |
| <tbody>
| |
| </tbody>
| |
| </table></td></tr>
| |
| </table></td></tr>`
| |
| | |
| | |
| class Cargo {
| |
| static _fail(jqXHR, textStatus, errorThrown) {
| |
| console.log(textStatus);
| |
| }
| |
|
| |
| static _wrap_success(func) {
| |
| return function(data) {
| |
| data = data.cargoquery;
| |
| func(data);
| |
| }
| |
| }
| |
|
| |
| static query (query, success, fail) {
| |
| if (typeof fail === 'undefined') {
| |
| fail = Cargo._fail;
| |
| }
| |
|
| |
| query.action = 'cargoquery';
| |
|
| |
| // Query size for normal users is limited to 500
| |
| if (typeof query.limit === 'undefined' || (typeof query.limit === 'number' && query.limit > 500)) {
| |
| query.limit = 500;
| |
| }
| |
|
| |
| if (Array.isArray(query.tables)) {
| |
| query.tables = query.tables.join(', ');
| |
| }
| |
|
| |
| if (Array.isArray(query.fields)) {
| |
| query.fields.forEach(function (value, index) {
| |
| if (!value.includes('=')) {
| |
| query.fields[index] = `${value}=${value}`;
| |
| }
| |
| });
| |
|
| |
| query.fields = query.fields.join(', ');
| |
| }
| |
| var mwa = new mw.Api();
| |
|
| |
| console.log(query.where);
| |
|
| |
| mwa.get(query).done(Cargo._wrap_success(success)).fail(fail);
| |
| }
| |
| }
| |
| | |
| class Util {
| |
| static bool(value) {
| |
| switch(value) {
| |
| case false:
| |
| case 0:
| |
| case 'disabled':
| |
| case 'off':
| |
| case 'no':
| |
| case '':
| |
| case 'deactivated':
| |
| return false;
| |
| default:
| |
| return true;
| |
| }
| |
| }
| |
| }
| |
| | |
| class StatCalculation {
| |
| static added(values, value) {
| |
| values.forEach(function (other) {
| |
| value.add(other);
| |
| });
| |
| }
| |
| static increased(values, value) {
| |
| var multi = new Stat('', 0);
| |
| values.forEach(function (inc) {
| |
| multi.add(inc);
| |
| });
| |
| multi.div(100);
| |
| multi.add(1);
| |
| value.mult(multi);
| |
| }
| |
| static more(values, value) {
| |
| values.forEach(function (multi) {
| |
| var temp = multi.copy();
| |
| temp.div(100);
| |
| temp.add(1);
| |
| value.mult(temp);
| |
| });
| |
| }
| |
| static less(values, value) {
| |
| values.forEach(function (multi) {
| |
| var temp = multi.copy();
| |
| temp.div(100);
| |
| // since it's less it's negative and subtracted from 1
| |
| temp.mult(-1);
| |
| temp.add(1);
| |
| value.mult(temp);
| |
| });
| |
| }
| |
| }
| |
| | |
| class Stat {
| |
| constructor(id, min, max) {
| |
| this.id = id;
| |
| this.min = min;
| |
| if (typeof max === 'undefined') {
| |
| max = min;
| |
| }
| |
| this.max = max;
| |
| }
| |
|
| |
| copy() {
| |
| return new Stat(this.id, this.min, this.max);
| |
| }
| |
|
| |
| str(func) {
| |
| if (this.min == this.max) {
| |
| if (typeof func === 'undefined') {
| |
| return this.min;
| |
| } else {
| |
| return func(this.min);
| |
| }
| |
| } else {
| |
| var min;
| |
| var max;
| |
| if (typeof func === 'undefined') {
| |
| min = this.min;
| |
| max = this.max;
| |
| } else {
| |
| min = func(this.min);
| |
| max = func(this.max);
| |
| }
| |
| return `(${min} ${i18n.range} ${max})`;
| |
| }
| |
| }
| |
|
| |
| is_zero() {
| |
| return (this.min == 0 && this.max == 0);
| |
| }
| |
|
| |
| _type_check(other, func_number, func_stat) {
| |
| var t = typeof other;
| |
| if (t === 'number') {
| |
| func_number(this);
| |
| } else if (t === 'object' && other.constructor.name == 'Stat') {
| |
| func_stat(this);
| |
| } else {
| |
| throw 'Stat arithmetric requires a stat object or number, got type "' + t + '"';
| |
| }
| |
| }
| |
|
| |
| add(other) {
| |
| this._type_check(other,
| |
| function(stat) {
| |
| stat.min += other;
| |
| stat.max += other;
| |
| },
| |
| function(stat) {
| |
| stat.min += other.min;
| |
| stat.max += other.max;
| |
| },
| |
| )
| |
| }
| |
| sub(other) {
| |
| this._type_check(other,
| |
| function(stat) {
| |
| stat.min -= other;
| |
| stat.max -= other;
| |
| },
| |
| function(stat) {
| |
| stat.min -= other.min;
| |
| stat.max -= other.max;
| |
| },
| |
| )
| |
| }
| |
| mult(other) {
| |
| this._type_check(other,
| |
| function(stat) {
| |
| stat.min *= other;
| |
| stat.max *= other;
| |
| },
| |
| function(stat) {
| |
| stat.min *= other.min;
| |
| stat.max *= other.max;
| |
| },
| |
| )
| |
| }
| |
| div(other) {
| |
| this._type_check(other,
| |
| function(stat) {
| |
| stat.min /= other;
| |
| stat.max /= other;
| |
| },
| |
| function(stat) {
| |
| stat.min /= other.min;
| |
| stat.max /= other.max;
| |
| },
| |
| )
| |
| }
| |
| }
| |
| | |
| class Monster {
| |
| constructor(metadata_id) {
| |
| var controls = {
| |
| level: {
| |
| 'var': `<!--{$level}-->`,
| |
| 'default': 1,
| |
| 'type': 'input',
| |
| 'check': 'number',
| |
| 'func_update': this.update_level.bind(this),
| |
| 'func_validate': function (value) {
| |
| var level = Number(value);
| |
| if (level < 1 || level > 100) {
| |
| alert(i18n.invalid_level);
| |
| return -1;
| |
| }
| |
| return level;
| |
| },
| |
| },
| |
| rarity: {
| |
| 'var': `<!--{$rarity}-->`,
| |
| 'default': 'Normal',
| |
| 'type': 'select',
| |
| 'check': 'value',
| |
| 'func_update': this.update_rarity.bind(this),
| |
| 'func_validate': function (value) {
| |
| if (typeof rarities[value] === 'undefined') {
| |
| alert(i18n.invalid_rarity);
| |
| return -1;
| |
| }
| |
| return value;
| |
| },
| |
| },
| |
| is_map_monster: {
| |
| 'var': `<!--{$is_map_monster}-->`,
| |
| 'default': false,
| |
| 'type': 'input',
| |
| 'check': 'checked',
| |
| 'func_update': this.update_is_map_monster.bind(this),
| |
| 'func_validate': function (value) {
| |
| return Util.bool(value);
| |
| },
| |
| },
| |
| is_map_boss: {
| |
| 'var': `<!--{$is_map_boss}-->`,
| |
| 'default': false,
| |
| 'type': 'input',
| |
| 'check': 'checked',
| |
| 'func_update': this.update_is_map_boss.bind(this),
| |
| 'func_validate': function (value) {
| |
| return Util.bool(value);
| |
| },
| |
| },
| |
| is_minion: {
| |
| 'var': `<!--{$is_minion}-->`,
| |
| 'default': false,
| |
| 'type': 'input',
| |
| 'check': 'checked',
| |
| 'func_update': this.update_is_minion.bind(this),
| |
| 'func_validate': function (value) {
| |
| return Util.bool(value);
| |
| },
| |
| },
| |
| difficulty: {
| |
| 'var': `<!--{$difficulty}-->`,
| |
| 'default': 'part1',
| |
| 'type': 'select',
| |
| 'check': 'value',
| |
| 'func_update': this.update_difficulty.bind(this),
| |
| 'func_validate': function (value) {
| |
| if (typeof difficulties[value] === 'undefined') {
| |
| alert(i18n.invalid_difficulty);
| |
| return -1;
| |
| }
| |
| return value;
| |
| },
| |
| },
| |
| };
| |
| this.metadata_id = metadata_id;
| |
| this.data = Monster.base_data_by_id[this.metadata_id];
| |
| this.stats = {};
| |
| this.stat_text = '';
| |
| // TODO: Maybe set a parameter on the widget to determine default here?
| |
| this.show = true;
| |
|
| |
| for (const [_, mod_id] of Object.entries(this.data['monsters.mod_ids'])) {
| |
| var mod = Monster.mods[mod_id];
| |
| this._update_stat_text(mod.stat_text);
| |
| for (const [_, stat] of Object.entries(mod.stats)) {
| |
| this.update_stat(stat.id, stat.copy());
| |
| }
| |
| }
| |
|
| |
|
| |
| var html = $(html_infobox);
| |
| html.attr('id', this.metadata_id);
| |
|
| |
| $('.monster_container > tbody').append(html);
| |
| this.html = $(`tr[id="${this.metadata_id}"`);
| |
|
| |
| this.tbl = this.html.find('table.monster_table');
| |
| this.stat_tbl = this.html.find('table.monster_stats');
| |
| this.stat_bdy = this.stat_tbl.find('tbody');
| |
|
| |
| this.html.find('tr.monster_name th').click(this.toggle_show.bind(this));
| |
|
| |
| this.ctl = this.html.find('table.controls');
| |
|
| |
| //
| |
| // Set and init defaults
| |
| //
| |
| for (const [name, cdata] of Object.entries(controls)) {
| |
| this[name] = cdata.default;
| |
| }
| |
|
| |
| this.update_rarity(this.rarity);
| |
| // Also updates map boss and map monster
| |
| this.update_level(this.level);
| |
|
| |
| // Will also set current difficulty
| |
| this.update_difficulty(this.difficulty);
| |
| this.update_is_minion(this.is_minion);
| |
|
| |
| // Widget parameters or set defaults
| |
| for (const [name, cdata] of Object.entries(controls)) {
| |
| var ele = this.ctl.find(`${cdata.type}[name="${name}"]`);
| |
|
| |
| // Still not entirely sure how the widget works
| |
| if (cdata.var == '<!--' + '{$' + name + '}-->' || cdata.var === '') {
| |
| cdata.var = cdata.default;
| |
| } else {
| |
| var rtr = cdata.func_validate(cdata.var);
| |
| if (rtr == -1) {
| |
| cdata.var = cdata.default;
| |
| } else {
| |
| cdata.var = rtr;
| |
| this[name] = cdata.var;
| |
| cdata.func_update(rtr);
| |
| }
| |
| }
| |
|
| |
| // Update the UI element accordingly regardless of the original HTML value
| |
| if (cdata.type == 'input') {
| |
| switch (cdata.check) {
| |
| case 'checked':
| |
| ele.prop('checked', cdata.var);
| |
| break;
| |
| case 'number':
| |
| ele.prop('value', cdata.var);
| |
| break;
| |
| }
| |
| } else if (cdata.type == 'select') {
| |
| if (name == 'rarity') {
| |
| ele.prop('selectedIndex', rarities[cdata.var]);
| |
| } else if (name == 'difficulty') {
| |
| for (const [index, value] of Object.entries(difficulty_order)) {
| |
| if (value == cdata.var) {
| |
| ele.prop('selectedIndex', index);
| |
| break;
| |
| }
| |
| }
| |
| }
| |
| }
| |
|
| |
| // Add listener function
| |
| var m = this;
| |
| ele.change(function () {
| |
| var value;
| |
| // In this context, "this" is the element that called this function
| |
| switch (cdata.check) {
| |
| case 'checked':
| |
| value = this.checked;
| |
| break;
| |
| case 'number':
| |
| value = Number(this.value);
| |
| break;
| |
| case 'value':
| |
| default:
| |
| value = this.value;
| |
| }
| |
| cdata.func_update(value);
| |
| m.update_infobox();
| |
| });
| |
| }
| |
|
| |
| // Static infobox properties
| |
| this.html.find('em.monster_name').html(this.data['monsters.name']);
| |
|
| |
| // Can be directly inserted into the releveant fields
| |
| for (const [i, key] of Object.entries([
| |
| 'size',
| |
| 'minimum_attack_distance',
| |
| 'maximum_attack_distance',
| |
| ])) {
| |
| this.tbl.find(`.monster_${key}`).html(this.data[`monsters.${key}`]);
| |
| }
| |
|
| |
| this.tbl.find('.monster_skill_ids').html(this.data['monsters.skill_ids'].join(', '));
| |
|
| |
| var tags = this.data['monsters.tags'].concat(this.data['monster_types.tags']);
| |
| this.tbl.find('.monster_tags').html(tags.join(', '));
| |
|
| |
| this.tbl.find('.monster_metadata_id').html(`<a href="/${this.data['monsters._pageName']}">${this.data['monsters.metadata_id']}`);
| |
|
| |
| // Update for the defaults
| |
| this.update_infobox();
| |
| }
| |
|
| |
| toggle_show() {
| |
| if (this.show) {
| |
| this.ctl.css('display', 'none');
| |
| this.tbl.css('display', 'none');
| |
| this.stat_tbl.css('display', 'none');
| |
| this.show = false;
| |
| } else {
| |
| this.ctl.css('display', 'table');
| |
| this.tbl.css('display', 'table');
| |
| this.stat_tbl.css('display', 'table');
| |
| this.show = true;
| |
| }
| |
| }
| |
|
| |
| _update_stat_text(stat_text) {
| |
| if (stat_text == '') {
| |
| return;
| |
| }
| |
|
| |
| stat_text = new DOMParser().parseFromString(stat_text, "text/html").documentElement.textContent;
| |
| // [[a|b]] style wiki links
| |
| stat_text = stat_text.replace(/\[\[([^\|]+)\|([^\]]+)\]\]/g, '<a href="/$1">$2</a>');
| |
| // [[a]] style wiki links
| |
| stat_text = stat_text.replace(/\[\[([^\]]+)\]\]/g, '<a href="/$1">$1</a>');
| |
|
| |
| if (this.stat_text == '') {
| |
| this.stat_text = stat_text;
| |
| } else {
| |
| this.stat_text += '<br>' + stat_text;
| |
| }
| |
| }
| |
|
| |
| update_level(level) {
| |
| // Deletes old and adds new in one go
| |
| this.update_is_map_boss(this.is_map_boss, this.level, level);
| |
| this.update_is_map_monster(this.is_map_monster, this.level, level);
| |
|
| |
| // Only deletes the old level
| |
| this._update_rarity(this.rarity, true, this.level);
| |
| this.level = level;
| |
|
| |
| // Rarity data for new level
| |
| this._update_rarity(this.rarity, false, this.level);
| |
| }
| |
|
| |
| _update_difficulty_mods(difficulty, del=false) {
| |
| for (const [_, mod_id] of Object.entries(this.data[`monsters.${difficulty}_mod_ids`])) {
| |
| var mod = Monster.mods[mod_id];
| |
| //TODO
| |
| //this.stat_text += '<br>' + mod.stat_text;
| |
| for (const [_, stat] of Object.entries(mod.stats)) {
| |
| this.update_stat(stat.id, stat.copy(), del);
| |
| }
| |
| }
| |
| }
| |
|
| |
| update_difficulty(difficulty) {
| |
| var old = this.difficulty;
| |
| if (old == difficulty) {
| |
| return;
| |
| }
| |
|
| |
| if (typeof difficulty !== 'undefined') {
| |
| if (typeof difficulties[difficulty] === 'undefined') {
| |
| alert('Undefined difficulty. You probably messed with JS.');
| |
| } else {
| |
| this.difficulty = difficulty;
| |
| }
| |
| } else {
| |
| for (const [_, diff] of Object.entries(difficulty_order)) {
| |
| if (this.level <= difficulties[diff]) {
| |
| this.difficulty = diff;
| |
| break;
| |
| }
| |
| }
| |
| }
| |
|
| |
| this._update_difficulty_mods(old, true);
| |
| this._update_difficulty_mods(difficulty, false);
| |
| }
| |
|
| |
| _update_map_monster_stats (stats, del=false, level) {
| |
| for (var i=0;i<stats.length;i++) {
| |
| var variable = stats[i];
| |
| var v = Monster.level_data[level][variable];
| |
| if (typeof v === 'undefined') {
| |
| if (this.level < 66) {
| |
| console.log('Map monster is set but undefined damage/hp multiplier value at level ' + this.level);
| |
| return;
| |
| } else {
| |
| console.log('Map monster is set but undefined damage/hp multiplier value at level ' + this.level + '. The highest available value will be used');
| |
| for (var l=Monster.level_data.length-1; l>=0; l--) {
| |
| v = Monster.level_data[l][variable];
| |
| if (typeof v !== 'undefined') {
| |
| break;
| |
| }
| |
| }
| |
| }
| |
| } else {
| |
| this.update_stat(Monster.stat_map[variable], Monster.level_data[level][variable], del);
| |
| }
| |
| }
| |
| }
| |
|
| |
| update_is_map_monster(is_map_monster, level, new_level) {
| |
| if (typeof level === 'undefined') {
| |
| level = this.level;
| |
| }
| |
|
| |
| if (typeof new_level === 'undefined') {
| |
| new_level = level;
| |
| }
| |
|
| |
| // Must be toggled off before changing the value to make sure the stats are removed correclty
| |
| if (is_map_monster) {
| |
| this.ctl.find('input[name="is_map_boss"]').prop('disabled', false);
| |
| } else {
| |
| this.ctl.find('input[name="is_map_boss"]').prop('disabled', true);
| |
| // Can no longer be a map boss if it's not a map monster
| |
| if (this.is_map_boss) {
| |
| this.update_is_map_boss(false);
| |
| this.ctl.find('input[name="is_map_boss"]').prop('checked', false);
| |
| }
| |
| }
| |
|
| |
| if (this.is_map_monster) {
| |
| this._update_map_monster_stats(['map_life_multiplier', 'map_damage_multiplier'], true, level);
| |
| }
| |
| this.is_map_monster = is_map_monster;
| |
| if (is_map_monster) {
| |
| this._update_map_monster_stats(['map_life_multiplier', 'map_damage_multiplier'], false, new_level);
| |
| }
| |
| }
| |
|
| |
| update_is_map_boss(is_map_boss, level, new_level) {
| |
| if (typeof level === 'undefined') {
| |
| level = this.level;
| |
| }
| |
|
| |
| if (typeof new_level === 'undefined') {
| |
| new_level = level;
| |
| }
| |
|
| |
| // Only delete if it has been a map boss and map monster before to avoid putting stats into the negative
| |
| if (this.is_map_boss && this.is_map_monster) {
| |
| this._update_map_monster_stats(['boss_life', 'boss_damage', 'boss_item_quantity', 'boss_item_rarity'], true, level);
| |
| }
| |
| this.is_map_boss = is_map_boss;
| |
| // Only update if it is a map boss and map monster to avoid stacking stats incorrectly
| |
| if (is_map_boss && this.is_map_monster) {
| |
| this._update_map_monster_stats(['boss_life', 'boss_damage', 'boss_item_quantity', 'boss_item_rarity'], false, new_level);
| |
| }
| |
| }
| |
|
| |
| update_is_minion(is_minion) {
| |
| this.is_minion = is_minion;
| |
| // Either show regular life value or minion life value
| |
| if (is_minion) {
| |
| this.tbl.find('.monster_life').parent('tr').css('display', 'none');
| |
| this.tbl.find('.monster_summon_life').parent('tr').css('display', 'table-row');
| |
| this.ctl.find('select[name="rarity"]').prop('disabled', true);
| |
| this.ctl.find('input[name="is_map_monster"]').prop('disabled', true);
| |
| this.ctl.find('input[name="is_map_boss"]').prop('disabled', true);
| |
|
| |
| if (this.rarity != 'Normal') {
| |
| this.ctl.find('select[name="rarity"]').prop('selectedIndex', 0);
| |
| this.update_rarity('Normal');
| |
| }
| |
|
| |
| if (this.is_map_boss) {
| |
| this.ctl.find('input[name="is_map_boss"]').prop('checked', 0);
| |
| this.update_is_map_boss(false);
| |
| }
| |
|
| |
| if (this.is_map_monster) {
| |
| this.ctl.find('input[name="is_map_monster"]').prop('checked', 0);
| |
| this.update_is_map_monster(false);
| |
| }
| |
|
| |
| } else {
| |
| this.tbl.find('.monster_life').parent('tr').css('display', 'table-row');
| |
| this.tbl.find('.monster_summon_life').parent('tr').css('display', 'none');
| |
| this.ctl.find('select[name="rarity"]').prop('disabled', false);
| |
| this.ctl.find('input[name="is_map_monster"]').prop('disabled', false);
| |
| this.ctl.find('input[name="is_map_boss"]').prop('disabled', false);
| |
| }
| |
|
| |
| }
| |
|
| |
| // this can also be called via level change since monster rarity hp multiplier depend on the level
| |
| _update_rarity(rarity, del=false, level) {
| |
| for (const [stat_id, value] of Object.entries(Monster.rarity_data[rarity])) {
| |
| this.update_stat(stat_id, value, del);
| |
| }
| |
|
| |
| if (rarity == 'Magic' || rarity == 'Rare') {
| |
| if (typeof level === 'undefined') {
| |
| level = this.level;
| |
| }
| |
| var v = Monster.level_data[level][rarity + '_life_multiplier'];
| |
| // In this case (i.e. level > 84) the highest available will be used
| |
| // Since this might change in the future a loop is used to determine the max level
| |
| if (typeof v === 'undefined') {
| |
| for (var i=Monster.level_data.length; i>=0; i--) {
| |
| v = Monster.level_data[i][variable];
| |
| if (typeof v !== 'undefined') {
| |
| break;
| |
| }
| |
| }
| |
| }
| |
| this.update_stat(Monster.stat_map[rarity + '_life_multiplier'], v, del);
| |
| }
| |
| }
| |
|
| |
| update_rarity(rarity, level) {
| |
| // Delete stats from previous rarity level
| |
| this._update_rarity(this.rarity, true, level);
| |
|
| |
| this.rarity = rarity;
| |
| this._update_rarity(this.rarity, false, level);
| |
| }
| |
|
| |
| | |
|
| |
| update_stat(stat_id, value, del=false) {
| |
| var v = this.stats[stat_id];
| |
| if (typeof v === 'undefined') {
| |
| if (!del) {
| |
| if (typeof value === 'number') {
| |
| value = new Stat(stat_id, value);
| |
| } else {
| |
| value = value.copy();
| |
| }
| |
| this.stats[stat_id] = value;
| |
| }
| |
| // If the stat doesn't exist, we don't need to delete it
| |
| } else {
| |
| if (del) {
| |
| v.sub(value);
| |
| if (v.is_zero()) {
| |
| delete this.stats[stat_id];
| |
| }
| |
| } else {
| |
| v.add(value);
| |
| }
| |
| }
| |
| }
| |
|
| |
| calculate_property(type) {
| |
| var calc = Monster.calculation_params[type];
| |
| var value;
| |
| switch (calc.type) {
| |
| case 'level':
| |
| value = new Stat(type, Monster.level_data[this.level][type]);
| |
| if (typeof calc.base !== 'undefined') {
| |
| value.mult(this.data[calc.base]);
| |
| }
| |
| break;
| |
| case 'base':
| |
| value = new Stat(type, this.data[calc.base]);
| |
| break;
| |
| case 'resist':
| |
| var diff = this.difficulty;
| |
| var t = type.match(/(cold|chaos|fire|lightning)/)[1];
| |
| value = new Stat(type, this.data[`monster_resistances.${diff}_${t}`]);
| |
| break;
| |
| case 'none':
| |
| default:
| |
| var v;
| |
| if (typeof calc.value === 'undefined') {
| |
| v = 0;
| |
| } else {
| |
| v = calc.value;
| |
| }
| |
| value = new Stat(type, v);
| |
| }
| |
|
| |
| // Overriding is a special case that ingores all further calcuations
| |
| var stats = this.stats;
| |
| if (typeof calc.override !== 'undefined') {
| |
| for (const [stat_id, override_value] of Object.entries(calc.override)) {
| |
| var v = stats[stat_id];
| |
| if (typeof v !== 'undefined') {
| |
| // TODO: Support for overriding to specific values if needed
| |
| if (v != 0) {
| |
| return override_value;
| |
| }
| |
| }
| |
| }
| |
| }
| |
|
| |
| for (const [_, stat_calc_type] of Object.entries(['added', 'increased', 'more', 'less'])) {
| |
| var stat_ids = calc[stat_calc_type];
| |
| if (typeof stat_ids !== 'undefined') {
| |
| var values = [];
| |
| stat_ids.forEach(function (stat_id) {
| |
| var v = stats[stat_id];
| |
| if (typeof v !== 'undefined') {
| |
| values.push(v);
| |
| }
| |
| });
| |
| StatCalculation[stat_calc_type](values, value);
| |
| }
| |
| }
| |
|
| |
| return value;
| |
| }
| |
|
| |
| infobox_level_changed() {
| |
| this.update_level(Number(this.ctl.find('input[name="level"]')[0].value));
| |
| update_infobox();
| |
| }
| |
|
| |
| infobox_rarity_changed() {
| |
| this.update_rarity(this.ctl.find('select[name="rarity"]')[0].value);
| |
| update_infobox();
| |
| }
| |
|
| |
| infobox_is_map_monster_changed() {
| |
| this.update_is_map_monster(this.ctl.find('input[name="is_map_monster"]')[0].checked);
| |
| update_infobox();
| |
| }
| |
|
| |
| infobox_is_map_boss_changed() {
| |
| this.update_is_map_boss(this.ctl.find('input[name="is_map_boss"]')[0].checked);
| |
| update_infobox();
| |
| }
| |
|
| |
| infobox_diffculty_changed() {
| |
| this.update_difficulty(this.ctl.find('select[name="difficulty"]')[0].value);
| |
| update_infobox();
| |
| }
| |
|
| |
|
| |
| update_infobox() {
| |
| $('.monster_container').find('.info_header').css('display', 'table-cell').html(i18n.calculating);
| |
|
| |
| // Correct rarity of the header
| |
| this.html.find('em.monster_name').removeClass('-normal -magic -rare -unique').addClass('-' + this.rarity.toLowerCase());
| |
|
| |
| for (const [key, data] of Object.entries(Monster.calculation_params)) {
| |
| if (key.endsWith('resistance')) {
| |
| continue;
| |
| }
| |
|
| |
| this.tbl.find(`.monster_${key}`).html(this.calculate_property(key).str(data.fmt));
| |
| }
| |
|
| |
| for (const [i, resist_type] of Object.entries(['lightning', 'cold', 'fire', 'chaos'])) {
| |
| var key = resist_type + '_resistance';
| |
| this.tbl.find('.monster_resistance').find(`.-${resist_type}`).html(this.calculate_property(key).str(Monster.calculation_params[key].fmt));
| |
| }
| |
|
| |
| this.tbl.find('.monster_stat_text').html(this.stat_text);
| |
|
| |
| this.stat_bdy.find('tr').remove();
| |
| for (const [stat_id, stat] of Object.entries(this.stats)) {
| |
| this.stat_bdy.append(`<tr><td>${stat.id}</td><td>${stat.min}</td><td>${stat.max}</td></tr>`);
| |
| }
| |
|
| |
| // Done, remove the calculating notice
| |
| $('.monster_container').find('.info_header').css('display', 'none');
| |
| }
| |
| }
| |
| | |
| function fmt_number(digits, format) {
| |
| if (typeof digits === 'undefined') {
| |
| digits = 0;
| |
| }
| |
|
| |
| if (typeof format === 'undefined') {
| |
| format = '{1}';
| |
| }
| |
|
| |
| return function(value) {
| |
| value = value.toFixed(digits);
| |
| return format.replace(/\{1\}/, value);
| |
| }
| |
| }
| |
| | |
| Monster.level_data = [];
| |
| Monster.rarity_data = {
| |
| // Normal shouldn't have any stats associated with it.
| |
| Normal: {},
| |
| Magic: {},
| |
| Rare: {},
| |
| Unique: {},
| |
| };
| |
| Monster.mods = {};
| |
| Monster.base_data = {};
| |
| Monster.base_data_by_id = {};
| |
| | |
| // Map these stats back for calcuation purposes
| |
| Monster.stat_map = {
| |
| ['map_life_multiplier']: 'map_hidden_monster_life_+%_final',
| |
| ['map_damage_multiplier']: 'map_hidden_monster_damage_+%_final',
| |
| ['boss_life']: 'map_hidden_monster_life_+%_final',
| |
| ['boss_damage']: 'map_hidden_monster_damage_+%_final',
| |
| ['boss_item_quantity']: 'monster_dropped_item_quantity_+%',
| |
| ['boss_item_rarity']: 'monster_dropped_item_rarity_+%',
| |
| // I believe these are the ones used though not at the same time
| |
| ['Magic_life_multiplier']: 'monster_life_+%_final_from_rarity_table',
| |
| ['Rare_life_multiplier']: 'monster_life_+%_final_from_rarity_table',
| |
| };
| |
| | |
| // List of things affected by each stat to calculate a final value
| |
| Monster.calculation_params = {
| |
| damage: {
| |
| type: 'level',
| |
| base: 'monsters.damage_multiplier',
| |
| increased: [
| |
| 'damage_+%',
| |
| 'map_monsters_damage_+%',
| |
| 'map_boss_damage_+%',
| |
| ],
| |
| more: [
| |
| 'damage_+%_final',
| |
| 'monster_rarity_damage_+%_final',
| |
| 'map_hidden_monster_damage_+%_final',
| |
| //'map_hidden_monster_damage_+%_squared_final',
| |
| 'monster_damage_+%_final_from_watchstone',
| |
| ],
| |
| less: [
| |
| 'monster_rarity_attack_cast_speed_+%_and_damage_-%_final',
| |
| 'monster_base_type_attack_cast_speed_+%_and_damage_-%_final',
| |
| ],
| |
| fmt: fmt_number(0),
| |
| },
| |
| // map_boss_life_+permyriad_final_from_awakening_level
| |
| life: {
| |
| type: 'level',
| |
| base: 'monsters.health_multiplier',
| |
| added: [
| |
| 'base_maximum_life',
| |
| ],
| |
| increased: [
| |
| 'maximum_life_+%',
| |
| 'map_monsters_life_+%',
| |
| 'map_boss_maximum_life_+%',
| |
| ],
| |
| more: [
| |
| 'maximum_life_+%_final',
| |
| 'map_hidden_monster_life_+%_final',
| |
| //'map_hidden_monster_life_+%_times_6_final',
| |
| 'monster_life_+%_final_from_map',
| |
| 'monster_life_+%_final_from_rarity',
| |
| 'monster_life_+%_final_from_rarity_table',
| |
| 'monster_life_+%_final_from_watchstone',
| |
| ],
| |
| fmt: fmt_number(0),
| |
| },
| |
| summon_life: {
| |
| type: 'level',
| |
| base: 'monsters.health_multiplier',
| |
| added: [
| |
| 'base_maximum_life',
| |
| ],
| |
| increased: [
| |
| 'maximum_life_+%',
| |
| ],
| |
| more: [
| |
| 'maximum_life_+%_final',
| |
| ],
| |
| fmt: fmt_number(0),
| |
| },
| |
| evasion: {
| |
| type: 'level',
| |
| fmt: fmt_number(0),
| |
| },
| |
| accuracy: {
| |
| type: 'level',
| |
| increased: [
| |
| 'map_monsters_accuracy_rating_+%',
| |
| ],
| |
| fmt: fmt_number(0),
| |
| },
| |
| armour: {
| |
| type: 'level',
| |
| fmt: fmt_number(0),
| |
| },
| |
| experience: {
| |
| type: 'level',
| |
| base: 'monsters.experience_multiplier',
| |
| increased: [
| |
| 'map_hidden_experience_gain_+%',
| |
| ],
| |
| override: {
| |
| ['map_monsters_no_drops_or_experience']: 0,
| |
| },
| |
| fmt: fmt_number(0),
| |
| },
| |
| attack_speed: {
| |
| type: 'base',
| |
| base: 'monsters.attack_speed',
| |
| increased: [
| |
| 'map_monsters_attack_speed_+%',
| |
| 'map_boss_attack_and_cast_speed_+%',
| |
| ],
| |
| more: [
| |
| 'monster_rarity_attack_cast_speed_+%_and_damage_-%_final',
| |
| 'monster_base_type_attack_cast_speed_+%_and_damage_-%_final',
| |
| ],
| |
| fmt: fmt_number(2),
| |
| },
| |
| rarity: {
| |
| type: 'none',
| |
| added : [
| |
| 'monster_dropped_item_rarity_+%',
| |
| ],
| |
| //monster_dropped_item_quantity_+%_from_player_support
| |
| more: [
| |
| // These two should be multiplicative at least with each other as per GGG
| |
| 'map_item_drop_rarity_+%',
| |
| ],
| |
| fmt: fmt_number(0, '{1} %'),
| |
| },
| |
| quantity: {
| |
| type: 'none',
| |
| added: [
| |
| 'monster_dropped_item_quantity_+%',
| |
| ],
| |
| // monster_dropped_item_quantity_+%_from_player_support
| |
| more: [
| |
| // These two should be multiplicative at least with each other as per GGG
| |
| 'map_item_drop_quantity_+%',
| |
| 'monster_dropped_item_quantity_from_numplayers_+%',
| |
| ],
| |
| override: {
| |
| 'map_monsters_no_drops_or_experience': 0,
| |
| },
| |
| fmt: fmt_number(0, '{1} %'),
| |
| },
| |
| lightning_resistance: {
| |
| type: 'resist',
| |
| added: [
| |
| 'map_monsters_additional_lightning_resistance',
| |
| ],
| |
| fmt: fmt_number(0, '{1} %'),
| |
| },
| |
| cold_resistance: {
| |
| type: 'resist',
| |
| added: [
| |
| 'map_monsters_additional_cold_resistance',
| |
| ],
| |
| fmt: fmt_number(0, '{1} %'),
| |
| },
| |
| fire_resistance: {
| |
| type: 'resist',
| |
| added: [
| |
| 'map_monsters_additional_fire_resistance',
| |
| ],
| |
| fmt: fmt_number(0, '{1} %'),
| |
| },
| |
| chaos_resistance: {
| |
| type: 'resist',
| |
| added: [
| |
| 'map_monsters_additional_chaos_resistance',
| |
| ],
| |
| fmt: fmt_number(0, '{1} %'),
| |
| },
| |
| critical_strike_chance: {
| |
| type: 'base',
| |
| base: 'monsters.critical_strike_chance',
| |
| increased: [
| |
| 'map_monsters_critical_strike_chance_+%',
| |
| ],
| |
| fmt: fmt_number(2, '{1} %'),
| |
| },
| |
| critical_strike_multiplier: {
| |
| type: 'none',
| |
| value: 130,
| |
| base: 'monsters.critical_strike_multiplier',
| |
| added: [
| |
| 'map_monsters_critical_strike_multiplier_+',
| |
| ],
| |
| fmt: fmt_number(2, '{1} %'),
| |
| },
| |
| }
| |
| | |
| | |
| function _run_final_init() {
| |
| init_level = init_level + 1;
| |
| // Prevents from being run until all the cargo data is asyncronously loaded
| |
| if (init_level >= init_max) {
| |
| monster_finalize_init();
| |
| }
| |
| }
| |
| | |
| function monster_init() {
| |
| Cargo.query({
| |
| tables: ['monster_base_stats', 'monster_life_scaling', 'monster_map_multipliers'],
| |
| fields: [
| |
| 'accuracy',
| |
| 'armour',
| |
| 'monster_base_stats.damage=damage',
| |
| 'evasion',
| |
| 'experience',
| |
| 'monster_base_stats.level=level',
| |
| 'monster_base_stats.life=life',
| |
| 'summon_life',
| |
| 'magic=Magic_life_multiplier',
| |
| 'rare=Rare_life_multiplier',
| |
| 'monster_map_multipliers.life=map_life_multiplier',
| |
| 'monster_map_multipliers.damage=map_damage_multiplier',
| |
| 'boss_damage',
| |
| 'boss_item_quantity',
| |
| 'boss_item_rarity',
| |
| 'boss_life',
| |
| ],
| |
| join_on: 'monster_base_stats.level = monster_life_scaling.level, monster_base_stats.level=monster_map_multipliers.level',
| |
| }, function (data) {
| |
| data.forEach(function (value) {
| |
| var v = {};
| |
| for (const [field, field_value] of Object.entries(value.title)) {
| |
| v[field] = Number(field_value);
| |
| }
| |
| Monster.level_data[v.level] = v;
| |
| });
| |
| _run_final_init();
| |
| });
| |
|
| |
| Cargo.query({
| |
| tables: ['monsters', 'monster_types', 'monster_resistances'],
| |
| fields: [
| |
| //
| |
| // Monster data
| |
| //
| |
| 'monsters._pageName',
| |
|
| |
| 'monsters.attack_speed',
| |
| 'monsters.critical_strike_chance',
| |
| 'monsters.damage_multiplier',
| |
| 'monsters.experience_multiplier',
| |
| 'monsters.health_multiplier',
| |
|
| |
| //'monsters.minimum_attack_distance',
| |
| //'monsters.maximum_attack_distance',
| |
| //'monsters.skill_ids',
| |
| //'monsters.size',
| |
|
| |
| 'monsters.part1_mod_ids',
| |
| 'monsters.part2_mod_ids',
| |
| 'monsters.mod_ids',
| |
| 'monsters.endgame_mod_ids',
| |
|
| |
| 'monsters.metadata_id',
| |
| 'monsters.name',
| |
| // Apprently strips the table if I don't do this
| |
| 'monsters.tags__full=monsters.tags',
| |
| 'monsters.skill_ids',
| |
| 'monsters.minimum_attack_distance',
| |
| 'monsters.maximum_attack_distance',
| |
| 'monsters.size',
| |
|
| |
| //
| |
| // Monster type data
| |
| //
| |
|
| |
| // Apprently strips the table if I don't do this
| |
| 'monster_types.tags__full=monster_types.tags',
| |
| // This is kinda unconfirmed, want to leave it out for the moment
| |
| // 'monster_types.armour_multiplier',
| |
| // 'monster_types.damage_spread',
| |
| // 'monster_types.energy_shield_multiplier',
| |
| // 'monster_types.evasion_multiplier',
| |
|
| |
| //
| |
| // Monster resistance data
| |
| //
| |
| 'monster_resistances.part1_fire',
| |
| 'monster_resistances.part1_cold',
| |
| 'monster_resistances.part1_lightning',
| |
| 'monster_resistances.part1_chaos',
| |
| 'monster_resistances.part2_fire',
| |
| 'monster_resistances.part2_cold',
| |
| 'monster_resistances.part2_lightning',
| |
| 'monster_resistances.part2_chaos',
| |
| // Should rename these fields for consistency -.-
| |
| 'monster_resistances.maps_fire=monster_resistances.endgame_fire',
| |
| 'monster_resistances.maps_cold=monster_resistances.endgame_cold',
| |
| 'monster_resistances.maps_lightning=monster_resistances.endgame_lightning',
| |
| 'monster_resistances.maps_chaos=monster_resistances.endgame_chaos',
| |
| ],
| |
| join_on: 'monsters.monster_type_id=monster_types.id, monster_types.monster_resistance_id=monster_resistances.id',
| |
| //where: 'monsters.name LIKE "%Izaro%"',
| |
| //limit: 2,
| |
| //where: 'monsters.metadata_id="Metadata/Monsters/Atziri/Atziri"',
| |
| where: `<!--{$where}-->`,
| |
| }, function (data) {
| |
| if (data.length == 0) {
| |
| $('.monster_container').find('.info_header').text(i18n.missing_monster);
| |
|
| |
| return;
| |
| }
| |
| var query_mods = {};
| |
|
| |
| for (const [index, entry] of Object.entries(data)) {
| |
| var curdata = entry.title;
| |
|
| |
| // Need these as numbers to calcuate values later on
| |
| [
| |
| 'monsters.attack_speed',
| |
| 'monsters.critical_strike_chance',
| |
| 'monsters.damage_multiplier',
| |
| 'monsters.experience_multiplier',
| |
| 'monsters.health_multiplier',
| |
| 'monster_resistances.part1_fire',
| |
| 'monster_resistances.part1_cold',
| |
| 'monster_resistances.part1_lightning',
| |
| 'monster_resistances.part1_chaos',
| |
| 'monster_resistances.part2_fire',
| |
| 'monster_resistances.part2_cold',
| |
| 'monster_resistances.part2_lightning',
| |
| 'monster_resistances.part2_chaos',
| |
| // Should rename these fields for consistency -.-
| |
| 'monster_resistances.endgame_fire',
| |
| 'monster_resistances.endgame_cold',
| |
| 'monster_resistances.endgame_lightning',
| |
| 'monster_resistances.endgame_chaos',
| |
| ].forEach(function (value) {
| |
| curdata[value] = Number(curdata[value]);
| |
| });
| |
| // Needed as lists
| |
| [
| |
| 'monsters.part1_mod_ids',
| |
| 'monsters.part2_mod_ids',
| |
| 'monsters.mod_ids',
| |
| 'monsters.endgame_mod_ids',
| |
| 'monsters.tags',
| |
| 'monsters.skill_ids',
| |
| 'monster_types.tags',
| |
| ].forEach(function (key) {
| |
| var value = curdata[key];
| |
| if (value == "") {
| |
| value = [];
| |
| } else {
| |
| value = value.split(',');
| |
| }
| |
|
| |
| curdata[key] = value;
| |
| });
| |
|
| |
| Monster.base_data[index] = entry.title;
| |
| Monster.base_data_by_id[entry.title['monsters.metadata_id']] = entry.title;
| |
|
| |
| //
| |
| // Schedule mods for querying
| |
| //
| |
| [
| |
| 'monsters.part1_mod_ids',
| |
| 'monsters.part2_mod_ids',
| |
| 'monsters.mod_ids',
| |
| 'monsters.endgame_mod_ids',
| |
| ].forEach(function (field) {
| |
| curdata[field].forEach(function (mod_id) {
| |
| query_mods[mod_id] = true;
| |
| });
| |
| });
| |
| }
| |
|
| |
| var query_mods_array = [];
| |
| for (const [mod_id, a] of Object.entries(query_mods)) {
| |
| query_mods_array.push(mod_id);
| |
| }
| |
| // avoids query errors due to empty IN clause
| |
| if (query_mods_array.length > 500) {
| |
| //TODO
| |
| alert('FIXME: Over 500 mods');
| |
| } else if (query_mods_array.length > 0) {
| |
| query_mods = query_mods_array.join('", "');
| |
|
| |
| Cargo.query({
| |
| tables: ['mods', 'mod_stats'],
| |
| fields: [
| |
| 'mods.id=mod_id',
| |
| 'mods.stat_text=mods.stat_text',
| |
| 'mod_stats.id=stat_id',
| |
| 'mod_stats.min',
| |
| 'mod_stats.max',
| |
| ],
| |
| join_on: 'mods._pageID=mod_stats._pageID',
| |
| where: `
| |
| mods.id IN ("${query_mods}") OR (
| |
| mods.generation_type = 3
| |
| AND mods.domain = 3
| |
| AND mods.id REGEXP "Monster(Magic|Rare|Unique)[0-9]*$"
| |
| )`
| |
| }, function (data) {
| |
| data.forEach(function (value) {
| |
| var v = value.title;
| |
| var stat = new Stat(v.stat_id, Number(v['mod_stats.min']), Number(v['mod_stats.max']));
| |
| var rarity = v.mod_id.match(/Monster(Magic|Rare|Unique)[0-9]*$/);
| |
| if (rarity == null) {
| |
| if (typeof Monster.mods[v.mod_id] === 'undefined') {
| |
| Monster.mods[v.mod_id] = {
| |
| 'stat_text': v['mods.stat_text'],
| |
| 'stats': [],
| |
| };
| |
| }
| |
| Monster.mods[v.mod_id].stats.push(stat);
| |
| } else {
| |
| Monster.rarity_data[rarity[1]][v.stat_id] = stat;
| |
| }
| |
| });
| |
|
| |
| _run_final_init();
| |
| });
| |
| } else {
| |
| // need to increment in any case
| |
| _run_final_init();
| |
| }
| |
|
| |
| _run_final_init();
| |
| }, function(jqXHR, textStatus, errorThrown) {
| |
| console.log(textStatus);
| |
| // since JS doesn't actually show the DB error the query is duplicated in the template. The error will be shown there.
| |
| $('#monster_query_error').css('display', 'initial');
| |
| $('.monster_container').find('.info_header').html(i18n.query_error);
| |
| });
| |
| }
| |
| | |
| var m = {};
| |
| | |
| function monster_finalize_init() {
| |
| for (const [metadata_id, data] of Object.entries(Monster.base_data_by_id)) {
| |
| m[metadata_id] = new Monster(metadata_id);
| |
| }
| |
|
| |
| $('.monster_container').find('.info_header').css('display', 'none');
| |
| }
| |
| | |
| | |
| | |
| //
| |
| // Test functions
| |
| //
| |
| | |
| function test_stat() {
| |
| a = new Stat('id', 1, 1);
| |
| b = new Stat('id', 5, 5);
| |
| a.add(b);
| |
| console.log('+ 6?', a.min, a.max);
| |
| a.sub(b);
| |
| console.log('- 1?', a.min, a.max);
| |
| a.mult(b);
| |
| console.log('* 5?', a.min, a.max);
| |
| a.div(b);
| |
| console.log('/ 1?', a.min, a.max);
| |
| a.add(10);
| |
| console.log('+ 11?', a.min, a.max);
| |
| a.sub(10);
| |
| console.log('- 1?', a.min, a.max);
| |
| a.mult(10);
| |
| console.log('* 10?', a.min, a.max);
| |
| a.div(10);
| |
| console.log('/ 1?', a.min, a.max);
| |
| }
| |
| //test_stat();
| |
| | |
| | |
| //monster_init();
| |
| window.addEventListener('load', function() {
| |
| setTimeout(monster_init, 1000);
| |
| });
| |
| }</script>
| |