calculation stuff in ui
This commit is contained in:
parent
6b40ec0a11
commit
5fe8b2f773
181
data/calc.js
181
data/calc.js
@ -1,181 +0,0 @@
|
||||
// https://gamefaqs.gamespot.com/snes/563501-the-7th-saga/faqs/54038
|
||||
|
||||
exports.physicalAttack = (
|
||||
attackerPowerInnate,
|
||||
attackerPowerWeapon,
|
||||
targetGuardInnate,
|
||||
targetGuardArmor,
|
||||
targetGuardAccessory,
|
||||
options = {},
|
||||
) => {
|
||||
let attackPower = attackerPowerInnate;
|
||||
if (options.attackerDefending) {
|
||||
attackPower *= 1.5;
|
||||
}
|
||||
if (options.attackerPowerUp) {
|
||||
attackPower *= 2;
|
||||
}
|
||||
|
||||
let weaponPower = attackerPowerWeapon;
|
||||
if (options.attackerDefending) {
|
||||
weaponPower *= 1.5;
|
||||
}
|
||||
|
||||
let guardPower = targetGuardInnate;
|
||||
if (options.targetGuardDown) {
|
||||
guardPower /= 2;
|
||||
}
|
||||
|
||||
const totalAttackPower = attackPower + weaponPower;
|
||||
const totalGuardPower = guardPower + targetGuardArmor + targetGuardAccessory;
|
||||
|
||||
let averageDamage = totalAttackPower - (totalGuardPower / 2);
|
||||
|
||||
if (options.targetGuardUp) {
|
||||
averageDamage /= 2;
|
||||
}
|
||||
if (options.targetDefending) {
|
||||
averageDamage /= 2;
|
||||
}
|
||||
|
||||
let minDamage = averageDamage * 0.75;
|
||||
let maxDamage = averageDamage * 1.25;
|
||||
|
||||
const absoluteMin = 1;
|
||||
|
||||
return {
|
||||
normal: {
|
||||
avg: Math.max(absoluteMin, averageDamage),
|
||||
min: Math.max(absoluteMin, minDamage),
|
||||
max: Math.max(absoluteMin, maxDamage),
|
||||
},
|
||||
critical: {
|
||||
avg: Math.max(absoluteMin, averageDamage * 2),
|
||||
min: Math.max(absoluteMin, minDamage * 2),
|
||||
max: Math.max(absoluteMin, maxDamage * 2),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
exports.magicalAttack = (
|
||||
attackerMagicInnate,
|
||||
targetMagicInnate,
|
||||
targetResistanceInnate,
|
||||
targetResistanceArmor,
|
||||
targetResistanceAccessory,
|
||||
spellPower,
|
||||
options = {},
|
||||
) => {
|
||||
let attackerPower = attackerMagicInnate;
|
||||
|
||||
let targetPower = targetMagicInnate;
|
||||
if (options.targetMagicUp) {
|
||||
targetPower += 40;
|
||||
}
|
||||
|
||||
const targetResistance = 100 - targetResistanceInnate - targetResistanceArmor - targetResistanceAccessory;
|
||||
|
||||
const spellBonusAttackPower = attackerPower + (options.attackerMagicUp ? 40 : 0);
|
||||
const spellBonus = ((spellBonusAttackPower / 2) + spellPower) * (targetResistance / 100);
|
||||
|
||||
const averageDamage = attackerPower - targetPower + spellBonus;
|
||||
|
||||
return {
|
||||
avg: Math.max(1, averageDamage),
|
||||
min: Math.max(1, averageDamage * 0.75),
|
||||
max: Math.max(1, averageDamage * 1.25),
|
||||
};
|
||||
};
|
||||
|
||||
exports.hpCatcherAttack = (
|
||||
attackerMagicInnate,
|
||||
attackerMaxHP,
|
||||
attackerCurrentHP,
|
||||
targetCurrentHP,
|
||||
options = {},
|
||||
) => {
|
||||
let attackerPower = attackerMagicInnate;
|
||||
if (options.attackerMagicUp) {
|
||||
attackerPower += 40;
|
||||
}
|
||||
|
||||
const adjust = dmg => Math.min(attackerMaxHP - attackerCurrentHP, Math.min(Math.min(dmg, 50), targetCurrentHP));
|
||||
const minDamage = attackerPower / 2;
|
||||
|
||||
return {
|
||||
min: adjust(minDamage),
|
||||
max: adjust(minDamage + 15),
|
||||
};
|
||||
};
|
||||
|
||||
exports.mpCatcherAttack = (
|
||||
attackerMagicInnate,
|
||||
attackerMaxMP,
|
||||
attackerCurrentMP,
|
||||
targetCurrentMP,
|
||||
options = {},
|
||||
) => {
|
||||
let attackerPower = attackerMagicInnate;
|
||||
if (options.attackerMagicUp) {
|
||||
attackerPower += 40;
|
||||
}
|
||||
|
||||
const adjust = dmg => Math.min(attackerMaxMP - attackerCurrentMP, Math.min(Math.min(dmg, 40), targetCurrentMP));
|
||||
const minDamage = attackerPower / 2;
|
||||
|
||||
return {
|
||||
min: adjust(minDamage),
|
||||
max: adjust(minDamage + 15),
|
||||
};
|
||||
};
|
||||
|
||||
exports.effectSpellHitRate = (
|
||||
targetResistanceInnate,
|
||||
targetResistanceArmor,
|
||||
targetResistanceAccessory,
|
||||
) => {
|
||||
if (targetResistanceInnate === null) {
|
||||
// e.g. "Class 1" enemies
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Math.max(5, 100 - targetResistanceInnate - targetResistanceArmor - targetResistanceAccessory);
|
||||
};
|
||||
|
||||
exports.hitRate = (
|
||||
attackerSpeedInnate,
|
||||
targetSpeedInnate,
|
||||
options = {},
|
||||
) => {
|
||||
let attackerSpeed = attackerSpeedInnate;
|
||||
if (options.attackerSpeedUp) {
|
||||
attackerSpeed += 30;
|
||||
}
|
||||
|
||||
let targetSpeed = targetSpeedInnate;
|
||||
if (options.targetSpeedUp) {
|
||||
targetSpeed += 30;
|
||||
}
|
||||
|
||||
const hitRate = 85 + (0.8 * (attackerSpeed - targetSpeed));
|
||||
return Math.max(10, Math.min(98, hitRate));
|
||||
};
|
||||
|
||||
exports.runRate = (
|
||||
attackerSpeedInnate,
|
||||
targetSpeedInnate,
|
||||
options = {},
|
||||
) => {
|
||||
let attackerSpeed = attackerSpeedInnate;
|
||||
if (options.attackerSpeedUp) {
|
||||
attackerSpeed += 30;
|
||||
}
|
||||
|
||||
let targetSpeed = targetSpeedInnate;
|
||||
if (options.targetSpeedUp) {
|
||||
targetSpeed += 30;
|
||||
}
|
||||
|
||||
const runRate = 0.25 + (1.6 * (attackerSpeed - targetSpeed));
|
||||
return Math.max(0.1, Math.min(0.8, runRate));
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const {spells} = require('./spells');
|
||||
const {spells} = require('../web/static/spells');
|
||||
|
||||
const spellMap = spells.reduce((map, spell) => {
|
||||
map[spell.name] = spell;
|
||||
|
@ -1,82 +0,0 @@
|
||||
const attackSpell = (name, mp, power, element, multiple) => {
|
||||
return {
|
||||
type: 'Attack',
|
||||
name,
|
||||
mp,
|
||||
power,
|
||||
element,
|
||||
targets: multiple ? 'multi' : 'single',
|
||||
};
|
||||
};
|
||||
|
||||
const effectSpell = (name, mp, element, effect, multiple) => {
|
||||
return {
|
||||
type: 'Effect',
|
||||
name,
|
||||
mp,
|
||||
element,
|
||||
effect,
|
||||
targets: multiple ? 'multi' : 'single',
|
||||
};
|
||||
};
|
||||
|
||||
const healSpell = (name, mp, healingPower, effect) => {
|
||||
return {
|
||||
type: 'Healing',
|
||||
name,
|
||||
mp,
|
||||
healingPower,
|
||||
effect,
|
||||
targets: 'single',
|
||||
};
|
||||
};
|
||||
|
||||
const supportSpell = (name, mp, effect, locations) => {
|
||||
return {
|
||||
type: 'Support',
|
||||
name,
|
||||
mp,
|
||||
effect,
|
||||
locations,
|
||||
targets: 'single',
|
||||
};
|
||||
};
|
||||
|
||||
exports.spells = [
|
||||
attackSpell('Fire1', 3, 30, 'Fire'),
|
||||
attackSpell('Fire2', 12, 70, 'Fire'),
|
||||
attackSpell('Ice1', 3, 36, 'Ice'),
|
||||
attackSpell('Ice2', 12, 80, 'Ice'),
|
||||
attackSpell('Laser1', 3, 30, 'Thunder'),
|
||||
attackSpell('Laser2', 10, 50, 'Thunder'),
|
||||
attackSpell('Laser3', 20, 100, 'Thunder'),
|
||||
attackSpell('Firebird', 14, 30, 'Fire', true),
|
||||
attackSpell('Fireball', 32, 50, 'Fire', true),
|
||||
attackSpell('Blizzard1', 14, 34, 'Ice', true),
|
||||
attackSpell('Blizzard2', 32, 60, 'Ice', true),
|
||||
attackSpell('Thunder1', 10, 40, 'Thunder', true),
|
||||
attackSpell('Thunder2', 40, 75, 'Thunder', true),
|
||||
|
||||
effectSpell('Petrify', 10, 'Debuff', 'Petrification'),
|
||||
effectSpell('Poison', 0, 'Debuff', 'Poison (monsters only)'),
|
||||
effectSpell('Defense2', 5, 'Debuff', 'Halves guard'),
|
||||
effectSpell('HPCatcher', 6, 'Debuff', 'Drains up to 50 HP and heals caster'),
|
||||
effectSpell('MPCatcher', 8, 'Debuff', 'Drains up to 40 MP and heals caster'),
|
||||
effectSpell('Vacuum1', 30, 'Vacuum', 'Instant death'),
|
||||
effectSpell('Vacuum2', 60, 'Vacuum', 'Instant death', true),
|
||||
|
||||
healSpell('Heal1', 4, 40, 'Restores 40 HP'),
|
||||
healSpell('Heal2', 18, 90, 'Restores 90 HP'),
|
||||
healSpell('Heal3', 34, 'max', 'Restores all HP'),
|
||||
healSpell('Elixir', 120, 'max', 'Restores all HP and MP'),
|
||||
healSpell('Revive1', 40, 'max', 'Restores all HP to dead ally, fails ~50% of the time'),
|
||||
healSpell('Revive2', 90, 'max', 'Restores all HP to dead ally, always succeeds'),
|
||||
|
||||
supportSpell('Purify', 8, 'Removes petrification and poison', [ 'Battle', 'Map' ]),
|
||||
supportSpell('Defense1', 5, 'Halves physical damage from enemy', [ 'Battle' ]),
|
||||
supportSpell('Power', 6, 'Doubles power', [ 'Battle' ]),
|
||||
supportSpell('Agility', 3, 'Increases Speed by 30', [ 'Battle' ]),
|
||||
supportSpell('F.Shield', 16, 'Nullifies next attack spell', [ 'Battle' ]),
|
||||
supportSpell('Protect', 20, 'Nullifies next Vacuum spell', [ 'Battle' ]),
|
||||
supportSpell('Exit', 30, 'Escape cave/dungeon immediately', [ 'Map' ]),
|
||||
];
|
@ -2,8 +2,7 @@ const express = require('express');
|
||||
const path = require('path');
|
||||
|
||||
const enemies = require('../data/enemies.json');
|
||||
const calc = require('../data/calc');
|
||||
const spells = require('../data/spells');
|
||||
const spells = require('./static/spells');
|
||||
const items = require('../data/items');
|
||||
const exp = require('../data/exp');
|
||||
|
||||
@ -15,29 +14,67 @@ app.set('cache views', false);
|
||||
|
||||
app.use(express.static(path.join(__dirname)));
|
||||
|
||||
const render = (res, view, params) => {
|
||||
res.render(view, {
|
||||
...params,
|
||||
charSpells: spells.spells.concat([]).sort((a, b) => {
|
||||
if (a.power && b.power) {
|
||||
return -1;
|
||||
}
|
||||
if (!a.power && b.power) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (a.power === b.power) {
|
||||
return a.name.localeCompare(b.name);
|
||||
}
|
||||
|
||||
return a.power < b.power ? -1 : 1;
|
||||
}),
|
||||
charWeapons: items.weapons.concat([]).sort((a, b) => {
|
||||
if (a.attack === b.attack) {
|
||||
return a.name.localeCompare(b.name);
|
||||
}
|
||||
return a.attack === b.attack ? 0 : (a.attack < b.attack ? -1 : 1);
|
||||
}),
|
||||
charArmor: items.armor.concat([]).sort((a, b) => {
|
||||
if (a.defense === b.defense) {
|
||||
return a.name.localeCompare(b.name);
|
||||
}
|
||||
return a.defense === b.defense ? 0 : (a.defense < b.defense ? -1 : 1);
|
||||
}),
|
||||
charAccessories: items.accessories.concat([]).sort((a, b) => {
|
||||
if (a.defense === b.defense) {
|
||||
return a.name.localeCompare(b.name);
|
||||
}
|
||||
return a.defense === b.defense ? 0 : (a.defense < b.defense ? -1 : 1);
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
app.get([ '/', '/enemies' ], (req, res) => {
|
||||
res.render('enemies', {
|
||||
render(res, 'enemies', {
|
||||
context: 'enemies',
|
||||
enemies,
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/spells', (req, res) => {
|
||||
res.render('spells', {
|
||||
render(res, 'spells', {
|
||||
context: 'spells',
|
||||
spells: spells.spells.sort((a, b) => a.name.localeCompare(b.name)),
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/exp', (req, res) => {
|
||||
res.render('exp', {
|
||||
render(res, 'exp', {
|
||||
context: 'exp',
|
||||
exp: exp.exp,
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/weapons', (req, res) => {
|
||||
res.render('weapons', {
|
||||
render(res, 'weapons', {
|
||||
context: 'weapons',
|
||||
weapons: items.weapons.sort((a, b) => {
|
||||
if (a.attack === b.attack) {
|
||||
@ -49,7 +86,7 @@ app.get('/weapons', (req, res) => {
|
||||
});
|
||||
|
||||
app.get('/armor', (req, res) => {
|
||||
res.render('armor', {
|
||||
render(res, 'armor', {
|
||||
context: 'armor',
|
||||
armor: items.armor.sort((a, b) => {
|
||||
if (a.defense === b.defense) {
|
||||
@ -61,14 +98,14 @@ app.get('/armor', (req, res) => {
|
||||
});
|
||||
|
||||
app.get('/accessories', (req, res) => {
|
||||
res.render('accessories', {
|
||||
render(res, 'accessories', {
|
||||
context: 'accessories',
|
||||
accessories: items.accessories,
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/items', (req, res) => {
|
||||
res.render('items', {
|
||||
render(res, 'items', {
|
||||
context: 'items',
|
||||
items: items.items,
|
||||
});
|
||||
|
@ -49,7 +49,7 @@ table.sticky-header {
|
||||
|
||||
table.sticky-header tr.header th {
|
||||
position: sticky;
|
||||
top: 37px;
|
||||
top: 44px;
|
||||
background-color: #fcfac8;
|
||||
z-index: 2;
|
||||
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.25);
|
||||
@ -58,3 +58,7 @@ table.sticky-header tr.header th {
|
||||
th.sorted, td.sorted {
|
||||
background-color: #dae5f6 !important;
|
||||
}
|
||||
|
||||
table.row-clickable tbody.data td {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
184
web/static/calc.js
Normal file
184
web/static/calc.js
Normal file
@ -0,0 +1,184 @@
|
||||
// https://gamefaqs.gamespot.com/snes/563501-the-7th-saga/faqs/54038
|
||||
|
||||
(function(exports) {
|
||||
exports.physicalAttack = (
|
||||
attackerPowerInnate,
|
||||
attackerPowerWeapon,
|
||||
targetGuardInnate,
|
||||
targetGuardArmor,
|
||||
targetGuardAccessory,
|
||||
options = {},
|
||||
) => {
|
||||
let attackPower = attackerPowerInnate;
|
||||
if (options.attackerDefending) {
|
||||
attackPower *= 1.5;
|
||||
}
|
||||
if (options.attackerPowerUp) {
|
||||
attackPower *= 2;
|
||||
}
|
||||
|
||||
let weaponPower = attackerPowerWeapon;
|
||||
if (options.attackerDefending) {
|
||||
weaponPower *= 1.5;
|
||||
}
|
||||
|
||||
let guardPower = targetGuardInnate;
|
||||
if (options.targetGuardDown) {
|
||||
guardPower /= 2;
|
||||
}
|
||||
|
||||
const totalAttackPower = attackPower + weaponPower;
|
||||
const totalGuardPower = guardPower + targetGuardArmor + targetGuardAccessory;
|
||||
|
||||
let averageDamage = totalAttackPower - (totalGuardPower / 2);
|
||||
|
||||
if (options.targetGuardUp) {
|
||||
averageDamage /= 2;
|
||||
}
|
||||
if (options.targetDefending) {
|
||||
averageDamage /= 2;
|
||||
}
|
||||
|
||||
let minDamage = averageDamage * 0.75;
|
||||
let maxDamage = averageDamage * 1.25;
|
||||
|
||||
const absoluteMin = 1;
|
||||
|
||||
return {
|
||||
normal: {
|
||||
avg: Math.max(absoluteMin, averageDamage),
|
||||
min: Math.max(absoluteMin, minDamage),
|
||||
max: Math.max(absoluteMin, maxDamage),
|
||||
},
|
||||
critical: {
|
||||
avg: Math.max(absoluteMin, averageDamage * 2),
|
||||
min: Math.max(absoluteMin, minDamage * 2),
|
||||
max: Math.max(absoluteMin, maxDamage * 2),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
exports.magicalAttack = (
|
||||
attackerMagicInnate,
|
||||
targetMagicInnate,
|
||||
targetResistanceInnate,
|
||||
targetResistanceArmor,
|
||||
targetResistanceAccessory,
|
||||
spellPower,
|
||||
options = {},
|
||||
) => {
|
||||
let attackerPower = attackerMagicInnate;
|
||||
|
||||
let targetPower = targetMagicInnate;
|
||||
if (options.targetMagicUp) {
|
||||
targetPower += 40;
|
||||
}
|
||||
|
||||
const targetResistance = 100 - targetResistanceInnate - targetResistanceArmor - targetResistanceAccessory;
|
||||
|
||||
const spellBonusAttackPower = attackerPower + (options.attackerMagicUp ? 40 : 0);
|
||||
const spellBonus = ((spellBonusAttackPower / 2) + spellPower) * (targetResistance / 100);
|
||||
|
||||
const averageDamage = attackerPower - targetPower + spellBonus;
|
||||
|
||||
return {
|
||||
avg: Math.max(1, averageDamage),
|
||||
min: Math.max(1, averageDamage * 0.75),
|
||||
max: Math.max(1, averageDamage * 1.25),
|
||||
};
|
||||
};
|
||||
|
||||
exports.hpCatcherAttack = (
|
||||
attackerMagicInnate,
|
||||
attackerMaxHP,
|
||||
attackerCurrentHP,
|
||||
targetCurrentHP,
|
||||
options = {},
|
||||
) => {
|
||||
let attackerPower = attackerMagicInnate;
|
||||
if (options.attackerMagicUp) {
|
||||
attackerPower += 40;
|
||||
}
|
||||
|
||||
const adjust = dmg => Math.min(attackerMaxHP - attackerCurrentHP, Math.min(Math.min(dmg, 50), targetCurrentHP));
|
||||
const minDamage = attackerPower / 2;
|
||||
|
||||
return {
|
||||
min: adjust(minDamage),
|
||||
max: adjust(minDamage + 15),
|
||||
};
|
||||
};
|
||||
|
||||
exports.mpCatcherAttack = (
|
||||
attackerMagicInnate,
|
||||
attackerMaxMP,
|
||||
attackerCurrentMP,
|
||||
targetCurrentMP,
|
||||
options = {},
|
||||
) => {
|
||||
let attackerPower = attackerMagicInnate;
|
||||
if (options.attackerMagicUp) {
|
||||
attackerPower += 40;
|
||||
}
|
||||
|
||||
const adjust = dmg => Math.min(attackerMaxMP - attackerCurrentMP, Math.min(Math.min(dmg, 40), targetCurrentMP));
|
||||
const minDamage = attackerPower / 2;
|
||||
|
||||
return {
|
||||
min: adjust(minDamage),
|
||||
max: adjust(minDamage + 15),
|
||||
};
|
||||
};
|
||||
|
||||
exports.effectSpellHitRate = (
|
||||
targetResistanceInnate,
|
||||
targetResistanceArmor,
|
||||
targetResistanceAccessory,
|
||||
) => {
|
||||
if (targetResistanceInnate === null) {
|
||||
// e.g. "Class 1" enemies
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Math.max(5, 100 - targetResistanceInnate - targetResistanceArmor - targetResistanceAccessory);
|
||||
};
|
||||
|
||||
exports.hitRate = (
|
||||
attackerSpeedInnate,
|
||||
targetSpeedInnate,
|
||||
options = {},
|
||||
) => {
|
||||
let attackerSpeed = attackerSpeedInnate;
|
||||
if (options.attackerSpeedUp) {
|
||||
attackerSpeed += 30;
|
||||
}
|
||||
|
||||
let targetSpeed = targetSpeedInnate;
|
||||
if (options.targetSpeedUp) {
|
||||
targetSpeed += 30;
|
||||
}
|
||||
|
||||
const hitRate = 85 + (0.8 * (attackerSpeed - targetSpeed));
|
||||
return Math.max(10, Math.min(98, hitRate));
|
||||
};
|
||||
|
||||
exports.runRate = (
|
||||
attackerSpeedInnate,
|
||||
targetSpeedInnate,
|
||||
options = {},
|
||||
) => {
|
||||
let attackerSpeed = attackerSpeedInnate;
|
||||
if (options.attackerSpeedUp) {
|
||||
attackerSpeed += 30;
|
||||
}
|
||||
|
||||
let targetSpeed = targetSpeedInnate;
|
||||
if (options.targetSpeedUp) {
|
||||
targetSpeed += 30;
|
||||
}
|
||||
|
||||
const runRate = 0.25 + (1.6 * (attackerSpeed - targetSpeed));
|
||||
return Math.max(10, Math.min(80, runRate));
|
||||
};
|
||||
|
||||
}(typeof(module) !== 'undefined' ? module.exports : window.saga.calc));
|
BIN
web/static/images/7th-saga-apprentices.png
Normal file
BIN
web/static/images/7th-saga-apprentices.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 41 KiB |
BIN
web/static/images/7th-saga-enemies.png
Normal file
BIN
web/static/images/7th-saga-enemies.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 628 KiB |
@ -3,9 +3,28 @@
|
||||
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: () => {
|
||||
console.log('sorting data');
|
||||
const qs = new URLSearchParams(window.location.search);
|
||||
const col = qs.get('col');
|
||||
let dir = qs.get('dir');
|
||||
@ -158,6 +177,33 @@
|
||||
})
|
||||
.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 = () => {
|
||||
@ -184,20 +230,44 @@
|
||||
} catch (e) {}
|
||||
};
|
||||
|
||||
checkLocations();
|
||||
checkApprentices();
|
||||
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) {}
|
||||
};
|
||||
|
||||
window.saga.sortData();
|
||||
window.saga.filterApprentices();
|
||||
window.saga.filterLocations();
|
||||
|
||||
window.addEventListener('popstate', () => {
|
||||
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();
|
||||
@ -207,4 +277,187 @@
|
||||
|
||||
$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));
|
||||
|
84
web/static/spells.js
Normal file
84
web/static/spells.js
Normal file
@ -0,0 +1,84 @@
|
||||
(function(exports) {
|
||||
const attackSpell = (name, mp, power, element, multiple) => {
|
||||
return {
|
||||
type: 'Attack',
|
||||
name,
|
||||
mp,
|
||||
power,
|
||||
element,
|
||||
targets: multiple ? 'multi' : 'single',
|
||||
};
|
||||
};
|
||||
|
||||
const effectSpell = (name, mp, element, effect, multiple) => {
|
||||
return {
|
||||
type: 'Effect',
|
||||
name,
|
||||
mp,
|
||||
element,
|
||||
effect,
|
||||
targets: multiple ? 'multi' : 'single',
|
||||
};
|
||||
};
|
||||
|
||||
const healSpell = (name, mp, healingPower, effect) => {
|
||||
return {
|
||||
type: 'Healing',
|
||||
name,
|
||||
mp,
|
||||
healingPower,
|
||||
effect,
|
||||
targets: 'single',
|
||||
};
|
||||
};
|
||||
|
||||
const supportSpell = (name, mp, effect, locations) => {
|
||||
return {
|
||||
type: 'Support',
|
||||
name,
|
||||
mp,
|
||||
effect,
|
||||
locations,
|
||||
targets: 'single',
|
||||
};
|
||||
};
|
||||
|
||||
exports.spells = [
|
||||
attackSpell('Fire1', 3, 30, 'Fire'),
|
||||
attackSpell('Fire2', 12, 70, 'Fire'),
|
||||
attackSpell('Ice1', 3, 36, 'Ice'),
|
||||
attackSpell('Ice2', 12, 80, 'Ice'),
|
||||
attackSpell('Laser1', 3, 30, 'Thunder'),
|
||||
attackSpell('Laser2', 10, 50, 'Thunder'),
|
||||
attackSpell('Laser3', 20, 100, 'Thunder'),
|
||||
attackSpell('Firebird', 14, 30, 'Fire', true),
|
||||
attackSpell('Fireball', 32, 50, 'Fire', true),
|
||||
attackSpell('Blizzard1', 14, 34, 'Ice', true),
|
||||
attackSpell('Blizzard2', 32, 60, 'Ice', true),
|
||||
attackSpell('Thunder1', 10, 40, 'Thunder', true),
|
||||
attackSpell('Thunder2', 40, 75, 'Thunder', true),
|
||||
|
||||
effectSpell('Petrify', 10, 'Debuff', 'Petrification'),
|
||||
effectSpell('Poison', 0, 'Debuff', 'Poison (monsters only)'),
|
||||
effectSpell('Defense2', 5, 'Debuff', 'Halves guard'),
|
||||
effectSpell('HPCatcher', 6, 'Debuff', 'Drains up to 50 HP and heals caster'),
|
||||
effectSpell('MPCatcher', 8, 'Debuff', 'Drains up to 40 MP and heals caster'),
|
||||
effectSpell('Vacuum1', 30, 'Vacuum', 'Instant death'),
|
||||
effectSpell('Vacuum2', 60, 'Vacuum', 'Instant death', true),
|
||||
|
||||
healSpell('Heal1', 4, 40, 'Restores 40 HP'),
|
||||
healSpell('Heal2', 18, 90, 'Restores 90 HP'),
|
||||
healSpell('Heal3', 34, 'max', 'Restores all HP'),
|
||||
healSpell('Elixir', 120, 'max', 'Restores all HP and MP'),
|
||||
healSpell('Revive1', 40, 'max', 'Restores all HP to dead ally, fails ~50% of the time'),
|
||||
healSpell('Revive2', 90, 'max', 'Restores all HP to dead ally, always succeeds'),
|
||||
|
||||
supportSpell('Purify', 8, 'Removes petrification and poison', ['Battle', 'Map']),
|
||||
supportSpell('Defense1', 5, 'Halves physical damage from enemy', ['Battle']),
|
||||
supportSpell('Power', 6, 'Doubles power', ['Battle']),
|
||||
supportSpell('Agility', 3, 'Increases Speed by 30', ['Battle']),
|
||||
supportSpell('F.Shield', 16, 'Nullifies next attack spell', ['Battle']),
|
||||
supportSpell('Protect', 20, 'Nullifies next Vacuum spell', ['Battle']),
|
||||
supportSpell('Exit', 30, 'Escape cave/dungeon immediately', ['Map']),
|
||||
];
|
||||
}(typeof(module) !== 'undefined' ? module.exports : window.saga));
|
@ -1,30 +1,31 @@
|
||||
extends master.pug
|
||||
|
||||
block tab-content
|
||||
table#main-table.table.table-sm.table-borderless.table-striped.table-hover.sticky-header
|
||||
table#main-table.table.table-sm.table-borderless.table-striped.table-hover.sticky-header.row-clickable
|
||||
tr.header-above
|
||||
th(colspan="2")
|
||||
th(colspan="3").text-center.bg-info.text-light Reward
|
||||
th(colspan="7").text-center.bg-secondary.text-light Stats
|
||||
th(colspan="2").text-center.bg-info.text-light Reward
|
||||
th(colspan="6").text-center.bg-secondary.text-light Stats
|
||||
th(colspan="5").text-center.bg-dark.text-light Resistance
|
||||
th(colspan="2")
|
||||
tr.header
|
||||
th.align-middle Img
|
||||
+sortHeader('Name', 'name')
|
||||
+sortHeader('Gold', 'gold')
|
||||
+sortHeader('Exp', 'exp')
|
||||
th.align-middle Drops
|
||||
+sortHeader('HP', 'hp')
|
||||
+sortHeader('MP', 'mp')
|
||||
+sortHeader('Power', 'power')
|
||||
+sortHeader('Guard', 'guard')
|
||||
+sortHeader('Magic', 'magic')
|
||||
+sortHeader('Speed', 'speed')
|
||||
th.align-middle Spells
|
||||
+sortHeader('Fire', 'res-fire')
|
||||
+sortHeader('Ice', 'res-ice')
|
||||
+sortHeader('Thunder', 'res-thunder')
|
||||
+sortHeader('Vacuum', 'res-vacuum')
|
||||
+sortHeader('Debuff', 'res-debuff')
|
||||
th.align-middle Spells
|
||||
th.align-middle Drops
|
||||
tbody.data: each enemy in enemies
|
||||
tr(
|
||||
data-name=enemy.name
|
||||
@ -39,30 +40,128 @@ block tab-content
|
||||
data-res-fire=enemy.resistance.fire
|
||||
data-res-ice=enemy.resistance.ice
|
||||
data-res-thunder=enemy.resistance.thunder
|
||||
data-res-vacuum=enemy.resistance.vacuum
|
||||
data-res-debuff=enemy.resistance.debuff
|
||||
data-res-vacuum=(enemy.resistance.vacuum === null ? 100 : enemy.resistance.vacuum)
|
||||
data-res-debuff=(enemy.resistance.debuff === null ? 100 : enemy.resistance.debuff)
|
||||
)
|
||||
td
|
||||
td: strong= enemy.name
|
||||
td: code= enemy.gold
|
||||
td: code= enemy.exp
|
||||
td.text-right: code= enemy.gold
|
||||
td.text-right: code= enemy.exp
|
||||
td.text-right: code= enemy.hp
|
||||
td.text-right: code= enemy.mp
|
||||
td.text-right: code= enemy.power
|
||||
td.text-right: code= enemy.guard
|
||||
td.text-right: code= enemy.magic
|
||||
td.text-right: code= enemy.speed
|
||||
td.text-right: code= enemy.resistance.fire
|
||||
td.text-right: code= enemy.resistance.ice
|
||||
td.text-right: code= enemy.resistance.thunder
|
||||
td.text-right: code= enemy.resistance.vacuum === null ? 100 : enemy.resistance.vacuum
|
||||
td.text-right: code= enemy.resistance.debuff === null ? 100 : enemy.resistance.debuff
|
||||
td
|
||||
+na(enemy.spells.length)
|
||||
ul.list-horizontal: each spell in enemy.spells
|
||||
li= spell
|
||||
td
|
||||
+na(Object.keys(enemy.drops).length)
|
||||
ul.list-horizontal: each rate, item in enemy.drops
|
||||
li
|
||||
= item + ' (' + (rate * 100).toFixed(2) + '%)'
|
||||
td: code= enemy.hp
|
||||
td: code= enemy.mp
|
||||
td: code= enemy.power
|
||||
td: code= enemy.guard
|
||||
td: code= enemy.magic
|
||||
td: code= enemy.speed
|
||||
td
|
||||
+na(enemy.spells.length)
|
||||
ul.list-horizontal: each spell in enemy.spells
|
||||
li= spell
|
||||
td: code= enemy.resistance.fire
|
||||
td: code= enemy.resistance.ice
|
||||
td: code= enemy.resistance.thunder
|
||||
td: code= enemy.resistance.vacuum
|
||||
td: code= enemy.resistance.debuff
|
||||
|
||||
div#enemy-info-modal.modal(tabindex="-1")
|
||||
div.modal-dialog.modal-lg
|
||||
div.modal-content
|
||||
div.modal-header
|
||||
h5.modal-title
|
||||
span.name
|
||||
div
|
||||
| #[span.gold]/#[span.exp]
|
||||
div.modal-body
|
||||
div.row
|
||||
div.col-6
|
||||
table.table.table-horizontal
|
||||
tr
|
||||
th(colspan="2") Stats
|
||||
th(colspan="2") Resistances
|
||||
tr
|
||||
th HP/MP
|
||||
td
|
||||
span.hp
|
||||
| /
|
||||
span.mp
|
||||
th Fire
|
||||
td.res-fire
|
||||
tr
|
||||
th Power
|
||||
td.power
|
||||
th Ice
|
||||
td.res-ice
|
||||
tr
|
||||
th Guard
|
||||
td.guard
|
||||
th Thunder
|
||||
td.res-thunder
|
||||
tr
|
||||
th Magic
|
||||
td.magic
|
||||
th Vacuum
|
||||
td.res-vacuum
|
||||
tr
|
||||
th Speed
|
||||
td.speed
|
||||
th Debuff
|
||||
td.res-debuff
|
||||
|
||||
div.card.bg-light
|
||||
div.card-body
|
||||
table.table.table-sm.table-horizontal
|
||||
tr
|
||||
th Run rate
|
||||
td: code.run-rate
|
||||
th w/ speed up
|
||||
td: code.run-rate-speed-up
|
||||
tr
|
||||
th Hit rate
|
||||
td: code.hit-rate
|
||||
th w/ speed up
|
||||
td: code.hit-rate-speed-up
|
||||
tr
|
||||
th Debuff rate
|
||||
td: code.debuff-rate
|
||||
th
|
||||
td
|
||||
tr
|
||||
th Vacuum rate
|
||||
td: code.vacuum-rate
|
||||
th
|
||||
td
|
||||
|
||||
div.col-6
|
||||
div.card.bg-light
|
||||
div.card-body
|
||||
table.table.table-horizontal.table-sm
|
||||
tr
|
||||
th
|
||||
th
|
||||
th Default
|
||||
th Guard up
|
||||
tr
|
||||
th(rowspan="2").align-middle Physical
|
||||
th Normal
|
||||
td: code.physical-dmg
|
||||
td: code.physical-dmg-guard-up
|
||||
tr
|
||||
th Power up
|
||||
td: code.physical-dmg-power-up
|
||||
td: code.physical-dmg-power-up-guard-up
|
||||
tr
|
||||
th
|
||||
th
|
||||
th Default
|
||||
th Magic up
|
||||
each spell in charSpells.filter(x => !!x.power || x.name === 'HPCatcher' || x.name === 'MPCatcher')
|
||||
tr
|
||||
th= spell.name
|
||||
th
|
||||
td: code(class=("magic-dmg-" + spell.name))
|
||||
td: code(class=("magic-dmg-" + spell.name + '-magic-up'))
|
||||
|
@ -37,21 +37,79 @@ html
|
||||
label.form-check-label(for=id)= location
|
||||
|
||||
div.container-fluid
|
||||
ul.nav.nav-tabs.mt-4.px-4.position-sticky(style="top: 0; background-color: white; z-index: 1")
|
||||
li.nav-item: a.nav-link(href="/enemies" class=(context === 'enemies' ? 'active' : '')) Enemies
|
||||
li.nav-item: a.nav-link(href="/spells" class=(context === 'spells' ? 'active' : '')) Spells
|
||||
li.nav-item: a.nav-link(href="/items" class=(context === 'items' ? 'active' : '')) Items
|
||||
li.nav-item: a.nav-link(href="/weapons" class=(context === 'weapons' ? 'active' : '')) Weapons
|
||||
li.nav-item: a.nav-link(href="/armor" class=(context === 'armor' ? 'active' : '')) Armor
|
||||
li.nav-item: a.nav-link(href="/accessories" class=(context === 'accessories' ? 'active' : '')) Accessories
|
||||
li.nav-item: a.nav-link(href="/exp" class=(context === 'exp' ? 'active' : '')) Experience
|
||||
li.nav-item: a.nav-link(href="/calc" class=(context === 'calc' ? 'active' : '')) Calculations
|
||||
div.bg-light.position-sticky.pt-2.d-flex.justify-content-between(style="top: 0; background-color: white; z-index: 1")
|
||||
ul.nav.mr-auto.nav-tabs
|
||||
li.nav-item: a.nav-link(href="/enemies" class=(context === 'enemies' ? 'active' : '')) Enemies
|
||||
li.nav-item: a.nav-link(href="/spells" class=(context === 'spells' ? 'active' : '')) Spells
|
||||
li.nav-item: a.nav-link(href="/items" class=(context === 'items' ? 'active' : '')) Items
|
||||
li.nav-item: a.nav-link(href="/weapons" class=(context === 'weapons' ? 'active' : '')) Weapons
|
||||
li.nav-item: a.nav-link(href="/armor" class=(context === 'armor' ? 'active' : '')) Armor
|
||||
li.nav-item: a.nav-link(href="/accessories" class=(context === 'accessories' ? 'active' : '')) Accessories
|
||||
li.nav-item: a.nav-link(href="/exp" class=(context === 'exp' ? 'active' : '')) Experience
|
||||
div
|
||||
button.btn.btn-secondary.btn-sm(data-toggle="modal" data-target="#char-stats-modal") Character stats…
|
||||
div.tab-content
|
||||
div.tab-pane.show.active.mt-2
|
||||
block tab-content
|
||||
|
||||
|
||||
div#char-stats-modal.modal(tabindex="-1")
|
||||
div.modal-dialog
|
||||
div.modal-content
|
||||
div.modal-header
|
||||
h5.modal-title Character stats
|
||||
button.close(type="button" data-dismiss="modal")
|
||||
div.modal-body
|
||||
div.row
|
||||
div.col-6
|
||||
div.form-row.form-group
|
||||
label.col-form-label-sm.col-4(for="char-power") Power
|
||||
div.col-8: input#char-power.form-control(type="number" min="0" step="1" max="999" autocomplete="off")
|
||||
div.form-row.form-group
|
||||
label.col-form-label-sm.col-4(for="char-guard") Guard
|
||||
div.col-8: input#char-guard.form-control(type="number" min="0" step="1" max="999" autocomplete="off")
|
||||
div.form-row.form-group
|
||||
label.col-form-label-sm.col-4(for="char-magic") Magic
|
||||
div.col-8: input#char-magic.form-control(type="number" min="0" step="1" max="255" autocomplete="off")
|
||||
div.form-row.form-group
|
||||
label.col-form-label-sm.col-4(for="char-speed") Speed
|
||||
div.col-8: input#char-speed.form-control(type="number" min="0" step="1" max="255" autocomplete="off")
|
||||
div.col-6
|
||||
div.form-row.form-group
|
||||
label.col-form-label-sm.col-4(for="char-weapon") Weapon
|
||||
div.col-8: select#char-weapon.form-control(autocomplete="off")
|
||||
option(value="") Choose weapon
|
||||
each weapon in charWeapons
|
||||
option(value=weapon.name data-power=weapon.attack)
|
||||
= weapon.name + ' (' + weapon.attack + ')'
|
||||
div.form-row.form-group
|
||||
label.col-form-label-sm.col-4(for="char-armor") Armor
|
||||
div.col-8: select#char-armor.form-control(autocomplete="off")
|
||||
option(value="") Choose armor
|
||||
each armor in charArmor
|
||||
option(value=armor.name data-defense=armor.defense)
|
||||
= armor.name + ' (' + armor.defense + ')'
|
||||
div.form-row.form-group
|
||||
label.col-form-label-sm.col-4(for="char-armor") Accessory
|
||||
div.col-8: select#char-accessory.form-control(autocomplete="off")
|
||||
option(value="") Choose accessory
|
||||
each accessory in charAccessories
|
||||
option(
|
||||
value=accessory.name
|
||||
data-defense=accessory.defense
|
||||
data-res-fire=accessory.resistance.fire
|
||||
data-res-ice=accessory.resistance.ice
|
||||
data-res-thunder=accessory.resistance.thunder
|
||||
data-res-vacuum=accessory.resistance.vacuum
|
||||
data-res-debuff=accessory.resistance.debuff
|
||||
)= accessory.name + ' (' + accessory.defense + ')'
|
||||
div.modal-footer
|
||||
button.btn.btn-primary(data-dismiss="modal") Close
|
||||
|
||||
script(src="/static/jquery.js")
|
||||
script(src="/static/popper.js")
|
||||
script(src="/static/bootstrap.js")
|
||||
script(src="/static/js.cookie.js")
|
||||
script(src="/static/saga.js")
|
||||
script(src="/static/calc.js")
|
||||
script(src="/static/spells.js")
|
||||
|
@ -2,7 +2,7 @@ extends master.pug
|
||||
|
||||
block tab-content
|
||||
|
||||
div.row.d-flex.justify-content-center: div.col-12.col-md-8
|
||||
div.row: div.col-12.col-md-8
|
||||
+apprenticeFilterForm()
|
||||
table#main-table.table.table-sm.table-borderless.table-hover.table-striped.sticky-header
|
||||
thead: tr.header
|
||||
|
Loading…
Reference in New Issue
Block a user