step 10.15: done

This commit is contained in:
bepis
2026-10-23 06:16:38 +11:00
parent 2a79c4ea43
commit 81794c7dbd
7 changed files with 240 additions and 34 deletions
+25
View File
@@ -0,0 +1,25 @@
// src/game/inventory.js — Shared inventory helpers.
// Pure data operations; no DB, no Express.
/**
* Grant an item to a character's inventory.
* Stackable items merge into one row (increment quantity).
* Non-stackable items get one row per unit (quantity 1 each).
*
* @param {object} s — prepared statements cache (from service's getStmts)
* @param {number} characterId
* @param {object} item — item row with `stackable` and `id`
* @param {number} quantity
* @param {number} now — timestamp in ms
*/
function grantItem(s, characterId, item, quantity, now) {
if (item.stackable) {
const row = s.stackRow.get(characterId, item.id);
if (row) s.addToStack.run(quantity, row.id);
else s.insertInv.run(characterId, item.id, quantity, now);
} else {
for (let i = 0; i < quantity; i++) s.insertInv.run(characterId, item.id, 1, now);
}
}
module.exports = { grantItem };
+1 -11
View File
@@ -12,6 +12,7 @@ const { calculateRewards, rollLoot } = require('../game/rewards');
const { checkLevelUp, applyXpGain } = require('../game/leveling');
const { computeHp } = require('../game/hp');
const { getEquipmentBonuses } = require('../game/equipment');
const { grantItem } = require('../game/inventory');
const MIN_HP_PCT_TO_HUNT = 0.2;
const DEFEAT_GOLD_LOSS_PCT = 0.10;
@@ -59,17 +60,6 @@ function pickWeightedMonster(monsters, rng) {
return monsters[monsters.length - 1];
}
// Stackable items merge into one row per (character, item); others get one row per unit.
function grantItem(s, characterId, item, quantity, now) {
if (item.stackable) {
const row = s.stackRow.get(characterId, item.id);
if (row) s.addToStack.run(quantity, row.id);
else s.insertInv.run(characterId, item.id, quantity, now);
} else {
for (let i = 0; i < quantity; i++) s.insertInv.run(characterId, item.id, 1, now);
}
}
/**
* @param {number} characterId
* @param {number} zoneId
+20 -10
View File
@@ -5,6 +5,7 @@ const { AppError } = require('../utils/errors');
const { getEquipmentBonuses } = require('../game/equipment');
const { calcDerived } = require('../game/stats');
const { materialSellPrice } = require('../game/rewards');
const { grantItem } = require('../game/inventory');
// Prepared statements cached per DB handle (WeakMap).
const stmtCache = new WeakMap();
@@ -41,6 +42,21 @@ function getStmts(db) {
'SELECT * FROM characters WHERE id = ?'
),
// Get inventory row for stack check (used by shared grantItem).
stackRow: db.prepare(
'SELECT id FROM inventory WHERE character_id = ? AND item_id = ? AND equipped_slot IS NULL'
),
// Add to existing stack (used by shared grantItem).
addToStack: db.prepare(
'UPDATE inventory SET quantity = quantity + ? WHERE id = ?'
),
// Insert new inventory row (used by shared grantItem).
insertInv: db.prepare(
'INSERT INTO inventory (character_id, item_id, quantity, acquired_at) VALUES (?, ?, ?, ?)'
),
// Get inventory rows for a character + item (for selling).
inventoryRows: db.prepare(
'SELECT * FROM inventory WHERE character_id = ? AND item_id = ? AND quantity >= ?'
@@ -253,21 +269,15 @@ function buyItem(characterId, itemId, quantity) {
const now = require('../utils/time').now();
// Execute the purchase inside a transaction.
const doBuy = db.transaction((cost, ng, cid, iid, qty, acquiredAt) => {
const doBuy = db.transaction((ng, cid, itemObj, qty, acquiredAt) => {
// Deduct gold.
s.updateGold.run(ng, cid);
// If stackable and already owned, increment quantity.
const existing = s.inventoryRows.get(cid, iid, 1);
if (existing && item.stackable) {
s.incrementQty.run(qty, cid, iid);
} else {
// New inventory entry.
s.addToInventory.run(cid, iid, qty, acquiredAt);
}
// Grant item (stackable merges, non-stackable gets one row per unit).
grantItem(s, cid, itemObj, qty, acquiredAt);
});
doBuy(totalCost, newGold, characterId, itemId, quantity, now);
doBuy(newGold, characterId, item, quantity, now);
// Return updated inventory listing.
return listInventory(characterId);