step 10.12: done
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
// Equipment bonuses: sum of bonus_* fields over all equipped items for a character.
|
||||
// Single shared implementation used by characterService, huntService, inventoryService, shopService.
|
||||
const { getDb } = require('../db');
|
||||
|
||||
/**
|
||||
* Sum equipment bonuses for a character.
|
||||
* @param {number} characterId
|
||||
* @returns {{ bonus_str: number, bonus_agi: number, bonus_int: number, bonus_vit: number, bonus_attack: number, bonus_defense: number }}
|
||||
*/
|
||||
function getEquipmentBonuses(characterId) {
|
||||
const db = getDb();
|
||||
|
||||
const rows = db.prepare(
|
||||
`SELECT it.bonus_str, it.bonus_agi, it.bonus_int, it.bonus_vit,
|
||||
it.bonus_attack, it.bonus_defense
|
||||
FROM inventory i
|
||||
JOIN items it ON it.id = i.item_id
|
||||
WHERE i.character_id = ? AND i.equipped_slot IS NOT NULL`
|
||||
).all(characterId);
|
||||
|
||||
const bonuses = {
|
||||
bonus_str: 0,
|
||||
bonus_agi: 0,
|
||||
bonus_int: 0,
|
||||
bonus_vit: 0,
|
||||
bonus_attack: 0,
|
||||
bonus_defense: 0,
|
||||
};
|
||||
|
||||
for (const row of rows) {
|
||||
bonuses.bonus_str += row.bonus_str || 0;
|
||||
bonuses.bonus_agi += row.bonus_agi || 0;
|
||||
bonuses.bonus_int += row.bonus_int || 0;
|
||||
bonuses.bonus_vit += row.bonus_vit || 0;
|
||||
bonuses.bonus_attack += row.bonus_attack || 0;
|
||||
bonuses.bonus_defense += row.bonus_defense || 0;
|
||||
}
|
||||
|
||||
return bonuses;
|
||||
}
|
||||
|
||||
module.exports = { getEquipmentBonuses };
|
||||
@@ -6,6 +6,7 @@ const { getBaseStats } = require('../game/classes');
|
||||
const { calcDerived, computeEnergy, ENERGY_MAX, ENERGY_REGEN_SECONDS } = require('../game/stats');
|
||||
const { computeEnergy: computeEnergyPure } = require('../game/energy');
|
||||
const { computeHp } = require('../game/hp');
|
||||
const { getEquipmentBonuses } = require('../game/equipment');
|
||||
|
||||
|
||||
function wrapStmts(db) {
|
||||
@@ -78,11 +79,13 @@ function getCharacterProfile(userId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Compute derived stats (with no equipment bonuses for now)
|
||||
// Compute derived stats including equipment bonuses
|
||||
const equipBonuses = getEquipmentBonuses(character.id);
|
||||
const derived = calcDerived(
|
||||
{ str: character.str, agi: character.agi, int: character.int, vit: character.vit },
|
||||
character.level,
|
||||
character.class
|
||||
character.class,
|
||||
equipBonuses
|
||||
);
|
||||
|
||||
// Compute current energy with lazy regen
|
||||
@@ -117,7 +120,8 @@ function getCharacterProfile(userId) {
|
||||
derived.maxHp = calcDerived(
|
||||
{ str: character.str, agi: character.agi, int: character.int, vit: character.vit },
|
||||
character.level,
|
||||
character.class
|
||||
character.class,
|
||||
equipBonuses
|
||||
).maxHp;
|
||||
}
|
||||
|
||||
@@ -321,11 +325,13 @@ function rest(userId) {
|
||||
throw new AppError('NO_CHARACTER', 'No character found.', 404);
|
||||
}
|
||||
|
||||
// Compute derived stats to know max HP
|
||||
// Compute derived stats including equipment bonuses to know max HP
|
||||
const equipBonuses = getEquipmentBonuses(character.id);
|
||||
const derived = calcDerived(
|
||||
{ str: character.str, agi: character.agi, int: character.int, vit: character.vit },
|
||||
character.level,
|
||||
character.class
|
||||
character.class,
|
||||
equipBonuses
|
||||
);
|
||||
|
||||
const cost = character.level * 5;
|
||||
|
||||
@@ -11,6 +11,7 @@ const { resolveCombat } = require('../game/combat');
|
||||
const { calculateRewards, rollLoot } = require('../game/rewards');
|
||||
const { checkLevelUp, applyXpGain } = require('../game/leveling');
|
||||
const { computeHp } = require('../game/hp');
|
||||
const { getEquipmentBonuses } = require('../game/equipment');
|
||||
|
||||
const MIN_HP_PCT_TO_HUNT = 0.2;
|
||||
const DEFEAT_GOLD_LOSS_PCT = 0.10;
|
||||
@@ -94,7 +95,8 @@ function hunt(characterId, zoneId, opts = {}) {
|
||||
}
|
||||
|
||||
const baseStats = { str: ch.str, agi: ch.agi, int: ch.int, vit: ch.vit };
|
||||
const derived = calcDerived(baseStats, ch.level, ch.class);
|
||||
const equipBonuses = getEquipmentBonuses(characterId);
|
||||
const derived = calcDerived(baseStats, ch.level, ch.class, equipBonuses);
|
||||
|
||||
// Apply lazy HP regen before checking HP threshold
|
||||
const hpUpdatedAt = ch.hp_updated_at ?? ch.energy_updated_at;
|
||||
@@ -116,7 +118,7 @@ function hunt(characterId, zoneId, opts = {}) {
|
||||
if (monsters.length === 0) throw new AppError('ZONE_EMPTY', 'No monsters in this zone.', 500);
|
||||
const monster = pickWeightedMonster(monsters, rng);
|
||||
|
||||
const fight = resolveCombat({ ...baseStats, class: ch.class, level: ch.level, hpCurrent: ch.hp_current }, monster, rng);
|
||||
const fight = resolveCombat({ ...baseStats, class: ch.class, level: ch.level, hpCurrent: ch.hp_current, ...equipBonuses }, monster, rng);
|
||||
|
||||
let { xp, gold, level, unspent_points: unspent, kills, deaths } = ch;
|
||||
let hp = fight.hpRemaining;
|
||||
@@ -146,7 +148,7 @@ function hunt(characterId, zoneId, opts = {}) {
|
||||
leveledUp = true;
|
||||
level = xpResult.level;
|
||||
unspent += xpResult.unspentPoints - ch.unspent_points;
|
||||
hp = calcDerived(baseStats, level, ch.class).maxHp; // full heal at the NEW max HP
|
||||
hp = calcDerived(baseStats, level, ch.class, equipBonuses).maxHp; // full heal at the NEW max HP
|
||||
}
|
||||
} else {
|
||||
goldLost = Math.floor(ch.gold * DEFEAT_GOLD_LOSS_PCT);
|
||||
@@ -162,7 +164,7 @@ function hunt(characterId, zoneId, opts = {}) {
|
||||
winner: fight.winner,
|
||||
rounds: fight.rounds,
|
||||
hpRemaining: hp,
|
||||
hpMax: calcDerived(baseStats, level, ch.class).maxHp,
|
||||
hpMax: calcDerived(baseStats, level, ch.class, equipBonuses).maxHp,
|
||||
xpGained, goldGained, goldLost, loot, leveledUp,
|
||||
newLevel: level,
|
||||
energy: energy.energy - zone.energy_cost,
|
||||
|
||||
@@ -4,6 +4,7 @@ const { getDb } = require('../db');
|
||||
const { AppError } = require('../utils/errors');
|
||||
const { calcDerived, ENERGY_MAX } = require('../game/stats');
|
||||
const { computeHp } = require('../game/hp');
|
||||
const { getEquipmentBonuses } = require('../game/equipment');
|
||||
const time = require('../utils/time');
|
||||
|
||||
const VALID_SLOTS = new Set(['weapon', 'armor', 'trinket']);
|
||||
@@ -280,9 +281,10 @@ function useConsumable(characterId, itemId) {
|
||||
const applyEffect = db.transaction((rowId, qty, etype, evalue, charId, c, hat) => {
|
||||
// Apply the effect.
|
||||
if (etype === 'heal') {
|
||||
const equipBonuses = getEquipmentBonuses(charId);
|
||||
const maxHp = calcDerived(
|
||||
{ str: c.str, agi: c.agi, int: c.int, vit: c.vit },
|
||||
c.level, c.class
|
||||
c.level, c.class, equipBonuses
|
||||
).maxHp;
|
||||
// Apply lazy HP regen before healing
|
||||
const hpUpdatedAt = c.hp_updated_at ?? c.energy_updated_at;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Sell value = 40% of buy_price (materials with buy_price=0 use rarity default).
|
||||
const { getDb } = require('../db');
|
||||
const { AppError } = require('../utils/errors');
|
||||
const { getEquipmentBonuses } = require('../game/equipment');
|
||||
const { calcDerived } = require('../game/stats');
|
||||
|
||||
// Rarity defaults for materials (buy_price = 0).
|
||||
const MATERIAL_SELL_DEFAULTS = { common: 2, uncommon: 4, rare: 8, epic: 16, legendary: 32 };
|
||||
@@ -278,22 +280,7 @@ function listInventory(characterId) {
|
||||
const rows = s.listInventory.all(characterId);
|
||||
|
||||
// Calculate current derived stats including equipment bonuses.
|
||||
const equipBonuses = {
|
||||
bonus_str: 0, bonus_agi: 0, bonus_int: 0, bonus_vit: 0,
|
||||
bonus_attack: 0, bonus_defense: 0,
|
||||
};
|
||||
for (const row of rows) {
|
||||
if (row.equipped_slot) {
|
||||
equipBonuses.bonus_str += row.bonus_str || 0;
|
||||
equipBonuses.bonus_agi += row.bonus_agi || 0;
|
||||
equipBonuses.bonus_int += row.bonus_int || 0;
|
||||
equipBonuses.bonus_vit += row.bonus_vit || 0;
|
||||
equipBonuses.bonus_attack += row.bonus_attack || 0;
|
||||
equipBonuses.bonus_defense += row.bonus_defense || 0;
|
||||
}
|
||||
}
|
||||
|
||||
const { calcDerived } = require('../game/stats');
|
||||
const equipBonuses = getEquipmentBonuses(characterId);
|
||||
const derived = calcDerived(
|
||||
{ str: ch.str, agi: ch.agi, int: ch.int, vit: ch.vit },
|
||||
ch.level,
|
||||
|
||||
Reference in New Issue
Block a user