step 10.10: wip (not ticked)

This commit is contained in:
bepis
2026-10-23 01:38:39 +11:00
parent 28210ed56c
commit 68a3b42cc9
9 changed files with 428 additions and 20 deletions
+1
View File
@@ -66,6 +66,7 @@ function createApp() {
app.post('/api/character', requireAuth, characterRoutes.postCharacter);
app.get('/api/character', requireAuth, characterRoutes.getCharacter);
app.post('/api/character/allocate', requireAuth, characterRoutes.postAllocate);
app.post('/api/character/rest', requireAuth, characterRoutes.postRest);
// Hunt routes (require session)
app.post('/api/hunt', requireAuth, huntRoutes.postHunt);
+10 -2
View File
@@ -1,8 +1,8 @@
// Character routes: POST /api/character (create), GET /api/character (profile), POST /api/character/allocate.
// Character routes: POST /api/character (create), GET /api/character (profile), POST /api/character/allocate, POST /api/character/rest.
const zod = require('zod');
const { AppError } = require('../utils/errors');
const { requireAuth } = require('../middleware/session');
const { createCharacter, getCharacterProfile, allocatePoints } = require('../services/characterService');
const { createCharacter, getCharacterProfile, allocatePoints, rest } = require('../services/characterService');
const { CLASS_KEYS } = require('../game/classes');
// Validation schemas
@@ -64,10 +64,18 @@ async function handleAllocate(req, res) {
res.json({ ok: true, data: result });
}
async function handleRest(req, res) {
const userId = req.session.userId;
const result = rest(userId);
res.json({ ok: true, data: result });
}
module.exports = {
routes: {
postCharacter: wrap(handleCreate),
getCharacter: wrap(handleGet),
postAllocate: wrap(handleAllocate),
postRest: wrap(handleRest),
},
};
+78 -1
View File
@@ -283,4 +283,81 @@ function allocatePoints(userId, allocation) {
};
}
module.exports = { createCharacter, getCharacterProfile, spendEnergy, getCharacterIdForUser, allocatePoints };
/**
* Rest: heal to full max HP for gold cost = level * 5.
* Runs in a transaction. Errors: NOTHING_TO_HEAL, INSUFFICIENT_GOLD.
* @param {number} userId - the user's ID
* @returns {object} { character, derived } updated profile
*/
function rest(userId) {
const db = getDb();
const stmts = getStmts();
// Get character
const character = stmts.findByUserId.get(userId);
if (!character) {
throw new AppError('NO_CHARACTER', 'No character found.', 404);
}
// Compute derived stats to know max HP
const derived = calcDerived(
{ str: character.str, agi: character.agi, int: character.int, vit: character.vit },
character.level,
character.class
);
const cost = character.level * 5;
// Check if already at full HP
if (character.hp_current >= derived.maxHp) {
throw new AppError('NOTHING_TO_HEAL', 'Already at full health.', 422);
}
// Check if enough gold
if (character.gold < cost) {
throw new AppError('INSUFFICIENT_GOLD',
`Not enough gold. Need ${cost}, have ${character.gold}.`, 422);
}
// Heal to full and deduct gold in a transaction
const update = db.transaction((hp, gold, cid) => {
db.prepare(
'UPDATE characters SET hp_current = ?, gold = ? WHERE id = ?'
).run(hp, gold, cid);
});
update(derived.maxHp, character.gold - cost, character.id);
// Refresh character data
const updated = db.prepare('SELECT * FROM characters WHERE id = ?').get(character.id);
return {
character: {
id: updated.id,
name: updated.name,
class: updated.class,
level: updated.level,
xp: updated.xp,
gold: updated.gold,
str: updated.str,
agi: updated.agi,
int: updated.int,
vit: updated.vit,
unspent_points: updated.unspent_points,
hp_current: updated.hp_current,
maxHp: derived.maxHp,
kills: updated.kills,
deaths: updated.deaths,
},
derived: {
maxHp: derived.maxHp,
attack: derived.attack,
defense: derived.defense,
speed: derived.speed,
critChance: derived.critChance,
critMult: derived.critMult,
},
};
}
module.exports = { createCharacter, getCharacterProfile, spendEnergy, getCharacterIdForUser, allocatePoints, rest };