step 10.3: done
This commit is contained in:
@@ -48,7 +48,7 @@ async function handleClaim(req, res) {
|
||||
|
||||
const { expeditionId } = parsed.data;
|
||||
const characterId = getCharacterIdForUser(req.session.userId);
|
||||
const result = claimExpedition(characterId, expeditionId);
|
||||
const result = claimExpedition(characterId, expeditionId, req.body._opts);
|
||||
res.json({ ok: true, data: result });
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ const { getDb } = require('../db');
|
||||
const { AppError } = require('../utils/errors');
|
||||
const { computeEnergy } = require('../game/energy');
|
||||
const { applyXpGain } = require('../game/leveling');
|
||||
const { now, setNow } = require('../utils/time');
|
||||
const time = require('../utils/time');
|
||||
|
||||
// Prepared statements cached per DB handle (WeakMap).
|
||||
const stmtCache = new WeakMap();
|
||||
@@ -93,9 +93,11 @@ function getStmts(db) {
|
||||
* Start an expedition for a character.
|
||||
* @param {number} characterId
|
||||
* @param {number} defId
|
||||
* @param {{now?: number}} [opts] - injectable now for tests
|
||||
* @returns {object} { expedition, definition }
|
||||
*/
|
||||
function startExpedition(characterId, defId) {
|
||||
function startExpedition(characterId, defId, opts = {}) {
|
||||
const now = opts.now ?? time.now();
|
||||
const db = getDb();
|
||||
const s = getStmts(db);
|
||||
|
||||
@@ -117,7 +119,7 @@ function startExpedition(characterId, defId) {
|
||||
|
||||
// Lazy energy evaluation.
|
||||
const { energy, updatedAtMs } = computeEnergy(
|
||||
ch.energy, ch.energy_updated_at, now(),
|
||||
ch.energy, ch.energy_updated_at, now,
|
||||
{ max: 100, regenRate: 1, regenInterval: 180 }
|
||||
);
|
||||
|
||||
@@ -126,7 +128,7 @@ function startExpedition(characterId, defId) {
|
||||
}
|
||||
|
||||
const newEnergy = energy - def.energy_cost;
|
||||
const startedAt = now();
|
||||
const startedAt = now;
|
||||
const endsAt = startedAt + (def.duration_sec * 1000);
|
||||
|
||||
// Execute inside a transaction.
|
||||
@@ -168,16 +170,17 @@ function startExpedition(characterId, defId) {
|
||||
/**
|
||||
* Get the active (unclaimed) expedition for a character.
|
||||
* @param {number} characterId
|
||||
* @param {{now?: number}} [opts] - injectable now for tests
|
||||
* @returns {object|null} { expedition, definition, timeRemaining } or null
|
||||
*/
|
||||
function getActive(characterId) {
|
||||
function getActive(characterId, opts = {}) {
|
||||
const db = getDb();
|
||||
const s = getStmts(db);
|
||||
|
||||
const row = s.getActive.get(characterId);
|
||||
if (!row) return null;
|
||||
|
||||
const nowMs = now();
|
||||
const nowMs = opts.now ?? time.now();
|
||||
const timeRemaining = Math.max(0, row.ends_at - nowMs);
|
||||
|
||||
return {
|
||||
@@ -207,9 +210,10 @@ function getActive(characterId) {
|
||||
* Claim a finished expedition and grant rewards.
|
||||
* @param {number} characterId
|
||||
* @param {number} expeditionId
|
||||
* @param {{rng?: function, now?: number}} [opts] - injectable rng/now for tests
|
||||
* @returns {object} { xpGained, goldGained, expedition }
|
||||
*/
|
||||
function claimExpedition(characterId, expeditionId) {
|
||||
function claimExpedition(characterId, expeditionId, opts = {}) {
|
||||
const db = getDb();
|
||||
const s = getStmts(db);
|
||||
|
||||
@@ -224,7 +228,7 @@ function claimExpedition(characterId, expeditionId) {
|
||||
throw new AppError('ALREADY_CLAIMED', 'This expedition has already been claimed.', 409);
|
||||
}
|
||||
|
||||
const nowMs = now();
|
||||
const nowMs = opts.now ?? time.now();
|
||||
if (nowMs < exp.ends_at) {
|
||||
throw new AppError('NOT_FINISHED', 'Expedition has not finished yet.', 409);
|
||||
}
|
||||
@@ -237,9 +241,10 @@ function claimExpedition(characterId, expeditionId) {
|
||||
const ch = s.character.get(characterId);
|
||||
if (!ch) throw new AppError('NOT_FOUND', 'Character not found.', 404);
|
||||
|
||||
// Calculate rewards.
|
||||
// Calculate rewards using injectable rng.
|
||||
const xpGained = def.xp_reward;
|
||||
const goldGained = def.gold_min + Math.floor(Math.random() * (def.gold_max - def.gold_min + 1));
|
||||
const rng = opts.rng || (() => Math.random());
|
||||
const goldGained = def.gold_min + Math.floor(rng() * (def.gold_max - def.gold_min + 1));
|
||||
const newGold = ch.gold + goldGained;
|
||||
|
||||
// Apply XP gain (adds to current, handles level-up, accumulates stat points).
|
||||
|
||||
Reference in New Issue
Block a user