dok/routes/points.route.js
2026-04-09 13:23:08 +01:00

234 lines
8.5 KiB
JavaScript

/* ============================================================
routes/points.route.js
Punkte-Vergabe nach Arena-Matches
Jeder Spieler meldet sein eigenes Ergebnis:
POST /api/points/match → Punkte für diesen Spieler vergeben
GET /api/points/me → Aktueller Stand + Level-Fortschritt
============================================================ */
const express = require("express");
const router = express.Router();
const db = require("../database/database");
/* ── Punkte-Konfiguration ───────────────────────────────── */
const POINTS = {
"1v1": { win: 15, lose: 3 },
"2v2": { win: 12, lose: 2 },
"4v4": { win: 10, lose: 2 },
};
const DAILY_LIMIT = 500;
const SURRENDER_MIN_SEC = 60; // Aufgabe unter 60s → 0 Punkte
/* ── Auth-Guard ─────────────────────────────────────────── */
function requireLogin(req, res, next) {
if (!req.session?.user) return res.status(401).json({ error: "Nicht eingeloggt" });
next();
}
/* ═══════════════════════════════════════════════════════════
HELPER: Punkte vergeben
- accounts.arena_points += amount (NIE abgezogen → Level)
- user_buildings.points += amount (für Gebäude-Upgrades)
- Tages-Limit 500 prüfen
═══════════════════════════════════════════════════════════ */
async function awardPoints(accountId, amount) {
if (amount <= 0) return { awarded: 0, level_up: false };
const today = new Date().toISOString().slice(0, 10);
const [[acc]] = await db.query(
"SELECT arena_points, arena_points_today, arena_points_reset, level FROM accounts WHERE id = ?",
[accountId]
);
if (!acc) return { awarded: 0, level_up: false };
/* Tages-Reset */
const lastReset = acc.arena_points_reset
? new Date(acc.arena_points_reset).toISOString().slice(0, 10)
: null;
const pointsToday = lastReset === today ? acc.arena_points_today : 0;
/* Tages-Limit anwenden */
const remaining = Math.max(0, DAILY_LIMIT - pointsToday);
const actual = Math.min(amount, remaining);
if (actual === 0) {
return { awarded: 0, level_up: false, daily_limit_reached: true };
}
/* accounts: arena_points erhöhen (NIE abziehen) */
await db.query(
`UPDATE accounts
SET arena_points = arena_points + ?,
arena_points_today = ? + ?,
arena_points_reset = ?
WHERE id = ?`,
[actual, pointsToday, actual, today, accountId]
);
/* Alle Gebäude des Spielers ebenfalls gutschreiben */
await db.query(
"UPDATE user_buildings SET points = points + ? WHERE user_id = ?",
[actual, accountId]
);
/* Level-Up prüfen */
const newTotal = acc.arena_points + actual;
const [[levelRow]] = await db.query(
`SELECT MAX(level) AS level
FROM character_levels
WHERE points_cumulative <= ?`,
[newTotal]
);
const newLevel = levelRow?.level || 1;
const levelUp = newLevel > acc.level;
if (levelUp) {
await db.query(
"UPDATE accounts SET level = ? WHERE id = ?",
[newLevel, accountId]
);
console.log(`[Points] Level-Up! Spieler ${accountId}: ${acc.level}${newLevel}`);
}
return {
awarded: actual,
new_total: newTotal,
new_level: newLevel,
level_up: levelUp,
daily_limit_reached: actual < amount,
};
}
/* ═══════════════════════════════════════════════════════════
POST /api/points/match
Jeder Spieler ruft dies für sich selbst auf wenn das
Match endet (Sieg, Niederlage oder Aufgabe).
Body:
{
match_id: "match_1234_abc", -- eindeutige Match-ID
mode: "1v1"|"2v2"|"4v4",
result: "win"|"lose",
surrender: false, -- true wenn aufgegeben
duration_seconds: 240 -- Spieldauer in Sekunden
}
═══════════════════════════════════════════════════════════ */
router.post("/match", requireLogin, async (req, res) => {
const { match_id, mode, result, surrender, duration_seconds } = req.body;
const accountId = req.session.user.id;
if (!match_id || !mode || !POINTS[mode] || !["win", "lose"].includes(result)) {
return res.status(400).json({ error: "Ungültige Parameter" });
}
try {
/* Doppelte Vergabe für diesen Spieler verhindern */
const [[existing]] = await db.query(
"SELECT id FROM arena_match_players WHERE match_id = ? AND account_id = ?",
[match_id, accountId]
);
if (existing) {
return res.json({ success: true, already_processed: true });
}
/* Punkte berechnen */
const pts = POINTS[mode];
const isWin = result === "win";
const isSurr = !!surrender;
const shortMatch = duration_seconds && duration_seconds < SURRENDER_MIN_SEC;
let pointsToAward = 0;
if (shortMatch) {
pointsToAward = 0; // Zu kurz → keine Punkte
} else if (isWin) {
pointsToAward = pts.win;
} else if (isSurr) {
pointsToAward = 0; // Aufgegeben → keine Punkte
} else {
pointsToAward = pts.lose;
}
/* Punkte vergeben */
const awarded = await awardPoints(accountId, pointsToAward);
/* Match-Log: Eintrag für diesen Spieler */
await db.query(
`INSERT IGNORE INTO arena_matches (match_id, mode, surrender, duration_seconds)
VALUES (?, ?, ?, ?)`,
[match_id, mode, isSurr ? 1 : 0, duration_seconds || null]
);
await db.query(
`INSERT INTO arena_match_players (match_id, account_id, team, won, points)
VALUES (?, ?, 1, ?, ?)`,
[match_id, accountId, isWin ? 1 : 0, awarded.awarded]
);
console.log(
`[Points] ${mode} | Spieler ${accountId} | ${result} | +${awarded.awarded} Pts` +
(awarded.level_up ? ` | LEVEL UP → ${awarded.new_level}!` : "")
);
res.json({
success: true,
points_awarded: awarded.awarded,
total_arena_points: awarded.new_total,
level: awarded.new_level,
level_up: awarded.level_up,
daily_limit_reached: awarded.daily_limit_reached || false,
});
} catch (err) {
console.error("[Points] Fehler:", err);
res.status(500).json({ error: "Datenbankfehler" });
}
});
/* ═══════════════════════════════════════════════════════════
GET /api/points/me
Aktueller Punktestand + Level-Fortschritt für HUD
═══════════════════════════════════════════════════════════ */
router.get("/me", requireLogin, async (req, res) => {
const userId = req.session.user.id;
try {
const [[acc]] = await db.query(
"SELECT arena_points, level FROM accounts WHERE id = ?",
[userId]
);
if (!acc) return res.status(404).json({ error: "Spieler nicht gefunden" });
const [[curLevel]] = await db.query(
`SELECT level, points_cumulative FROM character_levels
WHERE points_cumulative <= ? ORDER BY level DESC LIMIT 1`,
[acc.arena_points]
);
const [[nextLevel]] = await db.query(
`SELECT level, points_cumulative FROM character_levels
WHERE points_cumulative > ? ORDER BY level ASC LIMIT 1`,
[acc.arena_points]
);
const curThreshold = curLevel?.points_cumulative || 0;
const nextThreshold = nextLevel?.points_cumulative || null;
const progress = nextThreshold
? Math.round((acc.arena_points - curThreshold) * 100 / (nextThreshold - curThreshold))
: 100;
res.json({
arena_points: acc.arena_points,
level: acc.level,
points_this_level: acc.arena_points - curThreshold,
points_for_next: nextThreshold ? nextThreshold - curThreshold : 0,
progress_percent: progress,
is_max_level: !nextLevel,
});
} catch (err) {
console.error("[Points] /me Fehler:", err);
res.status(500).json({ error: "Datenbankfehler" });
}
});
module.exports = router;