const mongoose = require('mongoose'); const playerDataSchema = new mongoose.Schema({ userId: { type: String, required: true, unique: true }, username: { type: String, required: true }, // Player stats stats: { level: { type: Number, default: 1 }, experience: { type: Number, default: 0 }, totalExperience: { type: Number, default: 0 }, credits: { type: Number, default: 1000 }, gems: { type: Number, default: 0 }, skillPoints: { type: Number, default: 0 }, totalKills: { type: Number, default: 0 }, dungeonsCleared: { type: Number, default: 0 }, questsCompleted: { type: Number, default: 0 }, playTime: { type: Number, default: 0 }, lastLogin: { type: Date, default: Date.now } }, // Game systems data inventory: { items: [{ type: mongoose.Schema.Types.Mixed }], maxSize: { type: Number, default: 50 } }, ship: { type: mongoose.Schema.Types.Mixed, default: null }, base: { type: mongoose.Schema.Types.Mixed, default: null }, quests: { active: [{ type: mongoose.Schema.Types.Mixed }], completed: [{ type: String }] }, skills: { type: mongoose.Schema.Types.Mixed, default: {} }, idleSystem: { type: mongoose.Schema.Types.Mixed, default: {} }, dungeonSystem: { type: mongoose.Schema.Types.Mixed, default: {} }, crafting: { type: mongoose.Schema.Types.Mixed, default: {} }, // Starbase customisation — wallpapers and room unlocks starbase: { type: mongoose.Schema.Types.Mixed, default: { wallpaper: null, ownedWallpapers: [], unlockedRooms: [], } }, // NPC Faction Reputation (GDD §15.3) — scale -2000 to +2000 reputation: { type: mongoose.Schema.Types.Mixed, default: null }, // Fleet missions (GDD §8.3) fleetMissions: { type: mongoose.Schema.Types.Mixed, default: [] }, // Ship construction queue (GDD §7.4) shipQueue: { type: mongoose.Schema.Types.Mixed, default: [] }, // Alliance membership (GDD §12) allianceId: { type: String, default: null }, allianceTag: { type: String, default: null }, allianceName: { type: String, default: null }, allianceRank: { type: String, default: null }, // Core resources — Metal, Gas, Crystal, Energy Cells, Dark Matter (GDD §5) resources: { type: mongoose.Schema.Types.Mixed, default: { metal: 500, gas: 200, crystal: 100, energyCells: 300, darkMatter: 0, lastTick: null } }, // Building slots — { buildingId: { level, buildQueue: { startedAt, completesAt } | null } } buildings: { type: mongoose.Schema.Types.Mixed, default: { command_center: { level: 1, buildQueue: null } } }, // Galaxy map — explored sectors and home sector exploredSectors: { type: [String], default: ['15_10'] }, homeSector: { type: String, default: '15_10' }, // Research progress research: { type: mongoose.Schema.Types.Mixed, default: { completed: [], inProgress: null, effects: {} } }, // Server-specific data currentServerId: { type: String, default: null }, lastServerJoin: { type: Date, default: null }, totalPlayTime: { type: Number, default: 0 }, // PvP Ranked (GDD Phase 3) pvp: { type: mongoose.Schema.Types.Mixed, default: { rating: 1000, // ELO-style starting rating wins: 0, losses: 0, winStreak: 0, bestStreak: 0, seasonRating: 1000, lastRankedAt: null, tier: 'bronze', // bronze / silver / gold / platinum / diamond history: [] // last 50 match records } }, // Raid participation (GDD Phase 3) raids: { type: mongoose.Schema.Types.Mixed, default: { completed: [], weeklyDone: false, monthlyBossDone: false, totalRaids: 0 } } }, { timestamps: true }); // Indexes for performance playerDataSchema.index({ username: 1 }); playerDataSchema.index({ 'stats.level': 1 }); // Note: userId field already has unique: true which creates an index automatically // Methods // GDD §3.2: XP_required(L) = 500 × L^1.65 function xpForLevel(level) { return Math.floor(500 * Math.pow(level, 1.65)); } playerDataSchema.methods.addExperience = function(amount) { this.stats.experience = (this.stats.experience || 0) + amount; this.stats.totalExperience = (this.stats.totalExperience || 0) + amount; // Level-up loop let leveled = false; while (this.stats.experience >= xpForLevel(this.stats.level + 1)) { this.stats.experience -= xpForLevel(this.stats.level + 1); this.stats.level += 1; this.stats.skillPoints = (this.stats.skillPoints || 0) + 1; // +1 skill point per level leveled = true; } this.stats.experienceToNextLevel = xpForLevel(this.stats.level + 1); return { newXP: this.stats.experience, level: this.stats.level, leveled }; }; playerDataSchema.methods.addCredits = function(amount) { this.stats.credits += amount; return this.stats.credits; }; playerDataSchema.methods.updatePlayTime = function() { const sessionTime = Date.now() - (this.lastLogin || Date.now()); this.stats.playTime += sessionTime; this.totalPlayTime += sessionTime; this.lastLogin = new Date(); return sessionTime; }; playerDataSchema.methods.joinServer = function(serverId) { this.currentServerId = serverId; this.lastServerJoin = new Date(); this.lastLogin = new Date(); }; playerDataSchema.methods.leaveServer = function() { console.log('[PLAYER DATA] leaveServer called for:', this.username); // Update play time before leaving const sessionTime = Date.now() - (this.lastLogin || Date.now()); this.stats.playTime += sessionTime; this.totalPlayTime += sessionTime; this.lastLogin = new Date(); // Clear server association this.currentServerId = null; this.lastServerJoin = null; console.log('[PLAYER DATA] Updated play time:', sessionTime, 'Total play time:', this.totalPlayTime); return sessionTime; }; module.exports = mongoose.model('PlayerData', playerDataSchema);