server data-driven now of quests, items, and dungeons + fixed saves

This commit is contained in:
Robert MacRae 2026-01-26 17:15:57 -04:00
parent 5e1a2e0037
commit 4aeb217e51
9 changed files with 3067 additions and 236 deletions

View File

@ -1,18 +0,0 @@
# Game Server Configuration
PORT=3002
NODE_ENV=production
# Database Configuration
MONGODB_URI=mongodb://localhost:27017/galaxystrikeonline
# API Server Configuration
API_SERVER_URL=http://localhost:3000
GAME_SERVER_URL=https://dev.gameserver.galaxystrike.online
# Optional: Server identification
SERVER_NAME=Dev Game Server
SERVER_REGION=local
MAX_PLAYERS=8
# Logging
LOG_LEVEL=info

View File

@ -3,9 +3,12 @@ const logger = require('../utils/logger');
const connectDB = async () => {
try {
console.log('[DATABASE] Connecting to MongoDB:', process.env.MONGODB_URI);
const conn = await mongoose.connect(process.env.MONGODB_URI);
console.log(`[DATABASE] MongoDB Connected: ${conn.connection.host}`);
logger.info(`MongoDB Connected: ${conn.connection.host}`);
} catch (error) {
console.error('[DATABASE] Database connection failed:', error);
logger.error('Database connection failed:', error);
process.exit(1);
}

View File

@ -0,0 +1,128 @@
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: {}
},
// Server-specific data
currentServerId: {
type: String,
default: null
},
lastServerJoin: {
type: Date,
default: null
},
totalPlayTime: {
type: Number,
default: 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
playerDataSchema.methods.addExperience = function(amount) {
this.stats.experience += amount;
this.stats.playTime += Date.now() - (this.lastLogin || Date.now());
this.lastLogin = new Date();
return this.stats.experience;
};
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);

View File

