2336 lines
92 KiB
JavaScript
2336 lines
92 KiB
JavaScript
/**
|
|
* Galaxy Strike Online - Economy System
|
|
* Manages credits, gems, transactions, and shop
|
|
*/
|
|
|
|
class Economy {
|
|
constructor(gameEngine) {
|
|
const debugLogger = window.debugLogger;
|
|
if (debugLogger) debugLogger.log('Economy constructor called');
|
|
|
|
this.game = gameEngine;
|
|
|
|
// Currency
|
|
this.credits = 1000;
|
|
this.gems = 10;
|
|
this.premiumCurrency = 0; // For future premium features
|
|
|
|
// Transaction history
|
|
this.transactionHistory = [];
|
|
this.transactions = []; // Add missing transactions array
|
|
|
|
// Owned cosmetics
|
|
this.ownedCosmetics = []; // Add missing owned cosmetics array
|
|
|
|
// Shop categories
|
|
this.shopCategories = {
|
|
ships: 'Ships',
|
|
weapons: 'Weapons',
|
|
armors: 'Armors',
|
|
materials: 'Materials',
|
|
cosmetics: 'Cosmetics',
|
|
// upgrades: 'Upgrades', // Temporarily disabled
|
|
consumables: 'Consumables',
|
|
buildings: 'Buildings'
|
|
};
|
|
|
|
// Random shop system
|
|
this.randomShopItems = {}; // Current random items per category
|
|
this.shopRefreshInterval = null; // Timer for 2-hour refresh
|
|
this.shopHeartbeatInterval = null; // Timer for live countdown updates
|
|
this.lastShopRefresh = null; // Timestamp of last refresh
|
|
this.SHOP_REFRESH_INTERVAL = 2 * 60 * 60 * 1000; // 2 hours in milliseconds
|
|
this.MAX_ITEMS_PER_CATEGORY = 6;
|
|
this.categoryPurchaseLimits = {}; // Track purchases per category per refresh
|
|
|
|
// Shop items
|
|
this.shopItems = {
|
|
ships: [
|
|
// Starter Cruiser Variants
|
|
{
|
|
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 }
|
|
},
|
|
{
|
|
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 }
|
|
},
|
|
{
|
|
id: 'starter_cruiser_rare',
|
|
name: 'Starter Cruiser III',
|
|
type: 'ship',
|
|
rarity: 'rare',
|
|
price: 25000,
|
|
currency: 'credits',
|
|
description: 'Elite starter cruiser with advanced weaponry',
|
|
texture: 'assets/textures/ships/starter_cruiser.png',
|
|
stats: { attack: 22, speed: 14, defense: 18, hull: 145 }
|
|
},
|
|
{
|
|
id: 'starter_cruiser_epic',
|
|
name: 'Starter Cruiser IV',
|
|
type: 'ship',
|
|
rarity: 'epic',
|
|
price: 45000,
|
|
currency: 'credits',
|
|
description: 'Master starter cruiser with elite modifications',
|
|
texture: 'assets/textures/ships/starter_cruiser.png',
|
|
stats: { attack: 26, speed: 16, defense: 22, hull: 175 }
|
|
},
|
|
{
|
|
id: 'starter_cruiser_legendary',
|
|
name: 'Starter Cruiser V',
|
|
type: 'ship',
|
|
rarity: 'legendary',
|
|
price: 75000,
|
|
currency: 'credits',
|
|
description: 'Legendary starter cruiser with unparalleled performance',
|
|
texture: 'assets/textures/ships/starter_cruiser.png',
|
|
stats: { attack: 32, speed: 18, defense: 28, hull: 210 }
|
|
},
|
|
|
|
// Light Destroyer Variants
|
|
{
|
|
id: 'light_destroyer_common',
|
|
name: 'Light Destroyer',
|
|
type: 'ship',
|
|
rarity: 'common',
|
|
price: 12000,
|
|
currency: 'credits',
|
|
description: 'Fast and maneuverable light destroyer',
|
|
texture: 'assets/textures/ships/light_destroyer.png',
|
|
stats: { attack: 18, speed: 18, defense: 10, hull: 80 }
|
|
},
|
|
{
|
|
id: 'light_destroyer_uncommon',
|
|
name: 'Light Destroyer II',
|
|
type: 'ship',
|
|
rarity: 'uncommon',
|
|
price: 28000,
|
|
currency: 'credits',
|
|
description: 'Enhanced light destroyer with improved speed',
|
|
texture: 'assets/textures/ships/light_destroyer.png',
|
|
stats: { attack: 22, speed: 22, defense: 12, hull: 95 }
|
|
},
|
|
{
|
|
id: 'light_destroyer_rare',
|
|
name: 'Light Destroyer III',
|
|
type: 'ship',
|
|
rarity: 'rare',
|
|
price: 55000,
|
|
currency: 'credits',
|
|
description: 'Elite light destroyer with superior agility',
|
|
texture: 'assets/textures/ships/light_destroyer.png',
|
|
stats: { attack: 26, speed: 26, defense: 15, hull: 115 }
|
|
},
|
|
{
|
|
id: 'light_destroyer_epic',
|
|
name: 'Light Destroyer IV',
|
|
type: 'ship',
|
|
rarity: 'epic',
|
|
price: 95000,
|
|
currency: 'credits',
|
|
description: 'Master light destroyer with exceptional speed',
|
|
texture: 'assets/textures/ships/light_destroyer.png',
|
|
stats: { attack: 30, speed: 30, defense: 18, hull: 140 }
|
|
},
|
|
{
|
|
id: 'light_destroyer_legendary',
|
|
name: 'Light Destroyer V',
|
|
type: 'ship',
|
|
rarity: 'legendary',
|
|
price: 150000,
|
|
currency: 'credits',
|
|
description: 'Legendary light destroyer with unmatched velocity',
|
|
texture: 'assets/textures/ships/light_destroyer.png',
|
|
stats: { attack: 36, speed: 36, defense: 22, hull: 170 }
|
|
},
|
|
|
|
// Heavy Destroyer Variants
|
|
{
|
|
id: 'heavy_destroyer_common',
|
|
name: 'Heavy Destroyer',
|
|
type: 'ship',
|
|
rarity: 'common',
|
|
price: 25000,
|
|
currency: 'credits',
|
|
description: 'Powerful heavy destroyer with strong weapons',
|
|
texture: 'assets/textures/ships/heavy_destroyer.png',
|
|
stats: { attack: 25, speed: 12, defense: 18, hull: 120 }
|
|
},
|
|
{
|
|
id: 'heavy_destroyer_uncommon',
|
|
name: 'Heavy Destroyer II',
|
|
type: 'ship',
|
|
rarity: 'uncommon',
|
|
price: 55000,
|
|
currency: 'credits',
|
|
description: 'Upgraded heavy destroyer with enhanced firepower',
|
|
texture: 'assets/textures/ships/heavy_destroyer.png',
|
|
stats: { attack: 30, speed: 14, defense: 22, hull: 145 }
|
|
},
|
|
{
|
|
id: 'heavy_destroyer_rare',
|
|
name: 'Heavy Destroyer III',
|
|
type: 'ship',
|
|
rarity: 'rare',
|
|
price: 110000,
|
|
currency: 'credits',
|
|
description: 'Elite heavy destroyer with devastating weapons',
|
|
texture: 'assets/textures/ships/heavy_destroyer.png',
|
|
stats: { attack: 35, speed: 16, defense: 26, hull: 175 }
|
|
},
|
|
{
|
|
id: 'heavy_destroyer_epic',
|
|
name: 'Heavy Destroyer IV',
|
|
type: 'ship',
|
|
rarity: 'epic',
|
|
price: 190000,
|
|
currency: 'credits',
|
|
description: 'Master heavy destroyer with overwhelming power',
|
|
texture: 'assets/textures/ships/heavy_destroyer.png',
|
|
stats: { attack: 40, speed: 18, defense: 30, hull: 210 }
|
|
},
|
|
{
|
|
id: 'heavy_destroyer_legendary',
|
|
name: 'Heavy Destroyer V',
|
|
type: 'ship',
|
|
rarity: 'legendary',
|
|
price: 300000,
|
|
currency: 'credits',
|
|
description: 'Legendary heavy destroyer with ultimate destruction',
|
|
texture: 'assets/textures/ships/heavy_destroyer.png',
|
|
stats: { attack: 48, speed: 20, defense: 36, hull: 255 }
|
|
},
|
|
|
|
// Heavy Cruiser Variants
|
|
{
|
|
id: 'heavy_cruiser_common',
|
|
name: 'Heavy Cruiser',
|
|
type: 'ship',
|
|
rarity: 'common',
|
|
price: 45000,
|
|
currency: 'credits',
|
|
description: 'Massive heavy cruiser with excellent defense',
|
|
texture: 'assets/textures/ships/heavy_cruiser.png',
|
|
stats: { attack: 22, speed: 8, defense: 25, hull: 150 }
|
|
},
|
|
{
|
|
id: 'heavy_cruiser_uncommon',
|
|
name: 'Heavy Cruiser II',
|
|
type: 'ship',
|
|
rarity: 'uncommon',
|
|
price: 95000,
|
|
currency: 'credits',
|
|
description: 'Enhanced heavy cruiser with superior armor',
|
|
texture: 'assets/textures/ships/heavy_cruiser.png',
|
|
stats: { attack: 26, speed: 9, defense: 30, hull: 180 }
|
|
},
|
|
{
|
|
id: 'heavy_cruiser_rare',
|
|
name: 'Heavy Cruiser III',
|
|
type: 'ship',
|
|
rarity: 'rare',
|
|
price: 190000,
|
|
currency: 'credits',
|
|
description: 'Elite heavy cruiser with fortress-like defense',
|
|
texture: 'assets/textures/ships/heavy_cruiser.png',
|
|
stats: { attack: 30, speed: 10, defense: 36, hull: 220 }
|
|
},
|
|
{
|
|
id: 'heavy_cruiser_epic',
|
|
name: 'Heavy Cruiser IV',
|
|
type: 'ship',
|
|
rarity: 'epic',
|
|
price: 320000,
|
|
currency: 'credits',
|
|
description: 'Master heavy cruiser with impenetrable armor',
|
|
texture: 'assets/textures/ships/heavy_cruiser.png',
|
|
stats: { attack: 34, speed: 11, defense: 42, hull: 265 }
|
|
},
|
|
{
|
|
id: 'heavy_cruiser_legendary',
|
|
name: 'Heavy Cruiser V',
|
|
type: 'ship',
|
|
rarity: 'legendary',
|
|
price: 500000,
|
|
currency: 'credits',
|
|
description: 'Legendary heavy cruiser with ultimate defense',
|
|
texture: 'assets/textures/ships/heavy_cruiser.png',
|
|
stats: { attack: 40, speed: 12, defense: 50, hull: 320 }
|
|
}
|
|
],
|
|
weapons: [
|
|
// Starter Blaster Variants
|
|
{
|
|
id: 'starter_blaster_common',
|
|
name: 'Starter Blaster',
|
|
type: 'weapon',
|
|
rarity: 'common',
|
|
price: 2000,
|
|
currency: 'credits',
|
|
description: 'Basic blaster for new pilots',
|
|
texture: 'assets/textures/weapons/starter_blaster.png',
|
|
stats: { damage: 10, fireRate: 2, range: 5, energy: 5 }
|
|
},
|
|
{
|
|
id: 'starter_blaster_uncommon',
|
|
name: 'Starter Blaster II',
|
|
type: 'weapon',
|
|
rarity: 'uncommon',
|
|
price: 5000,
|
|
currency: 'credits',
|
|
description: 'Enhanced starter blaster with better fire rate',
|
|
texture: 'assets/textures/weapons/starter_blaster.png',
|
|
stats: { damage: 12, fireRate: 2.5, range: 5.5, energy: 6 }
|
|
},
|
|
{
|
|
id: 'starter_blaster_rare',
|
|
name: 'Starter Blaster III',
|
|
type: 'weapon',
|
|
rarity: 'rare',
|
|
price: 12000,
|
|
currency: 'credits',
|
|
description: 'Elite starter blaster with improved damage',
|
|
texture: 'assets/textures/weapons/starter_blaster.png',
|
|
stats: { damage: 15, fireRate: 3, range: 6, energy: 7 }
|
|
},
|
|
{
|
|
id: 'starter_blaster_epic',
|
|
name: 'Starter Blaster IV',
|
|
type: 'weapon',
|
|
rarity: 'epic',
|
|
price: 25000,
|
|
currency: 'credits',
|
|
description: 'Master starter blaster with superior performance',
|
|
texture: 'assets/textures/weapons/starter_blaster.png',
|
|
stats: { damage: 18, fireRate: 3.5, range: 6.5, energy: 8 }
|
|
},
|
|
{
|
|
id: 'starter_blaster_legendary',
|
|
name: 'Starter Blaster V',
|
|
type: 'weapon',
|
|
rarity: 'legendary',
|
|
price: 50000,
|
|
currency: 'credits',
|
|
description: 'Legendary starter blaster with ultimate power',
|
|
texture: 'assets/textures/weapons/starter_blaster.png',
|
|
stats: { damage: 22, fireRate: 4, range: 7, energy: 10 }
|
|
},
|
|
|
|
// Laser Pistol Variants
|
|
{
|
|
id: 'laser_pistol_common',
|
|
name: 'Laser Pistol',
|
|
type: 'weapon',
|
|
rarity: 'common',
|
|
price: 3000,
|
|
currency: 'credits',
|
|
description: 'Compact laser pistol for close combat',
|
|
texture: 'assets/textures/weapons/laser_pistol.png',
|
|
stats: { damage: 8, fireRate: 3, range: 4, energy: 3 }
|
|
},
|
|
{
|
|
id: 'laser_pistol_uncommon',
|
|
name: 'Laser Pistol II',
|
|
type: 'weapon',
|
|
rarity: 'uncommon',
|
|
price: 7500,
|
|
currency: 'credits',
|
|
description: 'Enhanced laser pistol with better accuracy',
|
|
texture: 'assets/textures/weapons/laser_pistol.png',
|
|
stats: { damage: 10, fireRate: 3.5, range: 4.5, energy: 4 }
|
|
},
|
|
{
|
|
id: 'laser_pistol_rare',
|
|
name: 'Laser Pistol III',
|
|
type: 'weapon',
|
|
rarity: 'rare',
|
|
price: 18000,
|
|
currency: 'credits',
|
|
description: 'Elite laser pistol with piercing shots',
|
|
texture: 'assets/textures/weapons/laser_pistol.png',
|
|
stats: { damage: 13, fireRate: 4, range: 5, energy: 5 }
|
|
},
|
|
{
|
|
id: 'laser_pistol_epic',
|
|
name: 'Laser Pistol IV',
|
|
type: 'weapon',
|
|
rarity: 'epic',
|
|
price: 35000,
|
|
currency: 'credits',
|
|
description: 'Master laser pistol with rapid fire capability',
|
|
texture: 'assets/textures/weapons/laser_pistol.png',
|
|
stats: { damage: 16, fireRate: 4.5, range: 5.5, energy: 6 }
|
|
},
|
|
{
|
|
id: 'laser_pistol_legendary',
|
|
name: 'Laser Pistol V',
|
|
type: 'weapon',
|
|
rarity: 'legendary',
|
|
price: 70000,
|
|
currency: 'credits',
|
|
description: 'Legendary laser pistol with devastating power',
|
|
texture: 'assets/textures/weapons/laser_pistol.png',
|
|
stats: { damage: 20, fireRate: 5, range: 6, energy: 8 }
|
|
},
|
|
|
|
// Laser Sniper Rifle Variants
|
|
{
|
|
id: 'laser_sniper_rifle_common',
|
|
name: 'Laser Sniper Rifle',
|
|
type: 'weapon',
|
|
rarity: 'common',
|
|
price: 8000,
|
|
currency: 'credits',
|
|
description: 'Long-range laser sniper rifle for precision attacks',
|
|
texture: 'assets/textures/weapons/laser_sniper_rifle.png',
|
|
stats: { damage: 25, fireRate: 1, range: 10, energy: 8 }
|
|
},
|
|
{
|
|
id: 'laser_sniper_rifle_uncommon',
|
|
name: 'Laser Sniper Rifle II',
|
|
type: 'weapon',
|
|
rarity: 'uncommon',
|
|
price: 20000,
|
|
currency: 'credits',
|
|
description: 'Enhanced laser sniper rifle with better penetration',
|
|
texture: 'assets/textures/weapons/laser_sniper_rifle.png',
|
|
stats: { damage: 30, fireRate: 1.2, range: 11, energy: 9 }
|
|
},
|
|
{
|
|
id: 'laser_sniper_rifle_rare',
|
|
name: 'Laser Sniper Rifle III',
|
|
type: 'weapon',
|
|
rarity: 'rare',
|
|
price: 50000,
|
|
currency: 'credits',
|
|
description: 'Elite laser sniper rifle with deadly accuracy',
|
|
texture: 'assets/textures/weapons/laser_sniper_rifle.png',
|
|
stats: { damage: 36, fireRate: 1.4, range: 12, energy: 10 }
|
|
},
|
|
{
|
|
id: 'laser_sniper_rifle_epic',
|
|
name: 'Laser Sniper Rifle IV',
|
|
type: 'weapon',
|
|
rarity: 'epic',
|
|
price: 100000,
|
|
currency: 'credits',
|
|
description: 'Master laser sniper rifle with extreme range',
|
|
texture: 'assets/textures/weapons/laser_sniper_rifle.png',
|
|
stats: { damage: 42, fireRate: 1.6, range: 13, energy: 12 }
|
|
},
|
|
{
|
|
id: 'laser_sniper_rifle_legendary',
|
|
name: 'Laser Sniper Rifle V',
|
|
type: 'weapon',
|
|
rarity: 'legendary',
|
|
price: 200000,
|
|
currency: 'credits',
|
|
description: 'Legendary laser sniper rifle with unparalleled precision',
|
|
texture: 'assets/textures/weapons/laser_sniper_rifle.png',
|
|
stats: { damage: 50, fireRate: 2, range: 15, energy: 15 }
|
|
}
|
|
],
|
|
armors: [
|
|
// Basic Armor Variants
|
|
{
|
|
id: 'basic_armor_common',
|
|
name: 'Basic Armor',
|
|
type: 'armor',
|
|
rarity: 'common',
|
|
price: 1500,
|
|
currency: 'credits',
|
|
description: 'Light protection for beginners',
|
|
texture: 'assets/textures/armors/basic_armor.png',
|
|
stats: { defense: 5, durability: 20, weight: 2, energyShield: 0 }
|
|
},
|
|
{
|
|
id: 'basic_armor_uncommon',
|
|
name: 'Basic Armor II',
|
|
type: 'armor',
|
|
rarity: 'uncommon',
|
|
price: 4000,
|
|
currency: 'credits',
|
|
description: 'Improved basic armor with better durability',
|
|
texture: 'assets/textures/armors/basic_armor.png',
|
|
stats: { defense: 7, durability: 25, weight: 2.2, energyShield: 2 }
|
|
},
|
|
{
|
|
id: 'basic_armor_rare',
|
|
name: 'Basic Armor III',
|
|
type: 'armor',
|
|
rarity: 'rare',
|
|
price: 10000,
|
|
currency: 'credits',
|
|
description: 'Elite basic armor with energy shielding',
|
|
texture: 'assets/textures/armors/basic_armor.png',
|
|
stats: { defense: 10, durability: 30, weight: 2.5, energyShield: 5 }
|
|
},
|
|
{
|
|
id: 'basic_armor_epic',
|
|
name: 'Basic Armor IV',
|
|
type: 'armor',
|
|
rarity: 'epic',
|
|
price: 20000,
|
|
currency: 'credits',
|
|
description: 'Master basic armor with advanced protection',
|
|
texture: 'assets/textures/armors/basic_armor.png',
|
|
stats: { defense: 13, durability: 35, weight: 2.8, energyShield: 8 }
|
|
},
|
|
{
|
|
id: 'basic_armor_legendary',
|
|
name: 'Basic Armor V',
|
|
type: 'armor',
|
|
rarity: 'legendary',
|
|
price: 40000,
|
|
currency: 'credits',
|
|
description: 'Legendary basic armor with ultimate defense',
|
|
texture: 'assets/textures/armors/basic_armor.png',
|
|
stats: { defense: 17, durability: 40, weight: 3, energyShield: 12 }
|
|
},
|
|
|
|
// Medium Armor Variants
|
|
{
|
|
id: 'medium_armor_common',
|
|
name: 'Medium Armor',
|
|
type: 'armor',
|
|
rarity: 'common',
|
|
price: 5000,
|
|
currency: 'credits',
|
|
description: 'Balanced armor for general combat',
|
|
texture: 'assets/textures/armors/medium_armor.png',
|
|
stats: { defense: 12, durability: 40, weight: 5, energyShield: 3 }
|
|
},
|
|
{
|
|
id: 'medium_armor_uncommon',
|
|
name: 'Medium Armor II',
|
|
type: 'armor',
|
|
rarity: 'uncommon',
|
|
price: 12000,
|
|
currency: 'credits',
|
|
description: 'Enhanced medium armor with better shielding',
|
|
texture: 'assets/textures/armors/medium_armor.png',
|
|
stats: { defense: 15, durability: 50, weight: 5.5, energyShield: 6 }
|
|
},
|
|
{
|
|
id: 'medium_armor_rare',
|
|
name: 'Medium Armor III',
|
|
type: 'armor',
|
|
rarity: 'rare',
|
|
price: 30000,
|
|
currency: 'credits',
|
|
description: 'Elite medium armor with advanced systems',
|
|
texture: 'assets/textures/armors/medium_armor.png',
|
|
stats: { defense: 19, durability: 60, weight: 6, energyShield: 10 }
|
|
},
|
|
{
|
|
id: 'medium_armor_epic',
|
|
name: 'Medium Armor IV',
|
|
type: 'armor',
|
|
rarity: 'epic',
|
|
price: 60000,
|
|
currency: 'credits',
|
|
description: 'Master medium armor with superior protection',
|
|
texture: 'assets/textures/armors/medium_armor.png',
|
|
stats: { defense: 23, durability: 70, weight: 6.5, energyShield: 15 }
|
|
},
|
|
{
|
|
id: 'medium_armor_legendary',
|
|
name: 'Medium Armor V',
|
|
type: 'armor',
|
|
rarity: 'legendary',
|
|
price: 100000,
|
|
currency: 'credits',
|
|
description: 'Legendary medium armor with ultimate defense',
|
|
texture: 'assets/textures/armors/medium_armor.png',
|
|
stats: { defense: 28, durability: 80, weight: 7, energyShield: 20 }
|
|
},
|
|
|
|
// Heavy Armor Variants
|
|
{
|
|
id: 'heavy_armor_common',
|
|
name: 'Heavy Armor',
|
|
type: 'armor',
|
|
rarity: 'common',
|
|
price: 10000,
|
|
currency: 'credits',
|
|
description: 'Maximum protection with increased weight',
|
|
texture: 'assets/textures/armors/heavy_armor.png',
|
|
stats: { defense: 20, durability: 60, weight: 8, energyShield: 5 }
|
|
},
|
|
{
|
|
id: 'heavy_armor_uncommon',
|
|
name: 'Heavy Armor II',
|
|
type: 'armor',
|
|
rarity: 'uncommon',
|
|
price: 25000,
|
|
currency: 'credits',
|
|
description: 'Upgraded heavy armor with better energy shielding',
|
|
texture: 'assets/textures/armors/heavy_armor.png',
|
|
stats: { defense: 25, durability: 75, weight: 9, energyShield: 10 }
|
|
},
|
|
{
|
|
id: 'heavy_armor_rare',
|
|
name: 'Heavy Armor III',
|
|
type: 'armor',
|
|
rarity: 'rare',
|
|
price: 60000,
|
|
currency: 'credits',
|
|
description: 'Elite heavy armor with advanced protection systems',
|
|
texture: 'assets/textures/armors/heavy_armor.png',
|
|
stats: { defense: 31, durability: 90, weight: 10, energyShield: 16 }
|
|
},
|
|
{
|
|
id: 'heavy_armor_epic',
|
|
name: 'Heavy Armor IV',
|
|
type: 'armor',
|
|
rarity: 'epic',
|
|
price: 120000,
|
|
currency: 'credits',
|
|
description: 'Master heavy armor with superior defense capabilities',
|
|
texture: 'assets/textures/armors/heavy_armor.png',
|
|
stats: { defense: 37, durability: 105, weight: 11, energyShield: 23 }
|
|
},
|
|
{
|
|
id: 'heavy_armor_legendary',
|
|
name: 'Heavy Armor V',
|
|
type: 'armor',
|
|
rarity: 'legendary',
|
|
price: 200000,
|
|
currency: 'credits',
|
|
description: 'Legendary heavy armor with ultimate protection',
|
|
texture: 'assets/textures/armors/heavy_armor.png',
|
|
stats: { defense: 45, durability: 120, weight: 12, energyShield: 30 }
|
|
}
|
|
],
|
|
cosmetics: [
|
|
{
|
|
id: 'blue_paint',
|
|
name: 'Blue Paint Job',
|
|
type: 'cosmetic',
|
|
rarity: 'common',
|
|
price: 100,
|
|
currency: 'gems',
|
|
description: 'Custom blue paint for your ship'
|
|
},
|
|
{
|
|
id: 'golden_trim',
|
|
name: 'Golden Trim',
|
|
type: 'cosmetic',
|
|
rarity: 'rare',
|
|
price: 500,
|
|
currency: 'gems',
|
|
description: 'Luxurious golden trim for your ship'
|
|
}
|
|
],
|
|
consumables: [
|
|
{
|
|
id: 'health_kit',
|
|
name: 'Health Kit',
|
|
type: 'consumable',
|
|
rarity: 'common',
|
|
price: 15,
|
|
currency: 'credits',
|
|
description: 'Restores 50 health points',
|
|
texture: 'assets/textures/items/health_pack.png',
|
|
effect: { heal: 50 }
|
|
},
|
|
{
|
|
id: 'mega_health_kit',
|
|
name: 'Mega Health Kit',
|
|
type: 'consumable',
|
|
rarity: 'uncommon',
|
|
price: 50,
|
|
currency: 'credits',
|
|
description: 'Restores full health',
|
|
texture: 'assets/textures/items/mega_health_pack.png',
|
|
effect: { heal: 999 }
|
|
}
|
|
],
|
|
buildings: [
|
|
{
|
|
id: 'command_center',
|
|
name: 'Command Center',
|
|
type: 'building',
|
|
rarity: 'uncommon',
|
|
price: 50000,
|
|
currency: 'credits',
|
|
description: 'Central command facility for base operations and coordination',
|
|
texture: 'assets/textures/base/command_center.png',
|
|
stats: { command: 10, efficiency: 15, capacity: 20 }
|
|
},
|
|
{
|
|
id: 'mining_facility',
|
|
name: 'Mining Facility',
|
|
type: 'building',
|
|
rarity: 'common',
|
|
price: 25000,
|
|
currency: 'credits',
|
|
description: 'Automated mining facility for resource extraction and processing',
|
|
texture: 'assets/textures/base/mining_facility.png',
|
|
stats: { production: 12, efficiency: 8, storage: 15 }
|
|
}
|
|
],
|
|
materials: [
|
|
{
|
|
id: 'iron_ore',
|
|
name: 'Iron Ore',
|
|
type: 'material',
|
|
rarity: 'common',
|
|
price: 10,
|
|
currency: 'credits',
|
|
description: 'Basic metal ore used for crafting weapons and armor',
|
|
texture: 'assets/textures/items/iron_ore.png',
|
|
stackable: true
|
|
},
|
|
{
|
|
id: 'copper_ore',
|
|
name: 'Copper Ore',
|
|
type: 'material',
|
|
rarity: 'common',
|
|
price: 8,
|
|
currency: 'credits',
|
|
description: 'Conductive metal ore used for wiring and electronics',
|
|
texture: 'assets/textures/items/copper_ore.png',
|
|
stackable: true
|
|
},
|
|
{
|
|
id: 'tin_bar',
|
|
name: 'Tin Bar',
|
|
type: 'material',
|
|
rarity: 'common',
|
|
price: 12,
|
|
currency: 'credits',
|
|
description: 'Refined tin bar used for alloys and soldering',
|
|
texture: 'assets/textures/items/tin_bar.png',
|
|
stackable: true
|
|
},
|
|
{
|
|
id: 'copper_wire',
|
|
name: 'Copper Wire',
|
|
type: 'material',
|
|
rarity: 'common',
|
|
price: 8,
|
|
currency: 'credits',
|
|
description: 'Conductive wiring for electronic components',
|
|
texture: 'assets/textures/items/copper_wire.png',
|
|
stackable: true
|
|
},
|
|
{
|
|
id: 'energy_crystal',
|
|
name: 'Energy Crystal',
|
|
type: 'material',
|
|
rarity: 'uncommon',
|
|
price: 25,
|
|
currency: 'credits',
|
|
description: 'Crystallized energy source for power systems',
|
|
texture: 'assets/textures/items/energy_crystal.png',
|
|
stackable: true
|
|
},
|
|
{
|
|
id: 'leather',
|
|
name: 'Leather',
|
|
type: 'material',
|
|
rarity: 'common',
|
|
price: 12,
|
|
currency: 'credits',
|
|
description: 'Durable material for crafting armor and accessories',
|
|
texture: 'assets/textures/items/leather.png',
|
|
stackable: true
|
|
},
|
|
{
|
|
id: 'herbs',
|
|
name: 'Herbs',
|
|
type: 'material',
|
|
rarity: 'common',
|
|
price: 5,
|
|
currency: 'credits',
|
|
description: 'Medicinal herbs used for healing items',
|
|
texture: 'assets/textures/items/herbs.png',
|
|
stackable: true
|
|
},
|
|
{
|
|
id: 'bandages',
|
|
name: 'Bandages',
|
|
type: 'material',
|
|
rarity: 'common',
|
|
price: 3,
|
|
currency: 'credits',
|
|
description: 'Medical bandages used for crafting healing items',
|
|
texture: 'assets/textures/items/bandages.png',
|
|
stackable: true
|
|
},
|
|
{
|
|
id: 'steel_plate',
|
|
name: 'Steel Plate',
|
|
type: 'material',
|
|
rarity: 'uncommon',
|
|
price: 30,
|
|
currency: 'credits',
|
|
description: 'Reinforced steel plates used for advanced armor and ship components',
|
|
texture: 'assets/textures/items/stell_plate.png',
|
|
stackable: true
|
|
},
|
|
{
|
|
id: 'advanced_component',
|
|
name: 'Advanced Component',
|
|
type: 'material',
|
|
rarity: 'rare',
|
|
price: 75,
|
|
currency: 'credits',
|
|
description: 'High-tech component used for advanced equipment and upgrades',
|
|
texture: 'assets/textures/items/advanced_component.png',
|
|
stackable: true
|
|
},
|
|
{
|
|
id: 'advanced_components',
|
|
name: 'Advanced Components Set',
|
|
type: 'material',
|
|
rarity: 'epic',
|
|
price: 150,
|
|
currency: 'credits',
|
|
description: 'Complete set of advanced components for high-end manufacturing',
|
|
texture: 'assets/textures/items/advanced_components.png',
|
|
stackable: true
|
|
},
|
|
{
|
|
id: 'basic_circuitboard',
|
|
name: 'Basic Circuit Board',
|
|
type: 'material',
|
|
rarity: 'common',
|
|
price: 20,
|
|
currency: 'credits',
|
|
description: 'Basic electronic circuit board for simple devices',
|
|
texture: 'assets/textures/items/basic_circuitboard.png',
|
|
stackable: true
|
|
},
|
|
{
|
|
id: 'common_circuitboard',
|
|
name: 'Common Circuit Board',
|
|
type: 'material',
|
|
rarity: 'common',
|
|
price: 35,
|
|
currency: 'credits',
|
|
description: 'Standard circuit board for most electronic equipment',
|
|
texture: 'assets/textures/items/common_circuitboard.png',
|
|
stackable: true
|
|
},
|
|
{
|
|
id: 'advanced_circuitboard',
|
|
name: 'Advanced Circuit Board',
|
|
type: 'material',
|
|
rarity: 'rare',
|
|
price: 100,
|
|
currency: 'credits',
|
|
description: 'High-performance circuit board for advanced systems',
|
|
texture: 'assets/textures/items/advanced_circuitboard.png',
|
|
stackable: true
|
|
},
|
|
{
|
|
id: 'battery',
|
|
name: 'Battery',
|
|
type: 'material',
|
|
rarity: 'uncommon',
|
|
price: 35,
|
|
currency: 'credits',
|
|
description: 'Power batteries used for energy-based equipment',
|
|
texture: 'assets/textures/items/battery.png',
|
|
stackable: true
|
|
},
|
|
{
|
|
id: 'advanced_components',
|
|
name: 'Advanced Components',
|
|
type: 'material',
|
|
rarity: 'rare',
|
|
price: 150,
|
|
currency: 'credits',
|
|
description: 'Sophisticated electronic components for advanced ship systems',
|
|
stackable: true
|
|
}
|
|
]
|
|
};
|
|
|
|
// Owned cosmetics
|
|
this.ownedCosmetics = [];
|
|
|
|
if (debugLogger) debugLogger.log('Economy constructor completed', {
|
|
initialCredits: this.credits,
|
|
initialGems: this.gems,
|
|
shopCategoriesCount: Object.keys(this.shopCategories).length,
|
|
totalShopItems: Object.values(this.shopItems).reduce((total, category) => total + category.length, 0)
|
|
});
|
|
}
|
|
|
|
async initialize() {
|
|
const debugLogger = window.debugLogger;
|
|
console.log('[ECONOMY] Economy system initializing');
|
|
if (debugLogger) await debugLogger.startStep('economyInitialize');
|
|
|
|
try {
|
|
if (debugLogger) await debugLogger.logStep('Economy initialization started', {
|
|
currentCredits: this.credits,
|
|
currentGems: this.gems,
|
|
transactionHistoryLength: this.transactionHistory.length
|
|
});
|
|
|
|
// Setup shop purchase event listeners
|
|
this.setupShopEventListeners();
|
|
|
|
// Initialize random shop system
|
|
this.initializeRandomShop();
|
|
|
|
console.log('[ECONOMY] Economy system initialization completed');
|
|
if (debugLogger) await debugLogger.endStep('economyInitialize');
|
|
} catch (error) {
|
|
console.error('[ECONOMY] Error during initialization:', error);
|
|
if (debugLogger) await debugLogger.errorEvent(error, 'Economy Initialize');
|
|
}
|
|
}
|
|
|
|
initializeRandomShop() {
|
|
const debugLogger = window.debugLogger;
|
|
console.log('[ECONOMY] Initializing random shop system');
|
|
|
|
// Shop data is now loaded through the main save/load system in load()
|
|
// Only initialize if no shop data exists (new game)
|
|
if (!this.lastShopRefresh || Object.keys(this.randomShopItems).length === 0) {
|
|
console.log('[ECONOMY] No shop data found, generating new shop');
|
|
this.refreshRandomShop();
|
|
} else {
|
|
console.log('[ECONOMY] Shop data already loaded from save');
|
|
// Start the refresh timer for ongoing updates
|
|
this.startShopRefreshTimer();
|
|
}
|
|
|
|
if (debugLogger) debugLogger.logStep('Random shop system initialized', {
|
|
hasShopData: Object.keys(this.randomShopItems).length > 0,
|
|
lastRefresh: this.lastShopRefresh
|
|
});
|
|
}
|
|
|
|
refreshRandomShop() {
|
|
const debugLogger = window.debugLogger;
|
|
console.log('[ECONOMY] Refreshing random shop items');
|
|
|
|
const categories = Object.keys(this.shopCategories);
|
|
this.randomShopItems = {};
|
|
|
|
categories.forEach(category => {
|
|
const categoryItems = this.shopItems[category] || [];
|
|
if (categoryItems.length === 0) return;
|
|
|
|
// Group items by their base ID (remove tier suffixes)
|
|
const baseItemGroups = {};
|
|
categoryItems.forEach(item => {
|
|
// Extract base ID by removing tier suffixes (common, uncommon, rare, epic, legendary)
|
|
const baseId = item.id.replace(/_(common|uncommon|rare|epic|legendary)$/, '');
|
|
if (!baseItemGroups[baseId]) {
|
|
baseItemGroups[baseId] = [];
|
|
}
|
|
baseItemGroups[baseId].push(item);
|
|
});
|
|
|
|
// Get all unique base items
|
|
const uniqueBaseItems = Object.keys(baseItemGroups);
|
|
|
|
// Randomly select up to MAX_ITEMS_PER_CATEGORY base items
|
|
const shuffledBaseItems = [...uniqueBaseItems].sort(() => Math.random() - 0.5);
|
|
const selectedBaseItems = shuffledBaseItems.slice(0, Math.min(this.MAX_ITEMS_PER_CATEGORY, shuffledBaseItems.length));
|
|
|
|
// For each selected base item, randomly pick one tier variant
|
|
this.randomShopItems[category] = selectedBaseItems.map(baseId => {
|
|
const variants = baseItemGroups[baseId];
|
|
const selectedVariant = variants[Math.floor(Math.random() * variants.length)];
|
|
|
|
return {
|
|
...selectedVariant,
|
|
id: `${selectedVariant.id}_random_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
originalId: selectedVariant.id,
|
|
baseId: baseId,
|
|
price: Math.round(selectedVariant.price * (0.8 + Math.random() * 0.4)), // ±20% price variation
|
|
isRandomItem: true,
|
|
refreshTimestamp: Date.now()
|
|
};
|
|
});
|
|
|
|
// Reset purchase limits for this category
|
|
this.categoryPurchaseLimits[category] = 0;
|
|
|
|
if (debugLogger) debugLogger.logStep('Random shop category refreshed', {
|
|
category: category,
|
|
totalBaseItems: uniqueBaseItems.length,
|
|
baseItemsSelected: selectedBaseItems.length,
|
|
itemsGenerated: this.randomShopItems[category].length,
|
|
selectedVariants: this.randomShopItems[category].map(item => ({
|
|
baseId: item.baseId,
|
|
variantId: item.originalId,
|
|
rarity: item.rarity,
|
|
name: item.name
|
|
})),
|
|
purchaseLimitReset: true
|
|
});
|
|
});
|
|
|
|
this.lastShopRefresh = Date.now();
|
|
this.saveRandomShopData();
|
|
this.startShopRefreshTimer();
|
|
|
|
// Update UI if shop tab is active
|
|
this.updateShopUI();
|
|
|
|
this.game.showNotification('Shop inventory refreshed!', 'info', 3000);
|
|
|
|
if (debugLogger) debugLogger.logStep('Random shop refresh completed', {
|
|
categoriesRefreshed: categories.length,
|
|
totalItemsGenerated: Object.values(this.randomShopItems).flat().length,
|
|
nextRefresh: new Date(this.lastShopRefresh + this.SHOP_REFRESH_INTERVAL)
|
|
});
|
|
}
|
|
|
|
startShopRefreshTimer() {
|
|
// Clear existing timers
|
|
if (this.shopRefreshInterval) {
|
|
clearInterval(this.shopRefreshInterval);
|
|
}
|
|
if (this.shopHeartbeatInterval) {
|
|
clearInterval(this.shopHeartbeatInterval);
|
|
}
|
|
|
|
// Set up heartbeat timer for live countdown updates (every second)
|
|
this.shopHeartbeatInterval = setInterval(() => {
|
|
this.updateShopCountdown();
|
|
}, 1000);
|
|
|
|
// Set up refresh timer (every 2 hours)
|
|
this.shopRefreshInterval = setInterval(() => {
|
|
this.refreshRandomShop();
|
|
}, this.SHOP_REFRESH_INTERVAL);
|
|
|
|
console.log('[ECONOMY] Shop refresh timers started');
|
|
}
|
|
|
|
updateShopCountdown() {
|
|
// Only update if shop tab is active and using random shop
|
|
const shopTab = document.getElementById('shop-tab');
|
|
if (!shopTab || shopTab.style.display === 'none') {
|
|
return;
|
|
}
|
|
|
|
const activeCategory = document.querySelector('.shop-cat-btn.active')?.dataset.category || 'ships';
|
|
if (!this.randomShopItems[activeCategory]) {
|
|
return;
|
|
}
|
|
|
|
// Find and update the countdown element
|
|
const countdownElement = document.querySelector('.refresh-countdown');
|
|
if (countdownElement) {
|
|
countdownElement.innerHTML = `
|
|
<i class="fas fa-clock"></i>
|
|
Next refresh in: ${this.getShopRefreshCountdown()}
|
|
`;
|
|
}
|
|
}
|
|
|
|
stopShopRefreshTimer() {
|
|
if (this.shopRefreshInterval) {
|
|
clearInterval(this.shopRefreshInterval);
|
|
this.shopRefreshInterval = null;
|
|
}
|
|
if (this.shopHeartbeatInterval) {
|
|
clearInterval(this.shopHeartbeatInterval);
|
|
this.shopHeartbeatInterval = null;
|
|
}
|
|
console.log('[ECONOMY] Shop refresh timers stopped');
|
|
}
|
|
|
|
loadRandomShopData() {
|
|
try {
|
|
const savedData = localStorage.getItem('gso_random_shop');
|
|
if (savedData) {
|
|
const data = JSON.parse(savedData);
|
|
this.randomShopItems = data.randomShopItems || {};
|
|
this.categoryPurchaseLimits = data.categoryPurchaseLimits || {};
|
|
this.lastShopRefresh = data.lastShopRefresh || null;
|
|
console.log('[ECONOMY] Random shop data loaded from localStorage');
|
|
}
|
|
} catch (error) {
|
|
console.error('[ECONOMY] Error loading random shop data:', error);
|
|
this.randomShopItems = {};
|
|
this.categoryPurchaseLimits = {};
|
|
this.lastShopRefresh = null;
|
|
}
|
|
}
|
|
saveRandomShopData() {
|
|
try {
|
|
const data = {
|
|
randomShopItems: this.randomShopItems,
|
|
categoryPurchaseLimits: this.categoryPurchaseLimits,
|
|
lastShopRefresh: this.lastShopRefresh
|
|
};
|
|
localStorage.setItem('gso_random_shop', JSON.stringify(data));
|
|
console.log('[ECONOMY] Random shop data saved to localStorage');
|
|
} catch (error) {
|
|
console.error('[ECONOMY] Error saving random shop data:', error);
|
|
}
|
|
}
|
|
|
|
shouldRefreshShop() {
|
|
// Check if enough time has passed since the last actual refresh
|
|
if (!this.lastShopRefresh) {
|
|
return true; // No previous refresh, need to refresh
|
|
}
|
|
|
|
const now = Date.now();
|
|
const timeSinceRefresh = now - this.lastShopRefresh;
|
|
return timeSinceRefresh >= this.SHOP_REFRESH_INTERVAL;
|
|
}
|
|
|
|
getShopRefreshCountdown() {
|
|
const schedule = this.getRefreshSchedule();
|
|
const now = Date.now();
|
|
|
|
// Calculate time until next server refresh
|
|
const timeUntilRefresh = schedule.nextRefresh.getTime() - now;
|
|
|
|
if (timeUntilRefresh <= 0) {
|
|
return 'Refreshing...';
|
|
}
|
|
|
|
return this.formatTimeRemaining(timeUntilRefresh);
|
|
}
|
|
|
|
getNextRefreshTime() {
|
|
// Calculate the next refresh time based on server time
|
|
const now = Date.now();
|
|
|
|
if (!this.lastShopRefresh) {
|
|
// If no last refresh, next refresh is now + interval
|
|
return new Date(now + this.SHOP_REFRESH_INTERVAL);
|
|
}
|
|
|
|
// Calculate next refresh time
|
|
const timeSinceRefresh = now - this.lastShopRefresh;
|
|
const intervalsPassed = Math.floor(timeSinceRefresh / this.SHOP_REFRESH_INTERVAL);
|
|
const nextRefreshTime = this.lastShopRefresh + ((intervalsPassed + 1) * this.SHOP_REFRESH_INTERVAL);
|
|
|
|
return new Date(nextRefreshTime);
|
|
}
|
|
|
|
getRefreshSchedule() {
|
|
// Generate a consistent refresh schedule based on server time
|
|
const now = new Date();
|
|
const currentHour = now.getHours();
|
|
|
|
// Refresh at even hours: 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22
|
|
let nextRefreshHour;
|
|
|
|
if (currentHour % 2 === 0 && now.getMinutes() === 0 && now.getSeconds() === 0) {
|
|
// Exactly on an even hour at :00:00, next refresh is in 2 hours
|
|
nextRefreshHour = currentHour + 2;
|
|
} else {
|
|
// Find the next even hour
|
|
nextRefreshHour = currentHour + (2 - (currentHour % 2));
|
|
}
|
|
|
|
// Handle midnight wrap-around
|
|
if (nextRefreshHour >= 24) {
|
|
nextRefreshHour = nextRefreshHour % 24;
|
|
}
|
|
|
|
const nextRefresh = new Date(now);
|
|
nextRefresh.setHours(nextRefreshHour, 0, 0, 0);
|
|
|
|
// If the calculated time is in the past (shouldn't happen but just in case), add 2 hours
|
|
if (nextRefresh <= now) {
|
|
nextRefresh.setHours(nextRefresh.getHours() + 2);
|
|
}
|
|
|
|
console.log('[ECONOMY] Current time:', now.toLocaleTimeString());
|
|
console.log('[ECONOMY] Next refresh at:', nextRefresh.toLocaleTimeString());
|
|
|
|
return {
|
|
nextRefresh: nextRefresh,
|
|
interval: 2 * 60 * 60 * 1000, // 2 hours
|
|
schedule: 'Every 2 hours at even hours (2,4,6,8,10,12,14,16,18,20,22,00)'
|
|
};
|
|
}
|
|
|
|
formatTimeRemaining(timeUntilRefresh) {
|
|
const hours = Math.floor(timeUntilRefresh / (1000 * 60 * 60));
|
|
const minutes = Math.floor((timeUntilRefresh % (1000 * 60 * 60)) / (1000 * 60));
|
|
const seconds = Math.floor((timeUntilRefresh % (1000 * 60)) / 1000);
|
|
|
|
if (hours > 0) {
|
|
return `${hours}h ${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
|
} else {
|
|
return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
|
}
|
|
}
|
|
|
|
getItemCategory(item) {
|
|
// Determine category based on item type
|
|
switch (item.type) {
|
|
case 'ship': return 'ships';
|
|
case 'weapon': return 'weapons';
|
|
case 'armor': return 'armors';
|
|
case 'material': return 'materials';
|
|
case 'cosmetic': return 'cosmetics';
|
|
case 'upgrade': return 'upgrades';
|
|
case 'consumable': return 'consumables';
|
|
case 'building': return 'buildings';
|
|
default:
|
|
// For random items, check if we can find them in shop categories
|
|
for (const [category, items] of Object.entries(this.shopItems)) {
|
|
if (items.some(shopItem => shopItem.id === item.originalId || shopItem.id === item.id)) {
|
|
return category;
|
|
}
|
|
}
|
|
return 'unknown';
|
|
}
|
|
}
|
|
|
|
setupShopEventListeners() {
|
|
const debugLogger = window.debugLogger;
|
|
console.log('[ECONOMY] Setting up shop event listeners');
|
|
|
|
// Remove existing listeners to prevent duplicates
|
|
const shopItemsElement = document.getElementById('shopItems');
|
|
if (shopItemsElement) {
|
|
// Clone the element to remove all event listeners
|
|
const newElement = shopItemsElement.cloneNode(true);
|
|
shopItemsElement.parentNode.replaceChild(newElement, shopItemsElement);
|
|
|
|
// Setup shop purchase button event delegation on the new element
|
|
newElement.addEventListener('click', (event) => {
|
|
const purchaseBtn = event.target.closest('.shop-item-purchase-btn');
|
|
if (purchaseBtn && !purchaseBtn.disabled) {
|
|
const itemId = purchaseBtn.getAttribute('data-item-id');
|
|
console.log(`[ECONOMY] Shop purchase button clicked for item: ${itemId}`);
|
|
|
|
if (itemId) {
|
|
this.purchaseItem(itemId, 1);
|
|
} else {
|
|
console.error('[ECONOMY] No item ID found on purchase button');
|
|
}
|
|
}
|
|
});
|
|
|
|
console.log('[ECONOMY] Shop event listeners setup completed');
|
|
} else {
|
|
console.error('[ECONOMY] Shop items element not found');
|
|
}
|
|
}
|
|
|
|
// Currency management
|
|
addCredits(amount, source = 'unknown') {
|
|
const oldCredits = this.credits;
|
|
this.credits += amount;
|
|
this.addTransaction('credits', amount, source);
|
|
|
|
console.log(`[ECONOMY] Added ${amount} credits from ${source}. New total: ${this.credits}`);
|
|
|
|
// Update UI to show new credit amount
|
|
if (this.game.systems.ui && this.game.shouldUpdateGUI()) {
|
|
this.game.systems.ui.updateUI();
|
|
}
|
|
|
|
this.game.showNotification(`+${this.game.formatNumber(amount)} credits`, 'success', 2000);
|
|
}
|
|
|
|
removeCredits(amount) {
|
|
const oldCredits = this.credits;
|
|
|
|
if (this.credits < amount) {
|
|
this.game.showNotification('Not enough credits!', 'error', 3000);
|
|
return false;
|
|
}
|
|
|
|
this.credits -= amount;
|
|
this.addTransaction('credits', -amount, 'purchase');
|
|
|
|
console.log(`[ECONOMY] Removed ${amount} credits. New total: ${this.credits}`);
|
|
|
|
// Update UI to show new credit amount
|
|
if (this.game.systems.ui && this.game.shouldUpdateGUI()) {
|
|
this.game.systems.ui.updateUI();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
addGems(amount, source = 'unknown') {
|
|
const oldGems = this.gems;
|
|
this.gems += amount;
|
|
this.addTransaction('gems', amount, source);
|
|
|
|
console.log(`[ECONOMY] Added ${amount} gems from ${source}. New total: ${this.gems}`);
|
|
|
|
// Update UI to show new gem amount
|
|
if (this.game.systems.ui && this.game.shouldUpdateGUI()) {
|
|
this.game.systems.ui.updateUI();
|
|
}
|
|
|
|
this.game.showNotification(`+${this.game.formatNumber(amount)} gems`, 'success', 2000);
|
|
}
|
|
|
|
removeGems(amount) {
|
|
const oldGems = this.gems;
|
|
|
|
if (this.gems < amount) {
|
|
this.game.showNotification('Not enough gems!', 'error', 3000);
|
|
return false;
|
|
}
|
|
|
|
this.gems -= amount;
|
|
this.addTransaction('gems', -amount, 'purchase');
|
|
|
|
console.log(`[ECONOMY] Removed ${amount} gems. New total: ${this.gems}`);
|
|
|
|
// Update UI to show new gem amount
|
|
if (this.game.systems.ui && this.game.shouldUpdateGUI()) {
|
|
this.game.systems.ui.updateUI();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Transaction tracking
|
|
addTransaction(currency, amount, source) {
|
|
const debugLogger = window.debugLogger;
|
|
const transaction = {
|
|
id: Date.now() + Math.random().toString(36).substr(2, 9),
|
|
currency: currency,
|
|
amount: amount,
|
|
source: source,
|
|
timestamp: Date.now()
|
|
};
|
|
|
|
this.transactionHistory.unshift(transaction);
|
|
|
|
// Keep only last 100 transactions
|
|
const oldLength = this.transactionHistory.length;
|
|
if (this.transactionHistory.length > 100) {
|
|
this.transactionHistory = this.transactionHistory.slice(0, 100);
|
|
}
|
|
|
|
if (debugLogger) debugLogger.logStep('Transaction recorded', {
|
|
transactionId: transaction.id,
|
|
currency: currency,
|
|
amount: amount,
|
|
source: source,
|
|
timestamp: transaction.timestamp,
|
|
historyLength: this.transactionHistory.length,
|
|
wasTrimmed: oldLength > 100,
|
|
removedCount: oldLength > 100 ? oldLength - 100 : 0
|
|
});
|
|
}
|
|
|
|
// Shop functionality
|
|
purchaseItem(itemId, quantity = 1) {
|
|
const debugLogger = window.debugLogger;
|
|
const item = this.findShopItem(itemId);
|
|
|
|
if (!item) {
|
|
if (debugLogger) debugLogger.logStep('Item purchase failed - item not found', {
|
|
itemId: itemId,
|
|
quantity: quantity
|
|
});
|
|
this.game.showNotification('Item not found in shop', 'error', 3000);
|
|
return false;
|
|
}
|
|
|
|
const totalCost = item.price * quantity;
|
|
const currency = item.currency;
|
|
const oldCredits = this.credits;
|
|
const oldGems = this.gems;
|
|
|
|
if (debugLogger) debugLogger.logStep('Item purchase attempted', {
|
|
itemId: itemId,
|
|
itemName: item.name,
|
|
itemType: item.type,
|
|
quantity: quantity,
|
|
unitPrice: item.price,
|
|
totalCost: totalCost,
|
|
currency: currency,
|
|
currentCredits: oldCredits,
|
|
currentGems: oldGems
|
|
});
|
|
|
|
// Check if player can afford
|
|
if (currency === 'credits' && this.credits < totalCost) {
|
|
if (debugLogger) debugLogger.logStep('Item purchase failed - insufficient credits', {
|
|
totalCost: totalCost,
|
|
currentCredits: oldCredits,
|
|
deficit: totalCost - oldCredits
|
|
});
|
|
this.game.showNotification('Not enough credits!', 'error', 3000);
|
|
return false;
|
|
}
|
|
|
|
if (currency === 'gems' && this.gems < totalCost) {
|
|
if (debugLogger) debugLogger.logStep('Item purchase failed - insufficient gems', {
|
|
totalCost: totalCost,
|
|
currentGems: oldGems,
|
|
deficit: totalCost - oldGems
|
|
});
|
|
this.game.showNotification('Not enough gems!', 'error', 3000);
|
|
return false;
|
|
}
|
|
|
|
// Check if already owns this cosmetic
|
|
if (item.type === 'cosmetic' && this.ownedCosmetics.includes(item.id)) {
|
|
this.game.showNotification('You already own this cosmetic!', 'error', 3000);
|
|
return false;
|
|
}
|
|
|
|
// Process payment and give item based on type
|
|
if (currency === 'credits') {
|
|
this.credits -= totalCost;
|
|
} else if (currency === 'gems') {
|
|
this.gems -= totalCost;
|
|
}
|
|
|
|
switch (item.type) {
|
|
case 'ship':
|
|
this.purchaseShip(item, quantity);
|
|
break;
|
|
case 'cosmetic':
|
|
this.purchaseCosmetic(item, quantity);
|
|
break;
|
|
case 'consumable':
|
|
this.purchaseConsumable(item, quantity);
|
|
break;
|
|
case 'material':
|
|
this.purchaseMaterial(item, quantity);
|
|
break;
|
|
default:
|
|
console.warn(`[ECONOMY] Unknown item type: ${item.type}`);
|
|
return false;
|
|
}
|
|
|
|
// Random shop items don't have purchase limits - unlimited purchases allowed
|
|
if (item.isRandomItem) {
|
|
const category = this.getItemCategory(item);
|
|
|
|
if (debugLogger) debugLogger.logStep('Random shop purchase completed', {
|
|
itemId: itemId,
|
|
itemName: item.name,
|
|
category: category
|
|
});
|
|
}
|
|
|
|
// Update inventory UI to show the new item
|
|
if (this.game.systems.ui && this.game.systems.ui.updateInventory) {
|
|
this.game.systems.ui.updateInventory();
|
|
} else if (this.game.systems.ui && this.game.systems.ui.updateUI) {
|
|
this.game.systems.ui.updateUI();
|
|
} else {
|
|
console.warn('No UI update method available after purchase');
|
|
}
|
|
|
|
if (debugLogger) debugLogger.logStep('Item purchase completed successfully', {
|
|
itemId: itemId,
|
|
itemName: item.name,
|
|
itemType: item.type,
|
|
quantity: quantity,
|
|
totalCost: totalCost,
|
|
currency: currency,
|
|
oldCredits: oldCredits,
|
|
newCredits: this.credits,
|
|
oldGems: oldGems,
|
|
newGems: this.gems
|
|
});
|
|
|
|
// Update UI without calling updateShopUI to avoid circular updates
|
|
return true;
|
|
}
|
|
|
|
findShopItem(itemId) {
|
|
const debugLogger = window.debugLogger;
|
|
|
|
for (const category of Object.values(this.shopItems)) {
|
|
const item = category.find(i => i.id === itemId);
|
|
if (item) {
|
|
if (debugLogger) debugLogger.logStep('Shop item found', {
|
|
itemId: itemId,
|
|
itemName: item.name,
|
|
itemType: item.type,
|
|
itemPrice: item.price,
|
|
itemCurrency: item.currency
|
|
});
|
|
return item;
|
|
}
|
|
}
|
|
|
|
if (debugLogger) debugLogger.logStep('Shop item not found', {
|
|
itemId: itemId,
|
|
availableCategories: Object.keys(this.shopItems)
|
|
});
|
|
return null;
|
|
}
|
|
|
|
purchaseShip(ship) {
|
|
const debugLogger = window.debugLogger;
|
|
const player = this.game.systems.player;
|
|
const oldShipName = player.ship.name;
|
|
const oldShipClass = player.ship.class;
|
|
const oldAttributes = { ...player.attributes };
|
|
|
|
if (debugLogger) debugLogger.logStep('Ship purchase processing', {
|
|
shipId: ship.id,
|
|
shipName: ship.name,
|
|
shipType: ship.type,
|
|
shipStats: ship.stats,
|
|
oldShipName: oldShipName,
|
|
oldShipClass: oldShipClass
|
|
});
|
|
|
|
// Add ship to player's ship collection
|
|
// Add ship to base gallery
|
|
if (this.game.systems.baseSystem) {
|
|
this.game.systems.baseSystem.addShipToGallery(ship);
|
|
if (debugLogger) debugLogger.logStep('Ship added to base gallery');
|
|
}
|
|
|
|
// Replace current ship (no upgrade functionality)
|
|
player.ship.name = ship.name;
|
|
player.ship.class = ship.type;
|
|
player.ship.level = 1; // Reset level for new ship
|
|
player.ship.texture = ship.texture; // Copy texture for image display
|
|
|
|
// Set ship-specific stats (not just attributes)
|
|
if (ship.stats.health) {
|
|
player.ship.maxHealth = ship.stats.health;
|
|
player.ship.health = ship.stats.health;
|
|
}
|
|
if (ship.stats.attack) {
|
|
player.ship.attack = ship.stats.attack;
|
|
}
|
|
if (ship.stats.defense) {
|
|
player.ship.defense = ship.stats.defense;
|
|
}
|
|
if (ship.stats.speed) {
|
|
player.ship.speed = ship.stats.speed;
|
|
}
|
|
if (ship.stats.criticalChance) {
|
|
player.ship.criticalChance = ship.stats.criticalChance;
|
|
}
|
|
if (ship.stats.criticalDamage) {
|
|
player.ship.criticalDamage = ship.stats.criticalDamage;
|
|
}
|
|
|
|
// Also update player attributes for compatibility
|
|
for (const [stat, value] of Object.entries(ship.stats)) {
|
|
if (player.attributes[stat] !== undefined) {
|
|
player.attributes[stat] = value; // Replace stats instead of adding
|
|
}
|
|
}
|
|
|
|
player.updateUI();
|
|
|
|
// Also update ShipSystem display
|
|
if (this.game.systems.ship && this.game.systems.ship.updateCurrentShipDisplay) {
|
|
this.game.systems.ship.updateCurrentShipDisplay();
|
|
}
|
|
|
|
if (debugLogger) debugLogger.logStep('Ship purchase completed', {
|
|
shipId: ship.id,
|
|
oldShipName: oldShipName,
|
|
newShipName: player.ship.name,
|
|
oldShipClass: oldShipClass,
|
|
newShipClass: player.ship.class,
|
|
attributeChanges: Object.entries(ship.stats).map(([stat, value]) => ({
|
|
stat: stat,
|
|
oldValue: oldAttributes[stat],
|
|
newValue: player.attributes[stat],
|
|
change: value
|
|
}))
|
|
});
|
|
}
|
|
|
|
purchaseCosmetic(cosmetic) {
|
|
const debugLogger = window.debugLogger;
|
|
const oldOwnedCount = this.ownedCosmetics.length;
|
|
|
|
// Add to owned cosmetics
|
|
this.ownedCosmetics.push(cosmetic.id);
|
|
this.game.showNotification(`Cosmetic unlocked: ${cosmetic.name}`, 'success', 3000);
|
|
|
|
if (debugLogger) debugLogger.logStep('Cosmetic purchase completed', {
|
|
cosmeticId: cosmetic.id,
|
|
cosmeticName: cosmetic.name,
|
|
oldOwnedCount: oldOwnedCount,
|
|
newOwnedCount: this.ownedCosmetics.length,
|
|
totalOwnedCosmetics: this.ownedCosmetics.length
|
|
});
|
|
}
|
|
|
|
purchaseConsumable(consumable, quantity) {
|
|
const debugLogger = window.debugLogger;
|
|
const inventory = this.game.systems.inventory;
|
|
const oldInventorySize = inventory ? inventory.items.length : 0;
|
|
|
|
if (debugLogger) debugLogger.logStep('Consumable purchase processing', {
|
|
consumableId: consumable.id,
|
|
consumableName: consumable.name,
|
|
quantity: quantity,
|
|
effect: consumable.effect,
|
|
oldInventorySize: oldInventorySize
|
|
});
|
|
|
|
// Add to inventory
|
|
for (let i = 0; i < quantity; i++) {
|
|
const item = {
|
|
id: `${consumable.id}_${Date.now()}_${i}`,
|
|
name: consumable.name,
|
|
type: 'consumable',
|
|
rarity: consumable.rarity,
|
|
quantity: 1,
|
|
description: consumable.description,
|
|
consumable: true,
|
|
effect: consumable.effect
|
|
};
|
|
|
|
inventory.addItem(item);
|
|
}
|
|
|
|
if (debugLogger) debugLogger.logStep('Consumable purchase completed', {
|
|
consumableId: consumable.id,
|
|
quantity: quantity,
|
|
oldInventorySize: oldInventorySize,
|
|
newInventorySize: inventory ? inventory.items.length : 0,
|
|
itemsAdded: quantity
|
|
});
|
|
}
|
|
|
|
purchaseEquipment(equipment, quantity = 1) {
|
|
const inventory = this.game.systems.inventory;
|
|
const oldInventorySize = inventory ? inventory.items.length : 0;
|
|
|
|
// Add to inventory
|
|
for (let i = 0; i < quantity; i++) {
|
|
const item = {
|
|
id: `${equipment.id}_${Date.now()}_${i}`,
|
|
name: equipment.name,
|
|
type: equipment.type,
|
|
rarity: equipment.rarity,
|
|
quantity: 1,
|
|
description: equipment.description,
|
|
texture: equipment.texture,
|
|
stats: equipment.stats || {},
|
|
equipable: true
|
|
};
|
|
|
|
inventory.addItem(item);
|
|
}
|
|
}
|
|
|
|
purchaseMaterial(material, quantity = 1) {
|
|
const inventory = this.game.systems.inventory;
|
|
const debugLogger = window.debugLogger;
|
|
|
|
if (!inventory) {
|
|
console.error('[ECONOMY] Inventory system not available for material purchase');
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
const oldInventorySize = inventory.items.length;
|
|
console.log(`[DEBUG] Inventory state before purchase: ${oldInventorySize} items`);
|
|
|
|
// Create item object for inventory
|
|
const item = {
|
|
id: material.id,
|
|
name: material.name,
|
|
type: 'material',
|
|
rarity: material.rarity || 'common',
|
|
quantity: quantity,
|
|
description: material.description || `A ${material.name} material`,
|
|
stackable: true,
|
|
stats: {},
|
|
equipable: false,
|
|
slot: null
|
|
};
|
|
|
|
console.log(`[DEBUG] Adding item to inventory: ${item.name}`);
|
|
const added = inventory.addItem(item);
|
|
|
|
console.log(`[DEBUG] addItem() returned: ${added}, new inventory size: ${inventory.items.length}`);
|
|
|
|
if (debugLogger) debugLogger.logStep('Material purchase completed', {
|
|
materialId: material.id,
|
|
materialName: material.name,
|
|
quantity: quantity,
|
|
oldInventorySize: oldInventorySize,
|
|
newInventorySize: inventory.items.length,
|
|
addedSuccessfully: added
|
|
});
|
|
|
|
if (!added) {
|
|
console.error('Failed to add material to inventory');
|
|
return false;
|
|
} else {
|
|
// Update inventory UI to show the new material
|
|
if (this.game.systems.ui && this.game.systems.ui.updateInventory) {
|
|
this.game.systems.ui.updateInventory();
|
|
} else if (this.game.systems.ui && this.game.systems.ui.updateUI) {
|
|
this.game.systems.ui.updateUI();
|
|
} else {
|
|
console.warn('No UI update method available after material purchase');
|
|
}
|
|
console.log('[DEBUG] Material purchase completed and UI update called');
|
|
return true;
|
|
}
|
|
} catch (error) {
|
|
console.error('Exception in purchaseMaterial:', error);
|
|
if (debugLogger) debugLogger.errorEvent('Purchase Material Exception', error, {
|
|
materialId: material.id,
|
|
materialName: material.name
|
|
});
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Reward generation
|
|
generateRewards(difficulty = 'normal', source = 'dungeon') {
|
|
const debugLogger = window.debugLogger;
|
|
|
|
if (debugLogger) debugLogger.startStep('generateRewards', {
|
|
difficulty: difficulty,
|
|
source: source
|
|
});
|
|
|
|
const baseRewards = {
|
|
easy: { credits: [50, 150], gems: [0, 1], experience: [20, 50] },
|
|
normal: { credits: [100, 300], gems: [1, 3], experience: [50, 100] },
|
|
hard: { credits: [200, 500], gems: [2, 5], experience: [100, 200] },
|
|
extreme: { credits: [500, 1000], gems: [5, 10], experience: [200, 400] }
|
|
};
|
|
|
|
const rewards = baseRewards[difficulty] || baseRewards.normal;
|
|
const generatedRewards = {};
|
|
|
|
// Generate credits
|
|
generatedRewards.credits = Math.floor(
|
|
Math.random() * (rewards.credits[1] - rewards.credits[0] + 1) + rewards.credits[0]
|
|
);
|
|
|
|
// Generate gems
|
|
generatedRewards.gems = Math.floor(
|
|
Math.random() * (rewards.gems[1] - rewards.gems[0] + 1) + rewards.gems[0]
|
|
);
|
|
|
|
// Generate experience
|
|
generatedRewards.experience = Math.floor(
|
|
Math.random() * (rewards.experience[1] - rewards.experience[0] + 1) + rewards.experience[0]
|
|
);
|
|
|
|
// Bonus for premium currency (rare)
|
|
if (Math.random() < 0.05) { // 5% chance
|
|
generatedRewards.premiumCurrency = 1;
|
|
}
|
|
|
|
if (debugLogger) debugLogger.endStep('generateRewards', {
|
|
difficulty: difficulty,
|
|
source: source,
|
|
generatedRewards: generatedRewards,
|
|
rewardRanges: rewards
|
|
});
|
|
|
|
return generatedRewards;
|
|
}
|
|
|
|
giveRewards(rewards, source = 'unknown') {
|
|
const debugLogger = window.debugLogger;
|
|
const player = this.game.systems.player;
|
|
const oldCredits = this.credits;
|
|
const oldGems = this.gems;
|
|
const oldExperience = player.stats.experience;
|
|
const oldLevel = player.stats.level;
|
|
|
|
if (debugLogger) debugLogger.startStep('giveRewards', {
|
|
source: source,
|
|
rewards: rewards,
|
|
oldPlayerState: {
|
|
credits: oldCredits,
|
|
gems: oldGems,
|
|
experience: oldExperience,
|
|
level: oldLevel
|
|
}
|
|
});
|
|
|
|
let totalRewards = [];
|
|
|
|
// Add credits
|
|
if (rewards.credits > 0) {
|
|
this.addCredits(rewards.credits, source);
|
|
totalRewards.push(`${rewards.credits} credits`);
|
|
}
|
|
|
|
// Add gems
|
|
if (rewards.gems > 0) {
|
|
this.addGems(rewards.gems, source);
|
|
totalRewards.push(`${rewards.gems} gems`);
|
|
}
|
|
|
|
// Add premium currency
|
|
if (rewards.premiumCurrency > 0) {
|
|
this.addPremiumCurrency(rewards.premiumCurrency, source);
|
|
totalRewards.push(`${rewards.premiumCurrency} premium currency`);
|
|
}
|
|
|
|
// Add experience
|
|
if (rewards.experience > 0) {
|
|
player.addExperience(rewards.experience);
|
|
totalRewards.push(`${rewards.experience} experience`);
|
|
}
|
|
|
|
// Add materials
|
|
if (rewards.materials && rewards.materials.length > 0) {
|
|
const inventory = this.game.systems.inventory;
|
|
alert(`[DUNGEON DEBUG] Adding ${rewards.materials.length} materials to inventory\nInventory available: ${!!inventory}\nMaterials: ${JSON.stringify(rewards.materials, null, 2)}`);
|
|
|
|
for (const material of rewards.materials) {
|
|
// Create proper item object like in purchaseMaterial
|
|
const itemObject = {
|
|
id: material.id,
|
|
name: material.id.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase()), // Convert ID to readable name
|
|
type: 'material',
|
|
rarity: 'common',
|
|
quantity: material.quantity,
|
|
description: `A ${material.id.replace('_', ' ')} material`,
|
|
stackable: true,
|
|
stats: {},
|
|
equipable: false,
|
|
slot: null
|
|
};
|
|
|
|
alert(`[DUNGEON DEBUG] Fixed! Adding material as full item object:\n${JSON.stringify(itemObject, null, 2)}`);
|
|
inventory.addItem(itemObject);
|
|
totalRewards.push(`${material.quantity}x ${material.id}`);
|
|
}
|
|
}
|
|
|
|
// Add items
|
|
if (rewards.items && rewards.items.length > 0) {
|
|
const inventory = this.game.systems.inventory;
|
|
for (const item of rewards.items) {
|
|
inventory.addItem(item.id, item.quantity || 1);
|
|
totalRewards.push(`${item.quantity || 1}x ${item.id}`);
|
|
}
|
|
}
|
|
|
|
// Show reward notification
|
|
if (totalRewards.length > 0) {
|
|
const rewardText = totalRewards.join(', ');
|
|
this.game.showNotification(`Rewards: ${rewardText}`, 'success', 3000);
|
|
}
|
|
|
|
if (debugLogger) debugLogger.endStep('giveRewards', {
|
|
source: source,
|
|
rewardsGiven: rewards,
|
|
totalRewardsText: totalRewards.join(', '),
|
|
currencyChanges: {
|
|
credits: { old: oldCredits, new: this.credits, change: this.credits - oldCredits },
|
|
gems: { old: oldGems, new: this.gems, change: this.gems - oldGems }
|
|
},
|
|
playerChanges: {
|
|
experience: { old: oldExperience, new: player.stats.experience, change: player.stats.experience - oldExperience },
|
|
level: { old: oldLevel, new: player.stats.level, leveledUp: player.stats.level > oldLevel }
|
|
}
|
|
});
|
|
}
|
|
|
|
// Daily rewards and bonuses
|
|
claimDailyReward() {
|
|
const debugLogger = window.debugLogger;
|
|
const lastClaim = localStorage.getItem('lastDailyReward');
|
|
const today = new Date().toDateString();
|
|
|
|
if (debugLogger) debugLogger.startStep('claimDailyReward', {
|
|
lastClaim: lastClaim,
|
|
today: today,
|
|
alreadyClaimed: lastClaim === today
|
|
});
|
|
|
|
if (lastClaim === today) {
|
|
this.game.showNotification('Daily reward already claimed!', 'warning', 3000);
|
|
if (debugLogger) debugLogger.endStep('claimDailyReward', {
|
|
success: false,
|
|
reason: 'Already claimed today'
|
|
});
|
|
return false;
|
|
}
|
|
|
|
// Calculate reward based on consecutive days
|
|
const consecutiveDays = parseInt(localStorage.getItem('consecutiveDailyRewards') || '0') + 1;
|
|
const baseReward = 100;
|
|
const bonusMultiplier = Math.min(consecutiveDays * 0.1, 2); // Max 2x bonus
|
|
const credits = Math.floor(baseReward * (1 + bonusMultiplier));
|
|
const gems = Math.min(Math.floor(consecutiveDays / 7), 5); // 1 gem per week, max 5
|
|
|
|
if (debugLogger) debugLogger.logStep('Daily reward calculation', {
|
|
consecutiveDays: consecutiveDays,
|
|
baseReward: baseReward,
|
|
bonusMultiplier: bonusMultiplier,
|
|
creditsReward: credits,
|
|
gemsReward: gems
|
|
});
|
|
|
|
// Give rewards
|
|
this.addCredits(credits, 'daily_reward');
|
|
this.addGems(gems, 'daily_reward');
|
|
|
|
// Update daily reward tracking
|
|
localStorage.setItem('lastDailyReward', today);
|
|
localStorage.setItem('consecutiveDailyRewards', consecutiveDays.toString());
|
|
|
|
// Show notification
|
|
const rewardText = `${credits} credits${gems > 0 ? ` and ${gems} gems` : ''}`;
|
|
this.game.showNotification(`Daily reward claimed: ${rewardText}! (${consecutiveDays} day streak)`, 'success', 4000);
|
|
|
|
if (debugLogger) debugLogger.endStep('claimDailyReward', {
|
|
success: true,
|
|
consecutiveDays: consecutiveDays,
|
|
creditsReward: credits,
|
|
gemsReward: gems,
|
|
rewardText: rewardText,
|
|
newLastClaim: today,
|
|
newConsecutiveDays: consecutiveDays
|
|
});
|
|
|
|
return true;
|
|
}
|
|
|
|
// UI updates
|
|
updateUI() {
|
|
const debugLogger = window.debugLogger;
|
|
|
|
if (debugLogger) debugLogger.startStep('updateUI', {
|
|
currentCredits: this.credits,
|
|
currentGems: this.gems,
|
|
currentPremiumCurrency: this.premiumCurrency
|
|
});
|
|
|
|
// Update resource displays
|
|
const creditsElement = document.getElementById('credits');
|
|
if (creditsElement) {
|
|
creditsElement.textContent = this.game.formatNumber(this.credits);
|
|
}
|
|
|
|
const gemsElement = document.getElementById('gems');
|
|
if (gemsElement) {
|
|
gemsElement.textContent = this.game.formatNumber(this.gems);
|
|
}
|
|
|
|
const premiumElement = document.getElementById('premiumCurrency');
|
|
if (premiumElement) {
|
|
premiumElement.textContent = this.game.formatNumber(this.premiumCurrency);
|
|
}
|
|
|
|
// Update shop UI when shop tab is active
|
|
this.updateShopUI();
|
|
|
|
if (debugLogger) debugLogger.endStep('updateUI', {
|
|
creditsUpdated: !!creditsElement,
|
|
gemsUpdated: !!gemsElement,
|
|
premiumUpdated: !!premiumElement,
|
|
shopUIUpdated: true
|
|
});
|
|
}
|
|
|
|
// Reset economy (for new game)
|
|
reset() {
|
|
const debugLogger = window.debugLogger;
|
|
const oldState = {
|
|
credits: this.credits,
|
|
gems: this.gems,
|
|
premiumCurrency: this.premiumCurrency,
|
|
transactionCount: this.transactions.length,
|
|
ownedCosmeticsCount: this.ownedCosmetics.length
|
|
};
|
|
|
|
if (debugLogger) debugLogger.startStep('reset', {
|
|
oldState: oldState
|
|
});
|
|
|
|
this.credits = 1000;
|
|
this.gems = 10;
|
|
this.premiumCurrency = 0;
|
|
this.transactions = [];
|
|
this.ownedCosmetics = [];
|
|
|
|
// Reset daily rewards
|
|
localStorage.removeItem('lastDailyReward');
|
|
localStorage.removeItem('consecutiveDailyRewards');
|
|
|
|
if (debugLogger) debugLogger.endStep('reset', {
|
|
oldState: oldState,
|
|
newState: {
|
|
credits: this.credits,
|
|
gems: this.gems,
|
|
premiumCurrency: this.premiumCurrency,
|
|
transactionCount: this.transactions.length,
|
|
ownedCosmeticsCount: this.ownedCosmetics.length
|
|
}
|
|
});
|
|
}
|
|
|
|
// Clear all data
|
|
clear() {
|
|
const debugLogger = window.debugLogger;
|
|
const oldState = {
|
|
credits: this.credits,
|
|
gems: this.gems,
|
|
premiumCurrency: this.premiumCurrency,
|
|
transactionCount: this.transactions.length,
|
|
ownedCosmeticsCount: this.ownedCosmetics.length
|
|
};
|
|
|
|
if (debugLogger) debugLogger.startStep('clear', {
|
|
oldState: oldState
|
|
});
|
|
|
|
this.credits = 0;
|
|
this.gems = 0;
|
|
this.premiumCurrency = 0;
|
|
this.transactions = [];
|
|
this.ownedCosmetics = [];
|
|
|
|
if (debugLogger) debugLogger.endStep('clear', {
|
|
oldState: oldState,
|
|
newState: {
|
|
credits: this.credits,
|
|
gems: this.gems,
|
|
premiumCurrency: this.premiumCurrency,
|
|
transactionCount: this.transactions.length,
|
|
ownedCosmeticsCount: this.ownedCosmetics.length
|
|
}
|
|
});
|
|
}
|
|
|
|
updateShopUI() {
|
|
const debugLogger = window.debugLogger;
|
|
const shopItemsElement = document.getElementById('shopItems');
|
|
|
|
if (!shopItemsElement) {
|
|
if (debugLogger) debugLogger.log('updateShopUI: Shop items element not found');
|
|
return;
|
|
}
|
|
|
|
const activeCategory = document.querySelector('.shop-cat-btn.active')?.dataset.category || 'ships';
|
|
|
|
// Always use random shop items when the system is available
|
|
// If no random items exist for this category, trigger a refresh
|
|
if (!this.randomShopItems[activeCategory] || this.randomShopItems[activeCategory].length === 0) {
|
|
console.log(`[ECONOMY] No random items for ${activeCategory}, triggering refresh`);
|
|
this.refreshRandomShop();
|
|
}
|
|
|
|
const items = this.randomShopItems[activeCategory] || [];
|
|
const isRandomShop = true; // Always random shop when system is available
|
|
|
|
if (debugLogger) debugLogger.startStep('updateShopUI', {
|
|
activeCategory: activeCategory,
|
|
itemCount: items.length,
|
|
isRandomShop: isRandomShop,
|
|
currentCredits: this.credits,
|
|
currentGems: this.gems,
|
|
currentPremiumCurrency: this.premiumCurrency
|
|
});
|
|
|
|
shopItemsElement.innerHTML = '';
|
|
|
|
// Add shop refresh info if using random shop
|
|
if (isRandomShop) {
|
|
const refreshInfo = document.createElement('div');
|
|
refreshInfo.className = 'shop-refresh-info';
|
|
refreshInfo.innerHTML = `
|
|
<div class="refresh-info-left">
|
|
<div class="refresh-countdown">
|
|
<i class="fas fa-clock"></i>
|
|
Next refresh in: ${this.getShopRefreshCountdown()}
|
|
</div>
|
|
</div>
|
|
`;
|
|
shopItemsElement.appendChild(refreshInfo);
|
|
}
|
|
|
|
items.forEach(item => {
|
|
const itemElement = document.createElement('div');
|
|
itemElement.className = 'shop-item';
|
|
|
|
const canAfford = this.canAfford(item);
|
|
const isOwned = item.type === 'cosmetic' && this.ownedCosmetics.includes(item.id);
|
|
|
|
itemElement.innerHTML = `
|
|
<div class="shop-item-content">
|
|
${item.texture ?
|
|
`<div class="shop-item-image">
|
|
<img src="${item.texture}" alt="${item.name}" onerror="this.src='assets/textures/missing-texture.png'">
|
|
</div>` : ''}
|
|
<div class="shop-item-info">
|
|
<div class="shop-item-name">${item.name}</div>
|
|
<div class="shop-item-description">${item.description}</div>
|
|
${item.stats ? `
|
|
<div class="shop-item-stats">
|
|
${Object.entries(item.stats).map(([stat, value]) =>
|
|
`<div class="stat">${stat}: ${value}</div>`
|
|
).join('')}
|
|
</div>
|
|
` : ''}
|
|
<div class="shop-item-price">${this.formatPrice(item)}</div>
|
|
<div class="shop-item-rarity ${item.rarity}">${item.rarity}</div>
|
|
</div>
|
|
</div>
|
|
<button class="shop-item-purchase-btn ${!canAfford || isOwned ? 'disabled' : ''}"
|
|
data-item-id="${item.id}"
|
|
${!canAfford || isOwned ? 'disabled' : ''}>
|
|
${isOwned ? 'Owned' : canAfford ? 'Purchase' : 'Cannot Afford'}
|
|
</button>
|
|
`;
|
|
|
|
shopItemsElement.appendChild(itemElement);
|
|
|
|
if (debugLogger) debugLogger.logStep('Shop item rendered', {
|
|
itemId: item.id,
|
|
itemName: item.name,
|
|
itemCategory: activeCategory,
|
|
itemPrice: item.price,
|
|
itemCurrency: item.currency,
|
|
canAfford: canAfford,
|
|
isOwned: isOwned
|
|
});
|
|
});
|
|
|
|
// Re-setup event listeners since we just recreated all the buttons
|
|
this.setupShopEventListeners();
|
|
|
|
if (debugLogger) debugLogger.endStep('updateShopUI', {
|
|
activeCategory: activeCategory,
|
|
itemsRendered: items.length,
|
|
shopUIUpdated: true
|
|
});
|
|
}
|
|
|
|
// Testing and utility methods
|
|
testPurchase(itemId) {
|
|
const debugLogger = window.debugLogger;
|
|
|
|
if (debugLogger) debugLogger.startStep('testPurchase', {
|
|
itemId: itemId
|
|
});
|
|
|
|
const item = this.findShopItem(itemId);
|
|
if (!item) {
|
|
if (debugLogger) debugLogger.endStep('testPurchase', {
|
|
success: false,
|
|
reason: 'Item not found',
|
|
itemId: itemId
|
|
});
|
|
return false;
|
|
}
|
|
|
|
const result = this.purchaseItem(itemId);
|
|
|
|
if (debugLogger) debugLogger.endStep('testPurchase', {
|
|
success: result,
|
|
itemId: itemId,
|
|
itemName: item.name,
|
|
itemCategory: item.type
|
|
});
|
|
|
|
return result;
|
|
}
|
|
|
|
formatItemStats(item) {
|
|
const debugLogger = window.debugLogger;
|
|
|
|
if (debugLogger) debugLogger.startStep('formatItemStats', {
|
|
itemId: item.id,
|
|
itemType: item.type
|
|
});
|
|
|
|
let stats = [];
|
|
|
|
if (item.stats) {
|
|
for (const [stat, value] of Object.entries(item.stats)) {
|
|
stats.push(`${stat}: +${value}`);
|
|
}
|
|
}
|
|
|
|
if (item.effect) {
|
|
if (item.effect.attackMultiplier) {
|
|
stats.push(`Attack: x${item.effect.attackMultiplier}`);
|
|
}
|
|
if (item.effect.defense) {
|
|
stats.push(`Defense: +${item.effect.defense}`);
|
|
}
|
|
if (item.effect.healing) {
|
|
stats.push(`Healing: +${item.effect.healing}`);
|
|
}
|
|
if (item.effect.energyRestore) {
|
|
stats.push(`Energy: +${item.effect.energyRestore}`);
|
|
}
|
|
}
|
|
|
|
const formattedStats = stats.length > 0 ? stats.join(', ') : 'No special stats';
|
|
|
|
if (debugLogger) debugLogger.endStep('formatItemStats', {
|
|
itemId: item.id,
|
|
formattedStats: formattedStats,
|
|
statCount: stats.length
|
|
});
|
|
|
|
return formattedStats;
|
|
}
|
|
|
|
// Save and load
|
|
save() {
|
|
const debugLogger = window.debugLogger;
|
|
|
|
// if (debugLogger) debugLogger.startStep('save', {
|
|
// currentCredits: this.credits,
|
|
// currentGems: this.gems,
|
|
// currentPremiumCurrency: this.premiumCurrency,
|
|
// transactionCount: this.transactions.length,
|
|
// ownedCosmeticsCount: this.ownedCosmetics.length
|
|
// });
|
|
|
|
const saveData = {
|
|
credits: this.credits,
|
|
gems: this.gems,
|
|
premiumCurrency: this.premiumCurrency,
|
|
transactions: this.transactions,
|
|
ownedCosmetics: this.ownedCosmetics,
|
|
// Include shop data with timestamp for proper refresh calculation
|
|
shopData: {
|
|
randomShopItems: this.randomShopItems,
|
|
categoryPurchaseLimits: this.categoryPurchaseLimits,
|
|
lastShopRefresh: this.lastShopRefresh,
|
|
saveTimestamp: Date.now() // When this save was created
|
|
}
|
|
};
|
|
|
|
// if (debugLogger) debugLogger.endStep('save', {
|
|
// saveDataSize: JSON.stringify(saveData).length,
|
|
// economyState: saveData
|
|
// });
|
|
|
|
return saveData;
|
|
}
|
|
|
|
load(data) {
|
|
const debugLogger = window.debugLogger;
|
|
const oldState = {
|
|
credits: this.credits,
|
|
gems: this.gems,
|
|
premiumCurrency: this.premiumCurrency,
|
|
transactionCount: this.transactions.length,
|
|
ownedCosmeticsCount: this.ownedCosmetics.length
|
|
};
|
|
|
|
// if (debugLogger) debugLogger.startStep('load', {
|
|
// oldState: oldState,
|
|
// loadData: data
|
|
// });
|
|
|
|
try {
|
|
this.credits = data.credits || 0;
|
|
this.gems = data.gems || 0;
|
|
this.premiumCurrency = data.premiumCurrency || 0;
|
|
this.transactions = data.transactions || [];
|
|
this.ownedCosmetics = data.ownedCosmetics || [];
|
|
|
|
// Load shop data and calculate if refresh is needed
|
|
if (data.shopData) {
|
|
console.log('[ECONOMY] Loading shop data from save');
|
|
|
|
// Restore shop data
|
|
this.randomShopItems = data.shopData.randomShopItems || {};
|
|
this.categoryPurchaseLimits = data.shopData.categoryPurchaseLimits || {};
|
|
this.lastShopRefresh = data.shopData.lastShopRefresh || null;
|
|
|
|
// Calculate if shop should have refreshed while game was closed
|
|
const saveTimestamp = data.shopData.saveTimestamp || Date.now();
|
|
const currentTime = Date.now();
|
|
const timeSinceSave = currentTime - saveTimestamp;
|
|
|
|
if (this.lastShopRefresh) {
|
|
const timeSinceLastRefresh = currentTime - this.lastShopRefresh;
|
|
const totalIntervalsPassed = Math.floor(timeSinceLastRefresh / this.SHOP_REFRESH_INTERVAL);
|
|
|
|
if (totalIntervalsPassed > 0) {
|
|
console.log(`[ECONOMY] Shop missed ${totalIntervalsPassed} refresh(es) while game was closed, refreshing now`);
|
|
this.refreshRandomShop();
|
|
} else {
|
|
console.log('[ECONOMY] Shop items still valid, no refresh needed');
|
|
}
|
|
} else {
|
|
console.log('[ECONOMY] No previous shop refresh, generating new shop');
|
|
this.refreshRandomShop();
|
|
}
|
|
} else {
|
|
console.log('[ECONOMY] No shop data in save, initializing new shop');
|
|
this.refreshRandomShop();
|
|
}
|
|
|
|
if (debugLogger) debugLogger.endStep('load', {
|
|
success: true,
|
|
oldState: oldState,
|
|
newState: {
|
|
credits: this.credits,
|
|
gems: this.gems,
|
|
premiumCurrency: this.premiumCurrency,
|
|
transactionCount: this.transactions.length,
|
|
ownedCosmeticsCount: this.ownedCosmetics.length
|
|
}
|
|
});
|
|
} catch (error) {
|
|
if (debugLogger) debugLogger.errorEvent('load', error, {
|
|
oldState: oldState,
|
|
error: error.message
|
|
});
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// Utility methods for shop
|
|
canAfford(item) {
|
|
if (item.currency === 'credits') {
|
|
return this.credits >= item.price;
|
|
} else if (item.currency === 'gems') {
|
|
return this.gems >= item.price;
|
|
} else if (item.currency === 'premium') {
|
|
return this.premiumCurrency >= item.price;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
formatPrice(item) {
|
|
const currencySymbols = {
|
|
'credits': '₵',
|
|
'gems': '💎',
|
|
'premium': '⭐'
|
|
};
|
|
|
|
const symbol = currencySymbols[item.currency] || item.currency;
|
|
return `${symbol}${this.game.formatNumber(item.price)}`;
|
|
}
|
|
}
|