7th-saga/web/static/saga.js
2021-02-21 20:37:37 -08:00

464 lines
12 KiB
JavaScript

(function(window) {
const $apprenticeFilterForm = $('.apprentice-filter-form');
const $locationFilterForm = $('.location-filter-form');
const $table = $('#main-table');
let charStats = {
power: null,
guard: null,
magic: null,
speed: null,
weapon: null,
armor: null,
accessory: null,
};
const onCharStatChange = (readFromCookie) => {
if (!readFromCookie) {
window.Cookies.set('charStats', JSON.stringify(charStats), {
sameSite: 'strict',
});
} else {
}
};
window.saga = {
sortData: () => {
const qs = new URLSearchParams(window.location.search);
const col = qs.get('col');
let dir = qs.get('dir');
if (!col) {
return;
}
if (!$table.length) {
return;
}
if (dir !== 'asc' && dir !== 'desc') {
dir = 'asc';
}
const rows = $table.find('tbody.data tr').toArray();
const firstRow = rows[0];
if (!firstRow) {
return;
}
const coefficient = dir === 'desc' ? -1 : 1;
rows.sort((a, b) => {
let aVal = a.getAttribute('data-' + col);
let bVal = b.getAttribute('data-' + col);
const isNumber = !isNaN(Number(aVal));
if (isNumber && aVal) {
aVal = Number(aVal);
bVal = Number(bVal);
}
if (aVal === bVal) {
if (col === 'name') {
return 0;
}
const aName = a.getAttribute('data-name');
const bName = b.getAttribute('data-name');
if (typeof(aName) === 'string' && typeof(bName) === 'string') {
return aName.localeCompare(bName) * coefficient;
}
return 0;
}
if (typeof(aVal) === 'number' && typeof(bVal) === 'number') {
return (aVal < bVal ? -1 : 1) * coefficient;
}
if (typeof(aVal) === 'string' && typeof(bVal) === 'string') {
return aVal.localeCompare(bVal) * coefficient;
}
return 0;
});
let nextChild = null;
const headers = $table.find('tr.header th').removeClass('sorted').toArray();
const highlightedIndex = headers.findIndex(cell => cell.getAttribute('data-col') === col);
if (highlightedIndex === -1) {
throw new Error('could not find header column');
}
headers[highlightedIndex].classList.add('sorted');
for (let i = rows.length - 1; i >= 0; i--) {
const row = rows[i];
if (row === nextChild) {
continue;
}
$(row).find('td.sorted').removeClass('sorted');
$(row).find('td').eq(highlightedIndex).addClass('sorted');
row.parentNode.insertBefore(row, nextChild);
nextChild = row;
}
},
filterApprentices: () => {
if (!$apprenticeFilterForm.length) {
return;
}
const checked = [];
$apprenticeFilterForm.find('input[type="checkbox"]').toArray().map((input) => {
if (input.checked) {
checked.push(input.name);
}
});
window.Cookies.set('apprenticeFilter', checked.join(','), {
sameSite: 'strict',
});
$table
.find('tbody.data tr')
.hide()
.filter((i, row) => {
if (!checked.length) {
return true;
}
const users = ($(row).attr('data-users') || '').split(',');
if (!users.length) {
return true;
}
return checked.some((name) => users.includes(name));
})
.show();
},
filterLocations: () => {
if (!$locationFilterForm.length) {
return;
}
const checked = [];
$locationFilterForm.find('input[type="checkbox"]').toArray().map((input) => {
if (input.checked) {
checked.push(input.name);
}
});
window.Cookies.set('locationFilter', checked.join(','), {
sameSite: 'strict',
});
$table
.find('tbody.data tr')
.hide()
.filter((i, row) => {
if (!checked.length) {
return true;
}
const data = $(row).attr('data-locations').split(',');
if (!data.length) {
return true;
}
return data.some((name) => checked.includes(name));
})
.show();
},
updateCharStat: (stat, value) => {
charStats[stat] = value;
onCharStatChange();
},
updateCharWeapon: (name, power) => {
charStats.weapon = {
name,
power,
};
onCharStatChange();
},
updateCharArmor: (name, defense) => {
charStats.armor = {
name,
defense,
};
onCharStatChange();
},
updateCharAccessory: (name, defense, resistance) => {
charStats.accessory = {
name,
defense,
resistance,
};
onCharStatChange();
},
calc: {},
};
const checkLocations = () => {
if (!$locationFilterForm.length) {
return;
}
try {
window.Cookies.get('locationFilter').split(',').forEach((location) => {
$locationFilterForm.find(`input[type="checkbox"][name="${location}"]`).prop('checked', true);
});
} catch (e) {}
};
const checkApprentices = () => {
if (!$apprenticeFilterForm.length) {
return;
}
try {
window.Cookies.get('apprenticeFilter').split(',').forEach((apprentice) => {
$apprenticeFilterForm.find(`input[type="checkbox"][name="${apprentice}"]`).prop('checked', true);
});
} catch (e) {}
};
const checkCharStats = () => {
try {
const stats = JSON.parse(window.Cookies.get('charStats'));
if (stats.power !== null) {
$('#char-power').val(stats.power);
}
if (stats.guard !== null) {
$('#char-guard').val(stats.guard);
}
if (stats.magic !== null) {
$('#char-magic').val(stats.magic);
}
if (stats.speed !== null) {
$('#char-speed').val(stats.speed);
}
if (stats.weapon !== null) {
$('#char-weapon').val(stats.weapon.name);
}
if (stats.armor !== null) {
$('#char-armor').val(stats.armor.name);
}
if (stats.accessory !== null) {
$('#char-accessory').val(stats.accessory.name);
}
charStats = stats;
} catch (e) {}
};
const update = () => {
checkLocations();
checkApprentices();
checkCharStats();
window.saga.sortData();
window.saga.filterApprentices();
window.saga.filterLocations();
};
window.addEventListener('popstate', update);
$('.sortable-links a').click(function(e) {
e.preventDefault();
window.history.replaceState(null, '', $(this).attr('href'));
window.saga.sortData();
});
$apprenticeFilterForm.find('input[type="checkbox"]').on('change', window.saga.filterApprentices);
$locationFilterForm.find('input[type="checkbox"]').on('change', window.saga.filterLocations);
$('#char-stats-modal').find('input,select').on('change', (e) => {
const value = e.target.value;
const stat = e.target.id.replace(/^char-/, '');
const attrToNum = (name) => Number(e.target.querySelector(`option[value="${value}"]`).getAttribute('data-' + name));
switch (stat) {
case 'power':
case 'guard':
case 'magic':
case 'speed':
window.saga.updateCharStat(stat, Number(value));
break;
case 'weapon':
window.saga.updateCharWeapon(value, attrToNum('power'));
break;
case 'armor':
window.saga.updateCharArmor(value, attrToNum('defense'));
break;
case 'accessory':
window.saga.updateCharAccessory(
value,
attrToNum('defense'),
{
fire: attrToNum('res-fire'),
ice: attrToNum('res-ice'),
thunder: attrToNum('res-thunder'),
vacuum: attrToNum('res-vacuum'),
debuff: attrToNum('res-debuff'),
},
);
break;
}
});
update();
const $enemyInfoModal = $('#enemy-info-modal');
if ($enemyInfoModal.length) {
$table.find('tbody.data td').on('click', (e) => {
const rowData = $(e.target).closest('tr').data();
Object.keys(rowData).forEach((key) => {
const cls = key.replace(/[A-Z]/g, (c) => '-' + c.toLowerCase());
$enemyInfoModal.find('.' + cls).text(rowData[key]);
});
if (charStats.power === null && charStats.weapon === null) {
$enemyInfoModal.find('[class^="physical-"]').text('n/a');
} else {
const powerInnate = charStats.power || 0;
const powerWeapon = charStats.weapon ? charStats.weapon.power : 0;
const guardInnate = rowData.guard;
const guardArmor = 0;
const guardAccessory = 0;
const def = window.saga.calc.physicalAttack(
powerInnate,
powerWeapon,
guardInnate,
guardArmor,
guardAccessory,
);
const withGuard = window.saga.calc.physicalAttack(
powerInnate,
powerWeapon,
guardInnate,
guardArmor,
guardAccessory,
{
targetGuardUp: true,
}
);
const withPower = window.saga.calc.physicalAttack(
powerInnate,
powerWeapon,
guardInnate,
guardArmor,
guardAccessory,
{
attackerPowerUp: true,
}
);
const withPowerGuard = window.saga.calc.physicalAttack(
powerInnate,
powerWeapon,
guardInnate,
guardArmor,
guardAccessory,
{
attackerPowerUp: true,
targetGuardUp: true,
}
);
const dmgRange = (dmg) => `${Math.floor(dmg.normal.min)}-${Math.ceil(dmg.normal.max)}`;
$enemyInfoModal.find('.physical-dmg').text(dmgRange(def));
$enemyInfoModal.find('.physical-dmg-guard-up').text(dmgRange(withGuard));
$enemyInfoModal.find('.physical-dmg-power-up').text(dmgRange(withPower));
$enemyInfoModal.find('.physical-dmg-power-up-guard-up').text(dmgRange(withPowerGuard));
}
const magicDmg = (dmg) => `${Math.floor(dmg.min)}-${Math.ceil(dmg.max)}`;
const toPer = x => Math.round(x) + '%';
if (charStats.magic === null) {
$enemyInfoModal.find('[class^="magic-"]').text('n/a');
} else {
const attackerMagic = charStats.magic;
const targetMagic = rowData.magic;
const res = {
ice: rowData.resIce,
fire: rowData.resFire,
thunder: rowData.resThunder,
vacuum: rowData.resVacuum,
debuff: rowData.resDebuff,
};
const resArmor = 0;
const resAccessory = 0;
window.saga.spells.filter(x => !!x.power).forEach((spell) => {
const elementalRes = res[spell.element.toLowerCase()];
const def = window.saga.calc.magicalAttack(attackerMagic, targetMagic, elementalRes, resArmor, resAccessory, spell.power);
const magicUp = window.saga.calc.magicalAttack(attackerMagic, targetMagic, elementalRes, resArmor, resAccessory, spell.power, {
attackerMagicUp: true,
});
const prefix = '.magic-dmg-' + spell.name;
$enemyInfoModal.find(prefix).text(magicDmg(def));
$enemyInfoModal.find(prefix + '-magic-up').text(magicDmg(magicUp));
});
// hpcatcher
let def = window.saga.calc.hpCatcherAttack(charStats.magic, 999, 0, rowData.hp);
let magicUp = window.saga.calc.hpCatcherAttack(charStats.magic, 999, 0, rowData.hp, {
attackerMagicUp: true,
});
let prefix = '.magic-dmg-HPCatcher';
$enemyInfoModal.find(prefix).text(magicDmg(def));
$enemyInfoModal.find(prefix + '-magic-up').text(magicDmg(magicUp));
// mpcatcher
def = window.saga.calc.mpCatcherAttack(charStats.magic, 999, 0, rowData.mp);
magicUp = window.saga.calc.mpCatcherAttack(charStats.magic, 999, 0, rowData.mp, {
attackerMagicUp: true,
});
prefix = '.magic-dmg-MPCatcher';
$enemyInfoModal.find(prefix).text(magicDmg(def));
$enemyInfoModal.find(prefix + '-magic-up').text(magicDmg(magicUp));
}
if (charStats.speed !== null) {
const hitRate = window.saga.calc.hitRate(charStats.speed, rowData.speed);
const hitRateSpeed = window.saga.calc.hitRate(charStats.speed, rowData.speed, {
attackerSpeedUp: true,
});
const runRate = window.saga.calc.runRate(charStats.speed, rowData.speed);
const runRateSpeed = window.saga.calc.runRate(charStats.speed, rowData.speed, {
attackerSpeedUp: true,
});
$enemyInfoModal.find('.run-rate').text(toPer(runRate));
$enemyInfoModal.find('.run-rate-speed-up').text(toPer(runRateSpeed));
$enemyInfoModal.find('.hit-rate').text(toPer(hitRate));
$enemyInfoModal.find('.hit-rate-speed-up').text(toPer(hitRateSpeed));
}
const debuffRate = window.saga.calc.effectSpellHitRate(rowData.resDebuff === 100 ? null : rowData.resDebuff, 0, 0);
const vacuumRate = window.saga.calc.effectSpellHitRate(rowData.resVacuum === 100 ? null : rowData.resVacuum, 0, 0);
$enemyInfoModal.find('.debuff-rate').text(toPer(debuffRate));
$enemyInfoModal.find('.vacuum-rate').text(toPer(vacuumRate));
$enemyInfoModal.modal({
show: true,
});
});
} else {
}
}(window));