@ -263,4 +263,118 @@ router.post('/cleanup', async (req, res) => {
}
});
// Get room types
router.get('/room-types', async (req, res) => {
try {
const roomTypes = dungeonSystem.getRoomTypes();
res.json(roomTypes);
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get enemy templates
router.get('/enemy-templates', async (req, res) => {
try {
const enemyTemplates = dungeonSystem.getEnemyTemplates();
res.json(enemyTemplates);
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Start dungeon (simplified for client)
router.post('/start', async (req, res) => {
try {
const { dungeonId, userId } = req.body;
const instance = dungeonSystem.createInstance(dungeonId, userId, []);
res.json({
success: true,
instance
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Process encounter (simplified for client)
router.post('/encounter', async (req, res) => {
try {
const { instanceId, userId } = req.body;
const result = dungeonSystem.startEncounter(instanceId, userId);
res.json({
success: true,
encounter: result
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Complete dungeon (simplified for client)
router.post('/complete', async (req, res) => {
try {
const { instanceId, userId } = req.body;
const result = dungeonSystem.completeDungeon(instanceId);
res.json({
success: true,
rewards: result
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Get player dungeon status
router.get('/status/:userId', async (req, res) => {
try {
const { userId } = req.params;
const instance = dungeonSystem.getPlayerInstance(userId);
if (!instance) {
return res.json({
success: true,
status: {
hasActiveDungeon: false,
currentInstance: null
}
});
}
res.json({
success: true,
status: {
hasActiveDungeon: true,
currentInstance: instance
}
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
module.exports = router;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -9,12 +9,20 @@ class IdleSystem {
this.maxOfflineTime = 7 * 24 * 60 * 60 * 1000; // 7 days in milliseconds
this.playerLastActive = new Map(); // userId -> last active timestamp
this.playerProductionRates = new Map(); // userId -> production rates
this.playerAchievements = new Map(); // userId -> achievements
// Default production rates
this.defaultProductionRates = {
credits: 10, // credits per second (increased for better gameplay)
experience: 1, // experience per second (increased for better progression)
energy: 0.5 // energy regeneration per second
credits: 0.1, // 1 credit every 10 seconds (0.1 per second)
experience: 0, // no auto experience - only from dungeons
energy: 1/300 // 1 energy every 5 minutes (1/300 per second)
};
// Offline rates (different from online rates)
this.offlineProductionRates = {
credits: 1/60, // 1 credit every 1 minute (1/60 per second)
experience: 0, // no experience offline - only from dungeons
energy: 1/300 // 1 energy every 5 minutes (same as online)
};
// Idle rewards
@ -90,12 +98,13 @@ class IdleSystem {
const productionRates = this.playerProductionRates.get(userId);
const achievements = this.playerAchievements.get(userId);
// Calculate offline rewards
// Calculate offline rewards using offline rates
const offlineSeconds = Math.floor(cappedOfflineTime / 1000);
const offlineRates = this.offlineProductionRates;
const rewards = {
credits: Math.floor(productionRates.credits * offlineSeconds),
experience: Math.floor(productionRates.experience * offlineSeconds),
energy: Math.floor(productionRates.energy * offlineSeconds),
credits: Math.floor(offlineRates.credits * offlineSeconds),
experience: Math.floor(offlineRates.experience * offlineSeconds),
energy: Math.floor(offlineRates.energy * offlineSeconds),
items: []
};
@ -239,6 +248,27 @@ class IdleSystem {
return results;
}
// Generate online idle rewards (called periodically while player is online)
generateOnlineIdleRewards(userId, deltaTimeMs) {
const productionRates = this.playerProductionRates.get(userId);
if (!productionRates) {
return {
credits: 0,
experience: 0,
energy: 0
};
}
const deltaTimeSeconds = deltaTimeMs / 1000;
const rewards = {
credits: Math.floor(productionRates.credits * deltaTimeSeconds),
experience: Math.floor(productionRates.experience * deltaTimeSeconds),
energy: Math.floor(productionRates.energy * deltaTimeSeconds)
};
return rewards;
}
// Reset idle data for a user
resetPlayerData(userId) {
this.playerLastActive.delete(userId);

View File

@ -0,0 +1,618 @@
/**
* Galaxy Strike Online - Item System
* Centralized item management for shop, inventory, dungeons, and all game systems
*/
class ItemSystem {
constructor() {
// Master item catalog - single source of truth
this.itemCatalog = {
// Ships
ships: [
{
id: 'starter_cruiser_common',
name: 'Starter Cruiser',
type: 'ship',
rarity: 'common',
price: 5000,
currency: 'credits',
description: 'Reliable starter cruiser for new pilots',
texture: 'assets/textures/ships/starter_cruiser.png',
stats: { attack: 15, speed: 10, defense: 12, hull: 100 },
categories: ['shop', 'dungeon_reward'],
requirements: { level: 1 },
stackable: false
},
{
id: 'starter_cruiser_uncommon',
name: 'Starter Cruiser II',
type: 'ship',
rarity: 'uncommon',
price: 12000,
currency: 'credits',
description: 'Upgraded starter cruiser with enhanced systems',
texture: 'assets/textures/ships/starter_cruiser.png',
stats: { attack: 18, speed: 12, defense: 15, hull: 120 },
categories: ['shop', 'dungeon_reward'],
requirements: { level: 5 },
stackable: false
},
{
id: 'starter_cruiser_rare',
name: 'Starter Cruiser III',
type: 'ship',
rarity: 'rare',
price: 25000,
currency: 'credits',
description: 'Elite starter cruiser with maximum upgrades',
texture: 'assets/textures/ships/starter_cruiser.png',
stats: { attack: 22, speed: 15, defense: 18, hull: 150 },
categories: ['shop', 'dungeon_reward'],
requirements: { level: 10 },
stackable: false
},
{
id: 'interceptor_common',
name: 'Interceptor',
type: 'ship',
rarity: 'common',
price: 8000,
currency: 'credits',
description: 'Fast attack ship for hit-and-run tactics',
texture: 'assets/textures/ships/interceptor.png',
stats: { attack: 12, speed: 18, defense: 8, hull: 80 },
categories: ['shop', 'dungeon_reward'],
requirements: { level: 3 },
stackable: false
},
{
id: 'interceptor_uncommon',
name: 'Interceptor II',
type: 'ship',
rarity: 'uncommon',
price: 18000,
currency: 'credits',
description: 'Enhanced interceptor with improved weapons',
texture: 'assets/textures/ships/interceptor.png',
stats: { attack: 15, speed: 22, defense: 10, hull: 95 },
categories: ['shop', 'dungeon_reward'],
requirements: { level: 7 },
stackable: false
}
],
// Materials
materials: [
{
id: 'steel_plating',
name: 'Steel Plating',
type: 'material',
rarity: 'common',
price: 100,
currency: 'credits',
description: 'Basic armor material for ship upgrades',
categories: ['shop', 'dungeon_loot', 'crafting'],
requirements: { level: 1 },
stackable: true,
maxStack: 99,
effects: { defense: 5 }
},
{
id: 'energy_core',
name: 'Energy Core',
type: 'material',
rarity: 'uncommon',
price: 250,
currency: 'credits',
description: 'Power source for advanced upgrades',
categories: ['shop', 'dungeon_loot', 'crafting'],
requirements: { level: 5 },
stackable: true,
maxStack: 50,
effects: { energy: 10 }
},
{
id: 'quantum_fuel',
name: 'Quantum Fuel',
type: 'material',
rarity: 'rare',
price: 500,
currency: 'credits',
description: 'Advanced fuel for long-range travel',
categories: ['shop', 'dungeon_loot', 'crafting'],
requirements: { level: 10 },
stackable: true,
maxStack: 25,
effects: { speed: 15 }
},
{
id: 'dark_matter',
name: 'Dark Matter',
type: 'material',
rarity: 'legendary',
price: 2000,
currency: 'gems',
description: 'Exotic matter for ultimate upgrades',
categories: ['shop', 'dungeon_loot', 'crafting'],
requirements: { level: 15 },
stackable: true,
maxStack: 10,
effects: { attack: 20, defense: 20 }
}
],
// Consumables
consumables: [
{
id: 'repair_kit',
name: 'Repair Kit',
type: 'consumable',
rarity: 'common',
price: 50,
currency: 'credits',
description: 'Instantly repairs ship damage',
categories: ['shop', 'dungeon_loot'],
requirements: { level: 1 },
stackable: true,
maxStack: 20,
effects: { hull_repair: 50 },
consumable: true,
cooldown: 30000 // 30 seconds
},
{
id: 'energy_boost',
name: 'Energy Boost',
type: 'consumable',
rarity: 'common',
price: 75,
currency: 'credits',
description: 'Temporary energy increase',
categories: ['shop', 'dungeon_loot'],
requirements: { level: 1 },
stackable: true,
maxStack: 15,
effects: { energy_boost: 25, duration: 60000 }, // 1 minute
consumable: true,
cooldown: 45000 // 45 seconds
},
{
id: 'shield_booster',
name: 'Shield Booster',
type: 'consumable',
rarity: 'uncommon',
price: 150,
currency: 'credits',
description: 'Temporary shield enhancement',
categories: ['shop', 'dungeon_loot'],
requirements: { level: 5 },
stackable: true,
maxStack: 10,
effects: { shield_boost: 50, duration: 90000 }, // 1.5 minutes
consumable: true,
cooldown: 60000 // 1 minute
},
{
id: 'damage_amplifier',
name: 'Damage Amplifier',
type: 'consumable',
rarity: 'rare',
price: 300,
currency: 'credits',
description: 'Increases damage output temporarily',
categories: ['shop', 'dungeon_loot'],
requirements: { level: 8 },
stackable: true,
maxStack: 5,
effects: { damage_multiplier: 1.5, duration: 45000 }, // 45 seconds
consumable: true,
cooldown: 120000 // 2 minutes
}
],
// Cosmetics
cosmetics: [
{
id: 'red_paint',
name: 'Red Paint Job',
type: 'cosmetic',
rarity: 'common',
price: 500,
currency: 'credits',
description: 'Red color scheme for your ship',
categories: ['shop'],
requirements: { level: 1 },
stackable: false,
cosmetic: true,
slot: 'paint'
},
{
id: 'blue_paint',
name: 'Blue Paint Job',
type: 'cosmetic',
rarity: 'common',
price: 500,
currency: 'credits',
description: 'Blue color scheme for your ship',
categories: ['shop'],
requirements: { level: 1 },
stackable: false,
cosmetic: true,
slot: 'paint'
},
{
id: 'gold_trim',
name: 'Gold Trim',
type: 'cosmetic',
rarity: 'uncommon',
price: 1000,
currency: 'credits',
description: 'Gold accent trim for your ship',
categories: ['shop'],
requirements: { level: 5 },
stackable: false,
cosmetic: true,
slot: 'trim'
},
{
id: 'rainbow_effect',
name: 'Rainbow Engine Effect',
type: 'cosmetic',
rarity: 'rare',
price: 2500,
currency: 'credits',
description: 'Colorful engine trail effect',
categories: ['shop'],
requirements: { level: 10 },
stackable: false,
cosmetic: true,
slot: 'engine'
}
],
// Weapons
weapons: [
{
id: 'laser_cannon_common',
name: 'Laser Cannon',
type: 'weapon',
rarity: 'common',
price: 1000,
currency: 'credits',
description: 'Basic laser weapon for beginners',
categories: ['shop', 'dungeon_loot'],
requirements: { level: 1 },
stackable: false,
stats: { attack: 10, range: 100, fire_rate: 2 }
},
{
id: 'plasma_rifle_uncommon',
name: 'Plasma Rifle',
type: 'weapon',
rarity: 'uncommon',
price: 2500,
currency: 'credits',
description: 'Plasma-based weapon with increased damage',
categories: ['shop', 'dungeon_loot'],
requirements: { level: 5 },
stackable: false,
stats: { attack: 18, range: 120, fire_rate: 1.5 }
},
{
id: 'quantum_blaster_rare',
name: 'Quantum Blaster',
type: 'weapon',
rarity: 'rare',
price: 6000,
currency: 'credits',
description: 'Advanced quantum weapon with high damage output',
categories: ['shop', 'dungeon_loot'],
requirements: { level: 10 },
stackable: false,
stats: { attack: 30, range: 150, fire_rate: 1 }
}
],
// Armors
armors: [
{
id: 'basic_shield_common',
name: 'Basic Shield Generator',
type: 'armor',
rarity: 'common',
price: 800,
currency: 'credits',
description: 'Basic shield protection for beginners',
categories: ['shop', 'dungeon_loot'],
requirements: { level: 1 },
stackable: false,
stats: { defense: 8, shield_capacity: 50, recharge_rate: 5 }
},
{
id: 'energy_armor_uncommon',
name: 'Energy Armor',
type: 'armor',
rarity: 'uncommon',
price: 2000,
currency: 'credits',
description: 'Energy-based armor with enhanced protection',
categories: ['shop', 'dungeon_loot'],
requirements: { level: 5 },
stackable: false,
stats: { defense: 15, shield_capacity: 100, recharge_rate: 8 }
},
{
id: 'quantum_deflector_rare',
name: 'Quantum Deflector',
type: 'armor',
rarity: 'rare',
price: 5000,
currency: 'credits',
description: 'Advanced quantum armor with maximum protection',
categories: ['shop', 'dungeon_loot'],
requirements: { level: 10 },
stackable: false,
stats: { defense: 25, shield_capacity: 200, recharge_rate: 12 }
}
],
// Dungeon-specific rewards
dungeon_rewards: [
{
id: 'dungeon_key_basic',
name: 'Basic Dungeon Key',
type: 'key',
rarity: 'common',
price: 200,
currency: 'credits',
description: 'Key to enter basic dungeons',
categories: ['shop', 'dungeon_loot'],
requirements: { level: 1 },
stackable: true,
maxStack: 10,
consumable: true,
dungeon_access: ['basic_dungeon', 'mines']
},
{
id: 'dungeon_key_advanced',
name: 'Advanced Dungeon Key',
type: 'key',
rarity: 'uncommon',
price: 800,
currency: 'credits',
description: 'Key to enter advanced dungeons',
categories: ['shop', 'dungeon_loot'],
requirements: { level: 8 },
stackable: true,
maxStack: 5,
consumable: true,
dungeon_access: ['advanced_dungeon', 'fortress']
},
{
id: 'treasure_chest',
name: 'Treasure Chest',
type: 'container',
rarity: 'rare',
price: 0,
currency: 'credits',
description: 'Contains random valuable items',
categories: ['dungeon_loot'],
requirements: { level: 1 },
stackable: true,
maxStack: 5,
consumable: true,
loot_table: 'treasure_chest_common'
}
]
};
// Initialize item lookup maps for performance
this.itemLookup = new Map();
this.categoryLookup = new Map();
this.rarityLookup = new Map();
this.buildLookupMaps();
}
/**
* Build lookup maps for efficient item retrieval
*/
buildLookupMaps() {
// Build ID lookup
for (const [category, items] of Object.entries(this.itemCatalog)) {
for (const item of items) {
this.itemLookup.set(item.id, item);
// Build category lookup
if (!this.categoryLookup.has(category)) {
this.categoryLookup.set(category, []);
}
this.categoryLookup.get(category).push(item);
// Build rarity lookup
if (!this.rarityLookup.has(item.rarity)) {
this.rarityLookup.set(item.rarity, []);
}
this.rarityLookup.get(item.rarity).push(item);
}
}
}
/**
* Get item by ID
*/
getItem(itemId) {
return this.itemLookup.get(itemId) || null;
}
/**
* Get all items in a category
*/
getItemsByCategory(category) {
return this.categoryLookup.get(category) || [];
}
/**
* Get items by rarity
*/
getItemsByRarity(rarity) {
return this.rarityLookup.get(rarity) || [];
}
/**
* Get items available for specific context (shop, dungeon, etc.)
*/
getItemsByContext(context) {
const results = [];
for (const item of this.itemLookup.values()) {
if (item.categories && item.categories.includes(context)) {
results.push(item);
}
}
return results;
}
/**
* Get shop items (items available in shop)
*/
getShopItems() {
return this.getItemsByContext('shop');
}
/**
* Get dungeon loot items
*/
getDungeonLootItems() {
return this.getItemsByContext('dungeon_loot');
}
/**
* Get dungeon reward items
*/
getDungeonRewardItems() {
return this.getItemsByContext('dungeon_reward');
}
/**
* Get crafting materials
*/
getCraftingMaterials() {
return this.getItemsByContext('crafting');
}
/**
* Check if player meets item requirements
*/
canPlayerUseItem(playerData, item) {
if (!item.requirements) return true;
// Check level requirement
if (item.requirements.level && playerData.stats.level < item.requirements.level) {
return false;
}
// Add other requirement checks here (skills, quests, etc.)
return true;
}
/**
* Get filtered items for player (based on requirements)
*/
getAvailableItemsForPlayer(playerData, context = null) {
let items = context ? this.getItemsByContext(context) : Array.from(this.itemLookup.values());
return items.filter(item => this.canPlayerUseItem(playerData, item));
}
/**
* Generate random loot based on rarity weights
*/
generateLoot(rarityWeights = null, context = null) {
const defaultWeights = {
common: 50,
uncommon: 30,
rare: 15,
legendary: 5
};
const weights = rarityWeights || defaultWeights;
const availableItems = context ? this.getItemsByContext(context) : Array.from(this.itemLookup.values());
// Filter by rarity weights
const weightedItems = [];
for (const item of availableItems) {
const weight = weights[item.rarity] || 0;
for (let i = 0; i < weight; i++) {
weightedItems.push(item);
}
}
if (weightedItems.length === 0) return null;
// Random selection
const randomIndex = Math.floor(Math.random() * weightedItems.length);
return weightedItems[randomIndex];
}
/**
* Get item statistics
*/
getItemStats() {
const stats = {
totalItems: this.itemLookup.size,
byCategory: {},
byRarity: {},
byType: {}
};
for (const item of this.itemLookup.values()) {
// Count by category
for (const category of item.categories || []) {
stats.byCategory[category] = (stats.byCategory[category] || 0) + 1;
}
// Count by rarity
stats.byRarity[item.rarity] = (stats.byRarity[item.rarity] || 0) + 1;
// Count by type
stats.byType[item.type] = (stats.byType[item.type] || 0) + 1;
}
return stats;
}
/**
* Validate item data structure
*/
validateItem(item) {
const required = ['id', 'name', 'type', 'rarity'];
const missing = required.filter(field => !item[field]);
if (missing.length > 0) {
return { valid: false, errors: [`Missing required fields: ${missing.join(', ')}`] };
}
const errors = [];
// Validate type
if (!['ship', 'material', 'consumable', 'cosmetic', 'key', 'container'].includes(item.type)) {
errors.push(`Invalid item type: ${item.type}`);
}
// Validate rarity
if (!['common', 'uncommon', 'rare', 'legendary'].includes(item.rarity)) {
errors.push(`Invalid rarity: ${item.rarity}`);
}
// Validate stackable items
if (item.stackable && (!item.maxStack || item.maxStack < 1)) {
errors.push('Stackable items must have maxStack >= 1');
}
return { valid: errors.length === 0, errors };
}
}
module.exports = ItemSystem;

View File

@ -30,6 +30,176 @@ class QuestSystem {
timeLimit: null
});
// Main Story quests
this.addQuest('main_story_beginning', {
name: 'The Beginning',
description: 'Start your journey as a space pilot',
type: 'main',
difficulty: 'easy',
requirements: {
level: 1,
battlesWon: 1
},
rewards: {
experience: 200,
credits: 1000,
skillPoints: 2
},
prerequisites: [],
repeatable: false,
timeLimit: null
});
this.addQuest('main_story_first_dungeon', {
name: 'First Dungeon',
description: 'Complete your first dungeon run',
type: 'main',
difficulty: 'medium',
requirements: {
level: 5,
dungeonsCleared: 1
},
rewards: {
experience: 500,
credits: 2000,
items: ['rare_weapon']
},
prerequisites: ['main_story_beginning'],
repeatable: false,
timeLimit: null
});
this.addQuest('main_story_space_exploration', {
name: 'Space Explorer',
description: 'Explore 10 different sectors',
type: 'main',
difficulty: 'medium',
requirements: {
level: 10,
sectorsExplored: 10
},
rewards: {
experience: 1000,
credits: 5000,
items: ['explorer_badge']
},
prerequisites: ['main_story_first_dungeon'],
repeatable: false,
timeLimit: null
});
// Daily quests
this.addQuest('daily_battles', {
name: 'Daily Battles',
description: 'Win 5 battles today',
type: 'daily',
difficulty: 'easy',
requirements: {
battlesWon: 5
},
rewards: {
experience: 150,
credits: 750,
gems: 5
},
prerequisites: [],
repeatable: true,
timeLimit: 24 * 60 * 60 * 1000 // 24 hours
});
this.addQuest('daily_exploration', {
name: 'Daily Exploration',
description: 'Explore 3 new sectors today',
type: 'daily',
difficulty: 'easy',
requirements: {
sectorsExplored: 3
},
rewards: {
experience: 100,
credits: 500,
gems: 3
},
prerequisites: [],
repeatable: true,
timeLimit: 24 * 60 * 60 * 1000 // 24 hours
});
this.addQuest('daily_resources', {
name: 'Daily Resource Collection',
description: 'Collect 1000 resources today',
type: 'daily',
difficulty: 'medium',
requirements: {
resourcesCollected: 1000
},
rewards: {
experience: 200,
credits: 1000,
gems: 8
},
prerequisites: [],
repeatable: true,
timeLimit: 24 * 60 * 60 * 1000 // 24 hours
});
// Weekly quests
this.addQuest('weekly_champion', {
name: 'Weekly Champion',
description: 'Win 50 battles this week',
type: 'weekly',
difficulty: 'hard',
requirements: {
battlesWon: 50
},
rewards: {
experience: 2000,
credits: 10000,
gems: 50,
items: ['champion_title']
},
prerequisites: [],
repeatable: true,
timeLimit: 7 * 24 * 60 * 60 * 1000 // 7 days
});
this.addQuest('weekly_dungeon_master', {
name: 'Weekly Dungeon Master',
description: 'Complete 10 dungeons this week',
type: 'weekly',
difficulty: 'hard',
requirements: {
dungeonsCleared: 10
},
rewards: {
experience: 3000,
credits: 15000,
gems: 75,
items: ['dungeon_master_cape']
},
prerequisites: [],
repeatable: true,
timeLimit: 7 * 24 * 60 * 60 * 1000 // 7 days
});
this.addQuest('weekly_wealth_collector', {
name: 'Weekly Wealth Collector',
description: 'Earn 10000 credits this week',
type: 'weekly',
difficulty: 'medium',
requirements: {
creditsEarned: 10000
},
rewards: {
experience: 1500,
credits: 5000,
gems: 25
},
prerequisites: [],
repeatable: true,
timeLimit: 7 * 24 * 60 * 60 * 1000 // 7 days
});
// Combat quests
this.addQuest('warrior_path', {
name: 'Warrior Path',
@ -152,18 +322,89 @@ class QuestSystem {
initializePlayerData(userId) {
if (!this.playerQuests.has(userId)) {
this.playerQuests.set(userId, {
const playerData = {
activeQuests: new Map(),
completedQuests: new Map(),
questHistory: [],
totalQuestsCompleted: 0,
dailyQuestsCompleted: 0,
weeklyQuestsCompleted: 0
});
};
// Assign starting quests to new players
this.assignStartingQuests(userId, playerData);
this.playerQuests.set(userId, playerData);
}
return this.playerQuests.get(userId);
}
assignStartingQuests(userId, playerData) {
console.log(`[QUEST SYSTEM] Assigning starting quests to player ${userId}`);
// Assign main story quests
const mainStoryQuests = ['main_story_beginning', 'main_story_first_dungeon'];
mainStoryQuests.forEach(questId => {
const quest = this.quests.get(questId);
if (quest && !playerData.activeQuests.has(questId) && !playerData.completedQuests.has(questId)) {
playerData.activeQuests.set(questId, {
...quest,
progress: 0,
startedAt: Date.now()
});
console.log(`[QUEST SYSTEM] Assigned main story quest: ${quest.name}`);
}
});
// Assign daily quests
this.generateDailyQuests(userId, playerData);
// Assign weekly quests
this.generateWeeklyQuests(userId, playerData);
console.log(`[QUEST SYSTEM] Player now has ${playerData.activeQuests.size} active quests`);
}
generateDailyQuests(userId, playerData) {
console.log(`[QUEST SYSTEM] Generating daily quests for player ${userId}`);
const dailyQuestTemplates = ['daily_battles', 'daily_exploration', 'daily_resources'];
dailyQuestTemplates.forEach(questId => {
const quest = this.quests.get(questId);
if (quest) {
playerData.activeQuests.set(questId, {
...quest,
progress: 0,
startedAt: Date.now(),
type: 'daily',
resetTime: Date.now() + (24 * 60 * 60 * 1000) // 24 hours from now
});
console.log(`[QUEST SYSTEM] Assigned daily quest: ${quest.name}`);
}
});
}
generateWeeklyQuests(userId, playerData) {
console.log(`[QUEST SYSTEM] Generating weekly quests for player ${userId}`);
const weeklyQuestTemplates = ['weekly_champion', 'weekly_dungeon_master', 'weekly_wealth_collector'];
weeklyQuestTemplates.forEach(questId => {
const quest = this.quests.get(questId);
if (quest) {
playerData.activeQuests.set(questId, {
...quest,
progress: 0,
startedAt: Date.now(),
type: 'weekly',
resetTime: Date.now() + (7 * 24 * 60 * 60 * 1000) // 7 days from now
});
console.log(`[QUEST SYSTEM] Assigned weekly quest: ${quest.name}`);
}
});
}
getPlayerData(userId) {
return this.playerQuests.get(userId) || this.initializePlayerData(userId);
}