mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 18:41:08 +02:00
Split monolithic RPGScene.js (1210 lines) into modular manager classes: - WorldManager, PlayerManager, NPCManager, ChatUI, StorageManager, SoundManager, TouchControls Key improvements: - Constants config (GAME_CONFIG) replacing all magic numbers - JSDoc types + jsconfig.json for IDE type-safety - LocalStorage persistence for progress, stats, and custom avatars - Synthesized sound effects via Web Audio API - 26 NPCs (up from 10) in 3 categories - Stats/leaderboard in main menu - Pixel editor avatar integration with RPG game - Mobile touch controls (virtual joystick + interact button) - Chat UI with typing indicator and conversation history - Interactive tutorial overlay for first-time players - Floating question mark over NPCs in range - Server hardened: rate limiting, input sanitization, CORS restrictions, API timeouts, conversation history cap - Particle effect object pooling - i18n framework with DE/EN and language switcher Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
92 lines
2.3 KiB
JavaScript
92 lines
2.3 KiB
JavaScript
/**
|
|
* @typedef {Object} GameSaveData
|
|
* @property {number[]} discoveredNPCs - IDs der entdeckten NPCs
|
|
* @property {number} totalGuesses - Gesamtanzahl der Rateversuche
|
|
* @property {number} totalRevealed - Gesamtanzahl aufgedeckter NPCs
|
|
* @property {number} bestStreak - Beste Serie korrekt erratener NPCs
|
|
* @property {number} currentStreak - Aktuelle Serie
|
|
* @property {Record<number, number>} guessesPerNPC - Anzahl Versuche pro NPC (ID -> Anzahl)
|
|
* @property {number} lastPlayed - Timestamp des letzten Spiels
|
|
*/
|
|
|
|
class StorageManager {
|
|
constructor() {
|
|
this.STORAGE_KEY = 'whopixels_save';
|
|
}
|
|
|
|
/** @returns {GameSaveData} */
|
|
load() {
|
|
try {
|
|
const saved = localStorage.getItem(this.STORAGE_KEY);
|
|
if (saved) {
|
|
return JSON.parse(saved);
|
|
}
|
|
} catch (error) {
|
|
console.error('Fehler beim Laden des Spielstands:', error);
|
|
}
|
|
return this._createDefault();
|
|
}
|
|
|
|
/** @param {GameSaveData} data */
|
|
save(data) {
|
|
try {
|
|
data.lastPlayed = Date.now();
|
|
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(data));
|
|
} catch (error) {
|
|
console.error('Fehler beim Speichern:', error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* NPC als entdeckt markieren und Statistiken aktualisieren
|
|
* @param {number} npcId
|
|
* @param {number} guessCount - Anzahl der Fragen bis zur Enthüllung
|
|
*/
|
|
recordDiscovery(npcId, guessCount) {
|
|
const data = this.load();
|
|
|
|
if (!data.discoveredNPCs.includes(npcId)) {
|
|
data.discoveredNPCs.push(npcId);
|
|
}
|
|
|
|
data.totalRevealed++;
|
|
data.totalGuesses += guessCount;
|
|
data.currentStreak++;
|
|
data.guessesPerNPC[npcId] = guessCount;
|
|
|
|
if (data.currentStreak > data.bestStreak) {
|
|
data.bestStreak = data.currentStreak;
|
|
}
|
|
|
|
this.save(data);
|
|
return data;
|
|
}
|
|
|
|
/** @returns {GameSaveData} */
|
|
_createDefault() {
|
|
return {
|
|
discoveredNPCs: [],
|
|
totalGuesses: 0,
|
|
totalRevealed: 0,
|
|
bestStreak: 0,
|
|
currentStreak: 0,
|
|
guessesPerNPC: {},
|
|
lastPlayed: 0,
|
|
};
|
|
}
|
|
|
|
reset() {
|
|
localStorage.removeItem(this.STORAGE_KEY);
|
|
}
|
|
|
|
/** @returns {{averageGuesses: number, totalRevealed: number, bestStreak: number}} */
|
|
getStats() {
|
|
const data = this.load();
|
|
return {
|
|
averageGuesses:
|
|
data.totalRevealed > 0 ? Math.round((data.totalGuesses / data.totalRevealed) * 10) / 10 : 0,
|
|
totalRevealed: data.totalRevealed,
|
|
bestStreak: data.bestStreak,
|
|
};
|
|
}
|
|
}
|