From 1495c41e43558d6d74ea2aa268912f18b0c1ce94 Mon Sep 17 00:00:00 2001 From: Robert MacRae Date: Tue, 10 Mar 2026 10:59:13 -0300 Subject: [PATCH] re-did the entire repo, and added many features and removed lingering features. --- Client-Server/electron-main copy.js | 451 -- Client-Server/index copy.html | 699 -- Client-Server/index.html | 699 -- Client-Server/js/LocalServer.js | 418 -- Client-Server/js/LocalServerManager.js | 224 - Client-Server/js/SimpleLocalServer.js | 535 -- Client-Server/js/core/Economy.js | 2335 ------- Client-Server/js/core/GameEngine.js | 1567 ----- Client-Server/js/data/GameData.js | 570 -- Client-Server/js/systems/CraftingSystem.js | 657 -- Client-Server/js/systems/DungeonSystem.js | 1985 ------ Client-Server/js/systems/SkillSystem.js | 596 -- Client/data/starbase-layout.json | 120 + Client/index.html | 1408 +++- Client/js/GameInitializer.js | 14 +- Client/js/core/Economy.js | 19 +- Client/js/data/GameData.js | 515 +- Client/js/systems/BaseSystem.js | 11 +- Client/js/systems/CraftingSystem.js | 772 +-- Client/js/systems/ItemSystem.js | 7 + Client/js/systems/SkillSystem.js | 753 +- Client/js/systems/StarbaseWorld.js | 1001 +++ .../.github/workflows/build-client.yml | 228 + Galaxy-Strike-Online-main/.gitignore | 139 + .../API/config/database.js | 19 + .../API/config/production.js | 131 + .../API/middleware/errorHandler.js | 134 + .../API/models/GameServer.js | 134 + .../API/models/Inventory.js | 306 + .../API/models/Player.js | 155 + Galaxy-Strike-Online-main/API/models/Ship.js | 189 + .../API/package-lock.json | 6068 +++++++++++++++++ Galaxy-Strike-Online-main/API/package.json | 39 + Galaxy-Strike-Online-main/API/routes/auth.js | 214 + .../API/routes/servers.js | 419 ++ .../API/scripts/createTestServer.js | 67 + .../API/scripts/migrate.js | 50 + .../API/scripts/migratePasswords.js | 71 + Galaxy-Strike-Online-main/API/scripts/seed.js | 196 + Galaxy-Strike-Online-main/API/server.js | 234 + .../API/socket/socketHandlers.js | 272 + .../API/systems/EconomySystem.js | 385 ++ .../API/systems/GameSystem.js | 293 + .../API/tests/api.test.js | 220 + Galaxy-Strike-Online-main/API/utils/logger.js | 27 + .../Client/data/starbase-layout.json | 120 + .../Client}/electron-main.js | 0 Galaxy-Strike-Online-main/Client/index.html | 3793 +++++++++++ .../Client}/js/GameInitializer.js | 524 +- .../Client}/js/SaveSystemIntegration.js | 41 +- .../Client}/js/SmartSaveManager.js | 51 +- .../Client}/js/core/DebugLogger.js | 32 +- .../Client/js/core/Economy.js | 905 +++ .../Client/js/core/GameEngine.js | 753 ++ .../Client}/js/core/Inventory.js | 74 +- .../Client}/js/core/Logger.js | 0 .../Client}/js/core/Player.js | 65 +- .../Client}/js/core/TextureManager.js | 0 .../Client/js/data/GameData.js | 125 + .../Client}/js/main.js | 0 .../Client}/js/systems/BaseSystem.js | 11 +- .../Client/js/systems/CraftingSystem.js | 403 ++ .../Client/js/systems/DungeonSystem.js | 832 +++ .../Client}/js/systems/IdleSystem.js | 29 +- .../Client/js/systems/ItemSystem.js | 468 ++ .../Client}/js/systems/QuestSystem.js | 851 +-- .../Client}/js/systems/ShipSystem.js | 42 +- .../Client/js/systems/SkillSystem.js | 407 ++ .../Client/js/systems/StarbaseWorld.js | 1001 +++ .../Client}/js/ui/LiveMainMenu.js | 2 +- .../Client}/js/ui/MainMenu.js | 0 .../Client}/js/ui/UIManager.js | 392 +- .../Client/locales/de.json | 1 + .../Client/locales/en.json | 58 + .../Client/locales/es.json | 1 + .../Client/locales/fr.json | 1 + .../Client/locales/ja.json | 1 + .../Client/locales/ko.json | 1 + .../Client/locales/pt.json | 1 + .../Client/locales/zh.json | 1 + .../Client}/package-lock.json | 0 .../Client}/package.json | 0 .../Client}/preload.js | 0 .../Client/styles/components.css | 216 + .../Client/styles/components.css.bak | 128 + .../Client/styles/main.css | 606 ++ .../Client/styles/main.css.bak | 33 + .../Client/styles/tables.css | 185 + .../Client/styles/tables.css.bak | 0 .../assets/images}/armors/basic_armor.png | Bin .../assets/images}/armors/heavy_armor.png | Bin .../assets/images}/armors/medium_armor.png | Bin .../assets/images}/base/command_center.png | Bin .../assets/images}/base/mining_facility.png | Bin .../images/items/consumables}/bandages.png | Bin .../images/items/consumables}/health_pack.png | Bin .../items/consumables}/mega_health_pack.png | Bin .../assets/images/items/cosmetics/temp | 0 .../materials}/advanced_circuitboard.png | Bin .../items/materials}/advanced_component.png | Bin .../items/materials}/advanced_components.png | Bin .../items/materials}/basic_circuitboard.png | Bin .../images/items/materials}/battery.png | Bin .../items/materials}/common_circuitboard.png | Bin .../images/items/materials}/copper_ore.png | Bin .../images/items/materials}/copper_wire.png | Bin .../items/materials}/energy_crystal.png | Bin .../assets/images/items/materials}/herbs.png | Bin .../images/items/materials}/iron_ore.png | Bin .../images/items/materials}/leather.png | Bin .../images/items/materials}/stell_plate.png | Bin .../images/items/materials}/tin_bar.png | Bin .../assets/images}/ships/heavy_cruiser.png | Bin .../assets/images}/ships/heavy_destroyer.png | Bin .../assets/images}/ships/light_destroyer.png | Bin .../assets/images}/ships/starter_cruiser.png | Bin .../GameServer/assets/images/ui/icons/temp | 0 .../assets/images/ui/placeholder.png | Bin .../assets/images}/weapons/laser_pistol.png | Bin .../images}/weapons/laser_sniper_rifle.png | Bin .../images}/weapons/starter_blaster.png | Bin .../GameServer/config/database.js | 17 + .../GameServer/create_placeholders.js | 191 + .../data/gso/category/quests/daily.json | 31 + .../data/gso/category/quests/main_story.json | 39 + .../data/gso/category/quests/monthly.json | 23 + .../data/gso/category/quests/weekly.json | 27 + .../data/gso/dungeons/alien_ruins.json | 50 + .../data/gso/dungeons/asteroid_mine.json | 50 + .../data/gso/dungeons/corrupted_vault.json | 56 + .../data/gso/dungeons/dungeons.json | 97 + .../GameServer/data/gso/dungeons/enemies.json | 15 + .../data/gso/dungeons/nebula_anomaly.json | 50 + .../data/gso/dungeons/pirate_lair.json | 50 + .../data/gso/dungeons/void_rift.json | 56 + .../data/gso/enemies/alien_guardian.json | 15 + .../data/gso/enemies/ancient_drone.json | 15 + .../data/gso/enemies/corrupted_ai.json | 15 + .../data/gso/enemies/crystal_golem.json | 15 + .../data/gso/enemies/energy_being.json | 15 + .../data/gso/enemies/mining_drone.json | 15 + .../data/gso/enemies/phase_shifter.json | 15 + .../data/gso/enemies/pirate_captain.json | 15 + .../data/gso/enemies/quantum_entity.json | 16 + .../data/gso/enemies/rock_creature.json | 15 + .../data/gso/enemies/security_drone.json | 15 + .../data/gso/enemies/space_pirate.json | 15 + .../data/gso/enemies/training_drone.json | 12 + .../data/gso/enemies/virus_program.json | 15 + .../GameServer/data/gso/items/.gitkeep | 0 .../backpack/backpack_basic_common.json | 14 + .../armour/backpack/backpack_field_rare.json | 14 + .../backpack/backpack_reactor_epic.json | 14 + .../items/armour/body/body_basic_common.json | 14 + .../items/armour/body/body_exosuit_epic.json | 14 + .../items/armour/body/body_plated_rare.json | 14 + .../armour/boots/boots_assault_rare.json | 14 + .../armour/boots/boots_basic_common.json | 14 + .../armour/boots/boots_gravity_epic.json | 14 + .../armour/hands/hands_basic_common.json | 14 + .../items/armour/hands/hands_combat_rare.json | 14 + .../items/armour/hands/hands_elite_epic.json | 14 + .../armour/helmet/helmet_basic_common.json | 14 + .../armour/helmet/helmet_reinforced_rare.json | 14 + .../armour/helmet/helmet_tactical_epic.json | 14 + .../armour/pants/pants_basic_common.json | 14 + .../items/armour/pants/pants_exo_epic.json | 14 + .../armour/pants/pants_reinforced_rare.json | 14 + .../items/armours/backpack_basic_common.json | 30 + .../gso/items/armours/body_basic_common.json | 29 + .../gso/items/armours/body_exosuit_epic.json | 28 + .../gso/items/armours/body_plated_rare.json | 29 + .../gso/items/armours/boots_basic_common.json | 29 + .../items/armours/gloves_basic_common.json | 29 + .../items/armours/helmet_basic_common.json | 29 + .../items/armours/helmet_reinforced_rare.json | 29 + .../gso/items/armours/pants_basic_common.json | 29 + .../items/consumables/credit_multiplier.json | 26 + .../items/consumables/energy_boost_large.json | 25 + .../items/consumables/energy_boost_small.json | 26 + .../data/gso/items/consumables/fuel_cell.json | 27 + .../items/consumables/health_kit_large.json | 26 + .../items/consumables/health_kit_small.json | 26 + .../items/consumables/scrap_pack_small.json | 35 + .../gso/items/consumables/starter_crate.json | 39 + .../gso/items/consumables/xp_booster.json | 26 + .../gso/items/decorations/room_armory.json | 10 + .../items/decorations/room_operations.json | 10 + .../items/decorations/room_research_lab.json | 10 + .../gso/items/decorations/room_throne.json | 10 + .../gso/items/decorations/room_vault.json | 10 + .../gso/items/decorations/wp_alien_ruins.json | 12 + .../items/decorations/wp_arctic_station.json | 12 + .../gso/items/decorations/wp_deep_ocean.json | 12 + .../items/decorations/wp_golden_empire.json | 12 + .../gso/items/decorations/wp_lava_forge.json | 12 + .../gso/items/decorations/wp_nebula_dawn.json | 12 + .../gso/items/decorations/wp_void_black.json | 12 + .../gso/items/decorations/wp_void_rift.json | 12 + .../misc/ballistic_alloy_common.json | 14 + .../carbon_titanium_composite_common.json | 14 + .../hullPlating/misc/chrono_alloy_common.json | 14 + .../misc/dimensional_alloy_common.json | 14 + .../misc/flux_core_alloy_common.json | 14 + .../misc/neutronium_composite_common.json | 14 + .../hullPlating/misc/phase_alloy_common.json | 14 + .../misc/plasma_channel_alloy_common.json | 14 + .../misc/reinforced_steel_common.json | 14 + .../misc/superconductive_alloy_common.json | 14 + .../hullPlating/misc/voidsteel_common.json | 14 + .../plating/ablative_plating_common.json | 14 + .../section/advanced_hull_section_common.json | 14 + .../section/armor_hull_section_common.json | 14 + .../section/exotic_hull_section_common.json | 14 + .../section/heavy_hull_section_common.json | 14 + .../section/light_hull_section_common.json | 14 + .../section/standard_hull_section_common.json | 14 + .../GameServer/data/gso/items/manifest.json | 20 + .../gso/items/materials/bio/bio_pulp.json | 12 + .../materials/bio/organic_insulation.json | 12 + .../gso/items/materials/bio/plant_matter.json | 12 + .../materials/chemical/aluminum_alloy.json | 12 + .../chemical/bio_composite_material.json | 12 + .../chemical/carbon_nanotube_alloy.json | 12 + .../chemical/carbon_titanium_composite.json | 16 + .../materials/chemical/ceramic_composite.json | 12 + .../materials/chemical/chrono_alloy.json | 16 + .../materials/chemical/dimensional_alloy.json | 16 + .../chemical/explosive_compound.json | 12 + .../items/materials/chemical/flex_alloy.json | 12 + .../materials/chemical/flux_core_alloy.json | 16 + .../chemical/fusion_rated_alloy.json | 12 + .../materials/chemical/glass_composite.json | 12 + .../chemical/neutronium_composite.json | 16 + .../items/materials/chemical/phase_alloy.json | 16 + .../chemical/radiation_shield_alloy.json | 12 + .../chemical/reactive_armor_alloy.json | 12 + .../materials/chemical/reinforced_steel.json | 16 + .../gso/items/materials/chemical/resin.json | 12 + .../materials/chemical/rubber_polymer.json | 12 + .../items/materials/chemical/servo_alloy.json | 12 + .../items/materials/chemical/steel_alloy.json | 12 + .../materials/chemical/titanium_alloy.json | 12 + .../materials/chemical/transparent_alloy.json | 12 + .../materials/chemical/tungsten_alloy.json | 12 + .../items/materials/chemical/voidsteel.json | 16 + .../electronics/ai_core_circuit.json | 12 + .../materials/electronics/basic_circuit.json | 12 + .../electronics/control_circuit.json | 12 + .../dimensional_logic_circuit.json | 12 + .../electronics/high_density_circuit.json | 12 + .../materials/electronics/logic_circuit.json | 12 + .../electronics/navigation_circuit.json | 12 + .../materials/electronics/power_circuit.json | 12 + .../electronics/processor_circuit.json | 12 + .../electronics/quantum_circuit.json | 12 + .../electronics/reactor_control_circuit.json | 12 + .../electronics/sensor_array_circuit.json | 12 + .../electronics/sentient_core_circuit.json | 12 + .../electronics/shield_control_circuit.json | 12 + .../electronics/stealth_circuit.json | 12 + .../electronics/warp_navigation_circuit.json | 12 + .../electronics/weapon_control_circuit.json | 12 + .../items/materials/fabric/plant_cloth.json | 12 + .../items/materials/fabric/plant_fiber.json | 12 + .../data/gso/items/materials/food/bread.json | 12 + .../data/gso/items/materials/food/fish.json | 12 + .../gso/items/materials/food/fish_meat.json | 12 + .../data/gso/items/materials/food/flour.json | 12 + .../gso/items/materials/food/fruit_plant.json | 12 + .../data/gso/items/materials/food/grain.json | 12 + .../gso/items/materials/food/grain_crop.json | 12 + .../gso/items/materials/food/meat_stew.json | 12 + .../gso/items/materials/food/ration_pack.json | 12 + .../items/materials/food/vegetable_plant.json | 12 + .../items/materials/food/vegetable_stew.json | 12 + .../items/materials/ingot/aluminum_ingot.json | 12 + .../items/materials/ingot/carbon_ingot.json | 12 + .../items/materials/ingot/chromium_ingot.json | 12 + .../items/materials/ingot/chronium_ingot.json | 12 + .../items/materials/ingot/copper_ingot.json | 12 + .../materials/ingot/darksteel_ingot.json | 12 + .../gso/items/materials/ingot/gold_ingot.json | 12 + .../gso/items/materials/ingot/iron_ingot.json | 12 + .../materials/ingot/magnesium_ingot.json | 12 + .../materials/ingot/neutronium_ingot.json | 12 + .../items/materials/ingot/platinum_ingot.json | 12 + .../items/materials/ingot/silver_ingot.json | 12 + .../items/materials/ingot/titanium_ingot.json | 12 + .../items/materials/ingot/tungsten_ingot.json | 12 + .../gso/items/materials/liquid/water.json | 12 + .../materials/misc/aerospace_aluminum.json | 12 + .../materials/misc/alien_fauna_sample.json | 12 + .../items/materials/misc/alien_protein.json | 12 + .../items/materials/misc/animal_carcass.json | 12 + .../gso/items/materials/misc/bandage.json | 12 + .../gso/items/materials/misc/body_exotic.json | 12 + .../gso/items/materials/misc/body_light.json | 12 + .../items/materials/misc/body_military.json | 12 + .../items/materials/misc/body_standard.json | 12 + .../gso/items/materials/misc/boots_light.json | 12 + .../items/materials/misc/boots_magnetic.json | 12 + .../items/materials/misc/boots_military.json | 12 + .../items/materials/misc/boots_standard.json | 12 + .../items/materials/misc/carbon_shale.json | 12 + .../items/materials/misc/chromium_steel.json | 12 + .../gso/items/materials/misc/cooked_fish.json | 12 + .../gso/items/materials/misc/cooked_meat.json | 12 + .../items/materials/misc/crystal_glass.json | 12 + .../data/gso/items/materials/misc/dough.json | 12 + .../items/materials/misc/flux_crystal.json | 12 + .../materials/misc/gloves_industrial.json | 12 + .../items/materials/misc/gloves_light.json | 12 + .../items/materials/misc/gloves_military.json | 12 + .../items/materials/misc/gloves_standard.json | 12 + .../items/materials/misc/helmet_exotic.json | 12 + .../items/materials/misc/helmet_light.json | 12 + .../items/materials/misc/helmet_military.json | 12 + .../items/materials/misc/helmet_standard.json | 12 + .../misc/hydroponic_growth_medium.json | 12 + .../gso/items/materials/misc/ice_chunk.json | 12 + .../items/materials/misc/iron_ore_common.json | 16 + .../materials/misc/life_support_basic.json | 12 + .../materials/misc/life_support_exotic.json | 12 + .../materials/misc/life_support_military.json | 12 + .../materials/misc/life_support_standard.json | 12 + .../gso/items/materials/misc/luxury_meal.json | 12 + .../items/materials/misc/magnetic_coil.json | 12 + .../items/materials/misc/medical_pack.json | 12 + .../items/materials/misc/military_ration.json | 12 + .../materials/misc/neutronium_shard.json | 12 + .../items/materials/misc/nutrient_paste.json | 12 + .../items/materials/misc/pants_exotic.json | 12 + .../gso/items/materials/misc/pants_heavy.json | 12 + .../gso/items/materials/misc/pants_light.json | 12 + .../items/materials/misc/pants_standard.json | 12 + .../items/materials/misc/phase_crystal.json | 12 + .../items/materials/misc/processed_wood.json | 12 + .../gso/items/materials/misc/protein_bar.json | 12 + .../gso/items/materials/misc/raw_fruits.json | 12 + .../gso/items/materials/misc/raw_meat.json | 12 + .../items/materials/misc/raw_vegetables.json | 12 + .../gso/items/materials/misc/raw_wood.json | 12 + .../items/materials/misc/silicon_wafer.json | 12 + .../data/gso/items/materials/misc/tree.json | 12 + .../gso/items/materials/misc/tree_sap.json | 12 + .../items/materials/misc/tungsten_steel.json | 12 + .../items/materials/misc/void_crystal.json | 12 + .../items/materials/misc/void_residue.json | 12 + .../materials/misc/wooden_components.json | 12 + .../gso/items/materials/ore/bauxite_ore.json | 12 + .../gso/items/materials/ore/chromium_ore.json | 12 + .../gso/items/materials/ore/chronite_ore.json | 12 + .../gso/items/materials/ore/copper_ore.json | 12 + .../items/materials/ore/dark_iron_ore.json | 12 + .../gso/items/materials/ore/gold_ore.json | 12 + .../gso/items/materials/ore/iron_ore.json | 12 + .../items/materials/ore/magnesium_ore.json | 12 + .../gso/items/materials/ore/platinum_ore.json | 12 + .../gso/items/materials/ore/silver_ore.json | 12 + .../gso/items/materials/ore/titanium_ore.json | 12 + .../gso/items/materials/ore/tungsten_ore.json | 12 + .../gso/items/materials/stone/fluxstone.json | 12 + .../gso/items/materials/wood/wood_planks.json | 12 + .../items/ships/dreadnought_legendary.json | 27 + .../gso/items/ships/heavy_cruiser_rare.json | 28 + .../gso/items/ships/interceptor_common.json | 28 + .../gso/items/ships/interceptor_uncommon.json | 28 + .../data/gso/items/ships/phantom_epic.json | 27 + .../items/ships/starter_cruiser_common.json | 28 + .../gso/items/ships/starter_cruiser_rare.json | 28 + .../items/ships/starter_cruiser_uncommon.json | 28 + .../assaultrifle/assault_rifle_common.json | 13 + .../assaultrifle/assault_rifle_epic.json | 13 + .../assaultrifle/assault_rifle_rare.json | 13 + .../weapon/dualpistol/dual_pistol_common.json | 13 + .../weapon/dualpistol/dual_pistol_epic.json | 13 + .../weapon/dualpistol/dual_pistol_rare.json | 13 + .../weapon/laserbow/laser_bow_common.json | 13 + .../items/weapon/laserbow/laser_bow_epic.json | 13 + .../items/weapon/laserbow/laser_bow_rare.json | 13 + .../lasercrossbow/laser_crossbow_common.json | 13 + .../lasercrossbow/laser_crossbow_epic.json | 13 + .../lasercrossbow/laser_crossbow_rare.json | 13 + .../weapon/pistol/pistol_marksman_rare.json | 13 + .../weapon/pistol/pistol_overcharge_epic.json | 13 + .../weapon/pistol/pistol_standard_common.json | 13 + .../plasmacutter/plasma_cutter_common.json | 13 + .../plasmacutter/plasma_cutter_epic.json | 13 + .../plasmacutter/plasma_cutter_rare.json | 13 + .../rocket_launcher_common.json | 13 + .../rocketlauncher/rocket_launcher_epic.json | 13 + .../rocketlauncher/rocket_launcher_rare.json | 13 + .../items/weapon/sniper/sniper_common.json | 13 + .../gso/items/weapon/sniper/sniper_epic.json | 13 + .../gso/items/weapon/sniper/sniper_rare.json | 13 + .../items/weapons/assault_rifle_common.json | 27 + .../gso/items/weapons/assault_rifle_rare.json | 27 + .../gso/items/weapons/laser_bow_epic.json | 26 + .../items/weapons/pistol_marksman_rare.json | 27 + .../items/weapons/pistol_standard_common.json | 27 + .../gso/items/weapons/plasma_cutter_rare.json | 27 + .../data/gso/items/weapons/sniper_common.json | 27 + .../gso/items/weapons/starter_blaster.json | 27 + .../data/gso/missions/faction_missions.json | 12 + .../data/gso/quests/daily/d_craft_3.json | 46 + .../gso/quests/daily/d_customize_ship.json | 44 + .../data/gso/quests/daily/d_decor_10.json | 46 + .../data/gso/quests/daily/d_dungeons_3.json | 44 + .../data/gso/quests/daily/d_explore_30.json | 35 + .../gso/quests/daily/d_kill_pirates_10.json | 46 + .../data/gso/quests/daily/d_login_5.json | 44 + .../data/gso/quests/daily/d_smelt_5.json | 46 + .../quests/daily/d_travel_planet_1000.json | 37 + .../gso/quests/main_story/ms_ch1_001.json | 78 + .../gso/quests/main_story/ms_ch1_002.json | 65 + .../gso/quests/main_story/ms_ch1_003.json | 63 + .../gso/quests/main_story/ms_ch2_001.json | 67 + .../gso/quests/main_story/ms_ch2_002.json | 63 + .../gso/quests/main_story/ms_ch2_003.json | 63 + .../gso/quests/main_story/ms_ch3_001.json | 63 + .../gso/quests/main_story/ms_ch3_002.json | 72 + .../GameServer/data/gso/quests/manifest.json | 12 + .../data/gso/quests/monthly/m_craft_100.json | 50 + .../data/gso/quests/monthly/m_decor_100.json | 46 + .../gso/quests/monthly/m_dungeons_50.json | 52 + .../gso/quests/monthly/m_guild_fight_10.json | 48 + .../data/gso/quests/monthly/m_hire_5.json | 50 + .../gso/quests/monthly/m_lightyears_100.json | 44 + .../data/gso/quests/monthly/m_raids_20.json | 48 + .../data/gso/quests/monthly/m_void_run.json | 50 + .../data/gso/quests/weekly/w_craft_20.json | 55 + .../data/gso/quests/weekly/w_dungeons_10.json | 48 + .../quests/weekly/w_explore_planets_3.json | 48 + .../data/gso/quests/weekly/w_friends_5.json | 39 + .../data/gso/quests/weekly/w_gather_50.json | 50 + .../gso/quests/weekly/w_guild_fight_3.json | 44 + .../gso/quests/weekly/w_lightyears_25.json | 44 + .../data/gso/quests/weekly/w_raids_3.json | 48 + .../quests/weekly/w_survive_hardcore_7.json | 39 + .../GameServer/data/gso/recipes/.gitkeep | 0 .../gso/recipes/alloys/ablative_plating.json | 18 + .../recipes/alloys/aerospace_aluminum.json | 18 + .../gso/recipes/alloys/ballistic_alloy.json | 18 + .../alloys/carbon_titanium_composite.json | 18 + .../gso/recipes/alloys/chromium_steel.json | 18 + .../data/gso/recipes/alloys/chrono_alloy.json | 19 + .../gso/recipes/alloys/dimensional_alloy.json | 19 + .../gso/recipes/alloys/flux_core_alloy.json | 19 + .../recipes/alloys/fusion_rated_alloy.json | 18 + .../recipes/alloys/neutronium_composite.json | 19 + .../data/gso/recipes/alloys/phase_alloy.json | 18 + .../recipes/alloys/reactive_armor_alloy.json | 18 + .../gso/recipes/alloys/reinforced_steel.json | 18 + .../data/gso/recipes/alloys/steel_alloy.json | 18 + .../recipes/alloys/superconductive_alloy.json | 19 + .../gso/recipes/alloys/titanium_alloy.json | 18 + .../gso/recipes/alloys/tungsten_steel.json | 18 + .../data/gso/recipes/alloys/voidsteel.json | 19 + .../data/gso/recipes/armours/body_basic.json | 18 + .../data/gso/recipes/armours/body_plated.json | 18 + .../data/gso/recipes/armours/boots_basic.json | 18 + .../gso/recipes/armours/gloves_basic.json | 18 + .../gso/recipes/armours/helmet_basic.json | 18 + .../data/gso/recipes/armours/pants_basic.json | 18 + .../gso/recipes/circuits/ai_core_circuit.json | 18 + .../gso/recipes/circuits/basic_circuit.json | 18 + .../gso/recipes/circuits/control_circuit.json | 18 + .../circuits/dimensional_logic_circuit.json | 19 + .../circuits/high_density_circuit.json | 18 + .../gso/recipes/circuits/logic_circuit.json | 18 + .../recipes/circuits/navigation_circuit.json | 18 + .../gso/recipes/circuits/power_circuit.json | 19 + .../recipes/circuits/processor_circuit.json | 19 + .../gso/recipes/circuits/quantum_circuit.json | 19 + .../circuits/reactor_control_circuit.json | 18 + .../circuits/sensor_array_circuit.json | 18 + .../circuits/sentient_core_circuit.json | 19 + .../circuits/shield_control_circuit.json | 18 + .../gso/recipes/circuits/stealth_circuit.json | 18 + .../circuits/warp_navigation_circuit.json | 18 + .../circuits/weapon_control_circuit.json | 18 + .../consumables/energy_boost_small.json | 18 + .../gso/recipes/consumables/fuel_cell.json | 18 + .../recipes/consumables/health_kit_large.json | 18 + .../recipes/consumables/health_kit_small.json | 18 + .../data/gso/recipes/crafting_tabs.json | 212 + .../recipes/food_organics/alien_protein.json | 17 + .../data/gso/recipes/food_organics/bread.json | 17 + .../recipes/food_organics/cooked_fish.json | 17 + .../recipes/food_organics/cooked_meat.json | 17 + .../data/gso/recipes/food_organics/dough.json | 18 + .../gso/recipes/food_organics/fish_meat.json | 17 + .../data/gso/recipes/food_organics/flour.json | 17 + .../data/gso/recipes/food_organics/grain.json | 17 + .../hydroponic_growth_medium.json | 19 + .../recipes/food_organics/luxury_meal.json | 19 + .../gso/recipes/food_organics/meat_stew.json | 19 + .../food_organics/military_ration.json | 18 + .../recipes/food_organics/nutrient_paste.json | 18 + .../recipes/food_organics/protein_bar.json | 18 + .../recipes/food_organics/ration_pack.json | 18 + .../gso/recipes/food_organics/raw_fruits.json | 17 + .../gso/recipes/food_organics/raw_meat.json | 17 + .../recipes/food_organics/raw_vegetables.json | 17 + .../recipes/food_organics/vegetable_stew.json | 18 + .../data/gso/recipes/food_organics/water.json | 17 + .../hull_sections/advanced_hull_section.json | 18 + .../hull_sections/armor_hull_section.json | 18 + .../hull_sections/exotic_hull_section.json | 18 + .../hull_sections/heavy_hull_section.json | 18 + .../hull_sections/light_hull_section.json | 18 + .../hull_sections/standard_hull_section.json | 18 + .../recipes/hulls/ablative_armor_hull.json | 15 + .../data/gso/recipes/hulls/advanced_hull.json | 18 + .../data/gso/recipes/hulls/armor_hull.json | 18 + .../recipes/hulls/chrono_stabilized_hull.json | 15 + .../hulls/dimensional_anchor_hull.json | 15 + .../gso/recipes/hulls/dreadframe_hull.json | 19 + .../data/gso/recipes/hulls/heavy_hull.json | 18 + .../gso/recipes/hulls/industrial_hull.json | 15 + .../gso/recipes/hulls/lightframe_hull.json | 18 + .../gso/recipes/hulls/phase_locked_hull.json | 19 + .../recipes/hulls/plasma_resistant_hull.json | 15 + .../hulls/reinforced_plating_hull.json | 15 + .../data/gso/recipes/hulls/standard_hull.json | 18 + .../hulls/standard_structural_hull.json | 15 + .../recipes/hulls/superconductive_hull.json | 15 + .../hulls/titanium_aerospace_hull.json | 15 + .../gso/recipes/hulls/voidsteel_hull.json | 18 + .../GameServer/data/gso/recipes/manifest.json | 21 + .../data/gso/recipes/organics/bandage.json | 18 + .../organics/bio_composite_material.json | 19 + .../data/gso/recipes/organics/bio_pulp.json | 17 + .../gso/recipes/organics/medical_pack.json | 18 + .../recipes/organics/organic_insulation.json | 18 + .../gso/recipes/organics/plant_cloth.json | 17 + .../gso/recipes/organics/plant_fiber.json | 17 + .../gso/recipes/organics/processed_wood.json | 17 + .../data/gso/recipes/organics/raw_wood.json | 17 + .../data/gso/recipes/organics/resin.json | 17 + .../data/gso/recipes/organics/tree_sap.json | 17 + .../gso/recipes/organics/wood_planks.json | 17 + .../recipes/organics/wooden_components.json | 18 + .../gso/recipes/smelting/aluminum_ingot.json | 17 + .../gso/recipes/smelting/carbon_ingot.json | 18 + .../gso/recipes/smelting/chromium_ingot.json | 17 + .../gso/recipes/smelting/chronium_ingot.json | 17 + .../gso/recipes/smelting/copper_ingot.json | 17 + .../gso/recipes/smelting/darksteel_ingot.json | 18 + .../data/gso/recipes/smelting/gold_ingot.json | 17 + .../data/gso/recipes/smelting/iron_ingot.json | 17 + .../gso/recipes/smelting/magnesium_ingot.json | 17 + .../recipes/smelting/neutronium_ingot.json | 17 + .../gso/recipes/smelting/platinum_ingot.json | 17 + .../gso/recipes/smelting/silver_ingot.json | 17 + .../gso/recipes/smelting/titanium_ingot.json | 17 + .../gso/recipes/smelting/tungsten_ingot.json | 17 + .../recipes/spacesuit_parts/body_exotic.json | 19 + .../recipes/spacesuit_parts/body_light.json | 18 + .../spacesuit_parts/body_military.json | 19 + .../spacesuit_parts/body_standard.json | 19 + .../recipes/spacesuit_parts/boots_light.json | 18 + .../spacesuit_parts/boots_magnetic.json | 19 + .../spacesuit_parts/boots_military.json | 19 + .../spacesuit_parts/boots_standard.json | 19 + .../spacesuit_parts/gloves_industrial.json | 19 + .../recipes/spacesuit_parts/gloves_light.json | 18 + .../spacesuit_parts/gloves_military.json | 19 + .../spacesuit_parts/gloves_standard.json | 19 + .../spacesuit_parts/helmet_exotic.json | 19 + .../recipes/spacesuit_parts/helmet_light.json | 19 + .../spacesuit_parts/helmet_military.json | 20 + .../spacesuit_parts/helmet_standard.json | 20 + .../spacesuit_parts/life_support_basic.json | 19 + .../spacesuit_parts/life_support_exotic.json | 19 + .../life_support_military.json | 19 + .../life_support_standard.json | 20 + .../recipes/spacesuit_parts/pants_exotic.json | 19 + .../recipes/spacesuit_parts/pants_heavy.json | 19 + .../recipes/spacesuit_parts/pants_light.json | 18 + .../spacesuit_parts/pants_standard.json | 19 + .../gso/recipes/weapons/assault_rifle.json | 19 + .../gso/recipes/weapons/plasma_cutter.json | 19 + .../gso/recipes/weapons/sniper_rifle.json | 19 + .../gso/recipes/weapons/standard_pistol.json | 18 + .../gso/recipes/weapons/starter_blaster.json | 18 + .../GameServer/data/gso/skills/combat.json | 48 + .../gso/skills/combat/defense_training.json | 15 + .../data/gso/skills/combat/piloting.json | 15 + .../gso/skills/combat/speed_reflexes.json | 15 + .../gso/skills/combat/tactical_analysis.json | 15 + .../gso/skills/combat/weapons_mastery.json | 15 + .../GameServer/data/gso/skills/crafting.json | 48 + .../gso/skills/crafting/armor_crafting.json | 15 + .../gso/skills/crafting/general_crafting.json | 14 + .../skills/crafting/resource_extraction.json | 15 + .../gso/skills/crafting/smelting_mastery.json | 15 + .../gso/skills/crafting/weapons_crafting.json | 15 + .../GameServer/data/gso/skills/manifest.json | 10 + .../GameServer/data/gso/skills/science.json | 49 + .../gso/skills/science/alien_technology.json | 15 + .../gso/skills/science/bio_engineering.json | 15 + .../skills/science/energy_manipulation.json | 14 + .../data/gso/skills/science/engineering.json | 14 + .../gso/skills/science/quantum_physics.json | 15 + .../GameServer/data/mods.db | Bin 0 -> 40960 bytes .../GameServer/generate_assets.js | 209 + .../GameServer/middleware/errorHandler.js | 40 + .../GameServer/models/PlayerData.js | 181 + .../GameServer/package-lock.json | 3293 +++++++++ .../GameServer/package.json | 39 + .../GameServer/routes/base.js | 243 + .../GameServer/routes/crafting.js | 212 + .../GameServer/routes/dungeons.js | 380 ++ .../GameServer/routes/idle.js | 282 + .../GameServer/routes/quests.js | 232 + .../GameServer/routes/ships.js | 452 ++ .../GameServer/routes/skills.js | 187 + .../GameServer/server.js | 2498 +++++++ .../services/ServerRegistrationService.js | 161 + .../GameServer/socket/socketHandlers.js | 393 ++ .../GameServer/systems/AllianceSystem.js | 177 + .../GameServer/systems/BaseSystem.js | 512 ++ .../GameServer/systems/ContentLoader.js | 305 + .../GameServer/systems/CraftingSystem.js | 30 + .../GameServer/systems/DungeonSystem.js | 171 + .../GameServer/systems/FleetSystem.js | 53 + .../GameServer/systems/GalaxyEventSystem.js | 122 + .../GameServer/systems/GalaxySystem.js | 112 + .../GameServer/systems/GameSystem.js | 284 + .../GameServer/systems/IdleSystem.js | 323 + .../GameServer/systems/ItemSystem.js | 210 + .../GameServer/systems/MarketSystem.js | 206 + .../GameServer/systems/MissionSystem.js | 237 + .../GameServer/systems/QuestSystem.js | 61 + .../GameServer/systems/ReputationSystem.js | 66 + .../GameServer/systems/ResearchSystem.js | 110 + .../GameServer/systems/ResourceSystem.js | 146 + .../GameServer/systems/SeasonSystem.js | 69 + .../GameServer/systems/ShipSystem.js | 488 ++ .../GameServer/systems/SkillSystem.js | 29 + .../GameServer/systems/SocialSystem.js | 113 + .../GameServer/utils/logger.js | 27 + Galaxy-Strike-Online-main/LICENSE | 21 + .../Wiki/SETUP_GUIDE.html | 256 + .../config/xp-progression.js | 250 + .../docs/Server-Modding-Guide.md | 376 + .../data/gso/category/quests/daily.json | 31 + .../data/gso/category/quests/main_story.json | 39 + .../data/gso/category/quests/monthly.json | 23 + .../data/gso/category/quests/weekly.json | 27 + GameServer/data/gso/dungeons/alien_ruins.json | 50 + .../data/gso/dungeons/asteroid_mine.json | 50 + .../data/gso/dungeons/corrupted_vault.json | 56 + .../data/gso/dungeons/nebula_anomaly.json | 50 + GameServer/data/gso/dungeons/pirate_lair.json | 50 + GameServer/data/gso/dungeons/void_rift.json | 56 + .../data/gso/enemies/alien_guardian.json | 15 + .../data/gso/enemies/ancient_drone.json | 15 + GameServer/data/gso/enemies/corrupted_ai.json | 15 + .../data/gso/enemies/crystal_golem.json | 15 + GameServer/data/gso/enemies/energy_being.json | 15 + GameServer/data/gso/enemies/mining_drone.json | 15 + .../data/gso/enemies/phase_shifter.json | 15 + .../data/gso/enemies/pirate_captain.json | 15 + .../data/gso/enemies/quantum_entity.json | 16 + .../data/gso/enemies/rock_creature.json | 15 + .../data/gso/enemies/security_drone.json | 15 + GameServer/data/gso/enemies/space_pirate.json | 15 + .../data/gso/enemies/training_drone.json | 12 + .../data/gso/enemies/virus_program.json | 15 + GameServer/data/gso/items/.gitkeep | 0 .../backpack/backpack_basic_common.json | 14 + .../armour/backpack/backpack_field_rare.json | 14 + .../backpack/backpack_reactor_epic.json | 14 + .../items/armour/body/body_basic_common.json | 14 + .../items/armour/body/body_exosuit_epic.json | 14 + .../items/armour/body/body_plated_rare.json | 14 + .../armour/boots/boots_assault_rare.json | 14 + .../armour/boots/boots_basic_common.json | 14 + .../armour/boots/boots_gravity_epic.json | 14 + .../armour/hands/hands_basic_common.json | 14 + .../items/armour/hands/hands_combat_rare.json | 14 + .../items/armour/hands/hands_elite_epic.json | 14 + .../armour/helmet/helmet_basic_common.json | 14 + .../armour/helmet/helmet_reinforced_rare.json | 14 + .../armour/helmet/helmet_tactical_epic.json | 14 + .../armour/pants/pants_basic_common.json | 14 + .../items/armour/pants/pants_exo_epic.json | 14 + .../armour/pants/pants_reinforced_rare.json | 14 + .../items/armours/backpack_basic_common.json | 30 + .../gso/items/armours/body_basic_common.json | 29 + .../gso/items/armours/body_exosuit_epic.json | 28 + .../gso/items/armours/body_plated_rare.json | 29 + .../gso/items/armours/boots_basic_common.json | 29 + .../items/armours/gloves_basic_common.json | 29 + .../items/armours/helmet_basic_common.json | 29 + .../items/armours/helmet_reinforced_rare.json | 29 + .../gso/items/armours/pants_basic_common.json | 29 + .../items/consumables/credit_multiplier.json | 26 + .../items/consumables/energy_boost_large.json | 25 + .../items/consumables/energy_boost_small.json | 26 + .../data/gso/items/consumables/fuel_cell.json | 27 + .../items/consumables/health_kit_large.json | 26 + .../items/consumables/health_kit_small.json | 26 + .../items/consumables/scrap_pack_small.json | 35 + .../gso/items/consumables/starter_crate.json | 39 + .../gso/items/consumables/xp_booster.json | 26 + .../gso/items/decorations/room_armory.json | 10 + .../items/decorations/room_operations.json | 10 + .../items/decorations/room_research_lab.json | 10 + .../gso/items/decorations/room_throne.json | 10 + .../gso/items/decorations/room_vault.json | 10 + .../gso/items/decorations/wp_alien_ruins.json | 12 + .../items/decorations/wp_arctic_station.json | 12 + .../gso/items/decorations/wp_deep_ocean.json | 12 + .../items/decorations/wp_golden_empire.json | 12 + .../gso/items/decorations/wp_lava_forge.json | 12 + .../gso/items/decorations/wp_nebula_dawn.json | 12 + .../gso/items/decorations/wp_void_black.json | 12 + .../gso/items/decorations/wp_void_rift.json | 12 + .../misc/ballistic_alloy_common.json | 14 + .../carbon_titanium_composite_common.json | 14 + .../hullPlating/misc/chrono_alloy_common.json | 14 + .../misc/dimensional_alloy_common.json | 14 + .../misc/flux_core_alloy_common.json | 14 + .../misc/neutronium_composite_common.json | 14 + .../hullPlating/misc/phase_alloy_common.json | 14 + .../misc/plasma_channel_alloy_common.json | 14 + .../misc/reinforced_steel_common.json | 14 + .../misc/superconductive_alloy_common.json | 14 + .../hullPlating/misc/voidsteel_common.json | 14 + .../plating/ablative_plating_common.json | 14 + .../section/advanced_hull_section_common.json | 14 + .../section/armor_hull_section_common.json | 14 + .../section/exotic_hull_section_common.json | 14 + .../section/heavy_hull_section_common.json | 14 + .../section/light_hull_section_common.json | 14 + .../section/standard_hull_section_common.json | 14 + GameServer/data/gso/items/manifest.json | 20 + .../gso/items/materials/bio/bio_pulp.json | 12 + .../materials/bio/organic_insulation.json | 12 + .../gso/items/materials/bio/plant_matter.json | 12 + .../materials/chemical/aluminum_alloy.json | 12 + .../chemical/bio_composite_material.json | 12 + .../chemical/carbon_nanotube_alloy.json | 12 + .../chemical/carbon_titanium_composite.json | 16 + .../materials/chemical/ceramic_composite.json | 12 + .../materials/chemical/chrono_alloy.json | 16 + .../materials/chemical/dimensional_alloy.json | 16 + .../chemical/explosive_compound.json | 12 + .../items/materials/chemical/flex_alloy.json | 12 + .../materials/chemical/flux_core_alloy.json | 16 + .../chemical/fusion_rated_alloy.json | 12 + .../materials/chemical/glass_composite.json | 12 + .../chemical/neutronium_composite.json | 16 + .../items/materials/chemical/phase_alloy.json | 16 + .../chemical/radiation_shield_alloy.json | 12 + .../chemical/reactive_armor_alloy.json | 12 + .../materials/chemical/reinforced_steel.json | 16 + .../gso/items/materials/chemical/resin.json | 12 + .../materials/chemical/rubber_polymer.json | 12 + .../items/materials/chemical/servo_alloy.json | 12 + .../items/materials/chemical/steel_alloy.json | 12 + .../materials/chemical/titanium_alloy.json | 12 + .../materials/chemical/transparent_alloy.json | 12 + .../materials/chemical/tungsten_alloy.json | 12 + .../items/materials/chemical/voidsteel.json | 16 + .../electronics/ai_core_circuit.json | 12 + .../materials/electronics/basic_circuit.json | 12 + .../electronics/control_circuit.json | 12 + .../dimensional_logic_circuit.json | 12 + .../electronics/high_density_circuit.json | 12 + .../materials/electronics/logic_circuit.json | 12 + .../electronics/navigation_circuit.json | 12 + .../materials/electronics/power_circuit.json | 12 + .../electronics/processor_circuit.json | 12 + .../electronics/quantum_circuit.json | 12 + .../electronics/reactor_control_circuit.json | 12 + .../electronics/sensor_array_circuit.json | 12 + .../electronics/sentient_core_circuit.json | 12 + .../electronics/shield_control_circuit.json | 12 + .../electronics/stealth_circuit.json | 12 + .../electronics/warp_navigation_circuit.json | 12 + .../electronics/weapon_control_circuit.json | 12 + .../items/materials/fabric/plant_cloth.json | 12 + .../items/materials/fabric/plant_fiber.json | 12 + .../data/gso/items/materials/food/bread.json | 12 + .../data/gso/items/materials/food/fish.json | 12 + .../gso/items/materials/food/fish_meat.json | 12 + .../data/gso/items/materials/food/flour.json | 12 + .../gso/items/materials/food/fruit_plant.json | 12 + .../data/gso/items/materials/food/grain.json | 12 + .../gso/items/materials/food/grain_crop.json | 12 + .../gso/items/materials/food/meat_stew.json | 12 + .../gso/items/materials/food/ration_pack.json | 12 + .../items/materials/food/vegetable_plant.json | 12 + .../items/materials/food/vegetable_stew.json | 12 + .../items/materials/ingot/aluminum_ingot.json | 12 + .../items/materials/ingot/carbon_ingot.json | 12 + .../items/materials/ingot/chromium_ingot.json | 12 + .../items/materials/ingot/chronium_ingot.json | 12 + .../items/materials/ingot/copper_ingot.json | 12 + .../materials/ingot/darksteel_ingot.json | 12 + .../gso/items/materials/ingot/gold_ingot.json | 12 + .../gso/items/materials/ingot/iron_ingot.json | 12 + .../materials/ingot/magnesium_ingot.json | 12 + .../materials/ingot/neutronium_ingot.json | 12 + .../items/materials/ingot/platinum_ingot.json | 12 + .../items/materials/ingot/silver_ingot.json | 12 + .../items/materials/ingot/titanium_ingot.json | 12 + .../items/materials/ingot/tungsten_ingot.json | 12 + .../gso/items/materials/liquid/water.json | 12 + .../materials/misc/aerospace_aluminum.json | 12 + .../materials/misc/alien_fauna_sample.json | 12 + .../items/materials/misc/alien_protein.json | 12 + .../items/materials/misc/animal_carcass.json | 12 + .../gso/items/materials/misc/bandage.json | 12 + .../gso/items/materials/misc/body_exotic.json | 12 + .../gso/items/materials/misc/body_light.json | 12 + .../items/materials/misc/body_military.json | 12 + .../items/materials/misc/body_standard.json | 12 + .../gso/items/materials/misc/boots_light.json | 12 + .../items/materials/misc/boots_magnetic.json | 12 + .../items/materials/misc/boots_military.json | 12 + .../items/materials/misc/boots_standard.json | 12 + .../items/materials/misc/carbon_shale.json | 12 + .../items/materials/misc/chromium_steel.json | 12 + .../gso/items/materials/misc/cooked_fish.json | 12 + .../gso/items/materials/misc/cooked_meat.json | 12 + .../items/materials/misc/crystal_glass.json | 12 + .../data/gso/items/materials/misc/dough.json | 12 + .../items/materials/misc/flux_crystal.json | 12 + .../materials/misc/gloves_industrial.json | 12 + .../items/materials/misc/gloves_light.json | 12 + .../items/materials/misc/gloves_military.json | 12 + .../items/materials/misc/gloves_standard.json | 12 + .../items/materials/misc/helmet_exotic.json | 12 + .../items/materials/misc/helmet_light.json | 12 + .../items/materials/misc/helmet_military.json | 12 + .../items/materials/misc/helmet_standard.json | 12 + .../misc/hydroponic_growth_medium.json | 12 + .../gso/items/materials/misc/ice_chunk.json | 12 + .../items/materials/misc/iron_ore_common.json | 16 + .../materials/misc/life_support_basic.json | 12 + .../materials/misc/life_support_exotic.json | 12 + .../materials/misc/life_support_military.json | 12 + .../materials/misc/life_support_standard.json | 12 + .../gso/items/materials/misc/luxury_meal.json | 12 + .../items/materials/misc/magnetic_coil.json | 12 + .../items/materials/misc/medical_pack.json | 12 + .../items/materials/misc/military_ration.json | 12 + .../materials/misc/neutronium_shard.json | 12 + .../items/materials/misc/nutrient_paste.json | 12 + .../items/materials/misc/pants_exotic.json | 12 + .../gso/items/materials/misc/pants_heavy.json | 12 + .../gso/items/materials/misc/pants_light.json | 12 + .../items/materials/misc/pants_standard.json | 12 + .../items/materials/misc/phase_crystal.json | 12 + .../items/materials/misc/processed_wood.json | 12 + .../gso/items/materials/misc/protein_bar.json | 12 + .../gso/items/materials/misc/raw_fruits.json | 12 + .../gso/items/materials/misc/raw_meat.json | 12 + .../items/materials/misc/raw_vegetables.json | 12 + .../gso/items/materials/misc/raw_wood.json | 12 + .../items/materials/misc/silicon_wafer.json | 12 + .../data/gso/items/materials/misc/tree.json | 12 + .../gso/items/materials/misc/tree_sap.json | 12 + .../items/materials/misc/tungsten_steel.json | 12 + .../items/materials/misc/void_crystal.json | 12 + .../items/materials/misc/void_residue.json | 12 + .../materials/misc/wooden_components.json | 12 + .../gso/items/materials/ore/bauxite_ore.json | 12 + .../gso/items/materials/ore/chromium_ore.json | 12 + .../gso/items/materials/ore/chronite_ore.json | 12 + .../gso/items/materials/ore/copper_ore.json | 12 + .../items/materials/ore/dark_iron_ore.json | 12 + .../gso/items/materials/ore/gold_ore.json | 12 + .../gso/items/materials/ore/iron_ore.json | 12 + .../items/materials/ore/magnesium_ore.json | 12 + .../gso/items/materials/ore/platinum_ore.json | 12 + .../gso/items/materials/ore/silver_ore.json | 12 + .../gso/items/materials/ore/titanium_ore.json | 12 + .../gso/items/materials/ore/tungsten_ore.json | 12 + .../gso/items/materials/stone/fluxstone.json | 12 + .../gso/items/materials/wood/wood_planks.json | 12 + .../items/ships/dreadnought_legendary.json | 27 + .../gso/items/ships/heavy_cruiser_rare.json | 28 + .../gso/items/ships/interceptor_common.json | 28 + .../gso/items/ships/interceptor_uncommon.json | 28 + .../data/gso/items/ships/phantom_epic.json | 27 + .../items/ships/starter_cruiser_common.json | 28 + .../gso/items/ships/starter_cruiser_rare.json | 28 + .../items/ships/starter_cruiser_uncommon.json | 28 + .../assaultrifle/assault_rifle_common.json | 13 + .../assaultrifle/assault_rifle_epic.json | 13 + .../assaultrifle/assault_rifle_rare.json | 13 + .../weapon/dualpistol/dual_pistol_common.json | 13 + .../weapon/dualpistol/dual_pistol_epic.json | 13 + .../weapon/dualpistol/dual_pistol_rare.json | 13 + .../weapon/laserbow/laser_bow_common.json | 13 + .../items/weapon/laserbow/laser_bow_epic.json | 13 + .../items/weapon/laserbow/laser_bow_rare.json | 13 + .../lasercrossbow/laser_crossbow_common.json | 13 + .../lasercrossbow/laser_crossbow_epic.json | 13 + .../lasercrossbow/laser_crossbow_rare.json | 13 + .../weapon/pistol/pistol_marksman_rare.json | 13 + .../weapon/pistol/pistol_overcharge_epic.json | 13 + .../weapon/pistol/pistol_standard_common.json | 13 + .../plasmacutter/plasma_cutter_common.json | 13 + .../plasmacutter/plasma_cutter_epic.json | 13 + .../plasmacutter/plasma_cutter_rare.json | 13 + .../rocket_launcher_common.json | 13 + .../rocketlauncher/rocket_launcher_epic.json | 13 + .../rocketlauncher/rocket_launcher_rare.json | 13 + .../items/weapon/sniper/sniper_common.json | 13 + .../gso/items/weapon/sniper/sniper_epic.json | 13 + .../gso/items/weapon/sniper/sniper_rare.json | 13 + .../items/weapons/assault_rifle_common.json | 27 + .../gso/items/weapons/assault_rifle_rare.json | 27 + .../gso/items/weapons/laser_bow_epic.json | 26 + .../items/weapons/pistol_marksman_rare.json | 27 + .../items/weapons/pistol_standard_common.json | 27 + .../gso/items/weapons/plasma_cutter_rare.json | 27 + .../data/gso/items/weapons/sniper_common.json | 27 + .../gso/items/weapons/starter_blaster.json | 27 + .../data/gso/quests/daily/d_craft_3.json | 46 + .../gso/quests/daily/d_customize_ship.json | 44 + .../data/gso/quests/daily/d_decor_10.json | 46 + .../data/gso/quests/daily/d_dungeons_3.json | 44 + .../data/gso/quests/daily/d_explore_30.json | 35 + .../gso/quests/daily/d_kill_pirates_10.json | 46 + .../data/gso/quests/daily/d_login_5.json | 44 + .../data/gso/quests/daily/d_smelt_5.json | 46 + .../quests/daily/d_travel_planet_1000.json | 37 + .../gso/quests/main_story/ms_ch1_001.json | 78 + .../gso/quests/main_story/ms_ch1_002.json | 65 + .../gso/quests/main_story/ms_ch1_003.json | 63 + .../gso/quests/main_story/ms_ch2_001.json | 67 + .../gso/quests/main_story/ms_ch2_002.json | 63 + .../gso/quests/main_story/ms_ch2_003.json | 63 + .../gso/quests/main_story/ms_ch3_001.json | 63 + .../gso/quests/main_story/ms_ch3_002.json | 72 + GameServer/data/gso/quests/manifest.json | 12 + .../data/gso/quests/monthly/m_craft_100.json | 50 + .../data/gso/quests/monthly/m_decor_100.json | 46 + .../gso/quests/monthly/m_dungeons_50.json | 52 + .../gso/quests/monthly/m_guild_fight_10.json | 48 + .../data/gso/quests/monthly/m_hire_5.json | 50 + .../gso/quests/monthly/m_lightyears_100.json | 44 + .../data/gso/quests/monthly/m_raids_20.json | 48 + .../data/gso/quests/monthly/m_void_run.json | 50 + .../data/gso/quests/weekly/w_craft_20.json | 55 + .../data/gso/quests/weekly/w_dungeons_10.json | 48 + .../quests/weekly/w_explore_planets_3.json | 48 + .../data/gso/quests/weekly/w_friends_5.json | 39 + .../data/gso/quests/weekly/w_gather_50.json | 50 + .../gso/quests/weekly/w_guild_fight_3.json | 44 + .../gso/quests/weekly/w_lightyears_25.json | 44 + .../data/gso/quests/weekly/w_raids_3.json | 48 + .../quests/weekly/w_survive_hardcore_7.json | 39 + GameServer/data/gso/recipes/.gitkeep | 0 .../gso/recipes/alloys/ablative_plating.json | 18 + .../recipes/alloys/aerospace_aluminum.json | 18 + .../gso/recipes/alloys/ballistic_alloy.json | 18 + .../alloys/carbon_titanium_composite.json | 18 + .../gso/recipes/alloys/chromium_steel.json | 18 + .../data/gso/recipes/alloys/chrono_alloy.json | 19 + .../gso/recipes/alloys/dimensional_alloy.json | 19 + .../gso/recipes/alloys/flux_core_alloy.json | 19 + .../recipes/alloys/fusion_rated_alloy.json | 18 + .../recipes/alloys/neutronium_composite.json | 19 + .../data/gso/recipes/alloys/phase_alloy.json | 18 + .../recipes/alloys/reactive_armor_alloy.json | 18 + .../gso/recipes/alloys/reinforced_steel.json | 18 + .../data/gso/recipes/alloys/steel_alloy.json | 18 + .../recipes/alloys/superconductive_alloy.json | 19 + .../gso/recipes/alloys/titanium_alloy.json | 18 + .../gso/recipes/alloys/tungsten_steel.json | 18 + .../data/gso/recipes/alloys/voidsteel.json | 19 + .../data/gso/recipes/armours/body_basic.json | 18 + .../data/gso/recipes/armours/body_plated.json | 18 + .../data/gso/recipes/armours/boots_basic.json | 18 + .../gso/recipes/armours/gloves_basic.json | 18 + .../gso/recipes/armours/helmet_basic.json | 18 + .../data/gso/recipes/armours/pants_basic.json | 18 + .../gso/recipes/circuits/ai_core_circuit.json | 18 + .../gso/recipes/circuits/basic_circuit.json | 18 + .../gso/recipes/circuits/control_circuit.json | 18 + .../circuits/dimensional_logic_circuit.json | 19 + .../circuits/high_density_circuit.json | 18 + .../gso/recipes/circuits/logic_circuit.json | 18 + .../recipes/circuits/navigation_circuit.json | 18 + .../gso/recipes/circuits/power_circuit.json | 19 + .../recipes/circuits/processor_circuit.json | 19 + .../gso/recipes/circuits/quantum_circuit.json | 19 + .../circuits/reactor_control_circuit.json | 18 + .../circuits/sensor_array_circuit.json | 18 + .../circuits/sentient_core_circuit.json | 19 + .../circuits/shield_control_circuit.json | 18 + .../gso/recipes/circuits/stealth_circuit.json | 18 + .../circuits/warp_navigation_circuit.json | 18 + .../circuits/weapon_control_circuit.json | 18 + .../consumables/energy_boost_small.json | 18 + .../gso/recipes/consumables/fuel_cell.json | 18 + .../recipes/consumables/health_kit_large.json | 18 + .../recipes/consumables/health_kit_small.json | 18 + .../data/gso/recipes/crafting_tabs.json | 212 + .../recipes/food_organics/alien_protein.json | 17 + .../data/gso/recipes/food_organics/bread.json | 17 + .../recipes/food_organics/cooked_fish.json | 17 + .../recipes/food_organics/cooked_meat.json | 17 + .../data/gso/recipes/food_organics/dough.json | 18 + .../gso/recipes/food_organics/fish_meat.json | 17 + .../data/gso/recipes/food_organics/flour.json | 17 + .../data/gso/recipes/food_organics/grain.json | 17 + .../hydroponic_growth_medium.json | 19 + .../recipes/food_organics/luxury_meal.json | 19 + .../gso/recipes/food_organics/meat_stew.json | 19 + .../food_organics/military_ration.json | 18 + .../recipes/food_organics/nutrient_paste.json | 18 + .../recipes/food_organics/protein_bar.json | 18 + .../recipes/food_organics/ration_pack.json | 18 + .../gso/recipes/food_organics/raw_fruits.json | 17 + .../gso/recipes/food_organics/raw_meat.json | 17 + .../recipes/food_organics/raw_vegetables.json | 17 + .../recipes/food_organics/vegetable_stew.json | 18 + .../data/gso/recipes/food_organics/water.json | 17 + .../hull_sections/advanced_hull_section.json | 18 + .../hull_sections/armor_hull_section.json | 18 + .../hull_sections/exotic_hull_section.json | 18 + .../hull_sections/heavy_hull_section.json | 18 + .../hull_sections/light_hull_section.json | 18 + .../hull_sections/standard_hull_section.json | 18 + .../recipes/hulls/ablative_armor_hull.json | 15 + .../data/gso/recipes/hulls/advanced_hull.json | 18 + .../data/gso/recipes/hulls/armor_hull.json | 18 + .../recipes/hulls/chrono_stabilized_hull.json | 15 + .../hulls/dimensional_anchor_hull.json | 15 + .../gso/recipes/hulls/dreadframe_hull.json | 19 + .../data/gso/recipes/hulls/heavy_hull.json | 18 + .../gso/recipes/hulls/industrial_hull.json | 15 + .../gso/recipes/hulls/lightframe_hull.json | 18 + .../gso/recipes/hulls/phase_locked_hull.json | 19 + .../recipes/hulls/plasma_resistant_hull.json | 15 + .../hulls/reinforced_plating_hull.json | 15 + .../data/gso/recipes/hulls/standard_hull.json | 18 + .../hulls/standard_structural_hull.json | 15 + .../recipes/hulls/superconductive_hull.json | 15 + .../hulls/titanium_aerospace_hull.json | 15 + .../gso/recipes/hulls/voidsteel_hull.json | 18 + GameServer/data/gso/recipes/manifest.json | 21 + .../data/gso/recipes/organics/bandage.json | 18 + .../organics/bio_composite_material.json | 19 + .../data/gso/recipes/organics/bio_pulp.json | 17 + .../gso/recipes/organics/medical_pack.json | 18 + .../recipes/organics/organic_insulation.json | 18 + .../gso/recipes/organics/plant_cloth.json | 17 + .../gso/recipes/organics/plant_fiber.json | 17 + .../gso/recipes/organics/processed_wood.json | 17 + .../data/gso/recipes/organics/raw_wood.json | 17 + .../data/gso/recipes/organics/resin.json | 17 + .../data/gso/recipes/organics/tree_sap.json | 17 + .../gso/recipes/organics/wood_planks.json | 17 + .../recipes/organics/wooden_components.json | 18 + .../gso/recipes/smelting/aluminum_ingot.json | 17 + .../gso/recipes/smelting/carbon_ingot.json | 18 + .../gso/recipes/smelting/chromium_ingot.json | 17 + .../gso/recipes/smelting/chronium_ingot.json | 17 + .../gso/recipes/smelting/copper_ingot.json | 17 + .../gso/recipes/smelting/darksteel_ingot.json | 18 + .../data/gso/recipes/smelting/gold_ingot.json | 17 + .../data/gso/recipes/smelting/iron_ingot.json | 17 + .../gso/recipes/smelting/magnesium_ingot.json | 17 + .../recipes/smelting/neutronium_ingot.json | 17 + .../gso/recipes/smelting/platinum_ingot.json | 17 + .../gso/recipes/smelting/silver_ingot.json | 17 + .../gso/recipes/smelting/titanium_ingot.json | 17 + .../gso/recipes/smelting/tungsten_ingot.json | 17 + .../recipes/spacesuit_parts/body_exotic.json | 19 + .../recipes/spacesuit_parts/body_light.json | 18 + .../spacesuit_parts/body_military.json | 19 + .../spacesuit_parts/body_standard.json | 19 + .../recipes/spacesuit_parts/boots_light.json | 18 + .../spacesuit_parts/boots_magnetic.json | 19 + .../spacesuit_parts/boots_military.json | 19 + .../spacesuit_parts/boots_standard.json | 19 + .../spacesuit_parts/gloves_industrial.json | 19 + .../recipes/spacesuit_parts/gloves_light.json | 18 + .../spacesuit_parts/gloves_military.json | 19 + .../spacesuit_parts/gloves_standard.json | 19 + .../spacesuit_parts/helmet_exotic.json | 19 + .../recipes/spacesuit_parts/helmet_light.json | 19 + .../spacesuit_parts/helmet_military.json | 20 + .../spacesuit_parts/helmet_standard.json | 20 + .../spacesuit_parts/life_support_basic.json | 19 + .../spacesuit_parts/life_support_exotic.json | 19 + .../life_support_military.json | 19 + .../life_support_standard.json | 20 + .../recipes/spacesuit_parts/pants_exotic.json | 19 + .../recipes/spacesuit_parts/pants_heavy.json | 19 + .../recipes/spacesuit_parts/pants_light.json | 18 + .../spacesuit_parts/pants_standard.json | 19 + .../gso/recipes/weapons/assault_rifle.json | 19 + .../gso/recipes/weapons/plasma_cutter.json | 19 + .../gso/recipes/weapons/sniper_rifle.json | 19 + .../gso/recipes/weapons/standard_pistol.json | 18 + .../gso/recipes/weapons/starter_blaster.json | 18 + .../gso/skills/combat/defense_training.json | 15 + .../data/gso/skills/combat/piloting.json | 15 + .../gso/skills/combat/speed_reflexes.json | 15 + .../gso/skills/combat/tactical_analysis.json | 15 + .../gso/skills/combat/weapons_mastery.json | 15 + .../gso/skills/crafting/armor_crafting.json | 15 + .../skills/crafting/resource_extraction.json | 15 + .../gso/skills/crafting/smelting_mastery.json | 15 + .../gso/skills/crafting/weapons_crafting.json | 15 + GameServer/data/gso/skills/manifest.json | 10 + .../gso/skills/science/alien_technology.json | 15 + .../gso/skills/science/bio_engineering.json | 15 + .../skills/science/energy_manipulation.json | 14 + .../data/gso/skills/science/engineering.json | 14 + .../gso/skills/science/quantum_physics.json | 15 + GameServer/models/PlayerData.js | 22 + GameServer/server.js | 1012 ++- GameServer/systems/ContentLoader.js | 305 + GameServer/systems/CraftingSystem.js | 289 +- GameServer/systems/DungeonSystem.js | 1704 +---- GameServer/systems/FleetSystem.js | 53 + GameServer/systems/GalaxySystem.js | 112 + GameServer/systems/ItemSystem.js | 1299 +--- GameServer/systems/QuestSystem.js | 858 +-- GameServer/systems/ResearchSystem.js | 110 + GameServer/systems/SkillSystem.js | 414 +- 1135 files changed, 58178 insertions(+), 17805 deletions(-) delete mode 100644 Client-Server/electron-main copy.js delete mode 100644 Client-Server/index copy.html delete mode 100644 Client-Server/index.html delete mode 100644 Client-Server/js/LocalServer.js delete mode 100644 Client-Server/js/LocalServerManager.js delete mode 100644 Client-Server/js/SimpleLocalServer.js delete mode 100644 Client-Server/js/core/Economy.js delete mode 100644 Client-Server/js/core/GameEngine.js delete mode 100644 Client-Server/js/data/GameData.js delete mode 100644 Client-Server/js/systems/CraftingSystem.js delete mode 100644 Client-Server/js/systems/DungeonSystem.js delete mode 100644 Client-Server/js/systems/SkillSystem.js create mode 100644 Client/data/starbase-layout.json create mode 100644 Client/js/systems/StarbaseWorld.js create mode 100644 Galaxy-Strike-Online-main/.github/workflows/build-client.yml create mode 100644 Galaxy-Strike-Online-main/.gitignore create mode 100644 Galaxy-Strike-Online-main/API/config/database.js create mode 100644 Galaxy-Strike-Online-main/API/config/production.js create mode 100644 Galaxy-Strike-Online-main/API/middleware/errorHandler.js create mode 100644 Galaxy-Strike-Online-main/API/models/GameServer.js create mode 100644 Galaxy-Strike-Online-main/API/models/Inventory.js create mode 100644 Galaxy-Strike-Online-main/API/models/Player.js create mode 100644 Galaxy-Strike-Online-main/API/models/Ship.js create mode 100644 Galaxy-Strike-Online-main/API/package-lock.json create mode 100644 Galaxy-Strike-Online-main/API/package.json create mode 100644 Galaxy-Strike-Online-main/API/routes/auth.js create mode 100644 Galaxy-Strike-Online-main/API/routes/servers.js create mode 100644 Galaxy-Strike-Online-main/API/scripts/createTestServer.js create mode 100644 Galaxy-Strike-Online-main/API/scripts/migrate.js create mode 100644 Galaxy-Strike-Online-main/API/scripts/migratePasswords.js create mode 100644 Galaxy-Strike-Online-main/API/scripts/seed.js create mode 100644 Galaxy-Strike-Online-main/API/server.js create mode 100644 Galaxy-Strike-Online-main/API/socket/socketHandlers.js create mode 100644 Galaxy-Strike-Online-main/API/systems/EconomySystem.js create mode 100644 Galaxy-Strike-Online-main/API/systems/GameSystem.js create mode 100644 Galaxy-Strike-Online-main/API/tests/api.test.js create mode 100644 Galaxy-Strike-Online-main/API/utils/logger.js create mode 100644 Galaxy-Strike-Online-main/Client/data/starbase-layout.json rename {Client-Server => Galaxy-Strike-Online-main/Client}/electron-main.js (100%) create mode 100644 Galaxy-Strike-Online-main/Client/index.html rename {Client-Server => Galaxy-Strike-Online-main/Client}/js/GameInitializer.js (52%) rename {Client-Server => Galaxy-Strike-Online-main/Client}/js/SaveSystemIntegration.js (89%) rename {Client-Server => Galaxy-Strike-Online-main/Client}/js/SmartSaveManager.js (63%) rename {Client-Server => Galaxy-Strike-Online-main/Client}/js/core/DebugLogger.js (83%) create mode 100644 Galaxy-Strike-Online-main/Client/js/core/Economy.js create mode 100644 Galaxy-Strike-Online-main/Client/js/core/GameEngine.js rename {Client-Server => Galaxy-Strike-Online-main/Client}/js/core/Inventory.js (94%) rename {Client-Server => Galaxy-Strike-Online-main/Client}/js/core/Logger.js (100%) rename {Client-Server => Galaxy-Strike-Online-main/Client}/js/core/Player.js (92%) rename {Client-Server => Galaxy-Strike-Online-main/Client}/js/core/TextureManager.js (100%) create mode 100644 Galaxy-Strike-Online-main/Client/js/data/GameData.js rename {Client-Server => Galaxy-Strike-Online-main/Client}/js/main.js (100%) rename {Client-Server => Galaxy-Strike-Online-main/Client}/js/systems/BaseSystem.js (99%) create mode 100644 Galaxy-Strike-Online-main/Client/js/systems/CraftingSystem.js create mode 100644 Galaxy-Strike-Online-main/Client/js/systems/DungeonSystem.js rename {Client-Server => Galaxy-Strike-Online-main/Client}/js/systems/IdleSystem.js (91%) create mode 100644 Galaxy-Strike-Online-main/Client/js/systems/ItemSystem.js rename {Client-Server => Galaxy-Strike-Online-main/Client}/js/systems/QuestSystem.js (73%) rename {Client-Server => Galaxy-Strike-Online-main/Client}/js/systems/ShipSystem.js (79%) create mode 100644 Galaxy-Strike-Online-main/Client/js/systems/SkillSystem.js create mode 100644 Galaxy-Strike-Online-main/Client/js/systems/StarbaseWorld.js rename {Client-Server => Galaxy-Strike-Online-main/Client}/js/ui/LiveMainMenu.js (99%) rename {Client-Server => Galaxy-Strike-Online-main/Client}/js/ui/MainMenu.js (100%) rename {Client-Server => Galaxy-Strike-Online-main/Client}/js/ui/UIManager.js (85%) create mode 100644 Galaxy-Strike-Online-main/Client/locales/de.json create mode 100644 Galaxy-Strike-Online-main/Client/locales/en.json create mode 100644 Galaxy-Strike-Online-main/Client/locales/es.json create mode 100644 Galaxy-Strike-Online-main/Client/locales/fr.json create mode 100644 Galaxy-Strike-Online-main/Client/locales/ja.json create mode 100644 Galaxy-Strike-Online-main/Client/locales/ko.json create mode 100644 Galaxy-Strike-Online-main/Client/locales/pt.json create mode 100644 Galaxy-Strike-Online-main/Client/locales/zh.json rename {Client-Server => Galaxy-Strike-Online-main/Client}/package-lock.json (100%) rename {Client-Server => Galaxy-Strike-Online-main/Client}/package.json (100%) rename {Client-Server => Galaxy-Strike-Online-main/Client}/preload.js (100%) create mode 100644 Galaxy-Strike-Online-main/Client/styles/components.css rename Client-Server/styles/components.css => Galaxy-Strike-Online-main/Client/styles/components.css.bak (92%) create mode 100644 Galaxy-Strike-Online-main/Client/styles/main.css rename Client-Server/styles/main.css => Galaxy-Strike-Online-main/Client/styles/main.css.bak (98%) create mode 100644 Galaxy-Strike-Online-main/Client/styles/tables.css rename Client-Server/styles/tables.css => Galaxy-Strike-Online-main/Client/styles/tables.css.bak (100%) rename {Client-Server/assets/textures => Galaxy-Strike-Online-main/GameServer/assets/images}/armors/basic_armor.png (100%) rename {Client-Server/assets/textures => Galaxy-Strike-Online-main/GameServer/assets/images}/armors/heavy_armor.png (100%) rename {Client-Server/assets/textures => Galaxy-Strike-Online-main/GameServer/assets/images}/armors/medium_armor.png (100%) rename {Client-Server/assets/textures => Galaxy-Strike-Online-main/GameServer/assets/images}/base/command_center.png (100%) rename {Client-Server/assets/textures => Galaxy-Strike-Online-main/GameServer/assets/images}/base/mining_facility.png (100%) rename {Client-Server/assets/textures/items => Galaxy-Strike-Online-main/GameServer/assets/images/items/consumables}/bandages.png (100%) rename {Client-Server/assets/textures/items => Galaxy-Strike-Online-main/GameServer/assets/images/items/consumables}/health_pack.png (100%) rename {Client-Server/assets/textures/items => Galaxy-Strike-Online-main/GameServer/assets/images/items/consumables}/mega_health_pack.png (100%) create mode 100644 Galaxy-Strike-Online-main/GameServer/assets/images/items/cosmetics/temp rename {Client-Server/assets/textures/items => Galaxy-Strike-Online-main/GameServer/assets/images/items/materials}/advanced_circuitboard.png (100%) rename {Client-Server/assets/textures/items => Galaxy-Strike-Online-main/GameServer/assets/images/items/materials}/advanced_component.png (100%) rename {Client-Server/assets/textures/items => Galaxy-Strike-Online-main/GameServer/assets/images/items/materials}/advanced_components.png (100%) rename {Client-Server/assets/textures/items => Galaxy-Strike-Online-main/GameServer/assets/images/items/materials}/basic_circuitboard.png (100%) rename {Client-Server/assets/textures/items => Galaxy-Strike-Online-main/GameServer/assets/images/items/materials}/battery.png (100%) rename {Client-Server/assets/textures/items => Galaxy-Strike-Online-main/GameServer/assets/images/items/materials}/common_circuitboard.png (100%) rename {Client-Server/assets/textures/items => Galaxy-Strike-Online-main/GameServer/assets/images/items/materials}/copper_ore.png (100%) rename {Client-Server/assets/textures/items => Galaxy-Strike-Online-main/GameServer/assets/images/items/materials}/copper_wire.png (100%) rename {Client-Server/assets/textures/items => Galaxy-Strike-Online-main/GameServer/assets/images/items/materials}/energy_crystal.png (100%) rename {Client-Server/assets/textures/items => Galaxy-Strike-Online-main/GameServer/assets/images/items/materials}/herbs.png (100%) rename {Client-Server/assets/textures/items => Galaxy-Strike-Online-main/GameServer/assets/images/items/materials}/iron_ore.png (100%) rename {Client-Server/assets/textures/items => Galaxy-Strike-Online-main/GameServer/assets/images/items/materials}/leather.png (100%) rename {Client-Server/assets/textures/items => Galaxy-Strike-Online-main/GameServer/assets/images/items/materials}/stell_plate.png (100%) rename {Client-Server/assets/textures/items => Galaxy-Strike-Online-main/GameServer/assets/images/items/materials}/tin_bar.png (100%) rename {Client-Server/assets/textures => Galaxy-Strike-Online-main/GameServer/assets/images}/ships/heavy_cruiser.png (100%) rename {Client-Server/assets/textures => Galaxy-Strike-Online-main/GameServer/assets/images}/ships/heavy_destroyer.png (100%) rename {Client-Server/assets/textures => Galaxy-Strike-Online-main/GameServer/assets/images}/ships/light_destroyer.png (100%) rename {Client-Server/assets/textures => Galaxy-Strike-Online-main/GameServer/assets/images}/ships/starter_cruiser.png (100%) create mode 100644 Galaxy-Strike-Online-main/GameServer/assets/images/ui/icons/temp rename Client-Server/assets/textures/missing-texture.png => Galaxy-Strike-Online-main/GameServer/assets/images/ui/placeholder.png (100%) rename {Client-Server/assets/textures => Galaxy-Strike-Online-main/GameServer/assets/images}/weapons/laser_pistol.png (100%) rename {Client-Server/assets/textures => Galaxy-Strike-Online-main/GameServer/assets/images}/weapons/laser_sniper_rifle.png (100%) rename {Client-Server/assets/textures => Galaxy-Strike-Online-main/GameServer/assets/images}/weapons/starter_blaster.png (100%) create mode 100644 Galaxy-Strike-Online-main/GameServer/config/database.js create mode 100644 Galaxy-Strike-Online-main/GameServer/create_placeholders.js create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/category/quests/daily.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/category/quests/main_story.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/category/quests/monthly.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/category/quests/weekly.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/alien_ruins.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/asteroid_mine.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/corrupted_vault.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/dungeons.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/enemies.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/nebula_anomaly.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/pirate_lair.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/void_rift.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/enemies/alien_guardian.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/enemies/ancient_drone.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/enemies/corrupted_ai.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/enemies/crystal_golem.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/enemies/energy_being.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/enemies/mining_drone.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/enemies/phase_shifter.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/enemies/pirate_captain.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/enemies/quantum_entity.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/enemies/rock_creature.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/enemies/security_drone.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/enemies/space_pirate.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/enemies/training_drone.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/enemies/virus_program.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/.gitkeep create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/backpack/backpack_basic_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/backpack/backpack_field_rare.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/backpack/backpack_reactor_epic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/body/body_basic_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/body/body_exosuit_epic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/body/body_plated_rare.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/boots/boots_assault_rare.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/boots/boots_basic_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/boots/boots_gravity_epic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/hands/hands_basic_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/hands/hands_combat_rare.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/hands/hands_elite_epic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/helmet/helmet_basic_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/helmet/helmet_reinforced_rare.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/helmet/helmet_tactical_epic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/pants/pants_basic_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/pants/pants_exo_epic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/pants/pants_reinforced_rare.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/backpack_basic_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/body_basic_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/body_exosuit_epic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/body_plated_rare.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/boots_basic_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/gloves_basic_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/helmet_basic_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/helmet_reinforced_rare.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/pants_basic_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/credit_multiplier.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/energy_boost_large.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/energy_boost_small.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/fuel_cell.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/health_kit_large.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/health_kit_small.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/scrap_pack_small.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/starter_crate.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/xp_booster.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/room_armory.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/room_operations.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/room_research_lab.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/room_throne.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/room_vault.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_alien_ruins.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_arctic_station.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_deep_ocean.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_golden_empire.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_lava_forge.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_nebula_dawn.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_void_black.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_void_rift.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/ballistic_alloy_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/carbon_titanium_composite_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/chrono_alloy_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/dimensional_alloy_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/flux_core_alloy_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/neutronium_composite_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/phase_alloy_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/plasma_channel_alloy_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/reinforced_steel_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/superconductive_alloy_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/voidsteel_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/plating/ablative_plating_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/section/advanced_hull_section_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/section/armor_hull_section_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/section/exotic_hull_section_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/section/heavy_hull_section_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/section/light_hull_section_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/section/standard_hull_section_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/manifest.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/bio/bio_pulp.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/bio/organic_insulation.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/bio/plant_matter.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/aluminum_alloy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/bio_composite_material.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/carbon_nanotube_alloy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/carbon_titanium_composite.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/ceramic_composite.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/chrono_alloy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/dimensional_alloy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/explosive_compound.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/flex_alloy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/flux_core_alloy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/fusion_rated_alloy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/glass_composite.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/neutronium_composite.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/phase_alloy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/radiation_shield_alloy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/reactive_armor_alloy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/reinforced_steel.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/resin.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/rubber_polymer.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/servo_alloy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/steel_alloy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/titanium_alloy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/transparent_alloy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/tungsten_alloy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/voidsteel.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/ai_core_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/basic_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/control_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/dimensional_logic_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/high_density_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/logic_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/navigation_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/power_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/processor_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/quantum_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/reactor_control_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/sensor_array_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/sentient_core_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/shield_control_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/stealth_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/warp_navigation_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/weapon_control_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/fabric/plant_cloth.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/fabric/plant_fiber.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/bread.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/fish.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/fish_meat.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/flour.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/fruit_plant.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/grain.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/grain_crop.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/meat_stew.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/ration_pack.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/vegetable_plant.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/vegetable_stew.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/aluminum_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/carbon_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/chromium_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/chronium_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/copper_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/darksteel_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/gold_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/iron_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/magnesium_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/neutronium_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/platinum_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/silver_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/titanium_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/tungsten_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/liquid/water.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/aerospace_aluminum.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/alien_fauna_sample.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/alien_protein.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/animal_carcass.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/bandage.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/body_exotic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/body_light.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/body_military.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/body_standard.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/boots_light.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/boots_magnetic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/boots_military.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/boots_standard.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/carbon_shale.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/chromium_steel.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/cooked_fish.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/cooked_meat.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/crystal_glass.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/dough.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/flux_crystal.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/gloves_industrial.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/gloves_light.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/gloves_military.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/gloves_standard.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/helmet_exotic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/helmet_light.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/helmet_military.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/helmet_standard.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/hydroponic_growth_medium.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/ice_chunk.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/iron_ore_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/life_support_basic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/life_support_exotic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/life_support_military.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/life_support_standard.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/luxury_meal.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/magnetic_coil.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/medical_pack.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/military_ration.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/neutronium_shard.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/nutrient_paste.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/pants_exotic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/pants_heavy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/pants_light.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/pants_standard.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/phase_crystal.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/processed_wood.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/protein_bar.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/raw_fruits.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/raw_meat.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/raw_vegetables.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/raw_wood.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/silicon_wafer.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/tree.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/tree_sap.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/tungsten_steel.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/void_crystal.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/void_residue.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/wooden_components.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/bauxite_ore.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/chromium_ore.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/chronite_ore.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/copper_ore.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/dark_iron_ore.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/gold_ore.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/iron_ore.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/magnesium_ore.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/platinum_ore.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/silver_ore.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/titanium_ore.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/tungsten_ore.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/stone/fluxstone.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/wood/wood_planks.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/dreadnought_legendary.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/heavy_cruiser_rare.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/interceptor_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/interceptor_uncommon.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/phantom_epic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/starter_cruiser_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/starter_cruiser_rare.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/starter_cruiser_uncommon.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/assaultrifle/assault_rifle_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/assaultrifle/assault_rifle_epic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/assaultrifle/assault_rifle_rare.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/dualpistol/dual_pistol_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/dualpistol/dual_pistol_epic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/dualpistol/dual_pistol_rare.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/laserbow/laser_bow_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/laserbow/laser_bow_epic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/laserbow/laser_bow_rare.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/lasercrossbow/laser_crossbow_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/lasercrossbow/laser_crossbow_epic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/lasercrossbow/laser_crossbow_rare.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/pistol/pistol_marksman_rare.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/pistol/pistol_overcharge_epic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/pistol/pistol_standard_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/plasmacutter/plasma_cutter_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/plasmacutter/plasma_cutter_epic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/plasmacutter/plasma_cutter_rare.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/rocketlauncher/rocket_launcher_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/rocketlauncher/rocket_launcher_epic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/rocketlauncher/rocket_launcher_rare.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/sniper/sniper_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/sniper/sniper_epic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/sniper/sniper_rare.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/assault_rifle_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/assault_rifle_rare.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/laser_bow_epic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/pistol_marksman_rare.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/pistol_standard_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/plasma_cutter_rare.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/sniper_common.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/starter_blaster.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/missions/faction_missions.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_craft_3.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_customize_ship.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_decor_10.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_dungeons_3.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_explore_30.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_kill_pirates_10.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_login_5.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_smelt_5.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_travel_planet_1000.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch1_001.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch1_002.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch1_003.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch2_001.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch2_002.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch2_003.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch3_001.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch3_002.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/manifest.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_craft_100.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_decor_100.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_dungeons_50.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_guild_fight_10.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_hire_5.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_lightyears_100.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_raids_20.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_void_run.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_craft_20.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_dungeons_10.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_explore_planets_3.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_friends_5.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_gather_50.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_guild_fight_3.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_lightyears_25.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_raids_3.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_survive_hardcore_7.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/.gitkeep create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/ablative_plating.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/aerospace_aluminum.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/ballistic_alloy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/carbon_titanium_composite.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/chromium_steel.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/chrono_alloy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/dimensional_alloy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/flux_core_alloy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/fusion_rated_alloy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/neutronium_composite.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/phase_alloy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/reactive_armor_alloy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/reinforced_steel.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/steel_alloy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/superconductive_alloy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/titanium_alloy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/tungsten_steel.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/voidsteel.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/armours/body_basic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/armours/body_plated.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/armours/boots_basic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/armours/gloves_basic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/armours/helmet_basic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/armours/pants_basic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/ai_core_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/basic_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/control_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/dimensional_logic_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/high_density_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/logic_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/navigation_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/power_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/processor_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/quantum_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/reactor_control_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/sensor_array_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/sentient_core_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/shield_control_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/stealth_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/warp_navigation_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/weapon_control_circuit.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/consumables/energy_boost_small.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/consumables/fuel_cell.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/consumables/health_kit_large.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/consumables/health_kit_small.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/crafting_tabs.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/alien_protein.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/bread.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/cooked_fish.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/cooked_meat.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/dough.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/fish_meat.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/flour.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/grain.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/hydroponic_growth_medium.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/luxury_meal.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/meat_stew.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/military_ration.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/nutrient_paste.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/protein_bar.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/ration_pack.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/raw_fruits.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/raw_meat.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/raw_vegetables.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/vegetable_stew.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/water.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hull_sections/advanced_hull_section.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hull_sections/armor_hull_section.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hull_sections/exotic_hull_section.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hull_sections/heavy_hull_section.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hull_sections/light_hull_section.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hull_sections/standard_hull_section.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/ablative_armor_hull.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/advanced_hull.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/armor_hull.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/chrono_stabilized_hull.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/dimensional_anchor_hull.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/dreadframe_hull.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/heavy_hull.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/industrial_hull.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/lightframe_hull.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/phase_locked_hull.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/plasma_resistant_hull.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/reinforced_plating_hull.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/standard_hull.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/standard_structural_hull.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/superconductive_hull.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/titanium_aerospace_hull.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/voidsteel_hull.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/manifest.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/bandage.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/bio_composite_material.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/bio_pulp.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/medical_pack.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/organic_insulation.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/plant_cloth.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/plant_fiber.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/processed_wood.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/raw_wood.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/resin.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/tree_sap.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/wood_planks.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/wooden_components.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/aluminum_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/carbon_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/chromium_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/chronium_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/copper_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/darksteel_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/gold_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/iron_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/magnesium_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/neutronium_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/platinum_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/silver_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/titanium_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/tungsten_ingot.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/body_exotic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/body_light.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/body_military.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/body_standard.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/boots_light.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/boots_magnetic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/boots_military.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/boots_standard.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/gloves_industrial.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/gloves_light.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/gloves_military.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/gloves_standard.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/helmet_exotic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/helmet_light.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/helmet_military.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/helmet_standard.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/life_support_basic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/life_support_exotic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/life_support_military.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/life_support_standard.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/pants_exotic.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/pants_heavy.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/pants_light.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/pants_standard.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/weapons/assault_rifle.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/weapons/plasma_cutter.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/weapons/sniper_rifle.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/weapons/standard_pistol.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/recipes/weapons/starter_blaster.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/skills/combat.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/skills/combat/defense_training.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/skills/combat/piloting.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/skills/combat/speed_reflexes.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/skills/combat/tactical_analysis.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/skills/combat/weapons_mastery.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/skills/crafting.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/skills/crafting/armor_crafting.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/skills/crafting/general_crafting.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/skills/crafting/resource_extraction.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/skills/crafting/smelting_mastery.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/skills/crafting/weapons_crafting.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/skills/manifest.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/skills/science.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/skills/science/alien_technology.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/skills/science/bio_engineering.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/skills/science/energy_manipulation.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/skills/science/engineering.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/gso/skills/science/quantum_physics.json create mode 100644 Galaxy-Strike-Online-main/GameServer/data/mods.db create mode 100644 Galaxy-Strike-Online-main/GameServer/generate_assets.js create mode 100644 Galaxy-Strike-Online-main/GameServer/middleware/errorHandler.js create mode 100644 Galaxy-Strike-Online-main/GameServer/models/PlayerData.js create mode 100644 Galaxy-Strike-Online-main/GameServer/package-lock.json create mode 100644 Galaxy-Strike-Online-main/GameServer/package.json create mode 100644 Galaxy-Strike-Online-main/GameServer/routes/base.js create mode 100644 Galaxy-Strike-Online-main/GameServer/routes/crafting.js create mode 100644 Galaxy-Strike-Online-main/GameServer/routes/dungeons.js create mode 100644 Galaxy-Strike-Online-main/GameServer/routes/idle.js create mode 100644 Galaxy-Strike-Online-main/GameServer/routes/quests.js create mode 100644 Galaxy-Strike-Online-main/GameServer/routes/ships.js create mode 100644 Galaxy-Strike-Online-main/GameServer/routes/skills.js create mode 100644 Galaxy-Strike-Online-main/GameServer/server.js create mode 100644 Galaxy-Strike-Online-main/GameServer/services/ServerRegistrationService.js create mode 100644 Galaxy-Strike-Online-main/GameServer/socket/socketHandlers.js create mode 100644 Galaxy-Strike-Online-main/GameServer/systems/AllianceSystem.js create mode 100644 Galaxy-Strike-Online-main/GameServer/systems/BaseSystem.js create mode 100644 Galaxy-Strike-Online-main/GameServer/systems/ContentLoader.js create mode 100644 Galaxy-Strike-Online-main/GameServer/systems/CraftingSystem.js create mode 100644 Galaxy-Strike-Online-main/GameServer/systems/DungeonSystem.js create mode 100644 Galaxy-Strike-Online-main/GameServer/systems/FleetSystem.js create mode 100644 Galaxy-Strike-Online-main/GameServer/systems/GalaxyEventSystem.js create mode 100644 Galaxy-Strike-Online-main/GameServer/systems/GalaxySystem.js create mode 100644 Galaxy-Strike-Online-main/GameServer/systems/GameSystem.js create mode 100644 Galaxy-Strike-Online-main/GameServer/systems/IdleSystem.js create mode 100644 Galaxy-Strike-Online-main/GameServer/systems/ItemSystem.js create mode 100644 Galaxy-Strike-Online-main/GameServer/systems/MarketSystem.js create mode 100644 Galaxy-Strike-Online-main/GameServer/systems/MissionSystem.js create mode 100644 Galaxy-Strike-Online-main/GameServer/systems/QuestSystem.js create mode 100644 Galaxy-Strike-Online-main/GameServer/systems/ReputationSystem.js create mode 100644 Galaxy-Strike-Online-main/GameServer/systems/ResearchSystem.js create mode 100644 Galaxy-Strike-Online-main/GameServer/systems/ResourceSystem.js create mode 100644 Galaxy-Strike-Online-main/GameServer/systems/SeasonSystem.js create mode 100644 Galaxy-Strike-Online-main/GameServer/systems/ShipSystem.js create mode 100644 Galaxy-Strike-Online-main/GameServer/systems/SkillSystem.js create mode 100644 Galaxy-Strike-Online-main/GameServer/systems/SocialSystem.js create mode 100644 Galaxy-Strike-Online-main/GameServer/utils/logger.js create mode 100644 Galaxy-Strike-Online-main/LICENSE create mode 100644 Galaxy-Strike-Online-main/Wiki/SETUP_GUIDE.html create mode 100644 Galaxy-Strike-Online-main/config/xp-progression.js create mode 100644 Galaxy-Strike-Online-main/docs/Server-Modding-Guide.md create mode 100644 GameServer/data/gso/category/quests/daily.json create mode 100644 GameServer/data/gso/category/quests/main_story.json create mode 100644 GameServer/data/gso/category/quests/monthly.json create mode 100644 GameServer/data/gso/category/quests/weekly.json create mode 100644 GameServer/data/gso/dungeons/alien_ruins.json create mode 100644 GameServer/data/gso/dungeons/asteroid_mine.json create mode 100644 GameServer/data/gso/dungeons/corrupted_vault.json create mode 100644 GameServer/data/gso/dungeons/nebula_anomaly.json create mode 100644 GameServer/data/gso/dungeons/pirate_lair.json create mode 100644 GameServer/data/gso/dungeons/void_rift.json create mode 100644 GameServer/data/gso/enemies/alien_guardian.json create mode 100644 GameServer/data/gso/enemies/ancient_drone.json create mode 100644 GameServer/data/gso/enemies/corrupted_ai.json create mode 100644 GameServer/data/gso/enemies/crystal_golem.json create mode 100644 GameServer/data/gso/enemies/energy_being.json create mode 100644 GameServer/data/gso/enemies/mining_drone.json create mode 100644 GameServer/data/gso/enemies/phase_shifter.json create mode 100644 GameServer/data/gso/enemies/pirate_captain.json create mode 100644 GameServer/data/gso/enemies/quantum_entity.json create mode 100644 GameServer/data/gso/enemies/rock_creature.json create mode 100644 GameServer/data/gso/enemies/security_drone.json create mode 100644 GameServer/data/gso/enemies/space_pirate.json create mode 100644 GameServer/data/gso/enemies/training_drone.json create mode 100644 GameServer/data/gso/enemies/virus_program.json create mode 100644 GameServer/data/gso/items/.gitkeep create mode 100644 GameServer/data/gso/items/armour/backpack/backpack_basic_common.json create mode 100644 GameServer/data/gso/items/armour/backpack/backpack_field_rare.json create mode 100644 GameServer/data/gso/items/armour/backpack/backpack_reactor_epic.json create mode 100644 GameServer/data/gso/items/armour/body/body_basic_common.json create mode 100644 GameServer/data/gso/items/armour/body/body_exosuit_epic.json create mode 100644 GameServer/data/gso/items/armour/body/body_plated_rare.json create mode 100644 GameServer/data/gso/items/armour/boots/boots_assault_rare.json create mode 100644 GameServer/data/gso/items/armour/boots/boots_basic_common.json create mode 100644 GameServer/data/gso/items/armour/boots/boots_gravity_epic.json create mode 100644 GameServer/data/gso/items/armour/hands/hands_basic_common.json create mode 100644 GameServer/data/gso/items/armour/hands/hands_combat_rare.json create mode 100644 GameServer/data/gso/items/armour/hands/hands_elite_epic.json create mode 100644 GameServer/data/gso/items/armour/helmet/helmet_basic_common.json create mode 100644 GameServer/data/gso/items/armour/helmet/helmet_reinforced_rare.json create mode 100644 GameServer/data/gso/items/armour/helmet/helmet_tactical_epic.json create mode 100644 GameServer/data/gso/items/armour/pants/pants_basic_common.json create mode 100644 GameServer/data/gso/items/armour/pants/pants_exo_epic.json create mode 100644 GameServer/data/gso/items/armour/pants/pants_reinforced_rare.json create mode 100644 GameServer/data/gso/items/armours/backpack_basic_common.json create mode 100644 GameServer/data/gso/items/armours/body_basic_common.json create mode 100644 GameServer/data/gso/items/armours/body_exosuit_epic.json create mode 100644 GameServer/data/gso/items/armours/body_plated_rare.json create mode 100644 GameServer/data/gso/items/armours/boots_basic_common.json create mode 100644 GameServer/data/gso/items/armours/gloves_basic_common.json create mode 100644 GameServer/data/gso/items/armours/helmet_basic_common.json create mode 100644 GameServer/data/gso/items/armours/helmet_reinforced_rare.json create mode 100644 GameServer/data/gso/items/armours/pants_basic_common.json create mode 100644 GameServer/data/gso/items/consumables/credit_multiplier.json create mode 100644 GameServer/data/gso/items/consumables/energy_boost_large.json create mode 100644 GameServer/data/gso/items/consumables/energy_boost_small.json create mode 100644 GameServer/data/gso/items/consumables/fuel_cell.json create mode 100644 GameServer/data/gso/items/consumables/health_kit_large.json create mode 100644 GameServer/data/gso/items/consumables/health_kit_small.json create mode 100644 GameServer/data/gso/items/consumables/scrap_pack_small.json create mode 100644 GameServer/data/gso/items/consumables/starter_crate.json create mode 100644 GameServer/data/gso/items/consumables/xp_booster.json create mode 100644 GameServer/data/gso/items/decorations/room_armory.json create mode 100644 GameServer/data/gso/items/decorations/room_operations.json create mode 100644 GameServer/data/gso/items/decorations/room_research_lab.json create mode 100644 GameServer/data/gso/items/decorations/room_throne.json create mode 100644 GameServer/data/gso/items/decorations/room_vault.json create mode 100644 GameServer/data/gso/items/decorations/wp_alien_ruins.json create mode 100644 GameServer/data/gso/items/decorations/wp_arctic_station.json create mode 100644 GameServer/data/gso/items/decorations/wp_deep_ocean.json create mode 100644 GameServer/data/gso/items/decorations/wp_golden_empire.json create mode 100644 GameServer/data/gso/items/decorations/wp_lava_forge.json create mode 100644 GameServer/data/gso/items/decorations/wp_nebula_dawn.json create mode 100644 GameServer/data/gso/items/decorations/wp_void_black.json create mode 100644 GameServer/data/gso/items/decorations/wp_void_rift.json create mode 100644 GameServer/data/gso/items/hullPlating/misc/ballistic_alloy_common.json create mode 100644 GameServer/data/gso/items/hullPlating/misc/carbon_titanium_composite_common.json create mode 100644 GameServer/data/gso/items/hullPlating/misc/chrono_alloy_common.json create mode 100644 GameServer/data/gso/items/hullPlating/misc/dimensional_alloy_common.json create mode 100644 GameServer/data/gso/items/hullPlating/misc/flux_core_alloy_common.json create mode 100644 GameServer/data/gso/items/hullPlating/misc/neutronium_composite_common.json create mode 100644 GameServer/data/gso/items/hullPlating/misc/phase_alloy_common.json create mode 100644 GameServer/data/gso/items/hullPlating/misc/plasma_channel_alloy_common.json create mode 100644 GameServer/data/gso/items/hullPlating/misc/reinforced_steel_common.json create mode 100644 GameServer/data/gso/items/hullPlating/misc/superconductive_alloy_common.json create mode 100644 GameServer/data/gso/items/hullPlating/misc/voidsteel_common.json create mode 100644 GameServer/data/gso/items/hullPlating/plating/ablative_plating_common.json create mode 100644 GameServer/data/gso/items/hullPlating/section/advanced_hull_section_common.json create mode 100644 GameServer/data/gso/items/hullPlating/section/armor_hull_section_common.json create mode 100644 GameServer/data/gso/items/hullPlating/section/exotic_hull_section_common.json create mode 100644 GameServer/data/gso/items/hullPlating/section/heavy_hull_section_common.json create mode 100644 GameServer/data/gso/items/hullPlating/section/light_hull_section_common.json create mode 100644 GameServer/data/gso/items/hullPlating/section/standard_hull_section_common.json create mode 100644 GameServer/data/gso/items/manifest.json create mode 100644 GameServer/data/gso/items/materials/bio/bio_pulp.json create mode 100644 GameServer/data/gso/items/materials/bio/organic_insulation.json create mode 100644 GameServer/data/gso/items/materials/bio/plant_matter.json create mode 100644 GameServer/data/gso/items/materials/chemical/aluminum_alloy.json create mode 100644 GameServer/data/gso/items/materials/chemical/bio_composite_material.json create mode 100644 GameServer/data/gso/items/materials/chemical/carbon_nanotube_alloy.json create mode 100644 GameServer/data/gso/items/materials/chemical/carbon_titanium_composite.json create mode 100644 GameServer/data/gso/items/materials/chemical/ceramic_composite.json create mode 100644 GameServer/data/gso/items/materials/chemical/chrono_alloy.json create mode 100644 GameServer/data/gso/items/materials/chemical/dimensional_alloy.json create mode 100644 GameServer/data/gso/items/materials/chemical/explosive_compound.json create mode 100644 GameServer/data/gso/items/materials/chemical/flex_alloy.json create mode 100644 GameServer/data/gso/items/materials/chemical/flux_core_alloy.json create mode 100644 GameServer/data/gso/items/materials/chemical/fusion_rated_alloy.json create mode 100644 GameServer/data/gso/items/materials/chemical/glass_composite.json create mode 100644 GameServer/data/gso/items/materials/chemical/neutronium_composite.json create mode 100644 GameServer/data/gso/items/materials/chemical/phase_alloy.json create mode 100644 GameServer/data/gso/items/materials/chemical/radiation_shield_alloy.json create mode 100644 GameServer/data/gso/items/materials/chemical/reactive_armor_alloy.json create mode 100644 GameServer/data/gso/items/materials/chemical/reinforced_steel.json create mode 100644 GameServer/data/gso/items/materials/chemical/resin.json create mode 100644 GameServer/data/gso/items/materials/chemical/rubber_polymer.json create mode 100644 GameServer/data/gso/items/materials/chemical/servo_alloy.json create mode 100644 GameServer/data/gso/items/materials/chemical/steel_alloy.json create mode 100644 GameServer/data/gso/items/materials/chemical/titanium_alloy.json create mode 100644 GameServer/data/gso/items/materials/chemical/transparent_alloy.json create mode 100644 GameServer/data/gso/items/materials/chemical/tungsten_alloy.json create mode 100644 GameServer/data/gso/items/materials/chemical/voidsteel.json create mode 100644 GameServer/data/gso/items/materials/electronics/ai_core_circuit.json create mode 100644 GameServer/data/gso/items/materials/electronics/basic_circuit.json create mode 100644 GameServer/data/gso/items/materials/electronics/control_circuit.json create mode 100644 GameServer/data/gso/items/materials/electronics/dimensional_logic_circuit.json create mode 100644 GameServer/data/gso/items/materials/electronics/high_density_circuit.json create mode 100644 GameServer/data/gso/items/materials/electronics/logic_circuit.json create mode 100644 GameServer/data/gso/items/materials/electronics/navigation_circuit.json create mode 100644 GameServer/data/gso/items/materials/electronics/power_circuit.json create mode 100644 GameServer/data/gso/items/materials/electronics/processor_circuit.json create mode 100644 GameServer/data/gso/items/materials/electronics/quantum_circuit.json create mode 100644 GameServer/data/gso/items/materials/electronics/reactor_control_circuit.json create mode 100644 GameServer/data/gso/items/materials/electronics/sensor_array_circuit.json create mode 100644 GameServer/data/gso/items/materials/electronics/sentient_core_circuit.json create mode 100644 GameServer/data/gso/items/materials/electronics/shield_control_circuit.json create mode 100644 GameServer/data/gso/items/materials/electronics/stealth_circuit.json create mode 100644 GameServer/data/gso/items/materials/electronics/warp_navigation_circuit.json create mode 100644 GameServer/data/gso/items/materials/electronics/weapon_control_circuit.json create mode 100644 GameServer/data/gso/items/materials/fabric/plant_cloth.json create mode 100644 GameServer/data/gso/items/materials/fabric/plant_fiber.json create mode 100644 GameServer/data/gso/items/materials/food/bread.json create mode 100644 GameServer/data/gso/items/materials/food/fish.json create mode 100644 GameServer/data/gso/items/materials/food/fish_meat.json create mode 100644 GameServer/data/gso/items/materials/food/flour.json create mode 100644 GameServer/data/gso/items/materials/food/fruit_plant.json create mode 100644 GameServer/data/gso/items/materials/food/grain.json create mode 100644 GameServer/data/gso/items/materials/food/grain_crop.json create mode 100644 GameServer/data/gso/items/materials/food/meat_stew.json create mode 100644 GameServer/data/gso/items/materials/food/ration_pack.json create mode 100644 GameServer/data/gso/items/materials/food/vegetable_plant.json create mode 100644 GameServer/data/gso/items/materials/food/vegetable_stew.json create mode 100644 GameServer/data/gso/items/materials/ingot/aluminum_ingot.json create mode 100644 GameServer/data/gso/items/materials/ingot/carbon_ingot.json create mode 100644 GameServer/data/gso/items/materials/ingot/chromium_ingot.json create mode 100644 GameServer/data/gso/items/materials/ingot/chronium_ingot.json create mode 100644 GameServer/data/gso/items/materials/ingot/copper_ingot.json create mode 100644 GameServer/data/gso/items/materials/ingot/darksteel_ingot.json create mode 100644 GameServer/data/gso/items/materials/ingot/gold_ingot.json create mode 100644 GameServer/data/gso/items/materials/ingot/iron_ingot.json create mode 100644 GameServer/data/gso/items/materials/ingot/magnesium_ingot.json create mode 100644 GameServer/data/gso/items/materials/ingot/neutronium_ingot.json create mode 100644 GameServer/data/gso/items/materials/ingot/platinum_ingot.json create mode 100644 GameServer/data/gso/items/materials/ingot/silver_ingot.json create mode 100644 GameServer/data/gso/items/materials/ingot/titanium_ingot.json create mode 100644 GameServer/data/gso/items/materials/ingot/tungsten_ingot.json create mode 100644 GameServer/data/gso/items/materials/liquid/water.json create mode 100644 GameServer/data/gso/items/materials/misc/aerospace_aluminum.json create mode 100644 GameServer/data/gso/items/materials/misc/alien_fauna_sample.json create mode 100644 GameServer/data/gso/items/materials/misc/alien_protein.json create mode 100644 GameServer/data/gso/items/materials/misc/animal_carcass.json create mode 100644 GameServer/data/gso/items/materials/misc/bandage.json create mode 100644 GameServer/data/gso/items/materials/misc/body_exotic.json create mode 100644 GameServer/data/gso/items/materials/misc/body_light.json create mode 100644 GameServer/data/gso/items/materials/misc/body_military.json create mode 100644 GameServer/data/gso/items/materials/misc/body_standard.json create mode 100644 GameServer/data/gso/items/materials/misc/boots_light.json create mode 100644 GameServer/data/gso/items/materials/misc/boots_magnetic.json create mode 100644 GameServer/data/gso/items/materials/misc/boots_military.json create mode 100644 GameServer/data/gso/items/materials/misc/boots_standard.json create mode 100644 GameServer/data/gso/items/materials/misc/carbon_shale.json create mode 100644 GameServer/data/gso/items/materials/misc/chromium_steel.json create mode 100644 GameServer/data/gso/items/materials/misc/cooked_fish.json create mode 100644 GameServer/data/gso/items/materials/misc/cooked_meat.json create mode 100644 GameServer/data/gso/items/materials/misc/crystal_glass.json create mode 100644 GameServer/data/gso/items/materials/misc/dough.json create mode 100644 GameServer/data/gso/items/materials/misc/flux_crystal.json create mode 100644 GameServer/data/gso/items/materials/misc/gloves_industrial.json create mode 100644 GameServer/data/gso/items/materials/misc/gloves_light.json create mode 100644 GameServer/data/gso/items/materials/misc/gloves_military.json create mode 100644 GameServer/data/gso/items/materials/misc/gloves_standard.json create mode 100644 GameServer/data/gso/items/materials/misc/helmet_exotic.json create mode 100644 GameServer/data/gso/items/materials/misc/helmet_light.json create mode 100644 GameServer/data/gso/items/materials/misc/helmet_military.json create mode 100644 GameServer/data/gso/items/materials/misc/helmet_standard.json create mode 100644 GameServer/data/gso/items/materials/misc/hydroponic_growth_medium.json create mode 100644 GameServer/data/gso/items/materials/misc/ice_chunk.json create mode 100644 GameServer/data/gso/items/materials/misc/iron_ore_common.json create mode 100644 GameServer/data/gso/items/materials/misc/life_support_basic.json create mode 100644 GameServer/data/gso/items/materials/misc/life_support_exotic.json create mode 100644 GameServer/data/gso/items/materials/misc/life_support_military.json create mode 100644 GameServer/data/gso/items/materials/misc/life_support_standard.json create mode 100644 GameServer/data/gso/items/materials/misc/luxury_meal.json create mode 100644 GameServer/data/gso/items/materials/misc/magnetic_coil.json create mode 100644 GameServer/data/gso/items/materials/misc/medical_pack.json create mode 100644 GameServer/data/gso/items/materials/misc/military_ration.json create mode 100644 GameServer/data/gso/items/materials/misc/neutronium_shard.json create mode 100644 GameServer/data/gso/items/materials/misc/nutrient_paste.json create mode 100644 GameServer/data/gso/items/materials/misc/pants_exotic.json create mode 100644 GameServer/data/gso/items/materials/misc/pants_heavy.json create mode 100644 GameServer/data/gso/items/materials/misc/pants_light.json create mode 100644 GameServer/data/gso/items/materials/misc/pants_standard.json create mode 100644 GameServer/data/gso/items/materials/misc/phase_crystal.json create mode 100644 GameServer/data/gso/items/materials/misc/processed_wood.json create mode 100644 GameServer/data/gso/items/materials/misc/protein_bar.json create mode 100644 GameServer/data/gso/items/materials/misc/raw_fruits.json create mode 100644 GameServer/data/gso/items/materials/misc/raw_meat.json create mode 100644 GameServer/data/gso/items/materials/misc/raw_vegetables.json create mode 100644 GameServer/data/gso/items/materials/misc/raw_wood.json create mode 100644 GameServer/data/gso/items/materials/misc/silicon_wafer.json create mode 100644 GameServer/data/gso/items/materials/misc/tree.json create mode 100644 GameServer/data/gso/items/materials/misc/tree_sap.json create mode 100644 GameServer/data/gso/items/materials/misc/tungsten_steel.json create mode 100644 GameServer/data/gso/items/materials/misc/void_crystal.json create mode 100644 GameServer/data/gso/items/materials/misc/void_residue.json create mode 100644 GameServer/data/gso/items/materials/misc/wooden_components.json create mode 100644 GameServer/data/gso/items/materials/ore/bauxite_ore.json create mode 100644 GameServer/data/gso/items/materials/ore/chromium_ore.json create mode 100644 GameServer/data/gso/items/materials/ore/chronite_ore.json create mode 100644 GameServer/data/gso/items/materials/ore/copper_ore.json create mode 100644 GameServer/data/gso/items/materials/ore/dark_iron_ore.json create mode 100644 GameServer/data/gso/items/materials/ore/gold_ore.json create mode 100644 GameServer/data/gso/items/materials/ore/iron_ore.json create mode 100644 GameServer/data/gso/items/materials/ore/magnesium_ore.json create mode 100644 GameServer/data/gso/items/materials/ore/platinum_ore.json create mode 100644 GameServer/data/gso/items/materials/ore/silver_ore.json create mode 100644 GameServer/data/gso/items/materials/ore/titanium_ore.json create mode 100644 GameServer/data/gso/items/materials/ore/tungsten_ore.json create mode 100644 GameServer/data/gso/items/materials/stone/fluxstone.json create mode 100644 GameServer/data/gso/items/materials/wood/wood_planks.json create mode 100644 GameServer/data/gso/items/ships/dreadnought_legendary.json create mode 100644 GameServer/data/gso/items/ships/heavy_cruiser_rare.json create mode 100644 GameServer/data/gso/items/ships/interceptor_common.json create mode 100644 GameServer/data/gso/items/ships/interceptor_uncommon.json create mode 100644 GameServer/data/gso/items/ships/phantom_epic.json create mode 100644 GameServer/data/gso/items/ships/starter_cruiser_common.json create mode 100644 GameServer/data/gso/items/ships/starter_cruiser_rare.json create mode 100644 GameServer/data/gso/items/ships/starter_cruiser_uncommon.json create mode 100644 GameServer/data/gso/items/weapon/assaultrifle/assault_rifle_common.json create mode 100644 GameServer/data/gso/items/weapon/assaultrifle/assault_rifle_epic.json create mode 100644 GameServer/data/gso/items/weapon/assaultrifle/assault_rifle_rare.json create mode 100644 GameServer/data/gso/items/weapon/dualpistol/dual_pistol_common.json create mode 100644 GameServer/data/gso/items/weapon/dualpistol/dual_pistol_epic.json create mode 100644 GameServer/data/gso/items/weapon/dualpistol/dual_pistol_rare.json create mode 100644 GameServer/data/gso/items/weapon/laserbow/laser_bow_common.json create mode 100644 GameServer/data/gso/items/weapon/laserbow/laser_bow_epic.json create mode 100644 GameServer/data/gso/items/weapon/laserbow/laser_bow_rare.json create mode 100644 GameServer/data/gso/items/weapon/lasercrossbow/laser_crossbow_common.json create mode 100644 GameServer/data/gso/items/weapon/lasercrossbow/laser_crossbow_epic.json create mode 100644 GameServer/data/gso/items/weapon/lasercrossbow/laser_crossbow_rare.json create mode 100644 GameServer/data/gso/items/weapon/pistol/pistol_marksman_rare.json create mode 100644 GameServer/data/gso/items/weapon/pistol/pistol_overcharge_epic.json create mode 100644 GameServer/data/gso/items/weapon/pistol/pistol_standard_common.json create mode 100644 GameServer/data/gso/items/weapon/plasmacutter/plasma_cutter_common.json create mode 100644 GameServer/data/gso/items/weapon/plasmacutter/plasma_cutter_epic.json create mode 100644 GameServer/data/gso/items/weapon/plasmacutter/plasma_cutter_rare.json create mode 100644 GameServer/data/gso/items/weapon/rocketlauncher/rocket_launcher_common.json create mode 100644 GameServer/data/gso/items/weapon/rocketlauncher/rocket_launcher_epic.json create mode 100644 GameServer/data/gso/items/weapon/rocketlauncher/rocket_launcher_rare.json create mode 100644 GameServer/data/gso/items/weapon/sniper/sniper_common.json create mode 100644 GameServer/data/gso/items/weapon/sniper/sniper_epic.json create mode 100644 GameServer/data/gso/items/weapon/sniper/sniper_rare.json create mode 100644 GameServer/data/gso/items/weapons/assault_rifle_common.json create mode 100644 GameServer/data/gso/items/weapons/assault_rifle_rare.json create mode 100644 GameServer/data/gso/items/weapons/laser_bow_epic.json create mode 100644 GameServer/data/gso/items/weapons/pistol_marksman_rare.json create mode 100644 GameServer/data/gso/items/weapons/pistol_standard_common.json create mode 100644 GameServer/data/gso/items/weapons/plasma_cutter_rare.json create mode 100644 GameServer/data/gso/items/weapons/sniper_common.json create mode 100644 GameServer/data/gso/items/weapons/starter_blaster.json create mode 100644 GameServer/data/gso/quests/daily/d_craft_3.json create mode 100644 GameServer/data/gso/quests/daily/d_customize_ship.json create mode 100644 GameServer/data/gso/quests/daily/d_decor_10.json create mode 100644 GameServer/data/gso/quests/daily/d_dungeons_3.json create mode 100644 GameServer/data/gso/quests/daily/d_explore_30.json create mode 100644 GameServer/data/gso/quests/daily/d_kill_pirates_10.json create mode 100644 GameServer/data/gso/quests/daily/d_login_5.json create mode 100644 GameServer/data/gso/quests/daily/d_smelt_5.json create mode 100644 GameServer/data/gso/quests/daily/d_travel_planet_1000.json create mode 100644 GameServer/data/gso/quests/main_story/ms_ch1_001.json create mode 100644 GameServer/data/gso/quests/main_story/ms_ch1_002.json create mode 100644 GameServer/data/gso/quests/main_story/ms_ch1_003.json create mode 100644 GameServer/data/gso/quests/main_story/ms_ch2_001.json create mode 100644 GameServer/data/gso/quests/main_story/ms_ch2_002.json create mode 100644 GameServer/data/gso/quests/main_story/ms_ch2_003.json create mode 100644 GameServer/data/gso/quests/main_story/ms_ch3_001.json create mode 100644 GameServer/data/gso/quests/main_story/ms_ch3_002.json create mode 100644 GameServer/data/gso/quests/manifest.json create mode 100644 GameServer/data/gso/quests/monthly/m_craft_100.json create mode 100644 GameServer/data/gso/quests/monthly/m_decor_100.json create mode 100644 GameServer/data/gso/quests/monthly/m_dungeons_50.json create mode 100644 GameServer/data/gso/quests/monthly/m_guild_fight_10.json create mode 100644 GameServer/data/gso/quests/monthly/m_hire_5.json create mode 100644 GameServer/data/gso/quests/monthly/m_lightyears_100.json create mode 100644 GameServer/data/gso/quests/monthly/m_raids_20.json create mode 100644 GameServer/data/gso/quests/monthly/m_void_run.json create mode 100644 GameServer/data/gso/quests/weekly/w_craft_20.json create mode 100644 GameServer/data/gso/quests/weekly/w_dungeons_10.json create mode 100644 GameServer/data/gso/quests/weekly/w_explore_planets_3.json create mode 100644 GameServer/data/gso/quests/weekly/w_friends_5.json create mode 100644 GameServer/data/gso/quests/weekly/w_gather_50.json create mode 100644 GameServer/data/gso/quests/weekly/w_guild_fight_3.json create mode 100644 GameServer/data/gso/quests/weekly/w_lightyears_25.json create mode 100644 GameServer/data/gso/quests/weekly/w_raids_3.json create mode 100644 GameServer/data/gso/quests/weekly/w_survive_hardcore_7.json create mode 100644 GameServer/data/gso/recipes/.gitkeep create mode 100644 GameServer/data/gso/recipes/alloys/ablative_plating.json create mode 100644 GameServer/data/gso/recipes/alloys/aerospace_aluminum.json create mode 100644 GameServer/data/gso/recipes/alloys/ballistic_alloy.json create mode 100644 GameServer/data/gso/recipes/alloys/carbon_titanium_composite.json create mode 100644 GameServer/data/gso/recipes/alloys/chromium_steel.json create mode 100644 GameServer/data/gso/recipes/alloys/chrono_alloy.json create mode 100644 GameServer/data/gso/recipes/alloys/dimensional_alloy.json create mode 100644 GameServer/data/gso/recipes/alloys/flux_core_alloy.json create mode 100644 GameServer/data/gso/recipes/alloys/fusion_rated_alloy.json create mode 100644 GameServer/data/gso/recipes/alloys/neutronium_composite.json create mode 100644 GameServer/data/gso/recipes/alloys/phase_alloy.json create mode 100644 GameServer/data/gso/recipes/alloys/reactive_armor_alloy.json create mode 100644 GameServer/data/gso/recipes/alloys/reinforced_steel.json create mode 100644 GameServer/data/gso/recipes/alloys/steel_alloy.json create mode 100644 GameServer/data/gso/recipes/alloys/superconductive_alloy.json create mode 100644 GameServer/data/gso/recipes/alloys/titanium_alloy.json create mode 100644 GameServer/data/gso/recipes/alloys/tungsten_steel.json create mode 100644 GameServer/data/gso/recipes/alloys/voidsteel.json create mode 100644 GameServer/data/gso/recipes/armours/body_basic.json create mode 100644 GameServer/data/gso/recipes/armours/body_plated.json create mode 100644 GameServer/data/gso/recipes/armours/boots_basic.json create mode 100644 GameServer/data/gso/recipes/armours/gloves_basic.json create mode 100644 GameServer/data/gso/recipes/armours/helmet_basic.json create mode 100644 GameServer/data/gso/recipes/armours/pants_basic.json create mode 100644 GameServer/data/gso/recipes/circuits/ai_core_circuit.json create mode 100644 GameServer/data/gso/recipes/circuits/basic_circuit.json create mode 100644 GameServer/data/gso/recipes/circuits/control_circuit.json create mode 100644 GameServer/data/gso/recipes/circuits/dimensional_logic_circuit.json create mode 100644 GameServer/data/gso/recipes/circuits/high_density_circuit.json create mode 100644 GameServer/data/gso/recipes/circuits/logic_circuit.json create mode 100644 GameServer/data/gso/recipes/circuits/navigation_circuit.json create mode 100644 GameServer/data/gso/recipes/circuits/power_circuit.json create mode 100644 GameServer/data/gso/recipes/circuits/processor_circuit.json create mode 100644 GameServer/data/gso/recipes/circuits/quantum_circuit.json create mode 100644 GameServer/data/gso/recipes/circuits/reactor_control_circuit.json create mode 100644 GameServer/data/gso/recipes/circuits/sensor_array_circuit.json create mode 100644 GameServer/data/gso/recipes/circuits/sentient_core_circuit.json create mode 100644 GameServer/data/gso/recipes/circuits/shield_control_circuit.json create mode 100644 GameServer/data/gso/recipes/circuits/stealth_circuit.json create mode 100644 GameServer/data/gso/recipes/circuits/warp_navigation_circuit.json create mode 100644 GameServer/data/gso/recipes/circuits/weapon_control_circuit.json create mode 100644 GameServer/data/gso/recipes/consumables/energy_boost_small.json create mode 100644 GameServer/data/gso/recipes/consumables/fuel_cell.json create mode 100644 GameServer/data/gso/recipes/consumables/health_kit_large.json create mode 100644 GameServer/data/gso/recipes/consumables/health_kit_small.json create mode 100644 GameServer/data/gso/recipes/crafting_tabs.json create mode 100644 GameServer/data/gso/recipes/food_organics/alien_protein.json create mode 100644 GameServer/data/gso/recipes/food_organics/bread.json create mode 100644 GameServer/data/gso/recipes/food_organics/cooked_fish.json create mode 100644 GameServer/data/gso/recipes/food_organics/cooked_meat.json create mode 100644 GameServer/data/gso/recipes/food_organics/dough.json create mode 100644 GameServer/data/gso/recipes/food_organics/fish_meat.json create mode 100644 GameServer/data/gso/recipes/food_organics/flour.json create mode 100644 GameServer/data/gso/recipes/food_organics/grain.json create mode 100644 GameServer/data/gso/recipes/food_organics/hydroponic_growth_medium.json create mode 100644 GameServer/data/gso/recipes/food_organics/luxury_meal.json create mode 100644 GameServer/data/gso/recipes/food_organics/meat_stew.json create mode 100644 GameServer/data/gso/recipes/food_organics/military_ration.json create mode 100644 GameServer/data/gso/recipes/food_organics/nutrient_paste.json create mode 100644 GameServer/data/gso/recipes/food_organics/protein_bar.json create mode 100644 GameServer/data/gso/recipes/food_organics/ration_pack.json create mode 100644 GameServer/data/gso/recipes/food_organics/raw_fruits.json create mode 100644 GameServer/data/gso/recipes/food_organics/raw_meat.json create mode 100644 GameServer/data/gso/recipes/food_organics/raw_vegetables.json create mode 100644 GameServer/data/gso/recipes/food_organics/vegetable_stew.json create mode 100644 GameServer/data/gso/recipes/food_organics/water.json create mode 100644 GameServer/data/gso/recipes/hull_sections/advanced_hull_section.json create mode 100644 GameServer/data/gso/recipes/hull_sections/armor_hull_section.json create mode 100644 GameServer/data/gso/recipes/hull_sections/exotic_hull_section.json create mode 100644 GameServer/data/gso/recipes/hull_sections/heavy_hull_section.json create mode 100644 GameServer/data/gso/recipes/hull_sections/light_hull_section.json create mode 100644 GameServer/data/gso/recipes/hull_sections/standard_hull_section.json create mode 100644 GameServer/data/gso/recipes/hulls/ablative_armor_hull.json create mode 100644 GameServer/data/gso/recipes/hulls/advanced_hull.json create mode 100644 GameServer/data/gso/recipes/hulls/armor_hull.json create mode 100644 GameServer/data/gso/recipes/hulls/chrono_stabilized_hull.json create mode 100644 GameServer/data/gso/recipes/hulls/dimensional_anchor_hull.json create mode 100644 GameServer/data/gso/recipes/hulls/dreadframe_hull.json create mode 100644 GameServer/data/gso/recipes/hulls/heavy_hull.json create mode 100644 GameServer/data/gso/recipes/hulls/industrial_hull.json create mode 100644 GameServer/data/gso/recipes/hulls/lightframe_hull.json create mode 100644 GameServer/data/gso/recipes/hulls/phase_locked_hull.json create mode 100644 GameServer/data/gso/recipes/hulls/plasma_resistant_hull.json create mode 100644 GameServer/data/gso/recipes/hulls/reinforced_plating_hull.json create mode 100644 GameServer/data/gso/recipes/hulls/standard_hull.json create mode 100644 GameServer/data/gso/recipes/hulls/standard_structural_hull.json create mode 100644 GameServer/data/gso/recipes/hulls/superconductive_hull.json create mode 100644 GameServer/data/gso/recipes/hulls/titanium_aerospace_hull.json create mode 100644 GameServer/data/gso/recipes/hulls/voidsteel_hull.json create mode 100644 GameServer/data/gso/recipes/manifest.json create mode 100644 GameServer/data/gso/recipes/organics/bandage.json create mode 100644 GameServer/data/gso/recipes/organics/bio_composite_material.json create mode 100644 GameServer/data/gso/recipes/organics/bio_pulp.json create mode 100644 GameServer/data/gso/recipes/organics/medical_pack.json create mode 100644 GameServer/data/gso/recipes/organics/organic_insulation.json create mode 100644 GameServer/data/gso/recipes/organics/plant_cloth.json create mode 100644 GameServer/data/gso/recipes/organics/plant_fiber.json create mode 100644 GameServer/data/gso/recipes/organics/processed_wood.json create mode 100644 GameServer/data/gso/recipes/organics/raw_wood.json create mode 100644 GameServer/data/gso/recipes/organics/resin.json create mode 100644 GameServer/data/gso/recipes/organics/tree_sap.json create mode 100644 GameServer/data/gso/recipes/organics/wood_planks.json create mode 100644 GameServer/data/gso/recipes/organics/wooden_components.json create mode 100644 GameServer/data/gso/recipes/smelting/aluminum_ingot.json create mode 100644 GameServer/data/gso/recipes/smelting/carbon_ingot.json create mode 100644 GameServer/data/gso/recipes/smelting/chromium_ingot.json create mode 100644 GameServer/data/gso/recipes/smelting/chronium_ingot.json create mode 100644 GameServer/data/gso/recipes/smelting/copper_ingot.json create mode 100644 GameServer/data/gso/recipes/smelting/darksteel_ingot.json create mode 100644 GameServer/data/gso/recipes/smelting/gold_ingot.json create mode 100644 GameServer/data/gso/recipes/smelting/iron_ingot.json create mode 100644 GameServer/data/gso/recipes/smelting/magnesium_ingot.json create mode 100644 GameServer/data/gso/recipes/smelting/neutronium_ingot.json create mode 100644 GameServer/data/gso/recipes/smelting/platinum_ingot.json create mode 100644 GameServer/data/gso/recipes/smelting/silver_ingot.json create mode 100644 GameServer/data/gso/recipes/smelting/titanium_ingot.json create mode 100644 GameServer/data/gso/recipes/smelting/tungsten_ingot.json create mode 100644 GameServer/data/gso/recipes/spacesuit_parts/body_exotic.json create mode 100644 GameServer/data/gso/recipes/spacesuit_parts/body_light.json create mode 100644 GameServer/data/gso/recipes/spacesuit_parts/body_military.json create mode 100644 GameServer/data/gso/recipes/spacesuit_parts/body_standard.json create mode 100644 GameServer/data/gso/recipes/spacesuit_parts/boots_light.json create mode 100644 GameServer/data/gso/recipes/spacesuit_parts/boots_magnetic.json create mode 100644 GameServer/data/gso/recipes/spacesuit_parts/boots_military.json create mode 100644 GameServer/data/gso/recipes/spacesuit_parts/boots_standard.json create mode 100644 GameServer/data/gso/recipes/spacesuit_parts/gloves_industrial.json create mode 100644 GameServer/data/gso/recipes/spacesuit_parts/gloves_light.json create mode 100644 GameServer/data/gso/recipes/spacesuit_parts/gloves_military.json create mode 100644 GameServer/data/gso/recipes/spacesuit_parts/gloves_standard.json create mode 100644 GameServer/data/gso/recipes/spacesuit_parts/helmet_exotic.json create mode 100644 GameServer/data/gso/recipes/spacesuit_parts/helmet_light.json create mode 100644 GameServer/data/gso/recipes/spacesuit_parts/helmet_military.json create mode 100644 GameServer/data/gso/recipes/spacesuit_parts/helmet_standard.json create mode 100644 GameServer/data/gso/recipes/spacesuit_parts/life_support_basic.json create mode 100644 GameServer/data/gso/recipes/spacesuit_parts/life_support_exotic.json create mode 100644 GameServer/data/gso/recipes/spacesuit_parts/life_support_military.json create mode 100644 GameServer/data/gso/recipes/spacesuit_parts/life_support_standard.json create mode 100644 GameServer/data/gso/recipes/spacesuit_parts/pants_exotic.json create mode 100644 GameServer/data/gso/recipes/spacesuit_parts/pants_heavy.json create mode 100644 GameServer/data/gso/recipes/spacesuit_parts/pants_light.json create mode 100644 GameServer/data/gso/recipes/spacesuit_parts/pants_standard.json create mode 100644 GameServer/data/gso/recipes/weapons/assault_rifle.json create mode 100644 GameServer/data/gso/recipes/weapons/plasma_cutter.json create mode 100644 GameServer/data/gso/recipes/weapons/sniper_rifle.json create mode 100644 GameServer/data/gso/recipes/weapons/standard_pistol.json create mode 100644 GameServer/data/gso/recipes/weapons/starter_blaster.json create mode 100644 GameServer/data/gso/skills/combat/defense_training.json create mode 100644 GameServer/data/gso/skills/combat/piloting.json create mode 100644 GameServer/data/gso/skills/combat/speed_reflexes.json create mode 100644 GameServer/data/gso/skills/combat/tactical_analysis.json create mode 100644 GameServer/data/gso/skills/combat/weapons_mastery.json create mode 100644 GameServer/data/gso/skills/crafting/armor_crafting.json create mode 100644 GameServer/data/gso/skills/crafting/resource_extraction.json create mode 100644 GameServer/data/gso/skills/crafting/smelting_mastery.json create mode 100644 GameServer/data/gso/skills/crafting/weapons_crafting.json create mode 100644 GameServer/data/gso/skills/manifest.json create mode 100644 GameServer/data/gso/skills/science/alien_technology.json create mode 100644 GameServer/data/gso/skills/science/bio_engineering.json create mode 100644 GameServer/data/gso/skills/science/energy_manipulation.json create mode 100644 GameServer/data/gso/skills/science/engineering.json create mode 100644 GameServer/data/gso/skills/science/quantum_physics.json create mode 100644 GameServer/systems/ContentLoader.js create mode 100644 GameServer/systems/FleetSystem.js create mode 100644 GameServer/systems/GalaxySystem.js create mode 100644 GameServer/systems/ResearchSystem.js diff --git a/Client-Server/electron-main copy.js b/Client-Server/electron-main copy.js deleted file mode 100644 index 3296162..0000000 --- a/Client-Server/electron-main copy.js +++ /dev/null @@ -1,451 +0,0 @@ -const { app, BrowserWindow, Menu, shell, ipcMain } = require('electron'); -const path = require('path'); -const fs = require('fs'); -const logger = require('./js/core/Logger'); - -console.log('[MAIN PROCESS] Electron main process starting...'); -console.log('[MAIN PROCESS] Node.js version:', process.version); -console.log('[MAIN PROCESS] Electron version:', process.versions.electron); -console.log('[MAIN PROCESS] Platform:', process.platform); -console.log('[MAIN PROCESS] Current working directory:', process.cwd()); - -// Keep a global reference of the window object -let mainWindow; - -function createWindow() { - console.log('[MAIN PROCESS] createWindow() called'); - - try { - console.log('[MAIN PROCESS] Creating BrowserWindow...'); - // Create the browser window - mainWindow = new BrowserWindow({ - width: 1200, - height: 832, // 800 + 32px for custom title bar - minWidth: 1200, - minHeight: 832, - maxWidth: 1200, - maxHeight: 832, - resizable: false, - frame: false, - titleBarStyle: 'hidden', - webPreferences: { - nodeIntegration: true, - contextIsolation: false, - enableRemoteModule: true, - webSecurity: true - }, - icon: path.join(__dirname, 'assets/icon.png'), - show: false, // Don't show until ready-to-show - title: 'Galaxy Strike Online' - }); - - console.log('[MAIN PROCESS] BrowserWindow created successfully'); - console.log('[MAIN PROCESS] Loading index.html...'); - - // Load the index.html file - mainWindow.loadFile('index.html'); - - console.log('[MAIN PROCESS] index.html loaded, setting up electronAPI...'); - - // Set up electronAPI after DOM is ready - mainWindow.webContents.on('dom-ready', () => { - console.log('[MAIN PROCESS] DOM is ready, setting up electronAPI...'); - mainWindow.webContents.executeJavaScript(` - console.log('[RENDERER] Setting up electronAPI...'); - window.electronAPI = { - minimizeWindow: () => require('electron').ipcRenderer.send('minimize-window'), - closeWindow: () => require('electron').ipcRenderer.send('close-window'), - toggleFullscreen: () => require('electron').ipcRenderer.send('toggle-fullscreen'), - log: (level, message, data) => require('electron').ipcRenderer.send('log-message', { level, message, data }), - createSaveFolders: (saveSlots) => require('electron').ipcRenderer.invoke('create-save-folders', saveSlots), - testFileAccess: (slotPath) => require('electron').ipcRenderer.invoke('test-file-access', slotPath), - saveGame: (slot, saveData) => require('electron').ipcRenderer.invoke('save-game', slot, saveData), - loadGame: (slot) => require('electron').ipcRenderer.invoke('load-game', slot), - getPath: (name) => require('electron').ipcRenderer.invoke('get-path', name), - deleteSaveFile: (slot) => require('electron').ipcRenderer.invoke('delete-save-file', slot) - }; - console.log('[RENDERER] electronAPI setup completed'); - `).then(() => { - console.log('[MAIN PROCESS] electronAPI setup completed'); - }).catch((error) => { - console.error('[MAIN PROCESS] Failed to setup electronAPI:', error); - }); - }); - - // Show window when ready - mainWindow.once('ready-to-show', () => { - console.log('[MAIN PROCESS] Window ready-to-show event fired'); - mainWindow.show(); - }); - - // Open DevTools in development - if (process.argv.includes('--dev')) { - console.log('[MAIN PROCESS] Opening DevTools...'); - mainWindow.webContents.openDevTools(); - } - - // Handle window closed - mainWindow.on('closed', () => { - console.log('[MAIN PROCESS] Window closed event fired'); - mainWindow = null; - }); - - // Handle renderer process crashes - mainWindow.webContents.on('render-process-gone', (event, details) => { - console.error('[MAIN PROCESS] Renderer process crashed:', details); - console.error('[MAIN PROCESS] Crash reason:', details.reason); - console.error('[MAIN PROCESS] Exit code:', details.exitCode); - }); - - // Handle renderer process unresponsive - mainWindow.webContents.on('unresponsive', () => { - console.warn('[MAIN PROCESS] Renderer process unresponsive'); - }); - - // Handle renderer process responsive again - mainWindow.webContents.on('responsive', () => { - console.log('[MAIN PROCESS] Renderer process responsive again'); - }); - - // Handle console messages from renderer - mainWindow.webContents.on('console-message', (event, level, message, line, sourceId) => { - console.log(`[RENDERER CONSOLE] [${level}] ${message} (line: ${line}, source: ${sourceId})`); - }); - - // Handle page load errors - mainWindow.webContents.on('did-fail-load', (event, errorCode, errorDescription, validatedURL, isMainFrame) => { - console.error('[MAIN PROCESS] Page failed to load:', errorCode, errorDescription, validatedURL); - }); - - // Handle page load success - mainWindow.webContents.on('did-finish-load', () => { - console.log('[MAIN PROCESS] Page finished loading'); - }); - - // Handle DOM ready - mainWindow.webContents.on('dom-ready', () => { - console.log('[MAIN PROCESS] DOM is ready'); - }); - - // Handle external links - mainWindow.webContents.setWindowOpenHandler(({ url }) => { - console.log('[MAIN PROCESS] External link requested:', url); - shell.openExternal(url); - return { action: 'deny' }; - }); - - console.log('[MAIN PROCESS] createWindow() completed successfully'); - - } catch (error) { - console.error('[MAIN PROCESS] Error in createWindow():', error); - console.error('[MAIN PROCESS] Error stack:', error.stack); - } -} - -// IPC handlers for save operations -ipcMain.handle('create-save-folders', async (event, saveSlots) => { - console.log('[MAIN PROCESS] create-save-folders called with saveSlots:', saveSlots); - try { - const userDataPath = app.getPath('userData'); - console.log('[MAIN PROCESS] userDataPath:', userDataPath); - const savesDir = path.join(userDataPath, 'saves'); - console.log('[MAIN PROCESS] savesDir:', savesDir); - - // Create main saves directory - if (!fs.existsSync(savesDir)) { - console.log('[MAIN PROCESS] Creating saves directory:', savesDir); - fs.mkdirSync(savesDir, { recursive: true }); - console.log('[MAIN PROCESS] Saves directory created successfully'); - } else { - console.log('[MAIN PROCESS] Saves directory already exists'); - } - - const paths = { - base: savesDir, - slots: [] - }; - - // Create save slot directories - for (let i = 1; i <= saveSlots; i++) { - const slotDir = path.join(savesDir, `slot${i}`); - console.log(`[MAIN PROCESS] Checking/creating slot ${i} directory:`, slotDir); - if (!fs.existsSync(slotDir)) { - console.log(`[MAIN PROCESS] Creating slot ${i} directory`); - fs.mkdirSync(slotDir, { recursive: true }); - - // Create initial save info file - const saveInfo = { - slot: i, - created: new Date().toISOString(), - version: '1.0.0', - exists: false - }; - - const infoPath = path.join(slotDir, 'saveinfo.json'); - fs.writeFileSync(infoPath, JSON.stringify(saveInfo, null, 2)); - console.log(`[MAIN PROCESS] Created save info for slot ${i}`); - } else { - console.log(`[MAIN PROCESS] Slot ${i} directory already exists`); - } - paths.slots.push(slotDir); - } - - console.log('[MAIN PROCESS] Save folders created successfully, returning paths:', paths); - return { success: true, paths }; - } catch (error) { - console.error('[MAIN PROCESS] Failed to create save folders:', error); - return { success: false, error: error.message }; - } -}); - -ipcMain.handle('test-file-access', async (event, slotPath) => { - try { - const testFile = path.join(slotPath, 'access_test.txt'); - fs.writeFileSync(testFile, 'test'); - fs.unlinkSync(testFile); - return { success: true }; - } catch (error) { - return { success: false, error: error.message }; - } -}); - -ipcMain.handle('save-game', async (event, slot, saveData) => { - try { - const userDataPath = app.getPath('userData'); - const savesDir = path.join(userDataPath, 'saves'); - const slotDir = path.join(savesDir, `slot${slot}`); - - // Save game data - const saveFilePath = path.join(slotDir, 'save.json'); - fs.writeFileSync(saveFilePath, JSON.stringify(saveData, null, 2)); - - // Update save info - const infoPath = path.join(slotDir, 'saveinfo.json'); - const saveInfo = { - slot: slot, - created: new Date().toISOString(), - lastSaved: new Date().toISOString(), - version: '1.0.0', - exists: true, - playTime: saveData.gameTime || 0 - }; - fs.writeFileSync(infoPath, JSON.stringify(saveInfo, null, 2)); - - return { success: true }; - } catch (error) { - console.error('Failed to save game:', error); - return { success: false, error: error.message }; - } -}); - -ipcMain.handle('load-game', async (event, slot) => { - try { - const userDataPath = app.getPath('userData'); - const savesDir = path.join(userDataPath, 'saves'); - const slotDir = path.join(savesDir, `slot${slot}`); - const saveFilePath = path.join(slotDir, 'save.json'); - - if (fs.existsSync(saveFilePath)) { - const saveContent = fs.readFileSync(saveFilePath, 'utf8'); - const saveData = JSON.parse(saveContent); - return { success: true, data: saveData }; - } else { - return { success: false, error: 'Save file not found' }; - } - } catch (error) { - console.error('Failed to load game:', error); - return { success: false, error: error.message }; - } -}); - -ipcMain.handle('get-path', async (event, name) => { - try { - return app.getPath(name); - } catch (error) { - return null; - } -}); - -ipcMain.handle('delete-save-file', async (event, slot) => { - console.log('[MAIN PROCESS] delete-save-file called for slot:', slot); - try { - const userDataPath = app.getPath('userData'); - const savesDir = path.join(userDataPath, 'saves'); - const slotDir = path.join(savesDir, `slot${slot}`); - const saveFilePath = path.join(slotDir, 'save.json'); - const infoFilePath = path.join(slotDir, 'saveinfo.json'); - - console.log('[MAIN PROCESS] Attempting to delete save files from:', slotDir); - - let deletedFiles = []; - - // Delete save file if it exists - if (fs.existsSync(saveFilePath)) { - console.log('[MAIN PROCESS] Deleting save file:', saveFilePath); - fs.unlinkSync(saveFilePath); - deletedFiles.push('save.json'); - } - - // Delete save info file if it exists - if (fs.existsSync(infoFilePath)) { - console.log('[MAIN PROCESS] Deleting save info file:', infoFilePath); - fs.unlinkSync(infoFilePath); - deletedFiles.push('saveinfo.json'); - } - - // Create empty save info file to indicate slot is empty - const saveInfo = { - slot: slot, - created: new Date().toISOString(), - version: '1.0.0', - exists: false, - deleted: new Date().toISOString() - }; - fs.writeFileSync(infoFilePath, JSON.stringify(saveInfo, null, 2)); - - console.log('[MAIN PROCESS] Successfully deleted save files for slot', slot, ':', deletedFiles); - return { success: true, deletedFiles }; - } catch (error) { - console.error('[MAIN PROCESS] Failed to delete save file:', error); - return { success: false, error: error.message }; - } -}); - -// IPC handlers for window controls -// Handle logging from renderer process -ipcMain.on('log-message', async (event, { level, message, data }) => { - try { - switch (level) { - case 'error': - await logger.error(message, data); - break; - case 'warn': - await logger.warn(message, data); - break; - case 'info': - await logger.info(message, data); - break; - case 'debug': - await logger.debug(message, data); - break; - default: - await logger.info(message, data); - } - } catch (error) { - console.error('Failed to log message from renderer:', error); - // Fallback to console logging to prevent infinite loops - console.log(`[${level}] ${message}`, data || ''); - } -}); - -ipcMain.on('minimize-window', () => { - if (mainWindow) { - mainWindow.minimize(); - } -}); - -ipcMain.on('close-window', () => { - if (mainWindow) { - mainWindow.close(); - } -}); - -ipcMain.on('toggle-fullscreen', () => { - if (mainWindow) { - const isFullscreen = mainWindow.isFullScreen(); - if (isFullscreen) { - mainWindow.setFullScreen(false); - mainWindow.setSize(1200, 832); - mainWindow.center(); - } else { - mainWindow.setFullScreen(true); - } - } -}); - -// This method will be called when Electron has finished initialization -app.whenReady().then(async () => { - console.log('[MAIN PROCESS] Electron app ready, starting initialization...'); - - try { - // Initialize logger with app data path - console.log('[MAIN PROCESS] Initializing logger...'); - await logger.initialize(app.getPath('userData')); - console.log('[MAIN PROCESS] Logger initialized'); - - await logger.info('Galaxy Strike Online application starting'); - console.log('[MAIN PROCESS] Logger info message sent'); - - console.log('[MAIN PROCESS] Creating main window...'); - createWindow(); - - app.on('activate', () => { - console.log('[MAIN PROCESS] Activate event fired'); - // On macOS it's common to re-create a window in the app when the dock icon is clicked - if (BrowserWindow.getAllWindows().length === 0) { - console.log('[MAIN PROCESS] No windows exist, creating new window'); - createWindow(); - } - }); - - console.log('[MAIN PROCESS] App initialization completed successfully'); - - } catch (error) { - console.error('[MAIN PROCESS] Error during app initialization:', error); - console.error('[MAIN PROCESS] Error stack:', error.stack); - } -}).catch((error) => { - console.error('[MAIN PROCESS] Error in app.whenReady():', error); - console.error('[MAIN PROCESS] Error stack:', error.stack); -}); - -// Quit when all windows are closed -app.on('window-all-closed', () => { - // On macOS it's common for applications and their menu bar to stay active - if (process.platform !== 'darwin') { - logger.info('Application shutting down'); - app.quit(); - } -}); - -// Handle uncaught exceptions -process.on('uncaughtException', async (error) => { - console.error('[MAIN PROCESS] Uncaught Exception:', error); - console.error('[MAIN PROCESS] Uncaught Exception stack:', error.stack); - - try { - if (logger && typeof logger.errorEvent === 'function') { - await logger.errorEvent(error, 'Uncaught Exception in Main Process'); - } - } catch (logError) { - console.error('[MAIN PROCESS] Failed to log uncaught exception:', logError); - } - - console.error('[MAIN PROCESS] Application will continue running despite uncaught exception'); -}); - -// Handle unhandled promise rejections -process.on('unhandledRejection', (reason, promise) => { - console.error('[MAIN PROCESS] Unhandled Promise Rejection at:', promise, 'reason:', reason); - console.error('[MAIN PROCESS] Rejection reason stack:', reason.stack); -}); - -// Handle unhandled rejections -process.on('unhandledRejection', async (reason, promise) => { - // Avoid logging the logging system's own errors to prevent infinite loops - if (reason && reason.message && reason.message.includes('object could not be cloned')) { - console.warn('IPC cloning error detected - this is expected during logger initialization'); - return; - } - - await logger.error('Unhandled Rejection', { reason: reason.toString(), promise: promise.toString() }); - console.error('Unhandled Rejection at:', promise, 'reason:', reason); -}); - -// Security: Prevent new window creation -app.on('web-contents-created', (event, contents) => { - contents.on('new-window', (event, navigationUrl) => { - event.preventDefault(); - shell.openExternal(navigationUrl); - }); -}); diff --git a/Client-Server/index copy.html b/Client-Server/index copy.html deleted file mode 100644 index 51f732f..0000000 --- a/Client-Server/index copy.html +++ /dev/null @@ -1,699 +0,0 @@ - - - - - - Galaxy Strike Online - Space Idle MMORPG - - - - - - - - - - - -
-
- Galaxy Strike Online -
-
- - - -
-
- -
- - - - - - - - - - - -
- - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- Developer Console - -
-
-
-
- -
-
-
- - diff --git a/Client-Server/index.html b/Client-Server/index.html deleted file mode 100644 index 51f732f..0000000 --- a/Client-Server/index.html +++ /dev/null @@ -1,699 +0,0 @@ - - - - - - Galaxy Strike Online - Space Idle MMORPG - - - - - - - - - - - -
-
- Galaxy Strike Online -
-
- - - -
-
- -
- - - - - - - - - - - -
- - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- Developer Console - -
-
-
-
- -
-
-
- - diff --git a/Client-Server/js/LocalServer.js b/Client-Server/js/LocalServer.js deleted file mode 100644 index de6d955..0000000 --- a/Client-Server/js/LocalServer.js +++ /dev/null @@ -1,418 +0,0 @@ -/** - * Local Server for Singleplayer Mode - * A simplified server that runs within the Electron client for offline/singleplayer functionality - * NOTE: This version requires express, socket.io, and cors dependencies to be installed - */ - -class LocalServer { - constructor() { - this.app = null; - this.server = null; - this.io = null; - this.port = null; - this.isRunning = false; - this.connectedClients = new Map(); - - console.log('[LOCAL SERVER] LocalServer initialized'); - } - - async initialize() { - try { - // Try to require dependencies - if (typeof require !== 'undefined') { - const express = require('express'); - const { createServer } = require('http'); - const { Server } = require('socket.io'); - const cors = require('cors'); - - this.setupExpress(express, cors); - this.createServer = createServer; - this.ServerClass = Server; - - console.log('[LOCAL SERVER] Dependencies loaded successfully'); - return true; - } else { - console.warn('[LOCAL SERVER] require() not available, running in browser context'); - return false; - } - } catch (error) { - console.error('[LOCAL SERVER] Failed to load dependencies:', error.message); - console.log('[LOCAL SERVER] Please install dependencies: npm install express socket.io cors'); - return false; - } - } - - setupExpress(express, cors) { - // Initialize Express app - this.app = express(); - - // Middleware - this.app.use(cors({ - origin: ["http://localhost:3000", "http://127.0.0.1:3000", "file://"], - credentials: true - })); - this.app.use(express.json({ limit: '10mb' })); - this.app.use(express.urlencoded({ extended: true })); - - // Health check endpoint - this.app.get('/health', (req, res) => { - res.status(200).json({ - status: 'OK', - timestamp: new Date().toISOString(), - uptime: process.uptime(), - mode: 'local' - }); - }); - - // API version endpoint - this.app.get('/api/ssc/version', (req, res) => { - res.status(200).json({ - version: '1.0.0', - service: 'galaxystrikeonline-local-server', - timestamp: new Date().toISOString(), - mode: 'local' - }); - }); - - // Mock authentication endpoints for singleplayer - this.app.post('/api/auth/login', (req, res) => { - const { email, password } = req.body; - - // Auto-authenticate for singleplayer mode - const mockUser = { - id: 'local-user', - email: email || 'local@player.com', - username: 'Local Player', - token: 'local-token-' + Date.now(), - createdAt: new Date().toISOString() - }; - - res.status(200).json({ - success: true, - user: mockUser, - token: mockUser.token, - message: 'Logged in to local mode' - }); - }); - - this.app.post('/api/auth/register', (req, res) => { - const { email, password, username } = req.body; - - // Auto-register for singleplayer mode - const mockUser = { - id: 'local-user', - email: email || 'local@player.com', - username: username || 'Local Player', - token: 'local-token-' + Date.now(), - createdAt: new Date().toISOString() - }; - - res.status(201).json({ - success: true, - user: mockUser, - token: mockUser.token, - message: 'Registered in local mode' - }); - }); - - // Mock server browser endpoints - this.app.get('/api/servers', (req, res) => { - // Return a single local server - const localServer = { - id: 'local-server', - name: 'Local Singleplayer', - description: 'Your personal local server for singleplayer gaming', - type: 'private', - region: 'local', - maxPlayers: 1, - currentPlayers: 0, - owner: 'Local Player', - address: 'localhost', - port: this.port, - status: 'online', - createdAt: new Date().toISOString(), - ping: 0 - }; - - res.status(200).json({ - success: true, - servers: [localServer] - }); - }); - - // Mock game data endpoints - this.app.get('/api/game/player/:id', (req, res) => { - // Return player data from local storage if available - const playerId = req.params.id; - let saveData; - - try { - // In Electron context, access localStorage differently - if (typeof localStorage !== 'undefined') { - saveData = localStorage.getItem(`gso_save_slot_1`); - } - } catch (error) { - console.warn('[LOCAL SERVER] Could not access localStorage:', error); - } - - if (saveData) { - try { - const parsedData = JSON.parse(saveData); - res.status(200).json({ - success: true, - player: parsedData.player - }); - } catch (error) { - res.status(500).json({ - success: false, - error: 'Failed to parse save data' - }); - } - } else { - res.status(404).json({ - success: false, - error: 'No save data found' - }); - } - }); - - this.app.post('/api/game/player/:id/save', (req, res) => { - // Save player data to local storage - const playerId = req.params.id; - const playerData = req.body; - - try { - let existingSaveData = '{}'; - - // In Electron context, access localStorage differently - if (typeof localStorage !== 'undefined') { - existingSaveData = localStorage.getItem(`gso_save_slot_1`) || '{}'; - } - - const parsedExisting = JSON.parse(existingSaveData); - - // Merge player data - parsedExisting.player = playerData; - parsedExisting.lastSave = Date.now(); - - if (typeof localStorage !== 'undefined') { - localStorage.setItem(`gso_save_slot_1`, JSON.stringify(parsedExisting)); - } - - res.status(200).json({ - success: true, - message: 'Player data saved locally' - }); - } catch (error) { - res.status(500).json({ - success: false, - error: 'Failed to save player data' - }); - } - }); - } - - async start(port = 3004) { - if (this.isRunning) { - console.log('[LOCAL SERVER] Server is already running on port', this.port); - return { success: false, error: 'Server already running' }; - } - - try { - // Initialize dependencies if not already done - if (!this.app) { - const initialized = await this.initialize(); - if (!initialized) { - return { success: false, error: 'Failed to initialize server dependencies' }; - } - } - - this.port = port; - this.server = this.createServer(this.app); - this.io = new this.ServerClass(this.server, { - cors: { - origin: ["http://localhost:3000", "http://127.0.0.1:3000", "file://"], - methods: ["GET", "POST"] - } - }); - - // Setup Socket.IO handlers - this.setupSocketHandlers(); - - // Start the server - await new Promise((resolve, reject) => { - this.server.listen(port, (error) => { - if (error) { - reject(error); - } else { - resolve(); - } - }); - }); - - this.isRunning = true; - console.log(`[LOCAL SERVER] Local server started on port ${port}`); - - return { - success: true, - port: port, - url: `http://localhost:${port}` - }; - - } catch (error) { - console.error('[LOCAL SERVER] Failed to start server:', error); - return { success: false, error: error.message }; - } - } - - setupSocketHandlers() { - this.io.on('connection', (socket) => { - console.log('[LOCAL SERVER] Client connected:', socket.id); - this.connectedClients.set(socket.id, { - connectedAt: Date.now(), - playerData: null - }); - - // Handle authentication - socket.on('authenticate', (data) => { - console.log('[LOCAL SERVER] Authenticating client:', socket.id, data); - - // Auto-authenticate for local mode - socket.emit('authenticated', { - success: true, - user: { - id: 'local-user', - username: 'Local Player', - token: 'local-token-' + Date.now() - } - }); - - // Update client info - const clientInfo = this.connectedClients.get(socket.id); - if (clientInfo) { - clientInfo.playerData = { - id: 'local-user', - username: 'Local Player' - }; - } - }); - - // Handle game data sync - socket.on('saveGameData', (data) => { - console.log('[LOCAL SERVER] Saving game data for:', socket.id); - - // Save to localStorage (this will be handled by the client-side save system) - socket.emit('gameDataSaved', { - success: true, - timestamp: Date.now() - }); - }); - - socket.on('loadGameData', (data) => { - console.log('[LOCAL SERVER] Loading game data for:', socket.id); - - // Load from localStorage (this will be handled by the client-side load system) - let saveData; - - try { - if (typeof localStorage !== 'undefined') { - saveData = localStorage.getItem(`gso_save_slot_1`); - } - } catch (error) { - console.warn('[LOCAL SERVER] Could not access localStorage:', error); - } - - if (saveData) { - try { - const parsedData = JSON.parse(saveData); - socket.emit('gameDataLoaded', { - success: true, - data: parsedData - }); - } catch (error) { - socket.emit('gameDataLoaded', { - success: false, - error: 'Failed to parse save data' - }); - } - } else { - socket.emit('gameDataLoaded', { - success: false, - error: 'No save data found' - }); - } - }); - - // Handle disconnection - socket.on('disconnect', () => { - console.log('[LOCAL SERVER] Client disconnected:', socket.id); - this.connectedClients.delete(socket.id); - }); - - // Send welcome message - socket.emit('welcome', { - message: 'Connected to local server', - serverInfo: { - mode: 'local', - port: this.port, - timestamp: new Date().toISOString() - } - }); - }); - } - - async stop() { - if (!this.isRunning) { - console.log('[LOCAL SERVER] Server is not running'); - return { success: false, error: 'Server is not running' }; - } - - try { - // Disconnect all clients - if (this.io) { - this.io.disconnectSockets(); - } - - // Close the server - if (this.server) { - await new Promise((resolve) => { - this.server.close(resolve); - }); - } - - this.isRunning = false; - this.port = null; - this.connectedClients.clear(); - - console.log('[LOCAL SERVER] Local server stopped'); - return { success: true }; - - } catch (error) { - console.error('[LOCAL SERVER] Failed to stop server:', error); - return { success: false, error: error.message }; - } - } - - getStatus() { - return { - isRunning: this.isRunning, - port: this.port, - connectedClients: this.connectedClients.size, - uptime: this.isRunning ? process.uptime() : 0 - }; - } - - getUrl() { - return this.isRunning ? `http://localhost:${this.port}` : null; - } -} - -// Export for use in Node.js environment -if (typeof module !== 'undefined' && module.exports) { - module.exports = LocalServer; -} - -// Export for use in browser environment -if (typeof window !== 'undefined') { - window.LocalServer = LocalServer; -} diff --git a/Client-Server/js/LocalServerManager.js b/Client-Server/js/LocalServerManager.js deleted file mode 100644 index 6347d2c..0000000 --- a/Client-Server/js/LocalServerManager.js +++ /dev/null @@ -1,224 +0,0 @@ -/** - * Local Server Manager - * Manages the local server for singleplayer mode within the Electron client - */ - -class LocalServerManager { - constructor() { - this.localServer = null; - this.isRunning = false; - this.port = 3004; - this.startupAttempts = 0; - this.maxStartupAttempts = 3; - - console.log('[LOCAL SERVER MANAGER] LocalServerManager initialized'); - } - - async initialize() { - console.log('[LOCAL SERVER MANAGER] Initializing local server...'); - - try { - // In Electron renderer context, use SimpleLocalServer which doesn't require Node.js modules - if (typeof window !== 'undefined' && window.SimpleLocalServer) { - this.localServer = new window.SimpleLocalServer(); - console.log('[LOCAL SERVER MANAGER] SimpleLocalServer class loaded from window'); - return true; - } else if (typeof window !== 'undefined' && window.LocalServer) { - // Fallback to original LocalServer if available - this.localServer = new window.LocalServer(); - console.log('[LOCAL SERVER MANAGER] LocalServer class loaded from window'); - return true; - } else { - console.warn('[LOCAL SERVER MANAGER] No local server class available'); - return false; - } - } catch (error) { - console.error('[LOCAL SERVER MANAGER] Failed to initialize local server:', error); - console.log('[LOCAL SERVER MANAGER] Please ensure SimpleLocalServer.js is loaded properly'); - return false; - } - } - - async startServer() { - if (this.isRunning) { - console.log('[LOCAL SERVER MANAGER] Server is already running'); - return { success: true, port: this.port }; - } - - if (!this.localServer) { - const initialized = await this.initialize(); - if (!initialized) { - return { success: false, error: 'Failed to initialize server' }; - } - } - - console.log(`[LOCAL SERVER MANAGER] Attempting to start server on port ${this.port}`); - - try { - const result = await this.localServer.start(this.port); - - if (result.success) { - this.isRunning = true; - this.port = result.port; - this.startupAttempts = 0; - - console.log(`[LOCAL SERVER MANAGER] Server started successfully on port ${this.port}`); - console.log(`[LOCAL SERVER MANAGER] Server URL: ${result.url}`); - - // Update LiveMainMenu to use local server - this.updateMainMenuForLocalMode(); - - return result; - } else { - console.error('[LOCAL SERVER MANAGER] Failed to start server:', result.error); - this.startupAttempts++; - - // Try alternative ports if first attempt fails - if (this.startupAttempts < this.maxStartupAttempts) { - this.port = 3004 + this.startupAttempts; - console.log(`[LOCAL SERVER MANAGER] Retrying with port ${this.port}`); - return await this.startServer(); - } - - return result; - } - } catch (error) { - console.error('[LOCAL SERVER MANAGER] Exception starting server:', error); - return { success: false, error: error.message }; - } - } - - async stopServer() { - if (!this.isRunning || !this.localServer) { - console.log('[LOCAL SERVER MANAGER] Server is not running'); - return { success: true }; - } - - console.log('[LOCAL SERVER MANAGER] Stopping local server...'); - - try { - const result = await this.localServer.stop(); - - if (result.success) { - this.isRunning = false; - this.port = 3004; - console.log('[LOCAL SERVER MANAGER] Server stopped successfully'); - } else { - console.error('[LOCAL SERVER MANAGER] Failed to stop server:', result.error); - } - - return result; - } catch (error) { - console.error('[LOCAL SERVER MANAGER] Exception stopping server:', error); - return { success: false, error: error.message }; - } - } - - getStatus() { - if (!this.localServer) { - return { - isRunning: false, - port: null, - connectedClients: 0, - uptime: 0 - }; - } - - const status = this.localServer.getStatus(); - return { - ...status, - url: this.isRunning ? `http://localhost:${status.port}` : null - }; - } - - getServerUrl() { - return this.isRunning ? `http://localhost:${this.port}` : null; - } - - updateMainMenuForLocalMode() { - // Update LiveMainMenu to use local server URLs - if (window.liveMainMenu) { - console.log('[LOCAL SERVER MANAGER] Updating LiveMainMenu for local mode'); - - window.liveMainMenu.apiBaseUrl = `http://localhost:${this.port}/api`; - window.liveMainMenu.gameServerUrl = `http://localhost:${this.port}`; - window.liveMainMenu.isLocalMode = true; - - console.log(`[LOCAL SERVER MANAGER] Updated API URL to: ${window.liveMainMenu.apiBaseUrl}`); - console.log(`[LOCAL SERVER MANAGER] Updated Game Server URL to: ${window.liveMainMenu.gameServerUrl}`); - - // Also update GameInitializer URLs - if (window.gameInitializer) { - window.gameInitializer.updateServerUrls( - `http://localhost:${this.port}/api`, - `http://localhost:${this.port}` - ); - console.log('[LOCAL SERVER MANAGER] Updated GameInitializer URLs for local mode'); - } else { - console.warn('[LOCAL SERVER MANAGER] GameInitializer not available for URL update'); - } - } else { - console.warn('[LOCAL SERVER MANAGER] LiveMainMenu not available for update'); - } - } - - // Auto-start server when in singleplayer mode - async autoStartIfSingleplayer() { - // Check if we should auto-start (no external server available) - try { - // Try to connect to external server first - const response = await fetch('https://api.korvarix.com/health', { - method: 'GET', - timeout: 3000 - }); - - if (response.ok) { - console.log('[LOCAL SERVER MANAGER] External server available, not starting local server'); - return { success: false, reason: 'External server available' }; - } - } catch (error) { - console.log('[LOCAL SERVER MANAGER] External server not available, starting local server'); - } - - // Start local server - return await this.startServer(); - } - - // Handle server errors and restart if needed - handleServerError(error) { - console.error('[LOCAL SERVER MANAGER] Server error:', error); - - // Try to restart server if it crashes - if (this.isRunning) { - console.log('[LOCAL SERVER MANAGER] Attempting to restart server...'); - this.stopServer().then(() => { - setTimeout(() => { - this.startServer(); - }, 2000); // Wait 2 seconds before restarting - }); - } - } - - // Get local server info for UI display - getServerInfo() { - return { - isRunning: this.isRunning, - port: this.port, - url: this.getServerUrl(), - status: this.isRunning ? 'Online' : 'Offline', - mode: 'Local Singleplayer', - connectedClients: this.localServer ? this.localServer.connectedClients.size : 0, - uptime: this.localServer ? this.localServer.uptime : 0 - }; - } -} - -// Create global instance -window.localServerManager = new LocalServerManager(); - -// Auto-export for module systems -if (typeof module !== 'undefined' && module.exports) { - module.exports = LocalServerManager; -} - -console.log('[LOCAL SERVER MANAGER] LocalServerManager loaded and global instance created'); diff --git a/Client-Server/js/SimpleLocalServer.js b/Client-Server/js/SimpleLocalServer.js deleted file mode 100644 index b5d1215..0000000 --- a/Client-Server/js/SimpleLocalServer.js +++ /dev/null @@ -1,535 +0,0 @@ -/** - * Simple Local Server for Singleplayer Mode - * A mock server that simulates server responses without requiring Node.js dependencies - * This runs entirely in the browser/renderer context - */ - -class SimpleLocalServer { - constructor() { - this.isRunning = false; - this.port = 3004; - this.connectedClients = new Map(); - this.existingSaveData = null; - - // Check for existing save data on initialization - this.loadExistingSaveData(); - - this.mockData = { - servers: [{ - id: 'local-server', - name: 'Local Singleplayer', - description: 'Your personal local server for singleplayer gaming', - type: 'private', - region: 'local', - maxPlayers: 1, - currentPlayers: 0, - owner: 'Local Player', - address: 'localhost', - port: this.port, - status: 'online', - createdAt: new Date().toISOString(), - ping: 0 - }], - user: { - id: 'local-user', - email: 'local@player.com', - username: 'Local Player', - token: 'local-token-' + Date.now(), - createdAt: new Date().toISOString() - } - }; - - console.log('[SIMPLE LOCAL SERVER] SimpleLocalServer initialized'); - console.log('[SIMPLE LOCAL SERVER] Existing save data found:', !!this.existingSaveData); - } - - // Mock Socket.IO server for local mode - createMockSocket() { - console.log('[SIMPLE LOCAL SERVER] Creating mock Socket.IO connection'); - - const mockSocket = { - connected: false, - eventHandlers: {}, - - on: function(event, handler) { - console.log(`[MOCKET SOCKET] Registering event handler for: ${event}`); - if (!this.eventHandlers[event]) { - this.eventHandlers[event] = []; - } - this.eventHandlers[event].push(handler); - }, - - emit: function(event, data) { - console.log(`[MOCKET SOCKET] Emitting event: ${event}`, data); - }, - - connect: function() { - console.log('[MOCKET SOCKET] Connecting...'); - this.connected = true; - - // Simulate successful connection - setTimeout(() => { - if (this.eventHandlers['connect']) { - this.eventHandlers['connect'].forEach(handler => handler()); - } - }, 100); - }, - - disconnect: function() { - console.log('[MOCKET SOCKET] Disconnecting...'); - this.connected = false; - if (this.eventHandlers['disconnect']) { - this.eventHandlers['disconnect'].forEach(handler => handler()); - } - } - }; - - // Auto-connect - mockSocket.connect(); - - return mockSocket; - } - - loadExistingSaveData() { - try { - const saveData = localStorage.getItem(`gso_save_slot_1`); - if (saveData) { - this.existingSaveData = JSON.parse(saveData); - console.log('[SIMPLE LOCAL SERVER] Loaded existing save data:', { - hasPlayerData: !!this.existingSaveData.player, - playerLevel: this.existingSaveData.player?.stats?.level, - lastSave: this.existingSaveData.lastSave, - gameTime: this.existingSaveData.gameTime - }); - } else { - console.log('[SIMPLE LOCAL SERVER] No existing save data found'); - } - } catch (error) { - console.warn('[SIMPLE LOCAL SERVER] Error loading existing save data:', error); - this.existingSaveData = null; - } - } - - applyExistingSaveDataToGame() { - if (!this.existingSaveData || !window.game) { - console.log('[SIMPLE LOCAL SERVER] No existing save data or game not available'); - return false; - } - - try { - console.log('[SIMPLE LOCAL SERVER] Applying existing save data to game...'); - - // Apply save data to game systems - if (this.existingSaveData.player && window.game.systems.player) { - window.game.systems.player.load(this.existingSaveData.player); - console.log('[SIMPLE LOCAL SERVER] Player data applied'); - } - - if (this.existingSaveData.inventory && window.game.systems.inventory) { - window.game.systems.inventory.load(this.existingSaveData.inventory); - console.log('[SIMPLE LOCAL SERVER] Inventory data applied'); - } - - if (this.existingSaveData.economy && window.game.systems.economy) { - window.game.systems.economy.load(this.existingSaveData.economy); - console.log('[SIMPLE LOCAL SERVER] Economy data applied'); - } - - if (this.existingSaveData.idleSystem && window.game.systems.idleSystem) { - window.game.systems.idleSystem.load(this.existingSaveData.idleSystem); - console.log('[SIMPLE LOCAL SERVER] Idle system data applied'); - } - - if (this.existingSaveData.dungeonSystem && window.game.systems.dungeonSystem) { - window.game.systems.dungeonSystem.load(this.existingSaveData.dungeonSystem); - console.log('[SIMPLE LOCAL SERVER] Dungeon system data applied'); - } - - if (this.existingSaveData.skillSystem && window.game.systems.skillSystem) { - window.game.systems.skillSystem.load(this.existingSaveData.skillSystem); - console.log('[SIMPLE LOCAL SERVER] Skill system data applied'); - } - - if (this.existingSaveData.baseSystem && window.game.systems.baseSystem) { - window.game.systems.baseSystem.load(this.existingSaveData.baseSystem); - console.log('[SIMPLE LOCAL SERVER] Base system data applied'); - } - - if (this.existingSaveData.questSystem && window.game.systems.questSystem) { - window.game.systems.questSystem.load(this.existingSaveData.questSystem); - console.log('[SIMPLE LOCAL SERVER] Quest system data applied'); - } - - if (this.existingSaveData.gameTime && window.game) { - window.game.gameTime = this.existingSaveData.gameTime; - console.log('[SIMPLE LOCAL SERVER] Game time applied:', this.existingSaveData.gameTime); - } - - console.log('[SIMPLE LOCAL SERVER] All save data applied successfully'); - return true; - - } catch (error) { - console.error('[SIMPLE LOCAL SERVER] Error applying save data to game:', error); - return false; - } - } - - async start(port = 3004) { - if (this.isRunning) { - console.log('[SIMPLE LOCAL SERVER] Server is already running on port', this.port); - return { success: false, error: 'Server already running' }; - } - - try { - this.port = port; - this.isRunning = true; - - // Update mock server data with actual port - this.mockData.servers[0].port = port; - - console.log(`[SIMPLE LOCAL SERVER] Mock local server started on port ${port}`); - - return { - success: true, - port: port, - url: `http://localhost:${port}` - }; - - } catch (error) { - console.error('[SIMPLE LOCAL SERVER] Failed to start server:', error); - return { success: false, error: error.message }; - } - } - - async stop() { - if (!this.isRunning) { - console.log('[SIMPLE LOCAL SERVER] Server is not running'); - return { success: false, error: 'Server is not running' }; - } - - try { - this.isRunning = false; - this.connectedClients.clear(); - - console.log('[SIMPLE LOCAL SERVER] Mock local server stopped'); - return { success: true }; - - } catch (error) { - console.error('[SIMPLE LOCAL SERVER] Failed to stop server:', error); - return { success: false, error: error.message }; - } - } - - getStatus() { - return { - isRunning: this.isRunning, - port: this.port, - connectedClients: this.connectedClients.size, - uptime: this.isRunning ? 0 : 0 // Mock uptime - }; - } - - getUrl() { - return this.isRunning ? `http://localhost:${this.port}` : null; - } - - // Mock API methods that simulate server responses - async mockRequest(method, url, data = null) { - console.log(`[SIMPLE LOCAL SERVER] Mock ${method} ${url}`, data); - - // Simulate network delay - await new Promise(resolve => setTimeout(resolve, 100)); - - try { - if (url === '/health') { - return { - status: 200, - ok: true, - headers: { - get: (name) => name === 'content-type' ? 'application/json' : null - }, - json: () => Promise.resolve({ - status: 'OK', - timestamp: new Date().toISOString(), - uptime: 0, - mode: 'local' - }), - text: () => Promise.resolve(JSON.stringify({ - status: 'OK', - timestamp: new Date().toISOString(), - uptime: 0, - mode: 'local' - })) - }; - } - - if (url === '/api/ssc/version') { - return { - status: 200, - ok: true, - headers: { - get: (name) => name === 'content-type' ? 'application/json' : null - }, - json: () => Promise.resolve({ - version: '1.0.0', - service: 'galaxystrikeonline-local-server', - timestamp: new Date().toISOString(), - mode: 'local' - }), - text: () => Promise.resolve(JSON.stringify({ - version: '1.0.0', - service: 'galaxystrikeonline-local-server', - timestamp: new Date().toISOString(), - mode: 'local' - })) - }; - } - - if (url === '/api/auth/login' && method === 'POST') { - return { - status: 200, - ok: true, - headers: { - get: (name) => name === 'content-type' ? 'application/json' : null - }, - json: () => Promise.resolve({ - success: true, - user: this.mockData.user, - token: this.mockData.user.token, - message: 'Logged in to local mode' - }), - text: () => Promise.resolve(JSON.stringify({ - success: true, - user: this.mockData.user, - token: this.mockData.user.token, - message: 'Logged in to local mode' - })) - }; - } - - if (url === '/api/auth/register' && method === 'POST') { - return { - status: 201, - ok: true, - headers: { - get: (name) => name === 'content-type' ? 'application/json' : null - }, - json: () => Promise.resolve({ - success: true, - user: this.mockData.user, - token: this.mockData.user.token, - message: 'Registered in local mode' - }), - text: () => Promise.resolve(JSON.stringify({ - success: true, - user: this.mockData.user, - token: this.mockData.user.token, - message: 'Registered in local mode' - })) - }; - } - - if (url === '/api/servers') { - return { - status: 200, - ok: true, - headers: { - get: (name) => name === 'content-type' ? 'application/json' : null - }, - json: () => Promise.resolve({ - success: true, - servers: this.mockData.servers - }), - text: () => Promise.resolve(JSON.stringify({ - success: true, - servers: this.mockData.servers - })) - }; - } - - if (url.startsWith('/servers/') && url.endsWith('/join') && method === 'POST') { - // Mock server join response - const serverId = url.split('/')[2]; - return { - status: 200, - ok: true, - headers: { - get: (name) => name === 'content-type' ? 'application/json' : null - }, - json: () => Promise.resolve({ - success: true, - server: { - id: serverId, - name: 'Local Singleplayer', - address: 'localhost', - port: this.port, - gamePort: this.port + 1, - maxPlayers: 1, - currentPlayers: 1, - status: 'online' - }, - message: 'Joined server successfully' - }), - text: () => Promise.resolve(JSON.stringify({ - success: true, - server: { - id: serverId, - name: 'Local Singleplayer', - address: 'localhost', - port: this.port, - gamePort: this.port + 1, - maxPlayers: 1, - currentPlayers: 1, - status: 'online' - }, - message: 'Joined server successfully' - })) - }; - } - - if (url.startsWith('/api/game/player/') && method === 'GET') { - // Return player data from existing save data or localStorage - let saveData = this.existingSaveData; - - // If no existing save data, try localStorage - if (!saveData) { - try { - const localStorageData = localStorage.getItem(`gso_save_slot_1`); - if (localStorageData) { - saveData = JSON.parse(localStorageData); - } - } catch (error) { - console.warn('[SIMPLE LOCAL SERVER] Could not access localStorage:', error); - } - } - - if (saveData) { - return { - status: 200, - ok: true, - headers: { - get: (name) => name === 'content-type' ? 'application/json' : null - }, - json: () => Promise.resolve({ - success: true, - player: saveData.player - }), - text: () => Promise.resolve(JSON.stringify({ - success: true, - player: saveData.player - })) - }; - } else { - return { - status: 404, - ok: false, - headers: { - get: (name) => name === 'content-type' ? 'application/json' : null - }, - json: () => Promise.resolve({ - success: false, - error: 'No save data found' - }), - text: () => Promise.resolve(JSON.stringify({ - success: false, - error: 'No save data found' - })) - }; - } - } - - if (url.startsWith('/api/game/player/') && method === 'POST') { - // Save player data to localStorage - try { - let existingSaveData = '{}'; - - if (typeof localStorage !== 'undefined') { - existingSaveData = localStorage.getItem(`gso_save_slot_1`) || '{}'; - } - - const parsedExisting = JSON.parse(existingSaveData); - parsedExisting.player = data; - parsedExisting.lastSave = Date.now(); - - if (typeof localStorage !== 'undefined') { - localStorage.setItem(`gso_save_slot_1`, JSON.stringify(parsedExisting)); - } - - return { - status: 200, - ok: true, - headers: { - get: (name) => name === 'content-type' ? 'application/json' : null - }, - json: () => Promise.resolve({ - success: true, - message: 'Player data saved locally' - }), - text: () => Promise.resolve(JSON.stringify({ - success: true, - message: 'Player data saved locally' - })) - }; - } catch (error) { - return { - status: 500, - ok: false, - headers: { - get: (name) => name === 'content-type' ? 'application/json' : null - }, - json: () => Promise.resolve({ - success: false, - error: 'Failed to save player data' - }), - text: () => Promise.resolve(JSON.stringify({ - success: false, - error: 'Failed to save player data' - })) - }; - } - } - - // Default response for unknown endpoints - return { - status: 404, - ok: false, - headers: { - get: (name) => name === 'content-type' ? 'application/json' : null - }, - json: () => Promise.resolve({ - success: false, - error: 'Endpoint not found' - }), - text: () => Promise.resolve(JSON.stringify({ - success: false, - error: 'Endpoint not found' - })) - }; - - } catch (error) { - console.error('[SIMPLE LOCAL SERVER] Mock request error:', error); - return { - status: 500, - ok: false, - headers: { - get: (name) => name === 'content-type' ? 'application/json' : null - }, - json: () => Promise.resolve({ - success: false, - error: 'Internal server error' - }), - text: () => Promise.resolve(JSON.stringify({ - success: false, - error: 'Internal server error' - })) - }; - } - } -} - -// Export for use in browser environment -if (typeof window !== 'undefined') { - window.SimpleLocalServer = SimpleLocalServer; -} - -console.log('[SIMPLE LOCAL SERVER] SimpleLocalServer loaded and exported to window'); diff --git a/Client-Server/js/core/Economy.js b/Client-Server/js/core/Economy.js deleted file mode 100644 index ecdea9c..0000000 --- a/Client-Server/js/core/Economy.js +++ /dev/null @@ -1,2335 +0,0 @@ -/** - * 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 = ` - - 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 = ` -
-
- - Next refresh in: ${this.getShopRefreshCountdown()} -
-
- `; - 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 = ` -
- ${item.texture ? - `
- ${item.name} -
` : ''} -
-
${item.name}
-
${item.description}
- ${item.stats ? ` -
- ${Object.entries(item.stats).map(([stat, value]) => - `
${stat}: ${value}
` - ).join('')} -
- ` : ''} -
${this.formatPrice(item)}
-
${item.rarity}
-
-
- - `; - - 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)}`; - } -} diff --git a/Client-Server/js/core/GameEngine.js b/Client-Server/js/core/GameEngine.js deleted file mode 100644 index f991638..0000000 --- a/Client-Server/js/core/GameEngine.js +++ /dev/null @@ -1,1567 +0,0 @@ -/** - * Galaxy Strike Online - Game Engine - * Core game loop and state management - */ - -class GameEngine extends EventTarget { - constructor() { - super(); // Call EventTarget constructor first - - console.log('🛑 DEBUG STOP 1: GameEngine constructor starting'); - const debugLogger = window.debugLogger; - if (debugLogger) debugLogger.log('GameEngine constructor called', { - autoSaveInterval: 5, - timestamp: new Date().toISOString() - }); - - this.isRunning = false; - this.lastUpdate = 0; - this.gameTime = 0; - - // Auto-save settings - this.autoSaveInterval = 1; // Default 1 minute - this.autoSaveTimer = null; - this.lastAutoSave = 0; - - // Save slot information - this.saveSlotInfo = { - slot: 1, // Default save slot - useFileSystem: !!window.electronAPI // Use file system if Electron API is available - }; - - console.log('🛑 DEBUG STOP 2: Save slot info initialized:', this.saveSlotInfo); - - // GUI update settings - this.guiUpdateInterval = 1000; // Update GUI once per second (1000ms) - this.lastGUIUpdate = 0; - - // Game logic settings (independent of frame rate) - this.gameLogicInterval = 1000; // Update game logic every 1 second - this.gameLogicTimer = null; - - // Game state - this.state = { - paused: false, - currentTab: 'dashboard', - notifications: [], - loading: true - }; - - console.log('🛑 DEBUG STOP 3: Game state initialized:', this.state); - - // Systems - this.systems = {}; - - // Event listeners - this.eventListeners = new Map(); - - console.log('🛑 DEBUG STOP 4: About to call this.init()'); - this.init(); - console.log('🛑 DEBUG STOP 5: GameEngine constructor completed'); - } - - setMultiplayerMode(isMultiplayer, socket = null, serverData = null, currentUser = null) { - const debugLogger = window.debugLogger; - - console.log('[GAME ENGINE] Setting multiplayer mode:', isMultiplayer); - if (debugLogger) debugLogger.logStep('setMultiplayerMode', { isMultiplayer }); - - this.isMultiplayer = isMultiplayer; - this.socket = socket; - this.serverData = serverData; - this.currentUser = currentUser; - - // Store multiplayer settings for systems that need them - this.multiplayerConfig = { - isMultiplayer, - socket, - serverData, - currentUser - }; - - console.log('[GAME ENGINE] Multiplayer mode configured:', { - isMultiplayer, - hasSocket: !!socket, - hasServerData: !!serverData, - hasCurrentUser: !!currentUser - }); - } - - // Get random integer between min and max (inclusive) - getRandomInt(min, max) { - return Math.floor(Math.random() * (max - min + 1)) + min; - } - - // Get random float between min and max - getRandomFloat(min, max) { - return Math.random() * (max - min) + min; - } - - async init() { - console.log('[GAME ENGINE] Initializing game engine'); - const logger = window.logger; - const debugLogger = window.debugLogger; - - if (logger) await logger.info('Initializing game engine'); - if (debugLogger) await debugLogger.startStep('gameEngineInit'); - - try { - // Initialize core systems but don't setup new game data - await this.initializeSystemsForLoad(); - - if (debugLogger) await debugLogger.logStep('Systems initialized, setting up event listeners'); - - // Set up event listeners - await this.setupEventListeners(); - - if (debugLogger) await debugLogger.logStep('Event listeners setup complete'); - - if (logger) await logger.info('Game engine initialization completed'); - if (debugLogger) await debugLogger.endStep('gameEngineInit', { - systemsInitialized: Object.keys(this.systems).length, - eventListeners: this.eventListeners.size - }); - - } catch (error) { - console.error('Failed to initialize game:', error); - if (logger) await logger.errorEvent(error, 'Game Engine Initialization'); - if (debugLogger) await debugLogger.errorEvent(error, 'Game Engine Initialization'); - } - } - - async startGame(continueGame = false) { - const logger = window.logger; - const debugLogger = window.debugLogger; - - console.log('[GAME ENGINE] startGame called with continueGame =', continueGame); - if (logger) await logger.info('Starting game', { continueGame }); - if (debugLogger) await debugLogger.startStep('startGame', { continueGame }); - - try { - if (continueGame) { - console.log('[GAME ENGINE] Loading existing save data...'); - if (debugLogger) await debugLogger.logStep('Loading existing save data'); - await this.loadGame(); - console.log('[GAME ENGINE] Save data loaded'); - } else { - console.log('[GAME ENGINE] Creating new game...'); - if (debugLogger) await debugLogger.logStep('Creating new game'); - await this.newGame(); - console.log('[GAME ENGINE] New game created'); - } - - // Start game loop - // CRITICAL: Ensure UIManager is initialized before starting game - if (this.systems.ui) { - console.log('[GAME ENGINE] Final UIManager initialization check...'); - try { - await this.systems.ui.initialize(); - console.log('[GAME ENGINE] UIManager initialized successfully'); - } catch (error) { - console.error('[GAME ENGINE] UIManager initialization failed:', error); - } - } - - this.start(); - console.log('[GAME ENGINE] Game loop started'); - if (debugLogger) await debugLogger.logStep('Game loop started'); - - const loadingStatus = document.getElementById('loadingStatus'); - if (loadingStatus) { - console.log('[GAME ENGINE] Hiding loading status text'); - if (debugLogger) await debugLogger.logStep('Hiding loading status text'); - loadingStatus.classList.add('hidden'); - } - - const gameInterface = document.getElementById('gameInterface'); - if (gameInterface) { - console.log('[GAME ENGINE] Showing game interface'); - if (debugLogger) await debugLogger.logStep('Showing game interface'); - gameInterface.classList.remove('hidden'); - - // CRITICAL: Initialize UIManager after showing interface - console.log('[GAME ENGINE] Initializing UIManager after showing interface...'); - if (this.systems.ui) { - try { - await this.systems.ui.initialize(); - console.log('[GAME ENGINE] UIManager initialized successfully (post-interface)'); - } catch (error) { - console.error('[GAME ENGINE] UIManager initialization failed:', error); - } - } else { - console.log('[GAME ENGINE] UIManager not found when trying to initialize after interface'); - } - } else { - console.warn('[GAME ENGINE] gameInterface element not found'); - if (debugLogger) await debugLogger.warn('gameInterface element not found'); - } - - if (logger) await logger.info('Game started successfully'); - if (debugLogger) await debugLogger.endStep('startGame', { - continueGame, - isRunning: this.isRunning, - gameTime: this.gameTime - }); - - } catch (error) { - console.error('[GAME ENGINE] Failed to start game:', error); - if (logger) await logger.errorEvent(error, 'Game Start'); - if (debugLogger) await debugLogger.errorEvent(error, 'Game Start'); - } - } - - async initializeSystemsForLoad() { - const logger = window.logger; - const debugLogger = window.debugLogger; - - if (debugLogger) await debugLogger.startStep('initializeSystemsForLoad'); - - if (logger) { - await logger.timeAsync('Game Systems Initialization for Load', async () => { - await logger.info('Initializing game systems for loading'); - - if (debugLogger) await debugLogger.logStep('Initializing TextureManager'); - // Initialize texture manager first - this.systems.textureManager = new TextureManager(this); - if (logger) await logger.systemEvent('TextureManager', 'Initialized'); - if (debugLogger) await debugLogger.logStep('TextureManager initialized'); - - if (debugLogger) await debugLogger.logStep('Creating Player system (without initialization)'); - // Create systems but don't initialize with default data - this.systems.player = new Player(this); - if (logger) await logger.systemEvent('Player', 'Created'); - if (debugLogger) await debugLogger.logStep('Player system created'); - - if (debugLogger) await debugLogger.logStep('Creating Inventory system (without initialization)'); - this.systems.inventory = new Inventory(this); - if (logger) await logger.systemEvent('Inventory', 'Created'); - if (debugLogger) await debugLogger.logStep('Inventory system created'); - - if (debugLogger) await debugLogger.logStep('Creating Economy system (without initialization)'); - this.systems.economy = new Economy(this); - if (logger) await logger.systemEvent('Economy', 'Created'); - if (debugLogger) await debugLogger.logStep('Economy system created'); - - if (debugLogger) await debugLogger.logStep('Creating IdleSystem'); - this.systems.idleSystem = new IdleSystem(this); - if (logger) await logger.systemEvent('IdleSystem', 'Created'); - if (debugLogger) await debugLogger.logStep('IdleSystem created'); - - if (debugLogger) await debugLogger.logStep('Creating DungeonSystem'); - this.systems.dungeonSystem = new DungeonSystem(this); - if (logger) await logger.systemEvent('DungeonSystem', 'Created'); - if (debugLogger) await debugLogger.logStep('DungeonSystem created'); - - if (debugLogger) await debugLogger.logStep('Creating SkillSystem'); - this.systems.skillSystem = new SkillSystem(this); - if (logger) await logger.systemEvent('SkillSystem', 'Created'); - if (debugLogger) await debugLogger.logStep('SkillSystem created'); - - if (debugLogger) await debugLogger.logStep('Creating BaseSystem'); - this.systems.baseSystem = new BaseSystem(this); - if (logger) await logger.systemEvent('BaseSystem', 'Created'); - if (debugLogger) await debugLogger.logStep('BaseSystem created'); - - if (debugLogger) await debugLogger.logStep('Creating QuestSystem'); - this.systems.questSystem = new QuestSystem(this); - if (logger) await logger.systemEvent('QuestSystem', 'Created'); - if (debugLogger) await debugLogger.logStep('QuestSystem created'); - - if (debugLogger) await debugLogger.logStep('Creating ShipSystem'); - this.systems.shipSystem = new ShipSystem(this); - if (logger) await logger.systemEvent('ShipSystem', 'Created'); - if (debugLogger) await debugLogger.logStep('ShipSystem created'); - - if (debugLogger) await debugLogger.logStep('Creating UIManager'); - if (typeof UIManager !== 'undefined') { - console.log('[GAME ENGINE] UIManager class found, creating real UIManager'); - this.systems.ui = new UIManager(this); - // Expose UIManager globally for button onclick handlers - window.uiManager = this.systems.ui; - window.game.systems.ui = this.systems.ui; - if (logger) await logger.systemEvent('UIManager', 'Created'); - if (debugLogger) await debugLogger.logStep('UIManager created and exposed'); - } else { - console.error('[GAME ENGINE] UIManager class not found - this should not happen!'); - if (debugLogger) await debugLogger.error('UIManager class not found - this should not happen!'); - // Create minimal UI system to prevent crashes (should never be needed based on logs) - this.systems.ui = { - showNotification: (message, type, duration) => { - console.log(`[UI MINIMAL] ${type}: ${message}`); - }, - updateUI: () => { - console.log('[UI MINIMAL] updateUI called'); - }, - switchTab: (tabName) => { - console.log('[UI MINIMAL] switchTab called'); - } - }; - } - if (debugLogger) await debugLogger.logStep('Creating CraftingSystem'); - this.systems.crafting = new CraftingSystem(this); - if (logger) await logger.systemEvent('CraftingSystem', 'Created'); - if (debugLogger) await debugLogger.logStep('CraftingSystem created'); - - if (debugLogger) await debugLogger.endStep('initializeSystemsForLoad', { - systemsCreated: Object.keys(this.systems).length - }); - }); - } - } - - async initializeSystems() { - const logger = window.logger; - const debugLogger = window.debugLogger; - - if (debugLogger) await debugLogger.startStep('initializeSystems'); - - if (logger) { - await logger.timeAsync('Game Systems Initialization', async () => { - await logger.info('Initializing game systems'); - - if (debugLogger) await debugLogger.logStep('Initializing TextureManager'); - // Initialize texture manager first - this.systems.textureManager = new TextureManager(this); - if (logger) await logger.systemEvent('TextureManager', 'Initialized'); - if (debugLogger) await debugLogger.logStep('TextureManager initialized'); - - if (debugLogger) await debugLogger.logStep('Initializing Player system'); - // Initialize in dependency order - this.systems.player = new Player(this); - if (logger) await logger.systemEvent('Player', 'Initialized'); - if (debugLogger) await debugLogger.logStep('Player system initialized', { - playerLevel: this.systems.player.stats.level, - playerExperience: this.systems.player.stats.experience - }); - - if (debugLogger) await debugLogger.logStep('Initializing Inventory system'); - this.systems.inventory = new Inventory(this); - if (logger) await logger.systemEvent('Inventory', 'Initialized'); - if (debugLogger) await debugLogger.logStep('Inventory system initialized', { - inventorySize: this.systems.inventory.items.length - }); - - if (debugLogger) await debugLogger.logStep('Initializing Economy system'); - this.systems.economy = new Economy(this); - if (logger) await logger.systemEvent('Economy', 'Initialized'); - if (debugLogger) await debugLogger.logStep('Economy system initialized', { - credits: this.systems.economy.credits, - gems: this.systems.economy.gems - }); - - if (debugLogger) await debugLogger.logStep('Initializing IdleSystem'); - this.systems.idleSystem = new IdleSystem(this); - if (logger) await logger.systemEvent('IdleSystem', 'Initialized'); - if (debugLogger) await debugLogger.logStep('IdleSystem initialized'); - - if (debugLogger) await debugLogger.logStep('Initializing DungeonSystem'); - this.systems.dungeonSystem = new DungeonSystem(this); - if (logger) await logger.systemEvent('DungeonSystem', 'Initialized'); - if (debugLogger) await debugLogger.logStep('DungeonSystem initialized'); - - if (debugLogger) await debugLogger.logStep('Initializing SkillSystem'); - this.systems.skillSystem = new SkillSystem(this); - if (logger) await logger.systemEvent('SkillSystem', 'Initialized'); - if (debugLogger) await debugLogger.logStep('SkillSystem initialized'); - - if (debugLogger) await debugLogger.logStep('Initializing BaseSystem'); - this.systems.baseSystem = new BaseSystem(this); - if (logger) await logger.systemEvent('BaseSystem', 'Initialized'); - if (debugLogger) await debugLogger.logStep('BaseSystem initialized'); - - if (debugLogger) await debugLogger.logStep('Initializing QuestSystem'); - this.systems.questSystem = new QuestSystem(this); - await this.systems.questSystem.initialize(); - if (logger) await logger.systemEvent('QuestSystem', 'Initialized'); - if (debugLogger) await debugLogger.logStep('QuestSystem initialized'); - - if (debugLogger) await debugLogger.logStep('Initializing ShipSystem'); - this.systems.ship = new ShipSystem(this); - if (logger) await logger.systemEvent('ShipSystem', 'Initialized'); - if (debugLogger) await debugLogger.logStep('ShipSystem initialized'); - - if (debugLogger) await debugLogger.logStep('UIManager already initialized in initializeSystemsForLoad'); - - if (debugLogger) await debugLogger.logStep('Initializing CraftingSystem'); - this.systems.crafting = new CraftingSystem(this); - if (logger) await logger.systemEvent('CraftingSystem', 'Initialized'); - if (debugLogger) await debugLogger.logStep('CraftingSystem initialized'); - - if (logger) await logger.info('All game systems initialized'); - if (debugLogger) await debugLogger.logStep('All systems created, running individual initializations'); - - if (debugLogger) await debugLogger.logStep('Running individual system initializations'); - for (const [name, system] of Object.entries(this.systems)) { - if (system.initialize) { - if (debugLogger) await debugLogger.logStep(`Initializing ${name} system`); - await system.initialize(); - if (debugLogger) await debugLogger.logStep(`${name} system initialization complete`); - } else { - if (debugLogger) await debugLogger.logStep(`${name} system has no initialize method`); - } - } - - if (debugLogger) await debugLogger.endStep('initializeSystems', { - totalSystems: Object.keys(this.systems).length, - systemsList: Object.keys(this.systems) - }); - }); - } else { - // Fallback without timing - if (debugLogger) await debugLogger.logStep('Logger not available, using fallback initialization'); - - // Initialize texture manager first - this.systems.textureManager = new TextureManager(this); - - // Initialize in dependency order - this.systems.player = new Player(this); - this.systems.inventory = new Inventory(this); - this.systems.economy = new Economy(this); - this.systems.idleSystem = new IdleSystem(this); - this.systems.dungeonSystem = new DungeonSystem(this); - this.systems.skillSystem = new SkillSystem(this); - this.systems.baseSystem = new BaseSystem(this); - this.systems.questSystem = new QuestSystem(this); - // UIManager already initialized in initializeSystemsForLoad - this.systems.crafting = new CraftingSystem(this); - - for (const [name, system] of Object.entries(this.systems)) { - if (system.initialize) { - await system.initialize(); - } - } - - if (debugLogger) await debugLogger.endStep('initializeSystems', { - fallbackMode: true, - totalSystems: Object.keys(this.systems).length - }); - } - } - - async setupEventListeners() { - const debugLogger = window.debugLogger; - - if (debugLogger) await debugLogger.startStep('setupEventListeners'); - - // Window events - if (debugLogger) await debugLogger.logStep('Setting up window event listeners'); - window.addEventListener('beforeunload', () => { - if (debugLogger) debugLogger.logStep('beforeunload event triggered - saving game'); - this.save(); - }); - - window.addEventListener('visibilitychange', () => { - // if (debugLogger) debugLogger.logStep('visibilitychange event triggered', { - // hidden: document.hidden, - // visibilityState: document.visibilityState - // }); - if (document.hidden) { - // if (debugLogger) debugLogger.logStep('Document hidden - saving game'); - this.save(); - } - }); - - // Keyboard shortcuts - if (debugLogger) await debugLogger.logStep('Setting up keyboard shortcuts'); - document.addEventListener('keydown', (event) => { - if (debugLogger) debugLogger.logStep('Key pressed', { - key: event.key, - code: event.code, - ctrlKey: event.ctrlKey, - altKey: event.altKey, - shiftKey: event.shiftKey - }); - - // Ctrl+S for manual save - if (event.ctrlKey && event.key === 's') { - event.preventDefault(); - if (debugLogger) debugLogger.logStep('Manual save shortcut triggered (Ctrl+S)'); - this.save(); - } - }); - - // Game loop events - if (debugLogger) await debugLogger.logStep('Setting up game loop events'); - this.addEventListener('gameStarted', () => { - if (debugLogger) debugLogger.logStep('Game started event received'); - }); - - this.addEventListener('gameStopped', () => { - if (debugLogger) debugLogger.logStep('Game stopped event received'); - }); - - this.addEventListener('gameSaved', () => { - // if (debugLogger) debugLogger.logStep('Game saved event received'); - }); - - this.addEventListener('gameLoaded', () => { - if (debugLogger) debugLogger.logStep('Game loaded event received'); - }); - - // Setup UI event listeners - if (debugLogger) await debugLogger.logStep('Setting up UI event listeners'); - if (this.systems.ui && typeof this.systems.ui.setupEventListeners === 'function' && !this.uiEventListenersSetup) { - this.systems.ui.setupEventListeners(); - this.uiEventListenersSetup = true; // Prevent duplicate setup - if (debugLogger) await debugLogger.logStep('UI event listeners setup complete'); - } else if (this.uiEventListenersSetup) { - if (debugLogger) await debugLogger.logStep('UI event listeners already setup, skipping'); - } - - if (debugLogger) await debugLogger.endStep('setupEventListeners', { - windowEvents: 2, - keyboardShortcuts: 2, - gameLoopEvents: 4 - }); - } - - start() { - const debugLogger = window.debugLogger; - - if (this.isRunning) { - if (debugLogger) debugLogger.log('GameEngine.start() called but game is already running', { - isRunning: this.isRunning, - gameTime: this.gameTime - }); - return; - } - - if (debugLogger) debugLogger.logStep('Starting game engine', { - gameLogicInterval: this.gameLogicInterval - }); - - this.isRunning = true; - this.lastUpdate = performance.now(); - - if (debugLogger) debugLogger.logStep('Game engine started, beginning game loop'); - - this.gameLoop(); - - // Loading screen is now handled by startGame() method - // Don't duplicate the hiding logic here - - if (debugLogger) debugLogger.logStep('Game loop initiated'); - } - - async stop() { - const debugLogger = window.debugLogger; - - if (debugLogger) await debugLogger.startStep('stopGame'); - - console.log('[GAME ENGINE] Stopping game and saving...'); - if (debugLogger) await debugLogger.logStep('Stopping game engine'); - - if (!this.isRunning) { - if (debugLogger) debugLogger.logStep('Game already stopped, ignoring stop request'); - return; - } - - this.isRunning = false; - - // Clear game logic timer - if (this.gameLogicTimer) { - clearInterval(this.gameLogicTimer); - this.gameLogicTimer = null; - } - - // Clear auto-save timer - if (this.autoSaveTimer) { - clearInterval(this.autoSaveTimer); - this.autoSaveTimer = null; - } - - // Clear server polling timer (multiplayer mode) - if (this.serverPollTimer) { - clearInterval(this.serverPollTimer); - this.serverPollTimer = null; - } - - // Stop economy system timers - if (this.systems.economy) { - this.systems.economy.stopShopTimers(); - } - - console.log('[GAME ENGINE] Game stopped and saved successfully'); - - if (debugLogger) await debugLogger.endStep('stopGame', { - gameTime: this.gameTime, - isRunning: this.isRunning - }); - } - - startAutoSave() { - const debugLogger = window.debugLogger; - - // Load saved interval or use default - const savedInterval = localStorage.getItem('autoSaveInterval'); - this.autoSaveInterval = savedInterval ? parseInt(savedInterval) : 5; - - console.log(`[GAME ENGINE] Starting auto-save with ${this.autoSaveInterval} minute interval`); - if (debugLogger) debugLogger.logStep('Starting auto-save system', { - interval: this.autoSaveInterval, - intervalMs: this.autoSaveInterval * 60 * 1000, - savedInterval: savedInterval, - isRunning: this.isRunning - }); - - // Clear any existing timer - this.stopAutoSave(); - - // Set up new timer - this.autoSaveTimer = setInterval(async () => { - if (debugLogger) debugLogger.logStep('Auto-save timer triggered', { - isRunning: this.isRunning, - paused: this.state.paused, - gameTime: this.gameTime, - lastAutoSave: this.lastAutoSave - }); - - console.log('[GAME ENGINE] Auto-save timer triggered - isRunning:', this.isRunning, 'paused:', this.state.paused); - - if (this.isRunning && !this.state.paused) { - console.log('[GAME ENGINE] Auto-saving game...'); - if (debugLogger) debugLogger.logStep('Auto-saving game'); - - try { - await this.save(); - this.showNotification('Game auto-saved', 'info', 2000); - console.log('[GAME ENGINE] Auto-save completed successfully'); - - this.lastAutoSave = Date.now(); - if (debugLogger) debugLogger.logStep('Auto-save completed successfully', { - lastAutoSave: this.lastAutoSave - }); - - } catch (error) { - console.error('[GAME ENGINE] Auto-save failed:', error); - if (debugLogger) await debugLogger.errorEvent(error, 'Auto-save'); - } - } else { - console.log('[GAME ENGINE] Auto-save skipped - game not running or paused'); - if (debugLogger) debugLogger.logStep('Auto-save skipped', { - reason: this.isRunning ? 'paused' : 'not running', - isRunning: this.isRunning, - paused: this.state.paused - }); - } - }, this.autoSaveInterval * 60 * 1000); // Convert minutes to milliseconds - - this.lastAutoSave = Date.now(); - if (debugLogger) debugLogger.logStep('Auto-save timer started', { - timerId: this.autoSaveTimer, - lastAutoSave: this.lastAutoSave - }); - } - - stopAutoSave() { - const debugLogger = window.debugLogger; - - if (this.autoSaveTimer) { - console.log('[GAME ENGINE] Stopping auto-save timer'); - if (debugLogger) debugLogger.logStep('Stopping auto-save timer', { - timerId: this.autoSaveTimer, - wasRunning: true - }); - - clearInterval(this.autoSaveTimer); - this.autoSaveTimer = null; - - if (debugLogger) debugLogger.logStep('Auto-save timer stopped'); - } else { - if (debugLogger) debugLogger.logStep('stopAutoSave called but no timer was active', { - timerId: this.autoSaveTimer, - wasRunning: false - }); - } - } - - updateAutoSaveInterval(newInterval) { - const debugLogger = window.debugLogger; - - console.log(`[GAME ENGINE] Updating auto-save interval to ${newInterval} minutes`); - if (debugLogger) debugLogger.logStep('Updating auto-save interval', { - oldInterval: this.autoSaveInterval, - newInterval: newInterval, - isRunning: this.isRunning - }); - - this.autoSaveInterval = newInterval; - - // Save to localStorage - localStorage.setItem('autoSaveInterval', newInterval.toString()); - - // Restart auto-save with new interval if game is running - if (this.isRunning) { - if (debugLogger) debugLogger.logStep('Restarting auto-save with new interval'); - this.startAutoSave(); - } - - if (debugLogger) debugLogger.logStep('Auto-save interval updated successfully', { - currentInterval: this.autoSaveInterval, - savedToStorage: true - }); - } - - start() { - const debugLogger = window.debugLogger; - - if (this.isRunning) { - if (debugLogger) debugLogger.log('GameEngine.start() called but game is already running', { - isRunning: this.isRunning, - gameTime: this.gameTime - }); - return; - } - - if (debugLogger) debugLogger.logStep('Starting game engine', { - gameLogicInterval: this.gameLogicInterval - }); - - this.isRunning = true; - this.lastUpdate = Date.now(); - - // Start game logic timer (completely independent of frame rate) - console.log('[GAME ENGINE] Starting game logic timer with interval:', this.gameLogicInterval); - this.gameLogicTimer = setInterval(() => { - this.updateGameLogic(); - }, this.gameLogicInterval); - - // Start auto-save - this.startAutoSave(); - - // Check and apply any pending server data - if (this.checkAndApplyPendingServerData) { - this.checkAndApplyPendingServerData(); - } - - console.log('[GAME ENGINE] Game engine started'); - if (debugLogger) debugLogger.logStep('Game engine started successfully', { - gameLogicInterval: this.gameLogicInterval, - autoSaveInterval: this.autoSaveInterval - }); - } - - updateGameLogic() { - const debugLogger = window.debugLogger; - - // Use fixed 1-second interval for all updates - const fixedDelta = 1000; // 1 second in milliseconds - - console.log('[GAME ENGINE] Game logic update called - adding', fixedDelta, 'ms to playtime'); - - if (this.state.paused) { - if (debugLogger) debugLogger.logStep('Game logic update called but game is paused', { - gameTime: this.gameTime, - fixedDelta: fixedDelta - }); - return; - } - - this.gameTime += fixedDelta; - - - // Log update performance every 300 frames (approximately 5 seconds) - if (debugLogger && this.gameTime % 15000 === 0) { // Every 15 seconds of game time - debugLogger.logStep('Game logic update cycle', { - gameTime: this.gameTime, - fixedDelta: fixedDelta, - isRunning: this.isRunning, - paused: this.state.paused, - currentTab: this.state.currentTab - }); - } - - // Update player play time with fixed delta - if (this.systems.player && this.systems.player.updatePlayTime) { - console.log('[GAME ENGINE] Before update - player playTime:', this.systems.player.stats.playTime); - this.systems.player.updatePlayTime(fixedDelta); - console.log('[GAME ENGINE] After update - player playTime:', this.systems.player.stats.playTime); - } - - // Update all systems with fixed delta - for (const [name, system] of Object.entries(this.systems)) { - if (system && typeof system.update === 'function') { - try { - system.update(fixedDelta); - } catch (error) { - console.error(`[GAME ENGINE] Error updating ${name} system:`, error); - if (debugLogger) debugLogger.errorEvent(error, `Update ${name} system`); - } - } - } - - // Update UI displays (money, gems, energy) after system updates - // Only update UI if in multiplayer mode or if game is actively running - const shouldUpdateUI = this.systems && this.systems.ui && - (window.smartSaveManager?.isMultiplayer || this.isRunning); - - if (shouldUpdateUI) { - console.log('[GAME ENGINE] Updating UI displays'); - try { - this.systems.ui.updateUI(); - console.log('[GAME ENGINE] UI update completed'); - } catch (error) { - console.error('[GAME ENGINE] Error updating UI:', error); - } - } - - // Only emit UI update event if in multiplayer mode or game is actively running - const shouldEmitUIUpdate = window.smartSaveManager?.isMultiplayer || this.isRunning; - - if (shouldEmitUIUpdate) { - this.emitUIUpdateEvent('full'); - } - - // Emit game updated event - this.emit('gameUpdated', { gameTime: this.gameTime }); - } - - update(deltaTime) { - // Legacy method - game logic now handled by updateGameLogic() - // This method kept for compatibility but doesn't process game systems - } - - shouldUpdateGUI() { - // Use centralized UI update control from UIManager - if (this.systems && this.systems.ui && this.systems.ui.shouldUpdateUI) { - const currentTime = Date.now(); - if (currentTime - this.lastGUIUpdate >= this.guiUpdateInterval) { - this.lastGUIUpdate = currentTime; - return true; - } - } - return false; - } - - updateGUI() { - const debugLogger = window.debugLogger; - - console.log('[GAME ENGINE] Updating GUI'); - - // Update UI displays (money, gems, energy) after system updates - // Only update UI if in multiplayer mode or if game is actively running - const shouldUpdateUI = this.systems && this.systems.ui && - (window.smartSaveManager?.isMultiplayer || this.isRunning); - - if (shouldUpdateUI) { - console.log('[GAME ENGINE] Updating UI displays'); - try { - this.systems.ui.updateUI(); - console.log('[GAME ENGINE] UI update completed'); - } catch (error) { - console.error('[GAME ENGINE] Error updating UI:', error); - } - } else { - // Skip UI updates when not in multiplayer and game is not actively running - if (this.systems && this.systems.ui) { - console.log('[GAME ENGINE] Skipping GUI updates - not in multiplayer mode'); - } - } - } - - // Event system - on(event, callback) { - if (!this.eventListeners.has(event)) { - this.eventListeners.set(event, []); - } - this.eventListeners.get(event).push(callback); - } - - emit(event, data) { - if (this.eventListeners.has(event)) { - this.eventListeners.get(event).forEach(callback => { - try { - callback(data); - } catch (error) { - console.error(`[GAME ENGINE] Error in event listener for ${event}:`, error); - } - }); - } - - // Also dispatch as DOM event for UIManager - this.dispatchEvent(new CustomEvent(event, { detail: data })); - } - - emitUIUpdateEvent(type, data = {}) { - const eventData = { - type: type, - timestamp: Date.now(), - ...data - }; - - console.log('[GAME ENGINE] Emitting UI update event:', eventData); - this.emit('uiUpdate', eventData); - } - - // Notification system - async showNotification(message, type = 'info', duration = 3000) { - const logger = window.logger; - if (logger) await logger.playerAction('Notification', { message, type, duration }); - - const notification = { - id: Date.now(), - message, - type, - duration, - timestamp: Date.now() - }; - - this.state.notifications.push(notification); - - // Auto-remove notification after duration - setTimeout(() => { - this.removeNotification(notification.id); - }, duration); - - // Update UI - if (this.systems.ui) { - // UI updates handled by individual systems - } - } - - removeNotification(id) { - this.state.notifications = this.state.notifications.filter(notification => notification.id !== id); - } - - processNotifications() { - const now = Date.now(); - this.state.notifications = this.state.notifications.filter( - notification => now - notification.timestamp < notification.duration - ); - } - - // Save/Load system - async save() { - const logger = window.logger; - const debugLogger = window.debugLogger; - - console.log('[GAME ENGINE] Save method called'); - if (logger) await logger.info('Saving game'); - // if (debugLogger) await debugLogger.startStep('saveGame'); - - try { - // if (debugLogger) await debugLogger.logStep('Collecting save data from systems'); - console.log('[GAME ENGINE] Collecting save data from systems...'); - - const saveData = { - player: this.systems.player.save(), - inventory: this.systems.inventory.save(), - economy: this.systems.economy.save(), - idleSystem: this.systems.idleSystem.save(), - dungeonSystem: this.systems.dungeonSystem.save(), - skillSystem: this.systems.skillSystem.save(), - baseSystem: this.systems.baseSystem.save(), - questSystem: this.systems.questSystem.save(), - gameTime: this.gameTime, - lastSave: Date.now() - }; - - // if (debugLogger) await debugLogger.logStep('Save data collected', { - // playerLevel: saveData.player?.stats?.level, - // gameTime: this.gameTime, - // saveDataSize: JSON.stringify(saveData).length, - // }); - - // console.log('[GAME ENGINE] Save data collected, player level:', saveData.player?.stats?.level); - // console.log('[GAME ENGINE] Save slot info:', this.saveSlotInfo);} - - // Check if we should save to server (multiplayer mode) - if (window.smartSaveManager && window.smartSaveManager.isMultiplayer) { - console.log('[GAME ENGINE] Saving to server in multiplayer mode'); - if (debugLogger) await debugLogger.logStep('Saving to server in multiplayer mode'); - - try { - const serverSaveResult = await window.smartSaveManager.savePlayerData(saveData); - if (serverSaveResult) { - console.log('[GAME ENGINE] Game saved successfully to server'); - if (debugLogger) await debugLogger.logStep('Game saved successfully to server'); - } else { - console.warn('[GAME ENGINE] Server save failed, falling back to local storage'); - if (debugLogger) await debugLogger.warn('Server save failed, using local fallback'); - - // Fallback to localStorage - const saveKey = `gso_save_slot_${this.saveSlotInfo?.slot || 1}`; - localStorage.setItem(saveKey, JSON.stringify(saveData)); - } - } catch (error) { - console.error('[GAME ENGINE] Error saving to server:', error); - if (debugLogger) await debugLogger.errorEvent(error, 'Server Save'); - - // Fallback to localStorage - const saveKey = `gso_save_slot_${this.saveSlotInfo?.slot || 1}`; - localStorage.setItem(saveKey, JSON.stringify(saveData)); - console.log('[GAME ENGINE] Error fallback: Game saved to localStorage'); - } - } else { - // Local save logic (existing code) - const isLocalMode = window.liveMainMenu && window.liveMainMenu.isLocalMode; - - if (isLocalMode) { - console.log('[GAME ENGINE] Using localStorage for local mode save'); - if (debugLogger) await debugLogger.logStep('Saving via localStorage for local mode'); - - try { - const saveKey = `gso_save_slot_${this.saveSlotInfo.slot}`; - const saveString = JSON.stringify(saveData); - - // Save to localStorage - localStorage.setItem(saveKey, saveString); - - console.log('[GAME ENGINE] Game saved successfully to localStorage'); - console.log('[GAME ENGINE] Save key:', saveKey); - console.log('[GAME ENGINE] Save data size:', saveString.length, 'characters'); - console.log('[GAME ENGINE] Current localStorage keys:', Object.keys(localStorage)); - - // Verify save exists - const verifySave = localStorage.getItem(saveKey); - console.log('[GAME ENGINE] Save verification:', !!verifySave ? 'SUCCESS' : 'FAILED'); - - if (debugLogger) await debugLogger.logStep('Game saved successfully to localStorage', { - saveKey, - saveDataSize: saveString.length - }); - - // Emit save event - this.emit('gameSaved', { slot: this.saveSlotInfo.slot, saveData }); - // if (debugLogger) debugLogger.logStep('Game saved event emitted'); - - } catch (error) { - console.error('[GAME ENGINE] Failed to save to localStorage:', error); - if (debugLogger) await debugLogger.errorEvent(error, 'LocalStorage Save'); - throw error; - } - } else { - // Use file system if available, otherwise localStorage (original logic) - if (this.saveSlotInfo && this.saveSlotInfo.useFileSystem) { - if (window.electronAPI) { - // console.log('[GAME ENGINE] Using Electron API to save game'); - // if (debugLogger) await debugLogger.logStep('Saving via Electron API', { - // slot: this.saveSlotInfo.slot, - // useFileSystem: true - // }); - - try { - // Save through Electron API - const result = await window.electronAPI.saveGame(this.saveSlotInfo.slot, saveData); - if (result.success) { - // console.log('[GAME ENGINE] Game saved successfully via Electron API'); - if (debugLogger) await debugLogger.logStep('Game saved successfully via Electron API', { - slot: this.saveSlotInfo.slot, - result: result - }); - - // Emit save event - this.emit('gameSaved', { slot: this.saveSlotInfo.slot, saveData }); - // if (debugLogger) debugLogger.logStep('Game saved event emitted'); - - } else { - console.error('[GAME ENGINE] Failed to save via Electron API:', result.error); - if (debugLogger) await debugLogger.errorEvent(new Error(result.error), 'Electron API Save'); - - // Fallback to localStorage - if (debugLogger) await debugLogger.logStep('Falling back to localStorage'); - const saveKey = `gso_save_slot_${this.saveSlotInfo.slot}`; - localStorage.setItem(saveKey, JSON.stringify(saveData)); - console.log('[GAME ENGINE] Fallback: Game saved to localStorage with key:', saveKey); - if (debugLogger) await debugLogger.logStep('Game saved to localStorage fallback', { - saveKey, - dataSize: JSON.stringify(saveData).length - }); - } - } catch (error) { - console.error('[GAME ENGINE] Electron API save error:', error); - if (debugLogger) await debugLogger.errorEvent(error, 'Electron API Save'); - - // Fallback to localStorage - if (debugLogger) await debugLogger.logStep('Falling back to localStorage due to error'); - const saveKey = `gso_save_slot_${this.saveSlotInfo.slot}`; - localStorage.setItem(saveKey, JSON.stringify(saveData)); - console.log('[GAME ENGINE] Error fallback: Game saved to localStorage with key:', saveKey); - } - } else { - console.warn('[GAME ENGINE] Electron API not available, using localStorage'); - if (debugLogger) await debugLogger.warn('Electron API not available, using localStorage'); - - const saveKey = `gso_save_slot_${this.saveSlotInfo.slot}`; - localStorage.setItem(saveKey, JSON.stringify(saveData)); - console.log('[GAME ENGINE] Game saved to localStorage with key:', saveKey); - if (debugLogger) await debugLogger.logStep('Game saved to localStorage', { - saveKey, - dataSize: JSON.stringify(saveData).length - }); - } - } else { - // LocalStorage fallback - if (debugLogger) await debugLogger.logStep('Using localStorage save'); - const saveKey = `gso_save_slot_${this.saveSlotInfo.slot}`; - localStorage.setItem(saveKey, JSON.stringify(saveData)); - console.log('[GAME ENGINE] Game saved to localStorage with key:', saveKey); - if (debugLogger) await debugLogger.logStep('Game saved to localStorage', { - saveKey, - dataSize: JSON.stringify(saveData).length - }); - } - } - - if (logger) await logger.info('Game saved successfully'); - - // Check if we should also save to server (multiplayer mode) - if (window.smartSaveManager && window.smartSaveManager.isMultiplayer) { - console.log('[GAME ENGINE] Also saving to server in multiplayer mode'); - try { - await window.smartSaveManager.savePlayerData(saveData); - console.log('[GAME ENGINE] Game saved successfully to server'); - } catch (error) { - console.error('[GAME ENGINE] Error saving to server:', error); - // Don't throw error - local save was successful - } - } - - // if (debugLogger) await debugLogger.endStep('saveGame', { - // gameTime: this.gameTime, - // saveSlot: this.saveSlotInfo?.slot, - // success: true - // }); - - } - } catch (error) { - console.error('[GAME ENGINE] Save failed:', error); - if (logger) await logger.errorEvent(error, 'Game Save'); - if (debugLogger) await debugLogger.errorEvent(error, 'Game Save'); - throw error; - } - } - - async loadPlayerData(playerData) { - console.log('[GAME ENGINE] Loading server player data into game'); - - try { - // Apply player stats - if (playerData.stats && this.systems && this.systems.player) { - console.log('[GAME ENGINE] Applying player stats:', playerData.stats); - this.systems.player.load(playerData.stats); - } - - // Apply inventory - if (playerData.inventory && this.systems && this.systems.inventory) { - console.log('[GAME ENGINE] Applying player inventory:', playerData.inventory); - this.systems.inventory.load(playerData.inventory); - } - - // Apply ship data - if (playerData.ship && this.systems && this.systems.ship) { - console.log('[GAME ENGINE] Applying player ship:', playerData.ship); - this.systems.ship.load(playerData.ship); - } - - // Apply base data - if (playerData.base && this.systems && this.systems.base) { - console.log('[GAME ENGINE] Applying player base:', playerData.base); - this.systems.base.load(playerData.base); - } - - // Apply quest data - if (playerData.quests && this.systems && this.systems.quests) { - console.log('[GAME ENGINE] Applying player quests:', playerData.quests); - this.systems.quests.load(playerData.quests); - } - - // Apply skill data - if (playerData.skills && this.systems && this.systems.skills) { - console.log('[GAME ENGINE] Applying player skills:', playerData.skills); - this.systems.skills.load(playerData.skills); - } - - // Apply idle system data - if (playerData.idleSystem && this.systems && this.systems.idleSystem) { - console.log('[GAME ENGINE] Applying player idle system:', playerData.idleSystem); - this.systems.idleSystem.load(playerData.idleSystem); - } - - // Apply dungeon system data - if (playerData.dungeonSystem && this.systems && this.systems.dungeonSystem) { - console.log('[GAME ENGINE] Applying player dungeon system:', playerData.dungeonSystem); - this.systems.dungeonSystem.load(playerData.dungeonSystem); - } - - // Apply crafting system data - if (playerData.craftingSystem && this.systems && this.systems.craftingSystem) { - console.log('[GAME ENGINE] Applying player crafting system:', playerData.craftingSystem); - this.systems.craftingSystem.load(playerData.craftingSystem); - } - - // Apply game time - if (playerData.gameTime !== undefined) { - this.gameTime = playerData.gameTime; - console.log('[GAME ENGINE] Game time restored:', this.gameTime); - } - - console.log('[GAME ENGINE] Server player data applied successfully'); - - // Show notification to user - if (this.showNotification) { - this.showNotification(`Welcome back! Level ${playerData.stats?.level || 1}`, 'success', 3000); - } - - } catch (error) { - console.error('[GAME ENGINE] Error loading server player data:', error); - if (this.showNotification) { - this.showNotification('Failed to load server data!', 'error', 3000); - } - } - } - - async load() { - const logger = window.logger; - const debugLogger = window.debugLogger; - - console.log('[GAME ENGINE] Load method called'); - if (logger) await logger.info('Loading game'); - if (debugLogger) await debugLogger.startStep('loadGame'); - - try { - if (debugLogger) await debugLogger.logStep('Loading save data', { - saveSlot: this.saveSlotInfo?.slot, - useFileSystem: this.saveSlotInfo?.useFileSystem - }); - - let saveData; - - // Use file system if available, otherwise localStorage - if (this.saveSlotInfo && this.saveSlotInfo.useFileSystem && window.electronAPI) { - console.log('[GAME ENGINE] Loading via Electron API'); - if (debugLogger) await debugLogger.logStep('Loading via Electron API'); - - const result = await window.electronAPI.loadGame(this.saveSlotInfo.slot); - if (result.success) { - saveData = result.data; - console.log('[GAME ENGINE] Game loaded successfully via Electron API'); - if (debugLogger) await debugLogger.logStep('Game loaded via Electron API', { - slot: this.saveSlotInfo.slot, - dataSize: JSON.stringify(saveData).length - }); - } else { - console.error('[GAME ENGINE] Failed to load via Electron API:', result.error); - if (debugLogger) await debugLogger.errorEvent(new Error(result.error), 'Electron API Load'); - throw new Error(result.error); - } - } else { - // LocalStorage fallback - console.log('[GAME ENGINE] Loading from localStorage'); - if (debugLogger) await debugLogger.logStep('Loading from localStorage'); - - const saveKey = `gso_save_slot_${this.saveSlotInfo.slot}`; - const saveString = localStorage.getItem(saveKey); - - if (saveString) { - try { - saveData = JSON.parse(saveString); - console.log('[GAME ENGINE] Game loaded from localStorage'); - if (debugLogger) await debugLogger.logStep('Game loaded from localStorage', { - saveKey, - dataSize: saveString.length - }); - } catch (parseError) { - console.error('[GAME ENGINE] Failed to parse save data:', parseError); - if (debugLogger) await debugLogger.errorEvent(parseError, 'Parse Save Data'); - throw new Error('Corrupted save data'); - } - } else { - console.warn('[GAME ENGINE] No save data found in localStorage'); - if (debugLogger) await debugLogger.warn('No save data found in localStorage', { saveKey }); - throw new Error('No save data found'); - } - } - - // if (debugLogger) await debugLogger.logStep('Applying save data to systems'); - - // Apply save data to systems - if (saveData.player && this.systems.player) { - console.log('[GAME ENGINE] Loading player data...'); - this.systems.player.load(saveData.player); - if (debugLogger) await debugLogger.logStep('Player data loaded', { - level: saveData.player?.stats?.level, - experience: saveData.player?.stats?.experience - }); - } - - if (saveData.inventory && this.systems.inventory) { - console.log('[GAME ENGINE] Loading inventory data...'); - this.systems.inventory.load(saveData.inventory); - if (debugLogger) await debugLogger.logStep('Inventory data loaded', { - itemCount: saveData.inventory.items?.length || 0 - }); - } - - if (saveData.economy && this.systems.economy) { - console.log('[GAME ENGINE] Loading economy data...'); - this.systems.economy.load(saveData.economy); - if (debugLogger) await debugLogger.logStep('Economy data loaded', { - credits: saveData.economy.credits, - gems: saveData.economy.gems - }); - } - - if (saveData.gameTime) { - this.gameTime = saveData.gameTime; - if (debugLogger) await debugLogger.logStep('Game time restored', { - gameTime: this.gameTime - }); - } - - // Emit load event - this.emit('gameLoaded', { saveData }); - if (debugLogger) debugLogger.logStep('Game loaded event emitted'); - - if (logger) await logger.info('Game loaded successfully'); - if (debugLogger) await debugLogger.endStep('loadGame', { - gameTime: this.gameTime, - saveSlot: this.saveSlotInfo?.slot, - success: true - }); - - } catch (error) { - console.error('[GAME ENGINE] Load failed:', error); - if (logger) await logger.errorEvent(error, 'Game Load'); - if (debugLogger) await debugLogger.errorEvent(error, 'Game Load'); - throw error; - } - } - - async newGame() { - const logger = window.logger; - const debugLogger = window.debugLogger; - - console.log('[GAME ENGINE] Starting new game initialization'); - if (logger) await logger.info('Starting new game'); - if (debugLogger) await debugLogger.startStep('newGame'); - - try { - // For new games, we need to properly initialize systems with default data - if (debugLogger) await debugLogger.logStep('Initializing systems for new game'); - - // Initialize inventory with starting items - if (this.systems.inventory) { - console.log('[GAME ENGINE] Initializing inventory with starting items'); - if (debugLogger) await debugLogger.logStep('Initializing inventory with starting items'); - await this.systems.inventory.initialize(); - } - - // Initialize DungeonSystem for new game - if (this.systems.dungeonSystem) { - console.log('[GAME ENGINE] Initializing DungeonSystem for new game...'); - if (debugLogger) await debugLogger.logStep('Initializing DungeonSystem for new game'); - try { - await this.systems.dungeonSystem.initialize(); - console.log('[GAME ENGINE] DungeonSystem initialized successfully for new game'); - if (debugLogger) await debugLogger.logStep('DungeonSystem initialized successfully for new game'); - } catch (dungeonError) { - console.error('[GAME ENGINE] DungeonSystem initialization failed for new game:', dungeonError); - if (debugLogger) await debugLogger.errorEvent(dungeonError, 'DungeonSystem initialization failed for new game'); - } - } else { - console.log('[GAME ENGINE] DungeonSystem not found in systems!'); - } - - if (this.systems.player) { - console.log('[GAME ENGINE] Resetting player to initial state'); - if (debugLogger) await debugLogger.logStep('Resetting player to initial state'); - this.systems.player.resetToLevel1(); - console.log('[GAME ENGINE] Player reset completed'); - if (debugLogger) await debugLogger.logStep('Player reset completed', { - level: this.systems.player.stats.level, - experience: this.systems.player.stats.experience, - playTime: this.systems.player.stats.playTime - }); - - console.log('[GAME ENGINE] Setting up new player'); - if (debugLogger) await debugLogger.logStep('Setting up new player'); - this.systems.player.setupNewPlayer(); - console.log('[GAME ENGINE] Player setup completed'); - if (debugLogger) await debugLogger.logStep('Player setup completed', { - level: this.systems.player.stats.level, - experience: this.systems.player.stats.experience - }); - } else { - console.error('[GAME ENGINE] Player system not available'); - if (debugLogger) await debugLogger.error('Player system not available'); - } - - if (debugLogger) await debugLogger.logStep('Resetting economy system'); - console.log('[GAME ENGINE] Step 2: Resetting economy system'); - - if (this.systems.economy) { - console.log('[GAME ENGINE] Calling economy.reset()'); - if (debugLogger) await debugLogger.logStep('Calling economy.reset()'); - this.systems.economy.reset(); - console.log('[GAME ENGINE] Economy reset completed'); - if (debugLogger) await debugLogger.logStep('Economy reset completed', { - credits: this.systems.economy.credits, - gems: this.systems.economy.gems - }); - } else { - console.error('[GAME ENGINE] Economy system not available'); - if (debugLogger) await debugLogger.error('Economy system not available'); - } - - // Skip inventory reset - initialize() already handles proper setup - if (debugLogger) await debugLogger.logStep('Skipping inventory reset - already initialized'); - console.log('[GAME ENGINE] Skipping inventory reset - already initialized with starting items'); - - if (this.systems.inventory) { - if (debugLogger) await debugLogger.logStep('Inventory already initialized', { - itemCount: this.systems.inventory.items.length - }); - } else { - console.error('[GAME ENGINE] Inventory system not available'); - if (debugLogger) await debugLogger.error('Inventory system not available'); - } - - if (debugLogger) await debugLogger.logStep('Resetting quest system'); - console.log('[GAME ENGINE] Step 4: Resetting quest system'); - - if (this.systems.questSystem) { - console.log('[GAME ENGINE] Calling questSystem.reset()'); - if (debugLogger) await debugLogger.logStep('Calling questSystem.reset()'); - this.systems.questSystem.reset(); - console.log('[GAME ENGINE] Quest system reset completed'); - if (debugLogger) await debugLogger.logStep('Quest system reset completed'); - - // Activate the tutorial quest for new games - console.log('[GAME ENGINE] Activating tutorial quest for new game'); - if (debugLogger) await debugLogger.logStep('Activating tutorial quest'); - this.systems.questSystem.startQuest('tutorial_complete'); - console.log('[GAME ENGINE] Tutorial quest activated'); - if (debugLogger) await debugLogger.logStep('Tutorial quest activated'); - } else { - console.error('[GAME ENGINE] Quest system not available'); - if (debugLogger) await debugLogger.error('Quest system not available'); - } - - if (debugLogger) await debugLogger.logStep('Resetting game time'); - console.log('[GAME ENGINE] Step 5: Resetting game time'); - this.gameTime = 0; - console.log('[GAME ENGINE] Game time reset to 0'); - if (debugLogger) await debugLogger.logStep('Game time reset', { gameTime: this.gameTime }); - - if (logger) await logger.info('New game initialized successfully'); - if (debugLogger) await debugLogger.endStep('newGame', { - gameTime: this.gameTime, - playerLevel: this.systems.player?.stats.level, - success: true - }); - - } catch (error) { - console.error('[GAME ENGINE] Failed to initialize new game:', error); - if (logger) await logger.errorEvent(error, 'New Game Initialization'); - if (debugLogger) await debugLogger.errorEvent(error, 'New Game Initialization'); - throw error; - } - } - - // Utility methods - formatNumber(num) { - if (num >= 1e9) return (num / 1e9).toFixed(2) + 'B'; - if (num >= 1e6) return (num / 1e6).toFixed(2) + 'M'; - if (num >= 1e3) return (num / 1e3).toFixed(2) + 'K'; - return num.toString(); - } - - formatTime(seconds) { - const hours = Math.floor(seconds / 3600); - const minutes = Math.floor((seconds % 3600) / 60); - const secs = Math.floor(seconds % 60); - - if (hours > 0) { - return `${hours}h ${minutes}m ${secs}s`; - } else if (minutes > 0) { - return `${minutes}m ${secs}s`; - } else { - return `${secs}s`; - } - } - - toggleDebugConsole() { - const debugLogger = window.debugLogger; - // if (debugLogger) debugLogger.logStep('Toggle debug console requested'); - - // Implementation would go here - // console.log('[GAME ENGINE] Debug console toggle requested'); - } - - getPerformanceStats() { - const debugLogger = window.debugLogger; - - const stats = { - gameTime: this.gameTime, - isRunning: this.isRunning, - lastUpdate: this.lastUpdate, - memory: null - }; - - // Add memory info if available - if (window.performance && window.performance.memory) { - stats.memory = { - used: window.performance.memory.usedJSHeapSize, - total: window.performance.memory.totalJSHeapSize, - limit: window.performance.memory.jsHeapSizeLimit - }; - } - - // if (debugLogger) debugLogger.logStep('Performance stats requested', stats); - - return stats; - } - - // Simple loadPlayerData method for server data integration - async loadPlayerData(playerData) { - console.log('[GAME ENGINE] Loading server player data'); - - try { - // Apply basic player stats - if (playerData.stats && this.systems && this.systems.player) { - this.systems.player.load(playerData.stats); - console.log('[GAME ENGINE] Applied player stats:', playerData.stats); - } - - // Apply inventory - if (playerData.inventory && this.systems && this.systems.inventory) { - this.systems.inventory.load(playerData.inventory); - console.log('[GAME ENGINE] Applied inventory'); - } - - // Apply ship data - if (playerData.ship && this.systems && this.systems.ship) { - this.systems.ship.load(playerData.ship); - console.log('[GAME ENGINE] Applied ship data'); - } - - // Apply base data - if (playerData.base && this.systems && this.systems.base) { - this.systems.base.load(playerData.base); - console.log('[GAME ENGINE] Applied base data'); - } - - // Show notification - if (this.showNotification) { - this.showNotification(`Welcome back! Level ${playerData.stats?.level || 1}`, 'success', 3000); - } - - console.log('[GAME ENGINE] Server player data loaded successfully'); - - } catch (error) { - console.error('[GAME ENGINE] Error loading server player data:', error); - if (this.showNotification) { - this.showNotification('Failed to load server data!', 'error', 3000); - } - } - } -} - -// Global game instance -let game = null; - -// Export GameEngine to global scope -if (typeof window !== 'undefined') { - window.GameEngine = GameEngine; -} diff --git a/Client-Server/js/data/GameData.js b/Client-Server/js/data/GameData.js deleted file mode 100644 index 9ce79d7..0000000 --- a/Client-Server/js/data/GameData.js +++ /dev/null @@ -1,570 +0,0 @@ -/** - * Galaxy Strike Online - Game Data - * Static game data, constants, and configuration - */ - -// Game configuration -const GAME_CONFIG = { - version: '1.0.0', - name: 'Galaxy Strike Online', - maxLevel: 100, - saveInterval: 30000, // 30 seconds - notificationDuration: 3000, - maxNotifications: 5 -}; - -// Player defaults -const PLAYER_DEFAULTS = { - level: 1, - experience: 0, - skillPoints: 0, - credits: 1000, - gems: 10, - health: 100, - maxHealth: 100, - energy: 100, - maxEnergy: 100, - attack: 10, - defense: 5, - speed: 10, - criticalChance: 0.05, - criticalDamage: 1.5 -}; - -// Experience requirements -const EXPERIENCE_TABLE = []; -for (let i = 1; i <= 100; i++) { - EXPERIENCE_TABLE[i] = Math.floor(100 * Math.pow(1.5, i - 1)); -} - -// Item rarities with colors and multipliers -const ITEM_RARITIES = { - common: { - name: 'Common', - color: '#888888', - multiplier: 1.0, - dropChance: 0.60 - }, - uncommon: { - name: 'Uncommon', - color: '#00ff00', - multiplier: 1.2, - dropChance: 0.25 - }, - rare: { - name: 'Rare', - color: '#0088ff', - multiplier: 1.5, - dropChance: 0.10 - }, - epic: { - name: 'Epic', - color: '#8833ff', - multiplier: 2.0, - dropChance: 0.04 - }, - legendary: { - name: 'Legendary', - color: '#ff8800', - multiplier: 3.0, - dropChance: 0.01 - } -}; - -// Enemy types and stats -const ENEMY_TEMPLATES = { - space_pirate: { - name: 'Space Pirate', - health: 25, - attack: 10, - defense: 3, - speed: 8, - experience: 15, - credits: 12, - rarity: 'common' - }, - alien_guardian: { - name: 'Alien Guardian', - health: 50, - attack: 8, - defense: 5, - speed: 6, - experience: 25, - credits: 15, - rarity: 'common' - }, - mining_drone: { - name: 'Mining Drone', - health: 20, - attack: 8, - defense: 3, - speed: 5, - experience: 12, - credits: 8, - rarity: 'common' - }, - security_drone: { - name: 'Security Drone', - health: 35, - attack: 14, - defense: 4, - speed: 10, - experience: 22, - credits: 15, - rarity: 'uncommon' - }, - pirate_captain: { - name: 'Pirate Captain', - health: 40, - attack: 15, - defense: 6, - speed: 12, - experience: 30, - credits: 20, - rarity: 'uncommon' - }, - crystal_golem: { - name: 'Crystal Golem', - health: 80, - attack: 6, - defense: 10, - speed: 4, - experience: 35, - credits: 25, - rarity: 'rare' - }, - corrupted_ai: { - name: 'Corrupted AI', - health: 60, - attack: 20, - defense: 2, - speed: 15, - experience: 40, - credits: 30, - rarity: 'rare' - }, - energy_being: { - name: 'Energy Being', - health: 55, - attack: 22, - defense: 3, - speed: 18, - experience: 45, - credits: 35, - rarity: 'epic' - }, - quantum_entity: { - name: 'Quantum Entity', - health: 70, - attack: 35, - defense: 5, - speed: 20, - experience: 60, - credits: 50, - rarity: 'legendary' - } -}; - -// Dungeon configurations -const DUNGEON_CONFIGS = { - alien_ruins: { - name: 'Alien Ruins', - description: 'Ancient alien structures filled with mysterious technology', - difficulty: 'medium', - minLevel: 3, - roomCount: [5, 8], - enemyTypes: ['alien_guardian', 'ancient_drone', 'crystal_golem'], - rewardMultiplier: 1.2, - energyCost: 20 - }, - pirate_lair: { - name: 'Pirate Lair', - description: 'Dangerous pirate hideouts with valuable loot', - difficulty: 'easy', - minLevel: 1, - roomCount: [4, 6], - enemyTypes: ['space_pirate', 'pirate_captain', 'defense_turret'], - rewardMultiplier: 1.0, - energyCost: 15 - }, - corrupted_vault: { - name: 'Corrupted AI Vault', - description: 'Malfunctioning AI facilities with corrupted security', - difficulty: 'hard', - minLevel: 5, - roomCount: [6, 9], - enemyTypes: ['security_drone', 'corrupted_ai', 'virus_program'], - rewardMultiplier: 1.5, - energyCost: 25 - }, - asteroid_mine: { - name: 'Asteroid Mine', - description: 'Abandoned mining facilities in asteroid fields', - difficulty: 'easy', - minLevel: 2, - roomCount: [4, 7], - enemyTypes: ['mining_drone', 'rock_creature', 'explosive_asteroid'], - rewardMultiplier: 0.8, - energyCost: 10 - }, - nebula_anomaly: { - name: 'Nebula Anomaly', - description: 'Strange energy anomalies in deep space', - difficulty: 'extreme', - minLevel: 8, - roomCount: [7, 10], - enemyTypes: ['energy_being', 'phase_shifter', 'quantum_entity'], - rewardMultiplier: 2.0, - energyCost: 30 - } -}; - -// Skill definitions -const SKILL_DEFINITIONS = { - combat: { - weapons_mastery: { - name: 'Weapons Mastery', - description: 'Increases weapon damage and critical chance', - maxLevel: 10, - experiencePerLevel: 100, - effects: { - attack: 2, - criticalChance: 0.01 - }, - icon: 'fa-sword' - }, - shield_techniques: { - name: 'Shield Techniques', - description: 'Improves defense and energy efficiency', - maxLevel: 10, - experiencePerLevel: 100, - effects: { - defense: 2, - maxEnergy: 5 - }, - icon: 'fa-shield-alt' - }, - piloting: { - name: 'Piloting', - description: 'Enhances speed and evasion', - maxLevel: 10, - experiencePerLevel: 100, - effects: { - speed: 2, - criticalChance: 0.005 - }, - icon: 'fa-rocket' - } - }, - science: { - energy_manipulation: { - name: 'Energy Manipulation', - description: 'Better energy control and regeneration', - maxLevel: 10, - experiencePerLevel: 100, - effects: { - maxEnergy: 10, - energyRegeneration: 0.1 - }, - icon: 'fa-bolt' - }, - alien_technology: { - name: 'Alien Technology', - description: 'Understanding and using alien artifacts', - maxLevel: 10, - experiencePerLevel: 150, - effects: { - findRarity: 0.05, - itemValue: 0.1 - }, - icon: 'fa-atom' - } - }, - crafting: { - weapon_crafting: { - name: 'Weapon Crafting', - description: 'Create and upgrade weapons', - maxLevel: 10, - experiencePerLevel: 100, - effects: { - craftingBonus: 0.1, - weaponStats: 0.05 - }, - icon: 'fa-hammer' - }, - armor_forging: { - name: 'Armor Forging', - description: 'Forge protective armor and shields', - maxLevel: 10, - experiencePerLevel: 100, - effects: { - craftingBonus: 0.1, - armorStats: 0.05 - }, - icon: 'fa-anvil' - } - } -}; - -// Shop items -const SHOP_ITEMS = { - ships: [ - { - id: 'fighter_mk1', - name: 'Fighter Mk. I', - type: 'ship', - rarity: 'common', - price: 5000, - currency: 'credits', - description: 'Fast and agile fighter ship', - stats: { attack: 15, speed: 20, defense: 8 } - }, - { - id: 'cruiser_mk1', - name: 'Cruiser Mk. I', - type: 'ship', - rarity: 'uncommon', - price: 15000, - currency: 'credits', - description: 'Well-balanced cruiser for combat', - stats: { attack: 20, speed: 10, defense: 15 } - } - ], - upgrades: [ - { - id: 'weapon_upgrade_1', - name: 'Weapon Upgrade I', - type: 'upgrade', - rarity: 'common', - price: 500, - currency: 'credits', - description: 'Increases weapon damage by 10%', - effect: { attackMultiplier: 1.1 } - }, - { - id: 'shield_upgrade_1', - name: 'Shield Upgrade I', - type: 'upgrade', - rarity: 'common', - price: 400, - currency: 'credits', - description: 'Increases defense by 5 points', - effect: { defense: 5 } - } - ], - 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: 'mega_health_kit', - name: 'Mega Health Kit', - type: 'consumable', - rarity: 'uncommon', - price: 50, - currency: 'credits', - description: 'Restores full health', - effect: { heal: 999 } - }, - { - id: 'energy_boost', - name: 'Energy Boost', - type: 'consumable', - rarity: 'common', - price: 25, - currency: 'credits', - description: 'Restores 50 energy', - effect: { energy: 50 } - } - ] -}; - -// Starter equipment for new players -const STARTER_EQUIPMENT = { - starter_blaster: { - id: 'starter_blaster', - name: 'Common Blaster', - type: 'weapon', - rarity: 'common', - description: 'A reliable basic blaster for new pilots', - stats: { attack: 5, criticalChance: 0.02 }, - equipable: true, - slot: 'weapon', - value: 100, - stackable: false - }, - basic_armor: { - id: 'basic_armor', - name: 'Basic Armor', - type: 'armor', - rarity: 'common', - description: 'Standard issue armor for basic protection', - stats: { defense: 3, health: 10 }, - equipable: true, - slot: 'armor', - value: 150, - stackable: false - } -}; - -// Achievement definitions -const ACHIEVEMENTS = { - first_victory: { - name: 'First Victory', - description: 'Win your first dungeon', - requirement: { dungeonsCompleted: 1 }, - reward: { gems: 10, experience: 100 }, - icon: 'fa-trophy' - }, - dungeon_master: { - name: 'Dungeon Master', - description: 'Complete 50 dungeons', - requirement: { dungeonsCompleted: 50 }, - reward: { gems: 100, experience: 1000 }, - icon: 'fa-dungeon' - }, - level_master: { - name: 'Level Master', - description: 'Reach level 50', - requirement: { level: 50 }, - reward: { gems: 200, experience: 5000 }, - icon: 'fa-level-up-alt' - }, - wealthy_commander: { - name: 'Wealthy Commander', - description: 'Accumulate 1,000,000 credits', - requirement: { credits: 1000000 }, - reward: { gems: 150, experience: 2000 }, - icon: 'fa-coins' - }, - skill_expert: { - name: 'Skill Expert', - description: 'Max out any skill', - requirement: { maxSkillLevel: 10 }, - reward: { gems: 75, experience: 1500 }, - icon: 'fa-graduation-cap' - } -}; - -// Game messages and notifications -const GAME_MESSAGES = { - welcome: 'Welcome to Galaxy Strike Online, Commander!', - levelUp: 'Level Up! You are now level {level}!', - questCompleted: 'Quest completed: {questName}!', - dungeonCompleted: 'Dungeon completed! Time: {time}', - achievementUnlocked: 'Achievement Unlocked: {achievementName}!', - insufficientCredits: 'Not enough credits!', - insufficientGems: 'Not enough gems!', - insufficientEnergy: 'Not enough energy!', - inventoryFull: 'Inventory is full!', - skillPointsAvailable: 'You have {points} skill points available!', - dailyReward: 'Daily reward claimed! Day {day}', - offlineRewards: 'Welcome back! You were offline for {time}' -}; - -// Utility functions -const GameUtils = { - // Get random item from array - getRandomItem(array) { - return array[Math.floor(Math.random() * array.length)]; - }, - - // Get random integer between min and max (inclusive) - getRandomInt(min, max) { - return Math.floor(Math.random() * (max - min + 1)) + min; - }, - - // Get random float between min and max - getRandomFloat(min, max) { - return Math.random() * (max - min) + min; - }, - - // Check if chance succeeds - checkChance(chance) { - return Math.random() < chance; - }, - - // Format large numbers with suffixes - formatNumber(num) { - if (num >= 1000000) return (num / 1000000).toFixed(1) + 'M'; - if (num >= 1000) return (num / 1000).toFixed(1) + 'K'; - return Math.floor(num).toString(); - }, - - // Format time in milliseconds to readable string - formatTime(milliseconds) { - const seconds = Math.floor(milliseconds / 1000); - const minutes = Math.floor(seconds / 60); - const hours = Math.floor(minutes / 60); - const days = Math.floor(hours / 24); - - if (days > 0) return `${days}d ${hours % 24}h`; - if (hours > 0) return `${hours}h ${minutes % 60}m`; - if (minutes > 0) return `${minutes}m ${seconds % 60}s`; - return `${seconds}s`; - }, - - // Calculate experience needed for level - getExperienceForLevel(level) { - return EXPERIENCE_TABLE[level] || 0; - }, - - // Get item rarity by chance - getItemRarity() { - const roll = Math.random(); - let cumulative = 0; - - for (const [rarity, data] of Object.entries(ITEM_RARITIES)) { - cumulative += data.dropChance; - if (roll <= cumulative) { - return rarity; - } - } - - return 'common'; - }, - - // Deep clone object - deepClone(obj) { - return JSON.parse(JSON.stringify(obj)); - }, - - // Generate unique ID - generateId() { - return Date.now().toString() + Math.random().toString(36).substr(2, 9); - } -}; - -// Export for use in other modules -if (typeof module !== 'undefined' && module.exports) { - module.exports = { - GAME_CONFIG, - PLAYER_DEFAULTS, - EXPERIENCE_TABLE, - ITEM_RARITIES, - ENEMY_TEMPLATES, - DUNGEON_CONFIGS, - SKILL_DEFINITIONS, - SHOP_ITEMS, - ACHIEVEMENTS, - GAME_MESSAGES, - GameUtils - }; -} diff --git a/Client-Server/js/systems/CraftingSystem.js b/Client-Server/js/systems/CraftingSystem.js deleted file mode 100644 index a21618e..0000000 --- a/Client-Server/js/systems/CraftingSystem.js +++ /dev/null @@ -1,657 +0,0 @@ -/** - * Galaxy Strike Online - Crafting System - * Handles item crafting, recipes, and crafting skill progression - */ - -class CraftingSystem extends BaseSystem { - constructor(gameEngine) { - super(gameEngine); - - this.recipes = new Map(); - this.currentCategory = 'weapons'; - this.selectedRecipe = null; - - this.initializeRecipes(); - } - - initializeRecipes() { - // Weapon Recipes - this.addRecipe('basic_blaster', { - name: 'Basic Blaster', - category: 'weapons', - description: 'A simple energy blaster for beginners', - requirements: { - weapon_crafting: 1, - crafting: 1 - }, - materials: [ - { id: 'iron_ore', quantity: 5 }, - { id: 'energy_crystal', quantity: 2 } - ], - results: [ - { id: 'basic_blaster', quantity: 1 } - ], - experience: 10, - craftingTime: 3000 // 3 seconds - }); - - this.addRecipe('enhanced_blaster', { - name: 'Enhanced Blaster', - category: 'weapons', - description: 'An improved blaster with better damage output', - requirements: { - weapon_crafting: 3, - crafting: 5 - }, - materials: [ - { id: 'iron_ore', quantity: 10 }, - { id: 'energy_crystal', quantity: 5 }, - { id: 'copper_wire', quantity: 3 } - ], - results: [ - { id: 'enhanced_blaster', quantity: 1 } - ], - experience: 25, - craftingTime: 5000 // 5 seconds - }); - - this.addRecipe('laser_sniper_rifle', { - name: 'Laser Sniper Rifle', - category: 'weapons', - description: 'A long-range precision laser weapon', - requirements: { - weapon_crafting: 3, - crafting: 5 - }, - materials: [ - { id: 'advanced_circuitboard', quantity: 2 }, - { id: 'energy_crystal', quantity: 8 }, - { id: 'steel_plate', quantity: 5 }, - { id: 'copper_wire', quantity: 4 } - ], - results: [ - { id: 'laser_sniper_rifle', quantity: 1 } - ], - experience: 40, - craftingTime: 8000 // 8 seconds - }); - - this.addRecipe('plasma_cannon', { - name: 'Plasma Cannon', - category: 'weapons', - description: 'A devastating plasma-based weapon', - requirements: { - weapon_crafting: 5, - crafting: 7 - }, - materials: [ - { id: 'advanced_components', quantity: 3 }, - { id: 'energy_crystal', quantity: 12 }, - { id: 'steel_plate', quantity: 8 }, - { id: 'battery', quantity: 3 } - ], - results: [ - { id: 'plasma_cannon', quantity: 1 } - ], - experience: 60, - craftingTime: 12000 // 12 seconds - }); - - // Armor Recipes - this.addRecipe('basic_armor', { - name: 'Basic Armor', - category: 'armor', - description: 'Light armor providing basic protection', - requirements: { - armor_forging: 1, - crafting: 1 - }, - materials: [ - { id: 'iron_ore', quantity: 8 }, - { id: 'leather', quantity: 3 } - ], - results: [ - { id: 'basic_armor', quantity: 1 } - ], - experience: 15, - craftingTime: 4000 // 4 seconds - }); - - this.addRecipe('reinforced_armor', { - name: 'Reinforced Armor', - category: 'armor', - description: 'Heavy armor with enhanced protection', - requirements: { - armor_forging: 4, - crafting: 6 - }, - materials: [ - { id: 'iron_ore', quantity: 15 }, - { id: 'steel_plate', quantity: 5 }, - { id: 'leather', quantity: 5 } - ], - results: [ - { id: 'reinforced_armor', quantity: 1 } - ], - experience: 35, - craftingTime: 6000 // 6 seconds - }); - - // Item Recipes - this.addRecipe('health_kit', { - name: 'Health Kit', - category: 'items', - description: 'A medical kit that restores health', - requirements: { - crafting: 2 - }, - materials: [ - { id: 'herbs', quantity: 3 }, - { id: 'bandages', quantity: 2 } - ], - results: [ - { id: 'health_kit', quantity: 3 } - ], - experience: 5, - craftingTime: 2000 // 2 seconds - }); - - this.addRecipe('basic_circuit', { - name: 'Basic Circuit', - category: 'items', - description: 'Create a basic electronic circuit', - requirements: { - crafting: 3 - }, - materials: [ - { id: 'basic_circuitboard', quantity: 1 }, - { id: 'copper_wire', quantity: 2 } - ], - results: [ - { id: 'basic_circuit', quantity: 1 } - ], - experience: 8, - craftingTime: 2500 // 2.5 seconds - }); - - this.addRecipe('advanced_circuit', { - name: 'Advanced Circuit', - category: 'items', - description: 'Create an advanced electronic circuit', - requirements: { - crafting: 5 - }, - materials: [ - { id: 'advanced_circuitboard', quantity: 1 }, - { id: 'energy_crystal', quantity: 2 }, - { id: 'copper_wire', quantity: 3 } - ], - results: [ - { id: 'advanced_circuit', quantity: 1 } - ], - experience: 15, - craftingTime: 4000 // 4 seconds - }); - - this.addRecipe('electronic_device', { - name: 'Electronic Device', - category: 'items', - description: 'Create a complex electronic device', - requirements: { - crafting: 7 - }, - materials: [ - { id: 'advanced_components', quantity: 1 }, - { id: 'battery', quantity: 2 }, - { id: 'common_circuitboard', quantity: 1 } - ], - results: [ - { id: 'electronic_device', quantity: 1 } - ], - experience: 20, - craftingTime: 5000 // 5 seconds - }); - - // Ship Component Recipes - this.addRecipe('shield_generator', { - name: 'Shield Generator', - category: 'ships', - description: 'A basic shield generator for ship protection', - requirements: { - engineering: 2, - crafting: 4 - }, - materials: [ - { id: 'energy_crystal', quantity: 8 }, - { id: 'steel_plate', quantity: 5 }, - { id: 'copper_wire', quantity: 4 } - ], - results: [ - { id: 'shield_generator', quantity: 1 } - ], - experience: 30, - craftingTime: 8000 // 8 seconds - }); - - this.addRecipe('engine_upgrade', { - name: 'Engine Upgrade', - category: 'ships', - description: 'An upgrade that improves ship engine performance', - requirements: { - engineering: 5, - crafting: 7 - }, - materials: [ - { id: 'steel_plate', quantity: 10 }, - { id: 'energy_crystal', quantity: 6 }, - { id: 'rare_metal', quantity: 2 } - ], - results: [ - { id: 'engine_upgrade', quantity: 1 } - ], - experience: 50, - craftingTime: 10000 // 10 seconds - }); - - this.addRecipe('quantum_computer', { - name: 'Quantum Computer', - category: 'ships', - description: 'Advanced quantum computer for high-end ship systems', - requirements: { - engineering: 8, - crafting: 10 - }, - materials: [ - { id: 'advanced_components', quantity: 3 }, - { id: 'energy_crystal', quantity: 8 }, - { id: 'rare_metal', quantity: 4 }, - { id: 'copper_wire', quantity: 6 } - ], - results: [ - { id: 'quantum_computer', quantity: 1 } - ], - experience: 100, - craftingTime: 15000 // 15 seconds - }); - } - - addRecipe(id, recipe) { - recipe.id = id; - recipe.unlocked = false; - this.recipes.set(id, recipe); - } - - update(deltaTime) { - // Check for newly unlocked recipes - this.checkRecipeUnlocks(); - } - - checkRecipeUnlocks() { - const skillSystem = this.game.systems.skillSystem; - if (!skillSystem) return; - - for (const [id, recipe] of this.recipes) { - if (!recipe.unlocked) { - let canCraft = true; - - // Check skill requirements - if (recipe.requirements) { - for (const [skillName, requiredLevel] of Object.entries(recipe.requirements)) { - const skillLevel = skillSystem.getSkillLevel(skillName); - if (skillLevel < requiredLevel) { - canCraft = false; - break; - } - } - } - - if (canCraft) { - recipe.unlocked = true; - console.log(`[CRAFTING] Recipe unlocked: ${recipe.name}`); - } - } - } - } - - getRecipesByCategory(category) { - return Array.from(this.recipes.values()) - .filter(recipe => recipe.category === category); - } - - canCraftRecipe(recipeId) { - const recipe = this.recipes.get(recipeId); - if (!recipe) return false; - - // Check skill requirements - if (recipe.requirements) { - const skillSystem = this.game.systems.skillSystem; - if (!skillSystem) return false; - - for (const [skillName, requiredLevel] of Object.entries(recipe.requirements)) { - const skillLevel = skillSystem.getSkillLevel(skillName); - if (skillLevel < requiredLevel) { - return false; - } - } - } - - // Check materials - if (recipe.materials) { - for (const material of recipe.materials) { - const inventory = this.game.systems.inventory; - if (!inventory || !inventory.hasItem(material.id, material.quantity)) { - return false; - } - } - } - - return true; - } - - getMissingMaterials(recipeId) { - const recipe = this.recipes.get(recipeId); - if (!recipe || !recipe.materials) return []; - - const missing = []; - const inventory = this.game.systems.inventory; - - console.log(`[CRAFTING DEBUG] Checking materials for recipe: ${recipe.name}`); - console.log(`[CRAFTING DEBUG] Inventory system:`, inventory); - - for (const material of recipe.materials) { - let currentCount = 0; - - // Safely get current material count - if (inventory && typeof inventory.getItemCount === 'function') { - try { - currentCount = inventory.getItemCount(material.id); - // Ensure we have a valid number - currentCount = typeof currentCount === 'number' && !isNaN(currentCount) ? currentCount : 0; - } catch (error) { - console.log(`[CRAFTING DEBUG] Error getting count for ${material.id}:`, error); - currentCount = 0; - } - console.log(`[CRAFTING DEBUG] Material ${material.id}: current=${currentCount}, required=${material.quantity}`); - } else { - console.log(`[CRAFTING DEBUG] Inventory or getItemCount not available for ${material.id}`); - currentCount = 0; - } - - // Ensure required quantity is also a valid number - const requiredQuantity = typeof material.quantity === 'number' && !isNaN(material.quantity) ? material.quantity : 0; - - // Check if we have enough materials - if (currentCount < requiredQuantity) { - missing.push({ - id: material.id, - required: requiredQuantity, - current: currentCount, - missing: Math.max(0, requiredQuantity - currentCount) - }); - } - } - - console.log(`[CRAFTING DEBUG] Missing materials:`, missing); - return missing; - } - - async craftRecipe(recipeId) { - const recipe = this.recipes.get(recipeId); - if (!recipe) { - console.error(`[CRAFTING] Recipe not found: ${recipeId}`); - return false; - } - - if (!this.canCraftRecipe(recipeId)) { - console.log(`[CRAFTING] Cannot craft recipe: ${recipe.name}`); - return false; - } - - console.log(`[CRAFTING] Starting to craft: ${recipe.name}`); - - // Remove materials - if (recipe.materials) { - for (const material of recipe.materials) { - this.game.systems.inventory.removeItem(material.id, material.quantity); - } - } - - // Add crafting experience - if (recipe.experience) { - this.game.systems.skillSystem.awardCraftingExperience(recipe.experience); - } - - // Wait for crafting time - await new Promise(resolve => setTimeout(resolve, recipe.craftingTime)); - - // Add results to inventory - if (recipe.results) { - for (const result of recipe.results) { - this.game.systems.inventory.addItem(result.id, result.quantity); - } - } - - // Update quest progress - if (this.game.systems.questSystem) { - this.game.systems.questSystem.onItemCrafted(); - } - - console.log(`[CRAFTING] Successfully crafted: ${recipe.name}`); - return true; - } - - selectRecipe(recipeId) { - this.selectedRecipe = this.recipes.get(recipeId); - return this.selectedRecipe; - } - - getSelectedRecipe() { - return this.selectedRecipe; - } - - updateUI() { - this.updateRecipeList(); - this.updateCraftingDetails(); - this.updateCraftingInfo(); - } - - updateRecipeList() { - const recipeListElement = document.getElementById('recipeList'); - if (!recipeListElement) return; - - const recipes = this.getRecipesByCategory(this.currentCategory); - - recipeListElement.innerHTML = ''; - - if (recipes.length === 0) { - recipeListElement.innerHTML = '

No recipes available in this category

'; - return; - } - - recipes.forEach(recipe => { - const recipeElement = document.createElement('div'); - recipeElement.className = 'recipe-item'; - recipeElement.dataset.recipeId = recipe.id; - - const canCraft = this.canCraftRecipe(recipe.id); - const missingMaterials = this.getMissingMaterials(recipe.id); - - // Check if recipe is unlocked (skill requirements met) - const skillSystem = this.game.systems.skillSystem; - let skillRequirementsMet = true; - if (recipe.requirements && skillSystem) { - for (const [skillName, requiredLevel] of Object.entries(recipe.requirements)) { - const skillLevel = skillSystem.getSkillLevel(skillName); - if (skillLevel < requiredLevel) { - skillRequirementsMet = false; - break; - } - } - } - - // Apply styling based on status - if (!skillRequirementsMet) { - recipeElement.classList.add('locked'); - } else if (!canCraft) { - recipeElement.classList.add('missing-materials'); - } else { - recipeElement.classList.add('can-craft'); - } - - // Generate requirements text - const requirementsText = recipe.requirements ? - Object.entries(recipe.requirements).map(([skill, level]) => `${skill}: ${level}`).join(', ') : 'None'; - - // Generate materials with missing status - const materialsHtml = recipe.materials ? recipe.materials.map(mat => { - const missing = missingMaterials.find(m => m.id === mat.id); - const currentCount = missing ? missing.current : 0; - const requiredCount = mat.quantity || 0; - - if (missing) { - return `
- ${mat.id} - ${currentCount}/${requiredCount} -
`; - } else { - return `
- ${mat.id} - ${currentCount}/${requiredCount} -
`; - } - }).join('') : ''; - - recipeElement.innerHTML = ` -
-

${recipe.name}

- Level ${requirementsText} -
-
${recipe.description}
-
- ${materialsHtml} -
- ${missingMaterials.length > 0 ? ` -
- - Missing: ${missingMaterials.map(m => `${m.missing}x ${m.id}`).join(', ')} -
- ` : ''} -
- - ${recipe.craftingTime / 1000}s -
- `; - - recipeElement.addEventListener('click', () => { - this.selectRecipe(recipe.id); - this.updateCraftingDetails(); - }); - - recipeListElement.appendChild(recipeElement); - }); - } - - updateCraftingDetails() { - const detailsElement = document.getElementById('craftingDetails'); - if (!detailsElement) return; - - if (!this.selectedRecipe) { - detailsElement.innerHTML = ` -
-

Select a Recipe

-

Choose a recipe from the list to see details and craft items.

-
- `; - return; - } - - const recipe = this.selectedRecipe; - const canCraft = this.canCraftRecipe(recipe.id); - - detailsElement.innerHTML = ` -
-

${recipe.name}

-

${recipe.description}

- -
-

Requirements:

- ${recipe.requirements ? Object.entries(recipe.requirements).map(([skill, level]) => - `
- ${skill} - Level ${level} -
` - ).join('') : '

No special requirements

'} -
- -
-

Materials Needed:

- ${recipe.materials ? recipe.materials.map(mat => - `
- ${mat.id} - x${mat.quantity} - Have: ${this.game.systems.inventory?.getItemCount(mat.id) || 0} -
` - ).join('') : '

No materials needed

'} -
- -
-

Results:

- ${recipe.results ? recipe.results.map(result => - `
- ${result.id} - x${result.quantity} -
` - ).join('') : ''} -
- -
-
- - ${recipe.experience} XP -
-
- - ${recipe.craftingTime / 1000} seconds -
-
- - -
- `; - } - - updateCraftingInfo() { - const skillSystem = this.game.systems.skillSystem; - if (!skillSystem) return; - - const craftingLevel = skillSystem.getSkillLevel('crafting'); - const craftingExp = skillSystem.getSkillExperience('crafting'); - const expNeeded = skillSystem.getExperienceNeeded('crafting'); - - const levelElement = document.getElementById('craftingLevel'); - const expElement = document.getElementById('craftingExp'); - - if (levelElement) levelElement.textContent = craftingLevel; - if (expElement) expElement.textContent = `${craftingExp}/${expNeeded}`; - } - - switchCategory(category) { - this.currentCategory = category; - this.selectedRecipe = null; - - // Update UI only if in multiplayer mode or game is actively running - const shouldUpdateUI = window.smartSaveManager?.isMultiplayer || this.game?.isRunning; - - if (shouldUpdateUI) { - this.updateUI(); - } - } -} - -// Export for use in GameEngine -if (typeof module !== 'undefined' && module.exports) { - module.exports = CraftingSystem; -} diff --git a/Client-Server/js/systems/DungeonSystem.js b/Client-Server/js/systems/DungeonSystem.js deleted file mode 100644 index 851c485..0000000 --- a/Client-Server/js/systems/DungeonSystem.js +++ /dev/null @@ -1,1985 +0,0 @@ -/** - * Galaxy Strike Online - Dungeon System - * Manages procedural dungeon generation and exploration - */ - -class DungeonSystem { - constructor(gameEngine) { - this.game = gameEngine; - - // Dungeon configuration - this.dungeonTypes = { - tutorial: { - name: 'Tutorial Dungeon', - description: 'Learn the basics of dungeon exploration in this guided tutorial', - difficulty: 'tutorial', - enemyTypes: ['training_drone', 'practice_target'], - rewardMultiplier: 0.5, - oneTimeOnly: true, - healthType: 'player', // Ground mission - energyCost: 0 - }, - alien_ruins: { - name: 'Alien Ruins', - description: 'Ancient alien structures filled with mysterious technology', - difficulty: 'medium', - enemyTypes: ['alien_guardian', 'ancient_drone', 'crystal_golem'], - rewardMultiplier: 1.2, - healthType: 'player', // Ground mission - energyCost: 20 - }, - pirate_lair: { - name: 'Pirate Lair', - description: 'Dangerous pirate hideouts with valuable loot', - difficulty: 'easy', - enemyTypes: ['space_pirate', 'pirate_captain', 'defense_turret'], - rewardMultiplier: 1.0, - healthType: 'ship', // Space mission - energyCost: 15 - }, - corrupted_vault: { - name: 'Corrupted AI Vault', - description: ' malfunctioning AI facilities with corrupted security', - difficulty: 'hard', - enemyTypes: ['security_drone', 'corrupted_ai', 'virus_program'], - rewardMultiplier: 1.5, - healthType: 'ship', // Space mission - energyCost: 25 - }, - asteroid_mine: { - name: 'Asteroid Mine', - description: 'Abandoned mining facilities in asteroid fields', - difficulty: 'easy', - enemyTypes: ['mining_drone', 'rock_creature', 'explosive_asteroid'], - rewardMultiplier: 0.8, - healthType: 'ship', // Space mission - energyCost: 10 - }, - nebula_anomaly: { - name: 'Nebula Anomaly', - description: 'Strange energy anomalies in deep space', - difficulty: 'extreme', - enemyTypes: ['energy_being', 'phase_shifter', 'quantum_entity'], - rewardMultiplier: 2.0, - healthType: 'ship', // Space mission - energyCost: 30 - }, - - // NEW DUNGEONS - Space Theme - space_station: { - name: 'Abandoned Space Station', - description: 'A derelict space station floating in the void', - difficulty: 'medium', - enemyTypes: ['maintenance_drone', 'security_android', 'station_ai'], - rewardMultiplier: 1.3, - healthType: 'player', - energyCost: 22 - }, - comet_core: { - name: 'Comet Core', - description: 'The frozen heart of a passing comet', - difficulty: 'hard', - enemyTypes: ['ice_elemental', 'frost_wyrm', 'crystal_guardian'], - rewardMultiplier: 1.6, - healthType: 'ship', - energyCost: 28 - }, - black_hole_perimeter: { - name: 'Black Hole Perimeter', - description: 'Dangerous space near a black hole event horizon', - difficulty: 'extreme', - enemyTypes: ['gravity_wraith', 'void_stalker', 'singularity_spawn'], - rewardMultiplier: 2.5, - healthType: 'ship', - energyCost: 35 - }, - star_forge: { - name: 'Star Forge', - description: 'Ancient facility that harnesses stellar energy', - difficulty: 'hard', - enemyTypes: ['plasma_elemental', 'solar_guardian', 'fusion_core'], - rewardMultiplier: 1.8, - healthType: 'ship', - energyCost: 30 - }, - debris_field: { - name: 'Ship Debris Field', - description: 'Graveyard of destroyed spacecraft', - difficulty: 'easy', - enemyTypes: ['scrap_golem', 'hull_breacher', 'salage_drone'], - rewardMultiplier: 0.9, - healthType: 'ship', - energyCost: 12 - }, - - // NEW DUNGEONS - Planet Theme - jungle_temple: { - name: 'Jungle Temple', - description: 'Overgrown temple hidden in dense alien jungle', - difficulty: 'medium', - enemyTypes: ['plant_beast', 'tribal_warrior', 'jungle_spirit'], - rewardMultiplier: 1.4, - healthType: 'player', - energyCost: 25 - }, - desert_pyramid: { - name: 'Desert Pyramid', - description: 'Ancient pyramid buried under endless sand dunes', - difficulty: 'hard', - enemyTypes: ['sand_worm', 'mummy_guardian', 'heat_elemental'], - rewardMultiplier: 1.7, - healthType: 'player', - energyCost: 28 - }, - volcanic_caverns: { - name: 'Volcanic Caverns', - description: 'Molten caverns deep within an active volcano', - difficulty: 'hard', - enemyTypes: ['lava_elemental', 'fire_demon', 'magma_beast'], - rewardMultiplier: 1.6, - healthType: 'player', - energyCost: 26 - }, - arctic_research: { - name: 'Arctic Research Base', - description: 'Frozen research facility with failed experiments', - difficulty: 'medium', - enemyTypes: ['cryo_mutant', 'frost_android', 'ice_wraith'], - rewardMultiplier: 1.5, - healthType: 'player', - energyCost: 24 - }, - swamp_lair: { - name: 'Swamp Lair', - description: 'Murky swamp inhabited by strange creatures', - difficulty: 'easy', - enemyTypes: ['swamp_beast', 'toxic_spitter', 'mud_golem'], - rewardMultiplier: 1.1, - healthType: 'player', - energyCost: 18 - }, - - // NEW DUNGEONS - Technology Theme - cyber_realm: { - name: 'Cyber Realm', - description: 'Virtual reality space corrupted by malware', - difficulty: 'hard', - enemyTypes: ['glitch_wraith', 'firewall_guardian', 'data_vampire'], - rewardMultiplier: 1.9, - healthType: 'player', - energyCost: 32 - }, - robot_factory: { - name: 'Robot Factory', - description: 'Automated factory producing hostile machines', - difficulty: 'medium', - enemyTypes: ['assembly_drone', 'welder_bot', 'factory_overseer'], - rewardMultiplier: 1.4, - healthType: 'player', - energyCost: 23 - }, - quantum_lab: { - name: 'Quantum Laboratory', - description: 'Research facility experimenting with quantum physics', - difficulty: 'extreme', - enemyTypes: ['quantum_phantom', 'particle_accelerator', 'reality_bender'], - rewardMultiplier: 2.3, - healthType: 'player', - energyCost: 38 - }, - server_farm: { - name: 'Server Farm', - description: 'Massive data center with rogue security programs', - difficulty: 'medium', - enemyTypes: ['sentinel_program', 'data_corruptor', 'system_guardian'], - rewardMultiplier: 1.3, - healthType: 'player', - energyCost: 21 - }, - - // NEW DUNGEONS - Biome/Elemental Theme - crystal_caves: { - name: 'Crystal Caves', - description: 'Caves filled with energy-infused crystals', - difficulty: 'medium', - enemyTypes: ['crystal_golem', 'shard_elemental', 'resonance_beast'], - rewardMultiplier: 1.4, - healthType: 'player', - energyCost: 22 - }, - toxic_wastes: { - name: 'Toxic Wastes', - description: 'Polluted wasteland filled with mutated creatures', - difficulty: 'hard', - enemyTypes: ['mutant_horror', 'toxic_slime', 'radiation_beast'], - rewardMultiplier: 1.7, - healthType: 'player', - energyCost: 27 - }, - shadow_realm: { - name: 'Shadow Realm', - description: 'Dark dimension inhabited by shadow creatures', - difficulty: 'extreme', - enemyTypes: ['shadow_demon', 'nightmare_stalker', 'void_walker'], - rewardMultiplier: 2.2, - healthType: 'player', - energyCost: 36 - }, - time_anomaly: { - name: 'Time Anomaly', - description: 'Area where time flows unpredictably', - difficulty: 'extreme', - enemyTypes: ['temporal_paradox', 'future_soldier', 'past_guardian'], - rewardMultiplier: 2.4, - healthType: 'player', - energyCost: 40 - }, - - // NEW DUNGEONS - Military/War Theme - war_zone: { - name: 'Active War Zone', - description: 'Battlefield with ongoing combat operations', - difficulty: 'hard', - enemyTypes: ['enemy_soldier', 'combat_drone', 'field_commander'], - rewardMultiplier: 1.8, - healthType: 'player', - energyCost: 29 - }, - military_base: { - name: 'Abandoned Military Base', - description: 'Former military installation with automated defenses', - difficulty: 'medium', - enemyTypes: ['turret_system', 'combat_android', 'base_commander'], - rewardMultiplier: 1.5, - healthType: 'player', - energyCost: 24 - }, - weapons_testing: { - name: 'Weapons Testing Facility', - description: 'Secret facility testing advanced weaponry', - difficulty: 'hard', - enemyTypes: ['weapon_drone', 'test_subject', 'chief_scientist'], - rewardMultiplier: 1.9, - healthType: 'player', - energyCost: 31 - }, - - // NEW DUNGEONS - Special/Unique Theme - dream_scape: { - name: 'Dream Scape', - description: 'Surreal landscape shaped by collective dreams', - difficulty: 'medium', - enemyTypes: ['nightmare_creature', 'dream_guardian', 'subconscious_demon'], - rewardMultiplier: 1.6, - healthType: 'player', - energyCost: 26 - }, - memory_palace: { - name: 'Memory Palace', - description: 'Mental realm storing forgotten memories', - difficulty: 'hard', - enemyTypes: ['memory_fragment', 'forgetfulness_demon', 'nostalgia_spirit'], - rewardMultiplier: 1.7, - healthType: 'player', - energyCost: 28 - }, - dimension_rift: { - name: 'Dimension Rift', - description: 'Tear between dimensions with interdimensional invaders', - difficulty: 'extreme', - enemyTypes: ['rift_demon', 'dimensional_hunter', 'reality_tear'], - rewardMultiplier: 2.6, - healthType: 'player', - energyCost: 42 - } - }; - - // Current dungeon state - this.currentDungeon = null; - this.currentRoom = null; - this.dungeonProgress = 0; - this.isExploring = false; - - // Dungeon templates - this.roomTypes = { - entrance: { name: 'Entrance', enemies: 0, rewards: false }, - corridor: { name: 'Corridor', enemies: 1, rewards: false }, - chamber: { name: 'Chamber', enemies: 2, rewards: true }, - treasure: { name: 'Treasure Room', enemies: 0, rewards: true }, - boss: { name: 'Boss Room', enemies: 1, rewards: true, isBoss: true }, - exit: { name: 'Exit', enemies: 0, rewards: false } - }; - - // Enemy templates - this.enemyTemplates = { - // Original enemies - training_drone: { - name: 'Training Drone', - health: 10, - attack: 5, - defense: 2, - experience: 5, - credits: 3 - }, - practice_target: { - name: 'Practice Target', - health: 5, - attack: 0, - defense: 0, - experience: 2, - credits: 1 - }, - alien_guardian: { - name: 'Alien Guardian', - health: 50, - attack: 8, - defense: 5, - experience: 25, - credits: 15 - }, - ancient_drone: { - name: 'Ancient Drone', - health: 30, - attack: 12, - defense: 2, - experience: 20, - credits: 10 - }, - crystal_golem: { - name: 'Crystal Golem', - health: 80, - attack: 6, - defense: 10, - experience: 35, - credits: 25 - }, - space_pirate: { - name: 'Space Pirate', - health: 25, - attack: 10, - defense: 3, - experience: 15, - credits: 12 - }, - pirate_captain: { - name: 'Pirate Captain', - health: 40, - attack: 15, - defense: 6, - experience: 30, - credits: 20 - }, - defense_turret: { - name: 'Defense Turret', - health: 20, - attack: 18, - defense: 8, - experience: 18, - credits: 8 - }, - security_drone: { - name: 'Security Drone', - health: 35, - attack: 14, - defense: 4, - experience: 22, - credits: 15 - }, - corrupted_ai: { - name: 'Corrupted AI', - health: 60, - attack: 20, - defense: 2, - experience: 40, - credits: 30 - }, - virus_program: { - name: 'Virus Program', - health: 15, - attack: 25, - defense: 1, - experience: 20, - credits: 12 - }, - mining_drone: { - name: 'Mining Drone', - health: 20, - attack: 8, - defense: 3, - experience: 12, - credits: 8 - }, - rock_creature: { - name: 'Rock Creature', - health: 45, - attack: 6, - defense: 12, - experience: 25, - credits: 15 - }, - explosive_asteroid: { - name: 'Explosive Asteroid', - health: 10, - attack: 30, - defense: 0, - experience: 15, - credits: 5 - }, - energy_being: { - name: 'Energy Being', - health: 55, - attack: 22, - defense: 3, - experience: 45, - credits: 35 - }, - phase_shifter: { - name: 'Phase Shifter', - health: 30, - attack: 28, - defense: 1, - experience: 35, - credits: 25 - }, - quantum_entity: { - name: 'Quantum Entity', - health: 70, - attack: 35, - defense: 5, - experience: 60, - credits: 50 - }, - - // NEW ENEMIES - Space Theme - maintenance_drone: { - name: 'Maintenance Drone', - health: 28, - attack: 11, - defense: 4, - experience: 18, - credits: 12 - }, - security_android: { - name: 'Security Android', - health: 42, - attack: 16, - defense: 7, - experience: 28, - credits: 20 - }, - station_ai: { - name: 'Station AI', - health: 65, - attack: 24, - defense: 3, - experience: 45, - credits: 32 - }, - ice_elemental: { - name: 'Ice Elemental', - health: 38, - attack: 18, - defense: 8, - experience: 32, - credits: 24 - }, - frost_wyrm: { - name: 'Frost Wyrm', - health: 72, - attack: 26, - defense: 6, - experience: 52, - credits: 38 - }, - gravity_wraith: { - name: 'Gravity Wraith', - health: 85, - attack: 32, - defense: 4, - experience: 68, - credits: 48 - }, - void_stalker: { - name: 'Void Stalker', - health: 78, - attack: 38, - defense: 5, - experience: 72, - credits: 52 - }, - singularity_spawn: { - name: 'Singularity Spawn', - health: 95, - attack: 42, - defense: 8, - experience: 85, - credits: 65 - }, - plasma_elemental: { - name: 'Plasma Elemental', - health: 58, - attack: 28, - defense: 4, - experience: 48, - credits: 35 - }, - solar_guardian: { - name: 'Solar Guardian', - health: 82, - attack: 34, - defense: 7, - experience: 65, - credits: 48 - }, - fusion_core: { - name: 'Fusion Core', - health: 68, - attack: 30, - defense: 12, - experience: 58, - credits: 42 - }, - scrap_golem: { - name: 'Scrap Golem', - health: 35, - attack: 14, - defense: 9, - experience: 22, - credits: 16 - }, - hull_breacher: { - name: 'Hull Breacher', - health: 32, - attack: 20, - defense: 3, - experience: 26, - credits: 18 - }, - salage_drone: { - name: 'Salvage Drone', - health: 22, - attack: 12, - defense: 5, - experience: 16, - credits: 11 - }, - - // NEW ENEMIES - Planet Theme - plant_beast: { - name: 'Plant Beast', - health: 48, - attack: 15, - defense: 8, - experience: 35, - credits: 26 - }, - tribal_warrior: { - name: 'Tribal Warrior', - health: 38, - attack: 18, - defense: 6, - experience: 28, - credits: 20 - }, - jungle_spirit: { - name: 'Jungle Spirit', - health: 55, - attack: 22, - defense: 4, - experience: 42, - credits: 30 - }, - sand_worm: { - name: 'Sand Worm', - health: 75, - attack: 28, - defense: 9, - experience: 58, - credits: 42 - }, - mummy_guardian: { - name: 'Mummy Guardian', - health: 62, - attack: 24, - defense: 7, - experience: 48, - credits: 35 - }, - heat_elemental: { - name: 'Heat Elemental', - health: 52, - attack: 26, - defense: 3, - experience: 45, - credits: 32 - }, - lava_elemental: { - name: 'Lava Elemental', - health: 68, - attack: 30, - defense: 5, - experience: 55, - credits: 40 - }, - fire_demon: { - name: 'Fire Demon', - health: 72, - attack: 32, - defense: 6, - experience: 62, - credits: 45 - }, - magma_beast: { - name: 'Magma Beast', - health: 85, - attack: 28, - defense: 12, - experience: 68, - credits: 50 - }, - cryo_mutant: { - name: 'Cryo Mutant', - health: 45, - attack: 20, - defense: 7, - experience: 38, - credits: 28 - }, - frost_android: { - name: 'Frost Android', - health: 52, - attack: 22, - defense: 8, - experience: 42, - credits: 30 - }, - ice_wraith: { - name: 'Ice Wraith', - health: 58, - attack: 25, - defense: 4, - experience: 48, - credits: 35 - }, - swamp_beast: { - name: 'Swamp Beast', - health: 35, - attack: 16, - defense: 9, - experience: 24, - credits: 18 - }, - toxic_spitter: { - name: 'Toxic Spitter', - health: 28, - attack: 19, - defense: 3, - experience: 22, - credits: 16 - }, - mud_golem: { - name: 'Mud Golem', - health: 42, - attack: 12, - defense: 11, - experience: 26, - credits: 19 - }, - - // NEW ENEMIES - Technology Theme - glitch_wraith: { - name: 'Glitch Wraith', - health: 48, - attack: 26, - defense: 2, - experience: 45, - credits: 32 - }, - firewall_guardian: { - name: 'Firewall Guardian', - health: 65, - attack: 22, - defense: 8, - experience: 52, - credits: 38 - }, - data_vampire: { - name: 'Data Vampire', - health: 38, - attack: 30, - defense: 3, - experience: 35, - credits: 26 - }, - assembly_drone: { - name: 'Assembly Drone', - health: 32, - attack: 15, - defense: 6, - experience: 24, - credits: 17 - }, - welder_bot: { - name: 'Welder Bot', - health: 28, - attack: 20, - defense: 4, - experience: 22, - credits: 15 - }, - factory_overseer: { - name: 'Factory Overseer', - health: 58, - attack: 24, - defense: 7, - experience: 46, - credits: 33 - }, - quantum_phantom: { - name: 'Quantum Phantom', - health: 78, - attack: 35, - defense: 4, - experience: 68, - credits: 50 - }, - particle_accelerator: { - name: 'Particle Accelerator', - health: 92, - attack: 40, - defense: 6, - experience: 85, - credits: 62 - }, - reality_bender: { - name: 'Reality Bender', - health: 88, - attack: 45, - defense: 3, - experience: 92, - credits: 68 - }, - sentinel_program: { - name: 'Sentinel Program', - health: 42, - attack: 21, - defense: 8, - experience: 32, - credits: 24 - }, - data_corruptor: { - name: 'Data Corruptor', - health: 35, - attack: 25, - defense: 3, - experience: 28, - credits: 20 - }, - system_guardian: { - name: 'System Guardian', - health: 55, - attack: 23, - defense: 9, - experience: 42, - credits: 30 - }, - - // NEW ENEMIES - Biome/Elemental Theme - shard_elemental: { - name: 'Shard Elemental', - health: 45, - attack: 19, - defense: 10, - experience: 38, - credits: 28 - }, - resonance_beast: { - name: 'Resonance Beast', - health: 52, - attack: 22, - defense: 6, - experience: 42, - credits: 30 - }, - mutant_horror: { - name: 'Mutant Horror', - health: 68, - attack: 28, - defense: 5, - experience: 58, - credits: 42 - }, - toxic_slime: { - name: 'Toxic Slime', - health: 42, - attack: 18, - defense: 8, - experience: 32, - credits: 24 - }, - radiation_beast: { - name: 'Radiation Beast', - health: 75, - attack: 30, - defense: 4, - experience: 65, - credits: 48 - }, - shadow_demon: { - name: 'Shadow Demon', - health: 72, - attack: 34, - defense: 3, - experience: 68, - credits: 50 - }, - nightmare_stalker: { - name: 'Nightmare Stalker', - health: 85, - attack: 38, - defense: 5, - experience: 78, - credits: 58 - }, - void_walker: { - name: 'Void Walker', - health: 92, - attack: 42, - defense: 7, - experience: 88, - credits: 65 - }, - temporal_paradox: { - name: 'Temporal Paradox', - health: 78, - attack: 40, - defense: 4, - experience: 75, - credits: 55 - }, - future_soldier: { - name: 'Future Soldier', - health: 65, - attack: 32, - defense: 9, - experience: 58, - credits: 42 - }, - past_guardian: { - name: 'Past Guardian', - health: 70, - attack: 28, - defense: 12, - experience: 62, - credits: 45 - }, - - // NEW ENEMIES - Military/War Theme - enemy_soldier: { - name: 'Enemy Soldier', - health: 45, - attack: 20, - defense: 7, - experience: 35, - credits: 26 - }, - combat_drone: { - name: 'Combat Drone', - health: 38, - attack: 22, - defense: 5, - experience: 30, - credits: 22 - }, - field_commander: { - name: 'Field Commander', - health: 62, - attack: 28, - defense: 9, - experience: 52, - credits: 38 - }, - turret_system: { - name: 'Turret System', - health: 48, - attack: 26, - defense: 10, - experience: 38, - credits: 28 - }, - combat_android: { - name: 'Combat Android', - health: 55, - attack: 24, - defense: 8, - experience: 45, - credits: 33 - }, - base_commander: { - name: 'Base Commander', - health: 72, - attack: 30, - defense: 11, - experience: 62, - credits: 45 - }, - weapon_drone: { - name: 'Weapon Drone', - health: 42, - attack: 28, - defense: 4, - experience: 38, - credits: 28 - }, - test_subject: { - name: 'Test Subject', - health: 58, - attack: 25, - defense: 6, - experience: 48, - credits: 35 - }, - chief_scientist: { - name: 'Chief Scientist', - health: 35, - attack: 32, - defense: 3, - experience: 42, - credits: 30 - }, - - // NEW ENEMIES - Special/Unique Theme - nightmare_creature: { - name: 'Nightmare Creature', - health: 62, - attack: 28, - defense: 5, - experience: 55, - credits: 40 - }, - dream_guardian: { - name: 'Dream Guardian', - health: 68, - attack: 30, - defense: 8, - experience: 58, - credits: 42 - }, - subconscious_demon: { - name: 'Subconscious Demon', - health: 75, - attack: 34, - defense: 4, - experience: 68, - credits: 50 - }, - memory_fragment: { - name: 'Memory Fragment', - health: 48, - attack: 26, - defense: 6, - experience: 45, - credits: 33 - }, - forgetfulness_demon: { - name: 'Forgetfulness Demon', - health: 55, - attack: 30, - defense: 3, - experience: 48, - credits: 35 - }, - nostalgia_spirit: { - name: 'Nostalgia Spirit', - health: 52, - attack: 24, - defense: 9, - experience: 42, - credits: 30 - }, - rift_demon: { - name: 'Rift Demon', - health: 88, - attack: 44, - defense: 5, - experience: 92, - credits: 68 - }, - dimensional_hunter: { - name: 'Dimensional Hunter', - health: 95, - attack: 48, - defense: 8, - experience: 105, - credits: 78 - }, - reality_tear: { - name: 'Reality Tear', - health: 102, - attack: 52, - defense: 3, - experience: 115, - credits: 85 - } - }; - - // Statistics - this.stats = { - dungeonsAttempted: 0, - dungeonsCompleted: 0, - totalEnemiesDefeated: 0, - bestTime: Infinity, - totalLootEarned: 0 - }; - } - - async initialize() { - this.generateDungeonList(); - } - - generateDungeonList() { - - const dungeonListElement = document.getElementById('dungeonList'); - if (!dungeonListElement) { - console.error('Dungeon list element not found!'); - return; - } - - // Clear existing list - dungeonListElement.innerHTML = ''; - - const questSystem = this.game.systems.questSystem; - const firstStepsQuest = questSystem ? questSystem.findQuest('tutorial_complete') : null; - const showTutorialDungeon = firstStepsQuest && firstStepsQuest.status === 'active'; - - Object.entries(this.dungeonTypes).forEach(([key, dungeon]) => { - // Skip tutorial dungeon unless First Steps quest is active - if (key === 'tutorial' && !showTutorialDungeon) { - return; - } - - const dungeonElement = document.createElement('div'); - dungeonElement.className = 'dungeon-item'; - dungeonElement.dataset.dungeonType = key; - - // Check if tutorial dungeon is completed - const isCompleted = key === 'tutorial' && this.game.systems.player.stats.tutorialDungeonCompleted; - const statusClass = isCompleted ? 'completed' : ''; - const statusText = isCompleted ? 'COMPLETED' : ''; - - dungeonElement.innerHTML = ` -
${dungeon.name}
-
${dungeon.difficulty.toUpperCase()}
- ${statusText ? `
${statusText}
` : ''} -
${dungeon.description}
-
Rewards: ${dungeon.rewardMultiplier}x
-
Energy Cost: ${dungeon.energyCost || this.getEnergyCost(key)}
- `; - - dungeonElement.addEventListener('click', () => { - if (isCompleted) { - this.game.showNotification('Tutorial dungeon has already been completed!', 'warning', 3000); - } else { - this.selectDungeon(key); - } - }); - - dungeonListElement.appendChild(dungeonElement); - }); - - } - - selectDungeon(type) { - - // Check energy cost - const energyCost = this.getEnergyCost(type); - const player = this.game.systems.player; - - - if (!player.useEnergy(energyCost)) { - this.game.showNotification(`Not enough energy! Need ${energyCost} energy`, 'error', 3000); - return; - } - - - // Ensure we're on the Dungeons tab so the user can see the dungeon - if (this.game.ui && this.game.ui.currentTab !== 'dungeons') { - this.game.ui.switchTab('dungeons'); - } - - // Remove previous selection - document.querySelectorAll('.dungeon-item').forEach(item => { - item.classList.remove('selected'); - }); - - // Add selection to clicked dungeon - const selectedElement = document.querySelector(`[data-dungeon-type="${type}"]`); - if (selectedElement) { - selectedElement.classList.add('selected'); - } - - - // Generate dungeon - this.generateDungeon(type); - this.displayDungeon(); - - - this.game.showNotification(`Entered dungeon! -${energyCost} energy`, 'info', 3000); - } - - getEnergyCost(type) { - const costs = { - tutorial: 0, - alien_ruins: 20, - pirate_lair: 15, - corrupted_vault: 25, - asteroid_mine: 10, - nebula_anomaly: 30 - }; - return costs[type] || 15; - } - - calculateDungeonRewards(type, difficulty) { - const dungeonTemplate = this.dungeonTypes[type]; - const baseRewards = { - credits: 50, - experience: 25, - items: [] - }; - - // Apply difficulty multiplier - const difficultyMultipliers = { - tutorial: 0.5, - easy: 1.0, - medium: 1.5, - hard: 2.0 - }; - - const multiplier = difficultyMultipliers[difficulty] || 1.0; - const rewardMultiplier = dungeonTemplate.rewardMultiplier || 1.0; - - baseRewards.credits = Math.floor(baseRewards.credits * multiplier * rewardMultiplier); - baseRewards.experience = Math.floor(baseRewards.experience * multiplier * rewardMultiplier); - - return baseRewards; - } - - generateDungeon(type) { - - const dungeonTemplate = this.dungeonTypes[type]; - - // Check if tutorial dungeon has already been completed - if (type === 'tutorial' && this.game.systems.player.stats.tutorialDungeonCompleted) { - this.game.showNotification('Tutorial dungeon has already been completed!', 'warning', 3000); - return; - } - - - this.currentDungeon = { - type: type, - name: dungeonTemplate.name, - description: dungeonTemplate.description, - difficulty: dungeonTemplate.difficulty, - healthType: dungeonTemplate.healthType, - enemyTypes: dungeonTemplate.enemyTypes, - rewardMultiplier: dungeonTemplate.rewardMultiplier, - rooms: [], - currentRoomIndex: 0, - startTime: Date.now(), - completed: false - }; - - - // Generate rooms - ensure minimum of 3 rooms (entrance, at least 1 middle, boss) - const roomCount = Math.max(3, this.game.getRandomInt(5, 8)); - this.currentDungeon.rooms = this.generateRoomLayout(roomCount, dungeonTemplate); - - // Set current room - this.currentRoom = this.currentDungeon.rooms[0]; - this.dungeonProgress = 0; - - - // Show dungeon view and hide list - this.showDungeonView(); - - } - - generateRoomLayout(roomCount, dungeonTemplate) { - const rooms = []; - - - // Always start with entrance - rooms.push(this.createRoom('entrance', dungeonTemplate)); - - // Generate middle rooms (subtract 2 for entrance and boss room) - const middleRoomCount = roomCount - 2; - - for (let i = 0; i < middleRoomCount; i++) { - const roomType = this.getRandomRoomType(); - const room = this.createRoom(roomType, dungeonTemplate); - rooms.push(room); - } - - // Always end with boss room (exit is handled by completing the boss room) - rooms.push(this.createRoom('boss', dungeonTemplate)); - - - // Verify room accessibility - for (let i = 0; i < rooms.length; i++) { - } - - return rooms; - } - - getRandomRoomType() { - const weights = { - corridor: 40, - chamber: 30, - treasure: 0, // Temporarily disabled - entrance: 5, - boss: 5, - exit: 0 - }; - - const totalWeight = Object.values(weights).reduce((sum, weight) => sum + weight, 0); - let random = Math.random() * totalWeight; - - for (const [type, weight] of Object.entries(weights)) { - random -= weight; - if (random <= 0) { - return type; - } - } - - return 'corridor'; - } - - createRoom(roomType, dungeonTemplate) { - const template = this.roomTypes[roomType]; - const room = { - type: roomType, - name: template.name, - enemies: [], - rewards: null, - explored: false, - completed: false - }; - - // Generate enemies - if (template.enemies > 0) { - for (let i = 0; i < template.enemies; i++) { - const enemyType = dungeonTemplate.enemyTypes[ - Math.floor(Math.random() * dungeonTemplate.enemyTypes.length) - ]; - room.enemies.push(this.createEnemy(enemyType, template.isBoss)); - } - } - - // Generate rewards - if (template.rewards) { - room.rewards = this.generateRoomRewards(dungeonTemplate.difficulty, template.isBoss); - } - - return room; - } - - createEnemy(enemyType, isBoss = false) { - const template = this.enemyTemplates[enemyType]; - const bossMultiplier = isBoss ? 2.5 : 1.0; - - return { - id: Date.now() + Math.random().toString(36).substr(2, 9), - type: enemyType, - name: isBoss ? `Boss ${template.name}` : template.name, - health: Math.floor(template.health * bossMultiplier), - maxHealth: Math.floor(template.health * bossMultiplier), - attack: Math.floor(template.attack * bossMultiplier), - defense: Math.floor(template.defense * bossMultiplier), - experience: Math.floor(template.experience * bossMultiplier), - credits: Math.floor(template.credits * bossMultiplier), - isBoss: isBoss - }; - } - - generateRoomRewards(difficulty, isBoss = false) { - const baseRewards = this.game.systems.economy.generateRewards(difficulty, 'dungeon'); - const multiplier = isBoss ? 2.0 : 1.0; - - const rewards = { - credits: Math.floor(baseRewards.credits * multiplier), - experience: Math.floor(baseRewards.experience * multiplier), - materials: [] - }; - - // Add crafting materials based on chance and difficulty - const materialChance = isBoss ? 0.9 : 0.4; - if (Math.random() < materialChance) { - const materialCount = isBoss ? this.game.getRandomInt(3, 6) : this.game.getRandomInt(1, 3); - - // Define material pools with weights for better balance - const materialPools = { - tutorial: [ - { id: 'iron_ore', weight: 40 }, - { id: 'copper_wire', weight: 30 }, - { id: 'herbs', weight: 20 }, - { id: 'bandages', weight: 10 } - ], - easy: [ - { id: 'iron_ore', weight: 30 }, - { id: 'copper_wire', weight: 25 }, - { id: 'energy_crystal', weight: 15 }, - { id: 'leather', weight: 15 }, - { id: 'herbs', weight: 10 }, - { id: 'bandages', weight: 5 } - ], - medium: [ - { id: 'iron_ore', weight: 25 }, - { id: 'steel_plate', weight: 20 }, - { id: 'energy_crystal', weight: 20 }, - { id: 'copper_wire', weight: 15 }, - { id: 'rare_metal', weight: 5 }, - { id: 'bandages', weight: 15 } - ], - hard: [ - { id: 'steel_plate', weight: 30 }, - { id: 'energy_crystal', weight: 25 }, - { id: 'rare_metal', weight: 15 }, - { id: 'battery', weight: 20 }, - { id: 'bandages', weight: 10 } - ], - extreme: [ - { id: 'rare_metal', weight: 30 }, - { id: 'energy_crystal', weight: 25 }, - { id: 'battery', weight: 25 }, - { id: 'advanced_components', weight: 20 } - ] - }; - - const pool = materialPools[difficulty] || materialPools.easy; - - // Helper function to get weighted random material - function getWeightedRandomMaterial(materialPool) { - const totalWeight = materialPool.reduce((sum, mat) => sum + mat.weight, 0); - let random = Math.random() * totalWeight; - - for (const material of materialPool) { - random -= material.weight; - if (random <= 0) { - return material.id; - } - } - return materialPool[0].id; // Fallback - } - - for (let i = 0; i < materialCount; i++) { - const material = getWeightedRandomMaterial(pool); - const quantity = this.game.getRandomInt(1, isBoss ? 3 : 2); - - rewards.materials.push({ - id: material, - quantity: quantity - }); - } - } - - return rewards; - } - - displayDungeon() { - - const dungeonViewElement = document.getElementById('dungeonView'); - - if (!dungeonViewElement) { - const allElements = document.querySelectorAll('[id*="dungeon"]'); - return; - } - - if (!this.currentDungeon) { - return; - } - - const room = this.currentRoom; - if (room) { - // Room exists, continue processing - } - - const progress = Math.floor((this.currentDungeon.currentRoomIndex / this.currentDungeon.rooms.length) * 100); - - - let content = ` -
-
-

${this.currentDungeon.name}

-
-
-
-
- Room ${this.currentDungeon.currentRoomIndex + 1} / ${this.currentDungeon.rooms.length} -
-
- -
- ${this.getHealthDisplay()} -
- -
-

${room.name}

- ${this.getRoomDescription(room)} -
- -
- ${this.getRoomContent(room)} -
- -
- ${this.getRoomActions(room)} -
-
- `; - - - dungeonViewElement.innerHTML = content; - - - // Only add event listeners for buttons that don't use inline onclick handlers - const completeDungeonBtn = dungeonViewElement.querySelector('button[onclick*="completeDungeon"]'); - if (completeDungeonBtn) { - completeDungeonBtn.addEventListener('click', (e) => { - e.preventDefault(); - this.completeDungeon(); - }); - } - - } - - getHealthDisplay() { - const player = this.game.systems.player; - const healthType = this.currentDungeon.healthType; - - - if (healthType === 'ship') { - const shipHealth = player.ship.health || 0; - const shipMaxHealth = player.ship.maxHealth || 1000; - const healthPercent = Math.round((shipHealth / shipMaxHealth) * 100); - return ` -
-
Ship Health
-
-
-
- ${shipHealth} / ${shipMaxHealth} -
- `; - } else { - const playerHealth = player.attributes.health || 0; - const playerMaxHealth = player.attributes.maxHealth || 100; - const healthPercent = Math.round((playerHealth / playerMaxHealth) * 100); - return ` -
-
Player Health
-
-
-
- ${playerHealth} / ${playerMaxHealth} -
- `; - } - } - - getRoomDescription(room) { - const descriptions = { - entrance: 'You enter the dungeon. The air is thick with anticipation.', - corridor: 'A narrow corridor stretches ahead. You can hear distant sounds.', - chamber: 'You enter a large chamber. The echoes of past battles linger here.', - treasure: 'Golden light glimmers from piles of treasure in this room.', - boss: 'A massive presence fills the room. This is the guardian of this dungeon.', - exit: 'You can see the exit. Freedom awaits!' - }; - - return `

${descriptions[room.type] || 'You continue exploring...'}

`; - } - - getRoomContent(room) { - if (room.completed) { - return '

This room has been cleared.

'; - } - - if (room.enemies.length > 0) { - const enemiesHtml = room.enemies.map(enemy => ` -
-
${enemy.name}
-
-
-
-
- ${enemy.health} / ${enemy.maxHealth} -
-
ATK: ${enemy.attack} | DEF: ${enemy.defense}
-
- `).join(''); - - return `
${enemiesHtml}
`; - } - - if (room.rewards && !room.completed) { - return '

Treasure awaits!

'; - } - - return '

The room is empty and quiet.

'; - } - - getRoomActions(room) { - - if (room.completed) { - if (this.currentDungeon.currentRoomIndex < this.currentDungeon.rooms.length - 1) { - return ''; - } else { - return ''; - } - } - - if (room.enemies.length > 0) { - const buttonHtml = ''; - return buttonHtml; - } - - if (room.rewards) { - return ''; - } - - // Check if this is the final room - if (this.currentDungeon.currentRoomIndex >= this.currentDungeon.rooms.length - 1) { - return ''; - } - - return ''; - } - async startCombat() { - - if (this.isExploring) { - return; - } - - this.isExploring = true; - const room = this.currentRoom; - const enemies = room.enemies.filter(e => e.health > 0); - - - if (enemies.length === 0) { - this.completeRoom(); - return; - } - - // Simulate combat - await this.simulateCombat(enemies); - } - - async simulateCombat(enemies) { - - try { - const player = this.game.systems.player; - - const healthType = this.currentDungeon.healthType; - - let combatLog = []; - - // Handle case where there are no enemies - if (enemies.length === 0) { - combatLog.push('Room was empty - no enemies found'); - this.completeRoom(); - return; - } - - for (const [index, enemy] of enemies.entries()) { - - // Player attacks - const playerDamage = player.calculateDamage(this.currentDungeon.difficulty); - - const actualDamage = Math.max(1, playerDamage.damage - enemy.defense); - enemy.health = Math.max(0, enemy.health - actualDamage); - - combatLog.push(`You dealt ${actualDamage} damage to ${enemy.name}${playerDamage.isCritical ? ' (CRITICAL!)' : ''}`); - - // Update UI after player attack to show enemy health change - this.displayDungeon(); - // Also update global player UI to ensure health bars are updated only if in multiplayer mode or game is actively running - const shouldUpdateUI = window.smartSaveManager?.isMultiplayer || this.game?.isRunning; - - if (shouldUpdateUI && player.updateUI) { - player.updateUI(); - } - - // Small delay to make the combat visible - await new Promise(resolve => setTimeout(resolve, 500)); - - // Enemy attacks if still alive - if (enemy.health > 0) { - let enemyDamage, damageTarget; - - if (healthType === 'ship') { - // Space missions: higher damage, target ship health - enemyDamage = Math.max(1, Math.floor(enemy.attack * 1.5) - player.ship.defense); - damageTarget = 'ship'; - this.applyShipDamage(enemyDamage); - combatLog.push(`${enemy.name} dealt ${enemyDamage} damage to your ship`); - } else { - // Ground missions: normal damage, target player health - enemyDamage = Math.max(1, enemy.attack - player.attributes.defense); - damageTarget = 'player'; - player.takeDamage(enemyDamage); - combatLog.push(`${enemy.name} dealt ${enemyDamage} damage to you`); - } - - // Update UI after enemy attack to show player health change - this.displayDungeon(); - // Also update global player UI to ensure health bars are updated - if (player.updateUI) { - player.updateUI(); - } - - // Small delay to make the combat visible - await new Promise(resolve => setTimeout(resolve, 500)); - - // Check for destruction - if (healthType === 'ship' && player.ship.health <= 0) { - this.handleShipDestruction(); - } else if (healthType === 'player' && player.attributes.health <= 0) { - this.handlePlayerDefeat(); - } - } else { - // Enemy defeated - player.addExperience(enemy.experience); - this.game.systems.economy.addCredits(enemy.credits, 'dungeon'); - player.incrementKills(); - this.stats.totalEnemiesDefeated++; - - // Update quest progress for combat objectives - if (this.game.systems.questSystem) { - this.game.systems.questSystem.onEnemyDefeated(); - } - - combatLog.push(`${enemy.name} defeated! +${enemy.experience} XP, +${enemy.credits} credits`); - - // Update UI after enemy defeat - this.displayDungeon(); - // Also update global player UI to ensure health bars are updated - if (player.updateUI) { - player.updateUI(); - } - - // Small delay to make the combat visible - await new Promise(resolve => setTimeout(resolve, 500)); - } - } - - // Show combat results - this.game.showNotification('Combat completed!', 'success', 3000); - combatLog.forEach(message => this.game.showNotification(message, 'info', 2000)); - - // Check if all enemies defeated - const remainingEnemies = this.currentRoom.enemies.filter(e => e.health > 0); - if (remainingEnemies.length === 0) { - this.completeRoom(); - } - - this.isExploring = false; - this.displayDungeon(); - } catch (error) { - console.error('[DUNGEON] Error in simulateCombat:', error); - console.error('[DUNGEON] Error stack:', error.stack); - this.isExploring = false; - } - } - - // Health system methods - applyShipDamage(amount) { - const player = this.game.systems.player; - player.ship.health = Math.max(0, player.ship.health - amount); - - // Only update player UI if in multiplayer mode or game is actively running - if (this.game.shouldUpdateGUI()) { - player.updateUI(); - } - - // Check for ship destruction - if (player.ship.health <= 0) { - this.handleShipDestruction(); - } - } - - handleShipDestruction() { - const player = this.game.systems.player; - - // Show destruction message - this.game.showNotification('Your ship has been destroyed!', 'error', 5000); - this.game.showNotification('Dungeon failed - all progress lost.', 'warning', 4000); - - // Remove the current ship from inventory - if (this.game.systems.inventory) { - this.game.systems.inventory.removeItem(player.ship); - } - - // Reset to a basic ship if available in inventory - const availableShips = this.game.systems.inventory.getItemsByType('ship'); - if (availableShips.length > 0) { - player.ship = availableShips[0]; - this.game.showNotification(`Switched to ${player.ship.name}`, 'info', 3000); - } else { - // No ships available - create a basic one - player.ship = { - name: 'Basic Fighter', - health: 1000, - maxHealth: 1000, - defense: 5, - attack: 10, - type: 'ship' - }; - this.game.showNotification('No ships available - using emergency fighter', 'warning', 3000); - } - - // Apply dungeon failure penalty - this.applyDungeonFailurePenalty(); - - // Exit dungeon - this.exitDungeon(); - } - - applyDungeonFailurePenalty() { - const player = this.game.systems.player; - - // Lose some credits as penalty - const creditPenalty = Math.floor(this.game.systems.economy.credits * 0.1); // 10% credit loss - this.game.systems.economy.removeCredits(creditPenalty); - - // Lose some experience - const expPenalty = Math.floor(player.experience * 0.05); // 5% exp loss - player.experience = Math.max(0, player.experience - expPenalty); - - // Update failure statistics - this.stats.dungeonsFailed++; - - this.game.showNotification(`Dungeon penalty: -${creditPenalty} credits, -${expPenalty} XP`, 'warning', 4000); - } - - handlePlayerDefeat() { - this.game.showNotification('You have been defeated!', 'error', 5000); - this.game.showNotification('You\'ve been forced to retreat from the dungeon.', 'warning', 4000); - - // Heal player to prevent death loop - const player = this.game.systems.player; - player.attributes.health = Math.floor(player.attributes.maxHealth * 0.5); - - // Exit dungeon - this.exitDungeon(); - } - - exitDungeon() { - this.currentDungeon = null; - this.currentRoom = null; - this.dungeonProgress = 0; - this.isExploring = false; - - // Return to main view - this.generateDungeonList(); - } - - claimRewards() { - const room = this.currentRoom; - if (!room.rewards || room.completed) return; - - // Give all rewards including materials - this.game.systems.economy.giveRewards(room.rewards, 'dungeon'); - - room.rewards = null; // Remove rewards after claiming - - this.game.showNotification('Rewards claimed!', 'success', 3000); - - // Use the proper completeRoom method to ensure consistent behavior - this.completeRoom(); - } - - completeRoom() { - const room = this.currentRoom; - room.completed = true; - room.explored = true; - - // Reset exploring flag when completing a room - this.isExploring = false; - - - // Auto-claim rewards if available - if (room.rewards) { - // Give credits and experience - this.game.systems.economy.giveRewards(room.rewards, 'dungeon'); - - // Add crafting materials to inventory - if (room.rewards.materials && room.rewards.materials.length > 0) { - let materialText = ''; - room.rewards.materials.forEach(material => { - this.game.systems.inventory.addItem(material.id, material.quantity); - materialText += `${material.quantity}x ${material.id}, `; - }); - - materialText = materialText.slice(0, -2); - - this.game.showNotification(`Materials found: ${materialText}`, 'success', 4000); - } - - room.rewards = null; - } - - this.game.showNotification(`${room.name} completed!`, 'success', 3000); - - // Check if this is a boss room - if so, automatically complete the dungeon - if (room.type === 'boss') { - setTimeout(() => { - this.completeDungeon(); - }, 2000); // Small delay to show the completion message first - } else { - this.displayDungeon(); - } - - } - - nextRoom() { - - // Log all rooms for debugging - this.currentDungeon.rooms.forEach((room, index) => { - }); - - if (this.currentDungeon.currentRoomIndex >= this.currentDungeon.rooms.length - 1) { - return; - } - - const oldIndex = this.currentDungeon.currentRoomIndex; - this.currentDungeon.currentRoomIndex++; - this.currentRoom = this.currentDungeon.rooms[this.currentDungeon.currentRoomIndex]; - - - this.displayDungeon(); - } - - completeDungeon() { - if (!this.currentDungeon) { - return; - } - - const completionTime = Date.now() - this.currentDungeon.startTime; - const player = this.game.systems.player; - const economy = this.game.systems.economy; - - // Generate proper rewards including materials - const baseRewards = this.generateRoomRewards(this.currentDungeon.difficulty, false); - - // Apply time bonus - const timeBonus = Math.floor(completionTime < 300000 ? 50 : 0); // Bonus for completing in under 5 minutes - baseRewards.credits += timeBonus; - baseRewards.experience += Math.floor(timeBonus / 2); - - // Give rewards - economy.giveRewards(baseRewards, 'dungeon'); - player.addExperience(baseRewards.experience); - - // Update statistics - this.stats.dungeonsCompleted++; - this.stats.totalTimeInDungeons += completionTime; - - // Update player dungeons cleared stat - player.stats.dungeonsCleared++; - - // Update quest progress for dungeon objectives - if (this.game.systems.questSystem) { - // Check if this is a tutorial dungeon - if (this.currentDungeon.type === 'tutorial') { - // Mark tutorial dungeon as completed in player stats - player.stats.tutorialDungeonCompleted = true; - // Update tutorial dungeon quest progress - this.game.systems.questSystem.updateTutorialDungeonProgress(); - } else { - // Update regular dungeon quest progress using the standard method - this.game.systems.questSystem.onDungeonCompleted(); - } - } - - // Show completion message - this.game.showNotification(`Dungeon completed! Time: ${this.game.formatTime(completionTime)}`, 'success', 5000); - if (timeBonus > 0) { - this.game.showNotification(`Time bonus: +${timeBonus} credits!`, 'info', 3000); - } - - // Reset dungeon state - this.currentDungeon = null; - this.currentRoom = null; - this.isExploring = false; - - // Return to dungeon list view - this.showDungeonList(); - this.generateDungeonList(); - - // Only force UI refresh if in multiplayer mode or game is actively running - if (this.game.shouldUpdateGUI()) { - this.updateUI(); - } - } - - showDungeonList() { - const dungeonListElement = document.getElementById('dungeonList'); - const dungeonViewElement = document.getElementById('dungeonView'); - - if (dungeonListElement) { - dungeonListElement.style.display = 'flex'; - } - - if (dungeonViewElement) { - dungeonViewElement.style.display = 'none'; - // Clear the dungeon view content to prevent frozen display - dungeonViewElement.innerHTML = ''; - } - } - - showDungeonView() { - - const dungeonListElement = document.getElementById('dungeonList'); - const dungeonViewElement = document.getElementById('dungeonView'); - - - if (dungeonListElement) { - dungeonListElement.style.display = 'none'; - } - - if (dungeonViewElement) { - dungeonViewElement.style.display = 'flex'; - } - - - // Display the current dungeon - this.displayDungeon(); - - } - - // UI updates - updateUI() { - // Update dungeon statistics if elements exist - const dungeonsClearedElement = document.getElementById('dungeonsCleared'); - if (dungeonsClearedElement) { - dungeonsClearedElement.textContent = this.stats.dungeonsCompleted; - } - } - - // Save/Load - save() { - return { - stats: this.stats, - currentDungeon: this.currentDungeon, - currentRoom: this.currentRoom, - dungeonProgress: this.dungeonProgress - }; - } - - load(data) { - if (data.stats) this.stats = { ...this.stats, ...data.stats }; - if (data.currentDungeon) this.currentDungeon = data.currentDungeon; - if (data.currentRoom) this.currentRoom = data.currentRoom; - if (data.dungeonProgress !== undefined) this.dungeonProgress = data.dungeonProgress; - } -} diff --git a/Client-Server/js/systems/SkillSystem.js b/Client-Server/js/systems/SkillSystem.js deleted file mode 100644 index 1148052..0000000 --- a/Client-Server/js/systems/SkillSystem.js +++ /dev/null @@ -1,596 +0,0 @@ -/** - * Galaxy Strike Online - Skill System - * Manages skills, progression, and specialization - */ - -class SkillSystem { - constructor(gameEngine) { - this.game = gameEngine; - - // Skill categories - this.categories = { - combat: 'Combat', - science: 'Science', - crafting: 'Crafting' - }; - - // Skill definitions - this.skills = { - combat: { - weapons_mastery: { - name: 'Weapons Mastery', - description: 'Increases weapon damage and critical chance', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - attack: 2, - criticalChance: 0.01 - }, - icon: 'fa-sword', - unlocked: true - }, - shield_techniques: { - name: 'Shield Techniques', - description: 'Improves defense and energy efficiency', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - defense: 2, - maxEnergy: 5 - }, - icon: 'fa-shield-alt', - unlocked: true - }, - piloting: { - name: 'Piloting', - description: 'Enhances speed and evasion', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - speed: 2, - criticalChance: 0.005 - }, - icon: 'fa-rocket', - unlocked: true - }, - tactical_analysis: { - name: 'Tactical Analysis', - description: 'Reveals enemy weaknesses and improves accuracy', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - criticalDamage: 0.05, - attack: 1 - }, - icon: 'fa-brain', - unlocked: false, - requiredLevel: 5 - } - }, - science: { - engineering: { - name: 'Engineering', - description: 'Technical skills for ship components and machinery', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - craftingBonus: 0.08, - shipStats: 0.03 - }, - icon: 'fa-wrench', - unlocked: true - }, - energy_manipulation: { - name: 'Energy Manipulation', - description: 'Better energy control and regeneration', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - maxEnergy: 10, - energyRegeneration: 0.1 - }, - icon: 'fa-bolt', - unlocked: true - }, - alien_technology: { - name: 'Alien Technology', - description: 'Understanding and using alien artifacts', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - findRarity: 0.05, - itemValue: 0.1 - }, - icon: 'fa-atom', - unlocked: false, - requiredLevel: 3 - }, - quantum_physics: { - name: 'Quantum Physics', - description: 'Advanced quantum mechanics for better equipment', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - criticalDamage: 0.1, - attack: 3 - }, - icon: 'fa-microscope', - unlocked: false, - requiredLevel: 8 - }, - bio_engineering: { - name: 'Bio-Engineering', - description: 'Biological enhancements and healing', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - maxHealth: 15, - healthRegeneration: 0.05 - }, - icon: 'fa-dna', - unlocked: false, - requiredLevel: 6 - } - }, - crafting: { - crafting: { - name: 'General Crafting', - description: 'Basic crafting skills for all items', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - craftingBonus: 0.05 - }, - icon: 'fa-hammer', - unlocked: true - }, - weapon_crafting: { - name: 'Weapon Crafting', - description: 'Create and upgrade weapons', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - craftingBonus: 0.1, - weaponStats: 0.05 - }, - icon: 'fa-hammer', - unlocked: true - }, - armor_forging: { - name: 'Armor Forging', - description: 'Forge protective armor and shields', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - craftingBonus: 0.1, - armorStats: 0.05 - }, - icon: 'fa-anvil', - unlocked: true - }, - resource_extraction: { - name: 'Resource Extraction', - description: 'Better resource gathering and efficiency', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - resourceBonus: 0.15, - findResources: 0.1 - }, - icon: 'fa-gem', - unlocked: false, - requiredLevel: 4 - }, - engineering: { - name: 'Engineering', - description: 'Advanced ship modifications and systems', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - shipUpgrades: 0.2, - systemEfficiency: 0.1 - }, - icon: 'fa-cogs', - unlocked: false, - requiredLevel: 7 - } - } - }; - - // Skill experience rates - this.experienceRates = { - combat: 1.0, - science: 0.8, - crafting: 0.6 - }; - - // Active buffs from skills - this.activeBuffs = {}; - } - - async initialize() { -} - - // Skill management - addSkillExperience(category, skillId, amount) { - const skill = this.skills[category]?.[skillId]; - if (!skill || skill.currentLevel >= skill.maxLevel) { - return false; - } - - skill.experience += amount; - - // Check for level up - while (skill.experience >= skill.experienceToNext && skill.currentLevel < skill.maxLevel) { - this.levelUpSkill(category, skillId); - } - - this.applySkillEffects(); -return true; - } - - levelUpSkill(category, skillId) { - const skill = this.skills[category][skillId]; - - // Handle excess experience - const excessExperience = skill.experience - skill.experienceToNext; - - skill.currentLevel++; - skill.experienceToNext = Math.floor(skill.experienceToNext * 1.5); - - // Set experience to excess (minimum 0) - skill.experience = Math.max(0, excessExperience); - - // Apply skill effects - this.applySkillEffects(); - - this.game.showNotification(`${skill.name} leveled up to ${skill.currentLevel}!`, 'success', 4000); - this.game.showNotification('Skill effects applied!', 'info', 3000); - } - - upgradeSkill(category, skillId) { - const skill = this.skills[category]?.[skillId]; - const player = this.game.systems.player; - - if (!skill) { - this.game.showNotification('Skill not found', 'error', 3000); - return false; - } - - if (!skill.unlocked) { - this.game.showNotification('Skill is locked', 'error', 3000); - return false; - } - - if (skill.currentLevel >= skill.maxLevel) { - this.game.showNotification('Skill is at maximum level', 'warning', 3000); - return false; - } - - if (player.stats.skillPoints < 1) { - this.game.showNotification('Not enough skill points', 'error', 3000); - return false; - } - - // Use skill point and level up - player.stats.skillPoints--; - this.levelUpSkill(category, skillId); - - // Update UI to refresh skill points display only if in multiplayer mode or game is actively running - const shouldUpdateUI = window.smartSaveManager?.isMultiplayer || this.game?.isRunning; - - if (shouldUpdateUI) { - this.updateUI(); - } - - return true; - } - - unlockSkill(category, skillId) { - const skill = this.skills[category]?.[skillId]; - const player = this.game.systems.player; - - if (!skill) { - this.game.showNotification('Skill not found', 'error', 3000); - return false; - } - - if (skill.unlocked) { - this.game.showNotification('Skill is already unlocked', 'warning', 3000); - return false; - } - - if (skill.requiredLevel && player.stats.level < skill.requiredLevel) { - this.game.showNotification(`Requires level ${skill.requiredLevel}`, 'error', 3000); - return false; - } - - if (player.stats.skillPoints < 2) { - this.game.showNotification('Requires 2 skill points to unlock', 'error', 3000); - return false; - } - - // Unlock skill - player.stats.skillPoints -= 2; - skill.unlocked = true; - skill.currentLevel = 1; - - this.applySkillEffects(); - - // Update UI to refresh skill points display only if in multiplayer mode or game is actively running - const shouldUpdateUI = window.smartSaveManager?.isMultiplayer || this.game?.isRunning; - - if (shouldUpdateUI) { - this.updateUI(); - } - - this.game.showNotification(`${skill.name} unlocked!`, 'success', 4000); - return true; - } - - applySkillEffects() { - const player = this.game.systems.player; - - // Reset to base stats first - this.resetToBaseStats(); - - // Apply all skill effects - Object.values(this.skills).forEach(skill => { - if (skill.level > 0) { - const skillData = this.skillData[skill.id]; - if (skillData && skillData.effects) { - Object.entries(skillData.effects).forEach(([effect, value]) => { - const totalEffect = value * skill.level; - - switch (effect) { - case 'attack': - player.attributes.attack += totalEffect; - break; - case 'defense': - player.attributes.defense += totalEffect; - break; - case 'speed': - player.attributes.speed += totalEffect; - break; - case 'maxHealth': - player.attributes.maxHealth += totalEffect; - break; - case 'maxEnergy': - player.attributes.maxEnergy += totalEffect; - break; - case 'criticalChance': - player.attributes.criticalChance += totalEffect; - break; - case 'criticalDamage': - player.attributes.criticalDamage += totalEffect; - break; - case 'energyRegeneration': - case 'healthRegeneration': - case 'craftingBonus': - case 'weaponStats': - case 'armorStats': - case 'resourceBonus': - case 'shipUpgrades': - case 'systemEfficiency': - case 'findRarity': - case 'itemValue': - case 'findResources': - // Store these for other systems to use - if (!this.activeBuffs[effect]) { - this.activeBuffs[effect] = 0; - } - this.activeBuffs[effect] += totalEffect; - break; - } - }); - } - } - }); - - player.updateUI(); - } - - resetToBaseStats() { - const player = this.game.systems.player; - - // Reset to base values (would need to store base stats separately) - // For now, we'll use initial values - const baseStats = { - attack: 10 + (player.stats.level - 1) * 2, - defense: 5 + (player.stats.level - 1) * 1, - speed: 10, - maxHealth: 100 + (player.stats.level - 1) * 10, - maxEnergy: 100 + (player.stats.level - 1) * 5, - criticalChance: 0.05, - criticalDamage: 1.5 - }; - - Object.assign(player.attributes, baseStats); - this.activeBuffs = {}; - } - - // Skill experience from actions - awardCombatExperience(amount) { - this.addSkillExperience('combat', 'weapons_mastery', amount); - this.addSkillExperience('combat', 'tactical_analysis', amount * 0.5); - } - - awardScienceExperience(amount) { - this.addSkillExperience('science', 'energy_manipulation', amount); - this.addSkillExperience('science', 'alien_technology', amount * 0.3); - } - - awardCraftingExperience(amount) { - this.addSkillExperience('crafting', 'weapon_crafting', amount); - this.addSkillExperience('crafting', 'armor_forging', amount * 0.5); - } - - // Skill checks - getSkillLevel(category, skillId) { - return this.skills[category]?.[skillId]?.currentLevel || 0; - } - - hasSkill(category, skillId, minimumLevel = 1) { - const skill = this.skills[category]?.[skillId]; - return skill && skill.unlocked && skill.currentLevel >= minimumLevel; - } - - getSkillBonus(effect) { - return this.activeBuffs[effect] || 0; - } - - // UI updates - updateUI() { - this.updateSkillsGrid(); - this.updateSkillPointsDisplay(); - } - - updateSkillsGrid() { - const skillsGridElement = document.getElementById('skillsGrid'); - if (!skillsGridElement) return; - - const activeCategory = document.querySelector('.skill-cat-btn.active')?.dataset.category || 'combat'; - const skills = this.skills[activeCategory] || {}; - - skillsGridElement.innerHTML = ''; - - Object.entries(skills).forEach(([skillId, skill]) => { - const skillElement = document.createElement('div'); - skillElement.className = `skill-item ${!skill.unlocked ? 'locked' : ''}`; - - const progressPercent = skill.currentLevel > 0 ? - (skill.experience / skill.experienceToNext) * 100 : 0; - - // Use texture manager for icon fallback - const iconClass = this.game.systems.textureManager ? - this.game.systems.textureManager.getIcon(skill.icon) : - (skill.icon || 'fa-question'); - - skillElement.innerHTML = ` -
-
- -
-
-
${skill.name}
-
Lv. ${skill.currentLevel}/${skill.maxLevel}
-
-
-
${skill.description}
- ${skill.currentLevel > 0 && skill.currentLevel < skill.maxLevel ? ` -
-
-
-
- ${skill.experience}/${skill.experienceToNext} XP -
- ` : skill.currentLevel >= skill.maxLevel ? ` -
- MAX LEVEL -
- ` : ''} -
- ${!skill.unlocked ? ` - - ` : skill.currentLevel < skill.maxLevel ? ` - - ` : ` - MAX LEVEL - `} -
- ${skill.requiredLevel && !skill.unlocked ? ` -
Requires Level ${skill.requiredLevel}
- ` : ''} - `; - - skillsGridElement.appendChild(skillElement); - }); - } - - updateSkillPointsDisplay() { - const player = this.game.systems.player; - // Update skill points display if element exists - const skillPointsElements = document.querySelectorAll('.skill-points'); - skillPointsElements.forEach(element => { - element.textContent = `Skill Points: ${player.stats.skillPoints}`; - }); - } - - // Save/Load - save() { - return { - skills: this.skills, - activeBuffs: this.activeBuffs - }; - } - - load(data) { - if (data.skills) { - // Deep merge to preserve structure - for (const [category, skills] of Object.entries(data.skills)) { - if (this.skills[category]) { - for (const [skillId, skillData] of Object.entries(skills)) { - if (this.skills[category][skillId]) { - Object.assign(this.skills[category][skillId], skillData); - } - } - } - } - } - - if (data.activeBuffs) { - this.activeBuffs = data.activeBuffs; - } - - this.applySkillEffects(); -} - -reset() { - this.skillPoints = 0; - this.unlockedSkills = []; - this.activeBuffs = []; - // Skills are already defined in constructor, just reset levels - Object.values(this.skills).forEach(category => { - Object.values(category).forEach(skill => { - skill.currentLevel = 0; - skill.experience = 0; - }); - }); -} - -clear() { - this.reset(); -} -} diff --git a/Client/data/starbase-layout.json b/Client/data/starbase-layout.json new file mode 100644 index 0000000..955519e --- /dev/null +++ b/Client/data/starbase-layout.json @@ -0,0 +1,120 @@ +{ + "_readme": [ + "Galaxy Strike Online — Starbase World Layout", + "Edit this file to customize your starbase. Changes take effect next time you visit the Starbases tab.", + "", + "GRID", + " cols / rows : overall size of the world (min 8×8, max ~32×26 before performance drops)", + "", + "STYLE (global defaults — all optional hex strings)", + " wallColor / wallColorLeft / wallColorRight / wallColorTop", + " floorColorEven / floorColorOdd", + " doorColor / doorFrameColor", + "", + "WALLS — each entry draws a run of wall tiles", + " col, row : start position", + " span : number of tiles (default 1)", + " dir : 'h' horizontal | 'v' vertical", + " color / colorLeft / colorRight / colorTop : per-segment color overrides", + "", + "DOORS — walkable openings; panel slides up when player is adjacent", + " col, row : position", + " color : panel color override", + " frameColor : pillar/frame color override", + "", + "ROOMS — named regions; rendered as ghost labels + used for per-room wallpapers", + " id : unique string (used by the unlock / wallpaper system)", + " label : display text", + " bounds : { col, row, cols, rows }", + " unlock : item id required to unlock this room (omit = always open)", + " Locked rooms are filled with sealed-wall tiles and shown as 'LOCKED'", + "", + "PLAYER START", + " col, row : spawn tile (must be walkable floor)" + ], + + "name": "Starbase Alpha-7", + + "grid": { "cols": 26, "rows": 20 }, + + "style": { + "wallColor": "#00d4ff", + "wallColorLeft": "#0c1626", + "wallColorRight": "#0a1220", + "wallColorTop": "#1a2840", + "floorColorEven": "#151c2e", + "floorColorOdd": "#111827", + "doorColor": "#00ffcc", + "doorFrameColor": "#00d4ff" + }, + + "walls": [ + { "col": 0, "row": 0, "span": 26, "dir": "h", "_": "top wall" }, + { "col": 0, "row": 19, "span": 26, "dir": "h", "_": "bottom wall" }, + { "col": 0, "row": 0, "span": 20, "dir": "v", "_": "left wall" }, + { "col": 25, "row": 0, "span": 20, "dir": "v", "_": "right wall" }, + + { "col": 1, "row": 6, "span": 24, "dir": "h", "_": "main hall separator", + "color": "#00d4ff", "colorLeft": "#0d1a2e", "colorRight": "#0a1525", "colorTop": "#182a42" }, + + { "col": 7, "row": 7, "span": 13, "dir": "v", "_": "left inner wall" }, + { "col": 17, "row": 7, "span": 2, "dir": "v", "_": "right stub top" }, + { "col": 17, "row": 10, "span": 10, "dir": "v", "_": "right stub bottom", + "color": "#4488ff", "colorLeft": "#0a1830", "colorRight": "#080e20", "colorTop": "#102040" }, + + { "col": 7, "row": 13, "span": 10, "dir": "h", "_": "operations divider", + "color": "#ff00ff", "colorLeft": "#1a0a20", "colorRight": "#120616", "colorTop": "#200a30" }, + + { "col": 17, "row": 13, "span": 6, "dir": "h", "_": "vault corridor wall", + "color": "#ffcc00", "colorLeft": "#1a1200", "colorRight": "#140e00", "colorTop": "#221800" } + ], + + "doors": [ + { "col": 13, "row": 6, "dir": "h", "_": "main hall → command centre" }, + { "col": 17, "row": 6, "dir": "h", "color": "#4488ff", "frameColor": "#0066ff", "_": "main hall → right wing" }, + { "col": 7, "row": 10, "dir": "v", "_": "left wing ↔ command centre" }, + { "col": 17, "row": 9, "dir": "v", "color": "#ff88ff", "frameColor": "#cc44cc", "_": "command centre ↔ right wing" }, + { "col": 7, "row": 15, "dir": "v", "_": "left wing → operations" }, + { "col": 13, "row": 13, "dir": "h", "color": "#ff00ff", "frameColor": "#cc00cc", "_": "command centre → operations" }, + { "col": 20, "row": 13, "dir": "h", "color": "#ffcc00", "frameColor": "#ddaa00", "_": "right wing → vault corridor" } + ], + + "rooms": [ + { + "id": "main_hall", + "label": "Main Hall", + "bounds": { "col": 1, "row": 1, "cols": 24, "rows": 5 } + }, + { + "id": "left_wing", + "label": "Armory Wing", + "bounds": { "col": 1, "row": 7, "cols": 6, "rows": 12 }, + "unlock": "room_armory" + }, + { + "id": "command_centre", + "label": "Command Centre", + "bounds": { "col": 8, "row": 7, "cols": 9, "rows": 6 } + }, + { + "id": "right_wing", + "label": "Research Lab", + "bounds": { "col": 18, "row": 7, "cols": 7, "rows": 6 }, + "unlock": "room_research_lab" + }, + { + "id": "operations", + "label": "Operations Centre", + "bounds": { "col": 8, "row": 14, "cols": 9, "rows": 5 }, + "unlock": "room_operations" + }, + { + "id": "commanders_vault", + "label": "Commander's Vault", + "bounds": { "col": 18, "row": 14, "cols": 7, "rows": 5 }, + "unlock": "room_vault" + } + ], + + "playerStart": { "col": 13, "row": 3 } +} diff --git a/Client/index.html b/Client/index.html index ac08ce0..3963f18 100644 --- a/Client/index.html +++ b/Client/index.html @@ -9,7 +9,6 @@ - @@ -297,6 +296,22 @@ Shop + + + + @@ -419,35 +434,16 @@
-
-
-
-

Base Information

-
-
- Power Usage: - 0/100 -
-
- Storage: - 1000 -
-
- Production Rate: - 0/s -
-
-
-
- -
-
-
-

Base Upgrades

-
- -
-
+
+

Your Starbase

+ +
+
+
Loading base…
+
+
+

Available to Build

+
@@ -522,23 +518,23 @@
- + @@ -669,6 +665,222 @@ + + + + +
+
+

Fleet Management

+
+
+
+

Loading fleet…

+
+
+ + +
+
+ + + +
+
+
+ +
+
+
+

Select a Sector

+
+ Click any visible sector on the map to view details. +
+
+
+

Legend

+
Empty Space
+
Asteroid Field
+
Trade Hub
+
NPC Territory
+
Ruins
+
Void Rift
+
Your Home
+
+
+
+
+ + +
+ +
+
+

Branches

+ + + + + + +
+
+
Loading research…
+
+
+
+ + +
+ +

🏆 Commander Rankings

+
+ + + + + +
+
+
Select a category above to view rankings.
+
+
+ - `; + `; } - + updateCraftingInfo() { const skillSystem = this.game.systems.skillSystem; if (!skillSystem) return; - + const craftingLevel = skillSystem.getSkillLevel('crafting'); - const craftingExp = skillSystem.getSkillExperience('crafting'); - const expNeeded = skillSystem.getExperienceNeeded('crafting'); - - const levelElement = document.getElementById('craftingLevel'); - const expElement = document.getElementById('craftingExp'); - - if (levelElement) levelElement.textContent = craftingLevel; - if (expElement) expElement.textContent = `${craftingExp}/${expNeeded}`; + const craftingExp = skillSystem.getSkillExperience('crafting'); + const expNeeded = skillSystem.getExperienceNeeded('crafting'); + + const levelEl = document.getElementById('craftingLevel'); + const expEl = document.getElementById('craftingExp'); + if (levelEl) levelEl.textContent = craftingLevel; + if (expEl) expEl.textContent = `${craftingExp}/${expNeeded}`; } - + switchCategory(category) { this.currentCategory = category; - this.selectedRecipe = null; - - // Update UI only if in multiplayer mode or game is actively running - const shouldUpdateUI = window.smartSaveManager?.isMultiplayer || this.game?.isRunning; - - if (shouldUpdateUI) { + this.selectedRecipe = null; + if (window.smartSaveManager?.isMultiplayer || this.game?.isRunning) { this.updateUI(); } } diff --git a/Client/js/systems/ItemSystem.js b/Client/js/systems/ItemSystem.js index 53af214..6d8ae06 100644 --- a/Client/js/systems/ItemSystem.js +++ b/Client/js/systems/ItemSystem.js @@ -430,6 +430,13 @@ class ItemSystem { } } + /** + * catalog getter — alias for shopItemsByCategory, used by Economy.updateShopUI + */ + get catalog() { + return this.shopItemsByCategory; + } + /** * Get system statistics */ diff --git a/Client/js/systems/SkillSystem.js b/Client/js/systems/SkillSystem.js index 1148052..cdce8a8 100644 --- a/Client/js/systems/SkillSystem.js +++ b/Client/js/systems/SkillSystem.js @@ -1,504 +1,330 @@ /** - * Galaxy Strike Online - Skill System - * Manages skills, progression, and specialization + * Galaxy Strike Online - Client Skill System + * Skill definitions are loaded from the server; this file handles + * local progression tracking, UI rendering, and skill-point spending. */ class SkillSystem { constructor(gameEngine) { this.game = gameEngine; - - // Skill categories + + // Populated after server responds to 'get_skills' + this.skills = { combat: {}, science: {}, crafting: {} }; + this.categories = { - combat: 'Combat', - science: 'Science', + combat: 'Combat', + science: 'Science', crafting: 'Crafting' }; - - // Skill definitions - this.skills = { - combat: { - weapons_mastery: { - name: 'Weapons Mastery', - description: 'Increases weapon damage and critical chance', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - attack: 2, - criticalChance: 0.01 - }, - icon: 'fa-sword', - unlocked: true - }, - shield_techniques: { - name: 'Shield Techniques', - description: 'Improves defense and energy efficiency', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - defense: 2, - maxEnergy: 5 - }, - icon: 'fa-shield-alt', - unlocked: true - }, - piloting: { - name: 'Piloting', - description: 'Enhances speed and evasion', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - speed: 2, - criticalChance: 0.005 - }, - icon: 'fa-rocket', - unlocked: true - }, - tactical_analysis: { - name: 'Tactical Analysis', - description: 'Reveals enemy weaknesses and improves accuracy', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - criticalDamage: 0.05, - attack: 1 - }, - icon: 'fa-brain', - unlocked: false, - requiredLevel: 5 - } - }, - science: { - engineering: { - name: 'Engineering', - description: 'Technical skills for ship components and machinery', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - craftingBonus: 0.08, - shipStats: 0.03 - }, - icon: 'fa-wrench', - unlocked: true - }, - energy_manipulation: { - name: 'Energy Manipulation', - description: 'Better energy control and regeneration', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - maxEnergy: 10, - energyRegeneration: 0.1 - }, - icon: 'fa-bolt', - unlocked: true - }, - alien_technology: { - name: 'Alien Technology', - description: 'Understanding and using alien artifacts', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - findRarity: 0.05, - itemValue: 0.1 - }, - icon: 'fa-atom', - unlocked: false, - requiredLevel: 3 - }, - quantum_physics: { - name: 'Quantum Physics', - description: 'Advanced quantum mechanics for better equipment', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - criticalDamage: 0.1, - attack: 3 - }, - icon: 'fa-microscope', - unlocked: false, - requiredLevel: 8 - }, - bio_engineering: { - name: 'Bio-Engineering', - description: 'Biological enhancements and healing', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - maxHealth: 15, - healthRegeneration: 0.05 - }, - icon: 'fa-dna', - unlocked: false, - requiredLevel: 6 - } - }, - crafting: { - crafting: { - name: 'General Crafting', - description: 'Basic crafting skills for all items', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - craftingBonus: 0.05 - }, - icon: 'fa-hammer', - unlocked: true - }, - weapon_crafting: { - name: 'Weapon Crafting', - description: 'Create and upgrade weapons', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - craftingBonus: 0.1, - weaponStats: 0.05 - }, - icon: 'fa-hammer', - unlocked: true - }, - armor_forging: { - name: 'Armor Forging', - description: 'Forge protective armor and shields', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - craftingBonus: 0.1, - armorStats: 0.05 - }, - icon: 'fa-anvil', - unlocked: true - }, - resource_extraction: { - name: 'Resource Extraction', - description: 'Better resource gathering and efficiency', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - resourceBonus: 0.15, - findResources: 0.1 - }, - icon: 'fa-gem', - unlocked: false, - requiredLevel: 4 - }, - engineering: { - name: 'Engineering', - description: 'Advanced ship modifications and systems', - maxLevel: 10, - currentLevel: 0, - experience: 0, - experienceToNext: 100, - effects: { - shipUpgrades: 0.2, - systemEfficiency: 0.1 - }, - icon: 'fa-cogs', - unlocked: false, - requiredLevel: 7 - } - } - }; - - // Skill experience rates - this.experienceRates = { - combat: 1.0, - science: 0.8, - crafting: 0.6 - }; - - // Active buffs from skills + + this.experienceRates = { combat: 1.0, science: 0.8, crafting: 0.6 }; this.activeBuffs = {}; + + // Loading state + this._loaded = false; + this._loading = false; } - + + // ------------------------------------------------------------------ // + // Initialisation — request skill definitions from the server + // ------------------------------------------------------------------ // async initialize() { -} - - // Skill management + if (this._loaded || this._loading) return; + this._loading = true; + + console.log('[SKILL SYSTEM] Requesting skill definitions from server'); + + if (!window.game?.socket) { + console.warn('[SKILL SYSTEM] No socket connection — skills will load when connected'); + this._loading = false; + return; + } + + try { + const serverSkills = await this._fetchSkillsFromServer(); + this._applyServerDefinitions(serverSkills); + this._loaded = true; + console.log('[SKILL SYSTEM] Skill definitions loaded from server'); + } catch (err) { + console.error('[SKILL SYSTEM] Failed to load skills from server:', err); + } finally { + this._loading = false; + } + } + + _fetchSkillsFromServer() { + return new Promise((resolve, reject) => { + const socket = window.game.socket; + + const timeout = setTimeout(() => { + socket.off('skills_data', handler); + reject(new Error('Skill data request timed out')); + }, 10000); + + const handler = (data) => { + clearTimeout(timeout); + socket.off('skills_data', handler); + if (data && (Array.isArray(data) || typeof data === 'object')) { + resolve(data); + } else { + reject(new Error('Invalid skill data from server')); + } + }; + + socket.on('skills_data', handler); + socket.emit('get_skills'); + }); + } + + /** + * Merge server skill definitions into the local skill map. + * Preserves any progress already loaded from playerData. + */ + _applyServerDefinitions(serverSkills) { + // Server may return an array or a categorised object + const asList = Array.isArray(serverSkills) + ? serverSkills + : Object.values(serverSkills).flat(); + + // Reset to empty categories first + this.skills = { combat: {}, science: {}, crafting: {} }; + + for (const skill of asList) { + const cat = skill.category || 'combat'; + if (!this.skills[cat]) this.skills[cat] = {}; + + // Keep any existing progress if already loaded from save data + const existing = this.skills[cat][skill.id] || {}; + this.skills[cat][skill.id] = { + name: skill.name, + description: skill.description, + maxLevel: skill.maxLevel || 100, + effects: skill.bonuses || skill.effects || {}, + icon: skill.icon || 'fa-star', + // Progress fields — kept from existing save data if present + currentLevel: existing.currentLevel ?? 0, + experience: existing.experience ?? 0, + experienceToNext: existing.experienceToNext ?? (skill.experiencePerLevel || 1000), + unlocked: existing.unlocked ?? (skill.defaultUnlocked !== false), + requiredLevel: skill.requiredLevel ?? null + }; + } + + console.log('[SKILL SYSTEM] Applied server definitions. Categories:', Object.keys(this.skills)); + } + + // ------------------------------------------------------------------ // + // Skill progression + // ------------------------------------------------------------------ // addSkillExperience(category, skillId, amount) { const skill = this.skills[category]?.[skillId]; - if (!skill || skill.currentLevel >= skill.maxLevel) { - return false; - } - + if (!skill || skill.currentLevel >= skill.maxLevel) return false; + skill.experience += amount; - - // Check for level up + while (skill.experience >= skill.experienceToNext && skill.currentLevel < skill.maxLevel) { this.levelUpSkill(category, skillId); } - + this.applySkillEffects(); -return true; - } - - levelUpSkill(category, skillId) { - const skill = this.skills[category][skillId]; - - // Handle excess experience - const excessExperience = skill.experience - skill.experienceToNext; - - skill.currentLevel++; - skill.experienceToNext = Math.floor(skill.experienceToNext * 1.5); - - // Set experience to excess (minimum 0) - skill.experience = Math.max(0, excessExperience); - - // Apply skill effects - this.applySkillEffects(); - - this.game.showNotification(`${skill.name} leveled up to ${skill.currentLevel}!`, 'success', 4000); - this.game.showNotification('Skill effects applied!', 'info', 3000); - } - - upgradeSkill(category, skillId) { - const skill = this.skills[category]?.[skillId]; - const player = this.game.systems.player; - - if (!skill) { - this.game.showNotification('Skill not found', 'error', 3000); - return false; - } - - if (!skill.unlocked) { - this.game.showNotification('Skill is locked', 'error', 3000); - return false; - } - - if (skill.currentLevel >= skill.maxLevel) { - this.game.showNotification('Skill is at maximum level', 'warning', 3000); - return false; - } - - if (player.stats.skillPoints < 1) { - this.game.showNotification('Not enough skill points', 'error', 3000); - return false; - } - - // Use skill point and level up - player.stats.skillPoints--; - this.levelUpSkill(category, skillId); - - // Update UI to refresh skill points display only if in multiplayer mode or game is actively running - const shouldUpdateUI = window.smartSaveManager?.isMultiplayer || this.game?.isRunning; - - if (shouldUpdateUI) { - this.updateUI(); - } - return true; } - - unlockSkill(category, skillId) { - const skill = this.skills[category]?.[skillId]; + + levelUpSkill(category, skillId) { + const skill = this.skills[category][skillId]; + const excess = skill.experience - skill.experienceToNext; + + skill.currentLevel++; + skill.experienceToNext = Math.floor(skill.experienceToNext * 1.5); + skill.experience = Math.max(0, excess); + + this.applySkillEffects(); + this.game.showNotification(`${skill.name} leveled up to ${skill.currentLevel}!`, 'success', 4000); + } + + upgradeSkill(category, skillId) { + const skill = this.skills[category]?.[skillId]; const player = this.game.systems.player; - - if (!skill) { - this.game.showNotification('Skill not found', 'error', 3000); - return false; + + if (!skill) { this.game.showNotification('Skill not found', 'error', 3000); return false; } + if (!skill.unlocked) { this.game.showNotification('Skill is locked', 'error', 3000); return false; } + if (skill.currentLevel >= skill.maxLevel){ this.game.showNotification('Skill is at maximum level', 'warning', 3000); return false; } + if (player.stats.skillPoints < 1) { this.game.showNotification('Not enough skill points', 'error', 3000); return false; } + + player.stats.skillPoints--; + this.levelUpSkill(category, skillId); + + if (window.smartSaveManager?.isMultiplayer || this.game?.isRunning) { + this.updateUI(); } - - if (skill.unlocked) { - this.game.showNotification('Skill is already unlocked', 'warning', 3000); - return false; - } - + return true; + } + + unlockSkill(category, skillId) { + const skill = this.skills[category]?.[skillId]; + const player = this.game.systems.player; + + if (!skill) { this.game.showNotification('Skill not found', 'error', 3000); return false; } + if (skill.unlocked) { this.game.showNotification('Skill is already unlocked', 'warning', 3000); return false; } if (skill.requiredLevel && player.stats.level < skill.requiredLevel) { this.game.showNotification(`Requires level ${skill.requiredLevel}`, 'error', 3000); return false; } - if (player.stats.skillPoints < 2) { this.game.showNotification('Requires 2 skill points to unlock', 'error', 3000); return false; } - - // Unlock skill + player.stats.skillPoints -= 2; skill.unlocked = true; skill.currentLevel = 1; - this.applySkillEffects(); - - // Update UI to refresh skill points display only if in multiplayer mode or game is actively running - const shouldUpdateUI = window.smartSaveManager?.isMultiplayer || this.game?.isRunning; - - if (shouldUpdateUI) { + + if (window.smartSaveManager?.isMultiplayer || this.game?.isRunning) { this.updateUI(); } - this.game.showNotification(`${skill.name} unlocked!`, 'success', 4000); return true; } - + applySkillEffects() { const player = this.game.systems.player; - - // Reset to base stats first this.resetToBaseStats(); - - // Apply all skill effects - Object.values(this.skills).forEach(skill => { - if (skill.level > 0) { - const skillData = this.skillData[skill.id]; - if (skillData && skillData.effects) { - Object.entries(skillData.effects).forEach(([effect, value]) => { - const totalEffect = value * skill.level; - - switch (effect) { - case 'attack': - player.attributes.attack += totalEffect; - break; - case 'defense': - player.attributes.defense += totalEffect; - break; - case 'speed': - player.attributes.speed += totalEffect; - break; - case 'maxHealth': - player.attributes.maxHealth += totalEffect; - break; - case 'maxEnergy': - player.attributes.maxEnergy += totalEffect; - break; - case 'criticalChance': - player.attributes.criticalChance += totalEffect; - break; - case 'criticalDamage': - player.attributes.criticalDamage += totalEffect; - break; - case 'energyRegeneration': - case 'healthRegeneration': - case 'craftingBonus': - case 'weaponStats': - case 'armorStats': - case 'resourceBonus': - case 'shipUpgrades': - case 'systemEfficiency': - case 'findRarity': - case 'itemValue': - case 'findResources': - // Store these for other systems to use - if (!this.activeBuffs[effect]) { - this.activeBuffs[effect] = 0; - } - this.activeBuffs[effect] += totalEffect; - break; - } - }); + + for (const category of Object.values(this.skills)) { + for (const skill of Object.values(category)) { + if (!skill.unlocked || skill.currentLevel <= 0) continue; + + for (const [effect, value] of Object.entries(skill.effects || {})) { + const total = value * skill.currentLevel; + switch (effect) { + case 'attack': player.attributes.attack += total; break; + case 'defense': player.attributes.defense += total; break; + case 'speed': player.attributes.speed += total; break; + case 'health': + case 'maxHealth': player.attributes.maxHealth += total; break; + case 'maxEnergy': player.attributes.maxEnergy += total; break; + case 'criticalChance': player.attributes.criticalChance += total; break; + case 'criticalDamage': player.attributes.criticalDamage += total; break; + default: + if (!this.activeBuffs[effect]) this.activeBuffs[effect] = 0; + this.activeBuffs[effect] += total; + } } } - }); - + } + player.updateUI(); } - + resetToBaseStats() { const player = this.game.systems.player; - - // Reset to base values (would need to store base stats separately) - // For now, we'll use initial values - const baseStats = { - attack: 10 + (player.stats.level - 1) * 2, - defense: 5 + (player.stats.level - 1) * 1, - speed: 10, - maxHealth: 100 + (player.stats.level - 1) * 10, - maxEnergy: 100 + (player.stats.level - 1) * 5, + const lvl = player.stats.level || 1; + Object.assign(player.attributes, { + attack: 10 + (lvl - 1) * 2, + defense: 5 + (lvl - 1) * 1, + speed: 10, + maxHealth: 100 + (lvl - 1) * 10, + maxEnergy: 100 + (lvl - 1) * 5, criticalChance: 0.05, criticalDamage: 1.5 - }; - - Object.assign(player.attributes, baseStats); + }); this.activeBuffs = {}; } - - // Skill experience from actions + + // ------------------------------------------------------------------ // + // Combat / science / crafting XP helpers + // ------------------------------------------------------------------ // awardCombatExperience(amount) { - this.addSkillExperience('combat', 'weapons_mastery', amount); + this.addSkillExperience('combat', 'weapons_mastery', amount); this.addSkillExperience('combat', 'tactical_analysis', amount * 0.5); } - + awardScienceExperience(amount) { this.addSkillExperience('science', 'energy_manipulation', amount); - this.addSkillExperience('science', 'alien_technology', amount * 0.3); + this.addSkillExperience('science', 'alien_technology', amount * 0.3); } - + awardCraftingExperience(amount) { - this.addSkillExperience('crafting', 'weapon_crafting', amount); - this.addSkillExperience('crafting', 'armor_forging', amount * 0.5); + this.addSkillExperience('crafting', 'weapons_crafting', amount); + this.addSkillExperience('crafting', 'armor_forging', amount * 0.5); } - - // Skill checks + + // ------------------------------------------------------------------ // + // Queries + // ------------------------------------------------------------------ // getSkillLevel(category, skillId) { + // Support single-arg form used by CraftingSystem: getSkillLevel('crafting') + if (skillId === undefined) { + let max = 0; + for (const cat of Object.values(this.skills)) { + if (cat[category]) max = Math.max(max, cat[category].currentLevel || 0); + } + return max; + } return this.skills[category]?.[skillId]?.currentLevel || 0; } - + + getSkillExperience(skillId) { + for (const cat of Object.values(this.skills)) { + if (cat[skillId]) return cat[skillId].experience || 0; + } + return 0; + } + + getExperienceNeeded(skillId) { + for (const cat of Object.values(this.skills)) { + if (cat[skillId]) return cat[skillId].experienceToNext || 0; + } + return 0; + } + hasSkill(category, skillId, minimumLevel = 1) { const skill = this.skills[category]?.[skillId]; return skill && skill.unlocked && skill.currentLevel >= minimumLevel; } - + getSkillBonus(effect) { return this.activeBuffs[effect] || 0; } - - // UI updates + + // ------------------------------------------------------------------ // + // UI + // ------------------------------------------------------------------ // updateUI() { this.updateSkillsGrid(); this.updateSkillPointsDisplay(); } - + updateSkillsGrid() { - const skillsGridElement = document.getElementById('skillsGrid'); - if (!skillsGridElement) return; - + const grid = document.getElementById('skillsGrid'); + if (!grid) return; + const activeCategory = document.querySelector('.skill-cat-btn.active')?.dataset.category || 'combat'; const skills = this.skills[activeCategory] || {}; - - skillsGridElement.innerHTML = ''; - + + if (!this._loaded) { + grid.innerHTML = '

Loading skills from server...

'; + return; + } + + grid.innerHTML = ''; + Object.entries(skills).forEach(([skillId, skill]) => { - const skillElement = document.createElement('div'); - skillElement.className = `skill-item ${!skill.unlocked ? 'locked' : ''}`; - - const progressPercent = skill.currentLevel > 0 ? - (skill.experience / skill.experienceToNext) * 100 : 0; - - // Use texture manager for icon fallback - const iconClass = this.game.systems.textureManager ? - this.game.systems.textureManager.getIcon(skill.icon) : - (skill.icon || 'fa-question'); - - skillElement.innerHTML = ` + const el = document.createElement('div'); + el.className = `skill-item ${!skill.unlocked ? 'locked' : ''}`; + + const progressPercent = skill.currentLevel > 0 + ? (skill.experience / skill.experienceToNext) * 100 + : 0; + + const iconClass = this.game.systems.textureManager + ? this.game.systems.textureManager.getIcon(skill.icon) + : (skill.icon || 'fa-question'); + + el.innerHTML = `
-
- -
+
${skill.name}
Lv. ${skill.currentLevel}/${skill.maxLevel}
@@ -508,89 +334,74 @@ return true; ${skill.currentLevel > 0 && skill.currentLevel < skill.maxLevel ? `
-
+
${skill.experience}/${skill.experienceToNext} XP
` : skill.currentLevel >= skill.maxLevel ? ` -
- MAX LEVEL -
+
MAX LEVEL
` : ''}
${!skill.unlocked ? ` - ` : skill.currentLevel < skill.maxLevel ? ` - - ` : ` - MAX LEVEL - `} + ` : `MAX LEVEL`}
${skill.requiredLevel && !skill.unlocked ? `
Requires Level ${skill.requiredLevel}
` : ''} `; - - skillsGridElement.appendChild(skillElement); + + grid.appendChild(el); }); } - + updateSkillPointsDisplay() { const player = this.game.systems.player; - // Update skill points display if element exists - const skillPointsElements = document.querySelectorAll('.skill-points'); - skillPointsElements.forEach(element => { - element.textContent = `Skill Points: ${player.stats.skillPoints}`; + document.querySelectorAll('.skill-points').forEach(el => { + el.textContent = `Skill Points: ${player.stats.skillPoints}`; }); } - - // Save/Load + + // ------------------------------------------------------------------ // + // Save / Load + // ------------------------------------------------------------------ // save() { - return { - skills: this.skills, - activeBuffs: this.activeBuffs - }; + return { skills: this.skills, activeBuffs: this.activeBuffs }; } - + load(data) { if (data.skills) { - // Deep merge to preserve structure for (const [category, skills] of Object.entries(data.skills)) { - if (this.skills[category]) { - for (const [skillId, skillData] of Object.entries(skills)) { - if (this.skills[category][skillId]) { - Object.assign(this.skills[category][skillId], skillData); - } + if (!this.skills[category]) this.skills[category] = {}; + for (const [skillId, skillData] of Object.entries(skills)) { + if (this.skills[category][skillId]) { + Object.assign(this.skills[category][skillId], skillData); + } else { + // Store progress even before server definitions arrive + this.skills[category][skillId] = { ...skillData }; } } } } - - if (data.activeBuffs) { - this.activeBuffs = data.activeBuffs; - } - + if (data.activeBuffs) this.activeBuffs = data.activeBuffs; this.applySkillEffects(); -} + } -reset() { - this.skillPoints = 0; - this.unlockedSkills = []; - this.activeBuffs = []; - // Skills are already defined in constructor, just reset levels - Object.values(this.skills).forEach(category => { - Object.values(category).forEach(skill => { - skill.currentLevel = 0; - skill.experience = 0; - }); - }); -} + reset() { + this.activeBuffs = {}; + for (const category of Object.values(this.skills)) { + for (const skill of Object.values(category)) { + skill.currentLevel = 0; + skill.experience = 0; + } + } + } -clear() { - this.reset(); -} + clear() { this.reset(); } } diff --git a/Client/js/systems/StarbaseWorld.js b/Client/js/systems/StarbaseWorld.js new file mode 100644 index 0000000..539761b --- /dev/null +++ b/Client/js/systems/StarbaseWorld.js @@ -0,0 +1,1001 @@ +/** + * Galaxy Strike Online — Starbase World v3 + * Isometric Habbo-style walkable environment. + * + * Features: + * - JSON-driven grid size, walls, doors, rooms + * - Per-segment wall colour overrides + * - Animated sliding doors (open when player is adjacent) + * - Locked rooms (filled with sealed tiles, show LOCKED badge) + * → unlock via room_unlock decoration items from shop/dungeons/quests + * - Wallpapers: global or per-room, purchasable/earnable + * → active wallpaper overrides style.floorColor* + style.wall* + style.door* + * - Wallpaper selector panel (bottom toolbar) + * - Click-to-walk BFS pathfinding + * - Pixel-art astronaut with walk animation + */ + +const TILE = { VOID: 0, FLOOR: 1, WALL: 2, DOOR: 3, LOCKED: 4 }; + +class StarbaseWorld { + constructor(canvasId, layoutJson, starbasePlayerData, decorationCatalog) { + this.canvas = document.getElementById(canvasId); + this.ctx = this.canvas.getContext('2d'); + + this.TW = 64; + this.TH = 32; + + this.layout = layoutJson || StarbaseWorld.defaultLayout(); + this.COLS = this.layout.grid.cols; + this.ROWS = this.layout.grid.rows; + + // Player's starbase save data { wallpaper, ownedWallpapers, unlockedRooms, roomWallpapers } + this.sbData = starbasePlayerData || { wallpaper: null, ownedWallpapers: [], unlockedRooms: [] }; + // Full decoration catalog from server + this.catalog = decorationCatalog || []; + + this.originX = 0; + this.originY = 0; + + const ps = this.layout.playerStart || { col: Math.floor(this.COLS/2), row: 2 }; + this.player = { + col: ps.col, row: ps.row, x: 0, y: 0, + path: [], moving: false, frame: 0, frameTimer: 0, + dir: 'se', name: 'Commander' + }; + + this.tileMap = this._buildMapFromLayout(); + + this.doorStates = {}; + this.doorDefs = {}; + for (const d of (this.layout.doors || [])) { + const k = `${d.col},${d.row}`; + this.doorStates[k] = { open: false, anim: 0 }; + this.doorDefs[k] = d; + } + + this.wallDefs = {}; + for (const w of (this.layout.walls || [])) { + for (let i = 0; i < (w.span || 1); i++) { + const c = w.dir === 'h' ? w.col + i : w.col; + const r = w.dir === 'v' ? w.row + i : w.row; + this.wallDefs[`${c},${r}`] = w; + } + } + + // Which room each floor tile belongs to (for per-room wallpaper) + this.tileRoom = this._buildRoomMap(); + + this.objects = this._buildObjects(); + + this.hoveredTile = null; + this._pendingInteract = null; + this._stars = null; + this.lastTime = 0; + this.running = false; + this.animId = null; + + // Wallpaper UI state + this._wpPanelOpen = false; + this._wpSelected = null; // wallpaperId being previewed + this._wpRoomTarget= null; // roomId to apply to (null = global) + + this._recenterCamera(); + this._bindInput(); + this._snapPlayerPos(); + this._buildWallpaperPanel(); + } + + // ─── Default layout ────────────────────────────────────────────────────── + static defaultLayout() { + return { + name: 'Starbase Alpha-7', grid: { cols: 20, rows: 16 }, + style: { + wallColor: '#00d4ff', wallColorLeft: '#0c1626', + wallColorRight: '#0a1220', wallColorTop: '#1a2840', + floorColorEven: '#151c2e', floorColorOdd: '#111827', + doorColor: '#00ffcc', doorFrameColor: '#00d4ff' + }, + walls: [ + { col:0, row:0, span:20, dir:'h' }, { col:0, row:15, span:20, dir:'h' }, + { col:0, row:0, span:16, dir:'v' }, { col:19, row:0, span:16, dir:'v' }, + ], + doors: [], rooms: [], playerStart: { col:10, row:2 } + }; + } + + // ─── Map builder ───────────────────────────────────────────────────────── + _buildMapFromLayout() { + const C = this.COLS, R = this.ROWS; + const map = Array.from({ length: R }, () => Array(C).fill(TILE.FLOOR)); + + for (const w of (this.layout.walls || [])) { + for (let i = 0; i < (w.span || 1); i++) { + const c = w.dir === 'h' ? w.col + i : w.col; + const r = w.dir === 'v' ? w.row + i : w.row; + if (r >= 0 && r < R && c >= 0 && c < C) map[r][c] = TILE.WALL; + } + } + for (const d of (this.layout.doors || [])) { + if (d.row >= 0 && d.row < R && d.col >= 0 && d.col < C) + map[d.row][d.col] = TILE.DOOR; + } + + // Seal locked rooms + for (const room of (this.layout.rooms || [])) { + if (!room.unlock) continue; + const owned = this.sbData.unlockedRooms || []; + if (owned.includes(room.id)) continue; // player has unlocked it + + const b = room.bounds; + for (let r = b.row; r < b.row + b.rows; r++) { + for (let c = b.col; c < b.col + b.cols; c++) { + if (r >= 0 && r < R && c >= 0 && c < C && map[r][c] === TILE.FLOOR) + map[r][c] = TILE.LOCKED; + } + } + } + return map; + } + + _buildRoomMap() { + const C = this.COLS, R = this.ROWS; + const rm = Array.from({ length: R }, () => Array(C).fill(null)); + for (const room of (this.layout.rooms || [])) { + const b = room.bounds; + for (let r = b.row; r < b.row + b.rows; r++) + for (let c = b.col; c < b.col + b.cols; c++) + if (r >= 0 && r < R && c >= 0 && c < C) rm[r][c] = room.id; + } + return rm; + } + + // ─── Style resolver ─────────────────────────────────────────────────────── + /** + * Returns the effective style for a given tile position. + * Priority: per-room wallpaper → global wallpaper → layout.style defaults + */ + _styleFor(col, row) { + const base = this.layout.style || {}; + const globalWpId = this.sbData.wallpaper; + const roomId = this.tileRoom[row]?.[col]; + const roomWpId = roomId && this.sbData.roomWallpapers?.[roomId]; + const activeWpId = this._wpSelected || roomWpId || globalWpId; + + if (!activeWpId) return base; + const wp = this.catalog.find(i => i.id === activeWpId && i.subtype === 'wallpaper'); + if (!wp?.preview) return base; + + // Merge: preview overrides base, but let per-wall-segment defs still win later + return { ...base, ...wp.preview }; + } + + // ─── Interactive objects ────────────────────────────────────────────────── + _buildObjects() { + const sc = c => Math.min(c, this.COLS - 2); + const sr = r => Math.min(r, this.ROWS - 2); + const mk = (id, col, row, icon, label, color, glow, action) => + ({ id, col: sc(col), row: sr(row), icon, label, color, glowColor: glow, action }); + const modal = (title, icon, desc, color, btn) => + () => this._showInteractModal(title, icon, desc, color, btn ? [btn] : []); + + return [ + mk('shop', 3, 2, '🛒','Trade Post', '#00d4ff','rgba(0,212,255,.6)', + modal('Trade Post','🛒','Access the galactic marketplace to buy ships, weapons, armour, wallpapers, and room unlocks.','#00d4ff',{label:'Open Shop',action:'shop'})), + mk('crafting', 21, 2, '⚗️','Forge', '#ff8800','rgba(255,136,0,.6)', + modal('Forge','⚗️','Smelt ores, combine alloys, build circuits and hull sections.','#ff8800',{label:'Open Crafting',action:'crafting'})), + mk('quests', 3, 16, '📋','Mission Board', '#ffdd00','rgba(255,221,0,.6)', + modal('Mission Board','📋','Daily missions, weekly challenges, and story campaign.','#ffdd00',{label:'View Quests',action:'quests'})), + mk('hangar', 21, 16, '🚀','Hangar', '#aa44ff','rgba(170,68,255,.6)', + modal('Hangar','🚀','Equip your active ship and prepare for dungeon runs.','#aa44ff',{label:'View Ships',action:'ships'})), + mk('skills', 12, 3, '🎓','Academy', '#00ff88','rgba(0,255,136,.6)', + modal('Academy','🎓','Improve combat, science, and crafting proficiencies.','#00ff88',{label:'Train Skills',action:'skills'})), + mk('terminal', 12, 10, '💻','Command Terminal', '#ff00ff','rgba(255,0,255,.6)', + modal('Command Terminal','💻','Monitor stats, server status, and your empire.','#ff00ff',{label:'Base Overview',action:'base'})), + mk('wallpaper',12, 17, '🎨','Wallpaper Desk', '#ffaa00','rgba(255,170,0,.6)', + () => this._toggleWallpaperPanel()), + ]; + } + + // ─── Coordinate helpers ─────────────────────────────────────────────────── + _isoProject(col, row) { + return { + x: this.originX + (col - row) * (this.TW / 2), + y: this.originY + (col + row) * (this.TH / 2) + }; + } + _screenToGrid(sx, sy) { + const tx = sx - this.originX, ty = sy - this.originY; + return { + col: Math.floor((tx / (this.TW/2) + ty / (this.TH/2)) / 2), + row: Math.floor((ty / (this.TH/2) - tx / (this.TW/2)) / 2) + }; + } + _recenterCamera() { + this.originX = this.canvas.width / 2; + this.originY = this.canvas.height * 0.13; + } + _snapPlayerPos() { + const iso = this._isoProject(this.player.col + 0.5, this.player.row + 0.5); + this.player.x = iso.x; + this.player.y = iso.y; + } + + // ─── Pathfinding ────────────────────────────────────────────────────────── + _walkable(col, row) { + if (col < 0 || row < 0 || col >= this.COLS || row >= this.ROWS) return false; + const t = this.tileMap[row][col]; + return t === TILE.FLOOR || t === TILE.DOOR; + } + _findPath(sc, sr, tc, tr) { + if (!this._walkable(tc, tr)) return []; + const key = (c, r) => `${c},${r}`; + const q = [{ c: sc, r: sr, path: [] }]; + const vis = new Set([key(sc, sr)]); + const dirs = [[1,0],[-1,0],[0,1],[0,-1]]; + while (q.length) { + const { c, r, path } = q.shift(); + if (c === tc && r === tr) return path; + for (const [dc, dr] of dirs) { + const nc = c+dc, nr = r+dr, k = key(nc, nr); + if (!vis.has(k) && this._walkable(nc, nr)) { + vis.add(k); + q.push({ c: nc, r: nr, path: [...path, { col: nc, row: nr }] }); + } + } + } + return []; + } + _findAdjacentWalkable(col, row) { + for (const [dc, dr] of [[0,-1],[0,1],[-1,0],[1,0]]) { + const nc = col+dc, nr = row+dr; + if (this._walkable(nc, nr)) return { col: nc, row: nr }; + } + return null; + } + + // ─── Input ──────────────────────────────────────────────────────────────── + _bindInput() { + this.canvas.addEventListener('click', e => this._onClick(e)); + this.canvas.addEventListener('mousemove', e => this._onMouseMove(e)); + this.canvas.addEventListener('mouseleave',() => { this.hoveredTile = null; }); + } + _canvasPos(e) { + const r = this.canvas.getBoundingClientRect(); + return { + x: (e.clientX - r.left) * (this.canvas.width / r.width), + y: (e.clientY - r.top) * (this.canvas.height / r.height) + }; + } + _onClick(e) { + const { x, y } = this._canvasPos(e); + const { col, row } = this._screenToGrid(x, y); + if (col < 0 || row < 0 || col >= this.COLS || row >= this.ROWS) return; + + // Clicking a locked tile → show unlock info + if (this.tileMap[row][col] === TILE.LOCKED) { + const roomId = this.tileRoom[row]?.[col]; + const room = this.layout.rooms?.find(rm => rm.id === roomId); + if (room) this._showLockedRoomModal(room); + return; + } + + const obj = this.objects.find(o => o.col === col && o.row === row); + if (obj) { + const adj = this._findAdjacentWalkable(obj.col, obj.row); + if (adj) { + this.player.path = this._findPath(this.player.col, this.player.row, adj.col, adj.row); + this.player.moving = this.player.path.length > 0; + this._pendingInteract = obj; + } else { obj.action(); } + return; + } + + this.player.path = this._findPath(this.player.col, this.player.row, col, row); + this.player.moving = this.player.path.length > 0; + this._pendingInteract = null; + } + _onMouseMove(e) { + const { x, y } = this._canvasPos(e); + const { col, row } = this._screenToGrid(x, y); + this.hoveredTile = (col>=0&&row>=0&&col { + if (!this.running) return; + const dt = Math.min((ts - this.lastTime) / 1000, 0.1); + this.lastTime = ts; + this._update(dt); + this._render(); + this.animId = requestAnimationFrame(loop); + }; + this.animId = requestAnimationFrame(loop); + } + stop() { + this.running = false; + if (this.animId) cancelAnimationFrame(this.animId); + } + resize(w, h) { + this.canvas.width = w; + this.canvas.height = h; + this._recenterCamera(); + this._snapPlayerPos(); + this._stars = null; + } + + // ─── Update ─────────────────────────────────────────────────────────────── + _update(dt) { + this._updatePlayer(dt); + this._updateDoors(dt); + } + _updatePlayer(dt) { + const p = this.player; + if (!p.moving || !p.path.length) return; + const SPEED = 4.5; + const next = p.path[0]; + const tPos = this._isoProject(next.col + 0.5, next.row + 0.5); + const dx = tPos.x - p.x, dy = tPos.y - p.y; + const dist = Math.sqrt(dx*dx + dy*dy); + const step = SPEED * this.TW * dt; + + const dC = next.col - p.col, dR = next.row - p.row; + if (dC>0&&dR===0) p.dir='se'; + else if (dC<0&&dR===0) p.dir='nw'; + else if (dR>0&&dC===0) p.dir='sw'; + else if (dR<0&&dC===0) p.dir='ne'; + + if (dist <= step) { + p.x = tPos.x; p.y = tPos.y; + p.col = next.col; p.row = next.row; + p.path.shift(); + if (!p.path.length) { + p.moving = false; + if (this._pendingInteract) { + const obj = this._pendingInteract; + this._pendingInteract = null; + setTimeout(() => obj.action(), 100); + } + } + } else { + p.x += (dx/dist)*step; + p.y += (dy/dist)*step; + } + p.frameTimer += dt; + if (p.frameTimer > 0.15) { p.frame = (p.frame+1)%4; p.frameTimer = 0; } + } + _updateDoors(dt) { + const p = this.player; + const SPEED = 3.5; + for (const key of Object.keys(this.doorStates)) { + const [dc, dr] = key.split(',').map(Number); + const state = this.doorStates[key]; + state.open = (Math.abs(p.col - dc) + Math.abs(p.row - dr)) <= 1; + const target = state.open ? 1 : 0; + if (state.anim < target) state.anim = Math.min(state.anim + SPEED*dt, 1); + else if (state.anim > target) state.anim = Math.max(state.anim - SPEED*dt, 0); + } + } + + // ─── Render ─────────────────────────────────────────────────────────────── + _render() { + const ctx = this.ctx; + ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + ctx.fillStyle = '#07091a'; + ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); + this._drawStarfield(); + + for (let r = 0; r < this.ROWS; r++) { + for (let c = 0; c < this.COLS; c++) { + const tile = this.tileMap[r][c]; + const iso = this._isoProject(c, r); + const hov = this.hoveredTile?.col===c && this.hoveredTile?.row===r; + const st = this._styleFor(c, r); + + if (tile === TILE.FLOOR) this._drawFloorTile(iso.x, iso.y, c, r, hov, st); + else if (tile === TILE.WALL) { + this._drawFloorTile(iso.x, iso.y, c, r, false, st); + this._drawWallTile(iso.x, iso.y, this.wallDefs[`${c},${r}`], st); + } + else if (tile === TILE.DOOR) { + this._drawFloorTile(iso.x, iso.y, c, r, hov, st); + this._drawDoorTile(iso.x, iso.y, c, r, this.doorDefs[`${c},${r}`], st); + } + else if (tile === TILE.LOCKED) this._drawLockedTile(iso.x, iso.y, c, r, hov); + } + } + + this._drawRoomLabels(); + + const sorted = [...this.objects].sort((a,b) => (a.col+a.row)-(b.col+b.row)); + for (const obj of sorted) { + const iso = this._isoProject(obj.col+0.5, obj.row+0.5); + this._drawObject(obj, iso.x, iso.y); + } + + this._drawPlayer(); + this._drawHUD(); + } + + // ─── Tile drawers ───────────────────────────────────────────────────────── + _drawStarfield() { + const ctx = this.ctx; + if (!this._stars) { + this._stars = []; + const rng = mulberry32(0xdeadbeef); + for (let i=0; i<120; i++) this._stars.push({ + x: rng()*this.canvas.width, y: rng()*(this.canvas.height*0.25), + r: rng()*1.5+0.3, a: rng()*0.6+0.2 + }); + } + for (const s of this._stars) { + ctx.beginPath(); ctx.arc(s.x, s.y, s.r, 0, Math.PI*2); + ctx.fillStyle = `rgba(180,210,255,${s.a})`; ctx.fill(); + } + } + + _drawFloorTile(x, y, col, row, hovered, st) { + const ctx = this.ctx, tw = this.TW, th = this.TH; + const even = (col+row)%2===0; + const base = even ? (st.floorColorEven||'#151c2e') : (st.floorColorOdd||'#111827'); + const fill = hovered ? this._lighten(base, 0.35) : base; + + ctx.beginPath(); + ctx.moveTo(x, y); ctx.lineTo(x+tw/2, y+th/2); + ctx.lineTo(x, y+th); ctx.lineTo(x-tw/2, y+th/2); + ctx.closePath(); + ctx.fillStyle = fill; ctx.fill(); + ctx.strokeStyle = hovered ? 'rgba(0,212,255,.5)' : 'rgba(0,212,255,.08)'; + ctx.lineWidth = hovered ? 1.5 : 0.8; ctx.stroke(); + } + + _drawWallTile(x, y, def, st) { + const ctx = this.ctx, tw = this.TW, th = this.TH, h = th*1.8; + const accent = (def?.color) || st.wallColor || '#00d4ff'; + const left = (def?.colorLeft) || st.wallColorLeft || '#0c1626'; + const right = (def?.colorRight) || st.wallColorRight || '#0a1220'; + const top = (def?.colorTop) || st.wallColorTop || '#1a2840'; + + ctx.beginPath(); + ctx.moveTo(x-tw/2, y+th/2 ); ctx.lineTo(x, y+th ); + ctx.lineTo(x, y+th+h ); ctx.lineTo(x-tw/2, y+th/2+h); + ctx.closePath(); ctx.fillStyle=left; ctx.fill(); + ctx.strokeStyle=this._alpha(accent,.15); ctx.lineWidth=0.8; ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(x, y+th ); ctx.lineTo(x+tw/2, y+th/2 ); + ctx.lineTo(x+tw/2, y+th/2+h); ctx.lineTo(x, y+th+h ); + ctx.closePath(); ctx.fillStyle=right; ctx.fill(); + ctx.strokeStyle=this._alpha(accent,.10); ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(x, y ); ctx.lineTo(x+tw/2, y+th/2); + ctx.lineTo(x, y+th ); ctx.lineTo(x-tw/2, y+th/2); + ctx.closePath(); ctx.fillStyle=top; ctx.fill(); + ctx.strokeStyle=this._alpha(accent,.35); ctx.lineWidth=1; ctx.stroke(); + } + + _drawDoorTile(x, y, col, row, def, st) { + const ctx = this.ctx, tw = this.TW, th = this.TH, h = th*1.8; + const key = `${col},${row}`; + const anim = (this.doorStates[key]||{anim:0}).anim; + const fc = (def?.frameColor) || st.doorFrameColor || '#00d4ff'; + const dc = (def?.color) || st.doorColor || '#00ffcc'; + const fw = tw*0.12; + const slide= anim*h*0.93; + + // Left pillar + ctx.beginPath(); + ctx.moveTo(x-tw/2, y+th/2 ); ctx.lineTo(x-tw/2+fw, y+th/2+fw*.5 ); + ctx.lineTo(x-tw/2+fw, y+th/2+h+fw*.5); ctx.lineTo(x-tw/2, y+th/2+h ); + ctx.closePath(); ctx.fillStyle=this._darken(fc,.25); ctx.fill(); + ctx.strokeStyle=this._alpha(fc,.6); ctx.lineWidth=0.8; ctx.stroke(); + + // Right pillar + ctx.beginPath(); + ctx.moveTo(x+tw/2, y+th/2 ); ctx.lineTo(x+tw/2-fw, y+th/2+fw*.5 ); + ctx.lineTo(x+tw/2-fw, y+th/2+h+fw*.5); ctx.lineTo(x+tw/2, y+th/2+h ); + ctx.closePath(); ctx.fillStyle=this._darken(fc,.3); ctx.fill(); + ctx.strokeStyle=this._alpha(fc,.5); ctx.stroke(); + + // Top lintel + ctx.beginPath(); + ctx.moveTo(x-tw/2+fw, y+th/2+fw*.5 ); ctx.lineTo(x+tw/2-fw, y+th/2+fw*.5 ); + ctx.lineTo(x+tw/2-fw, y+th/2+fw*1.5); ctx.lineTo(x-tw/2+fw, y+th/2+fw*1.5); + ctx.closePath(); ctx.fillStyle=this._darken(fc,.2); ctx.fill(); + ctx.strokeStyle=this._alpha(fc,.6); ctx.stroke(); + + // Sliding panel + const pTop = y+th/2+fw*1.5 - slide; + const pBottom = y+th/2+h; + const pw = tw - fw*2; + if (pBottom - pTop > 1) { + ctx.save(); + ctx.beginPath(); ctx.rect(x-tw/2, y+th/2+fw*1.5-h, tw, h*2); ctx.clip(); + ctx.beginPath(); + ctx.moveTo(x-pw/2, pTop ); ctx.lineTo(x+pw/2, pTop -th*.25); + ctx.lineTo(x+pw/2, pBottom-th*.25 ); ctx.lineTo(x-pw/2, pBottom ); + ctx.closePath(); + const pg = ctx.createLinearGradient(x, pTop, x, pBottom); + pg.addColorStop(0, this._alpha(dc,.9)); pg.addColorStop(1, this._alpha(dc,.35)); + ctx.fillStyle=pg; ctx.fill(); + ctx.strokeStyle=this._alpha(dc,.85); ctx.lineWidth=1; ctx.stroke(); + ctx.strokeStyle=this._alpha(dc,.18); ctx.lineWidth=0.5; + for (let ly=pTop+5; ly0.05) { + ctx.fillStyle=this._alpha(dc, anim*.7); + ctx.beginPath(); ctx.ellipse(x, pTop, pw/2, 4, 0, 0, Math.PI*2); ctx.fill(); + } + ctx.restore(); + } + + const pulse = anim<0.1 ? 0.25+Math.sin(performance.now()/400)*.12 : anim*.4; + const fg = ctx.createRadialGradient(x, y+th, 0, x, y+th, 18); + fg.addColorStop(0, this._alpha(dc, pulse)); fg.addColorStop(1,'transparent'); + ctx.fillStyle=fg; + ctx.beginPath(); ctx.ellipse(x, y+th, 18, 9, 0, 0, Math.PI*2); ctx.fill(); + } + + _drawLockedTile(x, y, col, row, hovered) { + const ctx = this.ctx, tw = this.TW, th = this.TH, h = th*1.8; + const now = performance.now()/1000; + const pulse = 0.3 + Math.sin(now*1.5)*0.1; + + // Sealed wall face — dark hatched + const fill = hovered ? '#1a1a2a' : '#0e0e1a'; + ctx.beginPath(); + ctx.moveTo(x, y); ctx.lineTo(x+tw/2, y+th/2); + ctx.lineTo(x, y+th); ctx.lineTo(x-tw/2, y+th/2); + ctx.closePath(); ctx.fillStyle=fill; ctx.fill(); + + // Hatch pattern + ctx.save(); + ctx.beginPath(); + ctx.moveTo(x, y); ctx.lineTo(x+tw/2, y+th/2); + ctx.lineTo(x, y+th); ctx.lineTo(x-tw/2, y+th/2); + ctx.closePath(); ctx.clip(); + ctx.strokeStyle=`rgba(80,80,120,${0.15+pulse*0.1})`; ctx.lineWidth=1; + for (let i=-tw; i i.id === wpId); + if (wp) { + ctx.font='700 9px "Orbitron",monospace'; + ctx.fillStyle=this._alpha(accent,.5); + ctx.textAlign='right'; + ctx.fillText(`🎨 ${wp.name}`, W-14, 36); + } + } + } + + // ─── Wallpaper panel ────────────────────────────────────────────────────── + _buildWallpaperPanel() { + document.getElementById('sb-wp-panel')?.remove(); + const wallpapers = this.catalog.filter(i => i.subtype === 'wallpaper'); + const owned = this.sbData.ownedWallpapers || []; + const rooms = (this.layout.rooms || []).filter(r => + !r.unlock || (this.sbData.unlockedRooms||[]).includes(r.id)); + + const panel = document.createElement('div'); + panel.id = 'sb-wp-panel'; + panel.style.cssText = ` + display:none; position:absolute; bottom:36px; left:0; right:0; + background:linear-gradient(180deg,transparent,rgba(7,9,24,.96)); + border-top:1px solid rgba(0,212,255,.25); + padding:12px 16px 10px; z-index:500; font-family:'Orbitron',monospace; + pointer-events:all; + `; + + // Room selector row + const roomTabs = rooms.map(r => + `` + ).join(''); + + // Wallpaper swatches + const swatches = wallpapers.map(wp => { + const isOwned = owned.includes(wp.id); + const p = wp.preview || {}; + const bg = p.wallColorTop || p.floorColorEven || '#1a2840'; + const fg = p.doorColor || p.wallColor || '#00ffcc'; + return ` +
+
+ ${isOwned ? '' : '
🔒
'} +
+
+ ${wp.name} +
`; + }).join(''); + + panel.innerHTML = ` +
+ WALLPAPER + Apply to: + + ${roomTabs} + + +
+
${swatches}
+
+ Hover a swatch to preview + +
+ `; + + const container = this.canvas.parentElement; + container.style.position = 'relative'; + container.appendChild(panel); + this._wpPanel = panel; + } + + _toggleWallpaperPanel() { + if (!this._wpPanel) this._buildWallpaperPanel(); + this._wpPanelOpen = !this._wpPanelOpen; + this._wpPanel.style.display = this._wpPanelOpen ? 'block' : 'none'; + if (this._wpPanelOpen) { + this._wpSelected = null; + this._wpRoomTarget = null; + } + } + + _wpSetRoom(roomId) { + this._wpRoomTarget = roomId; + // Highlight active tab + this._wpPanel.querySelectorAll('button[data-room]').forEach(b => { + b.style.background = b.dataset.room === roomId + ? 'rgba(0,212,255,.35)' : 'rgba(0,212,255,.12)'; + }); + } + + _wpPreview(wpId) { + const owned = this.sbData.ownedWallpapers || []; + if (!owned.includes(wpId)) return; + this._wpSelected = wpId; + const wp = this.catalog.find(i => i.id === wpId); + const nameEl = document.getElementById('sb-wp-preview-name'); + const applyBtn = document.getElementById('sb-wp-apply-btn'); + if (nameEl && wp) nameEl.textContent = wp.name + ' — ' + (wp.description || ''); + if (applyBtn) applyBtn.style.display = 'inline-block'; + } + + _wpApply(wpId) { + this._wpSelected = null; + const socket = window.game?.socket || window.gameInitializer?.socket; + if (!socket) { + // Offline: apply locally only + if (this._wpRoomTarget) { + if (!this.sbData.roomWallpapers) this.sbData.roomWallpapers = {}; + if (wpId) this.sbData.roomWallpapers[this._wpRoomTarget] = wpId; + else delete this.sbData.roomWallpapers[this._wpRoomTarget]; + } else { + this.sbData.wallpaper = wpId; + } + return; + } + socket.emit('set_wallpaper', { wallpaperId: wpId, roomId: this._wpRoomTarget || null }); + // Optimistic update + if (this._wpRoomTarget) { + if (!this.sbData.roomWallpapers) this.sbData.roomWallpapers = {}; + if (wpId) this.sbData.roomWallpapers[this._wpRoomTarget] = wpId; + else delete this.sbData.roomWallpapers[this._wpRoomTarget]; + } else { + this.sbData.wallpaper = wpId; + } + const applyBtn = document.getElementById('sb-wp-apply-btn'); + if (applyBtn) applyBtn.style.display = 'none'; + } + + // ─── Interaction modals ─────────────────────────────────────────────────── + _showInteractModal(title, icon, desc, color, buttons) { + document.getElementById('sb-interact-modal')?.remove(); + const modal = document.createElement('div'); + modal.id = 'sb-interact-modal'; + modal.style.cssText = ` + position:absolute;top:50%;left:50%;transform:translate(-50%,-50%); + background:linear-gradient(135deg,#0d1525,#111c30);border:2px solid ${color}; + border-radius:12px;padding:24px 28px;min-width:280px;max-width:360px;z-index:1000; + box-shadow:0 0 40px ${color}55,0 8px 32px rgba(0,0,0,.8); + font-family:'Orbitron',monospace;color:#fff;animation:sbModalIn .2s ease;pointer-events:all; + `; + const btnHtml = buttons.map(b=>` + + `).join(''); + modal.innerHTML=` +
+ ${icon} +
+
${title}
+
STARBASE STATION
+
+ +
+
${desc}
+
${btnHtml}
+ `; + this.canvas.parentElement.appendChild(modal); + setTimeout(() => modal.remove(), 8000); + } + + _showLockedRoomModal(room) { + const unlock = this.catalog.find(i => i.id === room.unlock); + const name = unlock?.name || room.unlock; + const price = unlock ? `${unlock.price} ${unlock.currency}` : '?'; + const rarity = unlock?.rarity || 'unknown'; + const color = '#ff8844'; + this._showInteractModal( + `${room.label} — Locked`, '🔒', + `This wing requires the ${name} (${rarity}) to unlock.

` + + (unlock?.categories?.includes('shop') ? `Purchase for ${price} in the Trade Post, or earn it as a dungeon drop.` : + `Earn this by clearing dungeons or completing quests.`), + color, + unlock?.categories?.includes('shop') ? [{ label: 'Visit Trade Post', action: 'shop' }] : [] + ); + } + + _handleModalAction(action) { + const map={shop:'[data-tab="shop"]',crafting:'[data-tab="crafting"]', + quests:'[data-tab="quests"]',skills:'[data-tab="skills"]',base:'[data-tab="base"]'}; + if (map[action]) { const b=document.querySelector(map[action]); if(b){b.click();return;} } + for (const b of document.querySelectorAll('[onclick]')) { + if ((b.getAttribute('onclick')||'').toLowerCase().includes(action)) { b.click(); return; } + } + } + + // ─── Color utilities ────────────────────────────────────────────────────── + _hexToRgb(hex) { + hex=hex.replace('#',''); + if (hex.length===3) hex=hex.split('').map(c=>c+c).join(''); + return { r:parseInt(hex.slice(0,2),16), g:parseInt(hex.slice(2,4),16), b:parseInt(hex.slice(4,6),16) }; + } + _darken(hex, f) { + const {r,g,b}=this._hexToRgb(hex); + return `rgb(${Math.round(r*f)},${Math.round(g*f)},${Math.round(b*f)})`; + } + _lighten(hex, f) { + const {r,g,b}=this._hexToRgb(hex); + return `rgb(${Math.min(255,Math.round(r+(255-r)*f))},${Math.min(255,Math.round(g+(255-g)*f))},${Math.min(255,Math.round(b+(255-b)*f))})`; + } + _alpha(hex, a) { + if (hex.startsWith('rgba')||hex.startsWith('rgb(')) return hex.replace(/[\d.]+\)$/,`${a})`); + const {r,g,b}=this._hexToRgb(hex); + return `rgba(${r},${g},${b},${a})`; + } +} + +// ─── JSON loader ────────────────────────────────────────────────────────────── +async function loadStarbaseWorld(canvasId) { + let layout; + try { + const resp = await fetch(`data/starbase-layout.json?v=${Date.now()}`); + if (!resp.ok) throw new Error(resp.status); + layout = await resp.json(); + } catch(e) { + console.warn('[STARBASE] Using default layout:', e.message); + layout = StarbaseWorld.defaultLayout(); + } + + // Fetch player starbase data from server + let sbData = { wallpaper: null, ownedWallpapers: [], unlockedRooms: [] }; + let catalog = []; + + const socket = window.game?.socket || window.gameInitializer?.socket; + if (socket) { + await new Promise(resolve => { + const timer = setTimeout(resolve, 3000); + socket.once('starbase_data', data => { + clearTimeout(timer); + if (data.success) { sbData = data.starbase; catalog = data.catalog || []; } + resolve(); + }); + socket.emit('get_starbase_data'); + }); + } + + if (window.starbaseWorld) { window.starbaseWorld.stop(); window.starbaseWorld = null; } + window.starbaseWorld = new StarbaseWorld(canvasId, layout, sbData, catalog); + + // Listen for server wallpaper confirmations + if (socket) { + socket.off('wallpaper_set'); + socket.on('wallpaper_set', data => { + if (data.success && window.starbaseWorld) { + window.starbaseWorld.sbData = data.starbase; + } + }); + } + + return window.starbaseWorld; +} + +// ─── Helpers ────────────────────────────────────────────────────────────────── +function mulberry32(a) { + return function() { + a|=0; a=a+0x6D2B79F5|0; + let t=Math.imul(a^a>>>15,1|a); + t=t+Math.imul(t^t>>>7,61|t)^t; + return ((t^t>>>14)>>>0)/4294967296; + }; +} + +(function(){ + if (document.getElementById('sb-anim-style')) return; + const s=document.createElement('style'); s.id='sb-anim-style'; + s.textContent=` + @keyframes sbModalIn { + from{opacity:0;transform:translate(-50%,-46%)} + to {opacity:1;transform:translate(-50%,-50%)} + } + `; + document.head.appendChild(s); +})(); diff --git a/Galaxy-Strike-Online-main/.github/workflows/build-client.yml b/Galaxy-Strike-Online-main/.github/workflows/build-client.yml new file mode 100644 index 0000000..53730ac --- /dev/null +++ b/Galaxy-Strike-Online-main/.github/workflows/build-client.yml @@ -0,0 +1,228 @@ +name: Build Client for All Platforms + +on: + push: + branches: [ main, develop ] + paths: + - 'Client/**' + pull_request: + branches: [ main ] + paths: + - 'Client/**' + workflow_dispatch: + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + cache: 'npm' + cache-dependency-path: Client/package-lock.json + + - name: Install system dependencies (Ubuntu) + if: matrix.os == 'ubuntu-latest' + run: | + sudo apt-get update + sudo apt-get install -y libnss3-dev libatk-bridge2.0-dev libdrm2 libxkbcommon-dev libxcomposite-dev libxdamage-dev libxrandr-dev libgbm-dev libxss1 libasound2-dev + + - name: Install system dependencies (macOS) + if: matrix.os == 'macos-latest' + run: | + # Install Xcode command line tools if not present + xcode-select --install 2>/dev/null || true + + - name: Install system dependencies (Windows) + if: matrix.os == 'windows-latest' + run: | + # Windows builds typically have required dependencies pre-installed + # Ensure chocolatey is available for any additional packages + choco --version || echo "Chocolatey not available" + + - name: Install dependencies + working-directory: ./Client + run: | + if [ "${{ matrix.os }}" = "windows-latest" ]; then + npm ci --include=dev + else + npm ci + fi + shell: bash + + - name: Clean previous builds (Windows) + if: matrix.os == 'windows-latest' + working-directory: ./Client + run: if exist dist rmdir /s /q dist + shell: cmd + + - name: Clean previous builds (Unix) + if: matrix.os != 'windows-latest' + working-directory: ./Client + run: rm -rf dist + shell: bash + + - name: Build for Linux + if: matrix.os == 'ubuntu-latest' + working-directory: ./Client + run: npm run build-linux + + - name: Build for Windows + if: matrix.os == 'windows-latest' + working-directory: ./Client + run: npm run build-win + + - name: Build for macOS + if: matrix.os == 'macos-latest' + working-directory: ./Client + run: npm run build-mac + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: client-${{ matrix.os }} + path: | + Client/dist/*.exe + Client/dist/*.msi + Client/dist/*.dmg + Client/dist/*.zip + Client/dist/*.AppImage + Client/dist/*.deb + Client/dist/*.rpm + Client/dist/*.app + retention-days: 30 + continue-on-error: true + + package: + needs: build + runs-on: ubuntu-latest + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + + - name: Create release directory + run: mkdir -p release + + - name: Organize executables by platform + run: | + # Create platform directories + mkdir -p release/Windows + mkdir -p release/macOS + mkdir -p release/Linux + + # Windows executables - copy directly from dist + if [ -d "artifacts/client-windows-latest" ]; then + cp artifacts/client-windows-latest/*.exe release/Windows/ 2>/dev/null || true + cp artifacts/client-windows-latest/*.msi release/Windows/ 2>/dev/null || true + fi + + # macOS executables - copy directly from dist + if [ -d "artifacts/client-macos-latest" ]; then + cp artifacts/client-macos-latest/*.dmg release/macOS/ 2>/dev/null || true + cp artifacts/client-macos-latest/*.zip release/macOS/ 2>/dev/null || true + # Handle .app bundles (directories) + if [ -d "artifacts/client-macos-latest" ]; then + find artifacts/client-macos-latest -maxdepth 1 -name "*.app" -type d -exec cp -r {} release/macOS/ \; 2>/dev/null || true + fi + fi + + # Linux executables - copy directly from dist + if [ -d "artifacts/client-ubuntu-latest" ]; then + cp artifacts/client-ubuntu-latest/*.AppImage release/Linux/ 2>/dev/null || true + cp artifacts/client-ubuntu-latest/*.deb release/Linux/ 2>/dev/null || true + cp artifacts/client-ubuntu-latest/*.rpm release/Linux/ 2>/dev/null || true + fi + + # List what we actually got + echo "=== Executables Found ===" + find release -type f -name "*" -o -name "*.app" | head -20 || true + + # Create platform info file + cat > release/README.txt << EOF + Galaxy Strike Online - Multi-Platform Client + ========================================== + + Windows: + - Run the .exe installer or portable executable + + macOS: + - Open the .dmg file and drag to Applications + - Or extract the .zip and run the .app + + Linux: + - Make the .AppImage executable: chmod +x *.AppImage + - Or install the .deb package: sudo dpkg -i *.deb + + Build Date: $(date -u +"%Y-%m-%d %H:%M:%S UTC") + Commit: ${{ github.sha }} + EOF + + - name: Create all-in-one zip + run: | + cd release + zip -r ../Galaxy-Strike-Online-Client-${{ github.sha }}.zip . + + - name: Upload all-in-one zip + uses: actions/upload-artifact@v4 + with: + name: galaxy-strike-online-client-all-platforms + path: Galaxy-Strike-Online-Client-${{ github.sha }}.zip + retention-days: 90 + + - name: Create Release (Main Branch) + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + uses: softprops/action-gh-release@v1 + with: + files: Galaxy-Strike-Online-Client-${{ github.sha }}.zip + name: Galaxy Strike Online Client - Latest + body: | + Latest multi-platform client release for Galaxy Strike Online. + + Includes: + - Windows (NSIS installer + Portable) + - macOS (DMG + ZIP) + - Linux (AppImage + Debian package) + + Commit: ${{ github.sha }} + Build Date: ${{ github.event.head_commit.timestamp }} + + **Download the all-in-one zip file below for all platforms.** + draft: false + prerelease: false + tag_name: latest + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Create Release (Tags) + if: startsWith(github.ref, 'refs/tags/') + uses: softprops/action-gh-release@v1 + with: + files: Galaxy-Strike-Online-Client-${{ github.sha }}.zip + name: Galaxy Strike Online Client - ${{ github.ref_name }} + body: | + Multi-platform client release for Galaxy Strike Online. + + Includes: + - Windows (NSIS installer + Portable) + - macOS (DMG + ZIP) + - Linux (AppImage + Debian package) + + Commit: ${{ github.sha }} + draft: false + prerelease: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/Galaxy-Strike-Online-main/.gitignore b/Galaxy-Strike-Online-main/.gitignore new file mode 100644 index 0000000..9a5aced --- /dev/null +++ b/Galaxy-Strike-Online-main/.gitignore @@ -0,0 +1,139 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.* +!.env.example + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Sveltekit cache directory +.svelte-kit/ + +# vitepress build output +**/.vitepress/dist + +# vitepress cache directory +**/.vitepress/cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# Firebase cache directory +.firebase/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v3 +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions + +# Vite logs files +vite.config.js.timestamp-* +vite.config.ts.timestamp-* diff --git a/Galaxy-Strike-Online-main/API/config/database.js b/Galaxy-Strike-Online-main/API/config/database.js new file mode 100644 index 0000000..25558f4 --- /dev/null +++ b/Galaxy-Strike-Online-main/API/config/database.js @@ -0,0 +1,19 @@ +const mongoose = require('mongoose'); +const logger = require('../utils/logger'); + +const connectDB = async () => { + try { + const mongoUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/galaxystrikeonline'; + + const conn = await mongoose.connect(mongoUri, { + // Remove deprecated options for newer MongoDB versions + }); + + logger.info(`MongoDB Connected: ${conn.connection.host}`); + } catch (error) { + logger.error('Database connection error:', error); + process.exit(1); + } +}; + +module.exports = connectDB; diff --git a/Galaxy-Strike-Online-main/API/config/production.js b/Galaxy-Strike-Online-main/API/config/production.js new file mode 100644 index 0000000..05f5364 --- /dev/null +++ b/Galaxy-Strike-Online-main/API/config/production.js @@ -0,0 +1,131 @@ +const logger = require('../utils/logger'); + +const productionConfig = { + // Server settings + port: process.env.PORT || 3001, + + // Database settings + database: { + uri: process.env.MONGODB_URI || 'mongodb://localhost:27017/galaxystrikeonline', + options: { + useNewUrlParser: true, + useUnifiedTopology: true, + maxPoolSize: 10, + serverSelectionTimeoutMS: 5000, + socketTimeoutMS: 45000, + } + }, + + // JWT settings + jwt: { + secret: process.env.JWT_SECRET, + expiresIn: '24h', + refreshExpiresIn: '7d' + }, + + // Redis settings (for sessions and caching) + redis: { + url: process.env.REDIS_URL || 'redis://localhost:6379', + options: { + retryDelayOnFailover: 100, + maxRetriesPerRequest: 3, + } + }, + + // CORS settings + cors: { + origin: process.env.CLIENT_URL || 'http://localhost:3000', + credentials: true, + methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], + allowedHeaders: ['Content-Type', 'Authorization'] + }, + + // Rate limiting + rateLimit: { + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100, // limit each IP to 100 requests per windowMs + message: 'Too many requests from this IP, please try again later.', + standardHeaders: true, + legacyHeaders: false, + }, + + // Socket.IO settings + socketio: { + cors: { + origin: process.env.CLIENT_URL || 'http://localhost:3000', + methods: ['GET', 'POST'] + }, + pingTimeout: 60000, + pingInterval: 25000, + maxHttpBufferSize: 1e8, // 100 MB + }, + + // Logging settings + logging: { + level: process.env.LOG_LEVEL || 'info', + format: process.env.NODE_ENV === 'production' ? 'json' : 'simple', + file: { + enabled: true, + filename: 'logs/app.log', + maxsize: 10485760, // 10MB + maxFiles: 5, + } + }, + + // Security settings + security: { + helmet: { + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'"], + scriptSrc: ["'self'"], + imgSrc: ["'self'", "data:", "https:"], + }, + }, + hsts: { + maxAge: 31536000, + includeSubDomains: true, + preload: true + } + }, + compression: { + level: 6, + threshold: 1024, + } + }, + + // Game settings + game: { + maxPlayersPerServer: 50, + serverCleanupInterval: 300000, // 5 minutes + inactivePlayerTimeout: 1800000, // 30 minutes + autoSaveInterval: 60000, // 1 minute + } +}; + +// Validate required environment variables +const validateConfig = () => { + const required = ['JWT_SECRET']; + const missing = required.filter(key => !process.env[key]); + + if (missing.length > 0) { + logger.error(`Missing required environment variables: ${missing.join(', ')}`); + process.exit(1); + } + + if (process.env.NODE_ENV === 'production') { + const prodRequired = ['MONGODB_URI', 'CLIENT_URL']; + const prodMissing = prodRequired.filter(key => !process.env[key]); + + if (prodMissing.length > 0) { + logger.error(`Missing required production environment variables: ${prodMissing.join(', ')}`); + process.exit(1); + } + } +}; + +module.exports = { + ...productionConfig, + validateConfig +}; diff --git a/Galaxy-Strike-Online-main/API/middleware/errorHandler.js b/Galaxy-Strike-Online-main/API/middleware/errorHandler.js new file mode 100644 index 0000000..97ff3ac --- /dev/null +++ b/Galaxy-Strike-Online-main/API/middleware/errorHandler.js @@ -0,0 +1,134 @@ +const logger = require('../utils/logger'); + +// Custom error classes +class AppError extends Error { + constructor(message, statusCode) { + super(message); + this.statusCode = statusCode; + this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error'; + this.isOperational = true; + + Error.captureStackTrace(this, this.constructor); + } +} + +class ValidationError extends AppError { + constructor(message) { + super(message, 400); + } +} + +class AuthenticationError extends AppError { + constructor(message = 'Authentication failed') { + super(message, 401); + } +} + +class AuthorizationError extends AppError { + constructor(message = 'Access denied') { + super(message, 403); + } +} + +class NotFoundError extends AppError { + constructor(message = 'Resource not found') { + super(message, 404); + } +} + +class ConflictError extends AppError { + constructor(message = 'Resource conflict') { + super(message, 409); + } +} + +class DatabaseError extends AppError { + constructor(message = 'Database operation failed') { + super(message, 500); + } +} + +// Error handling middleware +const errorHandler = (err, req, res, next) => { + let error = { ...err }; + error.message = err.message; + + // Log error + logger.error({ + error: err, + request: { + method: req.method, + url: req.url, + ip: req.ip, + userAgent: req.get('User-Agent'), + userId: req.userId + } + }); + + // Mongoose validation error + if (err.name === 'ValidationError') { + const message = Object.values(err.errors).map(val => val.message).join(', '); + error = new ValidationError(message); + } + + // Mongoose duplicate key error + if (err.code === 11000) { + const field = Object.keys(err.keyValue)[0]; + const value = err.keyValue[field]; + error = new ConflictError(`${field} '${value}' already exists`); + } + + // Mongoose cast error + if (err.name === 'CastError') { + error = new ValidationError(`Invalid ${err.path}: ${err.value}`); + } + + // JWT errors + if (err.name === 'JsonWebTokenError') { + error = new AuthenticationError('Invalid token'); + } + + if (err.name === 'TokenExpiredError') { + error = new AuthenticationError('Token expired'); + } + + // Default error + if (!error.isOperational) { + error = new AppError('Something went wrong', 500); + } + + res.status(error.statusCode || 500).json({ + status: error.status || 'error', + message: error.message, + ...(process.env.NODE_ENV === 'development' && { + stack: error.stack, + error: err + }) + }); +}; + +// Async error wrapper +const catchAsync = (fn) => { + return (req, res, next) => { + Promise.resolve(fn(req, res, next)).catch(next); + }; +}; + +// 404 handler +const notFound = (req, res, next) => { + const error = new NotFoundError(`Route ${req.originalUrl} not found`); + next(error); +}; + +module.exports = { + AppError, + ValidationError, + AuthenticationError, + AuthorizationError, + NotFoundError, + ConflictError, + DatabaseError, + errorHandler, + catchAsync, + notFound +}; diff --git a/Galaxy-Strike-Online-main/API/models/GameServer.js b/Galaxy-Strike-Online-main/API/models/GameServer.js new file mode 100644 index 0000000..a41c33c --- /dev/null +++ b/Galaxy-Strike-Online-main/API/models/GameServer.js @@ -0,0 +1,134 @@ +const mongoose = require('mongoose'); + +const gameServerSchema = new mongoose.Schema({ + serverId: { + type: String, + required: true, + unique: true + }, + name: { + type: String, + required: true + }, + type: { + type: String, + enum: ['public', 'private'], + default: 'public' + }, + region: { + type: String, + default: 'us-east' + }, + maxPlayers: { + type: Number, + default: 10, + min: 1, + max: 100 + }, + currentPlayers: { + type: Number, + default: 0 + }, + owner: { + userId: { type: String, required: true }, + username: { type: String, required: true } + }, + settings: { + password: { type: String, default: null }, + description: { type: String, default: '' }, + tags: [{ type: String }] + }, + status: { + type: String, + enum: ['waiting', 'active', 'full', 'offline'], + default: 'waiting' + }, + gameServerUrl: { + type: String, + default: null + }, + createdAt: { + type: Date, + default: Date.now + }, + lastActivity: { + type: Date, + default: Date.now + } +}, { + timestamps: true +}); + +// Indexes for performance (only for non-unique fields) +gameServerSchema.index({ type: 1 }); +gameServerSchema.index({ region: 1 }); +gameServerSchema.index({ status: 1 }); +gameServerSchema.index({ 'owner.userId': 1 }); + +// Methods +gameServerSchema.methods.addPlayer = function() { + if (this.currentPlayers < this.maxPlayers) { + this.currentPlayers += 1; + this.lastActivity = new Date(); + + if (this.currentPlayers >= this.maxPlayers) { + this.status = 'full'; + } else if (this.currentPlayers > 0) { + this.status = 'active'; + } + + return true; + } + return false; +}; + +gameServerSchema.methods.removePlayer = function() { + if (this.currentPlayers > 0) { + this.currentPlayers -= 1; + this.lastActivity = new Date(); + + if (this.currentPlayers === 0) { + this.status = 'waiting'; + } else if (this.currentPlayers < this.maxPlayers) { + this.status = 'active'; + } + + return true; + } + return false; +}; + +gameServerSchema.methods.isFull = function() { + return this.currentPlayers >= this.maxPlayers; +}; + +gameServerSchema.methods.canJoin = function() { + return this.status !== 'offline' && !this.isFull(); +}; + +// Static methods +gameServerSchema.statics.findAvailableServers = function(filters = {}) { + const query = { status: { $ne: 'offline' } }; + + if (filters.type) { + query.type = filters.type; + } + + if (filters.region) { + query.region = filters.region; + } + + return this.find(query).sort({ lastActivity: -1 }); +}; + +gameServerSchema.statics.cleanupOldServers = function(maxAge = 24 * 60 * 60 * 1000) { // 24 hours + const cutoffTime = new Date(Date.now() - maxAge); + return this.deleteMany({ + $or: [ + { lastActivity: { $lt: cutoffTime }, currentPlayers: 0 }, + { status: 'offline', lastActivity: { $lt: cutoffTime } } + ] + }); +}; + +module.exports = mongoose.model('GameServer', gameServerSchema); diff --git a/Galaxy-Strike-Online-main/API/models/Inventory.js b/Galaxy-Strike-Online-main/API/models/Inventory.js new file mode 100644 index 0000000..005beb9 --- /dev/null +++ b/Galaxy-Strike-Online-main/API/models/Inventory.js @@ -0,0 +1,306 @@ +const mongoose = require('mongoose'); + +const inventorySchema = new mongoose.Schema({ + userId: { + type: String, + required: true, + ref: 'Player' + }, + + // Inventory settings + maxSlots: { + type: Number, + default: 50 + }, + + // Items array + items: [{ + id: { + type: String, + required: true + }, + name: { + type: String, + required: true + }, + type: { + type: String, + required: true, + enum: ['weapon', 'armor', 'material', 'consumable'] + }, + rarity: { + type: String, + enum: ['common', 'uncommon', 'rare', 'epic', 'legendary'], + default: 'common' + }, + quantity: { + type: Number, + default: 1, + min: 1 + }, + + // Item stats (for weapons/armor) + stats: { + attack: { type: Number, default: 0 }, + defense: { type: Number, default: 0 }, + speed: { type: Number, default: 0 }, + criticalChance: { type: Number, default: 0 }, + criticalDamage: { type: Number, default: 1.5 }, + damage: { type: Number, default: 0 }, + fireRate: { type: Number, default: 0 }, + range: { type: Number, default: 0 }, + energy: { type: Number, default: 0 }, + health: { type: Number, default: 0 }, + maxHealth: { type: Number, default: 0 }, + durability: { type: Number, default: 0 }, + weight: { type: Number, default: 0 }, + energyShield: { type: Number, default: 0 } + }, + + // Item properties + description: { + type: String, + default: '' + }, + + // Equipment properties + equipable: { + type: Boolean, + default: false + }, + slot: { + type: String, + enum: ['weapon', 'armor', 'engine', 'shield', 'special'], + default: null + }, + isEquipped: { + type: Boolean, + default: false + }, + + // Consumable properties + consumable: { + type: Boolean, + default: false + }, + effect: { + health: { type: Number, default: 0 }, + energy: { type: Number, default: 0 }, + attack: { type: Number, default: 0 }, + defense: { type: Number, default: 0 }, + speed: { type: Number, default: 0 }, + duration: { type: Number, default: 0 } + }, + + // Stackable items + stackable: { + type: Boolean, + default: false + }, + + // Timestamps + acquiredAt: { + type: Date, + default: Date.now + }, + lastUsed: { + type: Date, + default: null + } + }], + + // Equipped items + equippedItems: { + weapon: { type: String, default: null }, + armor: { type: String, default: null }, + engine: { type: String, default: null }, + shield: { type: String, default: null }, + special: { type: String, default: null } + }, + + // Timestamps + updatedAt: { + type: Date, + default: Date.now + } +}, { + timestamps: true +}); + +// Indexes for performance +inventorySchema.index({ userId: 1 }); +inventorySchema.index({ 'items.id': 1 }); +inventorySchema.index({ 'items.type': 1 }); + +// Methods +inventorySchema.methods.addItem = function(itemData) { + // Check if item already exists and is stackable + const existingItem = this.items.find(item => + item.id === itemData.id && + item.type === itemData.type && + item.stackable + ); + + if (existingItem) { + existingItem.quantity += itemData.quantity || 1; + } else { + // Add new item + const newItem = { + ...itemData, + quantity: itemData.quantity || 1, + acquiredAt: new Date() + }; + + this.items.push(newItem); + } + + this.updatedAt = new Date(); + return this.save(); +}; + +inventorySchema.methods.removeItem = function(itemId, quantity = 1) { + const itemIndex = this.items.findIndex(item => item.id === itemId); + + if (itemIndex === -1) { + throw new Error('Item not found in inventory'); + } + + const item = this.items[itemIndex]; + + if (item.quantity > quantity) { + item.quantity -= quantity; + } else { + // Remove item completely + this.items.splice(itemIndex, 1); + + // Unequip if it was equipped + Object.keys(this.equippedItems).forEach(slot => { + if (this.equippedItems[slot] === itemId) { + this.equippedItems[slot] = null; + } + }); + } + + this.updatedAt = new Date(); + return this.save(); +}; + +inventorySchema.methods.hasItem = function(itemId, quantity = 1) { + const item = this.items.find(item => item.id === itemId); + return item && item.quantity >= quantity; +}; + +inventorySchema.methods.getItemCount = function(itemId) { + const item = this.items.find(item => item.id === itemId); + return item ? item.quantity : 0; +}; + +inventorySchema.methods.equipItem = function(itemId, slot) { + const item = this.items.find(item => item.id === itemId); + + if (!item) { + throw new Error('Item not found in inventory'); + } + + if (!item.equipable) { + throw new Error('Item is not equipable'); + } + + if (item.slot !== slot) { + throw new Error('Item cannot be equipped in this slot'); + } + + // Unequip current item in slot + if (this.equippedItems[slot]) { + const currentItem = this.items.find(item => item.id === this.equippedItems[slot]); + if (currentItem) { + currentItem.isEquipped = false; + } + } + + // Equip new item + this.equippedItems[slot] = itemId; + item.isEquipped = true; + item.lastUsed = new Date(); + + this.updatedAt = new Date(); + return this.save(); +}; + +inventorySchema.methods.unequipItem = function(slot) { + const itemId = this.equippedItems[slot]; + + if (!itemId) { + throw new Error('No item equipped in this slot'); + } + + const item = this.items.find(item => item.id === itemId); + if (item) { + item.isEquipped = false; + } + + this.equippedItems[slot] = null; + this.updatedAt = new Date(); + return this.save(); +}; + +inventorySchema.methods.useConsumable = function(itemId) { + const item = this.items.find(item => item.id === itemId); + + if (!item) { + throw new Error('Item not found in inventory'); + } + + if (!item.consumable) { + throw new Error('Item is not consumable'); + } + + if (item.quantity <= 0) { + throw new Error('No quantity left'); + } + + // Apply effects + const effects = { ...item.effect }; + + // Remove one from quantity + item.quantity -= 1; + item.lastUsed = new Date(); + + // Remove item if quantity is 0 + if (item.quantity === 0) { + const itemIndex = this.items.findIndex(item => item.id === itemId); + this.items.splice(itemIndex, 1); + } + + this.updatedAt = new Date(); + this.save(); + + return effects; +}; + +inventorySchema.methods.getInventorySummary = function() { + const summary = { + totalItems: this.items.length, + usedSlots: this.items.length, + maxSlots: this.maxSlots, + itemsByType: {}, + equippedItems: this.equippedItems + }; + + // Count items by type + this.items.forEach(item => { + summary.itemsByType[item.type] = (summary.itemsByType[item.type] || 0) + item.quantity; + }); + + return summary; +}; + +inventorySchema.methods.getItemsByType = function(type) { + return this.items.filter(item => item.type === type); +}; + +inventorySchema.methods.getItemsByRarity = function(rarity) { + return this.items.filter(item => item.rarity === rarity); +}; + +module.exports = mongoose.model('Inventory', inventorySchema); diff --git a/Galaxy-Strike-Online-main/API/models/Player.js b/Galaxy-Strike-Online-main/API/models/Player.js new file mode 100644 index 0000000..a8eaab0 --- /dev/null +++ b/Galaxy-Strike-Online-main/API/models/Player.js @@ -0,0 +1,155 @@ +const mongoose = require('mongoose'); + +const playerSchema = new mongoose.Schema({ + userId: { + type: String, + required: true, + unique: true + }, + username: { + type: String, + required: true + }, + email: { + type: String, + required: true, + unique: true + }, + + // Authentication + password: { + type: String, + required: true, + select: false // Don't include password in queries by default + }, + + // Player stats (simplified for API server) + stats: { + level: { type: Number, default: 1 }, + experience: { type: Number, default: 0 }, + credits: { type: Number, default: 1000 }, + dungeonsCleared: { type: Number, default: 0 }, + playTime: { type: Number, default: 0 }, + lastLogin: { type: Date, default: Date.now } + }, + + // Base attributes + attributes: { + health: { type: Number, default: 100 }, + maxHealth: { type: Number, default: 100 }, + energy: { type: Number, default: 100 }, + maxEnergy: { type: Number, default: 100 }, + attack: { type: Number, default: 10 }, + defense: { type: Number, default: 5 }, + speed: { type: Number, default: 10 }, + criticalChance: { type: Number, default: 0.05 }, + criticalDamage: { type: Number, default: 1.5 } + }, + + // Player info + info: { + name: { type: String, default: 'Commander' }, + title: { type: String, default: 'Rookie Pilot' }, + guild: { type: String, default: null }, + rank: { type: String, default: 'Cadet' } + }, + + // Current ship + currentShip: { + type: mongoose.Schema.Types.ObjectId, + ref: 'Ship' + }, + + // Settings + settings: { + autoSave: { type: Boolean, default: true }, + notifications: { type: Boolean, default: true }, + soundEffects: { type: Boolean, default: true }, + music: { type: Boolean, default: false }, + discordIntegration: { type: Boolean, default: false } + }, + + // Daily rewards + dailyRewards: { + lastClaim: { type: Date, default: null }, + consecutiveDays: { type: Number, default: 0 } + }, + + // Server info + currentServer: { type: String, default: null }, + + // Timestamps + createdAt: { type: Date, default: Date.now }, + updatedAt: { type: Date, default: Date.now } +}, { + timestamps: true +}); + +// Indexes for performance (only for non-unique fields) +playerSchema.index({ 'stats.level': 1 }); +playerSchema.index({ currentServer: 1 }); + +// Methods (simplified for API server) +playerSchema.methods.addExperience = function(amount) { + this.stats.experience += amount; + return this.stats.experience; +}; + +playerSchema.methods.addCredits = function(amount) { + this.stats.credits += amount; + return this.stats.credits; +}; + +playerSchema.methods.canAfford = function(cost) { + return this.stats.credits >= cost; +}; + +playerSchema.methods.spendCredits = function(cost) { + if (this.canAfford(cost)) { + this.stats.credits -= cost; + return true; + } + return false; +}; + +playerSchema.methods.updatePlayTime = function(sessionTime) { + this.stats.playTime += sessionTime; + this.stats.lastLogin = new Date(); +}; + +playerSchema.methods.claimDailyReward = function() { + const today = new Date(); + const lastClaim = this.dailyRewards.lastClaim; + + // Check if already claimed today + if (lastClaim && lastClaim.toDateString() === today.toDateString()) { + return { success: false, message: 'Daily reward already claimed today' }; + } + + // Check consecutive days + const yesterday = new Date(today); + yesterday.setDate(yesterday.getDate() - 1); + + if (lastClaim && lastClaim.toDateString() === yesterday.toDateString()) { + this.dailyRewards.consecutiveDays += 1; + } else { + this.dailyRewards.consecutiveDays = 1; + } + + this.dailyRewards.lastClaim = today; + + // Calculate reward based on consecutive days + const baseReward = 100; + const consecutiveBonus = (this.dailyRewards.consecutiveDays - 1) * 50; + const totalReward = baseReward + consecutiveBonus; + + this.addCredits(totalReward); + + return { + success: true, + reward: totalReward, + consecutiveDays: this.dailyRewards.consecutiveDays + }; +}; + +module.exports = mongoose.model('Player', playerSchema); diff --git a/Galaxy-Strike-Online-main/API/models/Ship.js b/Galaxy-Strike-Online-main/API/models/Ship.js new file mode 100644 index 0000000..270ca97 --- /dev/null +++ b/Galaxy-Strike-Online-main/API/models/Ship.js @@ -0,0 +1,189 @@ +const mongoose = require('mongoose'); + +const shipSchema = new mongoose.Schema({ + userId: { + type: String, + required: true, + ref: 'Player' + }, + + // Ship identification + id: { + type: String, + required: true, + unique: true + }, + name: { + type: String, + required: true + }, + class: { + type: String, + required: true, + enum: ['Fighter', 'Cruiser', 'Battleship', 'Carrier', 'Explorer'] + }, + level: { + type: Number, + default: 1 + }, + + // Ship stats + stats: { + health: { type: Number, required: true }, + maxHealth: { type: Number, required: true }, + attack: { type: Number, required: true }, + defense: { type: Number, required: true }, + speed: { type: Number, required: true }, + criticalChance: { type: Number, default: 0.05 }, + criticalDamage: { type: Number, default: 1.5 }, + hull: { type: Number, required: true } + }, + + // Ship appearance + texture: { + type: String, + required: true + }, + + // Ship progression + experience: { + type: Number, + default: 0 + }, + requiredExp: { + type: Number, + default: 100 + }, + upgrades: [{ + type: String + }], + + // Ship status + isEquipped: { + type: Boolean, + default: false + }, + isCurrent: { + type: Boolean, + default: false + }, + + // Shop information (if purchased) + price: { + type: Number, + default: 0 + }, + rarity: { + type: String, + enum: ['common', 'uncommon', 'rare', 'epic', 'legendary'], + default: 'common' + }, + description: { + type: String, + default: '' + }, + + // Timestamps + acquiredAt: { + type: Date, + default: Date.now + }, + lastUsed: { + type: Date, + default: Date.now + } +}, { + timestamps: true +}); + +// Indexes for performance +shipSchema.index({ userId: 1 }); +shipSchema.index({ id: 1 }); +shipSchema.index({ isEquipped: 1 }); +shipSchema.index({ isCurrent: 1 }); + +// Methods +shipSchema.methods.addExperience = function(amount) { + this.experience += amount; + + // Level up logic + while (this.experience >= this.requiredExp) { + this.experience -= this.requiredExp; + this.level += 1; + this.requiredExp = this.level * 100; + + // Increase stats on level up + this.stats.maxHealth += 10; + this.stats.health = this.stats.maxHealth; + this.stats.attack += 2; + this.stats.defense += 1; + this.stats.speed += 1; + } + + return this.level; +}; + +shipSchema.methods.takeDamage = function(damage) { + const actualDamage = Math.max(0, damage - this.stats.defense); + this.stats.health = Math.max(0, this.stats.health - actualDamage); + + if (this.stats.health === 0) { + this.isDestroyed = true; + } + + return actualDamage; +}; + +shipSchema.methods.heal = function(amount) { + const healAmount = Math.min(amount, this.stats.maxHealth - this.stats.health); + this.stats.health += healAmount; + this.isDestroyed = false; + + return healAmount; +}; + +shipSchema.methods.isAlive = function() { + return this.stats.health > 0; +}; + +shipSchema.methods.getStatSummary = function() { + return { + name: this.name, + class: this.class, + level: this.level, + health: `${this.stats.health}/${this.stats.maxHealth}`, + attack: this.stats.attack, + defense: this.stats.defense, + speed: this.stats.speed, + criticalChance: `${(this.stats.criticalChance * 100).toFixed(1)}%`, + criticalDamage: `${this.stats.criticalDamage}x` + }; +}; + +shipSchema.methods.upgrade = function(upgradeType) { + switch (upgradeType) { + case 'health': + this.stats.maxHealth += 20; + this.stats.health = this.stats.maxHealth; + break; + case 'attack': + this.stats.attack += 5; + break; + case 'defense': + this.stats.defense += 3; + break; + case 'speed': + this.stats.speed += 2; + break; + default: + throw new Error('Unknown upgrade type'); + } + + if (!this.upgrades.includes(upgradeType)) { + this.upgrades.push(upgradeType); + } + + this.lastUsed = new Date(); +}; + +module.exports = mongoose.model('Ship', shipSchema); diff --git a/Galaxy-Strike-Online-main/API/package-lock.json b/Galaxy-Strike-Online-main/API/package-lock.json new file mode 100644 index 0000000..60160b4 --- /dev/null +++ b/Galaxy-Strike-Online-main/API/package-lock.json @@ -0,0 +1,6068 @@ +{ + "name": "galaxystrikeonline-server", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "galaxystrikeonline-server", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "bcryptjs": "^2.4.3", + "compression": "^1.7.4", + "cors": "^2.8.5", + "dotenv": "^16.3.1", + "express": "^4.18.2", + "helmet": "^7.1.0", + "joi": "^17.11.0", + "jsonwebtoken": "^9.0.2", + "mongoose": "^8.0.3", + "rate-limiter-flexible": "^2.4.2", + "redis": "^4.6.11", + "socket.io": "^4.7.4", + "winston": "^3.11.0" + }, + "devDependencies": { + "jest": "^29.7.0", + "nodemon": "^3.0.2", + "supertest": "^6.3.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/core/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/traverse/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", + "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", + "license": "MIT", + "dependencies": { + "@so-ric/colorspace": "^1.1.6", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.4.tgz", + "integrity": "sha512-p7X/ytJDIdwUfFL/CLOhKgdfJe1Fa8uw9seJYvdOmnP9JBWGWHW69HkOixXS6Wy9yvGf1MbhcS6lVmrhy4jm2g==", + "license": "MIT", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@paralleldrive/cuid2": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", + "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.1.5" + } + }, + "node_modules/@redis/bloom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/client": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz", + "integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==", + "license": "MIT", + "peer": true, + "dependencies": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@redis/client/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/@redis/graph": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", + "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/json": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz", + "integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/search": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz", + "integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/time-series": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz", + "integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "license": "BSD-3-Clause" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@so-ric/colorspace": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", + "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", + "license": "MIT", + "dependencies": { + "color": "^5.0.2", + "text-hex": "1.0.x" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/node": { + "version": "25.0.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz", + "integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "license": "MIT" + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.10", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.10.tgz", + "integrity": "sha512-2VIKvDx8Z1a9rTB2eCkdPE5nSe28XnA+qivGnWHoB40hMMt/h1hSz0960Zqsn6ZyxWXUie0EBdElKv8may20AA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==", + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/bson": { + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001760", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz", + "integrity": "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/color": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz", + "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==", + "license": "MIT", + "dependencies": { + "color-convert": "^3.1.3", + "color-string": "^2.1.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-string": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz", + "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/color-string/node_modules/color-name": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", + "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz", + "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=14.6" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", + "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/component-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/dedent": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz", + "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "license": "ISC", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.267", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", + "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/engine.io": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", + "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", + "license": "MIT", + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/express": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "license": "MIT" + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formidable": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.5.tgz", + "integrity": "sha512-Oz5Hwvwak/DCaXVVUtPn4oLMLLy1CdclLKO1LFgU7XzDpVMUU5UjlSLpGMocyQNNk8F6IJW9M/YdooSn2MRI+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", + "dezalgo": "^1.0.4", + "once": "^1.4.0", + "qs": "^6.11.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/helmet": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.2.0.tgz", + "integrity": "sha512-ZRiwvN089JfMXokizgqEPXsl2Guk094yExfoDXR0cBYWxtBbaSww/w+vT4WEJsBW2iTUi1GgZ6swmoug3Oy4Xw==", + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/kareem": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "license": "MIT" + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "license": "MIT", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/logform/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT" + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mongodb": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.20.0.tgz", + "integrity": "sha512-Tl6MEIU3K4Rq3TSHd+sZQqRBoGlFsOgNrH5ltAcFBV62Re3Fd+FcaVf8uSEQFOJ51SDowDVttBTONMfoYWrWlQ==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.3.0", + "bson": "^6.10.4", + "mongodb-connection-string-url": "^3.0.2" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.3.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, + "node_modules/mongoose": { + "version": "8.20.4", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.20.4.tgz", + "integrity": "sha512-o4ABeT3IEk1Z4dGt3XjHJ0x9OjyWvakC1+btPpzWqCovqyidKGdbB05/g87cdh7AuWXFQKHOxt+L/OZOBps4hw==", + "license": "MIT", + "dependencies": { + "bson": "^6.10.4", + "kareem": "2.6.3", + "mongodb": "~6.20.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "engines": { + "node": ">=16.20.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mongoose/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "license": "MIT", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/mquery/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mquery/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nodemon": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.11.tgz", + "integrity": "sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nodemon/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "license": "MIT", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/rate-limiter-flexible": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/rate-limiter-flexible/-/rate-limiter-flexible-2.4.2.tgz", + "integrity": "sha512-rMATGGOdO1suFyf/mI5LYhts71g1sbdhmd6YvdiXO2gJnd42Tt6QS4JUKJKSWVVkMtBacm6l40FR7Trjo6Iruw==", + "license": "ISC" + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/redis": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.7.1.tgz", + "integrity": "sha512-S1bJDnqLftzHXHP8JsT5II/CtHWQrASX5K96REjWjlmWKrviSOLWmM7QnRLstAWsu1VBBV1ffV6DzCvxNP0UJQ==", + "license": "MIT", + "workspaces": [ + "./packages/*" + ], + "dependencies": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.6.1", + "@redis/graph": "1.1.1", + "@redis/json": "1.0.7", + "@redis/search": "1.2.0", + "@redis/time-series": "1.1.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", + "license": "MIT" + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "license": "MIT", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-adapter/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/superagent": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.1.2.tgz", + "integrity": "sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==", + "deprecated": "Please upgrade to superagent v10.2.2+, see release notes at https://github.com/forwardemail/superagent/releases/tag/v10.2.2 - maintenance is supported by Forward Email @ https://forwardemail.net", + "dev": true, + "license": "MIT", + "dependencies": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.4", + "debug": "^4.3.4", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.0", + "formidable": "^2.1.2", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.11.0", + "semver": "^7.3.8" + }, + "engines": { + "node": ">=6.4.0 <13 || >=14" + } + }, + "node_modules/superagent/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/superagent/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/superagent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/superagent/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/supertest": { + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.4.tgz", + "integrity": "sha512-erY3HFDG0dPnhw4U+udPfrzXa4xhSG+n4rxfRuZWCUvjFWwKl+OxWf/7zk50s84/fAAs7vf5QAb9uRa0cCykxw==", + "deprecated": "Please upgrade to supertest v7.1.3+, see release notes at https://github.com/forwardemail/supertest/releases/tag/v7.1.3 - maintenance is supported by Forward Email @ https://forwardemail.net", + "dev": true, + "license": "MIT", + "dependencies": { + "methods": "^1.1.2", + "superagent": "^8.1.2" + }, + "engines": { + "node": ">=6.4.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "license": "MIT" + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/winston": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz", + "integrity": "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==", + "license": "MIT", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.8", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "license": "MIT", + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/Galaxy-Strike-Online-main/API/package.json b/Galaxy-Strike-Online-main/API/package.json new file mode 100644 index 0000000..8d5c3c5 --- /dev/null +++ b/Galaxy-Strike-Online-main/API/package.json @@ -0,0 +1,39 @@ +{ + "name": "galaxystrikeonline-server", + "version": "1.0.0", + "description": "Galaxy Strike Online - Server Backend", + "license": "MIT", + "author": "Korvarix Studios", + "type": "commonjs", + "main": "server.js", + "scripts": { + "start": "node server.js", + "dev": "nodemon server.js", + "debug": "node --inspect server.js", + "test": "jest", + "migrate": "node scripts/migrate.js", + "seed": "node scripts/seed.js" + }, + "keywords": ["game", "server", "mmorpg", "api", "websocket"], + "dependencies": { + "express": "^4.18.2", + "cors": "^2.8.5", + "helmet": "^7.1.0", + "dotenv": "^16.3.1", + "bcryptjs": "^2.4.3", + "jsonwebtoken": "^9.0.2", + "mongoose": "^8.0.3", + "winston": "^3.11.0", + "joi": "^17.11.0", + "rate-limiter-flexible": "^2.4.2", + "compression": "^1.7.4" + }, + "devDependencies": { + "nodemon": "^3.0.2", + "jest": "^29.7.0", + "supertest": "^6.3.3" + }, + "engines": { + "node": ">=18.0.0" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/API/routes/auth.js b/Galaxy-Strike-Online-main/API/routes/auth.js new file mode 100644 index 0000000..1cc0372 --- /dev/null +++ b/Galaxy-Strike-Online-main/API/routes/auth.js @@ -0,0 +1,214 @@ +const express = require('express'); +const bcrypt = require('bcryptjs'); +const jwt = require('jsonwebtoken'); +const Joi = require('joi'); +const { RateLimiterMemory } = require('rate-limiter-flexible'); +const Player = require('../models/Player'); +const logger = require('../utils/logger'); + +const router = express.Router(); + +// Rate limiting for auth routes +const authLimiter = new RateLimiterMemory({ + keyGenerator: (req) => req.ip, + points: 5, // Number of requests + duration: 900, // Per 15 minutes (900 seconds) + blockDuration: 900, // Block for 15 minutes + message: 'Too many authentication attempts, please try again later.' +}); + +// Validation schemas +const registerSchema = Joi.object({ + username: Joi.string().min(3).max(30).required(), + email: Joi.string().email().required(), + password: Joi.string().min(6).required() +}); + +const loginSchema = Joi.object({ + email: Joi.string().email().required(), + password: Joi.string().required() +}); + +// Register route +router.post('/register', async (req, res) => { + try { + // Rate limiting check + const resLimiter = await authLimiter.consume(req.ip); + if (!resLimiter.remainingPoints) { + return res.status(429).json({ error: 'Too many authentication attempts, please try again later.' }); + } + + const { error } = registerSchema.validate(req.body); + if (error) { + return res.status(400).json({ error: error.details[0].message }); + } + + const { username, email, password } = req.body; + + // Check if user already exists + const existingUser = await Player.findOne({ + $or: [{ email }, { username }] + }); + + if (existingUser) { + return res.status(400).json({ + error: 'User with this email or username already exists' + }); + } + + // Hash password + const salt = await bcrypt.genSalt(10); + const hashedPassword = await bcrypt.hash(password, salt); + + // Create new player + const player = new Player({ + userId: `user_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, + username, + email, + password: hashedPassword, + createdAt: new Date(), + lastLogin: new Date() + }); + + await player.save(); + + // Create JWT token + const token = jwt.sign( + { userId: player.userId, email: player.email }, + process.env.JWT_SECRET || 'fallback_secret', + { expiresIn: '24h' } + ); + + logger.info(`New user registered: ${email}`); + + res.status(201).json({ + message: 'User registered successfully', + token, + user: { + userId: player.userId, + username: player.username, + email: player.email, + stats: player.stats + } + }); + + } catch (error) { + logger.error('Registration error:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +// Login route +router.post('/login', async (req, res) => { + try { + // Rate limiting check + const resLimiter = await authLimiter.consume(req.ip); + if (!resLimiter.remainingPoints) { + return res.status(429).json({ error: 'Too many authentication attempts, please try again later.' }); + } + + const { error } = loginSchema.validate(req.body); + if (error) { + return res.status(400).json({ error: error.details[0].message }); + } + + const { email, password } = req.body; + + // Find user + const player = await Player.findOne({ email }).select('+password'); + if (!player) { + return res.status(401).json({ error: 'Invalid credentials' }); + } + + // Check if password exists (for backward compatibility with existing users) + if (!player.password) { + logger.error('Player password field is missing for user:', email); + return res.status(401).json({ + error: 'Account migration required. Please re-register your account.', + requiresMigration: true + }); + } + + // Check password + const isPasswordValid = await bcrypt.compare(password, player.password); + if (!isPasswordValid) { + return res.status(401).json({ error: 'Invalid credentials' }); + } + + // Update last login + player.stats.lastLogin = new Date(); + await player.save(); + + // Create JWT token + const token = jwt.sign( + { userId: player.userId, email: player.email }, + process.env.JWT_SECRET || 'fallback_secret', + { expiresIn: '24h' } + ); + + logger.info(`User logged in: ${email}`); + + res.json({ + message: 'Login successful', + token, + user: { + userId: player.userId, + username: player.username, + email: player.email, + stats: player.stats, + info: player.info + } + }); + + } catch (error) { + logger.error('Login error:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +// Verify token route +router.get('/verify', async (req, res) => { + try { + const token = req.header('Authorization')?.replace('Bearer ', ''); + + if (!token) { + return res.status(401).json({ error: 'No token provided' }); + } + + const decoded = jwt.verify(token, process.env.JWT_SECRET || 'fallback_secret'); + + const player = await Player.findOne({ userId: decoded.userId }); + if (!player) { + return res.status(401).json({ error: 'Invalid token' }); + } + + res.json({ + valid: true, + user: { + userId: player.userId, + username: player.username, + email: player.email, + stats: player.stats, + info: player.info + } + }); + + } catch (error) { + logger.error('Token verification error:', error); + res.status(401).json({ error: 'Invalid token' }); + } +}); + +// Logout route +router.post('/logout', async (req, res) => { + try { + // In a real implementation, you might want to blacklist the token + // For now, we'll just return success + res.json({ message: 'Logout successful' }); + } catch (error) { + logger.error('Logout error:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +module.exports = router; diff --git a/Galaxy-Strike-Online-main/API/routes/servers.js b/Galaxy-Strike-Online-main/API/routes/servers.js new file mode 100644 index 0000000..47de7d5 --- /dev/null +++ b/Galaxy-Strike-Online-main/API/routes/servers.js @@ -0,0 +1,419 @@ +const express = require('express'); +const jwt = require('jsonwebtoken'); +const GameServer = require('../models/GameServer'); +const logger = require('../utils/logger'); + +const router = express.Router(); + +// Middleware to authenticate JWT token +const authenticateToken = (req, res, next) => { + const token = req.header('Authorization')?.replace('Bearer ', ''); + + if (!token) { + return res.status(401).json({ error: 'Access token required' }); + } + + try { + const decoded = jwt.verify(token, process.env.JWT_SECRET || 'fallback_secret'); + req.userId = decoded.userId; + next(); + } catch (error) { + res.status(401).json({ error: 'Invalid token' }); + } +}; + +// Register new game server (for GameServer instances to register themselves) +router.post('/register', async (req, res) => { + try { + const { serverId, name, type, region, maxPlayers, currentPlayers, gameServerUrl, owner } = req.body; + + logger.info(`[API SERVER] Game server registration request:`, { + serverId, name, type, region, maxPlayers, currentPlayers, gameServerUrl, owner + }); + + // Check if server already exists + const existingServer = await GameServer.findOne({ serverId }); + if (existingServer) { + // Update existing server + existingServer.name = name || existingServer.name; + existingServer.type = type || existingServer.type; + existingServer.region = region || existingServer.region; + existingServer.maxPlayers = maxPlayers || existingServer.maxPlayers; + existingServer.currentPlayers = currentPlayers !== undefined ? currentPlayers : existingServer.currentPlayers; + existingServer.gameServerUrl = gameServerUrl || existingServer.gameServerUrl; + existingServer.status = 'waiting'; + existingServer.lastActivity = new Date(); + + await existingServer.save(); + logger.info(`[API SERVER] Updated existing server: ${serverId} with ${existingServer.currentPlayers} players`); + + return res.json({ + success: true, + message: 'Server updated successfully', + server: existingServer + }); + } + + // Create new server + const newServer = new GameServer({ + serverId, + name: name || `Game Server ${serverId}`, + type: type || 'public', + region: region || 'us-east', + maxPlayers: maxPlayers || 10, + currentPlayers: currentPlayers !== undefined ? currentPlayers : 0, + owner: owner || { + userId: 'system', + username: 'System' + }, + status: 'waiting', + gameServerUrl, + createdAt: new Date(), + lastActivity: new Date() + }); + + await newServer.save(); + logger.info(`[API SERVER] Registered new server: ${serverId}`); + + res.status(201).json({ + success: true, + message: 'Server registered successfully', + server: newServer + }); + + } catch (error) { + logger.error('[API SERVER] Error registering server:', error); + res.status(500).json({ + success: false, + error: 'Failed to register server' + }); + } +}); + +// Update server status (for GameServer instances to update their status) +router.post('/update-status/:serverId', async (req, res) => { + try { + const { serverId } = req.params; + const { currentPlayers, status } = req.body; + + const server = await GameServer.findOne({ serverId }); + if (!server) { + return res.status(404).json({ + success: false, + error: 'Server not found' + }); + } + + if (currentPlayers !== undefined) { + server.currentPlayers = currentPlayers; + } + + if (status) { + server.status = status; + } + + server.lastActivity = new Date(); + await server.save(); + + logger.info(`[API SERVER] Updated server ${serverId} status:`, { + currentPlayers: server.currentPlayers, + status: server.status + }); + + res.json({ + success: true, + message: 'Server status updated successfully' + }); + + } catch (error) { + logger.error('[API SERVER] Error updating server status:', error); + res.status(500).json({ + success: false, + error: 'Failed to update server status' + }); + } +}); + +// Unregister game server (for GameServer instances to unregister themselves) +router.delete('/unregister/:serverId', async (req, res) => { + try { + const { serverId } = req.params; + + logger.info(`[API SERVER] Game server unregistration request:`, { serverId }); + + // Find and remove server + const server = await GameServer.findOneAndDelete({ serverId }); + + if (!server) { + return res.status(404).json({ + success: false, + error: 'Server not found' + }); + } + + logger.info(`[API SERVER] Unregistered server: ${serverId}`); + + res.json({ + success: true, + message: 'Server unregistered successfully' + }); + + } catch (error) { + logger.error('[API SERVER] Error unregistering server:', error); + res.status(500).json({ + success: false, + error: 'Failed to unregister server' + }); + } +}); + +// Get server list +router.get('/', authenticateToken, async (req, res) => { + try { + const { type, region } = req.query; + + // Build filters + const filters = {}; + if (type) filters.type = type; + if (region) filters.region = region; + + logger.info(`[API SERVER] Fetching servers for user ${req.userId} with filters:`, filters); + + // Get available servers from database + const servers = await GameServer.findAvailableServers(filters); + + logger.info(`[API SERVER] Found ${servers.length} servers in database`); + + // Format server list for client + const serverList = servers.map(server => ({ + id: server.serverId, + name: server.name, + type: server.type, + region: server.region, + currentPlayers: server.currentPlayers, + maxPlayers: server.maxPlayers, + status: server.status, + ownerName: server.owner.username, + createdAt: server.createdAt, + lastActivity: server.lastActivity + })); + + logger.info(`[API SERVER] Returning ${serverList.length} servers to client`); + + res.json({ + success: true, + servers: serverList, + totalServers: serverList.length + }); + + } catch (error) { + logger.error('Error getting server list:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +// Create new server +router.post('/create', authenticateToken, async (req, res) => { + try { + const { name, type = 'public', maxPlayers = 10, region = 'us-east', settings = {} } = req.body; + + if (!name) { + return res.status(400).json({ error: 'Server name required' }); + } + + // Generate unique server ID + const serverId = `server_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; + + // Get user info from token (you might want to fetch full user data) + const ownerUsername = req.body.username || 'Unknown'; // This should come from user data + + // Create new server in database + const newServer = new GameServer({ + serverId, + name, + type, + region, + maxPlayers, + owner: { + userId: req.userId, + username: ownerUsername + }, + settings, + gameServerUrl: process.env.GAME_SERVER_URL || 'https://api.korvarix.com' + }); + + await newServer.save(); + + logger.info(`Server created: ${serverId} by user ${req.userId}`); + + res.status(201).json({ + message: 'Server created successfully', + server: { + id: newServer.serverId, + name: newServer.name, + type: newServer.type, + region: newServer.region, + currentPlayers: newServer.currentPlayers, + maxPlayers: newServer.maxPlayers, + status: newServer.status, + ownerName: newServer.owner.username, + createdAt: newServer.createdAt + } + }); + + } catch (error) { + logger.error('Error creating server:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +// Join server +router.post('/:serverId/join', authenticateToken, async (req, res) => { + try { + const { serverId } = req.params; + + // Find server in database + const server = await GameServer.findOne({ serverId }); + + if (!server) { + return res.status(404).json({ error: 'Server not found' }); + } + + // Check if server can be joined + if (!server.canJoin()) { + return res.status(400).json({ error: 'Server is full or offline' }); + } + + // Add player to server + const playerAdded = server.addPlayer(); + if (!playerAdded) { + return res.status(400).json({ error: 'Server is full' }); + } + + await server.save(); + + logger.info(`User ${req.userId} joined server ${serverId}`); + + res.json({ + message: 'Joined server successfully', + server: { + id: server.serverId, + name: server.name, + currentPlayers: server.currentPlayers, + maxPlayers: server.maxPlayers, + gameServerUrl: server.gameServerUrl + } + }); + + } catch (error) { + logger.error('Error joining server:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +// Leave server +router.post('/:serverId/leave', authenticateToken, async (req, res) => { + try { + const { serverId } = req.params; + + // Find server in database + const server = await GameServer.findOne({ serverId }); + + if (!server) { + return res.status(404).json({ error: 'Server not found' }); + } + + // Remove player from server + const playerRemoved = server.removePlayer(); + if (playerRemoved) { + await server.save(); + logger.info(`User ${req.userId} left server ${serverId}`); + } + + // Update player's current server + const Player = require('../models/Player'); + await Player.findOneAndUpdate( + { userId: req.userId }, + { currentServer: null } + ); + + res.json({ + message: 'Left server successfully' + }); + + } catch (error) { + logger.error('Error leaving server:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +// Get server details +router.get('/:serverId', authenticateToken, async (req, res) => { + try { + const { serverId } = req.params; + + const server = await GameServer.findOne({ serverId }); + + if (!server) { + return res.status(404).json({ error: 'Server not found' }); + } + + res.json({ + server: { + id: server.serverId, + name: server.name, + type: server.type, + region: server.region, + currentPlayers: server.currentPlayers, + maxPlayers: server.maxPlayers, + status: server.status, + ownerName: server.owner.username, + settings: server.settings, + createdAt: server.createdAt, + lastActivity: server.lastActivity + } + }); + + } catch (error) { + logger.error('Error getting server details:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +// Get user's current server +router.get('/user/current', authenticateToken, async (req, res) => { + try { + const Player = require('../models/Player'); + const player = await Player.findOne({ userId: req.userId }); + + if (!player || !player.currentServer) { + return res.json({ currentServer: null }); + } + + const server = await GameServer.findOne({ serverId: player.currentServer }); + + if (!server) { + // Clear invalid server reference + await Player.findOneAndUpdate( + { userId: req.userId }, + { currentServer: null } + ); + return res.json({ currentServer: null }); + } + + res.json({ + currentServer: { + id: server.serverId, + name: server.name, + currentPlayers: server.currentPlayers, + maxPlayers: server.maxPlayers + } + }); + + } catch (error) { + logger.error('Error getting current server:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +module.exports = router; diff --git a/Galaxy-Strike-Online-main/API/scripts/createTestServer.js b/Galaxy-Strike-Online-main/API/scripts/createTestServer.js new file mode 100644 index 0000000..a60f454 --- /dev/null +++ b/Galaxy-Strike-Online-main/API/scripts/createTestServer.js @@ -0,0 +1,67 @@ +/** + * Create Test Server Script + * Adds a test server to the database for testing the server browser + */ + +const mongoose = require('mongoose'); +const GameServer = require('../models/GameServer'); +require('dotenv').config(); + +async function createTestServer() { + try { + // Connect to database + await mongoose.connect(process.env.MONGODB_URI); + console.log('Connected to database'); + + // Check if test server already exists + const existingServer = await GameServer.findOne({ serverId: 'test_server_001' }); + if (existingServer) { + console.log('Test server already exists, deleting it first...'); + await GameServer.deleteOne({ serverId: 'test_server_001' }); + } + + // Create test server + const testServer = new GameServer({ + serverId: 'test_server_001', + name: 'Test Server - Galaxy Strike', + type: 'public', + region: 'us-east', + maxPlayers: 10, + currentPlayers: 2, + owner: { + userId: 'test_user_001', + username: 'TestAdmin' + }, + settings: { + description: 'A test server for Galaxy Strike Online', + tags: ['test', 'beginner', 'pve'] + }, + status: 'active', + gameServerUrl: 'https://api.korvarix.com' + }); + + await testServer.save(); + console.log('Test server created successfully!'); + console.log('Server details:', { + id: testServer.serverId, + name: testServer.name, + type: testServer.type, + region: testServer.region, + currentPlayers: testServer.currentPlayers, + maxPlayers: testServer.maxPlayers, + status: testServer.status + }); + + } catch (error) { + console.error('Error creating test server:', error); + } finally { + await mongoose.disconnect(); + } +} + +// Run the script +if (require.main === module) { + createTestServer(); +} + +module.exports = createTestServer; diff --git a/Galaxy-Strike-Online-main/API/scripts/migrate.js b/Galaxy-Strike-Online-main/API/scripts/migrate.js new file mode 100644 index 0000000..147bbe3 --- /dev/null +++ b/Galaxy-Strike-Online-main/API/scripts/migrate.js @@ -0,0 +1,50 @@ +const mongoose = require('mongoose'); +const logger = require('../utils/logger'); +require('dotenv').config(); + +async function migrate() { + try { + logger.info('Starting database migration...'); + + // Connect to database + await mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/galaxystrikeonline'); + + logger.info('Connected to database'); + + // Create indexes for performance + const db = mongoose.connection.db; + + // Player indexes + await db.collection('players').createIndex({ userId: 1 }, { unique: true }); + await db.collection('players').createIndex({ email: 1 }, { unique: true }); + await db.collection('players').createIndex({ 'stats.level': 1 }); + await db.collection('players').createIndex({ currentServer: 1 }); + + // Ship indexes + await db.collection('ships').createIndex({ userId: 1 }); + await db.collection('ships').createIndex({ id: 1 }, { unique: true }); + await db.collection('ships').createIndex({ isEquipped: 1 }); + await db.collection('ships').createIndex({ isCurrent: 1 }); + + // Inventory indexes + await db.collection('inventories').createIndex({ userId: 1 }, { unique: true }); + await db.collection('inventories').createIndex({ 'items.id': 1 }); + await db.collection('inventories').createIndex({ 'items.type': 1 }); + + logger.info('Database migration completed successfully'); + + // Close connection + await mongoose.connection.close(); + logger.info('Database connection closed'); + + } catch (error) { + logger.error('Migration failed:', error); + process.exit(1); + } +} + +if (require.main === module) { + migrate(); +} + +module.exports = migrate; diff --git a/Galaxy-Strike-Online-main/API/scripts/migratePasswords.js b/Galaxy-Strike-Online-main/API/scripts/migratePasswords.js new file mode 100644 index 0000000..470af65 --- /dev/null +++ b/Galaxy-Strike-Online-main/API/scripts/migratePasswords.js @@ -0,0 +1,71 @@ +/** + * Password Migration Script + * Updates existing users to have password fields + */ + +const mongoose = require('mongoose'); +const bcrypt = require('bcryptjs'); +const Player = require('../models/Player'); +const logger = require('../utils/logger'); +require('dotenv').config(); + +async function migratePasswords() { + try { + // Connect to database + await mongoose.connect(process.env.MONGODB_URI); + logger.info('Connected to database for password migration'); + + // Find all users without passwords + const usersWithoutPasswords = await Player.find({ + password: { $exists: false } + }); + + logger.info(`Found ${usersWithoutPasswords.length} users without passwords`); + + if (usersWithoutPasswords.length === 0) { + logger.info('No users need password migration'); + return; + } + + // Update each user with a default password + for (const user of usersWithoutPasswords) { + // Generate a default password (you might want to use a different approach) + const defaultPassword = 'tempPassword123!'; + const salt = await bcrypt.genSalt(10); + const hashedPassword = await bcrypt.hash(defaultPassword, salt); + + await Player.updateOne( + { _id: user._id }, + { + $set: { + password: hashedPassword, + 'stats.lastLogin': new Date() + } + } + ); + + logger.info(`Migrated user: ${user.email} with default password`); + } + + logger.info('Password migration completed successfully'); + + // Output the default password for users to change + console.log('\n=== MIGRATION COMPLETE ==='); + console.log(`Updated ${usersWithoutPasswords.length} users`); + console.log('Default password for all migrated users: tempPassword123!'); + console.log('Users should change their password after first login\n'); + + } catch (error) { + logger.error('Password migration error:', error); + console.error('Migration failed:', error); + } finally { + await mongoose.disconnect(); + } +} + +// Run the migration +if (require.main === module) { + migratePasswords(); +} + +module.exports = migratePasswords; diff --git a/Galaxy-Strike-Online-main/API/scripts/seed.js b/Galaxy-Strike-Online-main/API/scripts/seed.js new file mode 100644 index 0000000..2825bda --- /dev/null +++ b/Galaxy-Strike-Online-main/API/scripts/seed.js @@ -0,0 +1,196 @@ +const mongoose = require('mongoose'); +const logger = require('../utils/logger'); +const Player = require('../models/Player'); +const Ship = require('../models/Ship'); +const Inventory = require('../models/Inventory'); +require('dotenv').config(); + +async function seed() { + try { + logger.info('Starting database seeding...'); + + // Connect to database + await mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/galaxystrikeonline'); + + logger.info('Connected to database'); + + // Clear existing data (optional - remove if you want to preserve data) + logger.info('Clearing existing data...'); + await Player.deleteMany({}); + await Ship.deleteMany({}); + await Inventory.deleteMany({}); + + // Create a test user + const testUser = new Player({ + userId: 'test_user_001', + username: 'TestPlayer', + email: 'test@example.com', + password: '$2a$10$example_hashed_password_here', + stats: { + level: 1, + experience: 0, + credits: 5000, + dungeonsCleared: 0, + playTime: 0, + lastLogin: new Date() + }, + attributes: { + health: 100, + maxHealth: 100, + energy: 100, + maxEnergy: 100, + attack: 10, + defense: 5, + speed: 10, + criticalChance: 0.05, + criticalDamage: 1.5 + }, + info: { + name: 'Commander', + title: 'Rookie Pilot', + guild: null, + rank: 'Cadet' + }, + settings: { + autoSave: true, + notifications: true, + soundEffects: true, + music: false, + discordIntegration: false + }, + dailyRewards: { + lastClaim: null, + consecutiveDays: 0 + } + }); + + await testUser.save(); + logger.info('Created test user'); + + // Create starter ship for test user + const starterShip = new Ship({ + userId: testUser.userId, + id: 'starter_cruiser_001', + name: 'Starter Cruiser', + class: 'Cruiser', + level: 1, + stats: { + health: 100, + maxHealth: 100, + attack: 15, + defense: 12, + speed: 10, + criticalChance: 0.05, + criticalDamage: 1.5, + hull: 100 + }, + texture: 'assets/textures/ships/starter_cruiser.png', + experience: 0, + requiredExp: 100, + upgrades: [], + isEquipped: true, + isCurrent: true, + price: 5000, + rarity: 'common', + description: 'Reliable starter cruiser for new pilots', + acquiredAt: new Date(), + lastUsed: new Date() + }); + + await starterShip.save(); + logger.info('Created starter ship'); + + // Update player with current ship + testUser.currentShip = starterShip._id; + await testUser.save(); + + // Create inventory for test user + const inventory = new Inventory({ + userId: testUser.userId, + maxSlots: 50, + items: [ + { + id: 'starter_blaster_common', + name: 'Common Blaster', + type: 'weapon', + rarity: 'common', + quantity: 1, + stats: { + attack: 5, + criticalChance: 0.02, + damage: 10, + fireRate: 2, + range: 5, + energy: 5 + }, + description: 'A reliable basic blaster for new pilots', + equipable: true, + slot: 'weapon', + isEquipped: false, + stackable: false, + acquiredAt: new Date() + }, + { + id: 'basic_armor_common', + name: 'Basic Armor', + type: 'armor', + rarity: 'common', + quantity: 1, + stats: { + defense: 3, + durability: 20, + weight: 2, + energyShield: 0 + }, + description: 'Light armor providing basic protection', + equipable: true, + slot: 'armor', + isEquipped: false, + stackable: false, + acquiredAt: new Date() + }, + { + id: 'health_kit', + name: 'Health Kit', + type: 'consumable', + rarity: 'common', + quantity: 3, + stats: {}, + description: 'A medical kit that restores health', + consumable: true, + effect: { + health: 50 + }, + stackable: true, + acquiredAt: new Date() + } + ], + equippedItems: { + weapon: null, + armor: null, + engine: null, + shield: null, + special: null + } + }); + + await inventory.save(); + logger.info('Created inventory with starter items'); + + logger.info('Database seeding completed successfully'); + + // Close connection + await mongoose.connection.close(); + logger.info('Database connection closed'); + + } catch (error) { + logger.error('Seeding failed:', error); + process.exit(1); + } +} + +if (require.main === module) { + seed(); +} + +module.exports = seed; diff --git a/Galaxy-Strike-Online-main/API/server.js b/Galaxy-Strike-Online-main/API/server.js new file mode 100644 index 0000000..2eacb55 --- /dev/null +++ b/Galaxy-Strike-Online-main/API/server.js @@ -0,0 +1,234 @@ +const express = require('express'); +const http = require('http'); +const cors = require('cors'); +const helmet = require('helmet'); +const compression = require('compression'); +const rateLimit = require('rate-limiter-flexible'); +require('dotenv').config(); + +const logger = require('./utils/logger'); +const connectDB = require('./config/database'); +const authRoutes = require('./routes/auth'); +const serverRoutes = require('./routes/servers'); +const { errorHandler, notFound } = require('./middleware/errorHandler'); +const GameServer = require('./models/GameServer'); + +// Override console.error to properly log error objects +const originalConsoleError = console.error; +console.error = (...args) => { + args.forEach(arg => { + if (arg instanceof Error) { + logger.error('Console Error:', { + message: arg.message, + stack: arg.stack, + name: arg.name + }); + } else if (typeof arg === 'object' && arg !== null) { + logger.error('Console Error Object:', arg); + } else { + logger.error('Console Error:', arg); + } + }); +}; + +const app = express(); +const server = http.createServer(app); + +// Middleware +app.use(helmet()); +app.use(compression()); +app.use(cors({ + origin: ["https://galaxystrike.online", "https://api.korvarix.com", "http://api.korvarix.com:3001", "https://dev.gameserver.galaxystrike.online"], + credentials: true +})); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true })); + +// Static file serving +app.use(express.static('../Website/dist')); + +// Rate limiting (more lenient for development) +const { RateLimiterMemory } = require('rate-limiter-flexible'); +const limiter = new RateLimiterMemory({ + keyGenerator: (req) => req.ip, + points: 1000, // limit each IP to 1000 requests per windowMs (increased from 100) + duration: 60, // 1 minute window (reduced from 15 minutes) + blockDuration: 60, // Block for 1 minute (reduced from 15 minutes) +}); + +app.use('/api/', async (req, res, next) => { + try { + // Skip rate limiting for localhost in development + const isLocalhost = req.ip === '127.0.0.1' || req.ip === '::1' || req.hostname === 'localhost'; + + if (!isLocalhost) { + const resLimiter = await limiter.consume(req.ip); + if (!resLimiter.remainingPoints) { + return res.status(429).json({ error: 'Too many requests, please try again later.' }); + } + } + next(); + } catch (rejRes) { + // Handle rate limit exceeded + const secs = Math.round(rejRes.msBeforeNext / 1000) || 1; + res.set('Retry-After', String(secs)); + res.status(429).json({ error: 'Too many requests, please try again later.' }); + } +}); + +// Routes - API Server Only (Auth + Server Browser) +app.use('/api/auth', authRoutes); +app.use('/api/servers', serverRoutes); + +// Manual cleanup endpoint (for testing) +app.post('/api/admin/cleanup-dead-servers', async (req, res) => { + try { + await cleanupDeadServers(); + res.json({ success: true, message: 'Dead server cleanup completed' }); + } catch (error) { + logger.error('Manual cleanup error:', error); + res.status(500).json({ success: false, error: 'Cleanup failed' }); + } +}); + +// Health check +app.get('/health', (req, res) => { + res.status(200).json({ + status: 'API Server OK', + service: 'galaxystrikeonline-api', + timestamp: new Date().toISOString(), + uptime: process.uptime() + }); +}); + +// API version endpoint +app.get('/api/ssc/version', (req, res) => { + res.status(200).json({ + version: '1.0.0', + service: 'galaxystrikeonline-api', + timestamp: new Date().toISOString() + }); +}); + +// Fallback route for SPA - only serve index.html for non-API routes +app.get('*', (req, res) => { + // Don't try to serve index.html for API routes + if (req.path.startsWith('/api/')) { + return res.status(404).json({ error: 'API endpoint not found' }); + } + + // Try dist first (for built files), fallback to public (for development) + const distPath = require('path').resolve(__dirname, '../dist/index.html'); + const publicPath = require('path').resolve(__dirname, '../public/index.html'); + + const fs = require('fs'); + if (fs.existsSync(distPath)) { + res.sendFile(distPath); + } else if (fs.existsSync(publicPath)) { + res.sendFile(publicPath); + } else { + res.status(404).json({ error: 'Frontend not found' }); + } +}); + +// Error handling +app.use(notFound); +app.use(errorHandler); + +// Clean up dead servers +async function cleanupDeadServers() { + try { + logger.info('[API SERVER] Starting dead server cleanup...'); + + // Find servers that haven't been updated in the last 5 minutes + const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000); + + const deadServers = await GameServer.find({ + lastActivity: { $lt: fiveMinutesAgo } + }); + + if (deadServers.length > 0) { + logger.info(`[API SERVER] Found ${deadServers.length} potentially dead servers, checking...`); + + for (const server of deadServers) { + const isAlive = await checkServerHealth(server.gameServerUrl); + + if (!isAlive) { + logger.info(`[API SERVER] Removing dead server: ${server.name} (${server.serverId})`); + await GameServer.deleteOne({ _id: server._id }); + } else { + logger.info(`[API SERVER] Server ${server.name} is still alive, updating lastActivity`); + server.lastActivity = new Date(); + await server.save(); + } + } + } else { + logger.info('[API SERVER] No dead servers found'); + } + + } catch (error) { + logger.error('[API SERVER] Error during dead server cleanup:', error); + } +} + +// Check if a server is healthy +async function checkServerHealth(serverUrl) { + try { + if (!serverUrl) { + return false; + } + + // Add /health endpoint to the URL + const healthUrl = serverUrl.endsWith('/') ? `${serverUrl}health` : `${serverUrl}/health`; + + const response = await fetch(healthUrl, { + method: 'GET', + timeout: 5000 // 5 second timeout + }); + + return response.ok; + } catch (error) { + logger.warn(`[API SERVER] Health check failed for ${serverUrl}:`, error.message); + return false; + } +} + +// Initialize database only (no game systems for API server) +async function startServer() { + try { + // Connect to database + await connectDB(); + logger.info('Database connected successfully'); + + // Start API server + const PORT = process.env.PORT || 3000; + server.listen(PORT, () => { + logger.info(`API Server running on port ${PORT}`); + logger.info('API Server handles: Authentication, Server Browser, User Data'); + + // Start dead server cleanup (every 2 minutes) + setInterval(cleanupDeadServers, 120000); + }); + } catch (error) { + logger.error('Failed to start API server:', error); + process.exit(1); + } +} + +// Handle uncaught errors +process.on('uncaughtException', (error) => { + logger.error('Uncaught Exception:', error); +}); + +process.on('unhandledRejection', (reason, promise) => { + logger.error('Unhandled Rejection at:', promise, 'reason:', reason); +}); + +// Handle HTTP server errors +server.on('error', (error) => { + logger.error('HTTP Server error:', error); +}); + +startServer(); + +module.exports = { app, server }; diff --git a/Galaxy-Strike-Online-main/API/socket/socketHandlers.js b/Galaxy-Strike-Online-main/API/socket/socketHandlers.js new file mode 100644 index 0000000..0e046bf --- /dev/null +++ b/Galaxy-Strike-Online-main/API/socket/socketHandlers.js @@ -0,0 +1,272 @@ +const logger = require('../utils/logger'); +const { getGameSystem } = require('../systems/GameSystem'); +const Player = require('../models/Player'); + +class SocketHandlers { + constructor(io) { + this.io = io; + this.connectedUsers = new Map(); // userId -> socket.id + this.userSockets = new Map(); // socket.id -> userId + } + + handleConnection(socket) { + logger.info(`Client connected: ${socket.id}`); + + // Authentication + socket.on('authenticate', async (token) => { + try { + const jwt = require('jsonwebtoken'); + const decoded = jwt.verify(token, process.env.JWT_SECRET || 'fallback_secret'); + + const player = await Player.findOne({ userId: decoded.userId }); + if (!player) { + socket.emit('auth_error', { error: 'Player not found' }); + return; + } + + // Store user connection + this.connectedUsers.set(decoded.userId, socket.id); + this.userSockets.set(socket.id, decoded.userId); + + socket.userId = decoded.userId; + socket.emit('authenticated', { userId: decoded.userId }); + + logger.info(`User authenticated: ${decoded.userId}`); + + // Join user to their current server if any + if (player.currentServer) { + socket.join(player.currentServer); + this.broadcastToServer(player.currentServer, 'user_joined', { + userId: decoded.userId, + username: player.username + }); + } + + } catch (error) { + logger.error('Authentication error:', error); + socket.emit('auth_error', { error: 'Invalid token' }); + } + }); + + // Server management + socket.on('join_server', async (data) => { + try { + if (!socket.userId) { + socket.emit('error', { error: 'Not authenticated' }); + return; + } + + const gameSystem = getGameSystem(); + const server = await gameSystem.joinServer(data.serverId, socket.userId); + + // Update player's current server + await Player.findOneAndUpdate( + { userId: socket.userId }, + { currentServer: data.serverId } + ); + + // Join socket room + socket.join(data.serverId); + + socket.emit('server_joined', { server }); + this.broadcastToServer(data.serverId, 'user_joined', { + userId: socket.userId, + serverId: data.serverId + }); + + logger.info(`User ${socket.userId} joined server ${data.serverId}`); + + } catch (error) { + logger.error('Error joining server:', error); + socket.emit('error', { error: error.message }); + } + }); + + socket.on('leave_server', async (data) => { + try { + if (!socket.userId) { + socket.emit('error', { error: 'Not authenticated' }); + return; + } + + const gameSystem = getGameSystem(); + const server = await gameSystem.leaveServer(data.serverId, socket.userId); + + // Update player's current server + await Player.findOneAndUpdate( + { userId: socket.userId }, + { currentServer: null } + ); + + // Leave socket room + socket.leave(data.serverId); + + socket.emit('server_left', { server }); + this.broadcastToServer(data.serverId, 'user_left', { + userId: socket.userId, + serverId: data.serverId + }); + + logger.info(`User ${socket.userId} left server ${data.serverId}`); + + } catch (error) { + logger.error('Error leaving server:', error); + socket.emit('error', { error: error.message }); + } + }); + + // Game actions + socket.on('game_action', async (data) => { + try { + if (!socket.userId) { + socket.emit('error', { error: 'Not authenticated' }); + return; + } + + const gameSystem = getGameSystem(); + const result = await gameSystem.processGameAction(socket.userId, data); + + socket.emit('action_result', { action: data.type, result }); + + // Broadcast relevant actions to server + if (data.broadcast && socket.userId) { + const player = await Player.findOne({ userId: socket.userId }); + if (player && player.currentServer) { + this.broadcastToServer(player.currentServer, 'user_action', { + userId: socket.userId, + username: player.username, + action: data.type, + result + }); + } + } + + } catch (error) { + logger.error('Error processing game action:', error); + socket.emit('error', { error: error.message }); + } + }); + + // Chat functionality + socket.on('send_message', async (data) => { + try { + if (!socket.userId) { + socket.emit('error', { error: 'Not authenticated' }); + return; + } + + const player = await Player.findOne({ userId: socket.userId }); + if (!player || !player.currentServer) { + socket.emit('error', { error: 'Not in a server' }); + return; + } + + const message = { + userId: socket.userId, + username: player.username, + message: data.message, + timestamp: new Date(), + type: data.type || 'chat' + }; + + // Broadcast to server + this.broadcastToServer(player.currentServer, 'new_message', message); + + logger.info(`Chat message from ${socket.userId} in server ${player.currentServer}`); + + } catch (error) { + logger.error('Error sending message:', error); + socket.emit('error', { error: error.message }); + } + }); + + // Real-time updates + socket.on('request_server_status', async () => { + try { + if (!socket.userId) { + socket.emit('error', { error: 'Not authenticated' }); + return; + } + + const player = await Player.findOne({ userId: socket.userId }); + if (!player || !player.currentServer) { + socket.emit('server_status', { server: null }); + return; + } + + const gameSystem = getGameSystem(); + const server = gameSystem.servers.get(player.currentServer); + + if (server) { + const players = await Player.find({ + userId: { $in: server.players } + }).select('userId username info.stats.level'); + + socket.emit('server_status', { + server: { + id: server.id, + name: server.name, + currentPlayers: server.players.length, + maxPlayers: server.maxPlayers, + players: players.map(p => ({ + userId: p.userId, + username: p.username, + level: p.info.stats.level + })) + } + }); + } + + } catch (error) { + logger.error('Error getting server status:', error); + socket.emit('error', { error: error.message }); + } + }); + + // Disconnection + socket.on('disconnect', async () => { + logger.info(`Client disconnected: ${socket.id}`); + + const userId = this.userSockets.get(socket.id); + if (userId) { + // Remove from tracking + this.connectedUsers.delete(userId); + this.userSockets.delete(socket.id); + + // Notify server if user was in one + const player = await Player.findOne({ userId }); + if (player && player.currentServer) { + this.broadcastToServer(player.currentServer, 'user_disconnected', { + userId, + username: player.username + }); + } + } + }); + } + + broadcastToServer(serverId, event, data) { + this.io.to(serverId).emit(event, data); + } + + sendToUser(userId, event, data) { + const socketId = this.connectedUsers.get(userId); + if (socketId) { + this.io.to(socketId).emit(event, data); + } + } + + broadcastToAll(event, data) { + this.io.emit(event, data); + } + + getConnectedUsers() { + return Array.from(this.connectedUsers.keys()); + } + + getUserCount() { + return this.connectedUsers.size; + } +} + +module.exports = SocketHandlers; diff --git a/Galaxy-Strike-Online-main/API/systems/EconomySystem.js b/Galaxy-Strike-Online-main/API/systems/EconomySystem.js new file mode 100644 index 0000000..5815d25 --- /dev/null +++ b/Galaxy-Strike-Online-main/API/systems/EconomySystem.js @@ -0,0 +1,385 @@ +const logger = require('../utils/logger'); + +class EconomySystem { + constructor() { + this.shopItems = { + ships: [], + weapons: [], + armors: [], + materials: [], + consumables: [] + }; + + this.dailyRewards = { + baseReward: 100, + consecutiveBonus: 50, + maxConsecutiveDays: 30 + }; + } + + async initialize() { + logger.info('Initializing Economy System...'); + + // Initialize shop items + await this.initializeShopItems(); + + logger.info('Economy System initialized successfully'); + } + + async initializeShopItems() { + // Ships + 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: 140 } + }, + { + id: 'starter_cruiser_epic', + name: 'Starter Cruiser IV', + type: 'ship', + rarity: 'epic', + price: 50000, + currency: 'credits', + description: 'Master starter cruiser with elite modifications', + texture: 'assets/textures/ships/starter_cruiser.png', + stats: { attack: 28, speed: 16, defense: 22, hull: 160 } + }, + { + id: 'starter_cruiser_legendary', + name: 'Starter Cruiser V', + type: 'ship', + rarity: 'legendary', + price: 100000, + currency: 'credits', + description: 'Legendary starter cruiser with unparalleled performance', + texture: 'assets/textures/ships/starter_cruiser.png', + stats: { attack: 35, speed: 18, defense: 28, hull: 180 } + } + ]; + + // Weapons + this.shopItems.weapons = [ + // Starter Blaster Variants + { + id: 'starter_blaster_common', + name: 'Common Blaster', + type: 'weapon', + rarity: 'common', + price: 1000, + 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: 2500, + currency: 'credits', + description: 'Improved blaster with better damage output', + texture: 'assets/textures/weapons/starter_blaster.png', + stats: { damage: 12, fireRate: 2.2, range: 5.5, energy: 6 } + }, + { + id: 'starter_blaster_rare', + name: 'Starter Blaster III', + type: 'weapon', + rarity: 'rare', + price: 5000, + currency: 'credits', + description: 'Advanced blaster with enhanced capabilities', + texture: 'assets/textures/weapons/starter_blaster.png', + stats: { damage: 15, fireRate: 2.5, range: 6, energy: 7 } + }, + { + id: 'starter_blaster_epic', + name: 'Starter Blaster IV', + type: 'weapon', + rarity: 'epic', + price: 10000, + currency: 'credits', + description: 'Elite blaster with superior performance', + texture: 'assets/textures/weapons/starter_blaster.png', + stats: { damage: 18, fireRate: 3, range: 6.5, energy: 8 } + }, + { + id: 'starter_blaster_legendary', + name: 'Starter Blaster V', + type: 'weapon', + rarity: 'legendary', + price: 20000, + 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 } + } + ]; + + // Armors + this.shopItems.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: 8000, + currency: 'credits', + description: 'Enhanced 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: 15000, + currency: 'credits', + description: 'Elite armor with advanced protection systems', + texture: 'assets/textures/armors/basic_armor.png', + stats: { defense: 15, durability: 35, weight: 3, energyShield: 10 } + }, + { + id: 'basic_armor_legendary', + name: 'Basic Armor V', + type: 'armor', + rarity: 'legendary', + price: 30000, + currency: 'credits', + description: 'Legendary armor with ultimate protection', + texture: 'assets/textures/armors/basic_armor.png', + stats: { defense: 20, durability: 40, weight: 3.5, energyShield: 15 } + } + ]; + + // Materials + this.shopItems.materials = [ + { + id: 'iron_ore', + name: 'Iron Ore', + type: 'material', + rarity: 'common', + price: 50, + currency: 'credits', + description: 'Raw iron ore used for crafting basic weapons and armor', + stackable: true + }, + { + id: 'copper_wire', + name: 'Copper Wire', + type: 'material', + rarity: 'common', + price: 75, + currency: 'credits', + description: 'Copper wiring used in electronic components', + stackable: true + }, + { + id: 'energy_crystal', + name: 'Energy Crystal', + type: 'material', + rarity: 'uncommon', + price: 200, + currency: 'credits', + description: 'Crystallized energy used for powered equipment', + stackable: true + }, + { + id: 'rare_metal', + name: 'Rare Metal', + type: 'material', + rarity: 'rare', + price: 500, + currency: 'credits', + description: 'Rare metallic alloy used for high-end crafting', + stackable: true + }, + { + id: 'advanced_components', + name: 'Advanced Components', + type: 'material', + rarity: 'rare', + price: 1000, + currency: 'credits', + description: 'Sophisticated electronic components for advanced ship systems', + stackable: true + } + ]; + + // Consumables + this.shopItems.consumables = [ + { + id: 'health_kit', + name: 'Health Kit', + type: 'consumable', + rarity: 'common', + price: 100, + currency: 'credits', + description: 'A medical kit that restores health', + consumable: true, + effect: { health: 50 } + }, + { + id: 'energy_pack', + name: 'Energy Pack', + type: 'consumable', + rarity: 'common', + price: 150, + currency: 'credits', + description: 'A pack that restores energy', + consumable: true, + effect: { energy: 25 } + }, + { + id: 'repair_kit', + name: 'Repair Kit', + type: 'consumable', + rarity: 'uncommon', + price: 300, + currency: 'credits', + description: 'A kit that repairs ship damage', + consumable: true, + effect: { health: 100 } + } + ]; + + logger.info(`Shop initialized with ${this.getTotalShopItems()} items`); + } + + getTotalShopItems() { + return Object.values(this.shopItems).reduce((total, category) => total + category.length, 0); + } + + getShopItems(category = null) { + if (category && this.shopItems[category]) { + return this.shopItems[category]; + } + return this.shopItems; + } + + getItem(itemId) { + for (const category of Object.values(this.shopItems)) { + const item = category.find(item => item.id === itemId); + if (item) return item; + } + return null; + } + + purchaseItem(userId, itemId, quantity = 1) { + const item = this.getItem(itemId); + if (!item) { + throw new Error('Item not found in shop'); + } + + const totalCost = item.price * quantity; + + return { + item, + quantity, + totalCost, + currency: item.currency + }; + } + + calculateDailyReward(consecutiveDays) { + const bonusMultiplier = Math.min(consecutiveDays - 1, this.dailyRewards.maxConsecutiveDays - 1); + const bonusAmount = bonusMultiplier * this.dailyRewards.consecutiveBonus; + const totalReward = this.dailyRewards.baseReward + bonusAmount; + + return { + baseReward: this.dailyRewards.baseReward, + consecutiveBonus: bonusAmount, + totalReward, + consecutiveDays + }; + } + + getRandomShopItems(category, count = 6) { + const items = this.shopItems[category] || []; + const shuffled = [...items].sort(() => Math.random() - 0.5); + return shuffled.slice(0, Math.min(count, items.length)); + } + + refreshShopInventory() { + logger.info('Refreshing shop inventory...'); + // This would typically involve database operations + // For now, we'll just log the refresh + return true; + } + + getShopStats() { + const stats = { + totalItems: this.getTotalShopItems(), + itemsByCategory: {}, + averagePriceByCategory: {} + }; + + for (const [category, items] of Object.entries(this.shopItems)) { + stats.itemsByCategory[category] = items.length; + + if (items.length > 0) { + const totalPrice = items.reduce((sum, item) => sum + item.price, 0); + stats.averagePriceByCategory[category] = Math.round(totalPrice / items.length); + } + } + + return stats; + } +} + +module.exports = EconomySystem; diff --git a/Galaxy-Strike-Online-main/API/systems/GameSystem.js b/Galaxy-Strike-Online-main/API/systems/GameSystem.js new file mode 100644 index 0000000..0279cc2 --- /dev/null +++ b/Galaxy-Strike-Online-main/API/systems/GameSystem.js @@ -0,0 +1,293 @@ +const logger = require('../utils/logger'); +const Player = require('../models/Player'); +const Ship = require('../models/Ship'); +const Inventory = require('../models/Inventory'); +const Economy = require('./EconomySystem'); + +class GameSystem { + constructor() { + this.players = new Map(); + this.servers = new Map(); + this.economy = new Economy(); + } + + async initializeGameSystems() { + logger.info('Initializing server-side game systems...'); + + // Initialize economy system + await this.economy.initialize(); + + logger.info('Game systems initialized successfully'); + } + + // Player management + async createPlayer(userId, playerData) { + try { + const player = new Player({ + userId, + ...playerData, + createdAt: new Date(), + lastLogin: new Date() + }); + + await player.save(); + this.players.set(userId, player); + + logger.info(`Created new player for user: ${userId}`); + return player; + } catch (error) { + logger.error('Error creating player:', error); + throw error; + } + } + + async loadPlayer(userId) { + try { + let player = this.players.get(userId); + + if (!player) { + player = await Player.findOne({ userId }).populate('ships inventory'); + if (player) { + this.players.set(userId, player); + } + } + + return player; + } catch (error) { + logger.error('Error loading player:', error); + throw error; + } + } + + async savePlayer(userId) { + try { + const player = this.players.get(userId); + if (player) { + await player.save(); + logger.info(`Saved player data for user: ${userId}`); + } + } catch (error) { + logger.error('Error saving player:', error); + throw error; + } + } + + // Ship management + async addShipToPlayer(userId, shipData) { + try { + const player = await this.loadPlayer(userId); + if (!player) { + throw new Error('Player not found'); + } + + const ship = new Ship({ + ...shipData, + userId, + acquiredAt: new Date() + }); + + await ship.save(); + player.ships.push(ship._id); + await player.save(); + + logger.info(`Added ship ${ship.name} to player ${userId}`); + return ship; + } catch (error) { + logger.error('Error adding ship to player:', error); + throw error; + } + } + + async equipShip(userId, shipId) { + try { + const player = await this.loadPlayer(userId); + if (!player) { + throw new Error('Player not found'); + } + + const ship = await Ship.findOne({ _id: shipId, userId }); + if (!ship) { + throw new Error('Ship not found'); + } + + // Unequip current ship + if (player.currentShip) { + await Ship.findByIdAndUpdate(player.currentShip, { isEquipped: false }); + } + + // Equip new ship + ship.isEquipped = true; + await ship.save(); + + player.currentShip = ship._id; + await player.save(); + + logger.info(`Equipped ship ${ship.name} for player ${userId}`); + return ship; + } catch (error) { + logger.error('Error equipping ship:', error); + throw error; + } + } + + // Server management + async createServer(serverData) { + try { + const serverId = `server_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; + + const server = { + id: serverId, + ...serverData, + createdAt: new Date(), + players: [], + status: 'active' + }; + + this.servers.set(serverId, server); + logger.info(`Created new server: ${serverId}`); + + return server; + } catch (error) { + logger.error('Error creating server:', error); + throw error; + } + } + + async joinServer(serverId, userId) { + try { + const server = this.servers.get(serverId); + if (!server) { + throw new Error('Server not found'); + } + + if (server.players.length >= server.maxPlayers) { + throw new Error('Server is full'); + } + + if (!server.players.includes(userId)) { + server.players.push(userId); + } + + logger.info(`Player ${userId} joined server ${serverId}`); + return server; + } catch (error) { + logger.error('Error joining server:', error); + throw error; + } + } + + async leaveServer(serverId, userId) { + try { + const server = this.servers.get(serverId); + if (!server) { + throw new Error('Server not found'); + } + + server.players = server.players.filter(id => id !== userId); + + if (server.players.length === 0) { + this.servers.delete(serverId); + logger.info(`Server ${serverId} deleted (no players)`); + } + + logger.info(`Player ${userId} left server ${serverId}`); + return server; + } catch (error) { + logger.error('Error leaving server:', error); + throw error; + } + } + + getServerList() { + return Array.from(this.servers.values()).map(server => ({ + id: server.id, + name: server.name, + type: server.type, + maxPlayers: server.maxPlayers, + currentPlayers: server.players.length, + status: server.status, + region: server.region, + createdAt: server.createdAt + })); + } + + // Game actions + async processGameAction(userId, actionData) { + try { + const player = await this.loadPlayer(userId); + if (!player) { + throw new Error('Player not found'); + } + + switch (actionData.type) { + case 'dungeon_enter': + return await this.handleDungeonEnter(player, actionData); + case 'ship_upgrade': + return await this.handleShipUpgrade(player, actionData); + case 'item_purchase': + return await this.handleItemPurchase(player, actionData); + case 'daily_reward': + return await this.handleDailyReward(player, actionData); + default: + throw new Error('Unknown action type'); + } + } catch (error) { + logger.error('Error processing game action:', error); + throw error; + } + } + + async handleDungeonEnter(player, data) { + // Dungeon logic will be implemented here + logger.info(`Player ${player.userId} entering dungeon`); + return { success: true, message: 'Dungeon entered' }; + } + + async handleShipUpgrade(player, data) { + // Ship upgrade logic will be implemented here + logger.info(`Player ${player.userId} upgrading ship`); + return { success: true, message: 'Ship upgraded' }; + } + + async handleItemPurchase(player, data) { + // Item purchase logic will be implemented here + logger.info(`Player ${player.userId} purchasing item`); + return { success: true, message: 'Item purchased' }; + } + + async handleDailyReward(player, data) { + // Daily reward logic will be implemented here + logger.info(`Player ${player.userId} claiming daily reward`); + return { success: true, message: 'Daily reward claimed' }; + } +} + +// Singleton instance +let gameSystem = null; + +async function initializeGameSystems() { + if (!gameSystem) { + gameSystem = new GameSystem(); + try { + await gameSystem.initializeGameSystems(); + } catch (error) { + logger.error('Failed to initialize game systems:', error); + throw error; + } + } + return gameSystem; +} + +function getGameSystem() { + if (!gameSystem) { + logger.warn('Game system not initialized. Call initializeGameSystems() first.'); + return null; + } + return gameSystem; +} + +module.exports = { + GameSystem, + initializeGameSystems, + getGameSystem +}; diff --git a/Galaxy-Strike-Online-main/API/tests/api.test.js b/Galaxy-Strike-Online-main/API/tests/api.test.js new file mode 100644 index 0000000..6c89004 --- /dev/null +++ b/Galaxy-Strike-Online-main/API/tests/api.test.js @@ -0,0 +1,220 @@ +const request = require('supertest'); +const mongoose = require('mongoose'); +const app = require('../server'); +const Player = require('../models/Player'); + +describe('API Tests', () => { + let token; + let testUser; + + beforeAll(async () => { + // Connect to test database + const mongoUri = process.env.MONGODB_TEST_URI || 'mongodb://localhost:27017/galaxystrikeonline_test'; + await mongoose.connect(mongoUri); + }); + + afterAll(async () => { + // Clean up and close connection + await Player.deleteMany({}); + await mongoose.connection.close(); + }); + + beforeEach(async () => { + // Clean up before each test + await Player.deleteMany({}); + }); + + describe('Authentication', () => { + test('POST /api/auth/register - should register new user', async () => { + const userData = { + username: 'testuser', + email: 'test@example.com', + password: 'password123' + }; + + const response = await request(app) + .post('/api/auth/register') + .send(userData) + .expect(201); + + expect(response.body).toHaveProperty('token'); + expect(response.body.user).toHaveProperty('username', userData.username); + expect(response.body.user).toHaveProperty('email', userData.email); + }); + + test('POST /api/auth/login - should login existing user', async () => { + // First register a user + const userData = { + username: 'testuser', + email: 'test@example.com', + password: 'password123' + }; + + await request(app) + .post('/api/auth/register') + .send(userData); + + // Then login + const loginData = { + email: userData.email, + password: userData.password + }; + + const response = await request(app) + .post('/api/auth/login') + .send(loginData) + .expect(200); + + expect(response.body).toHaveProperty('token'); + token = response.body.token; + testUser = response.body.user; + }); + + test('GET /api/auth/verify - should verify token', async () => { + // First login to get token + const userData = { + username: 'testuser', + email: 'test@example.com', + password: 'password123' + }; + + const registerResponse = await request(app) + .post('/api/auth/register') + .send(userData); + + token = registerResponse.body.token; + + const response = await request(app) + .get('/api/auth/verify') + .set('Authorization', `Bearer ${token}`) + .expect(200); + + expect(response.body).toHaveProperty('valid', true); + expect(response.body.user).toHaveProperty('username', userData.username); + }); + }); + + describe('Game API', () => { + beforeEach(async () => { + // Create and login a user for game tests + const userData = { + username: 'testuser', + email: 'test@example.com', + password: 'password123' + }; + + const loginResponse = await request(app) + .post('/api/auth/login') + .send({ + email: userData.email, + password: userData.password + }); + + token = loginResponse.body.token; + testUser = loginResponse.body.user; + }); + + test('GET /api/game/player - should get player data', async () => { + const response = await request(app) + .get('/api/game/player') + .set('Authorization', `Bearer ${token}`) + .expect(200); + + expect(response.body).toHaveProperty('userId'); + expect(response.body).toHaveProperty('stats'); + expect(response.body).toHaveProperty('attributes'); + }); + + test('GET /api/game/ships - should get player ships', async () => { + const response = await request(app) + .get('/api/game/ships') + .set('Authorization', `Bearer ${token}`) + .expect(200); + + expect(response.body).toHaveProperty('ships'); + expect(Array.isArray(response.body.ships)).toBe(true); + }); + + test('GET /api/game/inventory - should get player inventory', async () => { + const response = await request(app) + .get('/api/game/inventory') + .set('Authorization', `Bearer ${token}`) + .expect(200); + + expect(response.body).toHaveProperty('items'); + expect(response.body).toHaveProperty('summary'); + }); + + test('POST /api/game/daily-reward - should claim daily reward', async () => { + const response = await request(app) + .post('/api/game/daily-reward') + .set('Authorization', `Bearer ${token}`) + .expect(200); + + expect(response.body).toHaveProperty('success'); + }); + }); + + describe('Server API', () => { + beforeEach(async () => { + // Create and login a user for server tests + const userData = { + username: 'testuser', + email: 'test@example.com', + password: 'password123' + }; + + const loginResponse = await request(app) + .post('/api/auth/login') + .send({ + email: userData.email, + password: userData.password + }); + + token = loginResponse.body.token; + testUser = loginResponse.body.user; + }); + + test('GET /api/servers - should get server list', async () => { + const response = await request(app) + .get('/api/servers') + .set('Authorization', `Bearer ${token}`) + .expect(200); + + expect(response.body).toHaveProperty('servers'); + expect(response.body).toHaveProperty('totalServers'); + expect(Array.isArray(response.body.servers)).toBe(true); + }); + + test('POST /api/servers/create - should create new server', async () => { + const serverData = { + name: 'Test Server', + type: 'public', + maxPlayers: 10, + region: 'us-east' + }; + + const response = await request(app) + .post('/api/servers/create') + .set('Authorization', `Bearer ${token}`) + .send(serverData) + .expect(201); + + expect(response.body).toHaveProperty('message'); + expect(response.body.server).toHaveProperty('name', serverData.name); + expect(response.body.server).toHaveProperty('type', serverData.type); + }); + }); + + describe('Health Check', () => { + test('GET /health - should return health status', async () => { + const response = await request(app) + .get('/health') + .expect(200); + + expect(response.body).toHaveProperty('status', 'OK'); + expect(response.body).toHaveProperty('timestamp'); + expect(response.body).toHaveProperty('uptime'); + }); + }); +}); diff --git a/Galaxy-Strike-Online-main/API/utils/logger.js b/Galaxy-Strike-Online-main/API/utils/logger.js new file mode 100644 index 0000000..ca20d28 --- /dev/null +++ b/Galaxy-Strike-Online-main/API/utils/logger.js @@ -0,0 +1,27 @@ +const winston = require('winston'); + +const logger = winston.createLogger({ + level: process.env.LOG_LEVEL || 'info', + format: winston.format.combine( + winston.format.timestamp(), + winston.format.errors({ stack: true }), + winston.format.json() + ), + defaultMeta: { service: 'galaxystrikeonline-server' }, + transports: [ + new winston.transports.File({ filename: 'logs/error.log', level: 'error' }), + new winston.transports.File({ filename: 'logs/combined.log' }) + ] +}); + +// Add console transport for development +if (process.env.NODE_ENV !== 'production') { + logger.add(new winston.transports.Console({ + format: winston.format.combine( + winston.format.colorize(), + winston.format.simple() + ) + })); +} + +module.exports = logger; diff --git a/Galaxy-Strike-Online-main/Client/data/starbase-layout.json b/Galaxy-Strike-Online-main/Client/data/starbase-layout.json new file mode 100644 index 0000000..955519e --- /dev/null +++ b/Galaxy-Strike-Online-main/Client/data/starbase-layout.json @@ -0,0 +1,120 @@ +{ + "_readme": [ + "Galaxy Strike Online — Starbase World Layout", + "Edit this file to customize your starbase. Changes take effect next time you visit the Starbases tab.", + "", + "GRID", + " cols / rows : overall size of the world (min 8×8, max ~32×26 before performance drops)", + "", + "STYLE (global defaults — all optional hex strings)", + " wallColor / wallColorLeft / wallColorRight / wallColorTop", + " floorColorEven / floorColorOdd", + " doorColor / doorFrameColor", + "", + "WALLS — each entry draws a run of wall tiles", + " col, row : start position", + " span : number of tiles (default 1)", + " dir : 'h' horizontal | 'v' vertical", + " color / colorLeft / colorRight / colorTop : per-segment color overrides", + "", + "DOORS — walkable openings; panel slides up when player is adjacent", + " col, row : position", + " color : panel color override", + " frameColor : pillar/frame color override", + "", + "ROOMS — named regions; rendered as ghost labels + used for per-room wallpapers", + " id : unique string (used by the unlock / wallpaper system)", + " label : display text", + " bounds : { col, row, cols, rows }", + " unlock : item id required to unlock this room (omit = always open)", + " Locked rooms are filled with sealed-wall tiles and shown as 'LOCKED'", + "", + "PLAYER START", + " col, row : spawn tile (must be walkable floor)" + ], + + "name": "Starbase Alpha-7", + + "grid": { "cols": 26, "rows": 20 }, + + "style": { + "wallColor": "#00d4ff", + "wallColorLeft": "#0c1626", + "wallColorRight": "#0a1220", + "wallColorTop": "#1a2840", + "floorColorEven": "#151c2e", + "floorColorOdd": "#111827", + "doorColor": "#00ffcc", + "doorFrameColor": "#00d4ff" + }, + + "walls": [ + { "col": 0, "row": 0, "span": 26, "dir": "h", "_": "top wall" }, + { "col": 0, "row": 19, "span": 26, "dir": "h", "_": "bottom wall" }, + { "col": 0, "row": 0, "span": 20, "dir": "v", "_": "left wall" }, + { "col": 25, "row": 0, "span": 20, "dir": "v", "_": "right wall" }, + + { "col": 1, "row": 6, "span": 24, "dir": "h", "_": "main hall separator", + "color": "#00d4ff", "colorLeft": "#0d1a2e", "colorRight": "#0a1525", "colorTop": "#182a42" }, + + { "col": 7, "row": 7, "span": 13, "dir": "v", "_": "left inner wall" }, + { "col": 17, "row": 7, "span": 2, "dir": "v", "_": "right stub top" }, + { "col": 17, "row": 10, "span": 10, "dir": "v", "_": "right stub bottom", + "color": "#4488ff", "colorLeft": "#0a1830", "colorRight": "#080e20", "colorTop": "#102040" }, + + { "col": 7, "row": 13, "span": 10, "dir": "h", "_": "operations divider", + "color": "#ff00ff", "colorLeft": "#1a0a20", "colorRight": "#120616", "colorTop": "#200a30" }, + + { "col": 17, "row": 13, "span": 6, "dir": "h", "_": "vault corridor wall", + "color": "#ffcc00", "colorLeft": "#1a1200", "colorRight": "#140e00", "colorTop": "#221800" } + ], + + "doors": [ + { "col": 13, "row": 6, "dir": "h", "_": "main hall → command centre" }, + { "col": 17, "row": 6, "dir": "h", "color": "#4488ff", "frameColor": "#0066ff", "_": "main hall → right wing" }, + { "col": 7, "row": 10, "dir": "v", "_": "left wing ↔ command centre" }, + { "col": 17, "row": 9, "dir": "v", "color": "#ff88ff", "frameColor": "#cc44cc", "_": "command centre ↔ right wing" }, + { "col": 7, "row": 15, "dir": "v", "_": "left wing → operations" }, + { "col": 13, "row": 13, "dir": "h", "color": "#ff00ff", "frameColor": "#cc00cc", "_": "command centre → operations" }, + { "col": 20, "row": 13, "dir": "h", "color": "#ffcc00", "frameColor": "#ddaa00", "_": "right wing → vault corridor" } + ], + + "rooms": [ + { + "id": "main_hall", + "label": "Main Hall", + "bounds": { "col": 1, "row": 1, "cols": 24, "rows": 5 } + }, + { + "id": "left_wing", + "label": "Armory Wing", + "bounds": { "col": 1, "row": 7, "cols": 6, "rows": 12 }, + "unlock": "room_armory" + }, + { + "id": "command_centre", + "label": "Command Centre", + "bounds": { "col": 8, "row": 7, "cols": 9, "rows": 6 } + }, + { + "id": "right_wing", + "label": "Research Lab", + "bounds": { "col": 18, "row": 7, "cols": 7, "rows": 6 }, + "unlock": "room_research_lab" + }, + { + "id": "operations", + "label": "Operations Centre", + "bounds": { "col": 8, "row": 14, "cols": 9, "rows": 5 }, + "unlock": "room_operations" + }, + { + "id": "commanders_vault", + "label": "Commander's Vault", + "bounds": { "col": 18, "row": 14, "cols": 7, "rows": 5 }, + "unlock": "room_vault" + } + ], + + "playerStart": { "col": 13, "row": 3 } +} diff --git a/Client-Server/electron-main.js b/Galaxy-Strike-Online-main/Client/electron-main.js similarity index 100% rename from Client-Server/electron-main.js rename to Galaxy-Strike-Online-main/Client/electron-main.js diff --git a/Galaxy-Strike-Online-main/Client/index.html b/Galaxy-Strike-Online-main/Client/index.html new file mode 100644 index 0000000..280b427 --- /dev/null +++ b/Galaxy-Strike-Online-main/Client/index.html @@ -0,0 +1,3793 @@ + + + + + + Galaxy Strike Online - Space Idle MMORPG + + + + + + + + +
+
+ Galaxy Strike Online +
+
+ + + +
+
+ +
+ + + + + + + + + + + + + +
+
+

Fleet Management

+
+
+
+

Loading fleet…

+
+
+ + +
+
+ + + +
+
+
+ +
+
+
+

Select a Sector

+
+ Click any visible sector on the map to view details. +
+
+
+

Legend

+
Empty Space
+
Asteroid Field
+
Trade Hub
+
NPC Territory
+
Ruins
+
Void Rift
+
Your Home
+
+
+
+
+ + +
+ +
+
+

Branches

+ + + + + + +
+
+
Loading research…
+
+
+
+ + +
+ +

🏆 Commander Rankings

+
+ + + + + +
+
+
Select a category above to view rankings.
+
+
+ + +
+ + +

🚀 Fleet Missions

+

Send your ships on missions to earn resources, XP and loot. Missions run in real-time — check back to collect rewards.

+ +
+ + +
+ + +
+
+ +
+
+

📋 Mission Types

+
Loading…
+
+ +
+ + +
+

+ ⏱ Active Missions + +

+
+
No active missions
+
+
+
+
+ + + +
+ + +
+ + +

🛡 Alliance

+ + +
+
+
+

⚔ Create Alliance

+

Found your own alliance for 10,000 credits. Choose a unique name and 2–4 character tag.

+
+ + + + +
+
+
+

🔍 Find Alliance

+
+ + +
+
Search for alliances above
+
+
+
+ + + +
+ + +
+ + +

🏪 Player Market

+

Trade resources and items with other commanders. 2% listing fee. Listings expire after 24–72 hours.

+ +
+ + + +
+ + +
+
+ + + +
+
+
Loading market…
+
+
+ + + + + + +
+ + + + + +
+ + +

👥 Social

+ +
+ +
+
+

🤝 Add Friend

+
+ + +
+
+ +
+

📬 Friend Requests (0)

+
No pending requests
+
+ +
+

👾 Friends (0 online)

+
No friends yet
+
+
+ + +
+
+

+ 🌐 Faction Reputation (GDD §15.3) + +

+
Loading…
+
+
+

+ ⚔ Combat Log (GDD §9.5) + +

+
+
No combat history yet
+
+
+
+
+
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ 💬 Global Chat + +
+
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ Developer Console + +
+
+
+
+ +
+
+
+ + diff --git a/Client-Server/js/GameInitializer.js b/Galaxy-Strike-Online-main/Client/js/GameInitializer.js similarity index 52% rename from Client-Server/js/GameInitializer.js rename to Galaxy-Strike-Online-main/Client/js/GameInitializer.js index 5508e52..961427e 100644 --- a/Client-Server/js/GameInitializer.js +++ b/Galaxy-Strike-Online-main/Client/js/GameInitializer.js @@ -14,7 +14,7 @@ class GameInitializer { this.currentUser = null; this.socket = null; this.apiBaseUrl = 'https://api.korvarix.com/api'; // API Server - this.gameServerUrl = 'https://dev.gameserver.galaxystrike.online'; // Game Server for Socket.IO (local dev server) + this.gameServerUrl = 'https://dev.gameserver.galaxystrike.online'; // Game Server for Socket.IO console.log('[GAME INITIALIZER] Constructor - gameServerUrl set to:', this.gameServerUrl); } @@ -70,12 +70,13 @@ class GameInitializer { return; } - // FORCE THE URL - Override any undefined issues - const FORCED_URL = 'https://dev.gameserver.galaxystrike.online'; - console.log('[GAME INITIALIZER] FORCING URL to:', FORCED_URL); - console.log('[GAME INITIALIZER] Original this.gameServerUrl:', this.gameServerUrl); + // Resolve server URL: use serverData.url if set, otherwise gameServerUrl, then dev default + const FORCED_URL = (this.serverData && this.serverData.url) + ? this.serverData.url + : (this.gameServerUrl || 'https://dev.gameserver.galaxystrike.online'); + console.log('[GAME INITIALIZER] Connecting to:', FORCED_URL); - // Connect to the game server with FORCED URL + // Connect to the game server this.socket = io(FORCED_URL, { auth: { token: this.authToken, @@ -83,7 +84,7 @@ class GameInitializer { } }); - console.log('[GAME INITIALIZER] Socket.IO connection initiated to FORCED URL:', FORCED_URL); + console.log('[GAME INITIALIZER] Socket.IO connection initiated to:', FORCED_URL); // Socket event handlers this.socket.on('connect', () => { @@ -146,9 +147,117 @@ class GameInitializer { console.log('[GAME INITIALIZER] Game data saved:', data); this.onGameDataSaved(data); }); + + // Idle rewards events + this.socket.on('offlineRewardsClaimed', (data) => { + console.log('[GAME INITIALIZER] Offline rewards claimed:', data); + this.onOfflineRewardsClaimed(data); + }); + + this.socket.on('onlineIdleRewards', (data) => { + if (data.credits > 0 || data.experience > 0) { + console.log('[GAME INITIALIZER] Online idle rewards received:', data); + } + this.onOnlineIdleRewards(data); + }); + + // Quest completion events + this.socket.on('quest_completed', (data) => { + console.log('[GAME INITIALIZER] Quest completed:', data); + this.onQuestCompleted(data); + }); + + // Player stat update events + this.socket.on('player_stat_update', (data) => { + // Check for level-up + const oldLevel = this.serverPlayerData?.stats?.level || 0; + const newLevel = data.stats?.level || data.level || 0; + if (newLevel > oldLevel && oldLevel > 0) { + this.showNotification(`🎉 Level Up! Commander Level ${newLevel}`, 'success'); + if (window.GSO_Dashboard) GSO_Dashboard.refresh(this.serverPlayerData); + } + console.log('[GAME INITIALIZER] Player stat update:', data); + this.onPlayerStatUpdate(data); + }); + + // Quest data events + this.socket.on('quests_data', (data) => { + console.log('[GAME INITIALIZER] Quest data received:', data); + this.onQuestsData(data); + }); + + // PlayTime events + this.socket.on('playTimeUpdated', (data) => { + // Only log playtime updates every minute (600,000 ms) to reduce spam + if (!this.lastPlayTimeLog || Date.now() - this.lastPlayTimeLog > 60000) { + console.log('[GAME INITIALIZER] PlayTime updated:', `${Math.floor(data.playTime / 3600000)}h ${Math.floor((data.playTime % 3600000) / 60000)}m`); + this.lastPlayTimeLog = Date.now(); + } + this.onPlayTimeUpdated(data); + }); + + // Shop purchase events + this.socket.on('purchaseCompleted', (data) => { + console.log('[GAME INITIALIZER] Purchase completed:', data); + this.onPurchaseCompleted(data); + }); + + // Item system events + this.socket.on('shopItemsReceived', (data) => { + console.log('[GAME INITIALIZER] Shop items received:', data); + this.onShopItemsReceived(data); + }); + + this.socket.on('season_data', (data) => { + if (!data?.active) return; + const s = data.season; + const banner = document.getElementById('season-banner'); + if (banner) { + banner.style.display = 'flex'; + const set = (id,v) => { const el=document.getElementById(id); if(el) el.textContent=v; }; + set('season-icon', s.icon||'🌑'); + set('season-name', `Season ${s.id}: ${s.name}`); + set('season-desc', s.description||''); + set('season-eta', `${s.daysLeft} days remaining (${s.progress}%)`); + set('season-score', data.myScore ? `Your score: ${data.myScore.toLocaleString()}` : ''); + } + }); + + this.socket.on('galaxy_event_data', (data) => { + if (!data?.active) return; + const ev = data.event; + const banner = document.getElementById('galaxy-event-banner'); + if (banner) { + banner.style.display = 'flex'; + const set = (id,v) => { const el=document.getElementById(id); if(el) el.textContent=v; }; + set('gev-icon', ev.icon||'👾'); + set('gev-name', ev.name||'Galaxy Event'); + set('gev-desc', ev.desc||''); + set('gev-eta', ev.etaLabel||''); + set('gev-participants', `${ev.participantCount||0} commanders participating`); + } + }); + + this.socket.on('resource_update', (data) => { + // Keep serverPlayerData.resources in sync + if (this.serverPlayerData && data.resources) { + this.serverPlayerData.resources = data.resources; + } + if (window.GSO_Resources) GSO_Resources.update(data); + }); + + this.socket.on('itemDetailsReceived', (data) => { + console.log('[GAME INITIALIZER] Item details received:', data); + this.onItemDetailsReceived(data); + }); } onSocketConnected() { + // Expose socket globally for systems that need it + if (window.game) { + window.game.socket = this.socket; + } + // Join the server room this.socket.emit('joinServer', { serverId: this.serverData.id, @@ -232,6 +341,17 @@ class GameInitializer { if (data.success && data.playerData) { // Store server player data from authentication (this is our primary source) this.serverPlayerData = data.playerData; + this.currentUser = data.user; + + // CRITICAL: Force multiplayer mode and prevent fallback + if (window.smartSaveManager) { + console.log('[GAME INITIALIZER] FORCING multiplayer mode after authentication'); + window.smartSaveManager.setMultiplayerMode(true, this); + } + + // ItemSystem is now initialized by GameEngine - no need to initialize here + console.log('[GAME INITIALIZER] ItemSystem initialization handled by GameEngine'); + console.log('[GAME INITIALIZER] Using authentication data as primary source:', this.serverPlayerData); // NOW create GameEngine AFTER authentication is successful @@ -245,22 +365,12 @@ class GameInitializer { window.smartSaveManager.setMultiplayerMode(true, this); console.log('[GAME INITIALIZER] SmartSaveManager set to multiplayer mode'); - // Apply authentication data immediately + // Apply authentication data immediately (this will be stored for later) window.smartSaveManager.applyServerDataToGame(data.playerData); } - // Fallback: Apply authentication data to game if game is running - if (window.game && window.game.loadServerPlayerData) { - console.log('[GAME INITIALIZER] Applying authentication data to GameEngine:', data.playerData); - window.game.loadServerPlayerData(data.playerData); - console.log('[GAME INITIALIZER] Authentication data applied to GameEngine'); - - // Force UI refresh when authentication data is applied - if (window.game.systems && window.game.systems.ui && window.game.systems.ui.forceRefreshAllUI) { - console.log('[GAME INITIALIZER] Forcing UI refresh after authentication data application'); - window.game.systems.ui.forceRefreshAllUI(); - } - } + // NOTE: Don't apply to GameEngine here - it doesn't exist yet! + // The data will be applied in createGameEngineForMultiplayer() after the game is created. this.showNotification(`Welcome back! Level ${data.playerData.stats?.level || 1}`, 'success'); } else { @@ -276,11 +386,22 @@ class GameInitializer { // Create GameEngine instance window.game = new GameEngine(); + // CRITICAL: Set multiplayer mode BEFORE initializing systems + console.log('[GAME INITIALIZER] Setting multiplayer mode BEFORE initialization'); + window.game.setMultiplayerMode(true, this.socket, this.serverData, this.currentUser); + + // NOTE: Don't apply server data immediately - wait for full initialization + console.log('[GAME INITIALIZER] Server data ready, will apply after GameEngine initialization'); + console.log('[GAME INITIALIZER] - this.serverPlayerData:', !!this.serverPlayerData); + console.log('[GAME INITIALIZER] - window.game:', !!window.game); + console.log('[GAME INITIALIZER] - window.game.loadServerPlayerData:', !!window.game?.loadServerPlayerData); + // Initialize the game engine console.log('[GAME INITIALIZER] About to call window.game.init()'); const initPromise = window.game.init(); console.log('[GAME INITIALIZER] GameEngine.init() returned:', typeof initPromise, initPromise); + // Apply server data and refresh UI after initialization is complete initPromise.then(() => { console.log('[GAME INITIALIZER] GameEngine initialized successfully for multiplayer'); @@ -290,13 +411,23 @@ class GameInitializer { window.game.loadServerPlayerData(this.serverPlayerData); console.log('[GAME INITIALIZER] Server player data applied to GameEngine'); + // CRITICAL: Force immediate economy sync + if (window.game.systems && window.game.systems.economy && window.game.systems.economy.syncWithServerData) { + console.log('[GAME INITIALIZER] Forcing immediate economy sync with server data'); + window.game.systems.economy.syncWithServerData(this.serverPlayerData); + } + // Force UI refresh if (window.game.systems && window.game.systems.ui && window.game.systems.ui.forceRefreshAllUI) { console.log('[GAME INITIALIZER] Forcing UI refresh after data application'); window.game.systems.ui.forceRefreshAllUI(); + } else { + console.warn('[GAME INITIALIZER] UI refresh not available - systems:', !!window.game.systems, 'ui:', !!window.game.systems?.ui, 'forceRefreshAllUI:', !!window.game.systems?.ui?.forceRefreshAllUI); } } else { console.warn('[GAME INITIALIZER] No server player data or loadServerPlayerData method available'); + console.log('[GAME INITIALIZER] - this.serverPlayerData:', !!this.serverPlayerData); + console.log('[GAME INITIALIZER] - window.game.loadServerPlayerData:', !!window.game?.loadServerPlayerData); } // Start the game @@ -322,11 +453,11 @@ class GameInitializer { console.log('[GAME INITIALIZER] Data content:', data.data); console.log('[GAME INITIALIZER] Data keys:', data.data ? Object.keys(data.data) : 'No data object'); - if (data.success && data.data && Object.keys(data.data).length > 0) { - // Store server player data + // Only process if we don't already have good data from authentication + if (data.success && data.data && Object.keys(data.data).length > 0 && !this.serverPlayerData) { + console.log('[GAME INITIALIZER] Using gameDataLoaded as primary source (no auth data available)'); this.serverPlayerData = data.data; - console.log('[GAME INITIALIZER] Applying server data to game systems'); // Apply server data to game if game is running if (window.game && window.game.loadServerPlayerData) { window.game.loadServerPlayerData(data.data); @@ -335,17 +466,9 @@ class GameInitializer { if (window.game.systems && window.game.systems.ui && window.game.systems.ui.forceRefreshAllUI) { window.game.systems.ui.forceRefreshAllUI(); } - } else { - console.warn('[GAME INITIALIZER] No game or loadServerPlayerData method available'); } } else { - console.log('[GAME INITIALIZER] Ignoring empty game data - no player data to load'); - console.log('[GAME INITIALIZER] Data analysis:', { - success: data.success, - hasData: !!data.data, - dataKeys: data.data ? Object.keys(data.data) : [], - dataLength: data.data ? JSON.stringify(data.data).length : 0 - }); + console.log('[GAME INITIALIZER] Ignoring gameDataLoaded - already have data from authentication or data is empty'); } } @@ -414,7 +537,7 @@ class GameInitializer { // Configure game for multiplayer mode console.log('[GAME INITIALIZER] Configuring for multiplayer mode'); - window.game.setMultiplayerMode(true, this.socket, this.serverData, this.currentUser); + // Note: setMultiplayerMode already called in createGameEngineForMultiplayer() before initialization window.game.gameInitializer = this; // Store reference for server polling // DISABLE game logic in multiplayer - server is authoritative @@ -482,8 +605,24 @@ class GameInitializer { updateUIForMultiplayerMode() { // Update UI elements to show multiplayer mode const playerName = document.getElementById('playerName'); - if (playerName && this.currentUser) { - playerName.textContent = this.currentUser.username; + const playerTitle = document.getElementById('playerTitle'); + const playerUsername = document.getElementById('playerUsername'); + + if (this.currentUser) { + // Set the player name (rank/title) + if (playerName) { + playerName.textContent = 'Commander'; + } + + // Set the player title + if (playerTitle) { + playerTitle.textContent = '- Rookie Pilot'; + } + + // Set the username next to the level + if (playerUsername) { + playerUsername.textContent = this.currentUser.username + ' '; + } } // Show multiplayer-specific UI elements @@ -666,13 +805,223 @@ class GameInitializer { username: this.currentUser.username }); } else { - console.warn('[GAME INITIALIZER] Cannot authenticate - missing socket or user data'); - if (!this.socket) { - console.warn('[GAME INITIALIZER] Socket is null/undefined'); + // Try to get from localStorage as fallback + const storedUser = localStorage.getItem('currentUser'); + if (storedUser) { + try { + const user = JSON.parse(storedUser); + const username = user.username || 'anonymous'; + } catch (e) { + console.warn('[GAME INITIALIZER] Failed to parse stored user, using default'); + } + } else { + window.game.showNotification('No offline rewards available', 'info', 3000); } - if (!this.currentUser) { - console.warn('[GAME INITIALIZER] Current user is null/undefined'); + } + } + + onOnlineIdleRewards(data) { + if (window.game && window.game.systems) { + // Only log if there are actual rewards to process + if (data.credits > 0 || data.experience > 0 || data.energy > 0) { + console.log('[GAME INITIALIZER] Processing online idle rewards:', data); } + + // Update player balance with online idle rewards + if (data.credits > 0) { + // Update the Economy system's local credits + if (window.game.systems.economy) { + window.game.systems.economy.credits += data.credits; + + // Update UI immediately + if (window.game.ui) { + window.game.ui.updatePlayerStats(); + console.log('[GAME INITIALIZER] UI updated with new credits'); + } + } else { + console.warn('[GAME INITIALIZER] Economy system not available for credit update'); + } + + // Show notification + window.game.showNotification(`+${data.credits} credits (online idle)`, 'success', 2000); + } + + // Update experience if provided + if (data.experience > 0 && window.game.systems.player) { + window.game.systems.player.addExperience(data.experience); + console.log('[GAME INITIALIZER] Added idle experience:', data.experience); + } + } else { + console.warn('[GAME INITIALIZER] Game systems not available for idle rewards'); + } + } + + onPlayTimeUpdated(data) { + // PlayTime updates are handled in the socket listener with throttled logging + if (window.game && window.game.systems && window.game.systems.player) { + const player = window.game.systems.player; + + // Update playTime from server + player.stats.playTime = data.playTime; + + // Update UI + player.updateUI(); + } + } + + onPurchaseCompleted(data) { + if (data.success) { + // Update local player data with server response + if (window.game && window.game.systems && window.game.systems.economy) { + const economy = window.game.systems.economy; + + // Update currency balance + if (data.currency === 'credits') { + economy.credits = data.newBalance; + } else if (data.currency === 'gems') { + economy.gems = data.newBalance; + } + + // Add item to inventory if it's a consumable or material + if (data.item && (data.item.type === 'consumable' || data.item.type === 'material')) { + if (window.game.systems.inventory) { + console.log('[GAME INITIALIZER] Adding item to inventory:', data.item); + window.game.systems.inventory.addItem(data.item, data.quantity || 1); + window.game.showNotification(`${data.item.name} added to inventory!`, 'success', 3000); + } + } + + // Update owned ships if it's a ship + if (data.item && data.item.type === 'ship') { + if (!economy.ownedShips) economy.ownedShips = []; + if (!economy.ownedShips.includes(data.item.id)) { + economy.ownedShips.push(data.item.id); + console.log('[GAME INITIALIZER] Ship added to owned ships:', data.item.id); + + // Add ship to BaseSystem ship gallery + if (window.game.systems.baseSystem) { + console.log('[GAME INITIALIZER] BaseSystem available, adding ship to gallery'); + const shipData = { + id: data.item.id, + name: data.item.name, + class: data.item.name.replace(/\s+/g, '_').toLowerCase(), // Generate class from name + level: 1, + stats: data.item.stats || {}, + texture: data.item.texturePath || `assets/textures/ships/${data.item.id}.png`, + isCurrent: false, + rarity: data.item.rarity || 'common' + }; + + console.log('[GAME INITIALIZER] Ship data prepared:', shipData); + + // Initialize ship gallery if needed + if (!window.game.systems.baseSystem.purchasedShips) { + console.log('[GAME INITIALIZER] Initializing ship gallery'); + window.game.systems.baseSystem.initializeShipGallery(); + } + + // Check if ship already exists + const existingShip = window.game.systems.baseSystem.purchasedShips.find(s => s.id === shipData.id); + if (!existingShip) { + // Add ship to gallery + window.game.systems.baseSystem.purchasedShips.push(shipData); + console.log('[GAME INITIALIZER] Ship added to gallery. Total ships:', window.game.systems.baseSystem.purchasedShips.length); + + // Update the ship gallery UI + window.game.systems.baseSystem.updateShipGallery(); + console.log('[GAME INITIALIZER] Ship gallery UI updated'); + } else { + console.log('[GAME INITIALIZER] Ship already exists in gallery:', shipData.id); + } + } else { + console.error('[GAME INITIALIZER] BaseSystem not available for ship gallery'); + } + } + } + + // Update owned cosmetics if it's a cosmetic + if (data.item && data.item.type === 'cosmetic') { + if (!economy.ownedCosmetics) economy.ownedCosmetics = []; + if (!economy.ownedCosmetics.includes(data.item.id)) { + economy.ownedCosmetics.push(data.item.id); + console.log('[GAME INITIALIZER] Cosmetic added to owned cosmetics:', data.item.id); + } + } + + // Request fresh economy data from server to ensure sync + if (economy.requestEconomyData) { + setTimeout(() => { + economy.requestEconomyData(); + }, 500); + } + + // Also request economy data immediately to prevent reset + if (economy.requestEconomyData) { + economy.requestEconomyData(); + } + + // Update UI + economy.updateUI(); + + // Update inventory UI if item was added + if (data.item && (data.item.type === 'consumable' || data.item.type === 'material')) { + if (window.game.systems.inventory) { + window.game.systems.inventory.updateUI(); + } + } + + // Show success message + window.game.showNotification(`Purchased ${data.item.name}!`, 'success', 3000); + } + } else { + // Show error message + window.game.showNotification(`Purchase failed: ${data.error}`, 'error', 5000); + } + } + + onShopItemsReceived(data) { + if (data.success && window.game && window.game.systems && window.game.systems.itemSystem) { + console.log('[GAME INITIALIZER] Processing shop items data structure:', Object.keys(data)); + + // Handle both old (data.items) and new (data.shopItems) structures + let itemsToProcess = null; + + if (data.shopItems && typeof data.shopItems === 'object') { + // New structure: categorized items + console.log('[GAME INITIALIZER] Using new shop structure'); + itemsToProcess = Object.values(data.shopItems).flat(); + console.log('[GAME INITIALIZER] Flattened', itemsToProcess.length, 'items from categories'); + } else if (data.items && Array.isArray(data.items)) { + // Old structure: flat array + console.log('[GAME INITIALIZER] Using old shop structure'); + itemsToProcess = data.items; + console.log('[GAME INITIALIZER] Processing', itemsToProcess.length, 'items from flat array'); + } else { + console.warn('[GAME INITIALIZER] Invalid shop items structure:', data); + return; + } + + // Update ItemSystem with server data + window.game.systems.itemSystem.processServerItems(itemsToProcess); + console.log('[GAME INITIALIZER] ItemSystem updated with server shop items'); + + // Update Economy shop UI + if (window.game.systems.economy) { + window.game.systems.economy.updateShopUI(); + console.log('[GAME INITIALIZER] Economy shop UI updated'); + } + } else { + console.warn('[GAME INITIALIZER] Failed to receive shop items:', data); + } + } + + onItemDetailsReceived(data) { + // This is handled by the ItemSystem directly + // Just log for debugging + if (data.success) { + console.log('[GAME INITIALIZER] Item details received for:', data.item.name); + } else { + console.warn('[GAME INITIALIZER] Failed to receive item details:', data); } } @@ -696,6 +1045,101 @@ class GameInitializer { this.currentUser = null; this.serverPlayerData = null; } + + onQuestCompleted(data) { + console.log('[GAME INITIALIZER] Processing quest completion:', data); + + // Show quest completion notification + if (window.game && window.game.showNotification) { + const questName = data.questId.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()); + window.game.showNotification(`Quest Completed: ${questName}! 🎯`, 'success', 5000); + } + + // Award quest rewards + if (data.rewards) { + if (data.rewards.experience > 0 && window.game.systems.player) { + window.game.systems.player.addExperience(data.rewards.experience); + console.log('[GAME INITIALIZER] Awarded quest experience:', data.rewards.experience); + } + + if (data.rewards.credits > 0 && window.game.systems.economy) { + window.game.systems.economy.credits += data.rewards.credits; + console.log('[GAME INITIALIZER] Awarded quest credits:', data.rewards.credits); + } + } + + // Update quest UI if quest system exists + if (window.game && window.game.systems && window.game.systems.questSystem) { + window.game.systems.questSystem.completeQuest(data.questId, data.rewards); + + // Force fetch fresh quest data from server (Fix #1 - Corrected) + console.log('[GAME INITIALIZER] Requesting fresh quest data from server after completion'); + + // Request fresh quest data from server + if (window.gameInitializer && window.gameInitializer.socket) { + window.gameInitializer.socket.emit('get_quests'); + } + } + + // Force quest UI refresh for server-driven quests + if (typeof updateQuestDisplay === 'function') { + updateQuestDisplay(); + } + + // Update UI + if (window.game && window.game.ui) { + window.game.ui.updatePlayerStats(); + } + } + + onPlayerStatUpdate(data) { + console.log('[GAME INITIALIZER] Processing player stat update:', data); + + if (window.game && window.game.systems && window.game.systems.player && window.game.systems.player.stats) { + // Update the player stat + window.game.systems.player.stats[data.stat] = data.value; + console.log('[GAME INITIALIZER] Updated player stat:', data.stat, '=', data.value); + + // Update UI to reflect the change + if (window.game.ui) { + window.game.ui.updatePlayerStats(); + } + + // Update quest system to check for quest progress + if (window.game.systems.questSystem) { + window.game.systems.questSystem.checkQuestAvailability(); + } + } + } + + onQuestsData(data) { + console.log('[GAME INITIALIZER] Processing quest data from server:', data); + console.log('[GAME INITIALIZER] Quest data keys:', Object.keys(data || {})); + console.log('[GAME INITIALIZER] Main quests count:', (data?.mainQuests || []).length); + console.log('[GAME INITIALIZER] Daily quests count:', (data?.dailyQuests || []).length); + console.log('[GAME INITIALIZER] Weekly quests count:', (data?.weeklyQuests || []).length); + + if (window.game && window.game.systems && window.game.systems.questSystem) { + // Load quest data into the quest system + if (window.game.systems.questSystem.loadServerQuests) { + console.log('[GAME INITIALIZER] Calling loadServerQuests with data'); + window.game.systems.questSystem.loadServerQuests(data); + console.log('[GAME INITIALIZER] Loaded quest data into quest system'); + } else { + console.log('[GAME INITIALIZER] loadServerQuests method not found'); + } + + // Update quest UI + if (typeof updateQuestDisplay === 'function') { + console.log('[GAME INITIALIZER] Calling updateQuestDisplay'); + updateQuestDisplay(); + } else { + console.log('[GAME INITIALIZER] updateQuestDisplay function not found'); + } + } else { + console.log('[GAME INITIALIZER] Quest system not available'); + } + } } // Create global instance diff --git a/Client-Server/js/SaveSystemIntegration.js b/Galaxy-Strike-Online-main/Client/js/SaveSystemIntegration.js similarity index 89% rename from Client-Server/js/SaveSystemIntegration.js rename to Galaxy-Strike-Online-main/Client/js/SaveSystemIntegration.js index 91f5e77..85f5e2e 100644 --- a/Client-Server/js/SaveSystemIntegration.js +++ b/Galaxy-Strike-Online-main/Client/js/SaveSystemIntegration.js @@ -11,40 +11,15 @@ function integrateWithGameEngine() { // Store original save method const originalSave = window.game.save; - // Override save method + // Override game save method window.game.save = async function() { - console.log('[SAVE INTEGRATION] Game save called'); + // console.log('[SAVE INTEGRATION] Game save called'); - // In multiplayer mode, don't save - server is authoritative - if (window.smartSaveManager && window.smartSaveManager.isMultiplayer) { - console.log('[SAVE INTEGRATION] Multiplayer mode - client save disabled, server is authoritative'); - this.showNotification('Server manages your game data', 'info', 2000); - return true; - } - - try { - // Get current game data - const gameData = this.getSaveData ? this.getSaveData() : {}; - - // Use SmartSaveManager for singleplayer - if (window.smartSaveManager) { - const success = await window.smartSaveManager.savePlayerData(gameData); - - if (success) { - this.showNotification('Game saved!', 'success', 2000); - } else { - this.showNotification('Save failed!', 'error', 3000); - } - - return success; - } else { - // Fallback to original save - return await originalSave.call(this); - } - } catch (error) { - console.error('[SAVE INTEGRATION] Save error:', error); - this.showNotification('Save error!', 'error', 3000); - return false; + if (window.smartSaveManager) { + await window.smartSaveManager.save(); + } else { + // Fallback to original save if SmartSaveManager not available + return await originalSave.call(this); } }; @@ -225,7 +200,7 @@ function addSaveModeUI() { indicator.id = 'save-mode-indicator'; indicator.style.cssText = ` position: fixed; - top: 10px; + bottom: 10px; right: 10px; background: rgba(0, 0, 0, 0.8); color: white; diff --git a/Client-Server/js/SmartSaveManager.js b/Galaxy-Strike-Online-main/Client/js/SmartSaveManager.js similarity index 63% rename from Client-Server/js/SmartSaveManager.js rename to Galaxy-Strike-Online-main/Client/js/SmartSaveManager.js index 36bb306..91234e1 100644 --- a/Client-Server/js/SmartSaveManager.js +++ b/Galaxy-Strike-Online-main/Client/js/SmartSaveManager.js @@ -14,9 +14,11 @@ class SmartSaveManager { } setMultiplayerMode(isMultiplayer, gameInitializer = null) { + const oldMode = this.isMultiplayer; this.isMultiplayer = isMultiplayer; this.gameInitializer = gameInitializer; + console.log(`[SMART SAVE] Mode change: ${oldMode ? 'multiplayer' : 'singleplayer'} -> ${isMultiplayer ? 'multiplayer' : 'singleplayer'}`); console.log(`[SMART SAVE] Set to ${isMultiplayer ? 'multiplayer' : 'singleplayer'} mode`); if (isMultiplayer && gameInitializer) { @@ -136,9 +138,52 @@ class SmartSaveManager { this.serverPlayerData = serverData; - // Apply to game if game is running - if (window.game && window.game.loadPlayerData) { - window.game.loadPlayerData(serverData); + // Apply to game if game is running (try both methods) + if (window.game) { + console.log('[SMART SAVE] Game is available, checking for data loading methods'); + console.log('[SMART SAVE] - loadPlayerData:', !!window.game.loadPlayerData); + console.log('[SMART SAVE] - loadServerPlayerData:', !!window.game.loadServerPlayerData); + + if (window.game.loadServerPlayerData) { + console.log('[SMART SAVE] Using loadServerPlayerData method'); + window.game.loadServerPlayerData(serverData); + console.log('[SMART SAVE] Server data applied to game, forcing UI refresh'); + + // Force UI refresh after applying server data + if (window.game.systems && window.game.systems.ui && window.game.systems.ui.forceRefreshAllUI) { + console.log('[SMART SAVE] Forcing UI refresh after server data application'); + window.game.systems.ui.forceRefreshAllUI(); + } else { + console.warn('[SMART SAVE] UI refresh not available after server data application - systems:', !!window.game.systems, 'ui:', !!window.game.systems?.ui, 'forceRefreshAllUI:', !!window.game.systems?.ui?.forceRefreshAllUI); + } + } else if (window.game.loadPlayerData) { + console.log('[SMART SAVE] Using loadPlayerData method'); + window.game.loadPlayerData(serverData); + console.log('[SMART SAVE] Server data applied to game, forcing UI refresh'); + + // Force UI refresh after applying server data + if (window.game.systems && window.game.systems.ui && window.game.systems.ui.forceRefreshAllUI) { + console.log('[SMART SAVE] Forcing UI refresh after server data application'); + window.game.systems.ui.forceRefreshAllUI(); + } else { + console.warn('[SMART SAVE] UI refresh not available after server data application - systems:', !!window.game.systems, 'ui:', !!window.game.systems?.ui, 'forceRefreshAllUI:', !!window.game.systems?.ui?.forceRefreshAllUI); + + // Try delayed UI refresh since UIManager might not be ready yet + console.log('[SMART SAVE] Attempting delayed UI refresh...'); + setTimeout(() => { + if (window.game.systems && window.game.systems.ui && window.game.systems.ui.forceRefreshAllUI) { + console.log('[SMART SAVE] Delayed UI refresh successful'); + window.game.systems.ui.forceRefreshAllUI(); + } else { + console.warn('[SMART SAVE] Delayed UI refresh also failed - systems:', !!window.game.systems, 'ui:', !!window.game.systems?.ui, 'forceRefreshAllUI:', !!window.game.systems?.ui?.forceRefreshAllUI); + } + }, 2000); // Wait 2 seconds for systems to initialize + } + } else { + console.warn('[SMART SAVE] No data loading method available on game object'); + } + } else { + console.warn('[SMART SAVE] Game not available for data application'); } // Store for game engine diff --git a/Client-Server/js/core/DebugLogger.js b/Galaxy-Strike-Online-main/Client/js/core/DebugLogger.js similarity index 83% rename from Client-Server/js/core/DebugLogger.js rename to Galaxy-Strike-Online-main/Client/js/core/DebugLogger.js index ad59a1f..947f156 100644 --- a/Client-Server/js/core/DebugLogger.js +++ b/Galaxy-Strike-Online-main/Client/js/core/DebugLogger.js @@ -5,7 +5,9 @@ class DebugLogger { constructor() { - this.debugEnabled = true; // Always enabled + // Completely disable debug logging to prevent console flooding + this.debugEnabled = false; + this.startTime = performance.now(); this.stepTimers = new Map(); this.debugLogs = []; // Store logs in memory @@ -15,10 +17,15 @@ class DebugLogger { this.logger = window.logger || null; // Log initialization - this.log('=== DEBUG SESSION STARTED ==='); + if (this.debugEnabled) { + this.log('=== DEBUG SESSION STARTED ==='); + } } async log(message, data = null) { + // Skip logging if debug is disabled + if (!this.debugEnabled) return; + const timestamp = new Date().toISOString(); const stackTrace = new Error().stack; @@ -55,13 +62,13 @@ class DebugLogger { this.debugLogs.shift(); } - // Always log to console - console.log(`[DEBUG] ${message}`, data || ''); + // Skip console logging to prevent flooding + // console.log(`[DEBUG] ${message}`, data || ''); - // Log performance data to console - if (performanceData.memory) { - console.log(`[PERF] ${performanceData.elapsed} | Memory: ${performanceData.memory.used}/${performanceData.memory.total}`); - } + // Skip performance logging to prevent flooding + // if (performanceData.memory) { + // console.log(`[PERF] ${performanceData.elapsed} | Memory: ${performanceData.memory.used}/${performanceData.memory.total}`); + // } // Use existing logger if available if (this.logger) { @@ -79,6 +86,9 @@ class DebugLogger { } async startStep(stepName) { + // Skip logging if debug is disabled + if (!this.debugEnabled) return; + this.stepTimers.set(stepName, performance.now()); await this.log(`STEP START: ${stepName}`, { type: 'step_start', @@ -88,6 +98,9 @@ class DebugLogger { } async endStep(stepName, data = null) { + // Skip logging if debug is disabled + if (!this.debugEnabled) return; + const startTime = this.stepTimers.get(stepName); const duration = startTime ? (performance.now() - startTime).toFixed(2) : 'N/A'; @@ -101,6 +114,9 @@ class DebugLogger { } async logStep(stepName, data = null) { + // Skip logging if debug is disabled + if (!this.debugEnabled) return; + await this.log(`STEP: ${stepName}`, { type: 'step', step: stepName, diff --git a/Galaxy-Strike-Online-main/Client/js/core/Economy.js b/Galaxy-Strike-Online-main/Client/js/core/Economy.js new file mode 100644 index 0000000..0c2497f --- /dev/null +++ b/Galaxy-Strike-Online-main/Client/js/core/Economy.js @@ -0,0 +1,905 @@ +/** + * Galaxy Strike Online - Economy System + * Manages player currency, transactions, and shop functionality + * Now uses server-side ItemSystem for all item data + */ + +class Economy { + constructor(gameEngine) { + this.game = gameEngine; + + // Currency - don't override in multiplayer mode, will be set by server data + if (window.smartSaveManager?.isMultiplayer) { + this.credits = 0; // Will be updated by server + this.gems = 0; // Will be updated by server + this.premiumCurrency = 0; // Will be updated by server + } else { + this.credits = 10000; // Starting credits for singleplayer + this.gems = 50; // Starting premium currency + this.premiumCurrency = 0; // Additional premium currency + } + + // Transaction history + this.transactions = []; + + // Shop categories + this.shopCategories = { + ships: 'Ships', + weapons: 'Weapons', + armors: 'Armors', + cosmetics: 'Cosmetics', + consumables: 'Consumables', + materials: 'Materials' + }; + + // Random shop system - now uses server ItemSystem + 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.currentShopData = null; // Current shop data from server + this.SHOP_REFRESH_INTERVAL = 2 * 60 * 60 * 1000; // 2 hours in milliseconds + this.MAX_ITEMS_PER_CATEGORY = 8; + this.categoryPurchaseLimits = {}; // Track purchases per category per refresh + + // Shop items - now loaded from server ItemSystem + this.shopItems = null; // Will be populated by ItemSystem in multiplayer + + // Owned cosmetics + this.ownedCosmetics = []; + + // Owned ships + this.ownedShips = []; + + console.log('[ECONOMY] Economy system initialized'); + + // Initialize global purchase function + Economy.initGlobalPurchaseFunction(); + } + + // Create global purchase function for shop buttons + static initGlobalPurchaseFunction() { + window.purchaseShopItem = function(itemId) { + console.log('[GLOBAL] Purchase shop item called:', itemId); + + if (window.game && window.game.systems && window.game.systems.economy) { + window.game.systems.economy.purchaseItem(itemId, 1); + } else { + console.error('[GLOBAL] Economy system not available for purchase'); + } + }; + + // Add test function for idle system + window.testIdleRewards = function() { + console.log('[GLOBAL] Testing idle rewards...'); + + if (window.game && window.game.socket) { + window.game.socket.emit('testIdleRewards', {}); + + // Listen for response + window.game.socket.once('testIdleRewards', (data) => { + console.log('[GLOBAL] Test idle rewards response:', data); + }); + } else { + console.error('[GLOBAL] No socket available for idle test'); + } + }; + + // Add socket event monitor + window.monitorSocketEvents = function() { + if (window.game && window.game.socket) { + console.log('[GLOBAL] Monitoring socket events...'); + + // Monitor all incoming events + const originalOn = window.game.socket.on; + window.game.socket.on = function(event, callback) { + const wrappedCallback = function(data) { + if (event === 'onlineIdleRewards' || event === 'economy_data') { + console.log('[SOCKET MONITOR] Received event:', event, data); + } + return callback(data); + }; + return originalOn.call(this, event, wrappedCallback); + }; + + console.log('[GLOBAL] Socket event monitoring enabled'); + } else { + console.error('[GLOBAL] No socket available for monitoring'); + } + }; + + // Add function to give player energy for testing dungeons + window.addEnergy = function(amount = 50) { + if (window.game && window.game.systems && window.game.systems.player) { + const player = window.game.systems.player; + const oldEnergy = player.attributes.energy || 0; + player.attributes.energy = Math.min(oldEnergy + amount, player.attributes.maxEnergy || 100); + console.log('[GLOBAL] Added energy:', oldEnergy, '->', player.attributes.energy); + + // Update UI + if (player.updateUI) { + player.updateUI(); + } + + // Update dungeon UI if available + if (window.game.systems.dungeonSystem && window.game.systems.dungeonSystem.updateUI) { + window.game.systems.dungeonSystem.updateUI(); + } + + return player.attributes.energy; + } else { + console.error('[GLOBAL] Player system not available'); + return 0; + } + }; + + console.log('[GLOBAL] Global functions initialized - purchaseShopItem() and testIdleRewards() available'); + } + + /** + * Set up socket listeners for economy data synchronization + */ + setupSocketListeners() { + if (!this.game.socket) { + console.warn('[ECONOMY] No socket available for economy sync'); + return; + } + + // Listen for economy data updates from server + this.game.socket.on('economy_data', (data) => { + console.log('[ECONOMY] Received economy data from server:', data); + console.log('[ECONOMY] Current credits before update:', this.credits); + console.log('[ECONOMY] Current gems before update:', this.gems); + + this.credits = data.credits || 0; + this.gems = data.gems || 0; + + console.log('[ECONOMY] Updated credits:', this.credits); + console.log('[ECONOMY] Updated gems:', this.gems); + + // Update UI immediately + if (this.game.ui) { + this.game.ui.updatePlayerStats(); + } + + console.log('[ECONOMY] Economy synced - Credits:', this.credits, 'Gems:', this.gems); + }); + + // Note: onlineIdleRewards is handled by GameInitializer to avoid duplicate event handling + + // Listen for play time updates from server + this.game.socket.on('playTimeUpdated', (data) => { + console.log('[ECONOMY] Received play time update from server:', data); + + // Update player stats if available + if (this.game.systems.player && this.game.systems.player.stats) { + this.game.systems.player.stats.playTime = data.playTime; + } + }); + } + + /** + * Request economy data from server + */ + requestEconomyData() { + if (this.game.socket) { + console.log('[ECONOMY] Requesting economy data from server'); + this.game.socket.emit('get_economy_data'); + } else { + console.warn('[ECONOMY] Cannot request economy data - no socket available'); + } + } + + async initialize() { + console.log('[ECONOMY] Initializing economy system'); + + // In multiplayer mode, wait for ItemSystem to be ready (handled by event listener) + this.game.on('itemSystemReady', () => { + console.log('[ECONOMY] ItemSystem is ready, updating shop UI'); + this.updateShopUI(); + }); + if (window.smartSaveManager?.isMultiplayer) { + console.log('[ECONOMY] Multiplayer mode - waiting for ItemSystem to be ready'); + // ItemSystem initialization removed - wait for event instead + } else { + console.log('[ECONOMY] Singleplayer mode - using local shop data'); + // Initialize random shop for singleplayer + this.initializeRandomShop(); + } + + console.log('[ECONOMY] Economy system initialized'); + } + + // Shop functionality - now uses ItemSystem in multiplayer + purchaseItem(itemId, quantity = 1) { + const debugLogger = window.debugLogger; + + // In multiplayer mode, send request to server + if (window.smartSaveManager?.isMultiplayer) { + if (debugLogger) debugLogger.logStep('Sending purchase request to server', { + itemId: itemId, + quantity: quantity + }); + + // Send purchase request to server + if (window.game && window.game.socket) { + window.game.socket.emit('purchaseItem', { + itemId: itemId, + quantity: quantity + }); + + // Show loading message + this.game.showNotification('Processing purchase...', 'info', 2000); + } else { + this.game.showNotification('Not connected to server', 'error', 3000); + } + return; + } + + // Singleplayer mode - use local logic + 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; + } + + // Show success message + this.game.showNotification(`Purchased ${item.name}!`, 'success', 3000); + + 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; + + console.log('[ECONOMY] Looking for shop item:', itemId); + console.log('[ECONOMY] Multiplayer mode:', window.smartSaveManager?.isMultiplayer); + console.log('[ECONOMY] ItemSystem available:', !!(this.game.systems.itemSystem)); + + // In multiplayer mode, use ItemSystem (required) + if (window.smartSaveManager?.isMultiplayer) { + // Check if ItemSystem is ready before using it + if (!this.game.systems.itemSystem || !this.game.systems.itemSystem.itemCatalog) { + console.log('[ECONOMY] ItemSystem not ready yet, cannot find shop item'); + if (debugLogger) debugLogger.logStep('Shop item lookup failed - ItemSystem not ready', { + itemId: itemId, + multiplayer: true + }); + return null; + } + + // Search in ItemSystem catalog + const item = this.game.systems.itemSystem.itemCatalog.get(itemId); + if (item) { + console.log('[ECONOMY] Found item in ItemSystem:', item.name); + return item; + } else { + console.log('[ECONOMY] Item not found in ItemSystem:', itemId); + return null; + } + } else { + // Singleplayer mode - search in local random shop + for (const categoryItems of Object.values(this.randomShopItems)) { + const item = categoryItems.find(item => item.id === itemId); + if (item) return item; + } + return null; + } + } + + // Purchase methods + 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 }; + + // Update player ship + player.ship = { + name: ship.name, + class: ship.id, + texture: ship.texture, + stats: ship.stats || {} + }; + + // Update player attributes + if (ship.stats) { + player.attributes = { ...player.attributes, ...ship.stats }; + } + + // Add to owned ships + if (!player.ownedShips) { + player.ownedShips = []; + } + if (!player.ownedShips.includes(ship.id)) { + player.ownedShips.push(ship.id); + } + + // Add ship to BaseSystem ship gallery (singleplayer) + if (this.game.systems.baseSystem) { + const shipData = { + id: ship.id, + name: ship.name, + class: ship.name.replace(/\s+/g, '_').toLowerCase(), // Generate class from name + level: 1, + stats: ship.stats || {}, + texture: ship.texture || `assets/textures/ships/${ship.id}.png`, + isCurrent: false, + rarity: ship.rarity || 'common' + }; + + // Initialize ship gallery if needed + if (!this.game.systems.baseSystem.purchasedShips) { + this.game.systems.baseSystem.initializeShipGallery(); + } + + // Add ship to gallery + this.game.systems.baseSystem.purchasedShips.push(shipData); + + // Update the ship gallery UI + this.game.systems.baseSystem.updateShipGallery(); + + console.log('[ECONOMY] Ship added to BaseSystem gallery (singleplayer):', shipData.name); + } + + if (debugLogger) debugLogger.logStep('Ship purchase completed', { + shipId: ship.id, + shipName: ship.name, + oldShipName: oldShipName, + oldShipClass: oldShipClass, + newShipName: ship.name, + newShipClass: ship.id, + oldAttributes: oldAttributes, + newAttributes: player.attributes + }); + } + + 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; + + // Create item object for inventory + const item = { + id: consumable.id, + name: consumable.name, + type: consumable.type, + rarity: consumable.rarity, + quantity: quantity, + description: consumable.description, + texture: consumable.texture, + stats: consumable.stats || {}, + acquired: new Date().toISOString() + }; + + try { + const oldInventorySize = inventory.items.length; + inventory.addItem(item); + + if (debugLogger) debugLogger.logStep('Consumable purchase completed', { + itemId: consumable.id, + itemName: consumable.name, + quantity: quantity, + oldInventorySize: oldInventorySize, + newInventorySize: inventory.items.length + }); + } catch (error) { + console.error('[ECONOMY] Error adding consumable to inventory:', error); + this.game.showNotification('Failed to add item to inventory', 'error', 3000); + } + } + + purchaseMaterial(material, quantity) { + const debugLogger = window.debugLogger; + const inventory = this.game.systems.inventory; + + // Create item object for inventory + const item = { + id: material.id, + name: material.name, + type: material.type, + rarity: material.rarity, + quantity: quantity, + description: material.description, + texture: material.texture, + stackable: material.stackable || true, + acquired: new Date().toISOString() + }; + + try { + const oldInventorySize = inventory.items.length; + inventory.addItem(item); + + if (debugLogger) debugLogger.logStep('Material purchase completed', { + itemId: material.id, + itemName: material.name, + quantity: quantity, + oldInventorySize: oldInventorySize, + newInventorySize: inventory.items.length + }); + } catch (error) { + console.error('[ECONOMY] Error adding material to inventory:', error); + this.game.showNotification('Failed to add item to inventory', 'error', 3000); + } + } + + // Currency management + addCredits(amount, source = 'unknown') { + const oldCredits = this.credits; + this.credits += amount; + + // Add transaction + this.addTransaction({ + type: 'credit', + amount: amount, + source: source, + balance: this.credits, + timestamp: new Date().toISOString() + }); + + console.log(`[ECONOMY] Added ${amount} credits from ${source}. New balance: ${this.credits}`); + this.updateUI(); + + return this.credits - oldCredits; + } + + addGems(amount, source = 'unknown') { + const oldGems = this.gems; + this.gems += amount; + + // Add transaction + this.addTransaction({ + type: 'gem', + amount: amount, + source: source, + balance: this.gems, + timestamp: new Date().toISOString() + }); + + console.log(`[ECONOMY] Added ${amount} gems from ${source}. New balance: ${this.gems}`); + this.updateUI(); + + return this.gems - oldGems; + } + + canAfford(cost, currency = 'credits') { + if (currency === 'credits') { + return this.credits >= cost; + } else if (currency === 'gems') { + return this.gems >= cost; + } else if (currency === 'premium') { + return this.premiumCurrency >= cost; + } + return false; + } + + // Transaction management + addTransaction(transaction) { + this.transactions.push(transaction); + this.transactionHistory.push(transaction); + + // Keep only last 100 transactions in memory + if (this.transactions.length > 100) { + this.transactions = this.transactions.slice(-100); + } + } + + // Manual sync with server data - call this to force update + syncWithServerData(serverPlayerData) { + console.log('[ECONOMY] Manual sync with server data:', { + serverCredits: serverPlayerData?.stats?.credits, + serverGems: serverPlayerData?.stats?.gems, + currentCredits: this.credits, + currentGems: this.gems + }); + + if (serverPlayerData?.stats?.credits !== undefined) { + this.credits = serverPlayerData.stats.credits; + console.log('[ECONOMY] Updated credits from server:', this.credits); + } + + if (serverPlayerData?.stats?.gems !== undefined) { + this.gems = serverPlayerData.stats.gems; + console.log('[ECONOMY] Updated gems from server:', this.gems); + } + + // Update UI after sync + this.updateUI(); + } + + // UI updates + updateUI() { + // Debug logging to track current values + console.log('[ECONOMY] updateUI called - Current values:', { + credits: this.credits, + gems: this.gems, + gameSystemsAvailable: !!(this.game && this.game.systems), + uiSystemAvailable: !!(this.game && this.game.systems && this.game.systems.ui) + }); + + // Update resource display + if (this.game.systems.ui) { + this.game.systems.ui.updateResourceDisplay(); + } + + // Update shop UI if open + this.updateShopUI(); + } + + updateShopUI() { + const debugLogger = window.debugLogger; + + console.log('[ECONOMY] updateShopUI called'); + + if (this.game.multiplayerMode && this.game.itemSystem) { + // Support both .catalog getter (new) and .shopItemsByCategory (legacy) + const shopItems = this.game.itemSystem.catalog || this.game.itemSystem.shopItemsByCategory || {}; + const activeCategory = this.game.itemSystem.activeCategory || 'ships'; + const categoryItems = shopItems[activeCategory] || []; + this.renderShopItems(categoryItems); + } else { + // Singleplayer mode - use local shop data + const items = Object.values(this.randomShopItems).flat(); + + // Convert to categorized structure for consistency + const categorizedItems = this.randomShopItems || {}; + this.renderShopItems(categorizedItems); + } + } + + renderShopItems(items) { + const shopItemsElement = document.getElementById('shopItems'); + if (!shopItemsElement) return; + + const activeCategory = document.querySelector('.shop-cat-btn.active')?.dataset.category || 'ships'; + console.log('[ECONOMY] Active shop category:', activeCategory); + + // Handle new shop data structure (items by category) or old structure (flat array) + let categoryItems = []; + + if (items && typeof items === 'object' && !Array.isArray(items)) { + // New structure: { ships: [...], weapons: [...], ... } + categoryItems = items[activeCategory] || []; + console.log('[ECONOMY] Using new shop structure - found', Object.keys(items).length, 'categories'); + } else if (Array.isArray(items)) { + // Old structure: flat array of items + const targetItemType = activeCategory.slice(0, -1); // Remove 's' from 'ships', 'weapons', etc. + categoryItems = items.filter(item => item.type === targetItemType); + console.log('[ECONOMY] Using old shop structure - filtered', items.length, 'total items'); + } else { + console.warn('[ECONOMY] Invalid shop items structure:', typeof items); + shopItemsElement.innerHTML = '

No items available

'; + return; + } + + console.log('[ECONOMY] Filtered items for category', activeCategory, ':', categoryItems.length, 'items'); + console.log('[ECONOMY] Item types in category:', categoryItems.map(item => item.type)); + + if (categoryItems.length === 0) { + shopItemsElement.innerHTML = '

No items available in this category

'; + return; + } + + shopItemsElement.innerHTML = categoryItems.map(item => { + const canAfford = this.canAfford(item.price, item.currency); + const isOwned = item.type === 'cosmetic' && this.ownedCosmetics.includes(item.id); + + // Generate image URL - server will serve images + const imageUrl = this.getItemImageUrl(item); + const placeholderUrl = 'https://dev.gameserver.galaxystrike.online/images/ui/placeholder.png'; + + return ` +
+
+
+ ${item.name} +
+
+
+

${item.name}

+ ${item.rarity} +
+
+

${item.description}

+
+ ${this.formatPrice(item)} +
+
+ +
+
+
+ `; + }).join(''); + + // Add event listeners to purchase buttons + shopItemsElement.querySelectorAll('.shop-item-purchase-btn').forEach(button => { + button.addEventListener('click', (e) => { + e.preventDefault(); + e.stopPropagation(); + + const itemId = button.getAttribute('data-item-id'); + if (itemId && !button.disabled) { + console.log('[ECONOMY] Purchase button clicked for item:', itemId); + this.purchaseItem(itemId, 1); + } + }); + }); + } + + formatPrice(item) { + if (!item.price) return 'Free'; + + const currency = item.currency || 'credits'; + const price = this.game.formatNumber(item.price); + + return `${price} ${currency}`; + } + + /** + * Get image URL for an item from server + */ + getItemImageUrl(item) { + if (!item) return 'https://dev.gameserver.galaxystrike.online/images/ui/placeholder.png'; + + // For multiplayer, ALWAYS get from server + if (window.smartSaveManager?.isMultiplayer && this.game.socket) { + const serverUrl = this.getServerUrl(); + + // Map item types to proper server paths + switch (item.type) { + case 'ship': + return `${serverUrl}/images/ships/${item.id}.png`; + case 'weapon': + return `${serverUrl}/images/weapons/${item.id}.png`; + case 'armor': + return `${serverUrl}/images/armors/${item.id}.png`; + case 'material': + return `${serverUrl}/images/items/materials/${item.id}.png`; + case 'consumable': + return `${serverUrl}/images/items/consumables/${item.id}.png`; + case 'cosmetic': + return `${serverUrl}/images/items/cosmetics/${item.id}.png`; + default: + return `${serverUrl}/images/ui/placeholder.png`; + } + } + + // For singleplayer, use local texture path (if available) + if (item.texture) { + return item.texture; + } + + // Fallback to server placeholder + return 'https://dev.gameserver.galaxystrike.online/images/ui/placeholder.png'; + } + + /** + * Get server URL for image requests + */ + getServerUrl() { + // Get server URL from socket connection + if (this.game.socket && this.game.socket.io && this.game.socket.io.uri) { + return this.game.socket.io.uri.replace('/socket.io', ''); + } + // Fallback to environment variable or production server + return process.env.SERVER_URL || 'https://dev.gameserver.galaxystrike.online'; + } + + // Save/Load functionality + save() { + return { + credits: this.credits, + gems: this.gems, + premiumCurrency: this.premiumCurrency, + transactions: this.transactions, + ownedCosmetics: this.ownedCosmetics, + shopData: { + randomShopItems: this.randomShopItems, + categoryPurchaseLimits: this.categoryPurchaseLimits, + lastShopRefresh: this.lastShopRefresh + } + }; + } + + load(data) { + if (data.credits !== undefined) this.credits = data.credits; + if (data.gems !== undefined) this.gems = data.gems; + if (data.premiumCurrency !== undefined) this.premiumCurrency = data.premiumCurrency; + if (data.transactions) this.transactions = data.transactions; + if (data.ownedCosmetics) this.ownedCosmetics = data.ownedCosmetics; + + // Load shop data + if (data.shopData) { + this.randomShopItems = data.shopData.randomShopItems || {}; + this.categoryPurchaseLimits = data.shopData.categoryPurchaseLimits || {}; + this.lastShopRefresh = data.shopData.lastShopRefresh || null; + } + + this.updateUI(); + } + + // Reset functionality + reset() { + const oldState = { + credits: this.credits, + gems: this.gems, + premiumCurrency: this.premiumCurrency, + transactionCount: this.transactions.length, + ownedCosmeticsCount: this.ownedCosmetics.length + }; + + this.credits = 1000; + this.gems = 10; + this.premiumCurrency = 0; + this.transactions = []; + this.ownedCosmetics = []; + + // Reset daily rewards + localStorage.removeItem('lastDailyReward'); + + this.updateUI(); + + return oldState; + } + + clear() { + const oldState = { + credits: this.credits, + gems: this.gems, + premiumCurrency: this.premiumCurrency, + transactionCount: this.transactions.length, + ownedCosmeticsCount: this.ownedCosmetics.length + }; + + this.credits = 0; + this.gems = 0; + this.premiumCurrency = 0; + this.transactions = []; + this.ownedCosmetics = []; + + this.updateUI(); + + return oldState; + } + + // Initialize random shop for singleplayer (minimal implementation) + initializeRandomShop() { + console.log('[ECONOMY] Random shop not available in singleplayer mode'); + this.randomShopItems = {}; + } + + // Get system statistics + getStats() { + return { + credits: this.credits, + gems: this.gems, + premiumCurrency: this.premiumCurrency, + transactionCount: this.transactions.length, + ownedCosmeticsCount: this.ownedCosmetics.length, + shopItemsCount: this.game.systems.itemSystem && this.game.systems.itemSystem.itemCatalog ? + this.game.systems.itemSystem.getStats().totalItems : 0 + }; + } +} + +// Export for use in other modules +if (typeof module !== 'undefined' && module.exports) { + module.exports = Economy; +} else { + window.Economy = Economy; +} diff --git a/Galaxy-Strike-Online-main/Client/js/core/GameEngine.js b/Galaxy-Strike-Online-main/Client/js/core/GameEngine.js new file mode 100644 index 0000000..7be3764 --- /dev/null +++ b/Galaxy-Strike-Online-main/Client/js/core/GameEngine.js @@ -0,0 +1,753 @@ +/** + * Galaxy Strike Online - Game Engine + * Core game loop and state management + */ + +class GameEngine extends EventTarget { + constructor() { + // Must call super() first since we extend EventTarget + super(); + + // Basic game state + this.isRunning = false; + this.isPaused = false; + this.gameTime = 0; + this.lastSaveTime = 0; + this.autoSaveInterval = 5000; // 5 seconds + this.gameLogicInterval = 1000; // 1 second for game updates + + // Game systems + this.systems = {}; + + // Save slot configuration + this.saveSlotInfo = { + slot: 1, + useFileSystem: true + }; + + // Game state + this.state = { + paused: false, + currentTab: 'dashboard', + notifications: [] + }; + + // Event listeners + this.eventListeners = new Map(); + + // Initialize immediately + this.init(); + } + + setMultiplayerMode(isMultiplayer, socket = null, serverData = null, currentUser = null) { + const debugLogger = window.debugLogger; + + console.log('[GAME ENGINE] Setting multiplayer mode:', isMultiplayer); + console.log('[GAME ENGINE] Previous mode was:', this.isMultiplayer); + if (debugLogger) debugLogger.logStep('setMultiplayerMode', { isMultiplayer, previousMode: this.isMultiplayer }); + + // CRITICAL: Once set to multiplayer, never allow fallback to singleplayer + if (this.isMultiplayer && !isMultiplayer) { + console.warn('[GAME ENGINE] ATTEMPTED FALLBACK TO SINGLEPLAYER - BLOCKING!'); + console.log('[GAME ENGINE] Preserving multiplayer mode'); + return; // Don't allow fallback to singleplayer + } + + this.isMultiplayer = isMultiplayer; + this.socket = socket; + this.serverData = serverData; + this.currentUser = currentUser; + + // Store multiplayer settings for systems that need them + this.multiplayerConfig = { + isMultiplayer, + socket, + serverData, + currentUser + }; + + console.log('[GAME ENGINE] Multiplayer mode configured:', { + isMultiplayer, + hasSocket: !!socket, + hasServerData: !!serverData, + hasCurrentUser: !!currentUser + }); + } + + // Get random integer between min and max (inclusive) + getRandomInt(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; + } + + // Get random float between min and max + getRandomFloat(min, max) { + return Math.random() * (max - min) + min; + } + + async init() { + console.log('[GAME ENGINE] Initializing game engine'); + const logger = window.logger; + const debugLogger = window.debugLogger; + + if (logger) await logger.info('Initializing game engine'); + if (debugLogger) await debugLogger.startStep('gameEngineInit'); + + try { + // In multiplayer mode, use simplified initialization to avoid hanging + if (this.isMultiplayer) { + console.log('[GAME ENGINE] Using simplified multiplayer initialization'); + try { + await this.initializeMultiplayerSystems(); + console.log('[GAME ENGINE] Multiplayer initialization complete - skipping event listeners'); + } catch (multiplayerError) { + console.error('[GAME ENGINE] Multiplayer systems initialization failed:', multiplayerError); + // Don't fall back to singleplayer - keep multiplayer mode but with minimal systems + console.log('[GAME ENGINE] Continuing with minimal multiplayer systems'); + // Create essential systems only + this.systems.player = new Player(this); + this.systems.inventory = new Inventory(this); + this.systems.economy = new Economy(this); + this.systems.itemSystem = new ItemSystem(this); + this.systems.idleSystem = new IdleSystem(this); + } + } else { + // Full initialization for singleplayer + await this.initializeSystemsForLoad(); + + if (debugLogger) await debugLogger.logStep('Systems initialized, setting up event listeners'); + + // Set up event listeners (only in singleplayer to avoid conflicts) + await this.setupEventListeners(); + } + + if (debugLogger) await debugLogger.endStep('gameEngineInit', { + systemsInitialized: Object.keys(this.systems).length, + eventListeners: this.eventListeners.size + }); + + } catch (error) { + console.error('Failed to initialize game:', error); + if (logger) await logger.errorEvent(error, 'Game Engine Initialization'); + if (debugLogger) await debugLogger.errorEvent(error, 'Game Engine Initialization'); + } + } + + // Simplified multiplayer-only system initialization to avoid hanging + async initializeMultiplayerSystems() { + console.log('[GAME ENGINE] Initializing multiplayer systems'); + + try { + // Initialize texture manager first + this.systems.textureManager = new TextureManager(this); + + // Create essential systems immediately + console.log('[GAME ENGINE] Creating essential systems'); + this.systems.player = new Player(this); + this.systems.inventory = new Inventory(this); + this.systems.economy = new Economy(this); + this.systems.ui = new UIManager(this); + this.systems.idleSystem = new IdleSystem(this); + this.systems.itemSystem = new ItemSystem(this); + + console.log('[GAME ENGINE] Essential systems created successfully'); + + // Initialize ItemSystem ONCE and emit event when ready + console.log('[GAME ENGINE] Initializing ItemSystem (single initialization)'); + this.systems.itemSystem.initialize().then(() => { + console.log('[GAME ENGINE] ItemSystem fully initialized, emitting ready event'); + this.emit('itemSystemReady'); + }).catch(error => { + console.error('[GAME ENGINE] ItemSystem initialization failed:', error); + // Still emit event so Economy can fallback gracefully + this.emit('itemSystemReady'); + }); + + // Initialize Economy (without ItemSystem - it will wait for the event) + console.log('[GAME ENGINE] Initializing Economy system'); + this.systems.economy.initialize().catch(error => { + console.error('[GAME ENGINE] Economy initialization failed:', error); + }); + + // Create additional systems asynchronously to avoid blocking + setTimeout(() => { + console.log('[GAME ENGINE] Creating additional multiplayer systems asynchronously'); + + if (typeof SkillSystem !== 'undefined') { + this.systems.skillSystem = new SkillSystem(this); + } + if (typeof DungeonSystem !== 'undefined') { + this.systems.dungeonSystem = new DungeonSystem(this); + // Initialize server-driven dungeon system + this.systems.dungeonSystem.initialize().then(() => { + console.log('[GAME ENGINE] DungeonSystem initialized with server data'); + }).catch(error => { + console.error('[GAME ENGINE] Failed to initialize DungeonSystem:', error); + }); + } + if (typeof QuestSystem !== 'undefined') { + this.systems.questSystem = new QuestSystem(this); + console.log('[GAME ENGINE] QuestSystem created'); + } + if (typeof CraftingSystem !== 'undefined') { + this.systems.crafting = new CraftingSystem(this); + } + if (typeof BaseSystem !== 'undefined') { + this.systems.base = new BaseSystem(this); + } + console.log('[GAME ENGINE] All multiplayer systems created asynchronously'); + }, 100); // Create after 100ms delay + + } catch (error) { + console.error('[GAME ENGINE] Error in multiplayer systems initialization:', error); + // Don't re-throw - allow game to continue with basic systems + console.log('[GAME ENGINE] Continuing with available systems'); + } + } + + async initializeSystemsForLoad() { + const logger = window.logger; + const debugLogger = window.debugLogger; + + if (debugLogger) await debugLogger.startStep('initializeSystemsForLoad'); + + if (logger) { + await logger.timeAsync('Game Systems Initialization for Load', async () => { + await logger.info('Initializing game systems for loading'); + + if (debugLogger) await debugLogger.logStep('Initializing TextureManager'); + // Initialize texture manager first + this.systems.textureManager = new TextureManager(this); + if (logger) await logger.systemEvent('TextureManager', 'Initialized'); + if (debugLogger) await debugLogger.logStep('TextureManager initialized'); + + if (debugLogger) await debugLogger.logStep('Creating Player system (without initialization)'); + // Create systems but don't initialize with default data + this.systems.player = new Player(this); + if (logger) await logger.systemEvent('Player', 'Created'); + if (debugLogger) await debugLogger.logStep('Player system created'); + + if (debugLogger) await debugLogger.logStep('Creating Inventory system (without initialization)'); + this.systems.inventory = new Inventory(this); + if (logger) await logger.systemEvent('Inventory', 'Created'); + if (debugLogger) await debugLogger.logStep('Inventory system created'); + + if (debugLogger) await debugLogger.logStep('Creating Economy system (without initialization)'); + this.systems.economy = new Economy(this); + if (logger) await logger.systemEvent('Economy', 'Created'); + if (debugLogger) await debugLogger.logStep('Economy system created'); + + // In multiplayer mode, skip singleplayer systems + if (!this.isMultiplayer) { + if (debugLogger) await debugLogger.logStep('Creating IdleSystem'); + this.systems.idleSystem = new IdleSystem(this); + if (logger) await logger.systemEvent('IdleSystem', 'Created'); + if (debugLogger) await debugLogger.logStep('IdleSystem created'); + + if (debugLogger) await debugLogger.logStep('Creating ItemSystem'); + this.systems.itemSystem = new ItemSystem(this); + if (logger) await logger.systemEvent('ItemSystem', 'Created'); + if (debugLogger) await debugLogger.logStep('ItemSystem created'); + } else { + console.log('[GAME ENGINE] Multiplayer mode - skipping singleplayer systems (IdleSystem, ItemSystem)'); + } + + if (debugLogger) await debugLogger.logStep('Creating UIManager'); + if (typeof UIManager !== 'undefined') { + console.log('[GAME ENGINE] UIManager class found, creating real UIManager'); + this.systems.ui = new UIManager(this); + // Expose UIManager globally for button onclick handlers + window.uiManager = this.systems.ui; + window.game.systems.ui = this.systems.ui; + if (logger) await logger.systemEvent('UIManager', 'Created'); + if (debugLogger) await debugLogger.logStep('UIManager created and exposed'); + } else { + console.error('[GAME ENGINE] UIManager class not found - this should not happen!'); + if (debugLogger) await debugLogger.error('UIManager class not found - this should not happen!'); + } + + if (debugLogger) await debugLogger.endStep('initializeSystemsForLoad', { + systemsCreated: Object.keys(this.systems).length + }); + }); + } + } + + async startGame(continueGame = false) { + const logger = window.logger; + const debugLogger = window.debugLogger; + + console.log('[GAME ENGINE] startGame called with continueGame =', continueGame); + if (logger) await logger.info('Starting game', { continueGame }); + if (debugLogger) await debugLogger.startStep('startGame', { continueGame }); + + try { + if (continueGame) { + console.log('[GAME ENGINE] Loading existing save data...'); + if (debugLogger) await debugLogger.logStep('Loading existing save data'); + await this.loadGame(); + console.log('[GAME ENGINE] Save data loaded'); + } else { + console.log('[GAME ENGINE] Creating new game...'); + if (debugLogger) await debugLogger.logStep('Creating new game'); + await this.newGame(); + console.log('[GAME ENGINE] New game created'); + } + + // Start game loop + this.start(); + console.log('[GAME ENGINE] Game loop started'); + if (debugLogger) await debugLogger.logStep('Game loop started'); + + const loadingStatus = document.getElementById('loadingStatus'); + if (loadingStatus) { + console.log('[GAME ENGINE] Hiding loading status text'); + if (debugLogger) await debugLogger.logStep('Hiding loading status text'); + loadingStatus.classList.add('hidden'); + } + + const gameInterface = document.getElementById('gameInterface'); + if (gameInterface) { + console.log('[GAME ENGINE] Showing game interface'); + if (debugLogger) await debugLogger.logStep('Showing game interface'); + gameInterface.classList.remove('hidden'); + } else { + console.warn('[GAME ENGINE] gameInterface element not found'); + if (debugLogger) await debugLogger.warn('gameInterface element not found'); + } + + if (logger) await logger.info('Game started successfully'); + if (debugLogger) await debugLogger.endStep('startGame', { + continueGame, + isRunning: this.isRunning, + gameTime: this.gameTime + }); + + } catch (error) { + console.error('[GAME ENGINE] Failed to start game:', error); + if (logger) await logger.errorEvent(error, 'Game Start'); + if (debugLogger) await debugLogger.errorEvent(error, 'Game Start'); + } + } + + start() { + const debugLogger = window.debugLogger; + + if (this.isRunning) { + if (debugLogger) debugLogger.log('GameEngine.start() called but game is already running', { + isRunning: this.isRunning, + gameTime: this.gameTime + }); + return; + } + + if (debugLogger) debugLogger.logStep('Starting game engine', { + gameLogicInterval: this.gameLogicInterval + }); + + this.isRunning = true; + this.lastUpdate = Date.now(); + + // Start game logic timer (completely independent of frame rate) + console.log('[GAME ENGINE] Starting game logic timer with interval:', this.gameLogicInterval); + this.gameLogicTimer = setInterval(() => { + this.updateGameLogic(); + }, this.gameLogicInterval); + + // Start auto-save + this.startAutoSave(); + + console.log('[GAME ENGINE] Game engine started'); + if (debugLogger) debugLogger.logStep('Game engine started successfully', { + gameLogicInterval: this.gameLogicInterval, + autoSaveInterval: this.autoSaveInterval + }); + } + + updateGameLogic() { + const debugLogger = window.debugLogger; + + // Use fixed 1-second interval for all updates + const fixedDelta = 1000; // 1 second in milliseconds + + if (this.state.paused) { + if (debugLogger) debugLogger.logStep('Game logic update called but game is paused', { + gameTime: this.gameTime, + fixedDelta: fixedDelta + }); + return; + } + + this.gameTime += fixedDelta; + + // Update player play time with fixed delta + if (this.systems.player && this.systems.player.updatePlayTime) { + this.systems.player.updatePlayTime(fixedDelta); + } + + // Update all systems with fixed delta + for (const [name, system] of Object.entries(this.systems)) { + if (system && typeof system.update === 'function') { + try { + system.update(fixedDelta); + } catch (error) { + console.error(`[GAME ENGINE] Error updating ${name} system:`, error); + if (debugLogger) debugLogger.errorEvent(error, `Update ${name} system`); + } + } + } + + // Update UI displays (money, gems, energy) after system updates + if (this.systems && this.systems.ui) { + try { + this.systems.ui.updateUI(); + } catch (error) { + console.error('[GAME ENGINE] Error updating UI:', error); + } + } + + // Emit game updated event + this.emit('gameUpdated', { gameTime: this.gameTime }); + } + + startAutoSave() { + const debugLogger = window.debugLogger; + + // Load saved interval or use default + const savedInterval = localStorage.getItem('autoSaveInterval'); + this.autoSaveInterval = savedInterval ? parseInt(savedInterval) : 5; + + console.log(`[GAME ENGINE] Starting auto-save with ${this.autoSaveInterval} minute interval`); + + // Clear any existing timer + this.stopAutoSave(); + + // Set up new timer + this.autoSaveTimer = setInterval(async () => { + console.log('[GAME ENGINE] Auto-save timer triggered - isRunning:', this.isRunning, 'paused:', this.state.paused); + + if (this.isRunning && !this.state.paused) { + console.log('[GAME ENGINE] Auto-saving game...'); + + try { + // In multiplayer mode, save to server + if (window.smartSaveManager?.isMultiplayer) { + console.log('[GAME ENGINE] Auto-saving to server...'); + if (this.socket) { + this.socket.emit('saveGameData', { + timestamp: Date.now(), + gameTime: this.gameTime + }); + } else { + console.warn('[GAME ENGINE] No socket available for server save'); + } + } else { + // Singleplayer mode - local save (not implemented yet) + console.log('[GAME ENGINE] Local auto-save not implemented'); + } + + this.showNotification('Game auto-saved', 'info', 2000); + console.log('[GAME ENGINE] Auto-save completed successfully'); + + } catch (error) { + console.error('[GAME ENGINE] Auto-save failed:', error); + if (debugLogger) await debugLogger.errorEvent(error, 'Auto-save'); + } + } else { + console.log('[GAME ENGINE] Auto-save skipped - game not running or paused'); + } + }, this.autoSaveInterval * 60 * 1000); // Convert minutes to milliseconds + } + + stopAutoSave() { + if (this.autoSaveTimer) { + console.log('[GAME ENGINE] Stopping auto-save timer'); + clearInterval(this.autoSaveTimer); + this.autoSaveTimer = null; + } + } + + // Notification system + async showNotification(message, type = 'info', duration = 3000) { + const logger = window.logger; + if (logger) await logger.playerAction('Notification', { message, type, duration }); + + const notification = { + id: Date.now(), + message, + type, + duration, + timestamp: Date.now() + }; + + this.state.notifications.push(notification); + + // Auto-remove notification after duration + setTimeout(() => { + this.removeNotification(notification.id); + }, duration); + + // Update UI + if (this.systems.ui) { + // UI updates handled by individual systems + } + } + + removeNotification(id) { + this.state.notifications = this.state.notifications.filter(notification => notification.id !== id); + } + + // Event system + on(event, callback) { + if (!this.eventListeners.has(event)) { + this.eventListeners.set(event, []); + } + this.eventListeners.get(event).push(callback); + } + + emit(event, data) { + if (this.eventListeners.has(event)) { + this.eventListeners.get(event).forEach(callback => { + try { + callback(data); + } catch (error) { + console.error(`[GAME ENGINE] Error in event listener for ${event}:`, error); + } + }); + } + + // Also dispatch as DOM event for UIManager + this.dispatchEvent(new CustomEvent(event, { detail: data })); + } + + // Utility methods + formatNumber(num) { + if (num >= 1e9) return (num / 1e9).toFixed(2) + 'B'; + if (num >= 1e6) return (num / 1e6).toFixed(2) + 'M'; + if (num >= 1e3) return (num / 1e3).toFixed(2) + 'K'; + return num.toString(); + } + + formatTime(seconds) { + const hours = Math.floor(seconds / 3600); + const minutes = Math.floor((seconds % 3600) / 60); + const secs = Math.floor(seconds % 60); + + if (hours > 0) { + return `${hours}h ${minutes}m ${secs}s`; + } else if (minutes > 0) { + return `${minutes}m ${secs}s`; + } else { + return `${secs}s`; + } + } + + getPerformanceStats() { + const debugLogger = window.debugLogger; + + const stats = { + gameTime: this.gameTime, + isRunning: this.isRunning, + lastUpdate: this.lastUpdate, + memory: null + }; + + // Add memory info if available + if (window.performance && window.performance.memory) { + stats.memory = { + used: window.performance.memory.usedJSHeapSize, + total: window.performance.memory.totalJSHeapSize, + limit: window.performance.memory.jsHeapSizeLimit + }; + } + + return stats; + } + + // Load server player data (transforms server format to client format) + async loadServerPlayerData(playerData) { + console.log('[GAME ENGINE] Loading server player data with format transformation'); + console.log('[GAME ENGINE] Original server data structure:', playerData); + + // Transform server data format to client format + const transformedData = { + ...playerData, + + // Transform quests from server format to client format + quests: playerData.quests ? { + mainQuests: playerData.quests.main || [], + dailyQuests: playerData.quests.daily || [], + weeklyQuests: playerData.quests.weekly || [], + tutorialQuests: playerData.quests.tutorial || [], + activeQuests: playerData.quests.active || [], + completedQuests: playerData.quests.completed || [] + } : { + mainQuests: [], + dailyQuests: [], + weeklyQuests: [], + tutorialQuests: [], + activeQuests: [], + completedQuests: [] + } + }; + + // DEBUG: Log quest data transformation + console.log('[GAME ENGINE] Quest data transformation:', { + serverQuests: playerData.quests, + transformedQuests: transformedData.quests, + mainQuestsCount: transformedData.quests.mainQuests.length, + dailyQuestsCount: transformedData.quests.dailyQuests.length, + weeklyQuestsCount: transformedData.quests.weeklyQuests.length, + tutorialQuestsCount: transformedData.quests.tutorialQuests.length + }); + + // Use crafting data from server or initialize empty + transformedData.crafting = playerData.crafting || { + skill: 1, + experience: 0, + knownRecipes: [], + completedDungeons: [], + currentInstance: null, + dungeonProgress: {} + }; + + return transformedData; + } + + async loadPlayerData(playerData) { + console.log('[GAME ENGINE] Loading server player data'); + console.log('[GAME ENGINE] Full playerData structure:', playerData); + console.log('[GAME ENGINE] PlayerData keys:', Object.keys(playerData)); + + try { + // Apply basic player stats + if (playerData.stats && this.systems && this.systems.player) { + console.log('[GAME ENGINE] Found player stats and player system, applying...'); + console.log('[GAME ENGINE] Server playerData.stats:', playerData.stats); + console.log('[GAME ENGINE] Server playerData keys:', Object.keys(playerData)); + + // Check for playTime in different possible locations + const possiblePlayTimeFields = [ + playerData.stats?.playTime, + playerData.playTime, + playerData.totalPlayTime, + playerData.stats?.totalPlayTime + ]; + + console.log('[GAME ENGINE] Possible playTime fields found:', possiblePlayTimeFields); + + // Preserve existing playTime if server doesn't provide it + const existingPlayTime = this.systems.player.stats.playTime || 0; + console.log('[GAME ENGINE] Preserving existing playTime:', existingPlayTime); + + this.systems.player.load(playerData.stats); + console.log('[GAME ENGINE] Applied player stats:', playerData.stats); + + // Restore playTime if it was lost + if (!this.systems.player.stats.playTime || this.systems.player.stats.playTime === 0) { + this.systems.player.stats.playTime = existingPlayTime; + console.log('[GAME ENGINE] Restored playTime to:', existingPlayTime); + } + + console.log('[GAME ENGINE] Final playTime after load:', this.systems.player.stats.playTime); + + // Apply credits from server data to economy system + if (playerData.stats.credits !== undefined && this.systems.economy) { + this.systems.economy.credits = playerData.stats.credits; + console.log('[GAME ENGINE] Applied credits from server:', playerData.stats.credits); + } + + // Apply gems from server data to economy system + if (playerData.stats.gems !== undefined && this.systems.economy) { + this.systems.economy.gems = playerData.stats.gems; + console.log('[GAME ENGINE] Applied gems from server:', playerData.stats.gems); + } + + // Force manual sync to ensure economy is updated + if (this.systems.economy && this.systems.economy.syncWithServerData) { + console.log('[GAME ENGINE] Forcing manual economy sync'); + this.systems.economy.syncWithServerData(playerData); + } + + // Request fresh economy data from server to ensure sync + if (this.systems.economy && this.systems.economy.requestEconomyData) { + setTimeout(() => { + this.systems.economy.requestEconomyData(); + }, 1000); // Delay to ensure socket is ready + } + + // Apply energy from server data to player attributes + if (playerData.stats.currentEnergy !== undefined && this.systems.player.attributes) { + this.systems.player.attributes.currentEnergy = playerData.stats.currentEnergy; + console.log('[GAME ENGINE] Applied current energy from server:', playerData.stats.currentEnergy); + } + + if (playerData.stats.maxEnergy !== undefined && this.systems.player.attributes) { + this.systems.player.attributes.maxEnergy = playerData.stats.maxEnergy; + console.log('[GAME ENGINE] Applied max energy from server:', playerData.stats.maxEnergy); + } + + // Ensure player has minimum energy for dungeon access + if (this.systems.player.attributes) { + // Check if energy is missing or too low + if (!this.systems.player.attributes.energy || this.systems.player.attributes.energy < 10) { + const oldEnergy = this.systems.player.attributes.energy; + this.systems.player.attributes.energy = 100; + this.systems.player.attributes.maxEnergy = Math.max(this.systems.player.attributes.maxEnergy || 0, 100); + console.log('[GAME ENGINE] Set minimum energy for dungeon access:', oldEnergy, '->', this.systems.player.attributes.energy); + } + + // Also ensure currentEnergy is set if it exists + if (this.systems.player.attributes.currentEnergy !== undefined) { + if (this.systems.player.attributes.currentEnergy < 10) { + this.systems.player.attributes.currentEnergy = 100; + console.log('[GAME ENGINE] Set minimum currentEnergy for dungeon access'); + } + } + } + + console.log('[GAME ENGINE] Final player stats after application:', this.systems.player.stats); + } else { + console.log('[GAME ENGINE] Missing player stats or player system'); + console.log('[GAME ENGINE] - playerData.stats:', !!playerData.stats); + console.log('[GAME ENGINE] - this.systems:', !!this.systems); + console.log('[GAME ENGINE] - this.systems.player:', !!this.systems?.player); + } + + // Apply inventory + if (playerData.inventory && this.systems && this.systems.inventory) { + this.systems.inventory.load(playerData.inventory); + console.log('[GAME ENGINE] Applied inventory'); + } + + // REMOVED: QuestSystem should be server-driven only + // Quest data will be handled by server-side systems only + + // Show notification + if (this.showNotification) { + this.showNotification(`Welcome back! Level ${playerData.stats?.level || 1}`, 'success', 3000); + } + + console.log('[GAME ENGINE] Server player data loaded successfully'); + + // Trigger UI update to refresh all tabs with new data + if (this.systems && this.systems.ui) { + this.systems.ui.updateUI(); + console.log('[GAME ENGINE] Triggered UI update after server data load'); + } + + } catch (error) { + console.error('[GAME ENGINE] Error loading server player data:', error); + if (this.showNotification) { + this.showNotification('Failed to load server data!', 'error', 3000); + } + } + } +} + +// Global game instance +let game = null; + +// Export GameEngine to global scope +if (typeof window !== 'undefined') { + window.GameEngine = GameEngine; +} diff --git a/Client-Server/js/core/Inventory.js b/Galaxy-Strike-Online-main/Client/js/core/Inventory.js similarity index 94% rename from Client-Server/js/core/Inventory.js rename to Galaxy-Strike-Online-main/Client/js/core/Inventory.js index 7804cfa..9a4dfe3 100644 --- a/Client-Server/js/core/Inventory.js +++ b/Galaxy-Strike-Online-main/Client/js/core/Inventory.js @@ -95,72 +95,24 @@ class Inventory { maxSlots: this.maxSlots }); - const startingItems = [ - { - id: 'starter_blaster_common', - name: 'Common Blaster', - type: 'weapon', - rarity: 'common', - quantity: 1, - stats: { attack: 5, criticalChance: 0.02 }, - description: 'A reliable basic blaster for new pilots', - equipable: true, - slot: 'weapon' - }, - { - id: 'basic_armor_common', - name: 'Basic Armor', - type: 'armor', - rarity: 'common', - quantity: 1, - stats: { defense: 3 }, - description: 'Light armor providing basic protection', - equipable: true, - slot: 'armor' - } - ]; - - if (debugLogger) debugLogger.logStep('Adding starting items', { - startingItemCount: startingItems.length, - startingItems: startingItems.map(item => ({ - id: item.id, - name: item.name, - type: item.type, - quantity: item.quantity - })) - }); - - startingItems.forEach(item => { - console.log(`[DEBUG] Adding starting item: ${item.name}`); - const result = this.addItem(item); - console.log(`[DEBUG] Starting item add result: ${result}, inventory size: ${this.items.length}`); - }); - - // Equip starter items - console.log('[INVENTORY] Equipping starter items'); - if (debugLogger) debugLogger.logStep('Equipping starter items'); - - // Equip starter blaster - const blasterItem = this.items.find(item => item.id === 'starter_blaster'); - if (blasterItem) { - console.log('[INVENTORY] Equipping starter blaster'); - this.equipItem(blasterItem.id); + // In multiplayer mode, starting items should come from server + if (window.smartSaveManager?.isMultiplayer) { + console.log('[INVENTORY] Multiplayer mode - starting items will be provided by server'); + if (debugLogger) debugLogger.logStep('Skipping starting items in multiplayer mode'); + if (debugLogger) debugLogger.endStep('Inventory.addStartingItems', { + finalItemCount: this.items.length, + itemsAdded: 0 + }); + return; } - // Equip basic armor - const armorItem = this.items.find(item => item.id === 'basic_armor'); - if (armorItem) { - console.log('[INVENTORY] Equipping basic armor'); - this.equipItem(armorItem.id); - } - - // Auto-stack starting items - if (debugLogger) debugLogger.logStep('Auto-stacking starting items'); - this.autoStackItems(); + // Singleplayer mode - no hardcoded starting items available + console.log('[INVENTORY] Singleplayer mode - no hardcoded starting items available'); + if (debugLogger) debugLogger.logStep('No starting items available in singleplayer mode'); if (debugLogger) debugLogger.endStep('Inventory.addStartingItems', { finalItemCount: this.items.length, - itemsAdded: startingItems.length + itemsAdded: 0 }); } diff --git a/Client-Server/js/core/Logger.js b/Galaxy-Strike-Online-main/Client/js/core/Logger.js similarity index 100% rename from Client-Server/js/core/Logger.js rename to Galaxy-Strike-Online-main/Client/js/core/Logger.js diff --git a/Client-Server/js/core/Player.js b/Galaxy-Strike-Online-main/Client/js/core/Player.js similarity index 92% rename from Client-Server/js/core/Player.js rename to Galaxy-Strike-Online-main/Client/js/core/Player.js index 9f84414..17056e4 100644 --- a/Client-Server/js/core/Player.js +++ b/Galaxy-Strike-Online-main/Client/js/core/Player.js @@ -537,9 +537,9 @@ class Player { upgrades: [] }; - console.log('=== DEBUG: Character Reset ==='); - console.log('Player health reset to:', this.attributes.health, '/', this.attributes.maxHealth); - console.log('Ship health reset to:', this.ship.health, '/', this.ship.maxHealth); + // console.log('=== DEBUG: Character Reset ==='); + // console.log('Player health reset to:', this.attributes.health, '/', this.attributes.maxHealth); + // console.log('Ship health reset to:', this.ship.health, '/', this.ship.maxHealth); // Reset skills this.skills = {}; @@ -692,16 +692,43 @@ class Player { } updatePlayTime(deltaTime) { + // DISABLED: Reduce console spam for quest debugging + /* console.log('[PLAYER] updatePlayTime called with deltaTime:', deltaTime, 'ms'); + console.log('[PLAYER] Game state check:', { + hasGame: !!this.game, + isRunning: this.game?.isRunning, + isPaused: this.game?.state?.paused, + isHidden: document.hidden + }); + */ + + // Only update playtime when game is actively running and not paused + if (!this.game || !this.game.isRunning || this.game.state.paused) { + // console.log('[PLAYER] Skipping playtime update - game not running or paused'); + return; + } + + // Also check if tab is visible (don't count time when tab is in background) + if (document.hidden) { + // console.log('[PLAYER] Skipping playtime update - tab hidden'); + return; + } + + // DISABLED: Reduce console spam for quest debugging + /* console.log('[PLAYER] Before update - playTime:', this.stats.playTime, 'ms'); + */ // Use real computer time delta this.stats.playTime += deltaTime; + // DISABLED: Reduce console spam for quest debugging + /* console.log('[PLAYER] After update - playTime:', this.stats.playTime, 'ms'); console.log('[PLAYER] PlayTime in seconds:', this.stats.playTime / 1000, 'seconds'); console.log('[PLAYER] PlayTime in minutes:', this.stats.playTime / 60000, 'minutes'); - console.log('[PLAYER] PlayTime in hours:', this.stats.playTime / 3600000, 'hours'); + */ } // UI updates @@ -717,10 +744,15 @@ class Player { // Update player info const playerNameElement = document.getElementById('playerName'); + const playerTitleElement = document.getElementById('playerTitle'); const playerLevelElement = document.getElementById('playerLevel'); if (playerNameElement) { - playerNameElement.textContent = `${this.info.name} - ${this.info.title}`; + playerNameElement.textContent = this.info.name; + } + + if (playerTitleElement) { + playerTitleElement.textContent = ` - ${this.info.title}`; } if (playerLevelElement) { @@ -767,6 +799,7 @@ class Player { if (debugLogger) debugLogger.logStep('Player UI update completed', { elementsUpdated: { playerName: !!playerNameElement, + playerTitle: !!playerTitleElement, playerLevel: !!playerLevelElement, totalKills: !!totalKillsElement, dungeonsCleared: !!dungeonsClearedElement, @@ -819,9 +852,29 @@ class Player { try { if (data.stats) { console.log('[PLAYER] Loading stats:', data.stats); + console.log('[PLAYER] Current playTime before load:', this.stats.playTime); + console.log('[PLAYER] Server playTime:', data.stats.playTime); + const oldStats = { ...this.stats }; - this.stats = { ...this.stats, ...data.stats }; + + // Preserve playTime if server doesn't provide it or provides 0 + const existingPlayTime = this.stats.playTime || 0; + const serverPlayTime = data.stats.playTime || 0; + + // Use server playTime if it's greater than existing, otherwise preserve existing + const preservedPlayTime = serverPlayTime > existingPlayTime ? serverPlayTime : existingPlayTime; + + console.log('[PLAYER] Preserving playTime:', preservedPlayTime, '(existing:', existingPlayTime, ', server:', serverPlayTime, ')'); + + // Merge stats but preserve playTime + this.stats = { + ...this.stats, + ...data.stats, + playTime: preservedPlayTime // Force preserve playTime + }; + console.log('[PLAYER] Level after stats load:', this.stats.level); + console.log('[PLAYER] PlayTime after stats load:', this.stats.playTime); if (debugLogger) debugLogger.logStep('Player stats loaded', { oldLevel: oldStats.level, diff --git a/Client-Server/js/core/TextureManager.js b/Galaxy-Strike-Online-main/Client/js/core/TextureManager.js similarity index 100% rename from Client-Server/js/core/TextureManager.js rename to Galaxy-Strike-Online-main/Client/js/core/TextureManager.js diff --git a/Galaxy-Strike-Online-main/Client/js/data/GameData.js b/Galaxy-Strike-Online-main/Client/js/data/GameData.js new file mode 100644 index 0000000..e87712b --- /dev/null +++ b/Galaxy-Strike-Online-main/Client/js/data/GameData.js @@ -0,0 +1,125 @@ +/** + * Galaxy Strike Online - Game Data + * UI constants and configuration only. + * All game content (items, skills, recipes, dungeons, enemies) is loaded from the server. + */ + +// Game configuration +const GAME_CONFIG = { + version: '1.0.0', + name: 'Galaxy Strike Online', + maxLevel: 100, + saveInterval: 30000, // 30 seconds + notificationDuration: 3000, + maxNotifications: 5 +}; + +// Player defaults (used only for initial UI state before server data arrives) +const PLAYER_DEFAULTS = { + level: 1, + experience: 0, + skillPoints: 0, + credits: 1000, + gems: 10, + health: 100, + maxHealth: 100, + energy: 100, + maxEnergy: 100, + attack: 10, + defense: 5, + speed: 10, + criticalChance: 0.05, + criticalDamage: 1.5 +}; + +// Experience requirements (client-side display only; server is authoritative) +const EXPERIENCE_TABLE = []; +for (let i = 1; i <= 100; i++) { + EXPERIENCE_TABLE[i] = Math.floor(100 * Math.pow(1.5, i - 1)); +} + +// Item rarity display properties (colours/labels only - drop rates are server-side) +const ITEM_RARITIES = { + common: { name: 'Common', color: '#888888', multiplier: 1.0 }, + uncommon: { name: 'Uncommon', color: '#00ff00', multiplier: 1.2 }, + rare: { name: 'Rare', color: '#0088ff', multiplier: 1.5 }, + epic: { name: 'Epic', color: '#8833ff', multiplier: 2.0 }, + legendary: { name: 'Legendary', color: '#ff8800', multiplier: 3.0 } +}; + +// Game messages and notifications +const GAME_MESSAGES = { + welcome: 'Welcome to Galaxy Strike Online, Commander!', + levelUp: 'Level Up! You are now level {level}!', + questCompleted: 'Quest completed: {questName}!', + dungeonCompleted: 'Dungeon completed! Time: {time}', + achievementUnlocked: 'Achievement Unlocked: {achievementName}!', + insufficientCredits: 'Not enough credits!', + insufficientGems: 'Not enough gems!', + insufficientEnergy: 'Not enough energy!', + inventoryFull: 'Inventory is full!', + skillPointsAvailable: 'You have {points} skill points available!', + dailyReward: 'Daily reward claimed! Day {day}', + offlineRewards: 'Welcome back! You were offline for {time}' +}; + +// Utility functions +const GameUtils = { + getRandomItem(array) { + return array[Math.floor(Math.random() * array.length)]; + }, + + getRandomInt(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; + }, + + getRandomFloat(min, max) { + return Math.random() * (max - min) + min; + }, + + checkChance(chance) { + return Math.random() < chance; + }, + + formatNumber(num) { + if (num >= 1000000) return (num / 1000000).toFixed(1) + 'M'; + if (num >= 1000) return (num / 1000).toFixed(1) + 'K'; + return Math.floor(num).toString(); + }, + + formatTime(milliseconds) { + const seconds = Math.floor(milliseconds / 1000); + const minutes = Math.floor(seconds / 60); + const hours = Math.floor(minutes / 60); + const days = Math.floor(hours / 24); + + if (days > 0) return `${days}d ${hours % 24}h`; + if (hours > 0) return `${hours}h ${minutes % 60}m`; + if (minutes > 0) return `${minutes}m ${seconds % 60}s`; + return `${seconds}s`; + }, + + getExperienceForLevel(level) { + return EXPERIENCE_TABLE[level] || 0; + }, + + deepClone(obj) { + return JSON.parse(JSON.stringify(obj)); + }, + + generateId() { + return Date.now().toString() + Math.random().toString(36).substr(2, 9); + } +}; + +// Export for use in other modules +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + GAME_CONFIG, + PLAYER_DEFAULTS, + EXPERIENCE_TABLE, + ITEM_RARITIES, + GAME_MESSAGES, + GameUtils + }; +} diff --git a/Client-Server/js/main.js b/Galaxy-Strike-Online-main/Client/js/main.js similarity index 100% rename from Client-Server/js/main.js rename to Galaxy-Strike-Online-main/Client/js/main.js diff --git a/Client-Server/js/systems/BaseSystem.js b/Galaxy-Strike-Online-main/Client/js/systems/BaseSystem.js similarity index 99% rename from Client-Server/js/systems/BaseSystem.js rename to Galaxy-Strike-Online-main/Client/js/systems/BaseSystem.js index b5acea3..663ccd4 100644 --- a/Client-Server/js/systems/BaseSystem.js +++ b/Galaxy-Strike-Online-main/Client/js/systems/BaseSystem.js @@ -1026,8 +1026,15 @@ class BaseSystem { } else if (view === 'ships') { this.updateShipGallery(); } else if (view === 'starbases') { - this.updateStarbaseList(); - this.updateStarbasePurchaseList(); + // Boot the isometric starbase world instead of the old list UI + if (typeof _startStarbaseWorld === 'function') { + _startStarbaseWorld(); + } + } else { + // Leaving starbases — stop the world loop + if (typeof _stopStarbaseWorld === 'function') { + _stopStarbaseWorld(); + } } } diff --git a/Galaxy-Strike-Online-main/Client/js/systems/CraftingSystem.js b/Galaxy-Strike-Online-main/Client/js/systems/CraftingSystem.js new file mode 100644 index 0000000..ac301fa --- /dev/null +++ b/Galaxy-Strike-Online-main/Client/js/systems/CraftingSystem.js @@ -0,0 +1,403 @@ +/** + * Galaxy Strike Online - Client Crafting System + * Recipe definitions are loaded from the server; this file handles + * local crafting logic, requirement checking, and UI rendering. + */ + +class CraftingSystem extends BaseSystem { + constructor(gameEngine) { + super(gameEngine); + + this.recipes = new Map(); // recipeId -> recipe object + this.currentCategory = 'weapons'; + this.selectedRecipe = null; + + this._loaded = false; + this._loading = false; + } + + // ------------------------------------------------------------------ // + // Initialisation — request recipes from the server + // ------------------------------------------------------------------ // + async initialize() { + if (this._loaded || this._loading) return; + this._loading = true; + + console.log('[CRAFTING SYSTEM] Requesting recipes from server'); + + if (!window.game?.socket) { + console.warn('[CRAFTING SYSTEM] No socket — recipes will load when connected'); + this._loading = false; + return; + } + + try { + const recipes = await this._fetchRecipesFromServer(); + this._applyServerRecipes(recipes); + this._loaded = true; + console.log(`[CRAFTING SYSTEM] Loaded ${this.recipes.size} recipes from server`); + } catch (err) { + console.error('[CRAFTING SYSTEM] Failed to load recipes from server:', err); + } finally { + this._loading = false; + } + } + + _fetchRecipesFromServer() { + return new Promise((resolve, reject) => { + const socket = window.game.socket; + + const timeout = setTimeout(() => { + socket.off('recipes_data', handler); + reject(new Error('Recipe data request timed out')); + }, 10000); + + const handler = (data) => { + clearTimeout(timeout); + socket.off('recipes_data', handler); + if (data && (Array.isArray(data) || typeof data === 'object')) { + resolve(data); + } else { + reject(new Error('Invalid recipe data from server')); + } + }; + + socket.on('recipes_data', handler); + socket.emit('get_recipes'); + }); + } + + _applyServerRecipes(serverRecipes) { + this.recipes.clear(); + + // Server may return array or object keyed by id + const asList = Array.isArray(serverRecipes) + ? serverRecipes + : Object.values(serverRecipes); + + for (const recipe of asList) { + if (!recipe.id) continue; + + // Normalise materials: server uses { itemId: qty } objects, client expects array + let materials = recipe.materials; + if (materials && !Array.isArray(materials)) { + materials = Object.entries(materials).map(([id, quantity]) => ({ id, quantity })); + } + + // Normalise results similarly + let results = recipe.results; + if (results && !Array.isArray(results)) { + results = Object.entries(results) + .filter(([k]) => k !== 'experience') + .map(([id, quantity]) => ({ id, quantity })); + } + + this.recipes.set(recipe.id, { + ...recipe, + materials: materials || [], + results: results || [], + category: recipe.type || recipe.category || 'items', + unlocked: false // will be resolved by checkRecipeUnlocks() + }); + } + } + + // ------------------------------------------------------------------ // + // Runtime + // ------------------------------------------------------------------ // + addRecipe(id, recipe) { + recipe.id = id; + recipe.unlocked = false; + this.recipes.set(id, recipe); + } + + update(deltaTime) { + this.checkRecipeUnlocks(); + } + + checkRecipeUnlocks() { + const skillSystem = this.game.systems.skillSystem; + if (!skillSystem) return; + + for (const [id, recipe] of this.recipes) { + if (recipe.unlocked) continue; + + let canUnlock = true; + if (recipe.requirements) { + for (const [skillName, requiredLevel] of Object.entries(recipe.requirements)) { + if (skillSystem.getSkillLevel(skillName) < requiredLevel) { + canUnlock = false; + break; + } + } + } + + if (canUnlock) { + recipe.unlocked = true; + console.log(`[CRAFTING] Recipe unlocked: ${recipe.name}`); + } + } + } + + getRecipesByCategory(category) { + return Array.from(this.recipes.values()) + .filter(r => r.category === category || r.type === category); + } + + canCraftRecipe(recipeId) { + const recipe = this.recipes.get(recipeId); + const skillSystem = this.game.systems.skillSystem; + const inventory = this.game.systems.inventory; + if (!recipe) return false; + + if (recipe.requirements && skillSystem) { + for (const [skillName, requiredLevel] of Object.entries(recipe.requirements)) { + if (skillSystem.getSkillLevel(skillName) < requiredLevel) return false; + } + } + + if (recipe.materials && inventory) { + for (const mat of recipe.materials) { + if (!inventory.hasItem(mat.id, mat.quantity)) return false; + } + } + + return true; + } + + getMissingMaterials(recipeId) { + const recipe = this.recipes.get(recipeId); + const inventory = this.game.systems.inventory; + if (!recipe?.materials) return []; + + const missing = []; + for (const mat of recipe.materials) { + let current = 0; + if (inventory?.getItemCount) { + try { current = inventory.getItemCount(mat.id) || 0; } catch (_) {} + } + const required = mat.quantity || 0; + if (current < required) { + missing.push({ id: mat.id, required, current, missing: required - current }); + } + } + return missing; + } + + async craftRecipe(recipeId) { + const recipe = this.recipes.get(recipeId); + if (!recipe || !this.canCraftRecipe(recipeId)) return false; + + console.log(`[CRAFTING] Crafting: ${recipe.name}`); + + if (recipe.materials) { + for (const mat of recipe.materials) { + this.game.systems.inventory.removeItem(mat.id, mat.quantity); + } + } + + if (recipe.experience && this.game.systems.skillSystem) { + this.game.systems.skillSystem.awardCraftingExperience(recipe.experience); + } + + await new Promise(resolve => setTimeout(resolve, recipe.craftingTime || 3000)); + + if (recipe.results) { + for (const result of recipe.results) { + this.game.systems.inventory.addItem(result.id, result.quantity); + } + } + + if (this.game.systems.questSystem) { + this.game.systems.questSystem.onItemCrafted?.(); + } + + console.log(`[CRAFTING] Done: ${recipe.name}`); + return true; + } + + selectRecipe(recipeId) { + this.selectedRecipe = this.recipes.get(recipeId); + return this.selectedRecipe; + } + + getSelectedRecipe() { return this.selectedRecipe; } + + // ------------------------------------------------------------------ // + // UI + // ------------------------------------------------------------------ // + updateUI() { + this.updateRecipeList(); + this.updateCraftingDetails(); + this.updateCraftingInfo(); + } + + updateRecipeList() { + const listEl = document.getElementById('recipeList'); + if (!listEl) return; + + if (!this._loaded) { + listEl.innerHTML = '

Loading recipes from server...

'; + return; + } + + const recipes = this.getRecipesByCategory(this.currentCategory); + listEl.innerHTML = ''; + + if (recipes.length === 0) { + listEl.innerHTML = '

No recipes available in this category

'; + return; + } + + recipes.forEach(recipe => { + const el = document.createElement('div'); + el.className = 'recipe-item'; + el.dataset.recipeId = recipe.id; + + const canCraft = this.canCraftRecipe(recipe.id); + const missingMats = this.getMissingMaterials(recipe.id); + const skillSystem = this.game.systems.skillSystem; + let skillsMet = true; + + if (recipe.requirements && skillSystem) { + for (const [skill, level] of Object.entries(recipe.requirements)) { + if (skillSystem.getSkillLevel(skill) < level) { skillsMet = false; break; } + } + } + + if (!skillsMet) el.classList.add('locked'); + else if (!canCraft) el.classList.add('missing-materials'); + else el.classList.add('can-craft'); + + const reqText = recipe.requirements + ? Object.entries(recipe.requirements).map(([s, l]) => `${s}: ${l}`).join(', ') + : 'None'; + + const matsHtml = recipe.materials.map(mat => { + const mis = missingMats.find(m => m.id === mat.id); + const cur = mis ? mis.current : (this.game.systems.inventory?.getItemCount(mat.id) || 0); + const cls = mis ? 'material-item missing' : 'material-item'; + return `
+ ${mat.id} + ${cur}/${mat.quantity} +
`; + }).join(''); + + el.innerHTML = ` +
+

${recipe.name}

+ Level ${reqText} +
+
${recipe.description || ''}
+
${matsHtml}
+ ${missingMats.length > 0 ? ` +
+ + Missing: ${missingMats.map(m => `${m.missing}x ${m.id}`).join(', ')} +
` : ''} +
+ + ${(recipe.craftingTime || 0) / 1000}s +
+ `; + + el.addEventListener('click', () => { + this.selectRecipe(recipe.id); + this.updateCraftingDetails(); + }); + + listEl.appendChild(el); + }); + } + + updateCraftingDetails() { + const detailsEl = document.getElementById('craftingDetails'); + if (!detailsEl) return; + + if (!this.selectedRecipe) { + detailsEl.innerHTML = ` +
+

Select a Recipe

+

Choose a recipe from the list to see details and craft items.

+
`; + return; + } + + const recipe = this.selectedRecipe; + const canCraft = this.canCraftRecipe(recipe.id); + + detailsEl.innerHTML = ` +
+

${recipe.name}

+

${recipe.description || ''}

+
+

Requirements:

+ ${recipe.requirements + ? Object.entries(recipe.requirements).map(([s, l]) => + `
+ ${s} + Level ${l} +
`).join('') + : '

No special requirements

'} +
+
+

Materials Needed:

+ ${recipe.materials.map(mat => + `
+ ${mat.id} + x${mat.quantity} + Have: ${this.game.systems.inventory?.getItemCount(mat.id) || 0} +
`).join('')} +
+
+

Results:

+ ${recipe.results.map(r => + `
+ ${r.id} + x${r.quantity} +
`).join('')} +
+
+
+ + ${recipe.experience || 0} XP +
+
+ + ${(recipe.craftingTime || 0) / 1000} seconds +
+
+ +
`; + } + + updateCraftingInfo() { + const skillSystem = this.game.systems.skillSystem; + if (!skillSystem) return; + + const craftingLevel = skillSystem.getSkillLevel('crafting'); + const craftingExp = skillSystem.getSkillExperience('crafting'); + const expNeeded = skillSystem.getExperienceNeeded('crafting'); + + const levelEl = document.getElementById('craftingLevel'); + const expEl = document.getElementById('craftingExp'); + if (levelEl) levelEl.textContent = craftingLevel; + if (expEl) expEl.textContent = `${craftingExp}/${expNeeded}`; + } + + switchCategory(category) { + this.currentCategory = category; + this.selectedRecipe = null; + if (window.smartSaveManager?.isMultiplayer || this.game?.isRunning) { + this.updateUI(); + } + } +} + +// Export for use in GameEngine +if (typeof module !== 'undefined' && module.exports) { + module.exports = CraftingSystem; +} diff --git a/Galaxy-Strike-Online-main/Client/js/systems/DungeonSystem.js b/Galaxy-Strike-Online-main/Client/js/systems/DungeonSystem.js new file mode 100644 index 0000000..ada1e64 --- /dev/null +++ b/Galaxy-Strike-Online-main/Client/js/systems/DungeonSystem.js @@ -0,0 +1,832 @@ +/** + * Galaxy Strike Online - Client Dungeon System + * Server-driven dungeon management client + */ + +// Create global function for dungeon start that's more reliable +window.startDungeon = function(dungeonId) { + console.log('[DUNGEON SYSTEM] startDungeon called with:', dungeonId); + console.log('[DUNGEON SYSTEM] Game available:', !!window.game); + console.log('[DUNGEON SYSTEM] Game systems available:', !!(window.game && window.game.systems)); + console.log('[DUNGEON SYSTEM] Dungeon system available:', !!(window.game && window.game.systems && window.game.systems.dungeonSystem)); + + if (window.game && window.game.systems && window.game.systems.dungeonSystem) { + return window.game.systems.dungeonSystem.startDungeon(dungeonId); + } + + console.warn('[DUNGEON SYSTEM] Game systems not available for dungeon start'); +}; + +// Create global function for process encounter that's more reliable +window.processEncounter = function() { + console.log('[DUNGEON SYSTEM] processEncounter called'); + + if (window.game && window.game.systems && window.game.systems.dungeonSystem) { + return window.game.systems.dungeonSystem.processEncounter(); + } + + console.warn('[DUNGEON SYSTEM] Game systems not available for process encounter'); +}; + +// Create global function for dungeon toggle that's more reliable +window.toggleDungeonSection = function(sectionId) { + // Try to use the dungeon system if available + if (window.game && window.game.systems && window.game.systems.dungeonSystem) { + return window.game.systems.dungeonSystem.toggleDungeonSection(sectionId); + } + + // Fallback: Direct DOM manipulation + const section = document.getElementById(sectionId); + const indicator = document.getElementById(`${sectionId}-indicator`); + + if (!section || !indicator) { + console.warn('[DUNGEON SYSTEM] Section or indicator not found:', sectionId); + return; + } + + const isCollapsed = section.classList.contains('collapsed'); + + if (isCollapsed) { + // Expand + section.classList.remove('collapsed'); + indicator.classList.remove('fa-chevron-right'); + indicator.classList.add('fa-chevron-down'); + } else { + // Collapse + section.classList.add('collapsed'); + indicator.classList.remove('fa-chevron-down'); + indicator.classList.add('fa-chevron-right'); + } + + // Save the state in the dungeon system if available + if (window.game && window.game.systems && window.game.systems.dungeonSystem) { + window.game.systems.dungeonSystem.collapseStates.set(sectionId, !isCollapsed); + } + + console.log(`[DUNGEON SYSTEM] Toggled section ${sectionId}: ${isCollapsed ? 'expanded' : 'collapsed'}`); +}; + +class DungeonSystem { + constructor(gameEngine) { + this.game = gameEngine; + + // Current dungeon state (runtime only) + this.currentDungeon = null; + this.currentRoom = null; + this.dungeonProgress = 0; + this.isExploring = false; + + // Debouncing to prevent multiple rapid clicks + this.lastProcessTime = 0; + this.processCooldown = 1000; // 1 second cooldown + + // Prevent duplicate event processing + this.lastEncounterData = null; + this.lastNextRoomData = null; + + // Store collapse states to preserve them during regeneration + this.collapseStates = new Map(); + + // Track last generation to prevent unnecessary regenerations + this.lastGenerationTime = 0; + this.generationThrottle = 500; // 500ms throttle + + // Server dungeons data + this.serverDungeons = null; + this.roomTypes = {}; + this.enemyTemplates = {}; + + console.log('[DUNGEON SYSTEM] Client DungeonSystem initialized - server-driven mode'); + + // Set up socket event listeners + this.setupSocketListeners(); + } + + /** + * Set up Socket.IO event listeners for dungeon data + */ + setupSocketListeners() { + if (!this.game.socket) { + console.warn('[DUNGEON SYSTEM] No socket available for event listeners'); + return; + } + + // Listen for dungeon data response + this.game.socket.on('dungeons_data', (data) => { + console.log('[DUNGEON SYSTEM] Received dungeons data:', data); + this.serverDungeons = data.dungeons || data; + console.log('[DUNGEON SYSTEM] Loaded grouped dungeons from server:', Object.keys(this.serverDungeons)); + // Update UI when data is loaded + this.forceGenerateDungeonList(); + }); + + // Listen for room types response + this.game.socket.on('room_types_data', (data) => { + console.log('[DUNGEON SYSTEM] Received room types data:', data); + this.roomTypes = data; + console.log(`[DUNGEON SYSTEM] Loaded ${Object.keys(this.roomTypes).length} room types from server`); + // Update UI when room data is loaded + this.forceGenerateDungeonList(); + }); + + // Listen for enemy templates response + this.game.socket.on('enemy_templates_data', (data) => { + console.log('[DUNGEON SYSTEM] Received enemy templates data:', data); + this.enemyTemplates = data; + console.log(`[DUNGEON SYSTEM] Loaded ${Object.keys(this.enemyTemplates).length} enemy templates from server`); + // Update UI when enemy data is loaded + this.forceGenerateDungeonList(); + }); + + // Listen for dungeon start response + this.game.socket.on('dungeon_started', (data) => { + console.log('[DUNGEON SYSTEM] Dungeon started:', data); + + // Handle error responses + if (data.success === false) { + console.error('[DUNGEON SYSTEM] Failed to start dungeon:', data.error); + if (this.game && this.game.showNotification) { + this.game.showNotification(data.error, 'error', 5000); + } + return; + } + + // Clear any existing dungeon state first + if (this.currentDungeon) { + console.warn('[DUNGEON SYSTEM] Clearing existing dungeon state before starting new one'); + this.currentDungeon = null; + this.currentRoom = null; + this.isExploring = false; + this.dungeonProgress = 0; + } + + this.currentDungeon = data.instance; + this.isExploring = true; + this.dungeonProgress = 0; + + console.log('[DUNGEON SYSTEM] About to update UI - State:', { + currentDungeon: !!this.currentDungeon, + isExploring: this.isExploring, + dungeonProgress: this.dungeonProgress, + gameUIManager: !!this.game.systems.ui, + instanceId: this.currentDungeon?.id + }); + + // Update UI to show dungeon exploration + this.updateUI(); + + // Show notification to player + if (this.game && this.game.showNotification) { + this.game.showNotification(`Entered ${data.instance.dungeonId} dungeon!`, 'success', 3000); + } + }); + + // Listen for encounter response + this.game.socket.on('encounter_data', (data) => { + // Skip duplicate events + if (this.lastEncounterData && + this.lastEncounterData.encounterIndex === data.encounterIndex && + this.lastEncounterData.encounter?.name === data.encounter?.name) { + console.log('[DUNGEON SYSTEM] Skipping duplicate encounter data'); + return; + } + this.lastEncounterData = data; + + console.log('[DUNGEON SYSTEM] Encounter received:', data); + console.log('[DUNGEON SYSTEM] Current state before update:', { + currentDungeonId: this.currentDungeon?.id, + currentProgress: this.dungeonProgress, + newEncounterIndex: data.encounterIndex, + encounterType: data.encounter?.type, + encounterName: data.encounter?.name + }); + + this.currentRoom = data.encounter; + this.dungeonProgress = data.encounterIndex; // Use server data, not local increment + + // Update UI to show the new encounter + this.updateUI(); + }); + + // Listen for encounter completion (auto-combat) + this.game.socket.on('encounter_completed', (data) => { + console.log('[DUNGEON SYSTEM] Encounter completed:', data); + if (data.success) { + // Check if dungeon is complete + if (data.isComplete) { + console.log('[DUNGEON SYSTEM] Dungeon completed!'); + + // Clear all dungeon state + this.currentDungeon = null; + this.currentRoom = null; + this.dungeonProgress = 0; + this.isExploring = false; + this.lastEncounterData = null; + this.lastNextRoomData = null; + + // Show completion notification + if (this.game && this.game.showNotification) { + this.game.showNotification('Dungeon completed! 🎉', 'success', 5000); + } + + // Force UI to show dungeon list + setTimeout(() => { + this.updateUI(); + }, 1000); + } else { + this.currentRoom = data.nextEncounter; + this.dungeonProgress = data.encounterIndex; + + // Show rewards notification + if (data.rewards && (data.rewards.credits > 0 || data.rewards.experience > 0)) { + const rewardText = []; + if (data.rewards.credits > 0) rewardText.push(`${data.rewards.credits} credits`); + if (data.rewards.experience > 0) rewardText.push(`${data.rewards.experience} exp`); + + if (this.game && this.game.showNotification) { + this.game.showNotification(`Combat complete! Gained: ${rewardText.join(', ')}`, 'success', 3000); + } + } + } + + // Update UI to show the new state + this.updateUI(); + } else { + console.error('[DUNGEON SYSTEM] Error completing encounter:', data.error); + } + }); + + // Listen for next room response + this.game.socket.on('next_room_data', (data) => { + // Skip duplicate events + if (this.lastNextRoomData && + this.lastNextRoomData.encounterIndex === data.encounterIndex && + this.lastNextRoomData.encounter?.name === data.encounter?.name) { + console.log('[DUNGEON SYSTEM] Skipping duplicate next room data'); + return; + } + this.lastNextRoomData = data; + + console.log('[DUNGEON SYSTEM] Next room received:', data); + console.log('[DUNGEON SYSTEM] Current state before update:', { + currentDungeonId: this.currentDungeon?.id, + currentProgress: this.dungeonProgress, + newEncounterIndex: data.encounterIndex, + encounterType: data.encounter?.type, + encounterName: data.encounter?.name, + isComplete: data.isComplete + }); + + if (data.success) { + this.currentRoom = data.encounter; + this.dungeonProgress = data.encounterIndex; + + // Update UI to show the new room + this.updateUI(); + } else { + console.error('[DUNGEON SYSTEM] Error moving to next room:', data.error); + } + }); + + // Listen for dungeon completion response + this.game.socket.on('dungeon_completed', (data) => { + console.log('[DUNGEON SYSTEM] Dungeon completed:', data); + // Reset dungeon state + this.currentDungeon = null; + this.currentRoom = null; + this.isExploring = false; + this.dungeonProgress = 0; + }); + + // Listen for dungeon status response + this.game.socket.on('dungeon_status', (data) => { + console.log('[DUNGEON SYSTEM] Dungeon status received:', data); + }); + + console.log('[DUNGEON SYSTEM] Socket event listeners set up'); + } + + /** + * Load dungeon data from server using Socket.IO packets + */ + async loadServerData() { + try { + console.log('[DUNGEON SYSTEM] Loading dungeon data from server via packets...'); + + if (!this.game.socket) { + console.error('[DUNGEON SYSTEM] No socket connection available'); + return; + } + + // Request dungeons from server + this.game.socket.emit('get_dungeons'); + + // Request room types from server + this.game.socket.emit('get_room_types'); + + // Request enemy templates from server + this.game.socket.emit('get_enemy_templates'); + + console.log('[DUNGEON SYSTEM] Server data requests sent via packets'); + + } catch (error) { + console.error('[DUNGEON SYSTEM] Error loading server data:', error); + } + } + + /** + * Get all available dungeons + */ + getAllDungeons() { + return this.serverDungeons; + } + + /** + * Get dungeons by difficulty + */ + getDungeonsByDifficulty(difficulty) { + return this.serverDungeons.filter(dungeon => dungeon.difficulty === difficulty); + } + + /** + * Get specific dungeon by ID + */ + getDungeon(dungeonId) { + return this.serverDungeons.find(dungeon => dungeon.id === dungeonId); + } + + /** + * Get room type by ID + */ + getRoomType(roomTypeId) { + return this.roomTypes[roomTypeId]; + } + + /** + * Get enemy template by ID + */ + getEnemyTemplate(enemyId) { + return this.enemyTemplates[enemyId]; + } + + /** + * Start exploring a dungeon using Socket.IO packets + */ + async startDungeon(dungeonId) { + try { + console.log(`[DUNGEON SYSTEM] Starting dungeon: ${dungeonId}`); + + if (!this.game.socket) { + console.error('[DUNGEON SYSTEM] No socket connection available'); + return null; + } + + // Send packet to start dungeon + this.game.socket.emit('start_dungeon', { + dungeonId: dungeonId, + userId: this.game.systems.player?.id || 'anonymous' + }); + + console.log('[DUNGEON SYSTEM] Dungeon start packet sent'); + return true; + + } catch (error) { + console.error('[DUNGEON SYSTEM] Error starting dungeon:', error); + return null; + } + } + + /** + * Process encounter in current dungeon room + */ + async processEncounter() { + // Debounce to prevent multiple rapid clicks + const now = Date.now(); + if (now - this.lastProcessTime < this.processCooldown) { + console.log('[DUNGEON SYSTEM] Process throttled, please wait...'); + return null; + } + this.lastProcessTime = now; + + try { + // Safety check - make sure we have an active dungeon + if (!this.currentDungeon) { + console.error('[DUNGEON SYSTEM] No active dungeon to process encounter for'); + return null; + } + + console.log(`[DUNGEON SYSTEM] Processing encounter for dungeon: ${this.currentDungeon.id}`); + + if (!this.game.socket) { + console.error('[DUNGEON SYSTEM] No socket connection available'); + return null; + } + + // Send packet to process encounter + this.game.socket.emit('process_encounter', { + instanceId: this.currentDungeon.id, + userId: this.game.systems.player?.id || 'anonymous' + }); + + console.log('[DUNGEON SYSTEM] Encounter process packet sent'); + return true; + + } catch (error) { + console.error('[DUNGEON SYSTEM] Error processing encounter:', error); + return null; + } + } + + /** + * Complete current dungeon using Socket.IO packets + */ + async completeDungeon() { + if (!this.currentDungeon || !this.isExploring) { + console.warn('[DUNGEON SYSTEM] No active dungeon to complete'); + return null; + } + + try { + console.log(`[DUNGEON SYSTEM] Completing dungeon: ${this.currentDungeon.id}`); + + if (!this.game.socket) { + console.error('[DUNGEON SYSTEM] No socket connection available'); + return null; + } + + // Send packet to complete dungeon + this.game.socket.emit('complete_dungeon', { + instanceId: this.currentDungeon.id, + userId: this.game.systems.player?.id || 'anonymous' + }); + + console.log('[DUNGEON SYSTEM] Dungeon completion packet sent'); + return true; + + } catch (error) { + console.error('[DUNGEON SYSTEM] Error completing dungeon:', error); + return null; + } + } + + /** + * Get player's current dungeon status using Socket.IO packets + */ + async getDungeonStatus() { + try { + if (!this.game.socket) { + console.error('[DUNGEON SYSTEM] No socket connection available'); + return null; + } + + // Send packet to get dungeon status + this.game.socket.emit('get_dungeon_status', { + userId: this.game.systems.player?.id || 'anonymous' + }); + + console.log('[DUNGEON SYSTEM] Dungeon status request packet sent'); + return true; + + } catch (error) { + console.error('[DUNGEON SYSTEM] Error getting dungeon status:', error); + } + + return null; + } + + /** + * Force generate dungeon list (bypasses throttle) + */ + forceGenerateDungeonList() { + this.lastGenerationTime = 0; // Reset throttle + this.generateDungeonList(); + } + + /** + * Generate dungeon list UI using server data + */ + generateDungeonList() { + const now = Date.now(); + + // Throttle generation to prevent excessive calls + if (now - this.lastGenerationTime < this.generationThrottle) { + return; // Silently skip instead of logging + } + + this.lastGenerationTime = now; + // console.log('[DUNGEON SYSTEM] Generating dungeon list UI'); + + const dungeonListElement = document.getElementById('dungeonList'); + if (!dungeonListElement) { + console.error('[DUNGEON SYSTEM] Dungeon list element not found'); + return; + } + + // Clear existing content + dungeonListElement.innerHTML = ''; + + if (!this.serverDungeons || Object.keys(this.serverDungeons).length === 0) { + dungeonListElement.innerHTML = '

Loading dungeons from server...

'; + return; + } + + // Generate HTML for each difficulty category + let html = ''; + + Object.entries(this.serverDungeons).forEach(([difficulty, dungeons]) => { + if (!dungeons || dungeons.length === 0) return; + + const difficultyClass = difficulty === 'tutorial' ? 'tutorial' : difficulty; + const difficultyTitle = difficulty === 'tutorial' ? 'Tutorial Dungeons' : + difficulty.charAt(0).toUpperCase() + difficulty.slice(1) + ' Dungeons'; + const difficultyIcon = this.getDifficultyIcon(difficulty); + const sectionId = `dungeon-section-${difficulty}`; + + // Add collapsible difficulty header + html += ` +
+
+
+ + ${difficultyTitle} + (${dungeons.length}) +
+
+ +
+
+
+ `; + + dungeons.forEach(dungeon => { + const canEnter = this.canEnterDungeon(dungeon); + const statusClass = canEnter ? 'available' : 'locked'; + const energyCost = dungeon.energyCost || 0; + const healthType = dungeon.healthType || 'player'; + const healthIcon = healthType === 'ship' ? '🚀' : '👤'; + + // Each dungeon in its own individual container using proper CSS classes + html += ` +
+
${dungeon.name}
+
+ ${difficulty} - ${energyCost} Energy +
+
${dungeon.description}
+
${healthIcon}
+
+ Enemies: +
+ ${this.generateEnemyList(dungeon.enemyTypes || [])} +
+
+ +
+ `; + }); + + // Close the section + html += ` +
+
+ `; + }); + + dungeonListElement.innerHTML = html; + + // Initialize default collapse states + this.initializeDungeonSections(); + } + + /** + * Initialize dungeon sections with saved collapse states + */ + initializeDungeonSections() { + // Default states: tutorial and easy expanded, others collapsed + const defaultStates = { + 'dungeon-section-tutorial': false, // expanded + 'dungeon-section-easy': false, // expanded + 'dungeon-section-medium': true, // collapsed + 'dungeon-section-hard': true, // collapsed + 'dungeon-section-extreme': true // collapsed + }; + + Object.entries(defaultStates).forEach(([sectionId, defaultCollapsed]) => { + const section = document.getElementById(sectionId); + const indicator = document.getElementById(`${sectionId}-indicator`); + + if (section && indicator) { + // Use saved state if available, otherwise use default + const shouldCollapse = this.collapseStates.has(sectionId) ? + this.collapseStates.get(sectionId) : defaultCollapsed; + + if (shouldCollapse) { + section.classList.add('collapsed'); + indicator.classList.remove('fa-chevron-down'); + indicator.classList.add('fa-chevron-right'); + } + } + }); + } + + /** + * Toggle dungeon section collapse/expand + */ + toggleDungeonSection(sectionId) { + // Check if game and systems are available + if (!window.game || !window.game.systems || !window.game.systems.dungeonSystem) { + console.warn('[DUNGEON SYSTEM] Game systems not available for toggle'); + return; + } + + const section = document.getElementById(sectionId); + const indicator = document.getElementById(`${sectionId}-indicator`); + + if (!section || !indicator) return; + + const isCollapsed = section.classList.contains('collapsed'); + + if (isCollapsed) { + // Expand + section.classList.remove('collapsed'); + indicator.classList.remove('fa-chevron-right'); + indicator.classList.add('fa-chevron-down'); + } else { + // Collapse + section.classList.add('collapsed'); + indicator.classList.remove('fa-chevron-down'); + indicator.classList.add('fa-chevron-right'); + } + + // Save the state + this.collapseStates.set(sectionId, !isCollapsed); + + console.log(`[DUNGEON SYSTEM] Toggled section ${sectionId}: ${isCollapsed ? 'expanded' : 'collapsed'}`); + } + + /** + * Get difficulty icon for dungeon + */ + getDifficultyIcon(difficulty) { + const icons = { + tutorial: 'fas fa-graduation-cap', + easy: 'fas fa-smile', + medium: 'fas fa-meh', + hard: 'fas fa-frown', + extreme: 'fas fa-skull' + }; + return icons[difficulty] || 'fas fa-question'; + } + + /** + * Generate enemy list HTML for dungeon + */ + generateEnemyList(enemyTypes) { + if (!enemyTypes || enemyTypes.length === 0) { + return 'No enemies'; + } + + let html = ''; + enemyTypes.forEach(enemyType => { + const enemy = this.getEnemyTemplate(enemyType); + if (enemy) { + html += ` +
+ ${enemy.name} +
+ ❤️ ${enemy.health} + ⚔️ ${enemy.attack} + 🛡️ ${enemy.defense} +
+
+ `; + } + }); + + return html; + } + + /** + * Check if player can enter dungeon + */ + canEnterDungeon(dungeon) { + if (!this.game.systems.player) { + return false; + } + + const playerLevel = this.game.systems.player.stats?.level || 1; + const minLevel = dungeon.minLevel || 1; + const maxLevel = dungeon.maxLevel || 999; + const energyCost = dungeon.energyCost || 0; + const playerEnergy = this.game.systems.player.attributes?.energy || 0; + + return playerLevel >= minLevel && + playerLevel <= maxLevel && + playerEnergy >= energyCost; + } + + /** + * Exit current dungeon + */ + exitDungeon() { + console.log('[DUNGEON SYSTEM] Exiting dungeon'); + + if (this.currentDungeon) { + // Send exit packet to server + if (this.game.socket) { + this.game.socket.emit('exit_dungeon', { + instanceId: this.currentDungeon.id, + userId: this.game.systems.player?.id || 'anonymous' + }); + } + } + + // Reset local state + this.currentDungeon = null; + this.currentRoom = null; + this.isExploring = false; + this.dungeonProgress = 0; + + // Update UI to show dungeon list + this.updateUI(); + + // Show notification + if (this.game && this.game.showNotification) { + this.game.showNotification('Exited dungeon', 'info', 2000); + } + } + + /** + * Move to next room (for rooms without enemies) + */ + moveToNextRoom() { + console.log('[DUNGEON SYSTEM] Moving to next room'); + + if (!this.currentDungeon) { + console.warn('[DUNGEON SYSTEM] No active dungeon to continue'); + return; + } + + // Request next room from server + if (this.game.socket) { + this.game.socket.emit('next_room', { + instanceId: this.currentDungeon.id, + userId: this.game.systems.player?.id || 'anonymous' + }); + } + } + + /** + * Update UI with current dungeon information + */ + updateUI() { + if (this.game.systems.ui) { + this.game.systems.ui.updateDungeonUI({ + currentDungeon: this.currentDungeon, + currentRoom: this.currentRoom, + progress: this.dungeonProgress, + isExploring: this.isExploring + }); + } else { + console.warn('[DUNGEON SYSTEM] UI manager not available in game.systems.ui'); + } + } + + /** + * Initialize system and load server data + */ + async initialize() { + console.log('[DUNGEON SYSTEM] Initializing client dungeon system...'); + + // Set up socket listeners if not already done + if (!this.game.socket) { + console.warn('[DUNGEON SYSTEM] Socket not available during initialization, will retry...'); + // Retry after a short delay + setTimeout(() => { + if (this.game.socket) { + this.setupSocketListeners(); + this.loadServerData(); + } else { + console.error('[DUNGEON SYSTEM] Socket still not available after retry'); + } + }, 1000); + return; + } + + this.setupSocketListeners(); + await this.loadServerData(); + } +} + +// Export DungeonSystem to global scope +if (typeof window !== 'undefined') { + window.DungeonSystem = DungeonSystem; +} + +// Export for use in GameEngine +if (typeof module !== 'undefined' && module.exports) { + module.exports = DungeonSystem; +} diff --git a/Client-Server/js/systems/IdleSystem.js b/Galaxy-Strike-Online-main/Client/js/systems/IdleSystem.js similarity index 91% rename from Client-Server/js/systems/IdleSystem.js rename to Galaxy-Strike-Online-main/Client/js/systems/IdleSystem.js index 0eec3bd..f15973d 100644 --- a/Client-Server/js/systems/IdleSystem.js +++ b/Galaxy-Strike-Online-main/Client/js/systems/IdleSystem.js @@ -12,11 +12,18 @@ class IdleSystem { this.lastActiveTime = Date.now(); this.accumulatedTime = 0; // Track time for resource generation - // Idle production rates + // Idle production rates (online rates) this.productionRates = { - 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) }; // Offline rewards @@ -144,6 +151,20 @@ class IdleSystem { } claimOfflineRewards() { + // In multiplayer mode, use server communication + if (window.smartSaveManager?.isMultiplayer) { + this.game.showNotification('Claiming offline rewards from server...', 'info', 2000); + + // Send request to server + if (window.game && window.game.socket) { + window.game.socket.emit('claimOfflineRewards', {}); + } else { + this.game.showNotification('Not connected to server', 'error', 3000); + } + return; + } + + // Singleplayer mode - use local logic if (this.offlineRewards.credits === 0 && this.offlineRewards.experience === 0 && this.offlineRewards.items.length === 0) { diff --git a/Galaxy-Strike-Online-main/Client/js/systems/ItemSystem.js b/Galaxy-Strike-Online-main/Client/js/systems/ItemSystem.js new file mode 100644 index 0000000..6d8ae06 --- /dev/null +++ b/Galaxy-Strike-Online-main/Client/js/systems/ItemSystem.js @@ -0,0 +1,468 @@ +/** + * Galaxy Strike Online - Client Item System + * Dynamically loads and manages items from the GameServer + */ + +class ItemSystem { + constructor(gameEngine) { + this.game = gameEngine; + + // Item storage + this.itemCatalog = new Map(); // itemId -> item data + this.shopItems = []; // Array of shop items (legacy) + this.shopItemsByCategory = {}; // Categorized shop items (new structure) + this.lastUpdated = null; + + // Loading state + this.isLoading = false; + this.loadPromise = null; + + // Event listeners + this.eventListeners = new Map(); + } + + /** + * Initialize the item system and load data from server + */ + async initialize() { + console.log('[ITEM SYSTEM] Initializing client item system'); + + if (this.loadPromise) { + return this.loadPromise; + } + + this.loadPromise = this.loadFromServer(); + return this.loadPromise; + } + + /** + * Load all items from the GameServer + */ + async loadFromServer() { + if (this.isLoading) { + console.log('[ITEM SYSTEM] Already loading items from server'); + return this.loadPromise; + } + + this.isLoading = true; + + try { + console.log('[ITEM SYSTEM] Loading items from GameServer - Multiplayer Mode'); + console.log('[ITEM SYSTEM] Socket connection status:', !!window.game?.socket); + + if (!window.game || !window.game.socket) { + throw new Error('Not connected to server - multiplayer mode requires server connection'); + } + + // Load shop items from server + const shopItems = await this.fetchShopItems(); + + // Handle new shop structure (categorized) vs old structure (flat array) + let totalItems = 0; + if (Array.isArray(shopItems)) { + // Old structure: flat array + totalItems = shopItems.length; + console.log('[ITEM SYSTEM] Received', totalItems, 'items from server (old structure)'); + this.processServerItems(shopItems); + this.shopItemsByCategory = {}; // Clear categorized data + } else if (shopItems && typeof shopItems === 'object') { + // New structure: categorized object + totalItems = Object.values(shopItems).reduce((sum, categoryItems) => sum + categoryItems.length, 0); + console.log('[ITEM SYSTEM] Received', totalItems, 'items from server (new structure)'); + console.log('[ITEM SYSTEM] Categories:', Object.keys(shopItems)); + + // Store categorized data + this.shopItemsByCategory = shopItems; + + // Flatten all items for processing + const allItems = Object.values(shopItems).flat(); + this.processServerItems(allItems); + } else { + console.warn('[ITEM SYSTEM] Invalid shop items structure received:', typeof shopItems); + totalItems = 0; + this.shopItemsByCategory = {}; + } + + this.lastUpdated = Date.now(); + console.log(`[ITEM SYSTEM] Successfully loaded ${this.itemCatalog.size} items from server`); + console.log('[ITEM SYSTEM] Item categories loaded:', Object.keys(this.itemCatalog).length); + + // Emit loaded event + this.emit('itemsLoaded', { + itemCount: this.itemCatalog.size, + shopItemCount: this.shopItems.length, + timestamp: this.lastUpdated + }); + + return true; + + } catch (error) { + console.error('[ITEM SYSTEM] Failed to load items from server:', error); + console.error('[ITEM SYSTEM] Error details:', { + message: error.message, + stack: error.stack, + socketConnected: !!window.game?.socket, + socketId: window.game?.socket?.id + }); + + // No fallback - emit error event + this.emit('itemsLoadError', error); + return false; + + } finally { + this.isLoading = false; + } + } + + /** + * Fetch shop items from the GameServer + */ + async fetchShopItems() { + console.log('[ITEM SYSTEM] Starting fetchShopItems'); + + if (!window.game || !window.game.socket) { + console.error('[ITEM SYSTEM] No socket connection available'); + throw new Error('Not connected to server'); + } + + console.log('[ITEM SYSTEM] Socket ID:', window.game.socket.id); + console.log('[ITEM SYSTEM] Socket connected:', window.game.socket.connected); + + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + console.error('[ITEM SYSTEM] Server request timeout after 10 seconds'); + window.game.socket.off('shopItemsReceived', handleResponse); + reject(new Error('Server request timeout')); + }, 10000); + + // Test server connection first + console.log('[ITEM SYSTEM] Testing server connection...'); + window.game.socket.emit('ping', { timestamp: Date.now() }); + + // Listen for ping response + const pingHandler = (data) => { + console.log('[ITEM SYSTEM] Ping response received:', data); + console.log('[ITEM SYSTEM] Server is responding! Ping roundtrip:', Date.now() - data.received, 'ms'); + window.game.socket.off('ping', pingHandler); + window.game.socket.off('pong', pingHandler); + }; + window.game.socket.on('ping', pingHandler); + + // Listen for pong response (backup) + const pongHandler = (data) => { + console.log('[ITEM SYSTEM] Pong response received:', data); + console.log('[ITEM SYSTEM] Server is responding! Pong roundtrip:', Date.now() - data.timestamp, 'ms'); + window.game.socket.off('pong', pongHandler); + }; + window.game.socket.on('pong', pongHandler); + + // Request shop items from server + console.log('[ITEM SYSTEM] Emitting getShopItems request'); + console.log('[ITEM SYSTEM] Socket state:', { + connected: window.game.socket.connected, + id: window.game.socket.id + }); + + window.game.socket.emit('getShopItems', {}); + console.log('[ITEM SYSTEM] Request sent, waiting for response...'); + + // Listen for response + const handleResponse = (data) => { + console.log('[ITEM SYSTEM] Received shopItemsReceived response:', data); + clearTimeout(timeout); + window.game.socket.off('shopItemsReceived', handleResponse); + + console.log('[ITEM SYSTEM] Response success:', data.success); + console.log('[ITEM SYSTEM] Response shopItems keys:', data.shopItems ? Object.keys(data.shopItems) : 'none'); + + if (data.success) { + console.log('[ITEM SYSTEM] Successfully received shop data'); + console.log('[ITEM SYSTEM] Response timestamp:', data.timestamp); + + // Log item counts per category + if (data.shopItems) { + Object.entries(data.shopItems).forEach(([category, items]) => { + console.log(`[ITEM SYSTEM] ${category}: ${items.length} items`); + }); + } + + resolve(data.shopItems || {}); + } else { + console.error('[ITEM SYSTEM] Server returned error:', data.error); + reject(new Error(data.error || 'Failed to load shop items')); + } + }; + + console.log('[ITEM SYSTEM] Setting up shopItemsReceived listener'); + window.game.socket.on('shopItemsReceived', handleResponse); + + // Verify the listener was set up + const listeners = window.game.socket.listeners('shopItemsReceived'); + console.log('[ITEM SYSTEM] shopItemsReceived listeners count:', listeners.length); + }); + } + + /** + * Fetch specific item details from server + */ + async fetchItemDetails(itemId) { + if (!window.game || !window.game.socket) { + throw new Error('Not connected to server'); + } + + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + reject(new Error('Server request timeout')); + }, 5000); + + // Request item details from server + window.game.socket.emit('getItemDetails', { itemId }); + + // Listen for response + const handleResponse = (data) => { + clearTimeout(timeout); + window.game.socket.off('itemDetailsReceived', handleResponse); + + if (data.success) { + // Cache the item + this.itemCatalog.set(itemId, data.item); + resolve(data.item); + } else { + reject(new Error(data.error || 'Item not found')); + } + }; + + window.game.socket.on('itemDetailsReceived', handleResponse); + }); + } + + /** + * Process items received from server + */ + processServerItems(items) { + // Safety check for items parameter + if (!items || !Array.isArray(items)) { + console.error('[ITEM SYSTEM] Invalid items parameter:', items); + console.error('[ITEM SYSTEM] Expected array, got:', typeof items); + return; + } + + console.log('[ITEM SYSTEM] Processing', items.length, 'items from server'); + console.log('[ITEM SYSTEM] Sample items:', items.slice(0, 3)); + + this.itemCatalog.clear(); + this.shopItems = []; + + for (const item of items) { + // Store in catalog + this.itemCatalog.set(item.id, item); + + // Add to shop items if available for shop + if (item.categories && item.categories.includes('shop')) { + this.shopItems.push(item); + } + + // console.log('[ITEM SYSTEM] Added item:', { + // id: item.id, + // name: item.name, + // type: item.type, + // rarity: item.rarity, + // price: item.price, + // categories: item.categories + // }); + } + + console.log('[ITEM SYSTEM] Processing complete - Catalog:', this.itemCatalog.size, 'Shop items:', this.shopItems.length); + console.log('[ITEM SYSTEM] Shop items by type:', this.shopItems.reduce((acc, item) => { + acc[item.type] = (acc[item.type] || 0) + 1; + return acc; + }, {})); + } + + /** + * Get item by ID + */ + getItem(itemId) { + // Return from cache if available + if (this.itemCatalog.has(itemId)) { + return this.itemCatalog.get(itemId); + } + + // Try to fetch from server if not cached + if (window.game && window.game.socket) { + this.fetchItemDetails(itemId).catch(error => { + console.warn(`[ITEM SYSTEM] Failed to fetch item ${itemId}:`, error); + }); + } + + return null; + } + + /** + * Get all shop items + */ + getShopItems() { + return [...this.shopItems]; + } + + /** + * Get shop items by category (new structure) + */ + getShopItemsByCategory() { + return this.shopItemsByCategory || {}; + } + + /** + * Get items by category + */ + getItemsByCategory(category) { + return Array.from(this.itemCatalog.values()).filter(item => + item.type === category || (item.categories && item.categories.includes(category)) + ); + } + + /** + * Get items by type + */ + getItemsByType(type) { + return Array.from(this.itemCatalog.values()).filter(item => item.type === type); + } + + /** + * Get items by rarity + */ + getItemsByRarity(rarity) { + return Array.from(this.itemCatalog.values()).filter(item => item.rarity === rarity); + } + + /** + * Check if player can use item based on requirements + */ + canPlayerUseItem(item, playerLevel = null) { + if (!item.requirements) return true; + + // Get player level if not provided + if (playerLevel === null && window.game && window.game.systems && window.game.systems.player) { + playerLevel = window.game.systems.player.level; + } + + // Check level requirement + if (item.requirements.level && playerLevel < item.requirements.level) { + return false; + } + + // Add other requirement checks here + + return true; + } + + /** + * Get filtered shop items for current player + */ + getAvailableShopItems() { + return this.shopItems.filter(item => this.canPlayerUseItem(item)); + } + + /** + * Format item price for display + */ + formatPrice(item) { + if (!item.price) return 'Free'; + + const currency = item.currency || 'credits'; + const price = this.game.formatNumber(item.price); + + return `${price} ${currency}`; + } + + /** + * Get item rarity color + */ + getRarityColor(rarity) { + const colors = { + common: '#888888', + uncommon: '#00ff00', + rare: '#0088ff', + legendary: '#ff8800', + epic: '#ff00ff' + }; + + return colors[rarity] || '#ffffff'; + } + + /** + * Refresh items from server + */ + async refresh() { + console.log('[ITEM SYSTEM] Refreshing items from server'); + return this.loadFromServer(); + } + + /** + * Event system + */ + on(event, callback) { + if (!this.eventListeners.has(event)) { + this.eventListeners.set(event, []); + } + this.eventListeners.get(event).push(callback); + } + + off(event, callback) { + if (this.eventListeners.has(event)) { + const listeners = this.eventListeners.get(event); + const index = listeners.indexOf(callback); + if (index > -1) { + listeners.splice(index, 1); + } + } + } + + emit(event, data) { + if (this.eventListeners.has(event)) { + for (const callback of this.eventListeners.get(event)) { + try { + callback(data); + } catch (error) { + console.error(`[ITEM SYSTEM] Error in event listener for ${event}:`, error); + } + } + } + } + + /** + * catalog getter — alias for shopItemsByCategory, used by Economy.updateShopUI + */ + get catalog() { + return this.shopItemsByCategory; + } + + /** + * Get system statistics + */ + getStats() { + const stats = { + totalItems: this.itemCatalog.size, + shopItems: this.shopItems.length, + lastUpdated: this.lastUpdated, + isLoading: this.isLoading, + socketConnected: !!(window.game?.socket), + socketId: window.game?.socket?.id + }; + + // Add category breakdown + stats.categories = {}; + for (const item of this.itemCatalog.values()) { + stats.categories[item.type] = (stats.categories[item.type] || 0) + 1; + } + + return stats; + } +} + +// Export for use in other modules +if (typeof module !== 'undefined' && module.exports) { + module.exports = ItemSystem; +} else { + window.ItemSystem = ItemSystem; +} diff --git a/Client-Server/js/systems/QuestSystem.js b/Galaxy-Strike-Online-main/Client/js/systems/QuestSystem.js similarity index 73% rename from Client-Server/js/systems/QuestSystem.js rename to Galaxy-Strike-Online-main/Client/js/systems/QuestSystem.js index 3e01127..aaddf2b 100644 --- a/Client-Server/js/systems/QuestSystem.js +++ b/Galaxy-Strike-Online-main/Client/js/systems/QuestSystem.js @@ -13,6 +13,10 @@ class QuestSystem { this.game = gameEngine; + // Server time synchronization + this.serverTimeOffset = 0; // Difference between server and client time + this.lastServerTimeSync = 0; + // Quest types this.questTypes = { main: 'Main Story', @@ -35,682 +39,56 @@ class QuestSystem { questStatus: Object.keys(this.questStatus) }); - // Main story quests - this.mainQuests = [ - { - id: 'tutorial_complete', - name: 'First Steps', - description: 'Complete the tutorial dungeon and learn the basics', - type: 'main', - status: 'available', - requirements: { level: 1 }, - objectives: [ - { id: 'clear_tutorial_dungeon', description: 'Complete the tutorial dungeon', target: 1, current: 0, type: 'tutorial_dungeon' }, - { id: 'reach_level_2', description: 'Reach level 2', target: 2, current: 0, type: 'level' } - ], - rewards: { credits: 500, experience: 100, gems: 5 }, - nextQuest: 'first_ship_upgrade' - }, - { - id: 'first_ship_upgrade', - name: 'Ship Enhancement', - description: 'Upgrade your ship for better performance', - type: 'main', - status: 'available', - requirements: { quest: 'tutorial_complete' }, - objectives: [ - { id: 'upgrade_weapon', description: 'Upgrade ship weapons', target: 1, current: 0, type: 'upgrade' }, - { id: 'upgrade_shield', description: 'Upgrade ship shields', target: 1, current: 0, type: 'upgrade' } - ], - rewards: { credits: 1000, experience: 200, gems: 10 }, - nextQuest: 'join_guild' - }, - { - id: 'join_guild', - name: 'Guild Recruitment', - description: 'Join a guild and participate in guild activities', - type: 'main', - status: 'available', - requirements: { quest: 'first_ship_upgrade', level: 5 }, - objectives: [ - { id: 'join_guild', description: 'Join a guild', target: 1, current: 0, type: 'guild' }, - { id: 'guild_contribution', description: 'Contribute to guild', target: 100, current: 0, type: 'contribution' } - ], - rewards: { credits: 2000, experience: 500, gems: 20 }, - nextQuest: 'master_commander' - }, - { - id: 'master_commander', - name: 'Master Commander', - description: 'Become a master commander and lead your fleet to victory', - type: 'main', - status: 'available', - requirements: { quest: 'join_guild', level: 10 }, - objectives: [ - { id: 'reach_level_10', description: 'Reach level 10', target: 10, current: 0, type: 'level' }, - { id: 'clear_10_dungeons', description: 'Clear 10 dungeons', target: 10, current: 0, type: 'dungeon' }, - { id: 'max_skill', description: 'Max out one skill', target: 10, current: 0, type: 'skill' } - ], - rewards: { credits: 5000, experience: 1000, gems: 50, item: 'legendary_weapon' } - } - ]; + // Main story quests - populated by server + this.mainQuests = []; - // All possible daily quests (20 total) - this.allDailyQuests = [ - // Easy quests (difficulty: 1) - { - id: 'daily_dungeon_easy', - name: 'Quick Dungeon Run', - description: 'Complete any dungeon', - type: 'daily', - difficulty: 1, - status: 'available', - objectives: [ - { id: 'clear_dungeon', description: 'Clear 1 dungeon', target: 1, current: 0, type: 'dungeon' } - ], - rewards: { credits: 100, experience: 25, gems: 1 } - }, - { - id: 'daily_combat_easy', - name: 'Light Combat', - description: 'Defeat a few enemies', - type: 'daily', - difficulty: 1, - status: 'available', - objectives: [ - { id: 'defeat_enemies', description: 'Defeat 10 enemies', target: 10, current: 0, type: 'combat' } - ], - rewards: { credits: 80, experience: 20, gems: 1 } - }, - { - id: 'daily_crafting_easy', - name: 'Basic Crafting', - description: 'Craft some items', - type: 'daily', - difficulty: 1, - status: 'available', - objectives: [ - { id: 'craft_items', description: 'Craft 2 items', target: 2, current: 0, type: 'crafting' } - ], - rewards: { credits: 90, experience: 22, gems: 1 } - }, - { - id: 'daily_level_easy', - name: 'Level Up', - description: 'Gain experience and level up', - type: 'daily', - difficulty: 1, - status: 'available', - objectives: [ - { id: 'gain_level', description: 'Gain 1 level', target: 1, current: 0, type: 'level' } - ], - rewards: { credits: 120, experience: 30, gems: 2 } - }, - { - id: 'daily_energy_easy', - name: 'Energy Management', - description: 'Use energy efficiently', - type: 'daily', - difficulty: 1, - status: 'available', - objectives: [ - { id: 'use_energy', description: 'Use 50 energy', target: 50, current: 0, type: 'energy' } - ], - rewards: { credits: 70, experience: 18, gems: 1 } - }, - // Medium quests (difficulty: 2) - { - id: 'daily_dungeon_medium', - name: 'Dungeon Explorer', - description: 'Complete multiple dungeons', - type: 'daily', - difficulty: 2, - status: 'available', - objectives: [ - { id: 'clear_dungeons', description: 'Clear 3 dungeons', target: 3, current: 0, type: 'dungeon' } - ], - rewards: { credits: 300, experience: 75, gems: 3 } - }, - { - id: 'daily_combat_medium', - name: 'Combat Training', - description: 'Defeat enemies in combat', - type: 'daily', - difficulty: 2, - status: 'available', - objectives: [ - { id: 'defeat_enemies', description: 'Defeat 20 enemies', target: 20, current: 0, type: 'combat' } - ], - rewards: { credits: 150, experience: 40, gems: 1 } - }, - { - id: 'daily_combat_hard', - name: 'Combat Veteran', - description: 'Defeat many enemies', - type: 'daily', - difficulty: 2, - status: 'available', - objectives: [ - { id: 'defeat_enemies', description: 'Defeat 50 enemies', target: 50, current: 0, type: 'combat' } - ], - rewards: { credits: 250, experience: 60, gems: 3 } - }, - { - id: 'daily_crafting_medium', - name: 'Master Crafter', - description: 'Craft many items', - type: 'daily', - difficulty: 2, - status: 'available', - objectives: [ - { id: 'craft_items', description: 'Craft 5 items', target: 5, current: 0, type: 'crafting' } - ], - rewards: { credits: 280, experience: 70, gems: 3 } - }, - { - id: 'daily_upgrade_medium', - name: 'Equipment Upgrade', - description: 'Upgrade your equipment', - type: 'daily', - difficulty: 2, - status: 'available', - objectives: [ - { id: 'upgrade_items', description: 'Upgrade 3 items', target: 3, current: 0, type: 'upgrade' } - ], - rewards: { credits: 320, experience: 80, gems: 4 } - }, - { - id: 'daily_wealth_medium', - name: 'Wealth Accumulator', - description: 'Earn credits', - type: 'daily', - difficulty: 2, - status: 'available', - objectives: [ - { id: 'earn_credits', description: 'Earn 1000 credits', target: 1000, current: 0, type: 'credits' } - ], - rewards: { credits: 400, experience: 50, gems: 3 } - }, - // Hard quests (difficulty: 3) - { - id: 'daily_dungeon_hard', - name: 'Dungeon Master', - description: 'Complete many dungeons', - type: 'daily', - difficulty: 3, - status: 'available', - objectives: [ - { id: 'clear_dungeons', description: 'Clear 5 dungeons', target: 5, current: 0, type: 'dungeon' } - ], - rewards: { credits: 600, experience: 150, gems: 6 } - }, - { - id: 'daily_combat_hard', - name: 'Combat Master', - description: 'Defeat many powerful enemies', - type: 'daily', - difficulty: 3, - status: 'available', - objectives: [ - { id: 'defeat_enemies', description: 'Defeat 100 enemies', target: 100, current: 0, type: 'combat' } - ], - rewards: { credits: 500, experience: 120, gems: 5 } - }, - { - id: 'daily_level_hard', - name: 'Power Leveling', - description: 'Gain multiple levels', - type: 'daily', - difficulty: 3, - status: 'available', - objectives: [ - { id: 'gain_levels', description: 'Gain 3 levels', target: 3, current: 0, type: 'level' } - ], - rewards: { credits: 700, experience: 200, gems: 7 } - }, - { - id: 'daily_boss_hard', - name: 'Boss Hunter', - description: 'Defeat boss enemies', - type: 'daily', - difficulty: 3, - status: 'available', - objectives: [ - { id: 'defeat_bosses', description: 'Defeat 3 bosses', target: 3, current: 0, type: 'boss' } - ], - rewards: { credits: 800, experience: 180, gems: 8 } - }, - { - id: 'daily_collection_hard', - name: 'Master Collector', - description: 'Collect rare items', - type: 'daily', - difficulty: 3, - status: 'available', - objectives: [ - { id: 'collect_rare', description: 'Collect 10 rare items', target: 10, current: 0, type: 'collection' } - ], - rewards: { credits: 650, experience: 140, gems: 6 } - }, - // Special quests (difficulty: 4) - { - id: 'daily_speedrun', - name: 'Speed Runner', - description: 'Complete dungeons quickly', - type: 'daily', - difficulty: 4, - status: 'available', - objectives: [ - { id: 'fast_dungeon', description: 'Complete 2 dungeons under 5 minutes', target: 2, current: 0, type: 'speedrun' } - ], - rewards: { credits: 1000, experience: 250, gems: 10 } - }, - { - id: 'daily_perfection', - name: 'Perfectionist', - description: 'Complete objectives without taking damage', - type: 'daily', - difficulty: 4, - status: 'available', - objectives: [ - { id: 'perfect_runs', description: '3 perfect dungeon runs', target: 3, current: 0, type: 'perfect' } - ], - rewards: { credits: 1200, experience: 300, gems: 12 } - }, - { - id: 'daily_multitask', - name: 'Multitask Master', - description: 'Complete multiple quest types', - type: 'daily', - difficulty: 4, - status: 'available', - objectives: [ - { id: 'dungeon_task', description: 'Clear 2 dungeons', target: 2, current: 0, type: 'dungeon' }, - { id: 'combat_task', description: 'Defeat 30 enemies', target: 30, current: 0, type: 'combat' }, - { id: 'craft_task', description: 'Craft 2 items', target: 2, current: 0, type: 'crafting' } - ], - rewards: { credits: 1500, experience: 400, gems: 15 } - }, - { - id: 'daily_endurance', - name: 'Endurance Test', - description: 'Complete long activities', - type: 'daily', - difficulty: 4, - status: 'available', - objectives: [ - { id: 'long_dungeon', description: 'Complete 1 dungeon without healing', target: 1, current: 0, type: 'endurance' } - ], - rewards: { credits: 1100, experience: 280, gems: 11 } - }, - { - id: 'daily_legendary', - name: 'Legendary Challenge', - description: 'Complete legendary difficulty content', - type: 'daily', - difficulty: 4, - status: 'available', - objectives: [ - { id: 'legendary_content', description: 'Complete 1 legendary dungeon', target: 1, current: 0, type: 'legendary' } - ], - rewards: { credits: 2000, experience: 500, gems: 20, item: 'rare_material' } - } - ]; - - // Weekly quests (25 total quests with varied objectives) - this.allWeeklyQuests = [ - // Combat-focused weekly quests - { - id: 'weekly_combat_basic', - name: 'Weekly Combat Duty', - description: 'Complete combat objectives throughout the week', - type: 'weekly', - difficulty: 1, - status: 'available', - objectives: [ - { id: 'defeat_enemies', description: 'Defeat 100 enemies', target: 100, current: 0, type: 'combat' } - ], - rewards: { credits: 800, experience: 200, gems: 8 } - }, - { - id: 'weekly_combat_elite', - name: 'Elite Hunter Weekly', - description: 'Hunt down elite enemies', - type: 'weekly', - difficulty: 3, - status: 'available', - objectives: [ - { id: 'defeat_elites', description: 'Defeat 25 elite enemies', target: 25, current: 0, type: 'elite_combat' } - ], - rewards: { credits: 1500, experience: 400, gems: 15 } - }, - { - id: 'weekly_boss_hunter', - name: 'Boss Hunter Weekly', - description: 'Defeat powerful boss enemies', - type: 'weekly', - difficulty: 4, - status: 'available', - objectives: [ - { id: 'defeat_bosses', description: 'Defeat 10 bosses', target: 10, current: 0, type: 'boss' } - ], - rewards: { credits: 2500, experience: 600, gems: 25, item: 'boss_material' } - }, - - // Dungeon-focused weekly quests - { - id: 'weekly_dungeon_explorer', - name: 'Weekly Dungeon Explorer', - description: 'Explore various dungeons', - type: 'weekly', - difficulty: 2, - status: 'available', - objectives: [ - { id: 'clear_dungeons', description: 'Clear 15 dungeons', target: 15, current: 0, type: 'dungeon' } - ], - rewards: { credits: 1200, experience: 300, gems: 12 } - }, - { - id: 'weekly_dungeon_master', - name: 'Weekly Dungeon Master', - description: 'Master difficult dungeons', - type: 'weekly', - difficulty: 3, - status: 'available', - objectives: [ - { id: 'clear_hard_dungeons', description: 'Clear 10 hard dungeons', target: 10, current: 0, type: 'hard_dungeon' } - ], - rewards: { credits: 2000, experience: 500, gems: 20 } - }, - { - id: 'weekly_dungeon_extreme', - name: 'Extreme Dungeon Challenge', - description: 'Conquer extreme difficulty dungeons', - type: 'weekly', - difficulty: 4, - status: 'available', - objectives: [ - { id: 'clear_extreme_dungeons', description: 'Clear 5 extreme dungeons', target: 5, current: 0, type: 'extreme_dungeon' } - ], - rewards: { credits: 3500, experience: 800, gems: 35, item: 'extreme_material' } - }, - - // Crafting and upgrade weekly quests - { - id: 'weekly_crafting_basic', - name: 'Weekly Crafting Session', - description: 'Craft items throughout the week', - type: 'weekly', - difficulty: 1, - status: 'available', - objectives: [ - { id: 'craft_items', description: 'Craft 20 items', target: 20, current: 0, type: 'crafting' } - ], - rewards: { credits: 600, experience: 150, gems: 6 } - }, - { - id: 'weekly_crafting_master', - name: 'Master Crafter Weekly', - description: 'Craft advanced items', - type: 'weekly', - difficulty: 3, - status: 'available', - objectives: [ - { id: 'craft_advanced', description: 'Craft 10 advanced items', target: 10, current: 0, type: 'advanced_crafting' } - ], - rewards: { credits: 1800, experience: 450, gems: 18 } - }, - { - id: 'weekly_upgrade_specialist', - name: 'Weekly Upgrade Specialist', - description: 'Upgrade equipment and systems', - type: 'weekly', - difficulty: 2, - status: 'available', - objectives: [ - { id: 'upgrade_items', description: 'Upgrade 15 items', target: 15, current: 0, type: 'upgrade' } - ], - rewards: { credits: 1400, experience: 350, gems: 14 } - }, - - // Progression weekly quests - { - id: 'weekly_level_up', - name: 'Weekly Level Up Challenge', - description: 'Gain levels throughout the week', - type: 'weekly', - difficulty: 2, - status: 'available', - objectives: [ - { id: 'gain_levels', description: 'Gain 5 levels', target: 5, current: 0, type: 'level' } - ], - rewards: { credits: 1000, experience: 250, gems: 10 } - }, - { - id: 'weekly_skill_master', - name: 'Weekly Skill Mastery', - description: 'Improve your skills', - type: 'weekly', - difficulty: 3, - status: 'available', - objectives: [ - { id: 'improve_skills', description: 'Gain 20 skill points', target: 20, current: 0, type: 'skill' } - ], - rewards: { credits: 1600, experience: 400, gems: 16 } - }, - - // Resource and wealth weekly quests - { - id: 'weekly_wealth_collector', - name: 'Weekly Wealth Collector', - description: 'Accumulate wealth', - type: 'weekly', - difficulty: 2, - status: 'available', - objectives: [ - { id: 'earn_credits', description: 'Earn 5000 credits', target: 5000, current: 0, type: 'credits' } - ], - rewards: { credits: 2000, experience: 300, gems: 12 } - }, - { - id: 'weekly_resource_gatherer', - name: 'Weekly Resource Gathering', - description: 'Collect valuable resources', - type: 'weekly', - difficulty: 2, - status: 'available', - objectives: [ - { id: 'collect_resources', description: 'Collect 500 resources', target: 500, current: 0, type: 'collection' } - ], - rewards: { credits: 1200, experience: 320, gems: 13 } - }, - - // Special activity weekly quests - { - id: 'weekly_energy_management', - name: 'Weekly Energy Management', - description: 'Use energy efficiently', - type: 'weekly', - difficulty: 1, - status: 'available', - objectives: [ - { id: 'use_energy', description: 'Use 500 energy', target: 500, current: 0, type: 'energy' } - ], - rewards: { credits: 800, experience: 180, gems: 8 } - }, - { - id: 'weekly_speed_demon', - name: 'Weekly Speed Demon', - description: 'Complete activities quickly', - type: 'weekly', - difficulty: 3, - status: 'available', - objectives: [ - { id: 'speed_runs', description: 'Complete 10 speed runs', target: 10, current: 0, type: 'speedrun' } - ], - rewards: { credits: 2200, experience: 550, gems: 22 } - }, - { - id: 'weekly_perfectionist', - name: 'Weekly Perfectionist', - description: 'Complete flawless runs', - type: 'weekly', - difficulty: 4, - status: 'available', - objectives: [ - { id: 'perfect_runs', description: 'Complete 8 perfect runs', target: 8, current: 0, type: 'perfect' } - ], - rewards: { credits: 3000, experience: 700, gems: 30, item: 'perfection_material' } - }, - - // Multi-objective weekly quests - { - id: 'weekly_all_rounder', - name: 'Weekly All-Rounder', - description: 'Complete various activities', - type: 'weekly', - difficulty: 3, - status: 'available', - objectives: [ - { id: 'dungeon_task', description: 'Clear 8 dungeons', target: 8, current: 0, type: 'dungeon' }, - { id: 'combat_task', description: 'Defeat 50 enemies', target: 50, current: 0, type: 'combat' }, - { id: 'craft_task', description: 'Craft 5 items', target: 5, current: 0, type: 'crafting' } - ], - rewards: { credits: 2500, experience: 600, gems: 25 } - }, - { - id: 'weekly_specialist', - name: 'Weekly Specialist', - description: 'Focus on specialized activities', - type: 'weekly', - difficulty: 3, - status: 'available', - objectives: [ - { id: 'special_dungeons', description: 'Clear 5 themed dungeons', target: 5, current: 0, type: 'themed_dungeon' }, - { id: 'special_crafting', description: 'Craft 8 themed items', target: 8, current: 0, type: 'themed_crafting' } - ], - rewards: { credits: 2300, experience: 580, gems: 23 } - }, - - // Exploration and discovery weekly quests - { - id: 'weekly_explorer', - name: 'Weekly Explorer', - description: 'Explore new areas and content', - type: 'weekly', - difficulty: 2, - status: 'available', - objectives: [ - { id: 'explore_areas', description: 'Explore 20 new areas', target: 20, current: 0, type: 'exploration' } - ], - rewards: { credits: 1300, experience: 340, gems: 13 } - }, - { - id: 'weekly_discovery', - name: 'Weekly Discovery', - description: 'Discover hidden secrets', - type: 'weekly', - difficulty: 3, - status: 'available', - objectives: [ - { id: 'discover_secrets', description: 'Discover 15 secrets', target: 15, current: 0, type: 'discovery' } - ], - rewards: { credits: 1900, experience: 480, gems: 19 } - }, - - // Endurance and challenge weekly quests - { - id: 'weekly_endurance', - name: 'Weekly Endurance Test', - description: 'Complete long-form challenges', - type: 'weekly', - difficulty: 3, - status: 'available', - objectives: [ - { id: 'endurance_runs', description: 'Complete 5 endurance runs', target: 5, current: 0, type: 'endurance' } - ], - rewards: { credits: 2100, experience: 530, gems: 21 } - }, - { - id: 'weekly_survivor', - name: 'Weekly Survivor', - description: 'Survive challenging conditions', - type: 'weekly', - difficulty: 4, - status: 'available', - objectives: [ - { id: 'survival_runs', description: 'Complete 3 survival runs', target: 3, current: 0, type: 'survival' } - ], - rewards: { credits: 3200, experience: 750, gems: 32, item: 'survival_material' } - }, - - // Social and community weekly quests - { - id: 'weekly_helper', - name: 'Weekly Helper', - description: 'Assist other players', - type: 'weekly', - difficulty: 2, - status: 'available', - objectives: [ - { id: 'assist_players', description: 'Assist 10 players', target: 10, current: 0, type: 'assist' } - ], - rewards: { credits: 1100, experience: 280, gems: 11 } - }, - { - id: 'weekly_leader', - name: 'Weekly Leader', - description: 'Lead group activities', - type: 'weekly', - difficulty: 3, - status: 'available', - objectives: [ - { id: 'lead_activities', description: 'Lead 5 group activities', target: 5, current: 0, type: 'leadership' } - ], - rewards: { credits: 1700, experience: 430, gems: 17 } - }, - - // Legendary weekly quests - { - id: 'weekly_legendary', - name: 'Weekly Legendary Challenge', - description: 'Complete legendary difficulty content', - type: 'weekly', - difficulty: 5, - status: 'available', - objectives: [ - { id: 'legendary_content', description: 'Complete 3 legendary dungeons', target: 3, current: 0, type: 'legendary' } - ], - rewards: { credits: 5000, experience: 1200, gems: 50, item: 'legendary_material' } - }, - { - id: 'weekly_mythic', - name: 'Weekly Mythic Trial', - description: 'Face mythic level challenges', - type: 'weekly', - difficulty: 5, - status: 'available', - objectives: [ - { id: 'mythic_trials', description: 'Complete 2 mythic trials', target: 2, current: 0, type: 'mythic' } - ], - rewards: { credits: 6000, experience: 1500, gems: 60, item: 'mythic_material' } - } - ]; - - // Currently active daily quests (3 random from allDailyQuests) + // Daily quests - populated by server this.dailyQuests = []; - this.selectedDailyQuests = []; - - // Currently active weekly quests (5 random from allWeeklyQuests) this.weeklyQuests = []; - this.selectedWeeklyQuests = []; - - // Current active quests this.activeQuests = []; this.completedQuests = []; this.failedQuests = []; - this.completedDailyQuests = []; // History of completed daily quests - this.completedWeeklyQuests = []; // History of completed weekly quests + + // Quest tracking arrays + this.selectedDailyQuests = []; + this.selectedWeeklyQuests = []; + this.completedDailyQuests = []; + this.completedWeeklyQuests = []; + + // Procedural quest templates (server-driven) + this.proceduralTemplates = {}; + + // Quest generation settings + this.maxProceduralQuests = 3; + this.proceduralQuestRefresh = 30 * 60 * 1000; // 30 minutes + + // Initialize stats + this.stats = { + questsCompleted: 0, + dailyQuestsCompleted: 0, + weeklyQuestsCompleted: 0, + totalRewardsEarned: { credits: 0, experience: 0, gems: 0 }, + lastDailyReset: this.getServerTime(), + lastWeeklyReset: this.getServerTime() + }; + + console.log('[QUEST SYSTEM] Client quest system initialized - waiting for server data'); + + if (debugLogger) debugLogger.endStep('QuestSystem.constructor', { + mainQuestsCount: this.mainQuests.length, + dailyQuestsCount: this.dailyQuests.length, + weeklyQuestsCount: this.weeklyQuests.length, + maxProceduralQuests: this.maxProceduralQuests, + proceduralQuestRefresh: this.proceduralQuestRefresh, + initialStats: this.stats, + dailyQuestsInitialized: this.dailyQuests.length, + weeklyQuestsInitialized: this.weeklyQuests.length + }); + + // Start countdown timers for server-driven quests + console.log('[QUEST SYSTEM] Starting countdown timers'); + this.startDailyCountdown(); + this.startWeeklyCountdown(); // Initialize daily quests with safety check try { @@ -788,14 +166,15 @@ class QuestSystem { this.maxProceduralQuests = 3; this.proceduralQuestRefresh = 30 * 60 * 1000; // 30 minutes - // Statistics + // Initialize stats this.stats = { questsCompleted: 0, + questsFailed: 0, dailyQuestsCompleted: 0, weeklyQuestsCompleted: 0, totalRewardsEarned: { credits: 0, experience: 0, gems: 0 }, - lastDailyReset: Date.now(), - lastWeeklyReset: Date.now() + lastDailyReset: this.getServerTime(), + lastWeeklyReset: this.getServerTime() }; // Initialize daily quests @@ -816,6 +195,43 @@ class QuestSystem { }); } + // Server time synchronization methods + getServerTime() { + // In multiplayer mode, use UTC time as server time + if (window.smartSaveManager?.isMultiplayer) { + // Get current UTC timestamp + const utcTime = Date.now(); + + console.log('[QUEST SYSTEM] Using UTC time as server time:', utcTime); + console.log('[QUEST SYSTEM] Local time:', Date.now()); + + return utcTime; + } + + // Fallback to client time in singleplayer + return Date.now(); + } + + getServerDate() { + // Create a date that displays in UTC + const timestamp = this.getServerTime(); + const utcDate = new Date(timestamp); + + // Force UTC display by using UTC methods + return { + getTime: () => timestamp, + getDay: () => utcDate.getUTCDay(), + getHours: () => utcDate.getUTCHours(), + getMinutes: () => utcDate.getUTCMinutes(), + getSeconds: () => utcDate.getUTCSeconds(), + getDate: () => utcDate.getUTCDate(), + getFullYear: () => utcDate.getUTCFullYear(), + getMonth: () => utcDate.getUTCMonth(), + toString: () => utcDate.toUTCString(), + valueOf: () => timestamp + }; + } + async initialize() { const debugLogger = window.debugLogger; @@ -1119,7 +535,7 @@ class QuestSystem { // Complete quest quest.status = 'completed'; - quest.completedAt = Date.now(); + quest.completedAt = this.getServerTime(); this.completedQuests.push(quest); if (debugLogger) debugLogger.logStep('Quest marked as completed', { @@ -1131,7 +547,7 @@ class QuestSystem { // Save completed daily quests to history if (quest.type === 'daily') { - const questCopy = { ...quest, completedAt: Date.now() }; + const questCopy = { ...quest, completedAt: this.getServerTime() }; this.completedDailyQuests.push(questCopy); if (debugLogger) debugLogger.logStep('Daily quest added to history', { @@ -1143,7 +559,7 @@ class QuestSystem { // Save completed weekly quests to history if (quest.type === 'weekly') { - const questCopy = { ...quest, completedAt: Date.now() }; + const questCopy = { ...quest, completedAt: this.getServerTime() }; this.completedWeeklyQuests.push(questCopy); if (debugLogger) debugLogger.logStep('Weekly quest added to history', { @@ -1542,7 +958,7 @@ class QuestSystem { this.completedQuests = []; this.dailyQuests = []; this.selectedDailyQuests = []; - this.lastDailyReset = Date.now(); + this.lastDailyReset = this.getServerTime(); // Reset main quest statuses this.mainQuests.forEach(quest => { @@ -1575,7 +991,7 @@ class QuestSystem { checkDailyReset() { const debugLogger = window.debugLogger; - const now = Date.now(); + const now = this.getServerTime(); const lastReset = this.lastDailyReset; const daysSinceReset = Math.floor((now - lastReset) / (24 * 60 * 60 * 1000)); @@ -1646,10 +1062,10 @@ class QuestSystem { updateDailyCountdown() { // Always update countdown so it's ready when user switches to daily tab - const now = new Date(); - const tomorrow = new Date(now); - tomorrow.setDate(tomorrow.getDate() + 1); - tomorrow.setHours(0, 0, 0, 0); // Set to midnight + const now = this.getServerDate(); + const tomorrow = new Date(); + tomorrow.setUTCDate(now.getDate() + 1); + tomorrow.setUTCHours(0, 0, 0, 0); // Set to midnight UTC const timeUntilReset = tomorrow - now; const hours = Math.floor(timeUntilReset / (1000 * 60 * 60)); @@ -1714,11 +1130,11 @@ class QuestSystem { checkWeeklyReset() { const debugLogger = window.debugLogger; - const now = Date.now(); + const now = this.getServerTime(); const lastReset = this.lastWeeklyReset || 0; - // Calculate if we need to reset based on Saturday midnight - const currentDateTime = new Date(); + // Calculate if we need to reset based on Saturday midnight (server time) + const currentDateTime = this.getServerDate(); const dayOfWeek = currentDateTime.getDay(); // 0 = Sunday, 1 = Monday, ..., 6 = Saturday const currentHour = currentDateTime.getHours(); const currentMinute = currentDateTime.getMinutes(); @@ -1814,7 +1230,7 @@ class QuestSystem { } updateWeeklyCountdown() { - const now = new Date(); + const now = this.getServerDate(); const dayOfWeek = now.getDay(); // 0 = Sunday, 1 = Monday, ..., 6 = Saturday const currentHour = now.getHours(); const currentMinute = now.getMinutes(); @@ -1848,19 +1264,19 @@ class QuestSystem { console.log('[QUEST SYSTEM] Days until Saturday:', daysUntilSaturday); - // Create the target reset time (Saturday midnight) - const nextSaturday = new Date(now); - nextSaturday.setDate(now.getDate() + daysUntilSaturday); - nextSaturday.setHours(0, 0, 0, 0); // Set to midnight + // Create the target reset time (Saturday midnight UTC) + const nextSaturday = new Date(); + nextSaturday.setUTCDate(now.getDate() + daysUntilSaturday); + nextSaturday.setUTCHours(0, 0, 0, 0); // Set to midnight UTC - console.log('[QUEST SYSTEM] Next Saturday reset time:', nextSaturday.toString()); + console.log('[QUEST SYSTEM] Next Saturday reset time:', nextSaturday.toUTCString()); let timeUntilReset = nextSaturday.getTime() - now.getTime(); // Ensure timeUntilReset is positive (handle edge cases) if (timeUntilReset <= 0) { console.log('[QUEST SYSTEM] Time until reset is negative or zero, adding 7 days'); - nextSaturday.setDate(nextSaturday.getDate() + 7); + nextSaturday.setUTCDate(nextSaturday.getDate() + 7); timeUntilReset = nextSaturday.getTime() - now.getTime(); } @@ -2065,8 +1481,58 @@ class QuestSystem { return completed.sort((a, b) => (b.completedAt || 0) - (a.completedAt || 0)); } + // Load quests from server data + loadServerQuests(serverQuestData) { + console.log('[QUEST SYSTEM] Loading server quest data:', serverQuestData); + + if (!serverQuestData) { + console.log('[QUEST SYSTEM] No server quest data provided'); + return; + } + + // Clear existing quests + this.mainQuests = []; + this.dailyQuests = []; + this.weeklyQuests = []; + this.activeQuests = []; + this.completedQuests = []; + + // Load quests from server data + if (serverQuestData.mainQuests && Array.isArray(serverQuestData.mainQuests)) { + this.mainQuests = serverQuestData.mainQuests; + console.log('[QUEST SYSTEM] Loaded', this.mainQuests.length, 'main quests'); + } + + if (serverQuestData.dailyQuests && Array.isArray(serverQuestData.dailyQuests)) { + this.dailyQuests = serverQuestData.dailyQuests; + console.log('[QUEST SYSTEM] Loaded', this.dailyQuests.length, 'daily quests'); + } + + if (serverQuestData.activeQuests && Array.isArray(serverQuestData.activeQuests)) { + this.activeQuests = serverQuestData.activeQuests; + console.log('[QUEST SYSTEM] Loaded', this.activeQuests.length, 'active quests'); + } + + if (serverQuestData.completedQuests && Array.isArray(serverQuestData.completedQuests)) { + this.completedQuests = serverQuestData.completedQuests; + console.log('[QUEST SYSTEM] Loaded', this.completedQuests.length, 'completed quests'); + } + + console.log('[QUEST SYSTEM] Server quest data loaded successfully'); + console.log('[QUEST SYSTEM] Total quests loaded:', { + main: this.mainQuests.length, + daily: this.dailyQuests.length, + active: this.activeQuests.length, + completed: this.completedQuests.length + }); + } + // UI updates updateUI() { + console.log('[QUEST SYSTEM] updateUI called'); + console.log('[QUEST SYSTEM] Game available:', !!this.game); + console.log('[QUEST SYSTEM] Multiplayer mode:', window.smartSaveManager?.isMultiplayer); + this.updateQuestList(); this.updateQuestStats(); } @@ -2084,6 +1550,7 @@ class QuestSystem { const quests = this.getQuestsByType(activeType); console.log(`[QUEST SYSTEM] Getting quests for type: ${activeType}, found: ${quests.length} quests`); + console.log('[QUEST SYSTEM] First few quests:', quests.slice(0, 3)); questListElement.innerHTML = ''; diff --git a/Client-Server/js/systems/ShipSystem.js b/Galaxy-Strike-Online-main/Client/js/systems/ShipSystem.js similarity index 79% rename from Client-Server/js/systems/ShipSystem.js rename to Galaxy-Strike-Online-main/Client/js/systems/ShipSystem.js index e3dcb7e..fc05106 100644 --- a/Client-Server/js/systems/ShipSystem.js +++ b/Galaxy-Strike-Online-main/Client/js/systems/ShipSystem.js @@ -60,6 +60,22 @@ class ShipSystem { shipGrid.appendChild(shipCard); }); } + + /** + * Get ship image URL from server or local + */ + getShipImageUrl(ship) { + if (!ship) return 'https://dev.gameserver.galaxystrike.online/images/ui/placeholder.png'; + + // For multiplayer, get from server + if (window.smartSaveManager?.isMultiplayer && window.game?.socket) { + const serverUrl = window.game.socket.io?.uri?.replace('/socket.io', '') || process.env.SERVER_URL || 'https://dev.gameserver.galaxystrike.online'; + return `${serverUrl}/images/ships/${ship.id}.png`; + } + + // For singleplayer, use local path + return ship.image || ship.texture || 'assets/textures/ships/starter_cruiser.png'; + } createShipCard(ship) { const card = document.createElement('div'); @@ -68,17 +84,13 @@ class ShipSystem { card.innerHTML = `
- ${ship.name} + ${ship.name}
${ship.rarity}
-
- -
`; return card; @@ -106,10 +118,22 @@ class ShipSystem { const ship = player.ship; if (elements.currentShipImage) { - // Use the ship's texture if available, otherwise fallback - const imagePath = ship.texture || `assets/textures/ships/starter_cruiser.png`; + // Use server image for multiplayer, local for singleplayer + let imagePath; + if (window.smartSaveManager?.isMultiplayer && window.game?.socket) { + const serverUrl = window.game.socket.io?.uri?.replace('/socket.io', '') || 'http://localhost:3002'; + imagePath = `${serverUrl}/images/ships/${ship.class || 'starter_cruiser'}.png`; + } else { + imagePath = ship.texture || `assets/textures/ships/starter_cruiser.png`; + } + elements.currentShipImage.src = imagePath; elements.currentShipImage.alt = ship.name; + elements.currentShipImage.onerror = function() { + this.src = window.smartSaveManager?.isMultiplayer ? + 'https://dev.gameserver.galaxystrike.online/images/ui/placeholder.png' : + 'assets/textures/missing-texture.png'; + }; } if (elements.currentShipName) elements.currentShipName.textContent = ship.name; if (elements.currentShipClass) elements.currentShipClass.textContent = ship.class || 'Unknown'; diff --git a/Galaxy-Strike-Online-main/Client/js/systems/SkillSystem.js b/Galaxy-Strike-Online-main/Client/js/systems/SkillSystem.js new file mode 100644 index 0000000..cdce8a8 --- /dev/null +++ b/Galaxy-Strike-Online-main/Client/js/systems/SkillSystem.js @@ -0,0 +1,407 @@ +/** + * Galaxy Strike Online - Client Skill System + * Skill definitions are loaded from the server; this file handles + * local progression tracking, UI rendering, and skill-point spending. + */ + +class SkillSystem { + constructor(gameEngine) { + this.game = gameEngine; + + // Populated after server responds to 'get_skills' + this.skills = { combat: {}, science: {}, crafting: {} }; + + this.categories = { + combat: 'Combat', + science: 'Science', + crafting: 'Crafting' + }; + + this.experienceRates = { combat: 1.0, science: 0.8, crafting: 0.6 }; + this.activeBuffs = {}; + + // Loading state + this._loaded = false; + this._loading = false; + } + + // ------------------------------------------------------------------ // + // Initialisation — request skill definitions from the server + // ------------------------------------------------------------------ // + async initialize() { + if (this._loaded || this._loading) return; + this._loading = true; + + console.log('[SKILL SYSTEM] Requesting skill definitions from server'); + + if (!window.game?.socket) { + console.warn('[SKILL SYSTEM] No socket connection — skills will load when connected'); + this._loading = false; + return; + } + + try { + const serverSkills = await this._fetchSkillsFromServer(); + this._applyServerDefinitions(serverSkills); + this._loaded = true; + console.log('[SKILL SYSTEM] Skill definitions loaded from server'); + } catch (err) { + console.error('[SKILL SYSTEM] Failed to load skills from server:', err); + } finally { + this._loading = false; + } + } + + _fetchSkillsFromServer() { + return new Promise((resolve, reject) => { + const socket = window.game.socket; + + const timeout = setTimeout(() => { + socket.off('skills_data', handler); + reject(new Error('Skill data request timed out')); + }, 10000); + + const handler = (data) => { + clearTimeout(timeout); + socket.off('skills_data', handler); + if (data && (Array.isArray(data) || typeof data === 'object')) { + resolve(data); + } else { + reject(new Error('Invalid skill data from server')); + } + }; + + socket.on('skills_data', handler); + socket.emit('get_skills'); + }); + } + + /** + * Merge server skill definitions into the local skill map. + * Preserves any progress already loaded from playerData. + */ + _applyServerDefinitions(serverSkills) { + // Server may return an array or a categorised object + const asList = Array.isArray(serverSkills) + ? serverSkills + : Object.values(serverSkills).flat(); + + // Reset to empty categories first + this.skills = { combat: {}, science: {}, crafting: {} }; + + for (const skill of asList) { + const cat = skill.category || 'combat'; + if (!this.skills[cat]) this.skills[cat] = {}; + + // Keep any existing progress if already loaded from save data + const existing = this.skills[cat][skill.id] || {}; + this.skills[cat][skill.id] = { + name: skill.name, + description: skill.description, + maxLevel: skill.maxLevel || 100, + effects: skill.bonuses || skill.effects || {}, + icon: skill.icon || 'fa-star', + // Progress fields — kept from existing save data if present + currentLevel: existing.currentLevel ?? 0, + experience: existing.experience ?? 0, + experienceToNext: existing.experienceToNext ?? (skill.experiencePerLevel || 1000), + unlocked: existing.unlocked ?? (skill.defaultUnlocked !== false), + requiredLevel: skill.requiredLevel ?? null + }; + } + + console.log('[SKILL SYSTEM] Applied server definitions. Categories:', Object.keys(this.skills)); + } + + // ------------------------------------------------------------------ // + // Skill progression + // ------------------------------------------------------------------ // + addSkillExperience(category, skillId, amount) { + const skill = this.skills[category]?.[skillId]; + if (!skill || skill.currentLevel >= skill.maxLevel) return false; + + skill.experience += amount; + + while (skill.experience >= skill.experienceToNext && skill.currentLevel < skill.maxLevel) { + this.levelUpSkill(category, skillId); + } + + this.applySkillEffects(); + return true; + } + + levelUpSkill(category, skillId) { + const skill = this.skills[category][skillId]; + const excess = skill.experience - skill.experienceToNext; + + skill.currentLevel++; + skill.experienceToNext = Math.floor(skill.experienceToNext * 1.5); + skill.experience = Math.max(0, excess); + + this.applySkillEffects(); + this.game.showNotification(`${skill.name} leveled up to ${skill.currentLevel}!`, 'success', 4000); + } + + upgradeSkill(category, skillId) { + const skill = this.skills[category]?.[skillId]; + const player = this.game.systems.player; + + if (!skill) { this.game.showNotification('Skill not found', 'error', 3000); return false; } + if (!skill.unlocked) { this.game.showNotification('Skill is locked', 'error', 3000); return false; } + if (skill.currentLevel >= skill.maxLevel){ this.game.showNotification('Skill is at maximum level', 'warning', 3000); return false; } + if (player.stats.skillPoints < 1) { this.game.showNotification('Not enough skill points', 'error', 3000); return false; } + + player.stats.skillPoints--; + this.levelUpSkill(category, skillId); + + if (window.smartSaveManager?.isMultiplayer || this.game?.isRunning) { + this.updateUI(); + } + return true; + } + + unlockSkill(category, skillId) { + const skill = this.skills[category]?.[skillId]; + const player = this.game.systems.player; + + if (!skill) { this.game.showNotification('Skill not found', 'error', 3000); return false; } + if (skill.unlocked) { this.game.showNotification('Skill is already unlocked', 'warning', 3000); return false; } + if (skill.requiredLevel && player.stats.level < skill.requiredLevel) { + this.game.showNotification(`Requires level ${skill.requiredLevel}`, 'error', 3000); + return false; + } + if (player.stats.skillPoints < 2) { + this.game.showNotification('Requires 2 skill points to unlock', 'error', 3000); + return false; + } + + player.stats.skillPoints -= 2; + skill.unlocked = true; + skill.currentLevel = 1; + this.applySkillEffects(); + + if (window.smartSaveManager?.isMultiplayer || this.game?.isRunning) { + this.updateUI(); + } + this.game.showNotification(`${skill.name} unlocked!`, 'success', 4000); + return true; + } + + applySkillEffects() { + const player = this.game.systems.player; + this.resetToBaseStats(); + + for (const category of Object.values(this.skills)) { + for (const skill of Object.values(category)) { + if (!skill.unlocked || skill.currentLevel <= 0) continue; + + for (const [effect, value] of Object.entries(skill.effects || {})) { + const total = value * skill.currentLevel; + switch (effect) { + case 'attack': player.attributes.attack += total; break; + case 'defense': player.attributes.defense += total; break; + case 'speed': player.attributes.speed += total; break; + case 'health': + case 'maxHealth': player.attributes.maxHealth += total; break; + case 'maxEnergy': player.attributes.maxEnergy += total; break; + case 'criticalChance': player.attributes.criticalChance += total; break; + case 'criticalDamage': player.attributes.criticalDamage += total; break; + default: + if (!this.activeBuffs[effect]) this.activeBuffs[effect] = 0; + this.activeBuffs[effect] += total; + } + } + } + } + + player.updateUI(); + } + + resetToBaseStats() { + const player = this.game.systems.player; + const lvl = player.stats.level || 1; + Object.assign(player.attributes, { + attack: 10 + (lvl - 1) * 2, + defense: 5 + (lvl - 1) * 1, + speed: 10, + maxHealth: 100 + (lvl - 1) * 10, + maxEnergy: 100 + (lvl - 1) * 5, + criticalChance: 0.05, + criticalDamage: 1.5 + }); + this.activeBuffs = {}; + } + + // ------------------------------------------------------------------ // + // Combat / science / crafting XP helpers + // ------------------------------------------------------------------ // + awardCombatExperience(amount) { + this.addSkillExperience('combat', 'weapons_mastery', amount); + this.addSkillExperience('combat', 'tactical_analysis', amount * 0.5); + } + + awardScienceExperience(amount) { + this.addSkillExperience('science', 'energy_manipulation', amount); + this.addSkillExperience('science', 'alien_technology', amount * 0.3); + } + + awardCraftingExperience(amount) { + this.addSkillExperience('crafting', 'weapons_crafting', amount); + this.addSkillExperience('crafting', 'armor_forging', amount * 0.5); + } + + // ------------------------------------------------------------------ // + // Queries + // ------------------------------------------------------------------ // + getSkillLevel(category, skillId) { + // Support single-arg form used by CraftingSystem: getSkillLevel('crafting') + if (skillId === undefined) { + let max = 0; + for (const cat of Object.values(this.skills)) { + if (cat[category]) max = Math.max(max, cat[category].currentLevel || 0); + } + return max; + } + return this.skills[category]?.[skillId]?.currentLevel || 0; + } + + getSkillExperience(skillId) { + for (const cat of Object.values(this.skills)) { + if (cat[skillId]) return cat[skillId].experience || 0; + } + return 0; + } + + getExperienceNeeded(skillId) { + for (const cat of Object.values(this.skills)) { + if (cat[skillId]) return cat[skillId].experienceToNext || 0; + } + return 0; + } + + hasSkill(category, skillId, minimumLevel = 1) { + const skill = this.skills[category]?.[skillId]; + return skill && skill.unlocked && skill.currentLevel >= minimumLevel; + } + + getSkillBonus(effect) { + return this.activeBuffs[effect] || 0; + } + + // ------------------------------------------------------------------ // + // UI + // ------------------------------------------------------------------ // + updateUI() { + this.updateSkillsGrid(); + this.updateSkillPointsDisplay(); + } + + updateSkillsGrid() { + const grid = document.getElementById('skillsGrid'); + if (!grid) return; + + const activeCategory = document.querySelector('.skill-cat-btn.active')?.dataset.category || 'combat'; + const skills = this.skills[activeCategory] || {}; + + if (!this._loaded) { + grid.innerHTML = '

Loading skills from server...

'; + return; + } + + grid.innerHTML = ''; + + Object.entries(skills).forEach(([skillId, skill]) => { + const el = document.createElement('div'); + el.className = `skill-item ${!skill.unlocked ? 'locked' : ''}`; + + const progressPercent = skill.currentLevel > 0 + ? (skill.experience / skill.experienceToNext) * 100 + : 0; + + const iconClass = this.game.systems.textureManager + ? this.game.systems.textureManager.getIcon(skill.icon) + : (skill.icon || 'fa-question'); + + el.innerHTML = ` +
+
+
+
${skill.name}
+
Lv. ${skill.currentLevel}/${skill.maxLevel}
+
+
+
${skill.description}
+ ${skill.currentLevel > 0 && skill.currentLevel < skill.maxLevel ? ` +
+
+
+
+ ${skill.experience}/${skill.experienceToNext} XP +
+ ` : skill.currentLevel >= skill.maxLevel ? ` +
MAX LEVEL
+ ` : ''} +
+ ${!skill.unlocked ? ` + + ` : skill.currentLevel < skill.maxLevel ? ` + + ` : `MAX LEVEL`} +
+ ${skill.requiredLevel && !skill.unlocked ? ` +
Requires Level ${skill.requiredLevel}
+ ` : ''} + `; + + grid.appendChild(el); + }); + } + + updateSkillPointsDisplay() { + const player = this.game.systems.player; + document.querySelectorAll('.skill-points').forEach(el => { + el.textContent = `Skill Points: ${player.stats.skillPoints}`; + }); + } + + // ------------------------------------------------------------------ // + // Save / Load + // ------------------------------------------------------------------ // + save() { + return { skills: this.skills, activeBuffs: this.activeBuffs }; + } + + load(data) { + if (data.skills) { + for (const [category, skills] of Object.entries(data.skills)) { + if (!this.skills[category]) this.skills[category] = {}; + for (const [skillId, skillData] of Object.entries(skills)) { + if (this.skills[category][skillId]) { + Object.assign(this.skills[category][skillId], skillData); + } else { + // Store progress even before server definitions arrive + this.skills[category][skillId] = { ...skillData }; + } + } + } + } + if (data.activeBuffs) this.activeBuffs = data.activeBuffs; + this.applySkillEffects(); + } + + reset() { + this.activeBuffs = {}; + for (const category of Object.values(this.skills)) { + for (const skill of Object.values(category)) { + skill.currentLevel = 0; + skill.experience = 0; + } + } + } + + clear() { this.reset(); } +} diff --git a/Galaxy-Strike-Online-main/Client/js/systems/StarbaseWorld.js b/Galaxy-Strike-Online-main/Client/js/systems/StarbaseWorld.js new file mode 100644 index 0000000..539761b --- /dev/null +++ b/Galaxy-Strike-Online-main/Client/js/systems/StarbaseWorld.js @@ -0,0 +1,1001 @@ +/** + * Galaxy Strike Online — Starbase World v3 + * Isometric Habbo-style walkable environment. + * + * Features: + * - JSON-driven grid size, walls, doors, rooms + * - Per-segment wall colour overrides + * - Animated sliding doors (open when player is adjacent) + * - Locked rooms (filled with sealed tiles, show LOCKED badge) + * → unlock via room_unlock decoration items from shop/dungeons/quests + * - Wallpapers: global or per-room, purchasable/earnable + * → active wallpaper overrides style.floorColor* + style.wall* + style.door* + * - Wallpaper selector panel (bottom toolbar) + * - Click-to-walk BFS pathfinding + * - Pixel-art astronaut with walk animation + */ + +const TILE = { VOID: 0, FLOOR: 1, WALL: 2, DOOR: 3, LOCKED: 4 }; + +class StarbaseWorld { + constructor(canvasId, layoutJson, starbasePlayerData, decorationCatalog) { + this.canvas = document.getElementById(canvasId); + this.ctx = this.canvas.getContext('2d'); + + this.TW = 64; + this.TH = 32; + + this.layout = layoutJson || StarbaseWorld.defaultLayout(); + this.COLS = this.layout.grid.cols; + this.ROWS = this.layout.grid.rows; + + // Player's starbase save data { wallpaper, ownedWallpapers, unlockedRooms, roomWallpapers } + this.sbData = starbasePlayerData || { wallpaper: null, ownedWallpapers: [], unlockedRooms: [] }; + // Full decoration catalog from server + this.catalog = decorationCatalog || []; + + this.originX = 0; + this.originY = 0; + + const ps = this.layout.playerStart || { col: Math.floor(this.COLS/2), row: 2 }; + this.player = { + col: ps.col, row: ps.row, x: 0, y: 0, + path: [], moving: false, frame: 0, frameTimer: 0, + dir: 'se', name: 'Commander' + }; + + this.tileMap = this._buildMapFromLayout(); + + this.doorStates = {}; + this.doorDefs = {}; + for (const d of (this.layout.doors || [])) { + const k = `${d.col},${d.row}`; + this.doorStates[k] = { open: false, anim: 0 }; + this.doorDefs[k] = d; + } + + this.wallDefs = {}; + for (const w of (this.layout.walls || [])) { + for (let i = 0; i < (w.span || 1); i++) { + const c = w.dir === 'h' ? w.col + i : w.col; + const r = w.dir === 'v' ? w.row + i : w.row; + this.wallDefs[`${c},${r}`] = w; + } + } + + // Which room each floor tile belongs to (for per-room wallpaper) + this.tileRoom = this._buildRoomMap(); + + this.objects = this._buildObjects(); + + this.hoveredTile = null; + this._pendingInteract = null; + this._stars = null; + this.lastTime = 0; + this.running = false; + this.animId = null; + + // Wallpaper UI state + this._wpPanelOpen = false; + this._wpSelected = null; // wallpaperId being previewed + this._wpRoomTarget= null; // roomId to apply to (null = global) + + this._recenterCamera(); + this._bindInput(); + this._snapPlayerPos(); + this._buildWallpaperPanel(); + } + + // ─── Default layout ────────────────────────────────────────────────────── + static defaultLayout() { + return { + name: 'Starbase Alpha-7', grid: { cols: 20, rows: 16 }, + style: { + wallColor: '#00d4ff', wallColorLeft: '#0c1626', + wallColorRight: '#0a1220', wallColorTop: '#1a2840', + floorColorEven: '#151c2e', floorColorOdd: '#111827', + doorColor: '#00ffcc', doorFrameColor: '#00d4ff' + }, + walls: [ + { col:0, row:0, span:20, dir:'h' }, { col:0, row:15, span:20, dir:'h' }, + { col:0, row:0, span:16, dir:'v' }, { col:19, row:0, span:16, dir:'v' }, + ], + doors: [], rooms: [], playerStart: { col:10, row:2 } + }; + } + + // ─── Map builder ───────────────────────────────────────────────────────── + _buildMapFromLayout() { + const C = this.COLS, R = this.ROWS; + const map = Array.from({ length: R }, () => Array(C).fill(TILE.FLOOR)); + + for (const w of (this.layout.walls || [])) { + for (let i = 0; i < (w.span || 1); i++) { + const c = w.dir === 'h' ? w.col + i : w.col; + const r = w.dir === 'v' ? w.row + i : w.row; + if (r >= 0 && r < R && c >= 0 && c < C) map[r][c] = TILE.WALL; + } + } + for (const d of (this.layout.doors || [])) { + if (d.row >= 0 && d.row < R && d.col >= 0 && d.col < C) + map[d.row][d.col] = TILE.DOOR; + } + + // Seal locked rooms + for (const room of (this.layout.rooms || [])) { + if (!room.unlock) continue; + const owned = this.sbData.unlockedRooms || []; + if (owned.includes(room.id)) continue; // player has unlocked it + + const b = room.bounds; + for (let r = b.row; r < b.row + b.rows; r++) { + for (let c = b.col; c < b.col + b.cols; c++) { + if (r >= 0 && r < R && c >= 0 && c < C && map[r][c] === TILE.FLOOR) + map[r][c] = TILE.LOCKED; + } + } + } + return map; + } + + _buildRoomMap() { + const C = this.COLS, R = this.ROWS; + const rm = Array.from({ length: R }, () => Array(C).fill(null)); + for (const room of (this.layout.rooms || [])) { + const b = room.bounds; + for (let r = b.row; r < b.row + b.rows; r++) + for (let c = b.col; c < b.col + b.cols; c++) + if (r >= 0 && r < R && c >= 0 && c < C) rm[r][c] = room.id; + } + return rm; + } + + // ─── Style resolver ─────────────────────────────────────────────────────── + /** + * Returns the effective style for a given tile position. + * Priority: per-room wallpaper → global wallpaper → layout.style defaults + */ + _styleFor(col, row) { + const base = this.layout.style || {}; + const globalWpId = this.sbData.wallpaper; + const roomId = this.tileRoom[row]?.[col]; + const roomWpId = roomId && this.sbData.roomWallpapers?.[roomId]; + const activeWpId = this._wpSelected || roomWpId || globalWpId; + + if (!activeWpId) return base; + const wp = this.catalog.find(i => i.id === activeWpId && i.subtype === 'wallpaper'); + if (!wp?.preview) return base; + + // Merge: preview overrides base, but let per-wall-segment defs still win later + return { ...base, ...wp.preview }; + } + + // ─── Interactive objects ────────────────────────────────────────────────── + _buildObjects() { + const sc = c => Math.min(c, this.COLS - 2); + const sr = r => Math.min(r, this.ROWS - 2); + const mk = (id, col, row, icon, label, color, glow, action) => + ({ id, col: sc(col), row: sr(row), icon, label, color, glowColor: glow, action }); + const modal = (title, icon, desc, color, btn) => + () => this._showInteractModal(title, icon, desc, color, btn ? [btn] : []); + + return [ + mk('shop', 3, 2, '🛒','Trade Post', '#00d4ff','rgba(0,212,255,.6)', + modal('Trade Post','🛒','Access the galactic marketplace to buy ships, weapons, armour, wallpapers, and room unlocks.','#00d4ff',{label:'Open Shop',action:'shop'})), + mk('crafting', 21, 2, '⚗️','Forge', '#ff8800','rgba(255,136,0,.6)', + modal('Forge','⚗️','Smelt ores, combine alloys, build circuits and hull sections.','#ff8800',{label:'Open Crafting',action:'crafting'})), + mk('quests', 3, 16, '📋','Mission Board', '#ffdd00','rgba(255,221,0,.6)', + modal('Mission Board','📋','Daily missions, weekly challenges, and story campaign.','#ffdd00',{label:'View Quests',action:'quests'})), + mk('hangar', 21, 16, '🚀','Hangar', '#aa44ff','rgba(170,68,255,.6)', + modal('Hangar','🚀','Equip your active ship and prepare for dungeon runs.','#aa44ff',{label:'View Ships',action:'ships'})), + mk('skills', 12, 3, '🎓','Academy', '#00ff88','rgba(0,255,136,.6)', + modal('Academy','🎓','Improve combat, science, and crafting proficiencies.','#00ff88',{label:'Train Skills',action:'skills'})), + mk('terminal', 12, 10, '💻','Command Terminal', '#ff00ff','rgba(255,0,255,.6)', + modal('Command Terminal','💻','Monitor stats, server status, and your empire.','#ff00ff',{label:'Base Overview',action:'base'})), + mk('wallpaper',12, 17, '🎨','Wallpaper Desk', '#ffaa00','rgba(255,170,0,.6)', + () => this._toggleWallpaperPanel()), + ]; + } + + // ─── Coordinate helpers ─────────────────────────────────────────────────── + _isoProject(col, row) { + return { + x: this.originX + (col - row) * (this.TW / 2), + y: this.originY + (col + row) * (this.TH / 2) + }; + } + _screenToGrid(sx, sy) { + const tx = sx - this.originX, ty = sy - this.originY; + return { + col: Math.floor((tx / (this.TW/2) + ty / (this.TH/2)) / 2), + row: Math.floor((ty / (this.TH/2) - tx / (this.TW/2)) / 2) + }; + } + _recenterCamera() { + this.originX = this.canvas.width / 2; + this.originY = this.canvas.height * 0.13; + } + _snapPlayerPos() { + const iso = this._isoProject(this.player.col + 0.5, this.player.row + 0.5); + this.player.x = iso.x; + this.player.y = iso.y; + } + + // ─── Pathfinding ────────────────────────────────────────────────────────── + _walkable(col, row) { + if (col < 0 || row < 0 || col >= this.COLS || row >= this.ROWS) return false; + const t = this.tileMap[row][col]; + return t === TILE.FLOOR || t === TILE.DOOR; + } + _findPath(sc, sr, tc, tr) { + if (!this._walkable(tc, tr)) return []; + const key = (c, r) => `${c},${r}`; + const q = [{ c: sc, r: sr, path: [] }]; + const vis = new Set([key(sc, sr)]); + const dirs = [[1,0],[-1,0],[0,1],[0,-1]]; + while (q.length) { + const { c, r, path } = q.shift(); + if (c === tc && r === tr) return path; + for (const [dc, dr] of dirs) { + const nc = c+dc, nr = r+dr, k = key(nc, nr); + if (!vis.has(k) && this._walkable(nc, nr)) { + vis.add(k); + q.push({ c: nc, r: nr, path: [...path, { col: nc, row: nr }] }); + } + } + } + return []; + } + _findAdjacentWalkable(col, row) { + for (const [dc, dr] of [[0,-1],[0,1],[-1,0],[1,0]]) { + const nc = col+dc, nr = row+dr; + if (this._walkable(nc, nr)) return { col: nc, row: nr }; + } + return null; + } + + // ─── Input ──────────────────────────────────────────────────────────────── + _bindInput() { + this.canvas.addEventListener('click', e => this._onClick(e)); + this.canvas.addEventListener('mousemove', e => this._onMouseMove(e)); + this.canvas.addEventListener('mouseleave',() => { this.hoveredTile = null; }); + } + _canvasPos(e) { + const r = this.canvas.getBoundingClientRect(); + return { + x: (e.clientX - r.left) * (this.canvas.width / r.width), + y: (e.clientY - r.top) * (this.canvas.height / r.height) + }; + } + _onClick(e) { + const { x, y } = this._canvasPos(e); + const { col, row } = this._screenToGrid(x, y); + if (col < 0 || row < 0 || col >= this.COLS || row >= this.ROWS) return; + + // Clicking a locked tile → show unlock info + if (this.tileMap[row][col] === TILE.LOCKED) { + const roomId = this.tileRoom[row]?.[col]; + const room = this.layout.rooms?.find(rm => rm.id === roomId); + if (room) this._showLockedRoomModal(room); + return; + } + + const obj = this.objects.find(o => o.col === col && o.row === row); + if (obj) { + const adj = this._findAdjacentWalkable(obj.col, obj.row); + if (adj) { + this.player.path = this._findPath(this.player.col, this.player.row, adj.col, adj.row); + this.player.moving = this.player.path.length > 0; + this._pendingInteract = obj; + } else { obj.action(); } + return; + } + + this.player.path = this._findPath(this.player.col, this.player.row, col, row); + this.player.moving = this.player.path.length > 0; + this._pendingInteract = null; + } + _onMouseMove(e) { + const { x, y } = this._canvasPos(e); + const { col, row } = this._screenToGrid(x, y); + this.hoveredTile = (col>=0&&row>=0&&col { + if (!this.running) return; + const dt = Math.min((ts - this.lastTime) / 1000, 0.1); + this.lastTime = ts; + this._update(dt); + this._render(); + this.animId = requestAnimationFrame(loop); + }; + this.animId = requestAnimationFrame(loop); + } + stop() { + this.running = false; + if (this.animId) cancelAnimationFrame(this.animId); + } + resize(w, h) { + this.canvas.width = w; + this.canvas.height = h; + this._recenterCamera(); + this._snapPlayerPos(); + this._stars = null; + } + + // ─── Update ─────────────────────────────────────────────────────────────── + _update(dt) { + this._updatePlayer(dt); + this._updateDoors(dt); + } + _updatePlayer(dt) { + const p = this.player; + if (!p.moving || !p.path.length) return; + const SPEED = 4.5; + const next = p.path[0]; + const tPos = this._isoProject(next.col + 0.5, next.row + 0.5); + const dx = tPos.x - p.x, dy = tPos.y - p.y; + const dist = Math.sqrt(dx*dx + dy*dy); + const step = SPEED * this.TW * dt; + + const dC = next.col - p.col, dR = next.row - p.row; + if (dC>0&&dR===0) p.dir='se'; + else if (dC<0&&dR===0) p.dir='nw'; + else if (dR>0&&dC===0) p.dir='sw'; + else if (dR<0&&dC===0) p.dir='ne'; + + if (dist <= step) { + p.x = tPos.x; p.y = tPos.y; + p.col = next.col; p.row = next.row; + p.path.shift(); + if (!p.path.length) { + p.moving = false; + if (this._pendingInteract) { + const obj = this._pendingInteract; + this._pendingInteract = null; + setTimeout(() => obj.action(), 100); + } + } + } else { + p.x += (dx/dist)*step; + p.y += (dy/dist)*step; + } + p.frameTimer += dt; + if (p.frameTimer > 0.15) { p.frame = (p.frame+1)%4; p.frameTimer = 0; } + } + _updateDoors(dt) { + const p = this.player; + const SPEED = 3.5; + for (const key of Object.keys(this.doorStates)) { + const [dc, dr] = key.split(',').map(Number); + const state = this.doorStates[key]; + state.open = (Math.abs(p.col - dc) + Math.abs(p.row - dr)) <= 1; + const target = state.open ? 1 : 0; + if (state.anim < target) state.anim = Math.min(state.anim + SPEED*dt, 1); + else if (state.anim > target) state.anim = Math.max(state.anim - SPEED*dt, 0); + } + } + + // ─── Render ─────────────────────────────────────────────────────────────── + _render() { + const ctx = this.ctx; + ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + ctx.fillStyle = '#07091a'; + ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); + this._drawStarfield(); + + for (let r = 0; r < this.ROWS; r++) { + for (let c = 0; c < this.COLS; c++) { + const tile = this.tileMap[r][c]; + const iso = this._isoProject(c, r); + const hov = this.hoveredTile?.col===c && this.hoveredTile?.row===r; + const st = this._styleFor(c, r); + + if (tile === TILE.FLOOR) this._drawFloorTile(iso.x, iso.y, c, r, hov, st); + else if (tile === TILE.WALL) { + this._drawFloorTile(iso.x, iso.y, c, r, false, st); + this._drawWallTile(iso.x, iso.y, this.wallDefs[`${c},${r}`], st); + } + else if (tile === TILE.DOOR) { + this._drawFloorTile(iso.x, iso.y, c, r, hov, st); + this._drawDoorTile(iso.x, iso.y, c, r, this.doorDefs[`${c},${r}`], st); + } + else if (tile === TILE.LOCKED) this._drawLockedTile(iso.x, iso.y, c, r, hov); + } + } + + this._drawRoomLabels(); + + const sorted = [...this.objects].sort((a,b) => (a.col+a.row)-(b.col+b.row)); + for (const obj of sorted) { + const iso = this._isoProject(obj.col+0.5, obj.row+0.5); + this._drawObject(obj, iso.x, iso.y); + } + + this._drawPlayer(); + this._drawHUD(); + } + + // ─── Tile drawers ───────────────────────────────────────────────────────── + _drawStarfield() { + const ctx = this.ctx; + if (!this._stars) { + this._stars = []; + const rng = mulberry32(0xdeadbeef); + for (let i=0; i<120; i++) this._stars.push({ + x: rng()*this.canvas.width, y: rng()*(this.canvas.height*0.25), + r: rng()*1.5+0.3, a: rng()*0.6+0.2 + }); + } + for (const s of this._stars) { + ctx.beginPath(); ctx.arc(s.x, s.y, s.r, 0, Math.PI*2); + ctx.fillStyle = `rgba(180,210,255,${s.a})`; ctx.fill(); + } + } + + _drawFloorTile(x, y, col, row, hovered, st) { + const ctx = this.ctx, tw = this.TW, th = this.TH; + const even = (col+row)%2===0; + const base = even ? (st.floorColorEven||'#151c2e') : (st.floorColorOdd||'#111827'); + const fill = hovered ? this._lighten(base, 0.35) : base; + + ctx.beginPath(); + ctx.moveTo(x, y); ctx.lineTo(x+tw/2, y+th/2); + ctx.lineTo(x, y+th); ctx.lineTo(x-tw/2, y+th/2); + ctx.closePath(); + ctx.fillStyle = fill; ctx.fill(); + ctx.strokeStyle = hovered ? 'rgba(0,212,255,.5)' : 'rgba(0,212,255,.08)'; + ctx.lineWidth = hovered ? 1.5 : 0.8; ctx.stroke(); + } + + _drawWallTile(x, y, def, st) { + const ctx = this.ctx, tw = this.TW, th = this.TH, h = th*1.8; + const accent = (def?.color) || st.wallColor || '#00d4ff'; + const left = (def?.colorLeft) || st.wallColorLeft || '#0c1626'; + const right = (def?.colorRight) || st.wallColorRight || '#0a1220'; + const top = (def?.colorTop) || st.wallColorTop || '#1a2840'; + + ctx.beginPath(); + ctx.moveTo(x-tw/2, y+th/2 ); ctx.lineTo(x, y+th ); + ctx.lineTo(x, y+th+h ); ctx.lineTo(x-tw/2, y+th/2+h); + ctx.closePath(); ctx.fillStyle=left; ctx.fill(); + ctx.strokeStyle=this._alpha(accent,.15); ctx.lineWidth=0.8; ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(x, y+th ); ctx.lineTo(x+tw/2, y+th/2 ); + ctx.lineTo(x+tw/2, y+th/2+h); ctx.lineTo(x, y+th+h ); + ctx.closePath(); ctx.fillStyle=right; ctx.fill(); + ctx.strokeStyle=this._alpha(accent,.10); ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(x, y ); ctx.lineTo(x+tw/2, y+th/2); + ctx.lineTo(x, y+th ); ctx.lineTo(x-tw/2, y+th/2); + ctx.closePath(); ctx.fillStyle=top; ctx.fill(); + ctx.strokeStyle=this._alpha(accent,.35); ctx.lineWidth=1; ctx.stroke(); + } + + _drawDoorTile(x, y, col, row, def, st) { + const ctx = this.ctx, tw = this.TW, th = this.TH, h = th*1.8; + const key = `${col},${row}`; + const anim = (this.doorStates[key]||{anim:0}).anim; + const fc = (def?.frameColor) || st.doorFrameColor || '#00d4ff'; + const dc = (def?.color) || st.doorColor || '#00ffcc'; + const fw = tw*0.12; + const slide= anim*h*0.93; + + // Left pillar + ctx.beginPath(); + ctx.moveTo(x-tw/2, y+th/2 ); ctx.lineTo(x-tw/2+fw, y+th/2+fw*.5 ); + ctx.lineTo(x-tw/2+fw, y+th/2+h+fw*.5); ctx.lineTo(x-tw/2, y+th/2+h ); + ctx.closePath(); ctx.fillStyle=this._darken(fc,.25); ctx.fill(); + ctx.strokeStyle=this._alpha(fc,.6); ctx.lineWidth=0.8; ctx.stroke(); + + // Right pillar + ctx.beginPath(); + ctx.moveTo(x+tw/2, y+th/2 ); ctx.lineTo(x+tw/2-fw, y+th/2+fw*.5 ); + ctx.lineTo(x+tw/2-fw, y+th/2+h+fw*.5); ctx.lineTo(x+tw/2, y+th/2+h ); + ctx.closePath(); ctx.fillStyle=this._darken(fc,.3); ctx.fill(); + ctx.strokeStyle=this._alpha(fc,.5); ctx.stroke(); + + // Top lintel + ctx.beginPath(); + ctx.moveTo(x-tw/2+fw, y+th/2+fw*.5 ); ctx.lineTo(x+tw/2-fw, y+th/2+fw*.5 ); + ctx.lineTo(x+tw/2-fw, y+th/2+fw*1.5); ctx.lineTo(x-tw/2+fw, y+th/2+fw*1.5); + ctx.closePath(); ctx.fillStyle=this._darken(fc,.2); ctx.fill(); + ctx.strokeStyle=this._alpha(fc,.6); ctx.stroke(); + + // Sliding panel + const pTop = y+th/2+fw*1.5 - slide; + const pBottom = y+th/2+h; + const pw = tw - fw*2; + if (pBottom - pTop > 1) { + ctx.save(); + ctx.beginPath(); ctx.rect(x-tw/2, y+th/2+fw*1.5-h, tw, h*2); ctx.clip(); + ctx.beginPath(); + ctx.moveTo(x-pw/2, pTop ); ctx.lineTo(x+pw/2, pTop -th*.25); + ctx.lineTo(x+pw/2, pBottom-th*.25 ); ctx.lineTo(x-pw/2, pBottom ); + ctx.closePath(); + const pg = ctx.createLinearGradient(x, pTop, x, pBottom); + pg.addColorStop(0, this._alpha(dc,.9)); pg.addColorStop(1, this._alpha(dc,.35)); + ctx.fillStyle=pg; ctx.fill(); + ctx.strokeStyle=this._alpha(dc,.85); ctx.lineWidth=1; ctx.stroke(); + ctx.strokeStyle=this._alpha(dc,.18); ctx.lineWidth=0.5; + for (let ly=pTop+5; ly0.05) { + ctx.fillStyle=this._alpha(dc, anim*.7); + ctx.beginPath(); ctx.ellipse(x, pTop, pw/2, 4, 0, 0, Math.PI*2); ctx.fill(); + } + ctx.restore(); + } + + const pulse = anim<0.1 ? 0.25+Math.sin(performance.now()/400)*.12 : anim*.4; + const fg = ctx.createRadialGradient(x, y+th, 0, x, y+th, 18); + fg.addColorStop(0, this._alpha(dc, pulse)); fg.addColorStop(1,'transparent'); + ctx.fillStyle=fg; + ctx.beginPath(); ctx.ellipse(x, y+th, 18, 9, 0, 0, Math.PI*2); ctx.fill(); + } + + _drawLockedTile(x, y, col, row, hovered) { + const ctx = this.ctx, tw = this.TW, th = this.TH, h = th*1.8; + const now = performance.now()/1000; + const pulse = 0.3 + Math.sin(now*1.5)*0.1; + + // Sealed wall face — dark hatched + const fill = hovered ? '#1a1a2a' : '#0e0e1a'; + ctx.beginPath(); + ctx.moveTo(x, y); ctx.lineTo(x+tw/2, y+th/2); + ctx.lineTo(x, y+th); ctx.lineTo(x-tw/2, y+th/2); + ctx.closePath(); ctx.fillStyle=fill; ctx.fill(); + + // Hatch pattern + ctx.save(); + ctx.beginPath(); + ctx.moveTo(x, y); ctx.lineTo(x+tw/2, y+th/2); + ctx.lineTo(x, y+th); ctx.lineTo(x-tw/2, y+th/2); + ctx.closePath(); ctx.clip(); + ctx.strokeStyle=`rgba(80,80,120,${0.15+pulse*0.1})`; ctx.lineWidth=1; + for (let i=-tw; i i.id === wpId); + if (wp) { + ctx.font='700 9px "Orbitron",monospace'; + ctx.fillStyle=this._alpha(accent,.5); + ctx.textAlign='right'; + ctx.fillText(`🎨 ${wp.name}`, W-14, 36); + } + } + } + + // ─── Wallpaper panel ────────────────────────────────────────────────────── + _buildWallpaperPanel() { + document.getElementById('sb-wp-panel')?.remove(); + const wallpapers = this.catalog.filter(i => i.subtype === 'wallpaper'); + const owned = this.sbData.ownedWallpapers || []; + const rooms = (this.layout.rooms || []).filter(r => + !r.unlock || (this.sbData.unlockedRooms||[]).includes(r.id)); + + const panel = document.createElement('div'); + panel.id = 'sb-wp-panel'; + panel.style.cssText = ` + display:none; position:absolute; bottom:36px; left:0; right:0; + background:linear-gradient(180deg,transparent,rgba(7,9,24,.96)); + border-top:1px solid rgba(0,212,255,.25); + padding:12px 16px 10px; z-index:500; font-family:'Orbitron',monospace; + pointer-events:all; + `; + + // Room selector row + const roomTabs = rooms.map(r => + `` + ).join(''); + + // Wallpaper swatches + const swatches = wallpapers.map(wp => { + const isOwned = owned.includes(wp.id); + const p = wp.preview || {}; + const bg = p.wallColorTop || p.floorColorEven || '#1a2840'; + const fg = p.doorColor || p.wallColor || '#00ffcc'; + return ` +
+
+ ${isOwned ? '' : '
🔒
'} +
+
+ ${wp.name} +
`; + }).join(''); + + panel.innerHTML = ` +
+ WALLPAPER + Apply to: + + ${roomTabs} + + +
+
${swatches}
+
+ Hover a swatch to preview + +
+ `; + + const container = this.canvas.parentElement; + container.style.position = 'relative'; + container.appendChild(panel); + this._wpPanel = panel; + } + + _toggleWallpaperPanel() { + if (!this._wpPanel) this._buildWallpaperPanel(); + this._wpPanelOpen = !this._wpPanelOpen; + this._wpPanel.style.display = this._wpPanelOpen ? 'block' : 'none'; + if (this._wpPanelOpen) { + this._wpSelected = null; + this._wpRoomTarget = null; + } + } + + _wpSetRoom(roomId) { + this._wpRoomTarget = roomId; + // Highlight active tab + this._wpPanel.querySelectorAll('button[data-room]').forEach(b => { + b.style.background = b.dataset.room === roomId + ? 'rgba(0,212,255,.35)' : 'rgba(0,212,255,.12)'; + }); + } + + _wpPreview(wpId) { + const owned = this.sbData.ownedWallpapers || []; + if (!owned.includes(wpId)) return; + this._wpSelected = wpId; + const wp = this.catalog.find(i => i.id === wpId); + const nameEl = document.getElementById('sb-wp-preview-name'); + const applyBtn = document.getElementById('sb-wp-apply-btn'); + if (nameEl && wp) nameEl.textContent = wp.name + ' — ' + (wp.description || ''); + if (applyBtn) applyBtn.style.display = 'inline-block'; + } + + _wpApply(wpId) { + this._wpSelected = null; + const socket = window.game?.socket || window.gameInitializer?.socket; + if (!socket) { + // Offline: apply locally only + if (this._wpRoomTarget) { + if (!this.sbData.roomWallpapers) this.sbData.roomWallpapers = {}; + if (wpId) this.sbData.roomWallpapers[this._wpRoomTarget] = wpId; + else delete this.sbData.roomWallpapers[this._wpRoomTarget]; + } else { + this.sbData.wallpaper = wpId; + } + return; + } + socket.emit('set_wallpaper', { wallpaperId: wpId, roomId: this._wpRoomTarget || null }); + // Optimistic update + if (this._wpRoomTarget) { + if (!this.sbData.roomWallpapers) this.sbData.roomWallpapers = {}; + if (wpId) this.sbData.roomWallpapers[this._wpRoomTarget] = wpId; + else delete this.sbData.roomWallpapers[this._wpRoomTarget]; + } else { + this.sbData.wallpaper = wpId; + } + const applyBtn = document.getElementById('sb-wp-apply-btn'); + if (applyBtn) applyBtn.style.display = 'none'; + } + + // ─── Interaction modals ─────────────────────────────────────────────────── + _showInteractModal(title, icon, desc, color, buttons) { + document.getElementById('sb-interact-modal')?.remove(); + const modal = document.createElement('div'); + modal.id = 'sb-interact-modal'; + modal.style.cssText = ` + position:absolute;top:50%;left:50%;transform:translate(-50%,-50%); + background:linear-gradient(135deg,#0d1525,#111c30);border:2px solid ${color}; + border-radius:12px;padding:24px 28px;min-width:280px;max-width:360px;z-index:1000; + box-shadow:0 0 40px ${color}55,0 8px 32px rgba(0,0,0,.8); + font-family:'Orbitron',monospace;color:#fff;animation:sbModalIn .2s ease;pointer-events:all; + `; + const btnHtml = buttons.map(b=>` + + `).join(''); + modal.innerHTML=` +
+ ${icon} +
+
${title}
+
STARBASE STATION
+
+ +
+
${desc}
+
${btnHtml}
+ `; + this.canvas.parentElement.appendChild(modal); + setTimeout(() => modal.remove(), 8000); + } + + _showLockedRoomModal(room) { + const unlock = this.catalog.find(i => i.id === room.unlock); + const name = unlock?.name || room.unlock; + const price = unlock ? `${unlock.price} ${unlock.currency}` : '?'; + const rarity = unlock?.rarity || 'unknown'; + const color = '#ff8844'; + this._showInteractModal( + `${room.label} — Locked`, '🔒', + `This wing requires the ${name} (${rarity}) to unlock.

` + + (unlock?.categories?.includes('shop') ? `Purchase for ${price} in the Trade Post, or earn it as a dungeon drop.` : + `Earn this by clearing dungeons or completing quests.`), + color, + unlock?.categories?.includes('shop') ? [{ label: 'Visit Trade Post', action: 'shop' }] : [] + ); + } + + _handleModalAction(action) { + const map={shop:'[data-tab="shop"]',crafting:'[data-tab="crafting"]', + quests:'[data-tab="quests"]',skills:'[data-tab="skills"]',base:'[data-tab="base"]'}; + if (map[action]) { const b=document.querySelector(map[action]); if(b){b.click();return;} } + for (const b of document.querySelectorAll('[onclick]')) { + if ((b.getAttribute('onclick')||'').toLowerCase().includes(action)) { b.click(); return; } + } + } + + // ─── Color utilities ────────────────────────────────────────────────────── + _hexToRgb(hex) { + hex=hex.replace('#',''); + if (hex.length===3) hex=hex.split('').map(c=>c+c).join(''); + return { r:parseInt(hex.slice(0,2),16), g:parseInt(hex.slice(2,4),16), b:parseInt(hex.slice(4,6),16) }; + } + _darken(hex, f) { + const {r,g,b}=this._hexToRgb(hex); + return `rgb(${Math.round(r*f)},${Math.round(g*f)},${Math.round(b*f)})`; + } + _lighten(hex, f) { + const {r,g,b}=this._hexToRgb(hex); + return `rgb(${Math.min(255,Math.round(r+(255-r)*f))},${Math.min(255,Math.round(g+(255-g)*f))},${Math.min(255,Math.round(b+(255-b)*f))})`; + } + _alpha(hex, a) { + if (hex.startsWith('rgba')||hex.startsWith('rgb(')) return hex.replace(/[\d.]+\)$/,`${a})`); + const {r,g,b}=this._hexToRgb(hex); + return `rgba(${r},${g},${b},${a})`; + } +} + +// ─── JSON loader ────────────────────────────────────────────────────────────── +async function loadStarbaseWorld(canvasId) { + let layout; + try { + const resp = await fetch(`data/starbase-layout.json?v=${Date.now()}`); + if (!resp.ok) throw new Error(resp.status); + layout = await resp.json(); + } catch(e) { + console.warn('[STARBASE] Using default layout:', e.message); + layout = StarbaseWorld.defaultLayout(); + } + + // Fetch player starbase data from server + let sbData = { wallpaper: null, ownedWallpapers: [], unlockedRooms: [] }; + let catalog = []; + + const socket = window.game?.socket || window.gameInitializer?.socket; + if (socket) { + await new Promise(resolve => { + const timer = setTimeout(resolve, 3000); + socket.once('starbase_data', data => { + clearTimeout(timer); + if (data.success) { sbData = data.starbase; catalog = data.catalog || []; } + resolve(); + }); + socket.emit('get_starbase_data'); + }); + } + + if (window.starbaseWorld) { window.starbaseWorld.stop(); window.starbaseWorld = null; } + window.starbaseWorld = new StarbaseWorld(canvasId, layout, sbData, catalog); + + // Listen for server wallpaper confirmations + if (socket) { + socket.off('wallpaper_set'); + socket.on('wallpaper_set', data => { + if (data.success && window.starbaseWorld) { + window.starbaseWorld.sbData = data.starbase; + } + }); + } + + return window.starbaseWorld; +} + +// ─── Helpers ────────────────────────────────────────────────────────────────── +function mulberry32(a) { + return function() { + a|=0; a=a+0x6D2B79F5|0; + let t=Math.imul(a^a>>>15,1|a); + t=t+Math.imul(t^t>>>7,61|t)^t; + return ((t^t>>>14)>>>0)/4294967296; + }; +} + +(function(){ + if (document.getElementById('sb-anim-style')) return; + const s=document.createElement('style'); s.id='sb-anim-style'; + s.textContent=` + @keyframes sbModalIn { + from{opacity:0;transform:translate(-50%,-46%)} + to {opacity:1;transform:translate(-50%,-50%)} + } + `; + document.head.appendChild(s); +})(); diff --git a/Client-Server/js/ui/LiveMainMenu.js b/Galaxy-Strike-Online-main/Client/js/ui/LiveMainMenu.js similarity index 99% rename from Client-Server/js/ui/LiveMainMenu.js rename to Galaxy-Strike-Online-main/Client/js/ui/LiveMainMenu.js index b18d573..5994b0f 100644 --- a/Client-Server/js/ui/LiveMainMenu.js +++ b/Galaxy-Strike-Online-main/Client/js/ui/LiveMainMenu.js @@ -24,7 +24,7 @@ class LiveMainMenu { this.currentUser = null; this.servers = []; // Renamed from serverList to avoid conflict with DOM element this.apiBaseUrl = 'https://api.korvarix.com/api'; // API Server URL - this.gameServerUrl = 'https://dev.gameserver.galaxystrike.online'; // Game Server URL for Socket.IO (local dev server) + this.gameServerUrl = 'https://dev.gameserver.galaxystrike.online'; // Game Server URL for Socket.IO this.localGameServerUrl = 'http://localhost:3002'; // Local Game Server URL this.isLocalMode = false; // Track if we're in local mode diff --git a/Client-Server/js/ui/MainMenu.js b/Galaxy-Strike-Online-main/Client/js/ui/MainMenu.js similarity index 100% rename from Client-Server/js/ui/MainMenu.js rename to Galaxy-Strike-Online-main/Client/js/ui/MainMenu.js diff --git a/Client-Server/js/ui/UIManager.js b/Galaxy-Strike-Online-main/Client/js/ui/UIManager.js similarity index 85% rename from Client-Server/js/ui/UIManager.js rename to Galaxy-Strike-Online-main/Client/js/ui/UIManager.js index b1bd8b7..1ca6847 100644 --- a/Client-Server/js/ui/UIManager.js +++ b/Galaxy-Strike-Online-main/Client/js/ui/UIManager.js @@ -43,16 +43,19 @@ class UIManager { const gameInterface = document.getElementById('gameInterface'); const navButtons = document.querySelectorAll('.nav-btn'); - if (debugLogger) debugLogger.logStep('DOM Check', { + console.log('[UI MANAGER] DOM Check:', { gameInterfaceExists: !!gameInterface, gameInterfaceHidden: gameInterface?.classList.contains('hidden'), navButtonsFound: navButtons.length, documentReady: document.readyState }); - if (gameInterface && !gameInterface.classList.contains('hidden') && navButtons.length > 0) { + // Less strict condition - proceed if we have nav buttons, even if interface is still hidden + if (navButtons.length > 0) { + console.log('[UI MANAGER] Navigation buttons found, proceeding with initialization'); this.proceedWithInitialization(); } else { + console.log('[UI MANAGER] Waiting for navigation buttons...'); setTimeout(waitForDOM, 100); } }; @@ -117,10 +120,6 @@ class UIManager { }); if (debugLogger) debugLogger.endStep('UIManager.setupNavigation', { - navButtonsSetup: navButtons.length, - skillCatButtonsSetup: skillCatButtons.length, - shopCatButtonsSetup: shopCatButtons.length, - questTabButtonsSetup: questTabButtons.length, craftingCatButtonsSetup: craftingCatButtons.length }); } @@ -140,10 +139,11 @@ class UIManager { if (navButtons.length === 0) { console.warn('[UIManager] No navigation buttons found!'); if (debugLogger) debugLogger.logStep('No navigation buttons found'); + return; } navButtons.forEach((btn, index) => { - console.log(`[UIManager] Setting up button ${index}:`, btn.dataset.tab); + console.log(`[UIManager] Setting up button ${index}:`, btn.dataset.tab, btn); // Check if button already has a listener to prevent duplicates if (btn.getAttribute('data-has-listener') === 'true') { console.log(`[UIManager] Button ${btn.dataset.tab} already has listener, skipping`); @@ -151,16 +151,30 @@ class UIManager { } btn.addEventListener('click', (e) => { - console.log(`[UIManager] Navigation button clicked: ${btn.dataset.tab}`); + console.log(`[UIManager] Navigation button clicked: ${btn.dataset.tab}`, e); const tab = btn.dataset.tab; if (tab) { this.switchTab(tab); + } else { + console.warn('[UIManager] Button clicked but no tab data found'); } }); - // Mark button as having a listener + // Mark as having listener btn.setAttribute('data-has-listener', 'true'); - console.log(`[UIManager] Button ${btn.dataset.tab} setup complete`); + console.log(`[UIManager] Event listener added to button: ${btn.dataset.tab}`); + }); + + // Add a global fallback click handler for navigation buttons + document.addEventListener('click', (e) => { + if (e.target.classList.contains('nav-btn') || e.target.closest('.nav-btn')) { + const button = e.target.classList.contains('nav-btn') ? e.target : e.target.closest('.nav-btn'); + const tab = button.dataset.tab; + if (tab && window.game && window.game.systems && window.game.systems.ui) { + console.log('[UI MANAGER] Global fallback: Navigation button clicked:', tab); + window.game.systems.ui.switchTab(tab); + } + } }); // Set up UI update event listener from GameEngine @@ -765,7 +779,7 @@ class UIManager { // Stop economy system timers if (this.game.systems.economy) { - this.game.systems.economy.stopShopTimers(); + this.game.systems.economy.stopShopRefreshTimer(); } if (isMultiplayer) { @@ -1037,7 +1051,7 @@ class UIManager { const oldTab = this.currentTab; // Update navigation buttons - const navButtons = document.querySelectorAll('.nav-btn'); + const navButtons = document.querySelectorAll('.nav-btn, .bottom-nav-btn, .nav-drawer-btn'); let navButtonsUpdated = 0; navButtons.forEach(btn => { @@ -1292,8 +1306,14 @@ class UIManager { }); try { + // Call economy system update this.game.systems.economy.updateUI(); + // Also call global shop display function for additional debugging + if (typeof updateShopDisplay === 'function') { + updateShopDisplay(); + } + if (debugLogger) debugLogger.endStep('UIManager.switchShopCategory', { success: true, category: category, @@ -1313,13 +1333,7 @@ class UIManager { } } - switchQuestType(type) { - const debugLogger = window.debugLogger; - - if (debugLogger) debugLogger.startStep('UIManager.switchQuestType', { - type: type - }); - + updateQuestTabs(type) { const questTabButtons = document.querySelectorAll('.quest-tab-btn'); let buttonsUpdated = 0; @@ -1339,7 +1353,13 @@ class UIManager { }); try { - this.game.systems.questSystem.updateUI(); + // REMOVED: Client-side QuestSystem is now server-driven + // this.game.systems.questSystem.updateUI(); + + // Call the global quest display update function instead + if (typeof updateQuestDisplay === 'function') { + updateQuestDisplay(); + } if (debugLogger) debugLogger.endStep('UIManager.switchQuestType', { success: true, @@ -1611,20 +1631,20 @@ class UIManager { // In multiplayer mode, ensure we're using server data if (window.smartSaveManager?.isMultiplayer) { - console.log('[UI MANAGER] Updating resource display in multiplayer mode - using server data'); + // console.log('[UI MANAGER] Updating resource display in multiplayer mode - using server data'); } - const debugLogger = window.debugLogger; + // const debugLogger = window.debugLogger; try { // Safety checks - return early if systems aren't available if (!this.game || !this.game.systems) { - if (debugLogger) debugLogger.log('Game systems not available, skipping resource display update'); + // if (debugLogger) debugLogger.log('Game systems not available, skipping resource display update'); return; } if (!this.game.systems.player || !this.game.systems.economy) { - if (debugLogger) debugLogger.log('Player or economy system not available, skipping resource display update'); + // if (debugLogger) debugLogger.log('Player or economy system not available, skipping resource display update'); return; } @@ -1633,18 +1653,18 @@ class UIManager { // Additional safety checks for player properties if (!player.stats || !player.attributes || !player.ship) { - if (debugLogger) debugLogger.log('Player properties not fully initialized, skipping resource display update'); + // if (debugLogger) debugLogger.log('Player properties not fully initialized, skipping resource display update'); return; } - if (debugLogger) debugLogger.startStep('UIManager.updateResourceDisplay', { - gameSystemsAvailable: !!(this.game && this.game.systems), - playerSystemAvailable: !!(this.game && this.game.systems && this.game.systems.player), - economySystemAvailable: !!(this.game && this.game.systems && this.game.systems.economy), - playerStatsAvailable: !!player.stats, - playerAttributesAvailable: !!player.attributes, - playerShipAvailable: !!player.ship - }); + // if (debugLogger) debugLogger.startStep('UIManager.updateResourceDisplay', { + // gameSystemsAvailable: !!(this.game && this.game.systems), + // playerSystemAvailable: !!(this.game && this.game.systems && this.game.systems.player), + // economySystemAvailable: !!(this.game && this.game.systems && this.game.systems.economy), + // playerStatsAvailable: !!player.stats, + // playerAttributesAvailable: !!player.attributes, + // playerShipAvailable: !!player.ship + // }); let elementsUpdated = 0; let elementsNotFound = 0; @@ -1654,7 +1674,7 @@ class UIManager { if (playerLevelElement) { const oldLevel = playerLevelElement.textContent; const playerLevel = player.stats.level || 1; - console.log('[UI MANAGER] Updating player level:', { oldLevel, newLevel: playerLevel, playerStats: player.stats }); + // console.log('[UI MANAGER] Updating player level:', { oldLevel, newLevel: playerLevel, playerStats: player.stats }); playerLevelElement.textContent = `Lv. ${playerLevel}`; elementsUpdated++; @@ -1665,7 +1685,7 @@ class UIManager { }); } else { elementsNotFound++; - if (debugLogger) debugLogger.log('Player level element not found'); + // if (debugLogger) debugLogger.log('Player level element not found'); } // Update ship level with safety checks @@ -1692,7 +1712,9 @@ class UIManager { if (creditsElement) { const oldCredits = creditsElement.textContent; const creditsAmount = economy.credits || 0; - console.log('[UI MANAGER] Updating credits:', { oldCredits, newCredits: creditsAmount, economyCredits: economy.credits, economySystem: !!economy }); + if (oldCredits !== this.game.formatNumber(creditsAmount)) { + console.log('[UI MANAGER] Credits updated:', this.game.formatNumber(creditsAmount)); + } creditsElement.textContent = this.game.formatNumber(creditsAmount); elementsUpdated++; @@ -1712,6 +1734,9 @@ class UIManager { if (gemsElement) { const oldGems = gemsElement.textContent; const gemsAmount = economy.gems || 0; + if (oldGems !== this.game.formatNumber(gemsAmount)) { + console.log('[UI MANAGER] Gems updated:', this.game.formatNumber(gemsAmount)); + } gemsElement.textContent = this.game.formatNumber(gemsAmount); elementsUpdated++; @@ -1730,10 +1755,13 @@ class UIManager { const energyElement = document.getElementById('energy'); if (energyElement) { const oldEnergy = energyElement.textContent; - // Ensure we're using the correct energy values, not credits - const currentEnergy = Math.floor(player.attributes.energy || 0); - const maxEnergy = Math.floor(player.attributes.maxEnergy || 100); - energyElement.textContent = `${currentEnergy}/${maxEnergy}`; + const currentEnergy = player.attributes.currentEnergy || player.attributes.energy || 0; + const maxEnergy = player.attributes.maxEnergy || 100; + const newEnergy = `${currentEnergy}/${maxEnergy}`; + if (oldEnergy !== newEnergy) { + console.log('[UI MANAGER] Energy updated:', newEnergy); + } + energyElement.textContent = newEnergy; elementsUpdated++; if (debugLogger) debugLogger.logStep('Energy updated', { @@ -1749,6 +1777,65 @@ class UIManager { if (debugLogger) debugLogger.log('Energy element not found'); } + // Update play time (keep this here as it's part of the core resource display) + const playTimeElement = document.getElementById('playTime'); + if (playTimeElement) { + const playTimeMs = player.stats.playTime || 0; + playTimeElement.textContent = this.formatPlayTime(playTimeMs); + elementsUpdated++; + } + + // Update player stats panel + const playerLevelDisplayElement = document.getElementById('playerLevelDisplay'); + if (playerLevelDisplayElement) { + playerLevelDisplayElement.textContent = player.stats.level || 1; + elementsUpdated++; + } + + const playerXPElement = document.getElementById('playerXP'); + if (playerXPElement) { + const currentXP = player.stats.experience || 0; + const xpToNext = player.stats.experienceToNext || 100; + playerXPElement.textContent = `${currentXP} / ${xpToNext}`; + elementsUpdated++; + } + + const skillPointsElement = document.getElementById('skillPoints'); + if (skillPointsElement) { + skillPointsElement.textContent = player.stats.skillPoints || 0; + elementsUpdated++; + } + + const totalXPElement = document.getElementById('totalXP'); + if (totalXPElement) { + totalXPElement.textContent = this.game.formatNumber(player.stats.totalXP || 0); + elementsUpdated++; + } + + const questsCompletedElement = document.getElementById('questsCompleted'); + if (questsCompletedElement) { + // Use player stats from server first, then quest system stats as fallback + const questsCompleted = player.stats.questsCompleted || + (this.game.systems.questSystem?.stats?.questsCompleted) || 0; + questsCompletedElement.textContent = questsCompleted; + elementsUpdated++; + } + + const lastLoginElement = document.getElementById('lastLogin'); + if (lastLoginElement) { + const lastLogin = player.stats.lastLogin; + if (lastLogin) { + const loginDate = new Date(lastLogin); + lastLoginElement.textContent = loginDate.toLocaleDateString() + ' ' + loginDate.toLocaleTimeString(); + } else { + lastLoginElement.textContent = 'Never'; + } + elementsUpdated++; + } + + // Update all player stats + this.updatePlayerStats(); + if (debugLogger) debugLogger.endStep('UIManager.updateResourceDisplay', { elementsUpdated: elementsUpdated, elementsNotFound: elementsNotFound, @@ -1767,33 +1854,132 @@ class UIManager { } } - updateUI() { - const debugLogger = window.debugLogger; + formatPlayTime(milliseconds) { + // Format play time showing only the two most relevant units + const totalSeconds = Math.floor(milliseconds / 1000); - if (debugLogger) debugLogger.startStep('UIManager.updateUI', { - currentTab: this.currentTab, - modalOpen: this.modalOpen - }); + if (totalSeconds < 60) { + // Less than 1 minute: show seconds only + return `${totalSeconds}s`; + } else if (totalSeconds < 3600) { + // Less than 1 hour: show minutes and seconds + const minutes = Math.floor(totalSeconds / 60); + const seconds = totalSeconds % 60; + return `${minutes}m ${seconds}s`; + } else if (totalSeconds < 86400) { + // Less than 1 day: show hours and minutes + const hours = Math.floor(totalSeconds / 3600); + const minutes = Math.floor((totalSeconds % 3600) / 60); + return `${hours}h ${minutes}m`; + } else { + // 1 day or more: show days and hours + const days = Math.floor(totalSeconds / 86400); + const hours = Math.floor((totalSeconds % 86400) / 3600); + return `${days}d ${hours}h`; + } + } + + updatePlayerStats() { + // Update just the player stats panel (excluding those handled in updateResourceDisplay) + if (!this.game || !this.game.systems || !this.game.systems.player) { + return; + } + + const player = this.game.systems.player; + let elementsUpdated = 0; + + // Update player stats panel + const playerLevelDisplayElement = document.getElementById('playerLevelDisplay'); + if (playerLevelDisplayElement) { + playerLevelDisplayElement.textContent = player.stats.level || 1; + elementsUpdated++; + } + + const playerXPElement = document.getElementById('playerXP'); + if (playerXPElement) { + const currentXP = player.stats.experience || 0; + const xpToNext = player.stats.experienceToNext || 100; + playerXPElement.textContent = `${currentXP} / ${xpToNext}`; + elementsUpdated++; + } + + const skillPointsElement = document.getElementById('skillPoints'); + if (skillPointsElement) { + skillPointsElement.textContent = player.stats.skillPoints || 0; + elementsUpdated++; + } + + const totalXPElement = document.getElementById('totalXP'); + if (totalXPElement) { + totalXPElement.textContent = this.game.formatNumber(player.stats.totalXP || 0); + elementsUpdated++; + } + + const questsCompletedElement = document.getElementById('questsCompleted'); + if (questsCompletedElement) { + // Use player stats from server first, then quest system stats as fallback + const questsCompleted = player.stats.questsCompleted || + (this.game.systems.questSystem?.stats?.questsCompleted) || 0; + questsCompletedElement.textContent = questsCompleted; + elementsUpdated++; + } + + const lastLoginElement = document.getElementById('lastLogin'); + if (lastLoginElement) { + const lastLogin = player.stats.lastLogin; + if (lastLogin) { + const loginDate = new Date(lastLogin); + lastLoginElement.textContent = loginDate.toLocaleDateString() + ' ' + loginDate.toLocaleTimeString(); + } else { + lastLoginElement.textContent = 'Never'; + } + elementsUpdated++; + } + + // Update stats moved from Idle Progress card + const totalKillsElement = document.getElementById('totalKills'); + if (totalKillsElement) { + totalKillsElement.textContent = this.game.formatNumber(player.stats.totalKills || 0); + elementsUpdated++; + } + + const dungeonsClearedElement = document.getElementById('dungeonsCleared'); + if (dungeonsClearedElement) { + dungeonsClearedElement.textContent = player.stats.dungeonsCleared || 0; + elementsUpdated++; + } + + // DISABLED: Reduce console spam for quest debugging + // console.log(`[UI MANAGER] Player stats updated: ${elementsUpdated} elements`); + } + + updateUI() { + // const debugLogger = window.debugLogger; + + // if (debugLogger) debugLogger.startStep('UIManager.updateUI', { + // currentTab: this.currentTab, + // modalOpen: this.modalOpen + // }); // Update resource display only if in multiplayer mode or game is actively running if (this.shouldUpdateUI()) { - if (debugLogger) debugLogger.logStep('Updating resource display'); + // if (debugLogger) debugLogger.logStep('Updating resource display'); this.updateResourceDisplay(); } // Update tab content only if in multiplayer mode or game is actively running if (this.shouldUpdateUI()) { - if (debugLogger) debugLogger.logStep('Updating tab content', { - currentTab: this.currentTab - }); + // if (debugLogger) debugLogger.logStep('Updating tab content', { + // currentTab: this.currentTab + // }); this.updateTabContent(this.currentTab); } - if (debugLogger) debugLogger.endStep('UIManager.updateUI', { - resourceDisplayUpdated: true, - tabContentUpdated: true, - currentTab: this.currentTab - }); + // if (debugLogger) debugLogger.endStep('UIManager.updateUI', { + // resourceDisplayUpdated: true, + // tabContentUpdated: true, + // currentTab: this.currentTab + // }); } // Menus @@ -1945,8 +2131,8 @@ class UIManager { } else { } - // Reset economy - if (this.game.systems.economy) { + // Reset economy - only reset if not in multiplayer mode + if (this.game.systems.economy && !window.smartSaveManager?.isMultiplayer) { this.game.systems.economy.credits = 1000; this.game.systems.economy.gems = 10; } @@ -2065,8 +2251,8 @@ class UIManager { }; } - // Clear economy data - if (this.game.systems.economy) { + // Clear economy data - only reset if not in multiplayer mode + if (this.game.systems.economy && !window.smartSaveManager?.isMultiplayer) { this.game.systems.economy.credits = 1000; this.game.systems.economy.gems = 10; } @@ -2451,6 +2637,94 @@ class UIManager { `; } + + // Update dungeon UI when player enters/exits dungeons + updateDungeonUI(dungeonData) { + console.log('[UI MANAGER] Updating dungeon UI:', dungeonData); + console.log('[UI MANAGER] Dungeon state check:', { + isExploring: dungeonData.isExploring, + hasCurrentDungeon: !!dungeonData.currentDungeon, + hasCurrentRoom: !!dungeonData.currentRoom, + progress: dungeonData.progress, + dungeonViewElement: !!document.getElementById('dungeonView') + }); + + // Check if dungeon is completed (progress = -1) + if (dungeonData.progress === -1 || !dungeonData.isExploring) { + // Player is not in dungeon or dungeon is completed - show normal dungeon list + console.log('[UI MANAGER] Player not exploring or dungeon completed, showing dungeon list'); + this.switchTab('dungeons'); + return; + } + + // Player is in dungeon - show exploration interface + const dungeonView = document.getElementById('dungeonView'); + if (dungeonView) { + console.log('[UI MANAGER] Found dungeonView element, creating exploration UI'); + const dungeon = dungeonData.currentDungeon; + const progress = dungeonData.progress || 0; + const currentRoom = dungeonData.currentRoom; + + let content = ` +
+
+

${dungeon.dungeonId} Dungeon

+
+ Progress: Room ${progress + 1} +
+ +
+ `; + + if (currentRoom) { + content += ` +
+

${currentRoom.name}

+

${currentRoom.description}

+
+
Enemies:
+ ${currentRoom.enemies && currentRoom.enemies.length > 0 ? + currentRoom.enemies.map((enemy, index) => { + console.log(`[UI MANAGER] Rendering enemy ${index}:`, enemy); + return ` +
+ ${enemy.name || 'Unknown Enemy'} +
HP: ${enemy.health || '??'} | ATK: ${enemy.attack || '??'} | DEF: ${enemy.defense || '??'}
+
+ `}).join('') : + '

No enemies in this room

' + } +
+ ${currentRoom.enemies && currentRoom.enemies.length > 0 ? + `` : + `` + } +
+ `; + } else { + content += ` +
+

Loading next room...

+ +
+ `; + } + + content += '
'; + dungeonView.innerHTML = content; + console.log('[UI MANAGER] Dungeon exploration UI applied successfully'); + } else { + console.error('[UI MANAGER] dungeonView element not found!'); + } + } } // Export UIManager to global scope diff --git a/Galaxy-Strike-Online-main/Client/locales/de.json b/Galaxy-Strike-Online-main/Client/locales/de.json new file mode 100644 index 0000000..36a5554 --- /dev/null +++ b/Galaxy-Strike-Online-main/Client/locales/de.json @@ -0,0 +1 @@ +{"_meta":{"lang":"de","name":"de","status":"placeholder - community translation needed"}} diff --git a/Galaxy-Strike-Online-main/Client/locales/en.json b/Galaxy-Strike-Online-main/Client/locales/en.json new file mode 100644 index 0000000..a0e3aae --- /dev/null +++ b/Galaxy-Strike-Online-main/Client/locales/en.json @@ -0,0 +1,58 @@ +{ + "_meta": { "lang": "en", "name": "English", "version": "1.0" }, + "nav": { + "dashboard": "Dashboard", + "dungeons": "Dungeons", + "skills": "Skills", + "base": "Base", + "quests": "Quests", + "inventory": "Inventory", + "crafting": "Crafting", + "shop": "Shop", + "fleet": "Fleet", + "galaxy": "Galaxy", + "research": "Research", + "leaderboard": "Ranks", + "missions": "Missions", + "alliance": "Alliance", + "market": "Market", + "social": "Social" + }, + "base": { + "buildings": "🏗 Buildings", + "shipyard": "🚀 Shipyard", + "starbase": "🌌 Starbase", + "overview": "📊 Overview" + }, + "resources": { + "metal": "Metal", + "gas": "Gas", + "crystal": "Crystal", + "energyCells": "Energy Cells", + "darkMatter": "Dark Matter", + "credits": "Credits", + "gems": "Gems" + }, + "actions": { + "build": "Build", + "upgrade": "Upgrade", + "cancel": "Cancel", + "collect": "Collect", + "launch": "Launch", + "search": "Search", + "join": "Join", + "leave": "Leave", + "deposit": "Deposit", + "withdraw": "Withdraw", + "buy": "Buy", + "sell": "Sell" + }, + "status": { + "online": "Online", + "offline": "Offline", + "inProgress": "In Progress", + "completed": "Completed", + "locked": "Locked", + "maxLevel": "Max Level" + } +} diff --git a/Galaxy-Strike-Online-main/Client/locales/es.json b/Galaxy-Strike-Online-main/Client/locales/es.json new file mode 100644 index 0000000..402a19e --- /dev/null +++ b/Galaxy-Strike-Online-main/Client/locales/es.json @@ -0,0 +1 @@ +{"_meta":{"lang":"es","name":"es","status":"placeholder - community translation needed"}} diff --git a/Galaxy-Strike-Online-main/Client/locales/fr.json b/Galaxy-Strike-Online-main/Client/locales/fr.json new file mode 100644 index 0000000..70ed77b --- /dev/null +++ b/Galaxy-Strike-Online-main/Client/locales/fr.json @@ -0,0 +1 @@ +{"_meta":{"lang":"fr","name":"fr","status":"placeholder - community translation needed"}} diff --git a/Galaxy-Strike-Online-main/Client/locales/ja.json b/Galaxy-Strike-Online-main/Client/locales/ja.json new file mode 100644 index 0000000..f867b19 --- /dev/null +++ b/Galaxy-Strike-Online-main/Client/locales/ja.json @@ -0,0 +1 @@ +{"_meta":{"lang":"ja","name":"ja","status":"placeholder - community translation needed"}} diff --git a/Galaxy-Strike-Online-main/Client/locales/ko.json b/Galaxy-Strike-Online-main/Client/locales/ko.json new file mode 100644 index 0000000..2dace50 --- /dev/null +++ b/Galaxy-Strike-Online-main/Client/locales/ko.json @@ -0,0 +1 @@ +{"_meta":{"lang":"ko","name":"ko","status":"placeholder - community translation needed"}} diff --git a/Galaxy-Strike-Online-main/Client/locales/pt.json b/Galaxy-Strike-Online-main/Client/locales/pt.json new file mode 100644 index 0000000..2119166 --- /dev/null +++ b/Galaxy-Strike-Online-main/Client/locales/pt.json @@ -0,0 +1 @@ +{"_meta":{"lang":"pt","name":"pt","status":"placeholder - community translation needed"}} diff --git a/Galaxy-Strike-Online-main/Client/locales/zh.json b/Galaxy-Strike-Online-main/Client/locales/zh.json new file mode 100644 index 0000000..3edad9b --- /dev/null +++ b/Galaxy-Strike-Online-main/Client/locales/zh.json @@ -0,0 +1 @@ +{"_meta":{"lang":"zh","name":"zh","status":"placeholder - community translation needed"}} diff --git a/Client-Server/package-lock.json b/Galaxy-Strike-Online-main/Client/package-lock.json similarity index 100% rename from Client-Server/package-lock.json rename to Galaxy-Strike-Online-main/Client/package-lock.json diff --git a/Client-Server/package.json b/Galaxy-Strike-Online-main/Client/package.json similarity index 100% rename from Client-Server/package.json rename to Galaxy-Strike-Online-main/Client/package.json diff --git a/Client-Server/preload.js b/Galaxy-Strike-Online-main/Client/preload.js similarity index 100% rename from Client-Server/preload.js rename to Galaxy-Strike-Online-main/Client/preload.js diff --git a/Galaxy-Strike-Online-main/Client/styles/components.css b/Galaxy-Strike-Online-main/Client/styles/components.css new file mode 100644 index 0000000..4e9d197 --- /dev/null +++ b/Galaxy-Strike-Online-main/Client/styles/components.css @@ -0,0 +1,216 @@ +/* ═══════════════════════════════════════════════════════════════════════ + Galaxy Strike Online — Component Styles (mobile-first) + ═══════════════════════════════════════════════════════════════════════ */ + +/* ── Buttons ─────────────────────────────────────────────────────────── */ +.btn { + padding:.6rem 1.1rem;border:none;border-radius:8px; + font-family:'Space Mono',monospace;font-weight:700;cursor:pointer; + transition:all .25s;text-transform:uppercase;letter-spacing:1px; + position:relative;overflow:hidden;font-size:.82rem;display:inline-flex;align-items:center;gap:.35rem; + -webkit-tap-highlight-color:transparent;touch-action:manipulation; +} +.btn::before{content:'';position:absolute;top:0;left:-100%;width:100%;height:100%;background:linear-gradient(90deg,transparent,rgba(255,255,255,.18),transparent);transition:left .5s} +.btn:hover::before{left:100%} +.btn-primary{background:var(--gradient-primary);color:var(--bg-primary);box-shadow:0 3px 12px rgba(0,212,255,.3)} +.btn-primary:hover{transform:translateY(-2px);box-shadow:0 5px 18px rgba(0,212,255,.4)} +.btn-secondary{background:var(--bg-tertiary);color:var(--text-primary);border:1px solid var(--border-color)} +.btn-secondary:hover{border-color:var(--primary-color);background:var(--hover-bg)} +.btn-success{background:linear-gradient(135deg,#00ff88,#00cc66);color:var(--bg-primary);box-shadow:0 3px 12px rgba(0,255,136,.3)} +.btn-warning{background:linear-gradient(135deg,#ffaa00,#ff8800);color:var(--bg-primary);box-shadow:0 3px 12px rgba(255,170,0,.3)} +.btn-danger,.btn-error{background:linear-gradient(135deg,#ff3366,#ff0033);color:var(--bg-primary);box-shadow:0 3px 12px rgba(255,51,102,.3)} +.btn-info{background:linear-gradient(135deg,#4fc3f7,#0288d1);color:var(--bg-primary)} +.btn:disabled{opacity:.5;cursor:not-allowed;transform:none!important} +/* Larger tap targets on touch */ +@media(pointer:coarse){.btn{min-height:42px;padding:.6rem 1.2rem}} + +/* ── Health / Progress bars ──────────────────────────────────────────── */ +.health-bar{margin:.75rem 0;padding:.6rem;border-radius:8px;background:rgba(0,0,0,.3);border:1px solid rgba(255,255,255,.08)} +.health-label{font-size:.82rem;font-weight:600;margin-bottom:.4rem;text-transform:uppercase;letter-spacing:1px} +.ship-health .health-label{color:#4a9eff} +.player-health .health-label{color:#4ade80} +.ship-health-fill{background:linear-gradient(90deg,#4a9eff,#00d4ff);border-radius:4px;transition:width .3s} +.player-health-fill{background:linear-gradient(90deg,#4ade80,#22c55e);border-radius:4px;transition:width .3s} +.health-bar span{display:block;text-align:center;margin-top:.4rem;font-size:.8rem;color:rgba(255,255,255,.75)} +.progress-bar{width:100%;height:8px;background:var(--bg-tertiary);border-radius:4px;overflow:hidden;position:relative} +.progress-fill{height:100%;background:var(--gradient-primary);border-radius:4px;transition:width .3s;position:relative} +.progress-fill::after{content:'';position:absolute;inset:0;background:linear-gradient(90deg,transparent,rgba(255,255,255,.3),transparent);animation:progressShine 2s infinite} +@keyframes progressShine{0%{transform:translateX(-100%)}100%{transform:translateX(100%)}} + +/* ── Ship stats ──────────────────────────────────────────────────────── */ +.current-ship-stats{background:rgba(0,0,0,.3);border:1px solid rgba(255,255,255,.08);border-radius:8px;padding:.75rem;margin-bottom:.75rem} +.stat-row{display:flex;justify-content:space-between;align-items:center;padding:.4rem 0;border-bottom:1px solid rgba(255,255,255,.05)} +.stat-row:last-child{border-bottom:none} +.stat-row .stat-label{color:#4a9eff;font-weight:600;text-transform:uppercase;letter-spacing:1px;font-size:.8rem} +.stat-row .stat-value{color:#fff;font-weight:700;font-size:.85rem} + +/* ── Modals ──────────────────────────────────────────────────────────── */ +.modal-overlay{position:fixed;inset:0;background:rgba(0,0,0,.8);display:flex;align-items:flex-end;justify-content:center;z-index:1000;backdrop-filter:blur(4px)} +.modal{background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:16px 16px 0 0;width:100%;max-height:90dvh;overflow-y:auto;box-shadow:0 -10px 40px rgba(0,0,0,.5);animation:modalSlideUp .3s ease} +@keyframes modalSlideUp{from{opacity:0;transform:translateY(40px)}to{opacity:1;transform:translateY(0)}} +.modal-header{padding:1rem 1.25rem;border-bottom:1px solid var(--border-color);display:flex;justify-content:space-between;align-items:center} +.modal-header h3{color:var(--primary-color);font-family:'Orbitron',sans-serif;font-weight:700;text-transform:uppercase;letter-spacing:1px;font-size:.95rem} +.modal-close{background:transparent;border:none;color:var(--text-secondary);font-size:1.4rem;cursor:pointer;padding:.4rem;transition:all .25s;border-radius:6px;-webkit-tap-highlight-color:transparent} +.modal-close:hover{color:var(--error-color);transform:rotate(90deg)} +.modal-body{padding:1rem 1.25rem} + +/* Alert modal */ +.alert-modal .modal-body{text-align:center;padding:1.5rem 1.25rem} +.alert-modal .alert-message{color:var(--text-primary);font-size:.95rem;line-height:1.5;margin-bottom:1.25rem;white-space:pre-line} +.alert-modal .modal-footer{padding:0 1.25rem 1.25rem;text-align:center} +.alert-modal .btn-alert{background:var(--gradient-primary);color:#fff;border:none;padding:.65rem 1.75rem;border-radius:8px;font-weight:600;cursor:pointer;transition:all .3s;text-transform:uppercase;letter-spacing:1px;min-height:42px} +.alert-modal.success .modal-header h3{color:var(--success-color)} .alert-modal.success .btn-alert{background:var(--success-color)} +.alert-modal.error .modal-header h3{color:var(--error-color)} .alert-modal.error .btn-alert{background:var(--error-color)} +.alert-modal.warning .modal-header h3{color:var(--warning-color)} .alert-modal.warning .btn-alert{background:var(--warning-color)} + +/* Confirmation modal */ +.confirmation-modal .modal-body{text-align:center;padding:1.5rem 1.25rem} +.confirmation-modal .confirm-message{color:var(--text-primary);font-size:.95rem;line-height:1.5;margin-bottom:1.25rem;white-space:pre-line} +.confirmation-modal .modal-footer{padding:0 1.25rem 1.25rem;display:flex;gap:.75rem;justify-content:center;flex-wrap:wrap} +.confirmation-modal .btn-confirm{background:var(--error-color);color:#fff;border:none;padding:.65rem 1.5rem;border-radius:8px;font-weight:600;cursor:pointer;transition:all .3s;text-transform:uppercase;letter-spacing:1px;min-height:42px} +.confirmation-modal .btn-cancel{background:var(--bg-tertiary);color:var(--text-primary);border:1px solid var(--border-color);padding:.65rem 1.5rem;border-radius:8px;font-weight:600;cursor:pointer;transition:all .3s;text-transform:uppercase;letter-spacing:1px;min-height:42px} +.confirmation-modal .btn-cancel:hover{background:var(--hover-bg);border-color:var(--primary-color)} + +/* Settings */ +.settings-menu{max-width:600px;margin:0 auto} +.settings-section{margin-bottom:1.5rem;padding:1.1rem;background:var(--bg-tertiary);border-radius:10px;border:1px solid var(--border-color)} +.settings-section h3{color:var(--primary-color);font-family:'Orbitron',sans-serif;font-weight:700;text-transform:uppercase;letter-spacing:1px;margin-bottom:.75rem;font-size:1rem} +.settings-section h4{color:var(--text-primary);font-family:'Orbitron',sans-serif;font-weight:600;margin-bottom:.4rem;font-size:.9rem} +.settings-section p{color:var(--text-secondary);margin-bottom:.75rem;line-height:1.5;font-size:.88rem} +.setting-group{margin-bottom:1.25rem} +.setting-group label{display:block;color:var(--text-primary);font-weight:600;margin-bottom:.4rem;font-size:.88rem} +.setting-select{width:100%;padding:.65rem;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:8px;color:var(--text-primary);font-size:.88rem;cursor:pointer;transition:all .3s;-webkit-appearance:none;appearance:none} +.setting-select:hover{border-color:var(--primary-color)} +.setting-select:focus{outline:none;border-color:var(--primary-color);box-shadow:0 0 0 2px rgba(74,158,255,.2)} +.setting-actions{display:flex;gap:.75rem;justify-content:flex-end;margin-top:.75rem;flex-wrap:wrap} +.reset-options{display:flex;flex-direction:column;gap:.75rem} +.reset-option{padding:.85rem;background:var(--bg-secondary);border-radius:8px;border:1px solid var(--border-color)} +.reset-option h4{color:var(--warning-color);margin-bottom:.4rem} +.reset-option ul{margin:.4rem 0;padding-left:1.25rem;color:var(--text-secondary);font-size:.82rem} +.reset-option li{margin-bottom:.2rem} + +/* ── Tooltips (desktop only — use title attr on mobile) ──────────────── */ +@media(min-width:1024px){ + .tooltip{position:relative;cursor:help} + .tooltip::before{content:attr(data-tooltip);position:absolute;bottom:125%;left:50%;transform:translateX(-50%);background:var(--bg-tertiary);color:var(--text-primary);padding:.4rem .85rem;border-radius:6px;border:1px solid var(--border-color);font-size:.78rem;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .25s;z-index:1000} + .tooltip::after{content:'';position:absolute;bottom:-5px;left:50%;transform:translateX(-50%);border:5px solid transparent;border-top-color:var(--border-color);opacity:0;pointer-events:none;transition:opacity .25s} + .tooltip:hover::before,.tooltip:hover::after{opacity:1} +} + +/* ── Notifications ───────────────────────────────────────────────────── */ +.notification{ + position:fixed;top:12px;right:12px;left:12px; + padding:.85rem 1.1rem;border-radius:10px;border:1px solid var(--border-color); + background:var(--bg-secondary);color:var(--text-primary); + box-shadow:0 8px 24px rgba(0,0,0,.35);z-index:2000; + animation:notifSlideIn .3s ease;font-size:.88rem; + max-width:420px;margin:0 auto; +} +@keyframes notifSlideIn{from{opacity:0;transform:translateY(-20px)}to{opacity:1;transform:translateY(0)}} +.notification.success{border-color:var(--success-color);background:linear-gradient(135deg,rgba(0,255,136,.12),rgba(0,204,102,.08))} +.notification.warning{border-color:var(--warning-color);background:linear-gradient(135deg,rgba(255,170,0,.12),rgba(255,136,0,.08))} +.notification.error {border-color:var(--error-color); background:linear-gradient(135deg,rgba(255,51,102,.12),rgba(255,0,51,.08))} +.notification.info {border-color:var(--primary-color);background:linear-gradient(135deg,rgba(0,212,255,.12),rgba(0,153,204,.08))} +@media(min-width:640px){ + .notification{left:auto;right:16px;top:16px;width:300px;margin:0} + @keyframes notifSlideIn{from{opacity:0;transform:translateX(100%)}to{opacity:1;transform:translateX(0)}} +} + +/* ── Inventory slots & item cards ────────────────────────────────────── */ +.inventory-slot.starbase-bonus-slot::before{content:'+';position:absolute;top:2px;right:2px;background:var(--primary-color);color:#fff;width:12px;height:12px;border-radius:50%;font-size:8px;display:flex;align-items:center;justify-content:center;font-weight:bold} +.item-card{width:100%;height:100%;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:8px;padding:.4rem;cursor:pointer;transition:all .25s;position:relative;overflow:hidden;display:flex;flex-direction:column;align-items:center;justify-content:center} +.item-card::before{content:'';position:absolute;top:0;left:0;right:0;height:2px;background:var(--gradient-primary);transform:scaleX(0);transition:transform .3s} +.item-card:hover{border-color:var(--primary-color);transform:translateY(-2px);box-shadow:0 4px 12px rgba(0,212,255,.18)} +.item-card:hover::before{transform:scaleX(1)} +.item-card.rare{border-color:#0088ff;box-shadow:0 0 8px rgba(0,136,255,.25)} +.item-card.epic{border-color:#8833ff;box-shadow:0 0 10px rgba(136,51,255,.25)} +.item-card.legendary{border-color:#ff8800;box-shadow:0 0 14px rgba(255,136,0,.25)} +.item-icon{flex:1;display:flex;align-items:center;justify-content:center;margin-bottom:.35rem} +.item-icon img,.item-icon i{width:50%;height:50%;min-width:50px;min-height:50px;max-width:120px;max-height:120px;object-fit:contain} +.item-info{text-align:center;font-size:clamp(.58rem,1.4vw,.82rem);line-height:1.1} +.item-name{font-weight:600;margin-bottom:.15rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%} +.item-rarity{font-size:clamp(.52rem,1.1vw,.7rem);opacity:.8} +.item-quantity{position:absolute;top:3px;right:3px;background:var(--primary-color);color:#fff;font-size:clamp(.5rem,1.1vw,.68rem);font-weight:bold;padding:2px 5px;border-radius:5px;min-width:18px;text-align:center} + +/* ── Skill items ─────────────────────────────────────────────────────── */ +.skill-item{background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:8px;padding:1rem;cursor:pointer;transition:all .25s} +.skill-item:hover{border-color:var(--primary-color);transform:translateY(-2px);box-shadow:0 4px 12px rgba(0,212,255,.18)} +.skill-item.locked{opacity:.5;cursor:not-allowed} +.skill-item.locked:hover{transform:none;box-shadow:none;border-color:var(--border-color)} +.skill-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:.4rem} +.skill-name{font-weight:700;color:var(--text-primary);font-size:.9rem} +.skill-level{color:var(--primary-color);font-weight:700;font-size:.9rem} +.skill-description{color:var(--text-secondary);font-size:.82rem;margin-bottom:.75rem} +.skill-progress{margin-top:.4rem} + +/* ── Quest items ─────────────────────────────────────────────────────── */ +.quest-item{background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:8px;padding:.9rem;cursor:pointer;transition:all .25s} +.quest-item:hover{border-color:var(--primary-color);transform:translateY(-2px);box-shadow:0 4px 12px rgba(0,212,255,.18)} +.quest-item.completed{border-color:var(--success-color);background:linear-gradient(135deg,rgba(0,255,136,.08),rgba(0,204,102,.06))} +.quest-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:.4rem;flex-wrap:wrap;gap:.3rem} +.quest-title{font-weight:700;color:var(--text-primary);font-size:.9rem} +.quest-reward{color:var(--warning-color);font-weight:700;font-size:.9rem} +.quest-description{color:var(--text-secondary);font-size:.82rem;margin-bottom:.75rem} +.quest-progress{margin-top:.4rem} + +/* ── Dungeon items ───────────────────────────────────────────────────── */ +.dungeon-item{background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:8px;padding:.75rem;cursor:pointer;transition:all .25s} +.dungeon-item:hover{border-color:var(--primary-color);transform:translateY(-2px);box-shadow:0 4px 12px rgba(0,212,255,.18)} +.dungeon-item.selected{border-color:var(--primary-color);background:rgba(0,212,255,.15)} +.dungeon-name{font-weight:700;color:var(--text-primary);margin-bottom:.2rem;font-size:.9rem} +.dungeon-difficulty{font-size:.78rem;margin-bottom:.2rem} +.dungeon-difficulty.easy{color:var(--success-color)} .dungeon-difficulty.medium{color:var(--warning-color)} .dungeon-difficulty.hard{color:var(--error-color)} +.dungeon-rewards{font-size:.78rem;color:var(--text-secondary)} + +/* Collapsible dungeon sections */ +.dungeon-section{margin-bottom:.75rem;border:1px solid var(--border-color);border-radius:8px;overflow:hidden;background:var(--bg-secondary)} +.difficulty-header.collapsible{padding:.8rem 1rem;cursor:pointer;user-select:none;display:flex;justify-content:space-between;align-items:center;transition:background-color .2s;margin:0;border:none;border-radius:0} +.difficulty-header.collapsible:hover{background:var(--bg-tertiary)} +.header-content{display:flex;align-items:center;gap:.4rem} +.header-content i{font-size:1.1rem} +.header-content span{font-weight:600;font-size:1rem} +.dungeon-count{background:rgba(255,255,255,.18);padding:.15rem .45rem;border-radius:10px;font-size:.75rem;font-weight:600;min-width:1.8rem;text-align:center} +.collapse-indicator{transition:transform .3s;background:rgba(255,255,255,.08);width:1.8rem;height:1.8rem;border-radius:50%;display:flex;align-items:center;justify-content:center} +.collapse-indicator i{font-size:.82rem;color:rgba(255,255,255,.85)} +.dungeon-content{padding:0 .75rem .75rem;max-height:2000px;overflow:hidden;transition:all .3s;opacity:1} +.dungeon-content.collapsed{max-height:0;padding:0 .75rem;opacity:0} +.difficulty-header.tutorial{background:linear-gradient(135deg,#1a56db,#2c5aa0);color:#fff} +.difficulty-header.easy{background:linear-gradient(135deg,var(--success-color),#27ae60);color:#fff} +.difficulty-header.medium{background:linear-gradient(135deg,var(--warning-color),#f39c12);color:#fff} +.difficulty-header.hard{background:linear-gradient(135deg,var(--error-color),#e74c3c);color:#fff} +.difficulty-header.extreme{background:linear-gradient(135deg,#8e44ad,#9b59b6);color:#fff} +.dungeon-content .dungeon-item{margin-bottom:.6rem;border-left:4px solid transparent} +.dungeon-content .dungeon-item:hover{border-left-color:var(--primary-color);transform:translateX(3px)} +.dungeon-content .dungeon-item:last-child{margin-bottom:0} + +/* ── Shop items (legacy card style) ─────────────────────────────────── */ +.shop-item{background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:8px;padding:1rem;transition:all .25s} +.shop-item:hover{border-color:var(--primary-color);transform:translateY(-2px);box-shadow:0 4px 12px rgba(0,212,255,.18)} +.shop-item.purchased{opacity:.5;cursor:not-allowed} +.shop-item.purchased:hover{transform:none;box-shadow:none;border-color:var(--border-color)} +.shop-name{font-weight:700;color:var(--text-primary);margin-bottom:.4rem;font-size:.9rem} +.shop-description{color:var(--text-secondary);font-size:.82rem;margin-bottom:.75rem} +.shop-price{display:flex;justify-content:space-between;align-items:center;margin-bottom:.75rem;flex-wrap:wrap;gap:.3rem} +.shop-cost{color:var(--warning-color);font-weight:700;font-size:.9rem} + +/* ── Loading states ──────────────────────────────────────────────────── */ +.loading{position:relative;overflow:hidden} +.loading::after{content:'';position:absolute;top:0;left:-100%;width:100%;height:100%;background:linear-gradient(90deg,transparent,rgba(0,212,255,.18),transparent);animation:loadingShine 1.5s infinite} +@keyframes loadingShine{0%{left:-100%}100%{left:100%}} + +/* ── Misc animations ─────────────────────────────────────────────────── */ +@keyframes pulse{0%,100%{opacity:1}50%{opacity:.5}} +@keyframes bounce{0%,100%{transform:translateY(0)}50%{transform:translateY(-8px)}} +@keyframes rotate{from{transform:rotate(0deg)}to{transform:rotate(360deg)}} +@keyframes glow{0%,100%{box-shadow:0 0 5px rgba(0,212,255,.5)}50%{box-shadow:0 0 18px rgba(0,212,255,.8)}} +.pulse{animation:pulse 2s infinite} +.bounce{animation:bounce 2s infinite} +.rotate{animation:rotate 2s linear infinite} +.glow{animation:glow 2s infinite} + +/* ── Desktop modal — centred (override bottom-sheet) ────────────────── */ +@media(min-width:640px){ + .modal-overlay{align-items:center} + .modal{border-radius:14px;max-width:580px;width:90%;max-height:80dvh} + @keyframes modalSlideUp{from{opacity:0;transform:translateY(-30px) scale(.95)}to{opacity:1;transform:translateY(0) scale(1)}} +} diff --git a/Client-Server/styles/components.css b/Galaxy-Strike-Online-main/Client/styles/components.css.bak similarity index 92% rename from Client-Server/styles/components.css rename to Galaxy-Strike-Online-main/Client/styles/components.css.bak index bc076f6..deecfc2 100644 --- a/Client-Server/styles/components.css +++ b/Galaxy-Strike-Online-main/Client/styles/components.css.bak @@ -1533,6 +1533,134 @@ body.fullscreen .shop-container { color: var(--text-secondary); } +/* Collapsible Dungeon Sections */ +.dungeon-section { + margin-bottom: 1rem; + border: 1px solid var(--border-color); + border-radius: 8px; + overflow: hidden; + background: var(--bg-secondary); +} + +.difficulty-header.collapsible { + padding: 1rem; + cursor: pointer; + user-select: none; + display: flex; + justify-content: space-between; + align-items: center; + transition: background-color 0.3s ease; + margin: 0; + border: none; + border-radius: 0; +} + +.difficulty-header.collapsible:hover { + background: var(--bg-tertiary); +} + +.header-content { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.header-content i { + font-size: 1.2rem; +} + +.header-content span { + font-weight: 600; + font-size: 1.1rem; +} + +.dungeon-count { + background: rgba(255, 255, 255, 0.2); + padding: 0.2rem 0.5rem; + border-radius: 12px; + font-size: 0.8rem; + font-weight: 600; + min-width: 2rem; + text-align: center; +} + +.collapse-indicator { + transition: transform 0.3s ease; + background: rgba(255, 255, 255, 0.1); + width: 2rem; + height: 2rem; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; +} + +.collapse-indicator i { + font-size: 0.9rem; + color: rgba(255, 255, 255, 0.9); +} + +.difficulty-header.collapsible:hover .collapse-indicator { + background: rgba(255, 255, 255, 0.2); + transform: scale(1.1); +} + +.dungeon-content { + padding: 0 1rem 1rem; + max-height: 2000px; + overflow: hidden; + transition: all 0.3s ease; + opacity: 1; +} + +.dungeon-content.collapsed { + max-height: 0; + padding: 0 1rem; + opacity: 0; +} + +/* Difficulty-specific colors for collapsible headers */ +.difficulty-header.tutorial { + background: linear-gradient(135deg, var(--info-color), #2c5aa0); + color: white; +} + +.difficulty-header.easy { + background: linear-gradient(135deg, var(--success-color), #27ae60); + color: white; +} + +.difficulty-header.medium { + background: linear-gradient(135deg, var(--warning-color), #f39c12); + color: white; +} + +.difficulty-header.hard { + background: linear-gradient(135deg, var(--error-color), #e74c3c); + color: white; +} + +.difficulty-header.extreme { + background: linear-gradient(135deg, #8e44ad, #9b59b6); + color: white; +} + +/* Enhanced Dungeon Items in Collapsible Sections */ +.dungeon-content .dungeon-item { + margin-bottom: 0.75rem; + border-left: 4px solid transparent; + transition: all 0.3s ease; +} + +.dungeon-content .dungeon-item:hover { + border-left-color: var(--primary-color); + transform: translateX(4px); +} + +.dungeon-content .dungeon-item:last-child { + margin-bottom: 0; +} + /* Shop Items */ .shop-item { background: var(--bg-tertiary); diff --git a/Galaxy-Strike-Online-main/Client/styles/main.css b/Galaxy-Strike-Online-main/Client/styles/main.css new file mode 100644 index 0000000..7b5a484 --- /dev/null +++ b/Galaxy-Strike-Online-main/Client/styles/main.css @@ -0,0 +1,606 @@ +/* ═══════════════════════════════════════════════════════════════════════ + Galaxy Strike Online — Main Styles + Mobile-first. Scales up to tablet (≥640px) and desktop (≥1024px). + ═══════════════════════════════════════════════════════════════════════ */ + +/* ── Reset ───────────────────────────────────────────────────────────── */ +*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; } + +:root { + --primary-color: #00d4ff; + --secondary-color: #ff6b35; + --accent-color: #ff00ff; + --bg-primary: #0a0e1a; + --bg-secondary: #151923; + --bg-tertiary: #1e2433; + --text-primary: #ffffff; + --text-secondary: #b8c5d6; + --text-muted: #6b7c93; + --border-color: #2a3241; + --success-color: #00ff88; + --warning-color: #ffaa00; + --error-color: #ff3366; + --card-bg: rgba(30,36,51,0.8); + --hover-bg: rgba(0,212,255,0.1); + --gradient-primary: linear-gradient(135deg,#00d4ff,#0099cc); + --gradient-secondary: linear-gradient(135deg,#ff6b35,#ff4500); + /* Layout tokens */ + --header-h: 52px; + --nav-h: 56px; + --pg: 0.75rem; + --r: 10px; +} + +html { font-size: 14px; -webkit-text-size-adjust: 100%; } +body { + font-family: 'Space Mono', monospace; + background: var(--bg-primary); color: var(--text-primary); + overflow: hidden; height: 100dvh; + background-image: + radial-gradient(circle at 20% 50%,rgba(0,212,255,.08) 0%,transparent 50%), + radial-gradient(circle at 80% 80%,rgba(255,107,53,.08) 0%,transparent 50%), + radial-gradient(circle at 40% 20%,rgba(255,0,255,.04) 0%,transparent 50%); +} +h1,h2,h3,.logo,.section-title,.menu-title { font-family:'Orbitron',sans-serif; } +.hidden { display:none!important; } +.text-center{text-align:center} .w-full{width:100%} +.flex{display:flex} .flex-column{flex-direction:column} +.flex-center{align-items:center;justify-content:center} +.flex-between{justify-content:space-between} .flex-wrap{flex-wrap:wrap} +.mt-1{margin-top:.5rem} .mt-2{margin-top:1rem} +.mb-1{margin-bottom:.5rem} .mb-2{margin-bottom:1rem} +.p-1{padding:.5rem} .p-2{padding:1rem} + +/* ── Scrollbars ──────────────────────────────────────────────────────── */ +::-webkit-scrollbar{width:5px;height:5px} +::-webkit-scrollbar-track{background:var(--bg-tertiary);border-radius:3px} +::-webkit-scrollbar-thumb{background:rgba(0,212,255,.35);border-radius:3px} +::-webkit-scrollbar-thumb:hover{background:rgba(0,212,255,.6)} + +/* ══════════════════════════════════════════════════════════════════════ + LOADING SCREEN + ══════════════════════════════════════════════════════════════════════ */ +.loading-screen { + position:fixed;inset:0;background:var(--bg-primary); + display:flex;align-items:center;justify-content:center; + z-index:9999;transition:opacity .5s ease; +} +.loading-content{text-align:center;width:90%;max-width:340px;padding:2rem} +.game-title { + font-family:'Orbitron',sans-serif; + font-size:clamp(1.6rem,8vw,3rem);font-weight:900; + background:var(--gradient-primary); + -webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent; + margin-bottom:2rem;text-transform:uppercase;letter-spacing:3px; +} +.loading-bar{width:100%;height:4px;background:var(--bg-tertiary);border-radius:2px;overflow:hidden;margin-bottom:1rem} +.loading-progress{height:100%;background:var(--gradient-primary);width:0%;transition:width .3s ease;animation:loading-pulse 2s infinite} +@keyframes loading-pulse{0%,100%{opacity:1}50%{opacity:.7}} +.loading-text{color:var(--text-secondary);font-size:.8rem} +.loading-indicator { + position:fixed;top:0;left:0;width:100%;height:3px; + background:linear-gradient(90deg,var(--primary-color) 0%,rgba(0,212,255,.3) 50%,var(--primary-color) 100%); + background-size:200% 100%;animation:loading-gradient 2s ease-in-out infinite; + z-index:10000;transition:opacity .3s ease; +} +.loading-indicator.hidden{opacity:0;pointer-events:none} +.loading-indicator.complete{background:linear-gradient(90deg,#4CAF50,#45a049);animation:none} +.loading-indicator.error{background:linear-gradient(90deg,#f44336,#d32f2f);animation:none} +@keyframes loading-gradient{0%{background-position:0% 50%}50%{background-position:100% 50%}100%{background-position:0% 50%}} +.loading-status { + position:fixed;top:8px;left:50%;transform:translateX(-50%); + background:var(--card-bg);border:1px solid var(--border-color);border-radius:8px; + padding:6px 14px;color:var(--text-primary);font-size:.82rem;z-index:10001; + transition:all .3s ease;box-shadow:0 4px 12px rgba(0,0,0,.15); +} +.loading-status.error{background:linear-gradient(90deg,#f44336,#d32f2f);color:#fff;border-color:#d32f2f} +.loading-status.hidden{opacity:0;transform:translateX(-50%) translateY(-20px)} + +/* ══════════════════════════════════════════════════════════════════════ + DESKTOP ELECTRON TITLE BAR + ══════════════════════════════════════════════════════════════════════ */ +.title-bar { + position:fixed;top:0;left:0;right:0;height:32px; + background:var(--bg-primary);border-bottom:1px solid var(--border-color); + display:none;justify-content:space-between;align-items:center;padding:0 8px; + z-index:10000;-webkit-app-region:drag;user-select:none; +} +body.electron-app .title-bar{display:flex} +body.electron-app #app{margin-top:32px} +.title-bar-title{font-size:13px;font-weight:600;color:var(--text-primary);font-family:'Orbitron',monospace} +.title-bar-right{display:flex;gap:4px} +.title-bar-btn{ + width:24px;height:24px;border:none;background:transparent; + color:var(--text-primary);border-radius:4px;cursor:pointer; + display:flex;align-items:center;justify-content:center; + font-size:12px;-webkit-app-region:no-drag;transition:background .2s; +} +.title-bar-btn:hover{background:var(--bg-secondary)} +.title-bar-btn.close-btn:hover{background:#e74c3c;color:#fff} +body.fullscreen .title-bar{display:none} +body.fullscreen #app{margin-top:0} + +/* ══════════════════════════════════════════════════════════════════════ + MAIN MENU + ══════════════════════════════════════════════════════════════════════ */ +.main-menu { + position:fixed;inset:0;background:var(--bg-primary); + display:flex;align-items:center;justify-content:center; + overflow-y:auto;padding:1rem 0; + background-image:radial-gradient(circle at 20% 50%,rgba(0,212,255,.1) 0%,transparent 50%), + radial-gradient(circle at 80% 80%,rgba(255,107,53,.1) 0%,transparent 50%); +} +.menu-container { + width:95%;max-width:800px;background:var(--card-bg); + border-radius:16px;border:1px solid var(--border-color); + box-shadow:0 20px 60px rgba(0,0,0,.5);backdrop-filter:blur(10px);overflow:hidden;margin:auto; +} +.menu-header{text-align:center;padding:clamp(1.5rem,5vw,2.5rem) 1rem 1.2rem;background:var(--gradient-primary)} +.menu-title{font-size:clamp(1.4rem,6vw,3rem);font-weight:900;color:var(--text-primary);text-transform:uppercase;letter-spacing:3px;margin-bottom:.5rem;text-shadow:0 0 20px rgba(0,212,255,.5)} +.menu-subtitle{font-size:clamp(.85rem,2.5vw,1.2rem);color:var(--text-secondary)} +.menu-content{padding:clamp(1rem,4vw,2rem)} +.menu-section{animation:fadeInUp .5s ease-out} +.section-title{font-size:clamp(1.1rem,4vw,1.8rem);color:var(--primary-color);text-align:center;margin-bottom:1.5rem;text-transform:uppercase;letter-spacing:2px} +.login-options{display:flex;flex-direction:column;gap:1rem;margin-bottom:1rem} +.btn-large{ + padding:clamp(.9rem,3vw,1.25rem) clamp(1rem,4vw,2.5rem); + font-size:clamp(.9rem,2.5vw,1.2rem);font-weight:600;border-radius:10px; + transition:all .3s;width:100%;display:flex;align-items:center;justify-content:center;gap:.5rem; +} +.login-notice{text-align:center;padding:.75rem;background:rgba(0,212,255,.1);border:1px solid var(--primary-color);border-radius:8px;color:var(--text-secondary);font-size:.85rem} +.login-notice i{color:var(--primary-color);margin-right:.4rem} + +/* Server browser */ +.server-controls{display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem;gap:.75rem;flex-wrap:wrap} +.server-filters{display:flex;gap:.5rem;flex-wrap:wrap} +.filter-select{background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:8px;padding:.5rem .75rem;color:var(--text-primary);font-family:'Space Mono',monospace;font-size:.85rem;cursor:pointer} +.filter-select:focus{outline:none;border-color:var(--primary-color)} +.server-list{max-height:min(55vh,360px);overflow-y:auto;border:1px solid var(--border-color);border-radius:10px;background:var(--bg-tertiary);margin-bottom:1rem} +.server-item{display:flex;justify-content:space-between;align-items:center;padding:.85rem 1rem;border-bottom:1px solid var(--border-color);cursor:pointer;transition:background .2s,border-left .2s} +.server-item:last-child{border-bottom:none} +.server-item:hover{background:var(--hover-bg);border-left:4px solid var(--primary-color)} +.server-name{font-size:1rem;font-weight:600;margin-bottom:3px} +.server-details{font-size:.8rem;color:var(--text-secondary);display:flex;gap:.75rem;flex-wrap:wrap} +.server-actions-right{display:flex;align-items:center;gap:.5rem;flex-shrink:0} +.server-player-count,.server-region{background:var(--bg-secondary);padding:3px 8px;border-radius:4px;font-size:.75rem;color:var(--text-secondary);border:1px solid var(--border-color)} +.server-type{padding:3px 8px;border-radius:4px;font-size:.75rem;font-weight:600;text-transform:uppercase} +.server-type.public{background:rgba(0,255,136,.15);color:var(--success-color);border:1px solid var(--success-color)} +.server-type.private{background:rgba(255,170,0,.15);color:var(--warning-color);border:1px solid var(--warning-color)} +.server-loading,.server-empty{text-align:center;padding:3rem 1rem;color:var(--text-muted)} +.server-loading i,.server-empty i{font-size:2.5rem;margin-bottom:.75rem;opacity:.5;display:block} +.server-loading i{animation:pulse 1.5s infinite} + +/* Server/Save confirmation — mobile: stacked */ +.server-confirmation,.save-confirmation,.options-grid{display:flex;flex-direction:column;gap:1rem;margin-bottom:1.5rem} +.server-preview,.save-preview,.save-info-display{background:rgba(0,212,255,.08);border:2px solid rgba(0,212,255,.3);border-radius:10px;padding:1rem;font-family:'Space Mono',monospace} +.server-preview h3,.save-preview h3,.save-info-display h3{color:var(--primary-color);margin-bottom:.75rem;text-align:center;font-size:1rem} +.server-details{color:var(--text-secondary);font-size:.9rem;line-height:1.6} +.server-info{margin:6px 0;display:flex;justify-content:space-between;align-items:center} +.server-info span{font-weight:600;color:var(--text-primary)} +.confirm-actions-left,.confirm-actions-right,.options-left,.options-right{display:flex;flex-direction:row;gap:.75rem;flex-wrap:wrap} +.confirm-actions-left .btn-large,.confirm-actions-right .btn-large,.options-left .btn-large,.options-right .btn-large{position:static;width:auto;flex:1;min-width:130px} +.confirm-navigation,.options-actions,.save-actions{display:flex;justify-content:center;margin-top:1rem} +.btn-join-server{background:linear-gradient(135deg,#00ff88,#00cc66)!important;color:#000!important;border:3px solid #00ff88!important;box-shadow:0 6px 20px rgba(0,255,136,.4)!important;font-weight:700!important} +.btn-join-server:hover{background:linear-gradient(135deg,#00ffaa,#00ff88)!important;transform:scale(1.04) translateY(-2px);box-shadow:0 8px 25px rgba(0,255,136,.5)!important} +.selected-server-info-center,.selected-save-info-center,.options-center{display:flex;flex-direction:column;justify-content:center;align-items:center;flex:1} +.save-details{color:#fff;font-size:.9em;line-height:1.6;white-space:pre-wrap} +.save-info{margin:3px 0;font-family:'Space Mono',monospace} +#saveInfoDetails{color:#fff;font-size:.9em;line-height:1.4} +.save-info-content{background:rgba(0,100,200,.2);border-radius:8px;padding:.75rem;margin:0} + +/* Save slots */ +.save-slots{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:1rem;margin-bottom:1.5rem} +.save-slot{background:var(--bg-secondary);border:2px solid var(--border-color);border-radius:10px;padding:1rem;cursor:pointer;transition:all .3s} +.save-slot:hover{border-color:var(--primary-color);transform:translateY(-2px);box-shadow:0 8px 25px rgba(0,212,255,.2)} +.slot-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:.75rem} +.slot-number{font-family:'Orbitron',sans-serif;font-weight:700} +.slot-status{padding:3px 10px;border-radius:20px;font-size:.75rem;font-weight:600;text-transform:uppercase} +.slot-status.empty{background:var(--bg-tertiary);color:var(--text-muted)} +.slot-status.has-data{background:var(--success-color);color:var(--bg-primary)} +.slot-name{font-weight:700;color:var(--text-primary);margin-bottom:4px} +.slot-details{color:var(--text-muted);font-size:.9rem} +.slot-btn{width:100%;padding:.55rem;background:var(--gradient-primary);border:none;border-radius:6px;color:var(--text-primary);font-weight:600;cursor:pointer;transition:all .3s} +.slot-btn:hover{transform:translateY(-1px);box-shadow:0 4px 15px rgba(0,212,255,.3)} + +/* Menu footer */ +.menu-footer{padding:.9rem 1.5rem;background:var(--bg-secondary);border-top:1px solid var(--border-color);display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:.5rem} +.version-text{color:var(--text-muted);font-size:.8rem} +.footer-links{display:flex;gap:1rem} +.link-btn{background:none;border:none;color:var(--text-muted);cursor:pointer;font-size:.85rem;transition:color .3s} +.link-btn:hover{color:var(--primary-color)} + +/* ══════════════════════════════════════════════════════════════════════ + GAME INTERFACE + ══════════════════════════════════════════════════════════════════════ */ +.game-interface{width:100vw;height:100dvh;display:flex;flex-direction:column;overflow:hidden} + +/* Header */ +.game-header{ + height:var(--header-h);background:var(--bg-secondary); + border-bottom:1px solid var(--border-color); + display:flex;align-items:center;justify-content:space-between; + padding:0 .75rem;backdrop-filter:blur(10px);flex-shrink:0; + position:sticky;top:0;z-index:500; +} +.header-left{display:flex;align-items:center;gap:.6rem;min-width:0} +.logo{font-size:clamp(1rem,4vw,1.5rem);font-weight:900;color:var(--primary-color);text-shadow:0 0 10px rgba(0,212,255,.5);white-space:nowrap} +.player-info{display:flex;flex-direction:column;gap:.1rem;min-width:0} +.player-info>div{display:flex;align-items:center;gap:.25rem;min-width:0} +.player-name{font-weight:700;font-size:.85rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:18ch} +.player-title{font-size:.75rem;color:var(--text-secondary);display:none} +.player-username{font-weight:600;color:var(--accent-color);font-size:.78rem} +.player-level{font-size:.75rem;color:var(--primary-color);white-space:nowrap} + +.header-center{flex:1;display:flex;justify-content:center;overflow:hidden;min-width:0;padding:0 .4rem} +.resources{display:flex;gap:.35rem;align-items:center;overflow-x:auto;flex-wrap:nowrap;scrollbar-width:none;-ms-overflow-style:none;padding:.15rem 0} +.resources::-webkit-scrollbar{display:none} +.resource{display:flex;align-items:center;gap:.3rem;padding:.28rem .55rem;background:var(--bg-tertiary);border-radius:20px;border:1px solid var(--border-color);white-space:nowrap;font-size:.75rem;transition:border-color .2s;flex-shrink:0} +.resource:hover{border-color:var(--primary-color)} +.resource i{color:var(--primary-color);font-size:.78rem} + +.header-right{display:flex;gap:.3rem;flex-shrink:0} +.header-right .btn{padding:.4rem .55rem;font-size:.8rem;border-radius:7px} + +/* ── TOP NAV — desktop only ──────────────────────────────────────────── */ +.main-nav{ + height:46px;background:var(--bg-tertiary);border-bottom:1px solid var(--border-color); + display:none;align-items:center;padding:0 .75rem;gap:.3rem; + overflow-x:auto;position:sticky;top:var(--header-h);z-index:490; + scrollbar-width:none; +} +.main-nav::-webkit-scrollbar{display:none} +.nav-btn{ + display:flex;align-items:center;gap:.35rem;padding:.38rem .7rem; + background:transparent;border:1px solid transparent;border-radius:7px; + color:var(--text-secondary);cursor:pointer;transition:all .25s; + white-space:nowrap;font-family:'Space Mono',monospace;font-size:.78rem; +} +.nav-btn:hover{background:var(--hover-bg);color:var(--text-primary);border-color:var(--primary-color)} +.nav-btn.active{background:var(--gradient-primary);color:var(--bg-primary);border-color:transparent;font-weight:700} +.nav-btn i{font-size:.85rem} + +/* ── BOTTOM NAV — mobile primary navigation ──────────────────────────── */ +.bottom-nav{ + position:fixed;bottom:0;left:0;right:0; + height:var(--nav-h); + background:var(--bg-secondary);border-top:1px solid var(--border-color); + display:flex;align-items:stretch;z-index:600; + overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none; + overscroll-behavior-x:contain;-webkit-overflow-scrolling:touch; + padding-bottom:env(safe-area-inset-bottom,0px); +} +.bottom-nav::-webkit-scrollbar{display:none} +.bottom-nav-btn{ + display:flex;flex-direction:column;align-items:center;justify-content:center; + gap:2px;padding:.28rem .4rem;background:transparent;border:none; + color:var(--text-muted);cursor:pointer;transition:color .2s,background .2s; + font-family:'Space Mono',monospace;font-size:.52rem;line-height:1.1; + flex:1;min-width:48px;max-width:68px;white-space:nowrap;overflow:hidden; + border-top:2px solid transparent; + -webkit-tap-highlight-color:transparent;touch-action:manipulation; +} +.bottom-nav-btn i{font-size:1rem;display:block} +.bottom-nav-btn.active{color:var(--primary-color);border-top-color:var(--primary-color);background:rgba(0,212,255,.06)} +.bottom-nav-more{ + background:transparent;border:none;display:flex;flex-direction:column;align-items:center;justify-content:center; + gap:2px;padding:.28rem .4rem;min-width:48px;max-width:68px;flex-shrink:0; + color:var(--text-muted);cursor:pointer;font-size:.52rem;border-top:2px solid transparent; + -webkit-tap-highlight-color:transparent;touch-action:manipulation;font-family:'Space Mono',monospace; +} +.bottom-nav-more i{font-size:1rem;display:block} +.bottom-nav-more:hover,.bottom-nav-more.open{color:var(--primary-color)} + +/* ── NAV DRAWER ──────────────────────────────────────────────────────── */ +.nav-drawer{ + position:fixed;bottom:var(--nav-h);left:0;right:0; + background:var(--bg-secondary);border-top:1px solid var(--border-color); + border-radius:16px 16px 0 0;z-index:590; + transform:translateY(100%);transition:transform .3s cubic-bezier(.4,0,.2,1); + max-height:58dvh;overflow-y:auto;padding:.75rem 0; + box-shadow:0 -8px 32px rgba(0,0,0,.4); +} +.nav-drawer.open{transform:translateY(0)} +.nav-drawer-handle{width:36px;height:4px;background:var(--border-color);border-radius:2px;margin:0 auto .75rem;cursor:pointer} +.nav-drawer-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:.25rem;padding:0 .75rem .5rem} +.nav-drawer-btn{ + display:flex;flex-direction:column;align-items:center;justify-content:center; + gap:4px;padding:.6rem .4rem; + background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:10px; + color:var(--text-secondary);cursor:pointer;font-size:.68rem;line-height:1.2; + transition:all .2s;text-align:center;font-family:'Space Mono',monospace; + -webkit-tap-highlight-color:transparent;touch-action:manipulation; +} +.nav-drawer-btn i{font-size:1.1rem;color:var(--text-muted)} +.nav-drawer-btn:hover,.nav-drawer-btn.active{background:var(--hover-bg);border-color:var(--primary-color);color:var(--primary-color)} +.nav-drawer-btn.active i{color:var(--primary-color)} +.nav-drawer-overlay{position:fixed;inset:0;z-index:580;background:rgba(0,0,0,.45);display:none} +.nav-drawer-overlay.open{display:block} + +/* ── MAIN CONTENT ────────────────────────────────────────────────────── */ +.main-content{ + flex:1;overflow-y:auto;overflow-x:hidden; + padding:var(--pg);background:var(--bg-primary); + padding-bottom:calc(var(--nav-h) + var(--pg) + env(safe-area-inset-bottom,0px)); + -webkit-overflow-scrolling:touch;overscroll-behavior-y:contain; +} +.tab-content{display:none;animation:fadeIn .2s ease} +.tab-content.active{display:block} +@keyframes fadeIn{from{opacity:0;transform:translateY(5px)}to{opacity:1;transform:translateY(0)}} +@keyframes fadeInUp{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}} + +/* ── DASHBOARD ───────────────────────────────────────────────────────── */ +.dashboard-grid{display:grid;grid-template-columns:1fr;gap:.75rem;max-width:1200px;margin:0 auto} +.card{background:var(--card-bg);border:1px solid var(--border-color);border-radius:var(--r);padding:1rem;backdrop-filter:blur(10px);transition:border-color .3s,box-shadow .3s} +.card:hover{border-color:var(--primary-color);box-shadow:0 0 18px rgba(0,212,255,.15)} +.card h3{color:var(--primary-color);margin-bottom:.75rem;font-family:'Orbitron',sans-serif;font-size:.88rem;font-weight:700;text-transform:uppercase;letter-spacing:1px} +.fleet-info,.idle-stats,.quick-actions{display:flex;flex-direction:column;gap:.6rem} +.ship-status{display:flex;align-items:center;gap:.75rem} +.ship-status i{font-size:1.6rem;color:var(--secondary-color)} +.player-stats-grid{display:grid;grid-template-columns:1fr 1fr;gap:.35rem} +.stat{display:flex;justify-content:space-between;padding:.35rem 0;border-bottom:1px solid var(--border-color);font-size:.8rem} +.stat:last-child{border-bottom:none} +.stat-label{color:var(--text-secondary)} .stat-value{color:var(--primary-color);font-weight:700} + +/* ── DUNGEONS ────────────────────────────────────────────────────────── */ +.dungeons-container{display:flex;flex-direction:column;gap:.75rem} +.dungeon-selector{background:var(--card-bg);border:1px solid var(--border-color);border-radius:var(--r);padding:.75rem} +.dungeon-list{display:flex;flex-direction:column;gap:.4rem} +.dungeon-view{background:var(--card-bg);border:1px solid var(--border-color);border-radius:var(--r);padding:1rem;min-height:180px;display:flex;align-items:center;justify-content:center} +.dungeon-placeholder{text-align:center;color:var(--text-muted)} +.dungeon-placeholder i{font-size:2.8rem;margin-bottom:.75rem;opacity:.5;display:block} + +/* ── SKILLS ──────────────────────────────────────────────────────────── */ +.skills-container{max-width:1200px;margin:0 auto} +.skill-categories,.quest-tabs,.crafting-categories,.shop-categories{ + display:flex;gap:.4rem;margin-bottom:1rem;flex-wrap:wrap;overflow-x:auto; + scrollbar-width:none;padding-bottom:.2rem; +} +.skill-categories::-webkit-scrollbar,.quest-tabs::-webkit-scrollbar,.crafting-categories::-webkit-scrollbar,.shop-categories::-webkit-scrollbar{display:none} +.skill-cat-btn,.quest-tab-btn,.crafting-cat-btn,.shop-cat-btn{ + padding:.4rem .85rem;background:var(--bg-tertiary);border:1px solid var(--border-color); + border-radius:7px;color:var(--text-secondary);cursor:pointer;transition:all .25s; + white-space:nowrap;font-family:'Space Mono',monospace;font-size:.78rem;flex-shrink:0; +} +.skill-cat-btn:hover,.quest-tab-btn:hover,.crafting-cat-btn:hover,.shop-cat-btn:hover{border-color:var(--primary-color);color:var(--text-primary)} +.skill-cat-btn.active,.quest-tab-btn.active,.crafting-cat-btn.active,.shop-cat-btn.active{background:var(--gradient-primary);color:var(--bg-primary);border-color:transparent} +.skills-grid{display:grid;grid-template-columns:1fr;gap:.75rem} +.skills-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem;padding:.75rem 1rem;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:10px;flex-wrap:wrap;gap:.5rem} +.skills-header h2{color:var(--primary-color);font-size:1rem;font-weight:600;display:flex;align-items:center;gap:.5rem;margin:0} +.skill-points-display{background:var(--bg-tertiary);padding:.4rem .75rem;border-radius:7px;border:1px solid var(--border-color)} +.skill-points{color:var(--warning-color);font-weight:600;font-size:.95rem} + +/* ── BASE ────────────────────────────────────────────────────────────── */ +.base-container{display:flex;flex-direction:column;gap:.75rem} +.base-view,.base-upgrades{background:var(--card-bg);border:1px solid var(--border-color);border-radius:var(--r);padding:.75rem} +.base-navigation{display:flex;gap:.35rem;margin-bottom:1rem;flex-wrap:wrap} +.base-nav-btn{padding:.4rem .8rem;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:7px;color:var(--text-secondary);cursor:pointer;transition:all .25s;font-size:.78rem;white-space:nowrap;font-family:'Space Mono',monospace} +.base-nav-btn:hover{border-color:var(--primary-color);color:var(--text-primary)} +.base-nav-btn.active{background:var(--gradient-primary);color:var(--bg-primary);border-color:transparent} +.base-rooms{display:grid;grid-template-columns:repeat(auto-fill,minmax(105px,1fr));gap:.5rem} +.room-item{background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:8px;padding:.55rem .4rem;cursor:pointer;transition:all .25s;text-align:center;min-height:70px;display:flex;flex-direction:column;align-items:center;justify-content:center} +.room-item:hover{border-color:var(--primary-color);transform:translateY(-2px)} +.room-item i{font-size:1.2rem;margin-bottom:.3rem;color:var(--primary-color)} +.room-item h4{margin:0;color:var(--text-primary);font-size:.7rem;font-weight:600} +.room-item p{margin:.15rem 0 0;color:var(--text-secondary);font-size:.62rem} + +.starbases-container{display:flex;flex-direction:column;gap:.75rem} +.starbase-section{display:flex;flex-direction:column} +.starbase-section h3{margin:0 0 .6rem;color:var(--text-primary);font-size:1rem;font-weight:600} +.starbase-list,.starbase-shop{background:var(--card-bg);border:1px solid var(--border-color);border-radius:var(--r);padding:.75rem;max-height:45dvh;overflow-y:auto} +.starbase-item{background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:8px;padding:.75rem;margin-bottom:.5rem;cursor:pointer;transition:all .25s;max-height:140px;overflow-y:auto} +.starbase-item:hover{border-color:var(--primary-color)} +.starbase-item h4{margin:0 0 .35rem;color:var(--text-primary);font-size:.85rem} +.starbase-item p{margin:0;color:var(--text-secondary);font-size:.78rem} +.starbase-item .level{color:var(--primary-color);font-weight:600} +.starbase-item .description{margin-top:.25rem;line-height:1.3;max-height:55px;overflow-y:auto;padding-right:.4rem} +.starbase-purchase-list{overflow-y:auto;max-height:40dvh} + +.base-visualization-container{display:flex;flex-direction:column;gap:.75rem} +#baseCanvas{background:var(--card-bg);border:1px solid var(--border-color);border-radius:var(--r);width:100%;height:min(260px,38dvh)} +.base-info-overlay{display:flex;flex-direction:column;gap:.5rem} +.base-stats-overlay{background:var(--card-bg);border:1px solid var(--border-color);border-radius:var(--r);padding:.75rem;max-height:220px;overflow-y:auto} +.base-stats-overlay h3{margin:0 0 .6rem;color:var(--text-primary);font-size:1rem;font-weight:600} +#baseInfoDisplay{color:var(--text-secondary);font-size:.88rem;line-height:1.4} + +.upgrade-list{display:grid;grid-template-columns:1fr 1fr;gap:.6rem;max-height:380px;overflow-y:auto;padding:.3rem} +.upgrade-item{background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:8px;padding:.75rem;cursor:pointer;transition:all .25s} +.upgrade-item:hover{border-color:var(--primary-color);transform:translateY(-2px)} +.upgrade-item h4{margin:0 0 .3rem;color:var(--text-primary);font-size:.82rem} +.upgrade-item p{margin:0;color:var(--text-secondary);font-size:.75rem} +.upgrade-item .cost{margin-top:.3rem;color:var(--primary-color);font-weight:600;font-size:.8rem} + +/* ── QUESTS ──────────────────────────────────────────────────────────── */ +.quests-container{max-width:1200px;margin:0 auto} +.quest-list{display:flex;flex-direction:column;gap:.75rem} +.quest-difficulty{display:flex;gap:2px;font-size:.88rem;color:#ffd700;margin-right:.75rem} +.difficulty-1{color:#4ade80} .difficulty-2{color:#60a5fa} .difficulty-3{color:#f59e0b} .difficulty-4{color:#ef4444} +.quest-header-info{display:flex;align-items:center;gap:.75rem} +.all-objectives-completed{color:#4ade80;font-weight:600;padding:.4rem;background:rgba(74,222,128,.1);border-radius:4px;text-align:center;font-size:.85rem} +.completion-time{font-size:.78rem;color:var(--text-secondary);margin-top:.4rem;text-align:right} +.daily-countdown,.weekly-countdown{margin-bottom:1rem;padding:.65rem;background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:8px} +.countdown-container{display:flex;align-items:center;gap:.4rem;justify-content:center;color:var(--text-primary);font-size:.85rem} +.countdown-container i{color:var(--primary-color)} + +/* ── INVENTORY ───────────────────────────────────────────────────────── */ +.inventory-container{display:flex;flex-direction:column;gap:.75rem} +.inventory-grid{background:var(--card-bg);border:1px solid var(--border-color);border-radius:var(--r);padding:.75rem;overflow-y:auto;max-height:50dvh} +.item-details{background:var(--card-bg);border:1px solid var(--border-color);border-radius:var(--r);padding:.75rem;min-height:110px} +.inventory-main{display:flex;gap:1rem;flex:1;min-height:0;flex-direction:column} +.inventory-section{flex:1;min-height:0;display:flex;flex-direction:column} +.inventory-section h3{margin:0 0 .75rem;color:var(--text-primary);font-size:1rem;font-weight:600} +#inventoryGrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(90px,1fr));gap:.5rem;padding:.35rem} +.inventory-slot{width:100%;aspect-ratio:1;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:8px;display:flex;align-items:center;justify-content:center;position:relative} +.inventory-slot.starbase-bonus-slot{border:2px solid var(--primary-color);background:rgba(0,212,255,.08)} + +/* Equipment */ +.equipment-section{margin-bottom:1.5rem;padding:.75rem;background:var(--bg-secondary);border-radius:10px;border:1px solid var(--border-color)} +.equipment-section h3{margin:0 0 .75rem;color:var(--text-primary);font-size:1rem;font-weight:600} +.equipment-slots{display:grid;grid-template-columns:repeat(auto-fit,minmax(100px,1fr));gap:.75rem} +.equipment-slot{display:flex;flex-direction:column;align-items:center;text-align:center} +.slot-label{font-size:.82rem;color:var(--text-secondary);margin-bottom:.4rem;font-weight:500} +.slot-container{width:70px;height:70px;border:2px solid var(--border-color);border-radius:8px;display:flex;align-items:center;justify-content:center;background:var(--bg-tertiary);transition:all .3s;cursor:pointer} +.slot-container:hover{border-color:var(--primary-color);box-shadow:0 0 10px rgba(0,123,255,.3)} + +/* ── SHOP ────────────────────────────────────────────────────────────── */ +.shop-container{max-width:1200px;margin:0 auto} +.shop-items{display:grid;grid-template-columns:1fr;gap:.75rem} +.shop-item.legacy{background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:8px;padding:1rem;transition:all .3s} +.shop-item.legacy:hover{border-color:var(--primary-color);transform:translateY(-2px);box-shadow:0 5px 15px rgba(0,212,255,.2)} +.shop-item-content{display:flex!important;align-items:flex-start!important;gap:.75rem!important} +.shop-item-image{flex-shrink:0!important;width:64px!important;height:64px!important;border-radius:6px!important;overflow:hidden!important;background:rgba(0,0,0,.3)!important;display:flex!important;align-items:center!important;justify-content:center!important} +.shop-item-name{font-size:1rem!important;font-weight:600!important;color:#00d4ff!important;margin-bottom:4px!important} +.shop-item-description{color:#fff!important;font-size:.85rem!important;margin-bottom:.5rem!important;line-height:1.4!important} +.shop-item-stats{display:flex!important;flex-wrap:wrap!important;gap:.4rem!important;margin-bottom:.5rem!important} +.shop-item-price{font-size:.95rem!important;font-weight:600!important;color:#ffd700!important;margin-bottom:.25rem!important} +.shop-item-rarity{display:inline-block!important;padding:2px 8px!important;border-radius:4px!important;font-size:.75rem!important;font-weight:600!important;text-transform:uppercase!important;margin-bottom:.5rem!important} +.shop-item-rarity.common{background:rgba(128,128,128,.2)!important;color:#808080!important;border:1px solid rgba(128,128,128,.4)!important} +.shop-item-rarity.uncommon{background:rgba(0,255,0,.2)!important;color:#00ff00!important;border:1px solid rgba(0,255,0,.4)!important} +.shop-item-rarity.rare{background:rgba(0,100,255,.2)!important;color:#0064ff!important;border:1px solid rgba(0,100,255,.4)!important} +.shop-item-rarity.epic{background:rgba(128,0,255,.2)!important;color:#8000ff!important;border:1px solid rgba(128,0,255,.4)!important} +.shop-item-rarity.legendary{background:rgba(255,128,0,.2)!important;color:#ff8000!important;border:1px solid rgba(255,128,0,.4)!important} +.shop-item-purchase-btn{width:100%!important;padding:.5rem 1rem!important;background:var(--gradient-primary)!important;color:#fff!important;border:none!important;border-radius:4px!important;cursor:pointer!important;font-weight:600!important;transition:all .3s!important;margin-top:.5rem!important} +.shop-item-purchase-btn:hover:not(.disabled){background:linear-gradient(135deg,#00ffcc,#00ccaa)!important;transform:translateY(-1px)!important} +.shop-item-purchase-btn.disabled{background:rgba(100,100,100,.3)!important;color:#666!important;cursor:not-allowed!important} +.shop-refresh-info{background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:8px;padding:.75rem;margin-bottom:1rem;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:.5rem} +.refresh-info-left{display:flex;flex-direction:column;gap:.35rem} +.refresh-countdown,.purchase-limit-info{display:flex;align-items:center;gap:.4rem;color:var(--text-secondary);font-size:.82rem} +.refresh-countdown i{color:var(--primary-color)} +.purchase-limit-info i{color:#ffd700} +.refresh-shop-btn{background:var(--gradient-primary);color:#fff;border:none;border-radius:6px;padding:.4rem .85rem;font-size:.82rem;cursor:pointer;transition:all .3s;display:flex;align-items:center;gap:.4rem;white-space:nowrap} +.refresh-shop-btn:hover{transform:translateY(-1px);box-shadow:0 4px 12px rgba(0,212,255,.3)} + +/* ── CRAFTING ────────────────────────────────────────────────────────── */ +.crafting-container{max-width:1200px;margin:0 auto} +.crafting-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem;padding:.75rem 1rem;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:10px;flex-wrap:wrap;gap:.5rem} +.crafting-header h2{color:var(--primary-color);font-size:1rem;font-weight:600;display:flex;align-items:center;gap:.5rem;margin:0} +.crafting-info{display:flex;gap:.75rem;align-items:center;flex-wrap:wrap} +.crafting-level,.crafting-experience{background:var(--bg-tertiary);padding:.38rem .7rem;border-radius:7px;border:1px solid var(--border-color);display:flex;align-items:center;gap:.4rem;color:var(--text-primary);font-size:.78rem} +.crafting-level i,.crafting-experience i{color:var(--primary-color)} +.crafting-content{display:flex;flex-direction:column;gap:.75rem} +.crafting-sidebar{background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:10px;padding:.75rem} +.crafting-categories h3{color:var(--primary-color);font-size:1rem;margin-bottom:.75rem;display:flex;align-items:center;gap:.5rem} +.crafting-main{display:flex;flex-direction:column;gap:.75rem} +.recipe-list{background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:10px;padding:.75rem;overflow-y:auto;max-height:45dvh} +.crafting-details{background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:10px;padding:.75rem} +.crafting-grid{display:grid;grid-template-columns:1fr;gap:.75rem} +.selected-recipe{display:flex;flex-direction:column;align-items:center;text-align:center;color:var(--text-muted)} +.selected-recipe i{font-size:2.5rem;margin-bottom:.75rem;opacity:.5} +.selected-recipe h3{margin-bottom:.5rem;color:var(--text-secondary)} + +/* Recipe items */ +.recipe-item{background:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:8px;padding:.85rem;margin-bottom:.75rem;cursor:pointer;transition:all .3s;position:relative;overflow:hidden} +.recipe-item:hover{background:var(--hover-bg);border-color:var(--primary-color);transform:translateY(-2px);box-shadow:0 4px 12px rgba(0,212,255,.2)} +.recipe-item.selected{background:var(--gradient-primary);border-color:var(--primary-color);box-shadow:0 4px 12px rgba(0,212,255,.3)} +.recipe-item.locked{opacity:.55;cursor:not-allowed} +.recipe-item.locked:hover{transform:none;border-color:var(--border-color)} +.recipe-item.can-craft{border-color:var(--success-color)} +.recipe-item.can-craft:hover{box-shadow:0 4px 12px rgba(0,255,136,.2)} +.recipe-item.missing-materials{opacity:.7;border-color:var(--warning-color)} +.recipe-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:.5rem} +.recipe-header h4{color:var(--text-primary);font-size:.95rem} +.recipe-level{background:var(--bg-primary);color:var(--warning-color);padding:3px 7px;border-radius:4px;font-size:.75rem;font-weight:600} +.recipe-description{color:var(--text-secondary);font-size:.82rem;margin-bottom:.6rem} +.recipe-materials{display:flex;gap:.4rem;flex-wrap:wrap} +.material-tag{background:var(--bg-primary);color:var(--text-muted);padding:2px 6px;border-radius:4px;font-size:.72rem} +.material-item{display:flex;justify-content:space-between;align-items:center;padding:5px 0;border-bottom:1px solid rgba(255,255,255,.08);font-size:.82rem} +.material-item:last-child{border-bottom:none} +.material-item.missing{color:var(--error-color)} +.material-name{color:var(--text-secondary)} .material-quantity{font-size:.82rem;font-weight:600;color:var(--text-primary)} +.material-item.missing .material-quantity{color:var(--error-color);font-weight:600} +.missing-materials-text{color:var(--error-color);font-size:.78rem;margin-top:.5rem;padding:.5rem;background:rgba(255,51,102,.1);border-radius:4px;border:1px solid rgba(255,51,102,.3)} +.recipe-time{display:flex;align-items:center;gap:.35rem;color:var(--text-muted);font-size:.78rem;margin-top:.4rem} +.recipe-time i{color:var(--primary-color)} + +/* ── CONSOLE WINDOW ──────────────────────────────────────────────────── */ +.console-window{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:min(580px,95vw);height:min(380px,75dvh);background:var(--bg-secondary);border:2px solid var(--primary-color);border-radius:8px;box-shadow:0 10px 30px rgba(0,0,0,.8);display:none;flex-direction:column;z-index:10000;font-family:'Courier New',monospace} +.console-header{background:var(--gradient-primary);padding:10px 15px;border-radius:6px 6px 0 0;display:flex;justify-content:space-between;align-items:center;font-weight:bold;font-size:14px} +.console-close{background:none;border:none;color:var(--text-primary);font-size:18px;cursor:pointer;padding:0;width:24px;height:24px;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:background .3s} +.console-close:hover{background:rgba(255,255,255,.2)} +.console-content{flex:1;display:flex;flex-direction:column;overflow:hidden} +.console-output{flex:1;padding:12px;overflow-y:auto;background:var(--bg-primary);color:var(--text-primary);font-size:12px;line-height:1.4;border-bottom:1px solid var(--border-color)} +.console-line{margin-bottom:4px;word-wrap:break-word} +.console-line.console-info{color:var(--primary-color)} .console-line.console-success{color:var(--success-color)} .console-line.console-error{color:var(--error-color)} .console-line.console-warning{color:var(--warning-color)} +.console-input-container{padding:10px;background:var(--bg-secondary)} +.console-input{width:100%;background:var(--bg-primary);border:1px solid var(--border-color);color:var(--text-primary);padding:7px 11px;font-family:'Courier New',monospace;font-size:12px;border-radius:4px;outline:none} +.console-input:focus{border-color:var(--primary-color);box-shadow:0 0 0 2px rgba(0,212,255,.2)} + +/* ══════════════════════════════════════════════════════════════════════ + ANIMATIONS + ══════════════════════════════════════════════════════════════════════ */ +@keyframes pulse{0%,100%{opacity:1}50%{opacity:.5}} +@keyframes progressShine{0%{transform:translateX(-100%)}100%{transform:translateX(100%)}} + +/* ══════════════════════════════════════════════════════════════════════ + TABLET ≥ 640px + ══════════════════════════════════════════════════════════════════════ */ +@media(min-width:640px){ + :root{--header-h:56px;--pg:1rem} + html{font-size:15px} + .player-title{display:inline} + .player-name{max-width:22ch} + .dashboard-grid{grid-template-columns:repeat(2,1fr)} + .skills-grid{grid-template-columns:repeat(2,1fr)} + .shop-items{grid-template-columns:repeat(2,1fr)} + .crafting-grid{grid-template-columns:repeat(2,1fr)} + #inventoryGrid{grid-template-columns:repeat(auto-fill,minmax(110px,1fr))} + .starbases-container{flex-direction:row} + .starbase-section{flex:1} + .base-rooms{grid-template-columns:repeat(auto-fill,minmax(120px,1fr))} + .upgrade-list{grid-template-columns:repeat(3,1fr)} + .nav-drawer-grid{grid-template-columns:repeat(5,1fr)} +} + +/* ══════════════════════════════════════════════════════════════════════ + DESKTOP ≥ 1024px — switch to top nav + ══════════════════════════════════════════════════════════════════════ */ +@media(min-width:1024px){ + :root{--header-h:60px;--pg:1rem} + html{font-size:15px} + .main-nav{display:flex} + .bottom-nav,.bottom-nav-more,.nav-drawer,.nav-drawer-overlay{display:none!important} + .main-content{padding-bottom:var(--pg)} + .player-title{display:inline} + .player-name{max-width:26ch;font-size:.95rem} + .resource{font-size:.85rem} + .dashboard-grid{grid-template-columns:repeat(3,1fr);gap:1rem} + .dungeons-container{flex-direction:row} + .dungeon-selector{width:270px;flex-shrink:0;max-height:calc(100dvh - 160px);overflow-y:auto} + .dungeon-view{flex:1;min-height:300px} + .base-container{flex-direction:row} + .base-view{flex:1} + .base-upgrades{width:270px;flex-shrink:0} + .skills-grid{grid-template-columns:repeat(3,1fr)} + .shop-items{grid-template-columns:repeat(3,1fr)} + .crafting-grid{grid-template-columns:repeat(3,1fr)} + .crafting-content{flex-direction:row} + .crafting-sidebar{width:210px;flex-shrink:0} + .crafting-main{flex-direction:row;flex:1} + .recipe-list{flex:1;max-height:none} + .crafting-details{width:290px;flex-shrink:0} + .inventory-container{flex-direction:row} + .inventory-main{flex-direction:row} + .inventory-grid{flex:1;max-height:none} + .item-details{width:270px;flex-shrink:0} + .starbases-container{flex-direction:row} + .starbase-list,.starbase-shop{max-height:calc(100dvh - 250px)} + .base-visualization-container{flex-direction:row} + #baseCanvas{flex:1;height:auto;min-height:280px} + .base-info-overlay{width:270px;flex-shrink:0} + .base-stats-overlay{max-height:calc(100dvh - 310px)} + .upgrade-list{grid-template-columns:repeat(auto-fill,minmax(170px,1fr))} + #inventoryGrid{grid-template-columns:repeat(auto-fill,minmax(130px,1fr))} + .server-confirmation,.save-confirmation,.options-grid{flex-direction:row;gap:2rem} + .confirm-actions-left,.confirm-actions-right,.options-left,.options-right{flex-direction:column;gap:.75rem;min-width:175px} + .confirm-actions-left .btn-large,.confirm-actions-right .btn-large,.options-left .btn-large,.options-right .btn-large{width:100%;flex:none;min-width:auto} + .base-navigation{justify-content:center} + .skill-categories,.quest-tabs,.crafting-categories,.shop-categories{justify-content:center;flex-wrap:nowrap} + .player-stats-grid{grid-template-columns:repeat(2,1fr)} +} + +/* ══════════════════════════════════════════════════════════════════════ + WIDE ≥ 1280px + ══════════════════════════════════════════════════════════════════════ */ +@media(min-width:1280px){ + .dashboard-grid{grid-template-columns:repeat(4,1fr)} + .skills-grid{grid-template-columns:repeat(4,1fr)} +} diff --git a/Client-Server/styles/main.css b/Galaxy-Strike-Online-main/Client/styles/main.css.bak similarity index 98% rename from Client-Server/styles/main.css rename to Galaxy-Strike-Online-main/Client/styles/main.css.bak index 2b6e807..2da022d 100644 --- a/Client-Server/styles/main.css +++ b/Galaxy-Strike-Online-main/Client/styles/main.css.bak @@ -1196,6 +1196,13 @@ body { .player-info { display: flex; flex-direction: column; + gap: 0.2rem; +} + +.player-info > div { + display: flex; + align-items: center; + gap: 0.3rem; } .player-name { @@ -1203,6 +1210,18 @@ body { color: var(--text-primary); } +.player-title { + font-weight: 500; + color: var(--text-secondary); + font-size: 0.9rem; +} + +.player-username { + font-weight: 600; + color: var(--accent-color); + font-size: 0.85rem; +} + .player-level { font-size: 0.8rem; color: var(--primary-color); @@ -1381,6 +1400,20 @@ body { gap: 0.5rem; } +.player-stats-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 0.5rem; + max-height: 400px; + overflow-y: auto; +} + +@media (max-width: 768px) { + .player-stats-grid { + grid-template-columns: 1fr; + } +} + .stat { display: flex; justify-content: space-between; diff --git a/Galaxy-Strike-Online-main/Client/styles/tables.css b/Galaxy-Strike-Online-main/Client/styles/tables.css new file mode 100644 index 0000000..2ac7c89 --- /dev/null +++ b/Galaxy-Strike-Online-main/Client/styles/tables.css @@ -0,0 +1,185 @@ +/* ═══════════════════════════════════════════════════════════════════════ + Galaxy Strike Online — Table & Ship Styles (mobile-first) + ═══════════════════════════════════════════════════════════════════════ */ + +/* ── Base table styles ───────────────────────────────────────────────── */ +.dungeon-table,.skills-table,.base-rooms-table,.base-upgrades-table, +.ship-gallery-table,.starbase-management-table,.starbase-shop-table, +.quests-table,.inventory-table,.shop-table { + width:100%;border-collapse:collapse;background:var(--card-bg); + border-radius:8px;overflow:hidden;box-shadow:0 4px 20px rgba(0,0,0,.25); + margin:8px 0;font-size:.8rem; +} +.dungeon-table th,.skills-table th,.base-rooms-table th,.base-upgrades-table th, +.ship-gallery-table th,.starbase-management-table th,.starbase-shop-table th, +.quests-table th,.inventory-table th,.shop-table th { + background:var(--gradient-primary);color:var(--text-primary); + padding:10px 12px;text-align:left;font-weight:600;font-size:.78rem; + border-bottom:2px solid rgba(255,255,255,.1); +} +.dungeon-table td,.skills-table td,.base-rooms-table td,.base-upgrades-table td, +.ship-gallery-table td,.starbase-management-table td,.starbase-shop-table td, +.quests-table td,.inventory-table td,.shop-table td { + padding:10px 12px;border-bottom:1px solid rgba(255,255,255,.07); + color:#e0e0e0;font-size:.78rem; +} +.dungeon-table tr:hover,.skills-table tr:hover,.base-rooms-table tr:hover, +.base-upgrades-table tr:hover,.ship-gallery-table tr:hover, +.starbase-management-table tr:hover,.starbase-shop-table tr:hover, +.quests-table tr:hover,.inventory-table tr:hover,.shop-table tr:hover { + background:rgba(102,126,234,.1);transition:background .25s; +} + +/* Difficulty colors */ +.dungeon-table .difficulty-easy{color:#00ff00} +.dungeon-table .difficulty-medium{color:#ffff00} +.dungeon-table .difficulty-hard{color:#ff9900} +.dungeon-table .difficulty-extreme{color:#ff0000} + +/* Skills table */ +.skills-table .skill-level{font-weight:bold;color:#667eea} +.skills-table .skill-progress{width:80px;height:6px;background:rgba(255,255,255,.1);border-radius:3px;overflow:hidden} +.skills-table .progress-fill{height:100%;background:linear-gradient(90deg,#667eea,#764ba2);transition:width .3s} + +/* Base tables */ +.base-rooms-table .room-status-active{color:#00ff00} +.base-rooms-table .room-status-inactive{color:#ff0000} +.base-rooms-table .room-status-upgrading{color:#ffff00} +.base-upgrades-table .upgrade-level{font-weight:bold;color:#667eea} + +/* Action buttons in tables */ +.dungeon-table .btn-action,.skills-table .btn-action,.base-rooms-table .btn-action, +.base-upgrades-table .btn-action,.ship-gallery-table .btn-action, +.starbase-management-table .btn-action,.starbase-shop-table .btn-action, +.quests-table .btn-action,.inventory-table .btn-action,.shop-table .btn-action { + padding:5px 10px;border:none;border-radius:4px;cursor:pointer; + font-size:.72rem;font-weight:600;transition:all .25s;text-transform:uppercase; + min-height:32px; +} +.btn-action.btn-primary{background:linear-gradient(135deg,#667eea,#764ba2);color:#fff} +.btn-action.btn-primary:hover{background:linear-gradient(135deg,#764ba2,#667eea);transform:translateY(-1px)} +.btn-action.btn-secondary{background:rgba(255,255,255,.1);color:#fff;border:1px solid rgba(255,255,255,.2)} +.btn-action.btn-secondary:hover{background:rgba(255,255,255,.2);transform:translateY(-1px)} +.btn-action.btn-success{background:linear-gradient(135deg,#00ff00,#00cc00);color:#fff} +.btn-action.btn-danger{background:linear-gradient(135deg,#ff0000,#cc0000);color:#fff} + +/* Specialty table cells */ +.starbase-management-table .starbase-level{font-weight:bold;color:#667eea} +.starbase-shop-table .starbase-cost{font-weight:bold;color:#ffd700} +.quests-table .quest-type-main{color:#667eea} .quests-table .quest-type-daily{color:#00ff00} +.quests-table .quest-type-procedural{color:#ff9900} .quests-table .quest-type-completed{color:#888} +.quests-table .quest-type-failed{color:#ff0000} +.quests-table .quest-progress,.inventory-table .item-stats{font-size:.72rem} +.inventory-table .item-rarity-common{color:#888} .inventory-table .item-rarity-uncommon{color:#00ff00} +.inventory-table .item-rarity-rare{color:#0088ff} .inventory-table .item-rarity-epic{color:#8833ff} +.inventory-table .item-rarity-legendary{color:#ff8800} +.shop-table .item-price{font-weight:bold;color:#ffd700} +.shop-table .item-description{font-size:.72rem;color:#ccc;max-width:180px} + +/* ── Ship grid ───────────────────────────────────────────────────────── */ +.ship-grid{display:grid;grid-template-columns:1fr;gap:12px;padding:12px 0} +.ship-card{ + background:var(--card-bg);border-radius:12px;padding:12px; + border:2px solid var(--primary-color);box-shadow:0 4px 20px rgba(0,0,0,.3); + transition:all .25s;position:relative;overflow:hidden; +} +.ship-card:hover{transform:translateY(-4px);box-shadow:0 8px 28px rgba(0,212,255,.35)} +.ship-card.active{border-color:var(--success-color);box-shadow:0 6px 22px rgba(0,255,136,.18)} +.ship-card.active::before{content:"ACTIVE";position:absolute;top:8px;right:8px;background:var(--gradient-secondary);color:var(--text-primary);padding:3px 7px;border-radius:4px;font-size:9px;font-weight:bold;text-transform:uppercase;letter-spacing:.5px} + +.ship-card-header{display:flex;flex-direction:row;align-items:center;gap:10px;margin-bottom:10px} +.ship-card-image{width:70px;height:70px;border-radius:8px;object-fit:cover;border:2px solid var(--primary-color);flex-shrink:0} +.ship-card-info{flex:1;min-width:0} +.ship-card-rarity{color:var(--text-secondary);font-size:.72rem;font-weight:bold;text-transform:uppercase;letter-spacing:.5px;padding:3px 7px;border-radius:4px;background:var(--hover-bg);border:1px solid var(--border-color);display:inline-block} +.ship-card-rarity.common{color:#888;border-color:#888} +.ship-card-rarity.rare{color:var(--primary-color);border-color:var(--primary-color)} +.ship-card-rarity.epic{color:var(--accent-color);border-color:var(--accent-color)} +.ship-card-rarity.legendary{color:var(--warning-color);border-color:var(--warning-color)} + +.ship-card-stats{display:grid;grid-template-columns:1fr 1fr;gap:6px;margin-bottom:10px} +.ship-card-stat{display:flex;justify-content:space-between;align-items:center;padding:5px 8px;background:rgba(255,255,255,.04);border-radius:4px;border:1px solid rgba(255,255,255,.08)} +.ship-card-stat .stat-label{color:var(--text-muted);font-size:.68rem;text-transform:uppercase;letter-spacing:.5px} +.ship-card-stat .stat-value{color:var(--text-secondary);font-weight:bold;font-size:.72rem} +.ship-card-stat .stat-value.health{color:var(--success-color)} .ship-card-stat .stat-value.attack{color:var(--warning-color)} +.ship-card-stat .stat-value.defense{color:var(--accent-color)} .ship-card-stat .stat-value.speed{color:var(--secondary-color)} + +.ship-card-actions{display:flex;gap:8px} +.ship-card-actions .btn-action{flex:1;padding:7px 10px;border:none;border-radius:6px;cursor:pointer;font-size:.72rem;font-weight:600;transition:all .25s;text-transform:uppercase;min-height:36px;-webkit-tap-highlight-color:transparent} +.btn-action.btn-switch{background:var(--gradient-primary);color:var(--text-primary)} +.btn-action.btn-switch:hover{background:var(--gradient-secondary);transform:translateY(-2px)} +.btn-action.btn-switch:disabled{background:var(--text-muted);cursor:not-allowed;transform:none} +.btn-action.btn-upgrade,.btn-action.btn-repair{background:var(--gradient-secondary);color:var(--text-primary)} +.btn-action.btn-upgrade:hover,.btn-action.btn-repair:hover{background:var(--gradient-primary);transform:translateY(-2px)} + +/* Ship layout (current ship + grid) */ +.ship-layout{display:flex;flex-direction:column;gap:1rem;margin-top:.75rem} +.current-ship-section{background:var(--card-bg);border-radius:8px;padding:1rem;border:2px solid var(--primary-color)} +.ship-grid-section{flex:1;min-width:0} +.ship-grid-section h4,.current-ship-section h4{color:var(--primary-color);margin-bottom:.75rem;font-size:.92rem;text-transform:uppercase;letter-spacing:1px} +.current-ship-section h4{text-align:center} +.current-ship-display{display:flex;flex-direction:column;align-items:center;gap:1rem;text-align:center} +.current-ship-image img{width:100px;height:100px;object-fit:cover;border-radius:8px;border:2px solid var(--primary-color);box-shadow:0 4px 15px rgba(0,212,255,.3)} +.current-ship-details{flex:1;text-align:center;min-width:0} +.current-ship-details h5{color:var(--text-primary);margin-bottom:.75rem;font-size:1.1rem;font-weight:bold} +.current-ship-stats{display:grid;grid-template-columns:1fr 1fr;gap:8px} +.ship-stat{display:flex;justify-content:space-between;align-items:center;padding:6px 10px;background:var(--hover-bg);border-radius:4px;border:1px solid var(--border-color)} +.ship-stat .stat-label{color:var(--text-muted);font-size:.7rem;text-transform:uppercase;letter-spacing:.5px} +.ship-stat .stat-value{color:var(--text-secondary);font-weight:bold;font-size:.82rem} +.ship-stat .stat-value.health{color:var(--success-color)} .ship-stat .stat-value.attack{color:var(--warning-color)} +.ship-stat .stat-value.defense{color:var(--accent-color)} .ship-stat .stat-value.speed{color:var(--secondary-color)} +.ship-table-section{margin-top:1.5rem} +.ship-table-section h4{color:#667eea;margin-bottom:.75rem;font-size:.92rem;text-transform:uppercase;letter-spacing:1px} + +/* ── Console window ──────────────────────────────────────────────────── */ +.console-window{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:min(580px,96vw);height:min(380px,70dvh);background:var(--bg-secondary);border:2px solid var(--primary-color);border-radius:8px;box-shadow:0 8px 30px rgba(0,0,0,.8);z-index:10000;display:none;flex-direction:column;font-family:'Courier New',monospace} +.console-header{background:var(--gradient-primary);color:var(--text-primary);padding:10px 15px;border-radius:6px 6px 0 0;display:flex;justify-content:space-between;align-items:center;font-weight:bold;font-size:13px} +.console-close{background:none;border:none;color:var(--text-primary);font-size:18px;cursor:pointer;padding:0;width:24px;height:24px;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:background .2s} +.console-close:hover{background:rgba(255,255,255,.2)} +.console-content{flex:1;display:flex;flex-direction:column;overflow:hidden} +.console-output{flex:1;padding:12px;overflow-y:auto;background:var(--bg-primary);color:var(--text-primary);font-size:12px;line-height:1.4;border-bottom:1px solid var(--border-color)} +.console-line{margin-bottom:4px;word-wrap:break-word} +.console-error{color:var(--error-color)} .console-success{color:var(--success-color)} +.console-warning{color:var(--warning-color)} .console-info{color:var(--primary-color)} +.console-input-container{padding:10px;background:var(--bg-secondary)} +.console-input{width:100%;background:var(--bg-primary);border:1px solid var(--border-color);color:var(--text-primary);padding:7px 11px;font-family:'Courier New',monospace;font-size:12px;border-radius:4px;outline:none} +.console-input:focus{border-color:var(--primary-color);box-shadow:0 0 0 2px rgba(0,212,255,.2)} +.console-input::placeholder{color:var(--text-muted)} + +/* ══════════════════════════════════════════════════════════════════════ + TABLET ≥ 640px + ══════════════════════════════════════════════════════════════════════ */ +@media(min-width:640px){ + .ship-grid{grid-template-columns:repeat(2,1fr)} + .ship-layout{flex-direction:row} + .current-ship-section{flex:0 0 320px} + .current-ship-display{flex-direction:row;align-items:flex-start;text-align:left} + .current-ship-details{text-align:left} + .current-ship-details h5{text-align:left} + .current-ship-stats{grid-template-columns:1fr} +} + +/* ══════════════════════════════════════════════════════════════════════ + DESKTOP ≥ 1024px + ══════════════════════════════════════════════════════════════════════ */ +@media(min-width:1024px){ + .ship-grid{grid-template-columns:repeat(auto-fill,minmax(200px,1fr))} + .ship-card-header{flex-direction:column;align-items:center;text-align:center} + .ship-card-image{width:80px;height:80px} + .current-ship-section{flex:0 0 380px} + .current-ship-stats{grid-template-columns:1fr 1fr} +} + +/* ── Responsive table fallback — horizontal scroll ───────────────────── */ +@media(max-width:639px){ + .dungeon-table,.skills-table,.base-rooms-table,.base-upgrades-table, + .ship-gallery-table,.starbase-management-table,.starbase-shop-table, + .quests-table,.inventory-table,.shop-table{ + display:block;overflow-x:auto;-webkit-overflow-scrolling:touch;white-space:nowrap; + } + .dungeon-table th,.skills-table th,.base-rooms-table th,.base-upgrades-table th, + .ship-gallery-table th,.starbase-management-table th,.starbase-shop-table th, + .quests-table th,.inventory-table th,.shop-table th{padding:8px 10px;font-size:.72rem} + .dungeon-table td,.skills-table td,.base-rooms-table td,.base-upgrades-table td, + .ship-gallery-table td,.starbase-management-table td,.starbase-shop-table td, + .quests-table td,.inventory-table td,.shop-table td{padding:8px 10px;font-size:.72rem} +} diff --git a/Client-Server/styles/tables.css b/Galaxy-Strike-Online-main/Client/styles/tables.css.bak similarity index 100% rename from Client-Server/styles/tables.css rename to Galaxy-Strike-Online-main/Client/styles/tables.css.bak diff --git a/Client-Server/assets/textures/armors/basic_armor.png b/Galaxy-Strike-Online-main/GameServer/assets/images/armors/basic_armor.png similarity index 100% rename from Client-Server/assets/textures/armors/basic_armor.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/armors/basic_armor.png diff --git a/Client-Server/assets/textures/armors/heavy_armor.png b/Galaxy-Strike-Online-main/GameServer/assets/images/armors/heavy_armor.png similarity index 100% rename from Client-Server/assets/textures/armors/heavy_armor.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/armors/heavy_armor.png diff --git a/Client-Server/assets/textures/armors/medium_armor.png b/Galaxy-Strike-Online-main/GameServer/assets/images/armors/medium_armor.png similarity index 100% rename from Client-Server/assets/textures/armors/medium_armor.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/armors/medium_armor.png diff --git a/Client-Server/assets/textures/base/command_center.png b/Galaxy-Strike-Online-main/GameServer/assets/images/base/command_center.png similarity index 100% rename from Client-Server/assets/textures/base/command_center.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/base/command_center.png diff --git a/Client-Server/assets/textures/base/mining_facility.png b/Galaxy-Strike-Online-main/GameServer/assets/images/base/mining_facility.png similarity index 100% rename from Client-Server/assets/textures/base/mining_facility.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/base/mining_facility.png diff --git a/Client-Server/assets/textures/items/bandages.png b/Galaxy-Strike-Online-main/GameServer/assets/images/items/consumables/bandages.png similarity index 100% rename from Client-Server/assets/textures/items/bandages.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/items/consumables/bandages.png diff --git a/Client-Server/assets/textures/items/health_pack.png b/Galaxy-Strike-Online-main/GameServer/assets/images/items/consumables/health_pack.png similarity index 100% rename from Client-Server/assets/textures/items/health_pack.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/items/consumables/health_pack.png diff --git a/Client-Server/assets/textures/items/mega_health_pack.png b/Galaxy-Strike-Online-main/GameServer/assets/images/items/consumables/mega_health_pack.png similarity index 100% rename from Client-Server/assets/textures/items/mega_health_pack.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/items/consumables/mega_health_pack.png diff --git a/Galaxy-Strike-Online-main/GameServer/assets/images/items/cosmetics/temp b/Galaxy-Strike-Online-main/GameServer/assets/images/items/cosmetics/temp new file mode 100644 index 0000000..e69de29 diff --git a/Client-Server/assets/textures/items/advanced_circuitboard.png b/Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/advanced_circuitboard.png similarity index 100% rename from Client-Server/assets/textures/items/advanced_circuitboard.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/advanced_circuitboard.png diff --git a/Client-Server/assets/textures/items/advanced_component.png b/Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/advanced_component.png similarity index 100% rename from Client-Server/assets/textures/items/advanced_component.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/advanced_component.png diff --git a/Client-Server/assets/textures/items/advanced_components.png b/Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/advanced_components.png similarity index 100% rename from Client-Server/assets/textures/items/advanced_components.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/advanced_components.png diff --git a/Client-Server/assets/textures/items/basic_circuitboard.png b/Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/basic_circuitboard.png similarity index 100% rename from Client-Server/assets/textures/items/basic_circuitboard.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/basic_circuitboard.png diff --git a/Client-Server/assets/textures/items/battery.png b/Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/battery.png similarity index 100% rename from Client-Server/assets/textures/items/battery.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/battery.png diff --git a/Client-Server/assets/textures/items/common_circuitboard.png b/Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/common_circuitboard.png similarity index 100% rename from Client-Server/assets/textures/items/common_circuitboard.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/common_circuitboard.png diff --git a/Client-Server/assets/textures/items/copper_ore.png b/Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/copper_ore.png similarity index 100% rename from Client-Server/assets/textures/items/copper_ore.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/copper_ore.png diff --git a/Client-Server/assets/textures/items/copper_wire.png b/Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/copper_wire.png similarity index 100% rename from Client-Server/assets/textures/items/copper_wire.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/copper_wire.png diff --git a/Client-Server/assets/textures/items/energy_crystal.png b/Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/energy_crystal.png similarity index 100% rename from Client-Server/assets/textures/items/energy_crystal.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/energy_crystal.png diff --git a/Client-Server/assets/textures/items/herbs.png b/Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/herbs.png similarity index 100% rename from Client-Server/assets/textures/items/herbs.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/herbs.png diff --git a/Client-Server/assets/textures/items/iron_ore.png b/Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/iron_ore.png similarity index 100% rename from Client-Server/assets/textures/items/iron_ore.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/iron_ore.png diff --git a/Client-Server/assets/textures/items/leather.png b/Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/leather.png similarity index 100% rename from Client-Server/assets/textures/items/leather.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/leather.png diff --git a/Client-Server/assets/textures/items/stell_plate.png b/Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/stell_plate.png similarity index 100% rename from Client-Server/assets/textures/items/stell_plate.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/stell_plate.png diff --git a/Client-Server/assets/textures/items/tin_bar.png b/Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/tin_bar.png similarity index 100% rename from Client-Server/assets/textures/items/tin_bar.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/items/materials/tin_bar.png diff --git a/Client-Server/assets/textures/ships/heavy_cruiser.png b/Galaxy-Strike-Online-main/GameServer/assets/images/ships/heavy_cruiser.png similarity index 100% rename from Client-Server/assets/textures/ships/heavy_cruiser.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/ships/heavy_cruiser.png diff --git a/Client-Server/assets/textures/ships/heavy_destroyer.png b/Galaxy-Strike-Online-main/GameServer/assets/images/ships/heavy_destroyer.png similarity index 100% rename from Client-Server/assets/textures/ships/heavy_destroyer.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/ships/heavy_destroyer.png diff --git a/Client-Server/assets/textures/ships/light_destroyer.png b/Galaxy-Strike-Online-main/GameServer/assets/images/ships/light_destroyer.png similarity index 100% rename from Client-Server/assets/textures/ships/light_destroyer.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/ships/light_destroyer.png diff --git a/Client-Server/assets/textures/ships/starter_cruiser.png b/Galaxy-Strike-Online-main/GameServer/assets/images/ships/starter_cruiser.png similarity index 100% rename from Client-Server/assets/textures/ships/starter_cruiser.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/ships/starter_cruiser.png diff --git a/Galaxy-Strike-Online-main/GameServer/assets/images/ui/icons/temp b/Galaxy-Strike-Online-main/GameServer/assets/images/ui/icons/temp new file mode 100644 index 0000000..e69de29 diff --git a/Client-Server/assets/textures/missing-texture.png b/Galaxy-Strike-Online-main/GameServer/assets/images/ui/placeholder.png similarity index 100% rename from Client-Server/assets/textures/missing-texture.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/ui/placeholder.png diff --git a/Client-Server/assets/textures/weapons/laser_pistol.png b/Galaxy-Strike-Online-main/GameServer/assets/images/weapons/laser_pistol.png similarity index 100% rename from Client-Server/assets/textures/weapons/laser_pistol.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/weapons/laser_pistol.png diff --git a/Client-Server/assets/textures/weapons/laser_sniper_rifle.png b/Galaxy-Strike-Online-main/GameServer/assets/images/weapons/laser_sniper_rifle.png similarity index 100% rename from Client-Server/assets/textures/weapons/laser_sniper_rifle.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/weapons/laser_sniper_rifle.png diff --git a/Client-Server/assets/textures/weapons/starter_blaster.png b/Galaxy-Strike-Online-main/GameServer/assets/images/weapons/starter_blaster.png similarity index 100% rename from Client-Server/assets/textures/weapons/starter_blaster.png rename to Galaxy-Strike-Online-main/GameServer/assets/images/weapons/starter_blaster.png diff --git a/Galaxy-Strike-Online-main/GameServer/config/database.js b/Galaxy-Strike-Online-main/GameServer/config/database.js new file mode 100644 index 0000000..82e0e3d --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/config/database.js @@ -0,0 +1,17 @@ +const mongoose = require('mongoose'); +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); + } +}; + +module.exports = connectDB; diff --git a/Galaxy-Strike-Online-main/GameServer/create_placeholders.js b/Galaxy-Strike-Online-main/GameServer/create_placeholders.js new file mode 100644 index 0000000..cf7989d --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/create_placeholders.js @@ -0,0 +1,191 @@ +/** + * Create placeholder images for existing items in ItemSystem + */ + +const fs = require('fs'); +const path = require('path'); + +// Create directories if they don't exist +const directories = [ + 'assets/images/ships', + 'assets/images/weapons', + 'assets/images/armors', + 'assets/images/items/materials', + 'assets/images/items/consumables', + 'assets/images/items/cosmetics', + 'assets/images/ui' +]; + +directories.forEach(dir => { + const fullPath = path.join(__dirname, dir); + if (!fs.existsSync(fullPath)) { + fs.mkdirSync(fullPath, { recursive: true }); + console.log(`Created directory: ${dir}`); + } +}); + +// Create a simple colored rectangle as placeholder +function createPlaceholder(width, height, color, text) { + // Create a simple 1x1 pixel colored PNG (base64 encoded) + // This is a minimal PNG with transparency + const createColorPNG = (r, g, b) => { + return Buffer.from([ + 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, // PNG signature + 0x00, 0x00, 0x00, 0x0D, // IHDR chunk length + 0x49, 0x48, 0x44, 0x52, // IHDR + width >> 24, width >> 16, width >> 8, width, // width + height >> 24, height >> 16, height >> 8, height, // height + 0x08, 0x02, 0x00, 0x00, 0x00, // bit depth, color type, compression, filter, interlace + 0x4B, 0x6D, 0x29, 0xDC, // CRC + 0x00, 0x00, 0x00, 0x0C, // IDAT chunk length + 0x49, 0x44, 0x41, 0x54, // IDAT + 0x08, 0x99, 0x01, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, // compressed data + 0x00, 0x00, 0x00, 0x00, // IEND chunk length + 0x49, 0x45, 0x4E, 0x44, // IEND + 0xAE, 0x42, 0x60, 0x82 // CRC + ]); + }; + + // For simplicity, let's copy existing images or create basic placeholders + return createColorPNG( + color === 'blue' ? 52 : color === 'red' ? 231 : color === 'green' ? 39 : 149, + color === 'blue' ? 152 : color === 'red' ? 76 : color === 'green' ? 174 : 165, + color === 'blue' ? 219 : color === 'red' ? 60 : color === 'green' ? 96 : 166 + ); +} + +// Items from your ItemSystem +const items = { + ships: [ + 'starter_cruiser_common', + 'starter_cruiser_uncommon', + 'starter_cruiser_rare', + 'interceptor_common', + 'interceptor_uncommon' + ], + weapons: [ + 'laser_pistol_common', + 'laser_pistol_uncommon', + 'laser_pistol_rare', + 'plasma_rifle_common', + 'plasma_rifle_uncommon', + 'plasma_rifle_rare' + ], + armors: [ + 'light_armor_common', + 'light_armor_uncommon', + 'light_armor_rare', + 'medium_armor_common', + 'medium_armor_uncommon', + 'medium_armor_rare' + ], + materials: [ + 'steel_plating', + 'energy_crystal', + 'rare_metal', + 'quantum_core', + 'nanomaterials', + 'dark_matter_fragment' + ], + consumables: [ + 'health_pack', + 'energy_boost', + 'shield_recharge', + 'repair_kit', + 'ammo_pack', + 'experience_boost' + ], + cosmetics: [ + 'cool_paint_job', + 'neon_lights', + 'custom_decal', + 'golden_trim', + 'carbon_fiber', + 'chrome_finish' + ] +}; + +// Copy existing images or create placeholders +console.log('Creating placeholder images...'); + +// For ships - copy existing or create placeholder +items.ships.forEach(shipId => { + const existingPath = path.join(__dirname, `assets/images/ships/starter_cruiser.png`); + const targetPath = path.join(__dirname, `assets/images/ships/${shipId}.png`); + + if (fs.existsSync(existingPath) && !fs.existsSync(targetPath)) { + fs.copyFileSync(existingPath, targetPath); + console.log(`Copied ship: ${shipId}.png`); + } else if (!fs.existsSync(targetPath)) { + const placeholder = createPlaceholder(80, 80, 'blue', shipId); + fs.writeFileSync(targetPath, placeholder); + console.log(`Created ship placeholder: ${shipId}.png`); + } +}); + +// For weapons - copy existing or create placeholder +items.weapons.forEach(weaponId => { + const existingPath = path.join(__dirname, `assets/images/weapons/starter_blaster.png`); + const targetPath = path.join(__dirname, `assets/images/weapons/${weaponId}.png`); + + if (fs.existsSync(existingPath) && !fs.existsSync(targetPath)) { + fs.copyFileSync(existingPath, targetPath); + console.log(`Copied weapon: ${weaponId}.png`); + } else if (!fs.existsSync(targetPath)) { + const placeholder = createPlaceholder(64, 64, 'red', weaponId); + fs.writeFileSync(targetPath, placeholder); + console.log(`Created weapon placeholder: ${weaponId}.png`); + } +}); + +// For armors - create placeholders +items.armors.forEach(armorId => { + const targetPath = path.join(__dirname, `assets/images/armors/${armorId}.png`); + if (!fs.existsSync(targetPath)) { + const placeholder = createPlaceholder(64, 64, 'green', armorId); + fs.writeFileSync(targetPath, placeholder); + console.log(`Created armor placeholder: ${armorId}.png`); + } +}); + +// For materials - create placeholders +items.materials.forEach(materialId => { + const targetPath = path.join(__dirname, `assets/images/items/materials/${materialId}.png`); + if (!fs.existsSync(targetPath)) { + const placeholder = createPlaceholder(48, 48, 'gray', materialId); + fs.writeFileSync(targetPath, placeholder); + console.log(`Created material placeholder: ${materialId}.png`); + } +}); + +// For consumables - create placeholders +items.consumables.forEach(consumableId => { + const targetPath = path.join(__dirname, `assets/images/items/consumables/${consumableId}.png`); + if (!fs.existsSync(targetPath)) { + const placeholder = createPlaceholder(48, 48, 'purple', consumableId); + fs.writeFileSync(targetPath, placeholder); + console.log(`Created consumable placeholder: ${consumableId}.png`); + } +}); + +// For cosmetics - create placeholders +items.cosmetics.forEach(cosmeticId => { + const targetPath = path.join(__dirname, `assets/images/items/cosmetics/${cosmeticId}.png`); + if (!fs.existsSync(targetPath)) { + const placeholder = createPlaceholder(64, 64, 'gold', cosmeticId); + fs.writeFileSync(targetPath, placeholder); + console.log(`Created cosmetic placeholder: ${cosmeticId}.png`); + } +}); + +// Create UI placeholder +const placeholderPath = path.join(__dirname, 'assets/images/ui/placeholder.png'); +if (!fs.existsSync(placeholderPath)) { + const placeholder = createPlaceholder(80, 80, 'gray', 'placeholder'); + fs.writeFileSync(placeholderPath, placeholder); + console.log('Created UI placeholder.png'); +} + +console.log('\n🎉 Placeholder images created successfully!'); +console.log('📦 Your shop should now display items with placeholder images!'); +console.log('🔄 Replace these with actual images as you create them.'); diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/category/quests/daily.json b/Galaxy-Strike-Online-main/GameServer/data/gso/category/quests/daily.json new file mode 100644 index 0000000..16ed319 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/category/quests/daily.json @@ -0,0 +1,31 @@ +{ + "tabId": "daily", + "title": "Dailies", + "description": "Resets daily. Small rewards, fast goals.", + "quests": [ + { + "id": "d_craft_3", + "name": "Quick Craft" + }, + { + "id": "d_dungeons_3", + "name": "Dungeon Hopping" + }, + { + "id": "d_explore_30", + "name": "Daily Survey" + }, + { + "id": "d_login_5", + "name": "Frequent Flyer" + }, + { + "id": "d_smelt_5", + "name": "Smelting Run" + }, + { + "id": "d_kill_pirates_10", + "name": "Pirate Bounty" + } + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/category/quests/main_story.json b/Galaxy-Strike-Online-main/GameServer/data/gso/category/quests/main_story.json new file mode 100644 index 0000000..6e15a7f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/category/quests/main_story.json @@ -0,0 +1,39 @@ +{ + "tabId": "main_story", + "title": "Main Story", + "description": "The core story of Galaxy Strike Online.", + "quests": [ + { + "id": "ms_ch1_001", + "name": "First Steps Into the Stars" + }, + { + "id": "ms_ch1_002", + "name": "Into the Unknown" + }, + { + "id": "ms_ch1_003", + "name": "The Art of Craft" + }, + { + "id": "ms_ch2_001", + "name": "Alien Incursion" + }, + { + "id": "ms_ch2_002", + "name": "Power Core" + }, + { + "id": "ms_ch2_003", + "name": "The Void Calls" + }, + { + "id": "ms_ch3_001", + "name": "Dreadnought Rising" + }, + { + "id": "ms_ch3_002", + "name": "Galaxy Strike" + } + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/category/quests/monthly.json b/Galaxy-Strike-Online-main/GameServer/data/gso/category/quests/monthly.json new file mode 100644 index 0000000..8f9c115 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/category/quests/monthly.json @@ -0,0 +1,23 @@ +{ + "tabId": "monthly", + "title": "Monthlies", + "description": "The ultimate monthly challenges.", + "quests": [ + { + "id": "m_raids_20", + "name": "Monthly Raider" + }, + { + "id": "m_dungeons_50", + "name": "Legendary Dungeon Diver" + }, + { + "id": "m_craft_100", + "name": "Master Craftsman" + }, + { + "id": "m_void_run", + "name": "Void Conqueror" + } + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/category/quests/weekly.json b/Galaxy-Strike-Online-main/GameServer/data/gso/category/quests/weekly.json new file mode 100644 index 0000000..2bafb4a --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/category/quests/weekly.json @@ -0,0 +1,27 @@ +{ + "tabId": "weekly", + "title": "Weeklies", + "description": "Resets weekly. Bigger challenges, better rewards.", + "quests": [ + { + "id": "w_raids_3", + "name": "Raid Week" + }, + { + "id": "w_craft_20", + "name": "Crafting Spree" + }, + { + "id": "w_dungeons_10", + "name": "Dungeon Crawler" + }, + { + "id": "w_survive_hardcore_7", + "name": "Hardcore Survivor" + }, + { + "id": "w_gather_50", + "name": "Resource Rush" + } + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/alien_ruins.json b/Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/alien_ruins.json new file mode 100644 index 0000000..155a83d --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/alien_ruins.json @@ -0,0 +1,50 @@ +{ + "id": "alien_ruins", + "name": "Alien Ruins", + "category": "exploration", + "description": "Ancient alien structures filled with mysterious technology", + "difficulty": "medium", + "minLevel": 3, + "roomCount": [ + 5, + 8 + ], + "energyCost": 20, + "rewardMultiplier": 1.2, + "enemyPool": [ + "alien_guardian", + "ancient_drone", + "crystal_golem" + ], + "bossId": "crystal_golem", + "rewards": { + "creditsMin": 200, + "creditsMax": 500, + "experienceMin": 150, + "experienceMax": 300 + }, + "ui": { + "icon": "fa-monument", + "color": "#4488cc" + }, + "lootTable": [ + { + "itemId": "wp_alien_ruins", + "weight": 8, + "qtyMin": 1, + "qtyMax": 1 + }, + { + "itemId": "energy_boost_small", + "weight": 60, + "qtyMin": 1, + "qtyMax": 3 + }, + { + "itemId": "health_kit_small", + "weight": 60, + "qtyMin": 1, + "qtyMax": 2 + } + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/asteroid_mine.json b/Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/asteroid_mine.json new file mode 100644 index 0000000..8164701 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/asteroid_mine.json @@ -0,0 +1,50 @@ +{ + "id": "asteroid_mine", + "name": "Asteroid Mine", + "category": "exploration", + "description": "Abandoned mining facilities in asteroid fields", + "difficulty": "easy", + "minLevel": 2, + "roomCount": [ + 4, + 7 + ], + "energyCost": 10, + "rewardMultiplier": 0.8, + "enemyPool": [ + "mining_drone", + "rock_creature", + "space_pirate" + ], + "bossId": "mining_drone", + "rewards": { + "creditsMin": 60, + "creditsMax": 180, + "experienceMin": 50, + "experienceMax": 120 + }, + "ui": { + "icon": "fa-meteor", + "color": "#888844" + }, + "lootTable": [ + { + "itemId": "wp_arctic_station", + "weight": 8, + "qtyMin": 1, + "qtyMax": 1 + }, + { + "itemId": "fuel_cell", + "weight": 60, + "qtyMin": 1, + "qtyMax": 5 + }, + { + "itemId": "health_kit_small", + "weight": 50, + "qtyMin": 1, + "qtyMax": 3 + } + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/corrupted_vault.json b/Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/corrupted_vault.json new file mode 100644 index 0000000..b1ca63f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/corrupted_vault.json @@ -0,0 +1,56 @@ +{ + "id": "corrupted_vault", + "name": "Corrupted AI Vault", + "category": "combat", + "description": "Malfunctioning AI facilities with corrupted security systems", + "difficulty": "hard", + "minLevel": 5, + "roomCount": [ + 6, + 9 + ], + "energyCost": 25, + "rewardMultiplier": 1.5, + "enemyPool": [ + "security_drone", + "corrupted_ai", + "virus_program" + ], + "bossId": "corrupted_ai", + "rewards": { + "creditsMin": 400, + "creditsMax": 900, + "experienceMin": 300, + "experienceMax": 600 + }, + "ui": { + "icon": "fa-microchip", + "color": "#44cc44" + }, + "lootTable": [ + { + "itemId": "room_vault", + "weight": 12, + "qtyMin": 1, + "qtyMax": 1 + }, + { + "itemId": "wp_void_black", + "weight": 10, + "qtyMin": 1, + "qtyMax": 1 + }, + { + "itemId": "credit_multiplier", + "weight": 40, + "qtyMin": 1, + "qtyMax": 1 + }, + { + "itemId": "xp_booster", + "weight": 40, + "qtyMin": 1, + "qtyMax": 2 + } + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/dungeons.json b/Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/dungeons.json new file mode 100644 index 0000000..1f8e223 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/dungeons.json @@ -0,0 +1,97 @@ +[ + { + "id": "pirate_lair", + "name": "Pirate Lair", + "description": "Dangerous pirate hideouts with valuable loot", + "difficulty": "easy", + "minLevel": 1, + "maxPlayers": 4, + "roomCount": [4, 6], + "energyCost": 15, + "rewardMultiplier": 1.0, + "enemyPool": ["space_pirate", "pirate_captain", "defense_turret"], + "lootTable": [ + { "itemId": "iron_ore", "weight": 40, "qtyMin": 1, "qtyMax": 5 }, + { "itemId": "copper_ore", "weight": 30, "qtyMin": 1, "qtyMax": 3 }, + { "itemId": "bandage", "weight": 20, "qtyMin": 1, "qtyMax": 2 }, + { "itemId": "pistol_standard_common", "weight": 10, "qtyMin": 1, "qtyMax": 1 } + ], + "ui": { "icon": "fa-skull-crossbones", "color": "#e67e22" } + }, + { + "id": "asteroid_mine", + "name": "Asteroid Mine", + "description": "Abandoned mining facilities rich in ore deposits", + "difficulty": "easy", + "minLevel": 2, + "maxPlayers": 4, + "roomCount": [4, 7], + "energyCost": 10, + "rewardMultiplier": 0.8, + "enemyPool": ["mining_drone", "rock_creature", "explosive_asteroid"], + "lootTable": [ + { "itemId": "iron_ore", "weight": 35, "qtyMin": 2, "qtyMax": 8 }, + { "itemId": "copper_ore", "weight": 30, "qtyMin": 1, "qtyMax": 6 }, + { "itemId": "titanium_ore", "weight": 20, "qtyMin": 1, "qtyMax": 3 }, + { "itemId": "gold_ore", "weight": 15, "qtyMin": 1, "qtyMax": 2 } + ], + "ui": { "icon": "fa-meteor", "color": "#95a5a6" } + }, + { + "id": "alien_ruins", + "name": "Alien Ruins", + "description": "Ancient alien structures filled with mysterious technology", + "difficulty": "medium", + "minLevel": 3, + "maxPlayers": 4, + "roomCount": [5, 8], + "energyCost": 20, + "rewardMultiplier": 1.2, + "enemyPool": ["alien_guardian", "ancient_drone", "crystal_golem"], + "lootTable": [ + { "itemId": "flux_crystal", "weight": 30, "qtyMin": 1, "qtyMax": 3 }, + { "itemId": "alien_fauna_sample", "weight": 25, "qtyMin": 1, "qtyMax": 2 }, + { "itemId": "basic_circuit", "weight": 25, "qtyMin": 1, "qtyMax": 2 }, + { "itemId": "assault_rifle_common", "weight": 20, "qtyMin": 1, "qtyMax": 1 } + ], + "ui": { "icon": "fa-monument", "color": "#8e44ad" } + }, + { + "id": "corrupted_vault", + "name": "Corrupted AI Vault", + "description": "Malfunctioning AI facilities with corrupted security systems", + "difficulty": "hard", + "minLevel": 5, + "maxPlayers": 4, + "roomCount": [6, 9], + "energyCost": 25, + "rewardMultiplier": 1.5, + "enemyPool": ["security_drone", "corrupted_ai", "virus_program"], + "lootTable": [ + { "itemId": "logic_circuit", "weight": 30, "qtyMin": 1, "qtyMax": 3 }, + { "itemId": "power_circuit", "weight": 25, "qtyMin": 1, "qtyMax": 2 }, + { "itemId": "control_circuit", "weight": 25, "qtyMin": 1, "qtyMax": 2 }, + { "itemId": "sniper_rare", "weight": 20, "qtyMin": 1, "qtyMax": 1 } + ], + "ui": { "icon": "fa-robot", "color": "#e74c3c" } + }, + { + "id": "nebula_anomaly", + "name": "Nebula Anomaly", + "description": "Strange energy anomalies in the deep void of space", + "difficulty": "extreme", + "minLevel": 8, + "maxPlayers": 4, + "roomCount": [7, 10], + "energyCost": 30, + "rewardMultiplier": 2.0, + "enemyPool": ["energy_being", "phase_shifter", "quantum_entity"], + "lootTable": [ + { "itemId": "quantum_circuit", "weight": 25, "qtyMin": 1, "qtyMax": 2 }, + { "itemId": "void_crystal", "weight": 25, "qtyMin": 1, "qtyMax": 2 }, + { "itemId": "phase_crystal", "weight": 25, "qtyMin": 1, "qtyMax": 2 }, + { "itemId": "plasma_cutter_epic", "weight": 25, "qtyMin": 1, "qtyMax": 1 } + ], + "ui": { "icon": "fa-star", "color": "#2980b9" } + } +] diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/enemies.json b/Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/enemies.json new file mode 100644 index 0000000..e3795a7 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/enemies.json @@ -0,0 +1,15 @@ +[ + { "id": "space_pirate", "name": "Space Pirate", "health": 25, "attack": 10, "defense": 3, "speed": 8, "experience": 15, "credits": 12, "rarity": "common" }, + { "id": "pirate_captain", "name": "Pirate Captain", "health": 40, "attack": 15, "defense": 6, "speed": 12, "experience": 30, "credits": 20, "rarity": "uncommon" }, + { "id": "defense_turret", "name": "Defense Turret", "health": 35, "attack": 14, "defense": 8, "speed": 0, "experience": 20, "credits": 15, "rarity": "common" }, + { "id": "mining_drone", "name": "Mining Drone", "health": 20, "attack": 8, "defense": 3, "speed": 5, "experience": 12, "credits": 8, "rarity": "common" }, + { "id": "rock_creature", "name": "Rock Creature", "health": 45, "attack": 6, "defense": 12, "speed": 3, "experience": 18, "credits": 10, "rarity": "common" }, + { "id": "alien_guardian", "name": "Alien Guardian", "health": 50, "attack": 8, "defense": 5, "speed": 6, "experience": 25, "credits": 15, "rarity": "common" }, + { "id": "ancient_drone", "name": "Ancient Drone", "health": 30, "attack": 12, "defense": 2, "speed": 10, "experience": 20, "credits": 10, "rarity": "uncommon" }, + { "id": "crystal_golem", "name": "Crystal Golem", "health": 80, "attack": 6, "defense": 10, "speed": 4, "experience": 35, "credits": 25, "rarity": "rare" }, + { "id": "security_drone", "name": "Security Drone", "health": 35, "attack": 14, "defense": 4, "speed": 10, "experience": 22, "credits": 15, "rarity": "uncommon" }, + { "id": "corrupted_ai", "name": "Corrupted AI", "health": 60, "attack": 20, "defense": 2, "speed": 15, "experience": 40, "credits": 30, "rarity": "rare" }, + { "id": "energy_being", "name": "Energy Being", "health": 55, "attack": 22, "defense": 3, "speed": 18, "experience": 45, "credits": 35, "rarity": "epic" }, + { "id": "phase_shifter", "name": "Phase Shifter", "health": 50, "attack": 25, "defense": 5, "speed": 20, "experience": 50, "credits": 40, "rarity": "epic" }, + { "id": "quantum_entity", "name": "Quantum Entity", "health": 70, "attack": 35, "defense": 5, "speed": 20, "experience": 60, "credits": 50, "rarity": "legendary" } +] diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/nebula_anomaly.json b/Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/nebula_anomaly.json new file mode 100644 index 0000000..f831df5 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/nebula_anomaly.json @@ -0,0 +1,50 @@ +{ + "id": "nebula_anomaly", + "name": "Nebula Anomaly", + "category": "endgame", + "description": "Strange energy anomalies deep in uncharted space", + "difficulty": "extreme", + "minLevel": 8, + "roomCount": [ + 7, + 10 + ], + "energyCost": 30, + "rewardMultiplier": 2.0, + "enemyPool": [ + "energy_being", + "phase_shifter", + "quantum_entity" + ], + "bossId": "quantum_entity", + "rewards": { + "creditsMin": 800, + "creditsMax": 2000, + "experienceMin": 600, + "experienceMax": 1200 + }, + "ui": { + "icon": "fa-star", + "color": "#cc44ff" + }, + "lootTable": [ + { + "itemId": "wp_nebula_dawn", + "weight": 10, + "qtyMin": 1, + "qtyMax": 1 + }, + { + "itemId": "wp_deep_ocean", + "weight": 8, + "qtyMin": 1, + "qtyMax": 1 + }, + { + "itemId": "xp_booster", + "weight": 60, + "qtyMin": 1, + "qtyMax": 2 + } + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/pirate_lair.json b/Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/pirate_lair.json new file mode 100644 index 0000000..cb7c4b3 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/pirate_lair.json @@ -0,0 +1,50 @@ +{ + "id": "pirate_lair", + "name": "Pirate Lair", + "category": "combat", + "description": "Dangerous pirate hideouts packed with loot", + "difficulty": "easy", + "minLevel": 1, + "roomCount": [ + 4, + 6 + ], + "energyCost": 15, + "rewardMultiplier": 1.0, + "enemyPool": [ + "space_pirate", + "pirate_captain", + "security_drone" + ], + "bossId": "pirate_captain", + "rewards": { + "creditsMin": 100, + "creditsMax": 300, + "experienceMin": 80, + "experienceMax": 160 + }, + "ui": { + "icon": "fa-skull-crossbones", + "color": "#cc4444" + }, + "lootTable": [ + { + "itemId": "wp_lava_forge", + "weight": 6, + "qtyMin": 1, + "qtyMax": 1 + }, + { + "itemId": "health_kit_large", + "weight": 40, + "qtyMin": 1, + "qtyMax": 2 + }, + { + "itemId": "fuel_cell", + "weight": 50, + "qtyMin": 1, + "qtyMax": 3 + } + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/void_rift.json b/Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/void_rift.json new file mode 100644 index 0000000..9ba4c70 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/dungeons/void_rift.json @@ -0,0 +1,56 @@ +{ + "id": "void_rift", + "name": "Void Rift", + "category": "endgame", + "description": "A tear in space-time inhabited by entities beyond comprehension", + "difficulty": "extreme", + "minLevel": 12, + "roomCount": [ + 8, + 12 + ], + "energyCost": 40, + "rewardMultiplier": 2.5, + "enemyPool": [ + "quantum_entity", + "energy_being", + "phase_shifter" + ], + "bossId": "quantum_entity", + "rewards": { + "creditsMin": 1500, + "creditsMax": 4000, + "experienceMin": 1000, + "experienceMax": 2000 + }, + "ui": { + "icon": "fa-circle-radiation", + "color": "#ff4488" + }, + "lootTable": [ + { + "itemId": "wp_void_rift", + "weight": 5, + "qtyMin": 1, + "qtyMax": 1 + }, + { + "itemId": "room_vault", + "weight": 3, + "qtyMin": 1, + "qtyMax": 1 + }, + { + "itemId": "xp_booster", + "weight": 50, + "qtyMin": 1, + "qtyMax": 2 + }, + { + "itemId": "credit_multiplier", + "weight": 30, + "qtyMin": 1, + "qtyMax": 1 + } + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/alien_guardian.json b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/alien_guardian.json new file mode 100644 index 0000000..d9400d8 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/alien_guardian.json @@ -0,0 +1,15 @@ +{ + "id": "alien_guardian", + "name": "Alien Guardian", + "rarity": "common", + "health": 50, + "attack": 8, + "defense": 5, + "speed": 6, + "experience": 25, + "credits": 15, + "dropTable": [ + "alien_fauna_sample", + "iron_ore" + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/ancient_drone.json b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/ancient_drone.json new file mode 100644 index 0000000..02cade1 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/ancient_drone.json @@ -0,0 +1,15 @@ +{ + "id": "ancient_drone", + "name": "Ancient Drone", + "rarity": "uncommon", + "health": 30, + "attack": 12, + "defense": 2, + "speed": 9, + "experience": 20, + "credits": 10, + "dropTable": [ + "basic_circuit", + "alien_fauna_sample" + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/corrupted_ai.json b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/corrupted_ai.json new file mode 100644 index 0000000..772a234 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/corrupted_ai.json @@ -0,0 +1,15 @@ +{ + "id": "corrupted_ai", + "name": "Corrupted AI", + "rarity": "rare", + "health": 60, + "attack": 20, + "defense": 2, + "speed": 15, + "experience": 40, + "credits": 30, + "dropTable": [ + "processor_circuit", + "logic_circuit" + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/crystal_golem.json b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/crystal_golem.json new file mode 100644 index 0000000..1452316 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/crystal_golem.json @@ -0,0 +1,15 @@ +{ + "id": "crystal_golem", + "name": "Crystal Golem", + "rarity": "rare", + "health": 80, + "attack": 6, + "defense": 10, + "speed": 4, + "experience": 35, + "credits": 25, + "dropTable": [ + "flux_crystal", + "titanium_ore" + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/energy_being.json b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/energy_being.json new file mode 100644 index 0000000..f30e729 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/energy_being.json @@ -0,0 +1,15 @@ +{ + "id": "energy_being", + "name": "Energy Being", + "rarity": "epic", + "health": 55, + "attack": 22, + "defense": 3, + "speed": 18, + "experience": 45, + "credits": 35, + "dropTable": [ + "void_crystal", + "quantum_circuit" + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/mining_drone.json b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/mining_drone.json new file mode 100644 index 0000000..3cb8875 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/mining_drone.json @@ -0,0 +1,15 @@ +{ + "id": "mining_drone", + "name": "Mining Drone", + "rarity": "common", + "health": 20, + "attack": 8, + "defense": 3, + "speed": 5, + "experience": 12, + "credits": 8, + "dropTable": [ + "iron_ore", + "copper_ore" + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/phase_shifter.json b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/phase_shifter.json new file mode 100644 index 0000000..c61f026 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/phase_shifter.json @@ -0,0 +1,15 @@ +{ + "id": "phase_shifter", + "name": "Phase Shifter", + "rarity": "epic", + "health": 50, + "attack": 25, + "defense": 4, + "speed": 22, + "experience": 55, + "credits": 45, + "dropTable": [ + "phase_crystal", + "void_crystal" + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/pirate_captain.json b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/pirate_captain.json new file mode 100644 index 0000000..ebbb472 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/pirate_captain.json @@ -0,0 +1,15 @@ +{ + "id": "pirate_captain", + "name": "Pirate Captain", + "rarity": "uncommon", + "health": 40, + "attack": 15, + "defense": 6, + "speed": 12, + "experience": 30, + "credits": 20, + "dropTable": [ + "steel_alloy", + "iron_ore" + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/quantum_entity.json b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/quantum_entity.json new file mode 100644 index 0000000..f0055a3 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/quantum_entity.json @@ -0,0 +1,16 @@ +{ + "id": "quantum_entity", + "name": "Quantum Entity", + "rarity": "legendary", + "health": 70, + "attack": 35, + "defense": 5, + "speed": 20, + "experience": 60, + "credits": 50, + "dropTable": [ + "void_crystal", + "neutronium_shard", + "dimensional_logic_circuit" + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/rock_creature.json b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/rock_creature.json new file mode 100644 index 0000000..503b92f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/rock_creature.json @@ -0,0 +1,15 @@ +{ + "id": "rock_creature", + "name": "Rock Creature", + "rarity": "common", + "health": 30, + "attack": 7, + "defense": 8, + "speed": 3, + "experience": 14, + "credits": 9, + "dropTable": [ + "iron_ore", + "carbon_ingot" + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/security_drone.json b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/security_drone.json new file mode 100644 index 0000000..8b0b47f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/security_drone.json @@ -0,0 +1,15 @@ +{ + "id": "security_drone", + "name": "Security Drone", + "rarity": "uncommon", + "health": 35, + "attack": 14, + "defense": 4, + "speed": 10, + "experience": 22, + "credits": 15, + "dropTable": [ + "basic_circuit", + "copper_ore" + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/space_pirate.json b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/space_pirate.json new file mode 100644 index 0000000..938491f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/space_pirate.json @@ -0,0 +1,15 @@ +{ + "id": "space_pirate", + "name": "Space Pirate", + "rarity": "common", + "health": 25, + "attack": 10, + "defense": 3, + "speed": 8, + "experience": 15, + "credits": 12, + "dropTable": [ + "iron_ore", + "bandage" + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/training_drone.json b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/training_drone.json new file mode 100644 index 0000000..5a7086d --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/training_drone.json @@ -0,0 +1,12 @@ +{ + "id": "training_drone", + "name": "Training Drone", + "rarity": "common", + "health": 10, + "attack": 5, + "defense": 2, + "speed": 6, + "experience": 5, + "credits": 3, + "dropTable": [] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/virus_program.json b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/virus_program.json new file mode 100644 index 0000000..9edeac6 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/enemies/virus_program.json @@ -0,0 +1,15 @@ +{ + "id": "virus_program", + "name": "Virus Program", + "rarity": "rare", + "health": 45, + "attack": 18, + "defense": 1, + "speed": 20, + "experience": 38, + "credits": 28, + "dropTable": [ + "processor_circuit", + "stealth_circuit" + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/.gitkeep b/Galaxy-Strike-Online-main/GameServer/data/gso/items/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/backpack/backpack_basic_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/backpack/backpack_basic_common.json new file mode 100644 index 0000000..915caec --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/backpack/backpack_basic_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "backpack_basic_common", + "texture": "assets/gso/textures/armour/backpack/backpack_basic_common.png", + "stats": { + "health": 20, + "resistance": 0.03, + "defenceRating": 1, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/backpack/backpack_field_rare.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/backpack/backpack_field_rare.json new file mode 100644 index 0000000..36eb7fd --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/backpack/backpack_field_rare.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "backpack_field_rare", + "texture": "assets/gso/textures/armour/backpack/backpack_field_rare.png", + "stats": { + "health": 40, + "resistance": 0.08, + "defenceRating": 3, + "reflectChance": 0.01 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/backpack/backpack_reactor_epic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/backpack/backpack_reactor_epic.json new file mode 100644 index 0000000..07d86c6 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/backpack/backpack_reactor_epic.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "backpack_reactor_epic", + "texture": "assets/gso/textures/armour/backpack/backpack_reactor_epic.png", + "stats": { + "health": 70, + "resistance": 0.15, + "defenceRating": 6, + "reflectChance": 0.03 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/body/body_basic_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/body/body_basic_common.json new file mode 100644 index 0000000..461b4cb --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/body/body_basic_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "body_basic_common", + "texture": "assets/gso/textures/armour/body/body_basic_common.png", + "stats": { + "health": 60, + "resistance": 0.1, + "defenceRating": 4, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/body/body_exosuit_epic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/body/body_exosuit_epic.json new file mode 100644 index 0000000..7f70a3f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/body/body_exosuit_epic.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "body_exosuit_epic", + "texture": "assets/gso/textures/armour/body/body_exosuit_epic.png", + "stats": { + "health": 180, + "resistance": 0.3, + "defenceRating": 14, + "reflectChance": 0.05 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/body/body_plated_rare.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/body/body_plated_rare.json new file mode 100644 index 0000000..3d724e8 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/body/body_plated_rare.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "body_plated_rare", + "texture": "assets/gso/textures/armour/body/body_plated_rare.png", + "stats": { + "health": 110, + "resistance": 0.2, + "defenceRating": 8, + "reflectChance": 0.02 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/boots/boots_assault_rare.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/boots/boots_assault_rare.json new file mode 100644 index 0000000..a4b641a --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/boots/boots_assault_rare.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "boots_assault_rare", + "texture": "assets/gso/textures/armour/boots/boots_assault_rare.png", + "stats": { + "health": 40, + "resistance": 0.12, + "defenceRating": 4, + "reflectChance": 0.01 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/boots/boots_basic_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/boots/boots_basic_common.json new file mode 100644 index 0000000..110c2a5 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/boots/boots_basic_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "boots_basic_common", + "texture": "assets/gso/textures/armour/boots/boots_basic_common.png", + "stats": { + "health": 20, + "resistance": 0.05, + "defenceRating": 2, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/boots/boots_gravity_epic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/boots/boots_gravity_epic.json new file mode 100644 index 0000000..ea5a086 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/boots/boots_gravity_epic.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "boots_gravity_epic", + "texture": "assets/gso/textures/armour/boots/boots_gravity_epic.png", + "stats": { + "health": 65, + "resistance": 0.2, + "defenceRating": 7, + "reflectChance": 0.025 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/hands/hands_basic_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/hands/hands_basic_common.json new file mode 100644 index 0000000..bbf01e6 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/hands/hands_basic_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "hands_basic_common", + "texture": "assets/gso/textures/armour/hands/hands_basic_common.png", + "stats": { + "health": 15, + "resistance": 0.04, + "defenceRating": 1, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/hands/hands_combat_rare.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/hands/hands_combat_rare.json new file mode 100644 index 0000000..4b698e2 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/hands/hands_combat_rare.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "hands_combat_rare", + "texture": "assets/gso/textures/armour/hands/hands_combat_rare.png", + "stats": { + "health": 30, + "resistance": 0.1, + "defenceRating": 3, + "reflectChance": 0.01 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/hands/hands_elite_epic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/hands/hands_elite_epic.json new file mode 100644 index 0000000..69a6ba1 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/hands/hands_elite_epic.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "hands_elite_epic", + "texture": "assets/gso/textures/armour/hands/hands_elite_epic.png", + "stats": { + "health": 50, + "resistance": 0.18, + "defenceRating": 6, + "reflectChance": 0.025 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/helmet/helmet_basic_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/helmet/helmet_basic_common.json new file mode 100644 index 0000000..88ea0a0 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/helmet/helmet_basic_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "helmet_basic_common", + "texture": "assets/gso/textures/armour/helmet/helmet_basic_common.png", + "stats": { + "health": 25, + "resistance": 0.05, + "defenceRating": 2, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/helmet/helmet_reinforced_rare.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/helmet/helmet_reinforced_rare.json new file mode 100644 index 0000000..557fb37 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/helmet/helmet_reinforced_rare.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "helmet_reinforced_rare", + "texture": "assets/gso/textures/armour/helmet/helmet_reinforced_rare.png", + "stats": { + "health": 45, + "resistance": 0.12, + "defenceRating": 5, + "reflectChance": 0.01 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/helmet/helmet_tactical_epic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/helmet/helmet_tactical_epic.json new file mode 100644 index 0000000..46d37de --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/helmet/helmet_tactical_epic.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "helmet_tactical_epic", + "texture": "assets/gso/textures/armour/helmet/helmet_tactical_epic.png", + "stats": { + "health": 70, + "resistance": 0.2, + "defenceRating": 8, + "reflectChance": 0.03 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/pants/pants_basic_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/pants/pants_basic_common.json new file mode 100644 index 0000000..87e7248 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/pants/pants_basic_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "pants_basic_common", + "texture": "assets/gso/textures/armour/pants/pants_basic_common.png", + "stats": { + "health": 30, + "resistance": 0.06, + "defenceRating": 2, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/pants/pants_exo_epic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/pants/pants_exo_epic.json new file mode 100644 index 0000000..3cdba58 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/pants/pants_exo_epic.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "pants_exo_epic", + "texture": "assets/gso/textures/armour/pants/pants_exo_epic.png", + "stats": { + "health": 85, + "resistance": 0.22, + "defenceRating": 9, + "reflectChance": 0.03 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/pants/pants_reinforced_rare.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/pants/pants_reinforced_rare.json new file mode 100644 index 0000000..6710535 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armour/pants/pants_reinforced_rare.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "pants_reinforced_rare", + "texture": "assets/gso/textures/armour/pants/pants_reinforced_rare.png", + "stats": { + "health": 55, + "resistance": 0.14, + "defenceRating": 5, + "reflectChance": 0.015 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/backpack_basic_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/backpack_basic_common.json new file mode 100644 index 0000000..229ac4f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/backpack_basic_common.json @@ -0,0 +1,30 @@ +{ + "templates": { + "armour": { + "id": "backpack_basic_common", + "name": "Basic Backpack", + "type": "armour", + "slot": "backpack", + "rarity": "common", + "price": 500, + "currency": "credits", + "description": "Lightweight backpack with extra inventory slots", + "texture": "assets/gso/textures/armour/backpack/backpack_basic_common.png", + "stats": { + "health": 10, + "resistance": 0.02, + "defenceRating": 1, + "reflectChance": 0.0, + "extraSlots": 4 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 1 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/body_basic_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/body_basic_common.json new file mode 100644 index 0000000..09c508b --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/body_basic_common.json @@ -0,0 +1,29 @@ +{ + "templates": { + "armour": { + "id": "body_basic_common", + "name": "Basic Body Armour", + "type": "armour", + "slot": "body", + "rarity": "common", + "price": 600, + "currency": "credits", + "description": "Standard-issue protection for rookie pilots", + "texture": "assets/gso/textures/armour/body/body_basic_common.png", + "stats": { + "health": 60, + "resistance": 0.1, + "defenceRating": 4, + "reflectChance": 0.0 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 1 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/body_exosuit_epic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/body_exosuit_epic.json new file mode 100644 index 0000000..826754b --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/body_exosuit_epic.json @@ -0,0 +1,28 @@ +{ + "templates": { + "armour": { + "id": "body_exosuit_epic", + "name": "Exosuit", + "type": "armour", + "slot": "body", + "rarity": "epic", + "price": 300, + "currency": "gems", + "description": "Powered exosuit that amplifies strength and endurance", + "texture": "assets/gso/textures/armour/body/body_exosuit_epic.png", + "stats": { + "health": 220, + "resistance": 0.35, + "defenceRating": 22, + "reflectChance": 0.1 + }, + "categories": [ + "shop" + ], + "requirements": { + "level": 20 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/body_plated_rare.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/body_plated_rare.json new file mode 100644 index 0000000..002370f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/body_plated_rare.json @@ -0,0 +1,29 @@ +{ + "templates": { + "armour": { + "id": "body_plated_rare", + "name": "Plated Combat Suit", + "type": "armour", + "slot": "body", + "rarity": "rare", + "price": 7000, + "currency": "credits", + "description": "Heavy-duty plated suit for frontline fighters", + "texture": "assets/gso/textures/armour/body/body_plated_rare.png", + "stats": { + "health": 120, + "resistance": 0.2, + "defenceRating": 12, + "reflectChance": 0.05 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 10 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/boots_basic_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/boots_basic_common.json new file mode 100644 index 0000000..dc376ea --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/boots_basic_common.json @@ -0,0 +1,29 @@ +{ + "templates": { + "armour": { + "id": "boots_basic_common", + "name": "Basic Boots", + "type": "armour", + "slot": "boots", + "rarity": "common", + "price": 350, + "currency": "credits", + "description": "Sturdy magnetic boots for zero-gravity environments", + "texture": "assets/gso/textures/armour/boots/boots_basic_common.png", + "stats": { + "health": 20, + "resistance": 0.05, + "defenceRating": 2, + "reflectChance": 0.0 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 1 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/gloves_basic_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/gloves_basic_common.json new file mode 100644 index 0000000..f0be1aa --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/gloves_basic_common.json @@ -0,0 +1,29 @@ +{ + "templates": { + "armour": { + "id": "gloves_basic_common", + "name": "Basic Gloves", + "type": "armour", + "slot": "hands", + "rarity": "common", + "price": 300, + "currency": "credits", + "description": "Reinforced tactical gloves with grip enhancement", + "texture": "assets/gso/textures/armour/hands/hands_basic_common.png", + "stats": { + "health": 15, + "resistance": 0.05, + "defenceRating": 2, + "reflectChance": 0.0 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 1 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/helmet_basic_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/helmet_basic_common.json new file mode 100644 index 0000000..e6a52d9 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/helmet_basic_common.json @@ -0,0 +1,29 @@ +{ + "templates": { + "armour": { + "id": "helmet_basic_common", + "name": "Basic Helmet", + "type": "armour", + "slot": "helmet", + "rarity": "common", + "price": 400, + "currency": "credits", + "description": "Hardened composite helmet protecting the head", + "texture": "assets/gso/textures/armour/helmet/helmet_basic_common.png", + "stats": { + "health": 30, + "resistance": 0.08, + "defenceRating": 3, + "reflectChance": 0.0 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 1 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/helmet_reinforced_rare.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/helmet_reinforced_rare.json new file mode 100644 index 0000000..947dd9e --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/helmet_reinforced_rare.json @@ -0,0 +1,29 @@ +{ + "templates": { + "armour": { + "id": "helmet_reinforced_rare", + "name": "Reinforced Helmet", + "type": "armour", + "slot": "helmet", + "rarity": "rare", + "price": 4500, + "currency": "credits", + "description": "Reinforced titanium helmet with HUD integration", + "texture": "assets/gso/textures/armour/helmet/helmet_reinforced_rare.png", + "stats": { + "health": 70, + "resistance": 0.18, + "defenceRating": 9, + "reflectChance": 0.03 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 8 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/pants_basic_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/pants_basic_common.json new file mode 100644 index 0000000..ae2e92d --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/armours/pants_basic_common.json @@ -0,0 +1,29 @@ +{ + "templates": { + "armour": { + "id": "pants_basic_common", + "name": "Basic Leg Guards", + "type": "armour", + "slot": "pants", + "rarity": "common", + "price": 350, + "currency": "credits", + "description": "Lightweight leg armour for mobile pilots", + "texture": "assets/gso/textures/armour/pants/pants_basic_common.png", + "stats": { + "health": 25, + "resistance": 0.07, + "defenceRating": 2, + "reflectChance": 0.0 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 1 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/credit_multiplier.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/credit_multiplier.json new file mode 100644 index 0000000..a5b87f6 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/credit_multiplier.json @@ -0,0 +1,26 @@ +{ + "templates": { + "consumable": { + "id": "credit_multiplier", + "name": "Credit Multiplier", + "type": "consumable", + "rarity": "rare", + "price": 50, + "currency": "gems", + "description": "Doubles credits earned for 1 hour", + "texture": "assets/gso/textures/consumables/credit_multiplier.png", + "effect": { + "creditMultiplier": 2, + "durationMinutes": 60 + }, + "categories": [ + "shop" + ], + "requirements": { + "level": 1 + }, + "stackable": true, + "maxStack": 5 + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/energy_boost_large.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/energy_boost_large.json new file mode 100644 index 0000000..a066e9a --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/energy_boost_large.json @@ -0,0 +1,25 @@ +{ + "templates": { + "consumable": { + "id": "energy_boost_large", + "name": "Max Energy Boost", + "type": "consumable", + "rarity": "uncommon", + "price": 100, + "currency": "credits", + "description": "Restores full energy", + "texture": "assets/gso/textures/consumables/energy_boost_large.png", + "effect": { + "energy": 999 + }, + "categories": [ + "shop" + ], + "requirements": { + "level": 1 + }, + "stackable": true, + "maxStack": 10 + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/energy_boost_small.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/energy_boost_small.json new file mode 100644 index 0000000..d50fca8 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/energy_boost_small.json @@ -0,0 +1,26 @@ +{ + "templates": { + "consumable": { + "id": "energy_boost_small", + "name": "Energy Boost", + "type": "consumable", + "rarity": "common", + "price": 30, + "currency": "credits", + "description": "Restores 50 energy", + "texture": "assets/gso/textures/consumables/energy_boost_small.png", + "effect": { + "energy": 50 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 1 + }, + "stackable": true, + "maxStack": 20 + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/fuel_cell.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/fuel_cell.json new file mode 100644 index 0000000..99f6d8d --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/fuel_cell.json @@ -0,0 +1,27 @@ +{ + "templates": { + "consumable": { + "id": "fuel_cell", + "name": "Fuel Cell", + "type": "consumable", + "rarity": "common", + "price": 20, + "currency": "credits", + "description": "Restores 25 energy \u2014 handy for long dungeon runs", + "texture": "assets/gso/textures/consumables/fuel_cell.png", + "effect": { + "energy": 25 + }, + "categories": [ + "shop", + "dungeon_loot", + "starter" + ], + "requirements": { + "level": 1 + }, + "stackable": true, + "maxStack": 50 + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/health_kit_large.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/health_kit_large.json new file mode 100644 index 0000000..a1208f6 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/health_kit_large.json @@ -0,0 +1,26 @@ +{ + "templates": { + "consumable": { + "id": "health_kit_large", + "name": "Large Health Kit", + "type": "consumable", + "rarity": "uncommon", + "price": 150, + "currency": "credits", + "description": "Restores full health", + "texture": "assets/gso/textures/consumables/health_kit_large.png", + "effect": { + "heal": 999 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 1 + }, + "stackable": true, + "maxStack": 10 + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/health_kit_small.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/health_kit_small.json new file mode 100644 index 0000000..e98ad52 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/health_kit_small.json @@ -0,0 +1,26 @@ +{ + "templates": { + "consumable": { + "id": "health_kit_small", + "name": "Small Health Kit", + "type": "consumable", + "rarity": "common", + "price": 50, + "currency": "credits", + "description": "Restores 50 health", + "texture": "assets/gso/textures/consumables/health_kit_small.png", + "effect": { + "heal": 50 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 1 + }, + "stackable": true, + "maxStack": 20 + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/scrap_pack_small.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/scrap_pack_small.json new file mode 100644 index 0000000..82fa4cd --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/scrap_pack_small.json @@ -0,0 +1,35 @@ +{ + "templates": { + "consumable": { + "id": "scrap_pack_small", + "name": "Small Scrap Pack", + "type": "consumable", + "rarity": "common", + "price": 0, + "currency": "credits", + "description": "A bundle of mixed crafting scraps", + "texture": "assets/gso/textures/consumables/scrap_pack_small.png", + "effect": { + "grantItems": [ + { + "id": "iron_ore", + "qty": 3 + }, + { + "id": "copper_ore", + "qty": 2 + } + ] + }, + "categories": [ + "quest_reward", + "dungeon_loot" + ], + "requirements": { + "level": 1 + }, + "stackable": true, + "maxStack": 10 + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/starter_crate.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/starter_crate.json new file mode 100644 index 0000000..de40f73 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/starter_crate.json @@ -0,0 +1,39 @@ +{ + "templates": { + "consumable": { + "id": "starter_crate", + "name": "Starter Crate", + "type": "consumable", + "rarity": "uncommon", + "price": 0, + "currency": "credits", + "description": "A crate of starter supplies for new recruits", + "texture": "assets/gso/textures/consumables/starter_crate.png", + "effect": { + "grantItems": [ + { + "id": "health_kit_small", + "qty": 3 + }, + { + "id": "energy_boost_small", + "qty": 3 + }, + { + "id": "fuel_cell", + "qty": 5 + } + ] + }, + "categories": [ + "quest_reward", + "starter" + ], + "requirements": { + "level": 1 + }, + "stackable": true, + "maxStack": 5 + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/xp_booster.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/xp_booster.json new file mode 100644 index 0000000..76bb9c8 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/consumables/xp_booster.json @@ -0,0 +1,26 @@ +{ + "templates": { + "consumable": { + "id": "xp_booster", + "name": "XP Booster", + "type": "consumable", + "rarity": "rare", + "price": 75, + "currency": "gems", + "description": "Doubles XP gained for 30 minutes", + "texture": "assets/gso/textures/consumables/xp_booster.png", + "effect": { + "xpMultiplier": 2, + "durationMinutes": 30 + }, + "categories": [ + "shop" + ], + "requirements": { + "level": 1 + }, + "stackable": true, + "maxStack": 5 + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/room_armory.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/room_armory.json new file mode 100644 index 0000000..0d5b312 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/room_armory.json @@ -0,0 +1,10 @@ +{ + "templates": { "decoration": { + "id": "room_armory", "name": "Armory Wing", "type": "decoration", + "subtype": "room_unlock", "rarity": "rare", "price": 5000, "currency": "credits", + "description": "Unlock the Armory Wing of your starbase. Houses weapon racks, armour stands, and a personal combat trainer.", + "texture": "assets/gso/textures/decorations/room_armory.png", + "roomId": "left_wing", + "categories": ["shop", "decoration"], "stackable": false + }} +} diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/room_operations.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/room_operations.json new file mode 100644 index 0000000..bf343ef --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/room_operations.json @@ -0,0 +1,10 @@ +{ + "templates": { "decoration": { + "id": "room_operations", "name": "Operations Centre", "type": "decoration", + "subtype": "room_unlock", "rarity": "epic", "price": 200, "currency": "gems", + "description": "Unlock the Operations Centre. A private command room for coordinating missions and monitoring your empire.", + "texture": "assets/gso/textures/decorations/room_operations.png", + "roomId": "operations", + "categories": ["shop", "decoration"], "stackable": false + }} +} diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/room_research_lab.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/room_research_lab.json new file mode 100644 index 0000000..7340a5f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/room_research_lab.json @@ -0,0 +1,10 @@ +{ + "templates": { "decoration": { + "id": "room_research_lab", "name": "Research Lab", "type": "decoration", + "subtype": "room_unlock", "rarity": "rare", "price": 6000, "currency": "credits", + "description": "Unlock the Research Lab. Advanced crafting terminals and science equipment for developing new technologies.", + "texture": "assets/gso/textures/decorations/room_research_lab.png", + "roomId": "right_wing", + "categories": ["shop", "decoration"], "stackable": false + }} +} diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/room_throne.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/room_throne.json new file mode 100644 index 0000000..8c8e9eb --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/room_throne.json @@ -0,0 +1,10 @@ +{ + "templates": { "decoration": { + "id": "room_throne", "name": "Throne Room", "type": "decoration", + "subtype": "room_unlock", "rarity": "legendary", "price": 0, "currency": "credits", + "description": "A legendary throne room earned only by those who have completed the Commander's Story Arc.", + "texture": "assets/gso/textures/decorations/room_throne.png", + "roomId": "throne_room", + "categories": ["quest_reward", "decoration"], "stackable": false + }} +} diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/room_vault.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/room_vault.json new file mode 100644 index 0000000..aff7bb3 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/room_vault.json @@ -0,0 +1,10 @@ +{ + "templates": { "decoration": { + "id": "room_vault", "name": "Commander's Vault", "type": "decoration", + "subtype": "room_unlock", "rarity": "epic", "price": 0, "currency": "credits", + "description": "A legendary vault room — only accessible to those who have conquered the Corrupted Vault dungeon.", + "texture": "assets/gso/textures/decorations/room_vault.png", + "roomId": "commanders_vault", + "categories": ["dungeon_loot", "decoration"], "stackable": false + }} +} diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_alien_ruins.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_alien_ruins.json new file mode 100644 index 0000000..eb82739 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_alien_ruins.json @@ -0,0 +1,12 @@ +{ + "templates": { "decoration": { + "id": "wp_alien_ruins", "name": "Alien Ruins", "type": "decoration", + "subtype": "wallpaper", "rarity": "epic", "price": 0, "currency": "credits", + "description": "Cryptic green glyphs on ancient stone. Salvaged from the Alien Ruins dungeon.", + "texture": "assets/gso/textures/decorations/wp_alien_ruins.png", + "preview": { "floorColorEven": "#0a1508", "floorColorOdd": "#081208", + "wallColor": "#44ff88", "wallColorLeft": "#0a1e0a", "wallColorRight": "#081808", "wallColorTop": "#102a10", + "doorColor": "#88ffaa", "doorFrameColor": "#44cc66" }, + "categories": ["dungeon_loot", "decoration"], "stackable": false + }} +} diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_arctic_station.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_arctic_station.json new file mode 100644 index 0000000..1283292 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_arctic_station.json @@ -0,0 +1,12 @@ +{ + "templates": { "decoration": { + "id": "wp_arctic_station", "name": "Arctic Station", "type": "decoration", + "subtype": "wallpaper", "rarity": "uncommon", "price": 1000, "currency": "credits", + "description": "Crisp whites and icy blues. A frozen outpost at the edge of the galaxy.", + "texture": "assets/gso/textures/decorations/wp_arctic_station.png", + "preview": { "floorColorEven": "#0e1820", "floorColorOdd": "#0c1418", + "wallColor": "#aaddff", "wallColorLeft": "#0c1828", "wallColorRight": "#0a1420", "wallColorTop": "#122030", + "doorColor": "#ddeeff", "doorFrameColor": "#88ccff" }, + "categories": ["shop", "decoration"], "stackable": false + }} +} diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_deep_ocean.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_deep_ocean.json new file mode 100644 index 0000000..b490ba2 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_deep_ocean.json @@ -0,0 +1,12 @@ +{ + "templates": { "decoration": { + "id": "wp_deep_ocean", "name": "Deep Ocean", "type": "decoration", + "subtype": "wallpaper", "rarity": "uncommon", "price": 900, "currency": "credits", + "description": "Bioluminescent blues and teals. Like a starbase built beneath an alien sea.", + "texture": "assets/gso/textures/decorations/wp_deep_ocean.png", + "preview": { "floorColorEven": "#081820", "floorColorOdd": "#061418", + "wallColor": "#00cccc", "wallColorLeft": "#062030", "wallColorRight": "#041820", "wallColorTop": "#083040", + "doorColor": "#00ffee", "doorFrameColor": "#00bbcc" }, + "categories": ["shop", "decoration"], "stackable": false + }} +} diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_golden_empire.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_golden_empire.json new file mode 100644 index 0000000..58a1e50 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_golden_empire.json @@ -0,0 +1,12 @@ +{ + "templates": { "decoration": { + "id": "wp_golden_empire", "name": "Golden Empire", "type": "decoration", + "subtype": "wallpaper", "rarity": "epic", "price": 150, "currency": "gems", + "description": "Opulent golds and deep bronzes. Reserved for commanders of the highest rank.", + "texture": "assets/gso/textures/decorations/wp_golden_empire.png", + "preview": { "floorColorEven": "#1a1200", "floorColorOdd": "#140e00", + "wallColor": "#ffcc00", "wallColorLeft": "#221500", "wallColorRight": "#1a1000", "wallColorTop": "#2e1c00", + "doorColor": "#ffee88", "doorFrameColor": "#ddaa00" }, + "categories": ["shop", "decoration"], "stackable": false + }} +} diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_lava_forge.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_lava_forge.json new file mode 100644 index 0000000..70e1e62 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_lava_forge.json @@ -0,0 +1,12 @@ +{ + "templates": { "decoration": { + "id": "wp_lava_forge", "name": "Lava Forge", "type": "decoration", + "subtype": "wallpaper", "rarity": "rare", "price": 2000, "currency": "credits", + "description": "Volcanic orange and molten red. A station carved from the heart of a dying star.", + "texture": "assets/gso/textures/decorations/wp_lava_forge.png", + "preview": { "floorColorEven": "#1e0a00", "floorColorOdd": "#180800", + "wallColor": "#ff4400", "wallColorLeft": "#2a0800", "wallColorRight": "#200600", "wallColorTop": "#350a00", + "doorColor": "#ffaa00", "doorFrameColor": "#ff6600" }, + "categories": ["shop", "decoration"], "stackable": false + }} +} diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_nebula_dawn.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_nebula_dawn.json new file mode 100644 index 0000000..fb87312 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_nebula_dawn.json @@ -0,0 +1,12 @@ +{ + "templates": { "decoration": { + "id": "wp_nebula_dawn", "name": "Nebula Dawn", "type": "decoration", + "subtype": "wallpaper", "rarity": "uncommon", "price": 800, "currency": "credits", + "description": "Soft pink and purple nebula clouds. A peaceful dawn among the stars.", + "texture": "assets/gso/textures/decorations/wp_nebula_dawn.png", + "preview": { "floorColorEven": "#1a1228", "floorColorOdd": "#150e20", + "wallColor": "#cc66ff", "wallColorLeft": "#1a0a28", "wallColorRight": "#140820", "wallColorTop": "#220d35", + "doorColor": "#ff88ff", "doorFrameColor": "#cc44cc" }, + "categories": ["shop", "decoration"], "stackable": false + }} +} diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_void_black.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_void_black.json new file mode 100644 index 0000000..4632c99 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_void_black.json @@ -0,0 +1,12 @@ +{ + "templates": { "decoration": { + "id": "wp_void_black", "name": "Void Black", "type": "decoration", + "subtype": "wallpaper", "rarity": "rare", "price": 1500, "currency": "credits", + "description": "Absolute darkness. The walls absorb all light — only edges glow.", + "texture": "assets/gso/textures/decorations/wp_void_black.png", + "preview": { "floorColorEven": "#060608", "floorColorOdd": "#040406", + "wallColor": "#444455", "wallColorLeft": "#0a0a0f", "wallColorRight": "#060608", "wallColorTop": "#111118", + "doorColor": "#8888ff", "doorFrameColor": "#6666cc" }, + "categories": ["shop", "decoration"], "stackable": false + }} +} diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_void_rift.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_void_rift.json new file mode 100644 index 0000000..2321673 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/decorations/wp_void_rift.json @@ -0,0 +1,12 @@ +{ + "templates": { "decoration": { + "id": "wp_void_rift", "name": "Void Rift", "type": "decoration", + "subtype": "wallpaper", "rarity": "legendary", "price": 0, "currency": "credits", + "description": "Fractured space-time tears through the walls. Only found deep in the Void Rift.", + "texture": "assets/gso/textures/decorations/wp_void_rift.png", + "preview": { "floorColorEven": "#060010", "floorColorOdd": "#04000c", + "wallColor": "#8800ff", "wallColorLeft": "#0c0020", "wallColorRight": "#080018", "wallColorTop": "#100028", + "doorColor": "#cc44ff", "doorFrameColor": "#9900ee" }, + "categories": ["dungeon_loot", "decoration"], "stackable": false + }} +} diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/ballistic_alloy_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/ballistic_alloy_common.json new file mode 100644 index 0000000..d646b25 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/ballistic_alloy_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "ballistic_alloy_common", + "texture": "assets/gso/textures/hullPlating/misc/ballistic_alloy.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/carbon_titanium_composite_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/carbon_titanium_composite_common.json new file mode 100644 index 0000000..238220e --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/carbon_titanium_composite_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "carbon_titanium_composite_common", + "texture": "assets/gso/textures/hullPlating/misc/carbon_titanium_composite.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/chrono_alloy_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/chrono_alloy_common.json new file mode 100644 index 0000000..c24c500 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/chrono_alloy_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "chrono_alloy_common", + "texture": "assets/gso/textures/hullPlating/misc/chrono_alloy.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/dimensional_alloy_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/dimensional_alloy_common.json new file mode 100644 index 0000000..bd6d924 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/dimensional_alloy_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "dimensional_alloy_common", + "texture": "assets/gso/textures/hullPlating/misc/dimensional_alloy.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/flux_core_alloy_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/flux_core_alloy_common.json new file mode 100644 index 0000000..79c38ac --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/flux_core_alloy_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "flux_core_alloy_common", + "texture": "assets/gso/textures/hullPlating/misc/flux_core_alloy.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/neutronium_composite_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/neutronium_composite_common.json new file mode 100644 index 0000000..5ee776e --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/neutronium_composite_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "neutronium_composite_common", + "texture": "assets/gso/textures/hullPlating/misc/neutronium_composite.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/phase_alloy_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/phase_alloy_common.json new file mode 100644 index 0000000..e598e36 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/phase_alloy_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "phase_alloy_common", + "texture": "assets/gso/textures/hullPlating/misc/phase_alloy.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/plasma_channel_alloy_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/plasma_channel_alloy_common.json new file mode 100644 index 0000000..9e6a70f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/plasma_channel_alloy_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "plasma_channel_alloy_common", + "texture": "assets/gso/textures/hullPlating/misc/plasma_channel_alloy.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/reinforced_steel_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/reinforced_steel_common.json new file mode 100644 index 0000000..f987a2c --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/reinforced_steel_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "reinforced_steel_common", + "texture": "assets/gso/textures/hullPlating/misc/reinforced_steel.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/superconductive_alloy_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/superconductive_alloy_common.json new file mode 100644 index 0000000..b6f44cb --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/superconductive_alloy_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "superconductive_alloy_common", + "texture": "assets/gso/textures/hullPlating/misc/superconductive_alloy.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/voidsteel_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/voidsteel_common.json new file mode 100644 index 0000000..27bbd6d --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/misc/voidsteel_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "voidsteel_common", + "texture": "assets/gso/textures/hullPlating/misc/voidsteel.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/plating/ablative_plating_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/plating/ablative_plating_common.json new file mode 100644 index 0000000..096cc7c --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/plating/ablative_plating_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "ablative_plating_common", + "texture": "assets/gso/textures/hullPlating/plating/ablative_plating.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/section/advanced_hull_section_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/section/advanced_hull_section_common.json new file mode 100644 index 0000000..d83b4c0 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/section/advanced_hull_section_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "advanced_hull_section_common", + "texture": "assets/gso/textures/hullPlating/section/advanced_hull_section.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/section/armor_hull_section_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/section/armor_hull_section_common.json new file mode 100644 index 0000000..06f0106 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/section/armor_hull_section_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "armor_hull_section_common", + "texture": "assets/gso/textures/hullPlating/section/armor_hull_section.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/section/exotic_hull_section_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/section/exotic_hull_section_common.json new file mode 100644 index 0000000..40f22f6 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/section/exotic_hull_section_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "exotic_hull_section_common", + "texture": "assets/gso/textures/hullPlating/section/exotic_hull_section.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/section/heavy_hull_section_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/section/heavy_hull_section_common.json new file mode 100644 index 0000000..9be3680 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/section/heavy_hull_section_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "heavy_hull_section_common", + "texture": "assets/gso/textures/hullPlating/section/heavy_hull_section.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/section/light_hull_section_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/section/light_hull_section_common.json new file mode 100644 index 0000000..72d22a2 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/section/light_hull_section_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "light_hull_section_common", + "texture": "assets/gso/textures/hullPlating/section/light_hull_section.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/section/standard_hull_section_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/section/standard_hull_section_common.json new file mode 100644 index 0000000..731d0a8 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/hullPlating/section/standard_hull_section_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "standard_hull_section_common", + "texture": "assets/gso/textures/hullPlating/section/standard_hull_section.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/manifest.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/manifest.json new file mode 100644 index 0000000..1beaf29 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/manifest.json @@ -0,0 +1,20 @@ +{ + "counts_by_type": { + "ships": 8, + "weapons": 8, + "armours": 9, + "consumables": 9, + "materials": { + "ore": 9, + "ingot": 10, + "chemical": 10, + "electronics": 10, + "misc": 10 + } + }, + "notes": [ + "One JSON per unique item. Grouped into data/gso/items///", + "Texture paths point into assets/gso/textures///.png", + "Server loads all items via ContentLoader.loadAllItems()" + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/bio/bio_pulp.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/bio/bio_pulp.json new file mode 100644 index 0000000..9694a7d --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/bio/bio_pulp.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "bio_pulp_common", + "texture": "assets/gso/textures/materials/bio/bio_pulp.png", + "stats": [], + "meta": { + "materialCategory": "bio" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/bio/organic_insulation.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/bio/organic_insulation.json new file mode 100644 index 0000000..77b72e7 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/bio/organic_insulation.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "organic_insulation_common", + "texture": "assets/gso/textures/materials/bio/organic_insulation.png", + "stats": [], + "meta": { + "materialCategory": "bio" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/bio/plant_matter.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/bio/plant_matter.json new file mode 100644 index 0000000..901dafd --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/bio/plant_matter.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "plant_matter_common", + "texture": "assets/gso/textures/materials/bio/plant_matter.png", + "stats": [], + "meta": { + "materialCategory": "bio" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/aluminum_alloy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/aluminum_alloy.json new file mode 100644 index 0000000..3a475d7 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/aluminum_alloy.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "aluminum_alloy_common", + "texture": "assets/gso/textures/materials/chemical/aluminum_alloy.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/bio_composite_material.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/bio_composite_material.json new file mode 100644 index 0000000..f7f90da --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/bio_composite_material.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "bio_composite_material_common", + "texture": "assets/gso/textures/materials/chemical/bio_composite_material.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/carbon_nanotube_alloy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/carbon_nanotube_alloy.json new file mode 100644 index 0000000..70cb067 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/carbon_nanotube_alloy.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "carbon_nanotube_alloy_common", + "texture": "assets/gso/textures/materials/chemical/carbon_nanotube_alloy.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/carbon_titanium_composite.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/carbon_titanium_composite.json new file mode 100644 index 0000000..a69db9d --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/carbon_titanium_composite.json @@ -0,0 +1,16 @@ +{ + "templates": { + "materials": { + "id": "carbon_titanium_composite", + "name": "Carbon-Titanium Composite", + "meta": { + "materialCategory": "chemical" + }, + "texture": "assets/gso/textures/materials/chemical/carbon_titanium_composite.png", + "price": 500, + "rarity": "rare", + "stackable": true, + "maxStack": 200 + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/ceramic_composite.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/ceramic_composite.json new file mode 100644 index 0000000..ef39f66 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/ceramic_composite.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "ceramic_composite_common", + "texture": "assets/gso/textures/materials/chemical/ceramic_composite.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/chrono_alloy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/chrono_alloy.json new file mode 100644 index 0000000..00dd70d --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/chrono_alloy.json @@ -0,0 +1,16 @@ +{ + "templates": { + "materials": { + "id": "chrono_alloy", + "name": "Chrono Alloy", + "meta": { + "materialCategory": "chemical" + }, + "texture": "assets/gso/textures/materials/chemical/chrono_alloy.png", + "price": 2000, + "rarity": "legendary", + "stackable": true, + "maxStack": 50 + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/dimensional_alloy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/dimensional_alloy.json new file mode 100644 index 0000000..25e33f4 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/dimensional_alloy.json @@ -0,0 +1,16 @@ +{ + "templates": { + "materials": { + "id": "dimensional_alloy", + "name": "Dimensional Alloy", + "meta": { + "materialCategory": "chemical" + }, + "texture": "assets/gso/textures/materials/chemical/dimensional_alloy.png", + "price": 5000, + "rarity": "legendary", + "stackable": true, + "maxStack": 25 + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/explosive_compound.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/explosive_compound.json new file mode 100644 index 0000000..dfd4c0c --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/explosive_compound.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "explosive_compound_common", + "texture": "assets/gso/textures/materials/chemical/explosive_compound.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/flex_alloy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/flex_alloy.json new file mode 100644 index 0000000..84ba999 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/flex_alloy.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "flex_alloy_common", + "texture": "assets/gso/textures/materials/chemical/flex_alloy.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/flux_core_alloy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/flux_core_alloy.json new file mode 100644 index 0000000..b9cebdd --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/flux_core_alloy.json @@ -0,0 +1,16 @@ +{ + "templates": { + "materials": { + "id": "flux_core_alloy", + "name": "Flux Core Alloy", + "meta": { + "materialCategory": "chemical" + }, + "texture": "assets/gso/textures/materials/chemical/flux_core_alloy.png", + "price": 600, + "rarity": "rare", + "stackable": true, + "maxStack": 200 + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/fusion_rated_alloy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/fusion_rated_alloy.json new file mode 100644 index 0000000..6a1c270 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/fusion_rated_alloy.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "fusion_rated_alloy_common", + "texture": "assets/gso/textures/materials/chemical/fusion_rated_alloy.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/glass_composite.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/glass_composite.json new file mode 100644 index 0000000..71ec3dc --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/glass_composite.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "glass_composite_common", + "texture": "assets/gso/textures/materials/chemical/glass_composite.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/neutronium_composite.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/neutronium_composite.json new file mode 100644 index 0000000..5ccf2f4 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/neutronium_composite.json @@ -0,0 +1,16 @@ +{ + "templates": { + "materials": { + "id": "neutronium_composite", + "name": "Neutronium Composite", + "meta": { + "materialCategory": "chemical" + }, + "texture": "assets/gso/textures/materials/chemical/neutronium_composite.png", + "price": 3000, + "rarity": "legendary", + "stackable": true, + "maxStack": 50 + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/phase_alloy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/phase_alloy.json new file mode 100644 index 0000000..1f01059 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/phase_alloy.json @@ -0,0 +1,16 @@ +{ + "templates": { + "materials": { + "id": "phase_alloy", + "name": "Phase Alloy", + "meta": { + "materialCategory": "chemical" + }, + "texture": "assets/gso/textures/materials/chemical/phase_alloy.png", + "price": 800, + "rarity": "epic", + "stackable": true, + "maxStack": 100 + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/radiation_shield_alloy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/radiation_shield_alloy.json new file mode 100644 index 0000000..2cfea79 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/radiation_shield_alloy.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "radiation_shield_alloy_common", + "texture": "assets/gso/textures/materials/chemical/radiation_shield_alloy.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/reactive_armor_alloy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/reactive_armor_alloy.json new file mode 100644 index 0000000..93ac7fe --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/reactive_armor_alloy.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "reactive_armor_alloy_common", + "texture": "assets/gso/textures/materials/chemical/reactive_armor_alloy.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/reinforced_steel.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/reinforced_steel.json new file mode 100644 index 0000000..85cc041 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/reinforced_steel.json @@ -0,0 +1,16 @@ +{ + "templates": { + "materials": { + "id": "reinforced_steel", + "name": "Reinforced Steel", + "meta": { + "materialCategory": "chemical" + }, + "texture": "assets/gso/textures/materials/chemical/reinforced_steel.png", + "price": 120, + "rarity": "common", + "stackable": true, + "maxStack": 500 + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/resin.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/resin.json new file mode 100644 index 0000000..97e5611 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/resin.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "resin_common", + "texture": "assets/gso/textures/materials/chemical/resin.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/rubber_polymer.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/rubber_polymer.json new file mode 100644 index 0000000..577cc78 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/rubber_polymer.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "rubber_polymer_common", + "texture": "assets/gso/textures/materials/chemical/rubber_polymer.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/servo_alloy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/servo_alloy.json new file mode 100644 index 0000000..2d20cd4 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/servo_alloy.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "servo_alloy_common", + "texture": "assets/gso/textures/materials/chemical/servo_alloy.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/steel_alloy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/steel_alloy.json new file mode 100644 index 0000000..84b7c43 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/steel_alloy.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "steel_alloy_common", + "texture": "assets/gso/textures/materials/chemical/steel_alloy.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/titanium_alloy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/titanium_alloy.json new file mode 100644 index 0000000..3ef1d08 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/titanium_alloy.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "titanium_alloy_common", + "texture": "assets/gso/textures/materials/chemical/titanium_alloy.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/transparent_alloy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/transparent_alloy.json new file mode 100644 index 0000000..888adba --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/transparent_alloy.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "transparent_alloy_common", + "texture": "assets/gso/textures/materials/chemical/transparent_alloy.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/tungsten_alloy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/tungsten_alloy.json new file mode 100644 index 0000000..1ff843a --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/tungsten_alloy.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "tungsten_alloy_common", + "texture": "assets/gso/textures/materials/chemical/tungsten_alloy.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/voidsteel.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/voidsteel.json new file mode 100644 index 0000000..20464b1 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/chemical/voidsteel.json @@ -0,0 +1,16 @@ +{ + "templates": { + "materials": { + "id": "voidsteel", + "name": "Voidsteel", + "meta": { + "materialCategory": "chemical" + }, + "texture": "assets/gso/textures/materials/chemical/voidsteel.png", + "price": 1200, + "rarity": "epic", + "stackable": true, + "maxStack": 100 + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/ai_core_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/ai_core_circuit.json new file mode 100644 index 0000000..3aaa251 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/ai_core_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "ai_core_circuit_common", + "texture": "assets/gso/textures/materials/electronics/ai_core_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/basic_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/basic_circuit.json new file mode 100644 index 0000000..785c0a7 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/basic_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "basic_circuit_common", + "texture": "assets/gso/textures/materials/electronics/basic_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/control_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/control_circuit.json new file mode 100644 index 0000000..65d1d4a --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/control_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "control_circuit_common", + "texture": "assets/gso/textures/materials/electronics/control_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/dimensional_logic_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/dimensional_logic_circuit.json new file mode 100644 index 0000000..126c3fc --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/dimensional_logic_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "dimensional_logic_circuit_common", + "texture": "assets/gso/textures/materials/electronics/dimensional_logic_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/high_density_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/high_density_circuit.json new file mode 100644 index 0000000..6aa4e33 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/high_density_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "high_density_circuit_common", + "texture": "assets/gso/textures/materials/electronics/high_density_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/logic_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/logic_circuit.json new file mode 100644 index 0000000..47a1720 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/logic_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "logic_circuit_common", + "texture": "assets/gso/textures/materials/electronics/logic_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/navigation_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/navigation_circuit.json new file mode 100644 index 0000000..92b938d --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/navigation_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "navigation_circuit_common", + "texture": "assets/gso/textures/materials/electronics/navigation_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/power_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/power_circuit.json new file mode 100644 index 0000000..88b7ad2 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/power_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "power_circuit_common", + "texture": "assets/gso/textures/materials/electronics/power_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/processor_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/processor_circuit.json new file mode 100644 index 0000000..155c515 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/processor_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "processor_circuit_common", + "texture": "assets/gso/textures/materials/electronics/processor_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/quantum_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/quantum_circuit.json new file mode 100644 index 0000000..c5c7fa9 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/quantum_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "quantum_circuit_common", + "texture": "assets/gso/textures/materials/electronics/quantum_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/reactor_control_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/reactor_control_circuit.json new file mode 100644 index 0000000..4087bde --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/reactor_control_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "reactor_control_circuit_common", + "texture": "assets/gso/textures/materials/electronics/reactor_control_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/sensor_array_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/sensor_array_circuit.json new file mode 100644 index 0000000..e07e563 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/sensor_array_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "sensor_array_circuit_common", + "texture": "assets/gso/textures/materials/electronics/sensor_array_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/sentient_core_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/sentient_core_circuit.json new file mode 100644 index 0000000..0db17ab --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/sentient_core_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "sentient_core_circuit_common", + "texture": "assets/gso/textures/materials/electronics/sentient_core_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/shield_control_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/shield_control_circuit.json new file mode 100644 index 0000000..e1dd4b1 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/shield_control_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "shield_control_circuit_common", + "texture": "assets/gso/textures/materials/electronics/shield_control_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/stealth_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/stealth_circuit.json new file mode 100644 index 0000000..3345f32 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/stealth_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "stealth_circuit_common", + "texture": "assets/gso/textures/materials/electronics/stealth_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/warp_navigation_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/warp_navigation_circuit.json new file mode 100644 index 0000000..ee3be9b --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/warp_navigation_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "warp_navigation_circuit_common", + "texture": "assets/gso/textures/materials/electronics/warp_navigation_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/weapon_control_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/weapon_control_circuit.json new file mode 100644 index 0000000..c0b8707 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/electronics/weapon_control_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "weapon_control_circuit_common", + "texture": "assets/gso/textures/materials/electronics/weapon_control_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/fabric/plant_cloth.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/fabric/plant_cloth.json new file mode 100644 index 0000000..5f8be11 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/fabric/plant_cloth.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "plant_cloth_common", + "texture": "assets/gso/textures/materials/fabric/plant_cloth.png", + "stats": [], + "meta": { + "materialCategory": "fabric" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/fabric/plant_fiber.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/fabric/plant_fiber.json new file mode 100644 index 0000000..2b3b757 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/fabric/plant_fiber.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "plant_fiber_common", + "texture": "assets/gso/textures/materials/fabric/plant_fiber.png", + "stats": [], + "meta": { + "materialCategory": "fabric" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/bread.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/bread.json new file mode 100644 index 0000000..d53913a --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/bread.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "bread_common", + "texture": "assets/gso/textures/materials/food/bread.png", + "stats": [], + "meta": { + "materialCategory": "food" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/fish.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/fish.json new file mode 100644 index 0000000..de1c1dd --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/fish.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "fish_common", + "texture": "assets/gso/textures/materials/food/fish.png", + "stats": [], + "meta": { + "materialCategory": "food" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/fish_meat.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/fish_meat.json new file mode 100644 index 0000000..cbbca71 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/fish_meat.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "fish_meat_common", + "texture": "assets/gso/textures/materials/food/fish_meat.png", + "stats": [], + "meta": { + "materialCategory": "food" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/flour.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/flour.json new file mode 100644 index 0000000..51214b6 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/flour.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "flour_common", + "texture": "assets/gso/textures/materials/food/flour.png", + "stats": [], + "meta": { + "materialCategory": "food" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/fruit_plant.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/fruit_plant.json new file mode 100644 index 0000000..2dd83c4 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/fruit_plant.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "fruit_plant_common", + "texture": "assets/gso/textures/materials/food/fruit_plant.png", + "stats": [], + "meta": { + "materialCategory": "food" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/grain.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/grain.json new file mode 100644 index 0000000..7c217eb --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/grain.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "grain_common", + "texture": "assets/gso/textures/materials/food/grain.png", + "stats": [], + "meta": { + "materialCategory": "food" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/grain_crop.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/grain_crop.json new file mode 100644 index 0000000..4d94bf6 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/grain_crop.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "grain_crop_common", + "texture": "assets/gso/textures/materials/food/grain_crop.png", + "stats": [], + "meta": { + "materialCategory": "food" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/meat_stew.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/meat_stew.json new file mode 100644 index 0000000..7708a18 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/meat_stew.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "meat_stew_common", + "texture": "assets/gso/textures/materials/food/meat_stew.png", + "stats": [], + "meta": { + "materialCategory": "food" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/ration_pack.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/ration_pack.json new file mode 100644 index 0000000..080b4cd --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/ration_pack.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "ration_pack_common", + "texture": "assets/gso/textures/materials/food/ration_pack.png", + "stats": [], + "meta": { + "materialCategory": "food" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/vegetable_plant.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/vegetable_plant.json new file mode 100644 index 0000000..617b2cc --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/vegetable_plant.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "vegetable_plant_common", + "texture": "assets/gso/textures/materials/food/vegetable_plant.png", + "stats": [], + "meta": { + "materialCategory": "food" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/vegetable_stew.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/vegetable_stew.json new file mode 100644 index 0000000..d701e38 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/food/vegetable_stew.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "vegetable_stew_common", + "texture": "assets/gso/textures/materials/food/vegetable_stew.png", + "stats": [], + "meta": { + "materialCategory": "food" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/aluminum_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/aluminum_ingot.json new file mode 100644 index 0000000..ce236e0 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/aluminum_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "aluminum_ingot_common", + "texture": "assets/gso/textures/materials/ingot/aluminum_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/carbon_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/carbon_ingot.json new file mode 100644 index 0000000..e4b6c3b --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/carbon_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "carbon_ingot_common", + "texture": "assets/gso/textures/materials/ingot/carbon_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/chromium_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/chromium_ingot.json new file mode 100644 index 0000000..8d7e546 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/chromium_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "chromium_ingot_common", + "texture": "assets/gso/textures/materials/ingot/chromium_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/chronium_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/chronium_ingot.json new file mode 100644 index 0000000..cdcf576 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/chronium_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "chronium_ingot_common", + "texture": "assets/gso/textures/materials/ingot/chronium_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/copper_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/copper_ingot.json new file mode 100644 index 0000000..e138c02 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/copper_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "copper_ingot_common", + "texture": "assets/gso/textures/materials/ingot/copper_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/darksteel_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/darksteel_ingot.json new file mode 100644 index 0000000..7f1f87f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/darksteel_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "darksteel_ingot_common", + "texture": "assets/gso/textures/materials/ingot/darksteel_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/gold_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/gold_ingot.json new file mode 100644 index 0000000..5daff67 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/gold_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "gold_ingot_common", + "texture": "assets/gso/textures/materials/ingot/gold_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/iron_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/iron_ingot.json new file mode 100644 index 0000000..fc720b8 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/iron_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "iron_ingot_common", + "texture": "assets/gso/textures/materials/ingot/iron_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/magnesium_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/magnesium_ingot.json new file mode 100644 index 0000000..2aa68f8 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/magnesium_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "magnesium_ingot_common", + "texture": "assets/gso/textures/materials/ingot/magnesium_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/neutronium_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/neutronium_ingot.json new file mode 100644 index 0000000..c41551b --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/neutronium_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "neutronium_ingot_common", + "texture": "assets/gso/textures/materials/ingot/neutronium_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/platinum_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/platinum_ingot.json new file mode 100644 index 0000000..f0facc6 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/platinum_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "platinum_ingot_common", + "texture": "assets/gso/textures/materials/ingot/platinum_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/silver_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/silver_ingot.json new file mode 100644 index 0000000..c1bf929 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/silver_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "silver_ingot_common", + "texture": "assets/gso/textures/materials/ingot/silver_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/titanium_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/titanium_ingot.json new file mode 100644 index 0000000..bee6654 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/titanium_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "titanium_ingot_common", + "texture": "assets/gso/textures/materials/ingot/titanium_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/tungsten_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/tungsten_ingot.json new file mode 100644 index 0000000..04cd018 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ingot/tungsten_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "tungsten_ingot_common", + "texture": "assets/gso/textures/materials/ingot/tungsten_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/liquid/water.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/liquid/water.json new file mode 100644 index 0000000..a582e19 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/liquid/water.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "water_common", + "texture": "assets/gso/textures/materials/liquid/water.png", + "stats": [], + "meta": { + "materialCategory": "liquid" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/aerospace_aluminum.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/aerospace_aluminum.json new file mode 100644 index 0000000..44e03d4 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/aerospace_aluminum.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "aerospace_aluminum_common", + "texture": "assets/gso/textures/materials/misc/aerospace_aluminum.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/alien_fauna_sample.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/alien_fauna_sample.json new file mode 100644 index 0000000..12c4fca --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/alien_fauna_sample.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "alien_fauna_sample_common", + "texture": "assets/gso/textures/materials/misc/alien_fauna_sample.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/alien_protein.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/alien_protein.json new file mode 100644 index 0000000..e2f0032 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/alien_protein.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "alien_protein_common", + "texture": "assets/gso/textures/materials/misc/alien_protein.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/animal_carcass.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/animal_carcass.json new file mode 100644 index 0000000..a5f7989 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/animal_carcass.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "animal_carcass_common", + "texture": "assets/gso/textures/materials/misc/animal_carcass.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/bandage.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/bandage.json new file mode 100644 index 0000000..99b667c --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/bandage.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "bandage_common", + "texture": "assets/gso/textures/materials/misc/bandage.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/body_exotic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/body_exotic.json new file mode 100644 index 0000000..1ac7c69 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/body_exotic.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "body_exotic_common", + "texture": "assets/gso/textures/materials/misc/body_exotic.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/body_light.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/body_light.json new file mode 100644 index 0000000..47f0e18 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/body_light.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "body_light_common", + "texture": "assets/gso/textures/materials/misc/body_light.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/body_military.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/body_military.json new file mode 100644 index 0000000..ed810d9 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/body_military.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "body_military_common", + "texture": "assets/gso/textures/materials/misc/body_military.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/body_standard.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/body_standard.json new file mode 100644 index 0000000..865e876 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/body_standard.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "body_standard_common", + "texture": "assets/gso/textures/materials/misc/body_standard.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/boots_light.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/boots_light.json new file mode 100644 index 0000000..cd3f5a8 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/boots_light.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "boots_light_common", + "texture": "assets/gso/textures/materials/misc/boots_light.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/boots_magnetic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/boots_magnetic.json new file mode 100644 index 0000000..b773376 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/boots_magnetic.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "boots_magnetic_common", + "texture": "assets/gso/textures/materials/misc/boots_magnetic.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/boots_military.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/boots_military.json new file mode 100644 index 0000000..7b16a29 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/boots_military.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "boots_military_common", + "texture": "assets/gso/textures/materials/misc/boots_military.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/boots_standard.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/boots_standard.json new file mode 100644 index 0000000..502e2a7 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/boots_standard.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "boots_standard_common", + "texture": "assets/gso/textures/materials/misc/boots_standard.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/carbon_shale.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/carbon_shale.json new file mode 100644 index 0000000..4f0011f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/carbon_shale.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "carbon_shale_common", + "texture": "assets/gso/textures/materials/misc/carbon_shale.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/chromium_steel.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/chromium_steel.json new file mode 100644 index 0000000..5a38bd2 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/chromium_steel.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "chromium_steel_common", + "texture": "assets/gso/textures/materials/misc/chromium_steel.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/cooked_fish.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/cooked_fish.json new file mode 100644 index 0000000..04fad5b --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/cooked_fish.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "cooked_fish_common", + "texture": "assets/gso/textures/materials/misc/cooked_fish.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/cooked_meat.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/cooked_meat.json new file mode 100644 index 0000000..f744a82 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/cooked_meat.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "cooked_meat_common", + "texture": "assets/gso/textures/materials/misc/cooked_meat.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/crystal_glass.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/crystal_glass.json new file mode 100644 index 0000000..ed0dd5c --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/crystal_glass.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "crystal_glass_common", + "texture": "assets/gso/textures/materials/misc/crystal_glass.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/dough.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/dough.json new file mode 100644 index 0000000..eaba2dd --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/dough.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "dough_common", + "texture": "assets/gso/textures/materials/misc/dough.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/flux_crystal.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/flux_crystal.json new file mode 100644 index 0000000..9bc919f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/flux_crystal.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "flux_crystal_common", + "texture": "assets/gso/textures/materials/misc/flux_crystal.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/gloves_industrial.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/gloves_industrial.json new file mode 100644 index 0000000..b734c4b --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/gloves_industrial.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "gloves_industrial_common", + "texture": "assets/gso/textures/materials/misc/gloves_industrial.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/gloves_light.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/gloves_light.json new file mode 100644 index 0000000..90fb50d --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/gloves_light.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "gloves_light_common", + "texture": "assets/gso/textures/materials/misc/gloves_light.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/gloves_military.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/gloves_military.json new file mode 100644 index 0000000..b6df73e --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/gloves_military.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "gloves_military_common", + "texture": "assets/gso/textures/materials/misc/gloves_military.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/gloves_standard.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/gloves_standard.json new file mode 100644 index 0000000..37f29e8 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/gloves_standard.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "gloves_standard_common", + "texture": "assets/gso/textures/materials/misc/gloves_standard.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/helmet_exotic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/helmet_exotic.json new file mode 100644 index 0000000..ff48b87 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/helmet_exotic.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "helmet_exotic_common", + "texture": "assets/gso/textures/materials/misc/helmet_exotic.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/helmet_light.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/helmet_light.json new file mode 100644 index 0000000..79da9f9 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/helmet_light.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "helmet_light_common", + "texture": "assets/gso/textures/materials/misc/helmet_light.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/helmet_military.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/helmet_military.json new file mode 100644 index 0000000..d47135c --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/helmet_military.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "helmet_military_common", + "texture": "assets/gso/textures/materials/misc/helmet_military.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/helmet_standard.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/helmet_standard.json new file mode 100644 index 0000000..1211052 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/helmet_standard.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "helmet_standard_common", + "texture": "assets/gso/textures/materials/misc/helmet_standard.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/hydroponic_growth_medium.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/hydroponic_growth_medium.json new file mode 100644 index 0000000..82b2306 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/hydroponic_growth_medium.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "hydroponic_growth_medium_common", + "texture": "assets/gso/textures/materials/misc/hydroponic_growth_medium.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/ice_chunk.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/ice_chunk.json new file mode 100644 index 0000000..dca1c4d --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/ice_chunk.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "ice_chunk_common", + "texture": "assets/gso/textures/materials/misc/ice_chunk.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/iron_ore_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/iron_ore_common.json new file mode 100644 index 0000000..e382e63 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/iron_ore_common.json @@ -0,0 +1,16 @@ +{ + "templates": { + "materials": { + "id": "iron_ore_common", + "name": "Raw Iron Chunk", + "meta": { + "materialCategory": "misc" + }, + "texture": "assets/gso/textures/materials/misc/iron_ore.png", + "price": 5, + "rarity": "common", + "stackable": true, + "maxStack": 999 + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/life_support_basic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/life_support_basic.json new file mode 100644 index 0000000..0bacdba --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/life_support_basic.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "life_support_basic_common", + "texture": "assets/gso/textures/materials/misc/life_support_basic.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/life_support_exotic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/life_support_exotic.json new file mode 100644 index 0000000..6b7e48d --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/life_support_exotic.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "life_support_exotic_common", + "texture": "assets/gso/textures/materials/misc/life_support_exotic.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/life_support_military.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/life_support_military.json new file mode 100644 index 0000000..99e6d10 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/life_support_military.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "life_support_military_common", + "texture": "assets/gso/textures/materials/misc/life_support_military.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/life_support_standard.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/life_support_standard.json new file mode 100644 index 0000000..e595921 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/life_support_standard.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "life_support_standard_common", + "texture": "assets/gso/textures/materials/misc/life_support_standard.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/luxury_meal.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/luxury_meal.json new file mode 100644 index 0000000..c172420 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/luxury_meal.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "luxury_meal_common", + "texture": "assets/gso/textures/materials/misc/luxury_meal.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/magnetic_coil.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/magnetic_coil.json new file mode 100644 index 0000000..6821764 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/magnetic_coil.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "magnetic_coil_common", + "texture": "assets/gso/textures/materials/misc/magnetic_coil.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/medical_pack.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/medical_pack.json new file mode 100644 index 0000000..a383c69 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/medical_pack.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "medical_pack_common", + "texture": "assets/gso/textures/materials/misc/medical_pack.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/military_ration.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/military_ration.json new file mode 100644 index 0000000..0d32df0 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/military_ration.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "military_ration_common", + "texture": "assets/gso/textures/materials/misc/military_ration.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/neutronium_shard.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/neutronium_shard.json new file mode 100644 index 0000000..ac6591e --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/neutronium_shard.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "neutronium_shard_common", + "texture": "assets/gso/textures/materials/misc/neutronium_shard.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/nutrient_paste.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/nutrient_paste.json new file mode 100644 index 0000000..5f7ba9f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/nutrient_paste.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "nutrient_paste_common", + "texture": "assets/gso/textures/materials/misc/nutrient_paste.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/pants_exotic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/pants_exotic.json new file mode 100644 index 0000000..f321099 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/pants_exotic.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "pants_exotic_common", + "texture": "assets/gso/textures/materials/misc/pants_exotic.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/pants_heavy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/pants_heavy.json new file mode 100644 index 0000000..4e7b70a --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/pants_heavy.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "pants_heavy_common", + "texture": "assets/gso/textures/materials/misc/pants_heavy.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/pants_light.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/pants_light.json new file mode 100644 index 0000000..e54f1b0 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/pants_light.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "pants_light_common", + "texture": "assets/gso/textures/materials/misc/pants_light.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/pants_standard.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/pants_standard.json new file mode 100644 index 0000000..8bbd979 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/pants_standard.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "pants_standard_common", + "texture": "assets/gso/textures/materials/misc/pants_standard.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/phase_crystal.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/phase_crystal.json new file mode 100644 index 0000000..0365dbb --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/phase_crystal.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "phase_crystal_common", + "texture": "assets/gso/textures/materials/misc/phase_crystal.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/processed_wood.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/processed_wood.json new file mode 100644 index 0000000..c856c4a --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/processed_wood.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "processed_wood_common", + "texture": "assets/gso/textures/materials/misc/processed_wood.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/protein_bar.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/protein_bar.json new file mode 100644 index 0000000..c1e9309 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/protein_bar.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "protein_bar_common", + "texture": "assets/gso/textures/materials/misc/protein_bar.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/raw_fruits.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/raw_fruits.json new file mode 100644 index 0000000..2c89214 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/raw_fruits.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "raw_fruits_common", + "texture": "assets/gso/textures/materials/misc/raw_fruits.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/raw_meat.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/raw_meat.json new file mode 100644 index 0000000..04380f6 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/raw_meat.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "raw_meat_common", + "texture": "assets/gso/textures/materials/misc/raw_meat.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/raw_vegetables.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/raw_vegetables.json new file mode 100644 index 0000000..5eced33 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/raw_vegetables.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "raw_vegetables_common", + "texture": "assets/gso/textures/materials/misc/raw_vegetables.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/raw_wood.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/raw_wood.json new file mode 100644 index 0000000..e823203 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/raw_wood.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "raw_wood_common", + "texture": "assets/gso/textures/materials/misc/raw_wood.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/silicon_wafer.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/silicon_wafer.json new file mode 100644 index 0000000..bd28e1b --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/silicon_wafer.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "silicon_wafer_common", + "texture": "assets/gso/textures/materials/misc/silicon_wafer.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/tree.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/tree.json new file mode 100644 index 0000000..f0c9fd8 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/tree.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "tree_common", + "texture": "assets/gso/textures/materials/misc/tree.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/tree_sap.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/tree_sap.json new file mode 100644 index 0000000..2acae8e --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/tree_sap.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "tree_sap_common", + "texture": "assets/gso/textures/materials/misc/tree_sap.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/tungsten_steel.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/tungsten_steel.json new file mode 100644 index 0000000..072f02c --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/tungsten_steel.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "tungsten_steel_common", + "texture": "assets/gso/textures/materials/misc/tungsten_steel.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/void_crystal.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/void_crystal.json new file mode 100644 index 0000000..2959095 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/void_crystal.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "void_crystal_common", + "texture": "assets/gso/textures/materials/misc/void_crystal.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/void_residue.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/void_residue.json new file mode 100644 index 0000000..51b1851 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/void_residue.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "void_residue_common", + "texture": "assets/gso/textures/materials/misc/void_residue.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/wooden_components.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/wooden_components.json new file mode 100644 index 0000000..7e524b4 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/misc/wooden_components.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "wooden_components_common", + "texture": "assets/gso/textures/materials/misc/wooden_components.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/bauxite_ore.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/bauxite_ore.json new file mode 100644 index 0000000..67239c3 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/bauxite_ore.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "bauxite_ore_common", + "texture": "assets/gso/textures/materials/ore/bauxite_ore.png", + "stats": [], + "meta": { + "materialCategory": "ore" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/chromium_ore.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/chromium_ore.json new file mode 100644 index 0000000..4b2dfe6 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/chromium_ore.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "chromium_ore_common", + "texture": "assets/gso/textures/materials/ore/chromium_ore.png", + "stats": [], + "meta": { + "materialCategory": "ore" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/chronite_ore.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/chronite_ore.json new file mode 100644 index 0000000..8b3b6cc --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/chronite_ore.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "chronite_ore_common", + "texture": "assets/gso/textures/materials/ore/chronite_ore.png", + "stats": [], + "meta": { + "materialCategory": "ore" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/copper_ore.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/copper_ore.json new file mode 100644 index 0000000..ae7c850 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/copper_ore.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "copper_ore_common", + "texture": "assets/gso/textures/materials/ore/copper_ore.png", + "stats": [], + "meta": { + "materialCategory": "ore" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/dark_iron_ore.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/dark_iron_ore.json new file mode 100644 index 0000000..4a5b5b2 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/dark_iron_ore.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "dark_iron_ore_common", + "texture": "assets/gso/textures/materials/ore/dark_iron_ore.png", + "stats": [], + "meta": { + "materialCategory": "ore" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/gold_ore.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/gold_ore.json new file mode 100644 index 0000000..463cd2b --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/gold_ore.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "gold_ore_common", + "texture": "assets/gso/textures/materials/ore/gold_ore.png", + "stats": [], + "meta": { + "materialCategory": "ore" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/iron_ore.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/iron_ore.json new file mode 100644 index 0000000..bbd8d8a --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/iron_ore.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "iron_ore_common", + "texture": "assets/gso/textures/materials/ore/iron_ore.png", + "stats": [], + "meta": { + "materialCategory": "ore" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/magnesium_ore.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/magnesium_ore.json new file mode 100644 index 0000000..47f162e --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/magnesium_ore.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "magnesium_ore_common", + "texture": "assets/gso/textures/materials/ore/magnesium_ore.png", + "stats": [], + "meta": { + "materialCategory": "ore" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/platinum_ore.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/platinum_ore.json new file mode 100644 index 0000000..bd044b6 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/platinum_ore.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "platinum_ore_common", + "texture": "assets/gso/textures/materials/ore/platinum_ore.png", + "stats": [], + "meta": { + "materialCategory": "ore" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/silver_ore.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/silver_ore.json new file mode 100644 index 0000000..93a220f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/silver_ore.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "silver_ore_common", + "texture": "assets/gso/textures/materials/ore/silver_ore.png", + "stats": [], + "meta": { + "materialCategory": "ore" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/titanium_ore.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/titanium_ore.json new file mode 100644 index 0000000..045c5ca --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/titanium_ore.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "titanium_ore_common", + "texture": "assets/gso/textures/materials/ore/titanium_ore.png", + "stats": [], + "meta": { + "materialCategory": "ore" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/tungsten_ore.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/tungsten_ore.json new file mode 100644 index 0000000..56ee49f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/ore/tungsten_ore.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "tungsten_ore_common", + "texture": "assets/gso/textures/materials/ore/tungsten_ore.png", + "stats": [], + "meta": { + "materialCategory": "ore" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/stone/fluxstone.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/stone/fluxstone.json new file mode 100644 index 0000000..073b760 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/stone/fluxstone.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "fluxstone_common", + "texture": "assets/gso/textures/materials/stone/fluxstone.png", + "stats": [], + "meta": { + "materialCategory": "stone" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/wood/wood_planks.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/wood/wood_planks.json new file mode 100644 index 0000000..bdc7092 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/materials/wood/wood_planks.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "wood_planks_common", + "texture": "assets/gso/textures/materials/wood/wood_planks.png", + "stats": [], + "meta": { + "materialCategory": "wood" + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/dreadnought_legendary.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/dreadnought_legendary.json new file mode 100644 index 0000000..425ef64 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/dreadnought_legendary.json @@ -0,0 +1,27 @@ +{ + "templates": { + "ship": { + "id": "dreadnought_legendary", + "name": "Dreadnought", + "type": "ship", + "rarity": "legendary", + "price": 500, + "currency": "gems", + "description": "The most powerful ship ever built \u2014 a living fortress", + "texture": "assets/gso/textures/ships/dreadnought_legendary.png", + "stats": { + "attack": 60, + "speed": 5, + "defense": 55, + "hull": 500 + }, + "categories": [ + "shop" + ], + "requirements": { + "level": 30 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/heavy_cruiser_rare.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/heavy_cruiser_rare.json new file mode 100644 index 0000000..c2e989b --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/heavy_cruiser_rare.json @@ -0,0 +1,28 @@ +{ + "templates": { + "ship": { + "id": "heavy_cruiser_rare", + "name": "Heavy Cruiser", + "type": "ship", + "rarity": "rare", + "price": 35000, + "currency": "credits", + "description": "Powerful heavy cruiser with superior firepower and armour", + "texture": "assets/gso/textures/ships/heavy_cruiser_rare.png", + "stats": { + "attack": 28, + "speed": 8, + "defense": 25, + "hull": 200 + }, + "categories": [ + "shop", + "dungeon_reward" + ], + "requirements": { + "level": 12 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/interceptor_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/interceptor_common.json new file mode 100644 index 0000000..4bc2757 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/interceptor_common.json @@ -0,0 +1,28 @@ +{ + "templates": { + "ship": { + "id": "interceptor_common", + "name": "Interceptor", + "type": "ship", + "rarity": "common", + "price": 8000, + "currency": "credits", + "description": "Fast attack ship for hit-and-run tactics", + "texture": "assets/gso/textures/ships/interceptor_common.png", + "stats": { + "attack": 12, + "speed": 18, + "defense": 8, + "hull": 80 + }, + "categories": [ + "shop", + "dungeon_reward" + ], + "requirements": { + "level": 3 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/interceptor_uncommon.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/interceptor_uncommon.json new file mode 100644 index 0000000..52561bb --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/interceptor_uncommon.json @@ -0,0 +1,28 @@ +{ + "templates": { + "ship": { + "id": "interceptor_uncommon", + "name": "Interceptor II", + "type": "ship", + "rarity": "uncommon", + "price": 18000, + "currency": "credits", + "description": "Enhanced interceptor with improved weapons", + "texture": "assets/gso/textures/ships/interceptor_uncommon.png", + "stats": { + "attack": 15, + "speed": 22, + "defense": 10, + "hull": 95 + }, + "categories": [ + "shop", + "dungeon_reward" + ], + "requirements": { + "level": 7 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/phantom_epic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/phantom_epic.json new file mode 100644 index 0000000..4e6f6b6 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/phantom_epic.json @@ -0,0 +1,27 @@ +{ + "templates": { + "ship": { + "id": "phantom_epic", + "name": "Phantom", + "type": "ship", + "rarity": "epic", + "price": 80000, + "currency": "credits", + "description": "A stealth vessel that strikes from the shadows", + "texture": "assets/gso/textures/ships/phantom_epic.png", + "stats": { + "attack": 35, + "speed": 30, + "defense": 14, + "hull": 130 + }, + "categories": [ + "shop" + ], + "requirements": { + "level": 20 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/starter_cruiser_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/starter_cruiser_common.json new file mode 100644 index 0000000..8a7f3d4 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/starter_cruiser_common.json @@ -0,0 +1,28 @@ +{ + "templates": { + "ship": { + "id": "starter_cruiser_common", + "name": "Starter Cruiser", + "type": "ship", + "rarity": "common", + "price": 5000, + "currency": "credits", + "description": "Reliable starter cruiser for new pilots", + "texture": "assets/gso/textures/ships/starter_cruiser_common.png", + "stats": { + "attack": 15, + "speed": 10, + "defense": 12, + "hull": 100 + }, + "categories": [ + "shop", + "dungeon_reward" + ], + "requirements": { + "level": 1 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/starter_cruiser_rare.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/starter_cruiser_rare.json new file mode 100644 index 0000000..f2fd32f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/starter_cruiser_rare.json @@ -0,0 +1,28 @@ +{ + "templates": { + "ship": { + "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/gso/textures/ships/starter_cruiser_rare.png", + "stats": { + "attack": 22, + "speed": 15, + "defense": 18, + "hull": 150 + }, + "categories": [ + "shop", + "dungeon_reward" + ], + "requirements": { + "level": 10 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/starter_cruiser_uncommon.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/starter_cruiser_uncommon.json new file mode 100644 index 0000000..73a6e15 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/ships/starter_cruiser_uncommon.json @@ -0,0 +1,28 @@ +{ + "templates": { + "ship": { + "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/gso/textures/ships/starter_cruiser_uncommon.png", + "stats": { + "attack": 18, + "speed": 12, + "defense": 15, + "hull": 120 + }, + "categories": [ + "shop", + "dungeon_reward" + ], + "requirements": { + "level": 5 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/assaultrifle/assault_rifle_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/assaultrifle/assault_rifle_common.json new file mode 100644 index 0000000..3fff28b --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/assaultrifle/assault_rifle_common.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "assault_rifle_common", + "texture": "assets/gso/textures/weapon/assaultrifle/assault_rifle_common.png", + "stats": { + "damage": 26, + "criticalChance": 0.04, + "attackRate": 6 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/assaultrifle/assault_rifle_epic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/assaultrifle/assault_rifle_epic.json new file mode 100644 index 0000000..b9964bb --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/assaultrifle/assault_rifle_epic.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "assault_rifle_epic", + "texture": "assets/gso/textures/weapon/assaultrifle/assault_rifle_epic.png", + "stats": { + "damage": 56, + "criticalChance": 0.14, + "attackRate": 8 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/assaultrifle/assault_rifle_rare.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/assaultrifle/assault_rifle_rare.json new file mode 100644 index 0000000..4bef56d --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/assaultrifle/assault_rifle_rare.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "assault_rifle_rare", + "texture": "assets/gso/textures/weapon/assaultrifle/assault_rifle_rare.png", + "stats": { + "damage": 38, + "criticalChance": 0.08, + "attackRate": 7 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/dualpistol/dual_pistol_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/dualpistol/dual_pistol_common.json new file mode 100644 index 0000000..12d45fb --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/dualpistol/dual_pistol_common.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "dual_pistol_common", + "texture": "assets/gso/textures/weapon/dualpistol/dual_pistol_common.png", + "stats": { + "damage": 14, + "criticalChance": 0.06, + "attackRate": 7 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/dualpistol/dual_pistol_epic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/dualpistol/dual_pistol_epic.json new file mode 100644 index 0000000..541f57a --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/dualpistol/dual_pistol_epic.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "dual_pistol_epic", + "texture": "assets/gso/textures/weapon/dualpistol/dual_pistol_epic.png", + "stats": { + "damage": 34, + "criticalChance": 0.2, + "attackRate": 11 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/dualpistol/dual_pistol_rare.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/dualpistol/dual_pistol_rare.json new file mode 100644 index 0000000..bb91d2c --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/dualpistol/dual_pistol_rare.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "dual_pistol_rare", + "texture": "assets/gso/textures/weapon/dualpistol/dual_pistol_rare.png", + "stats": { + "damage": 22, + "criticalChance": 0.12, + "attackRate": 9 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/laserbow/laser_bow_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/laserbow/laser_bow_common.json new file mode 100644 index 0000000..f421632 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/laserbow/laser_bow_common.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "laser_bow_common", + "texture": "assets/gso/textures/weapon/laserbow/laser_bow_common.png", + "stats": { + "damage": 40, + "criticalChance": 0.18, + "attackRate": 3 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/laserbow/laser_bow_epic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/laserbow/laser_bow_epic.json new file mode 100644 index 0000000..63c290a --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/laserbow/laser_bow_epic.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "laser_bow_epic", + "texture": "assets/gso/textures/weapon/laserbow/laser_bow_epic.png", + "stats": { + "damage": 95, + "criticalChance": 0.45, + "attackRate": 4 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/laserbow/laser_bow_rare.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/laserbow/laser_bow_rare.json new file mode 100644 index 0000000..1a5813b --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/laserbow/laser_bow_rare.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "laser_bow_rare", + "texture": "assets/gso/textures/weapon/laserbow/laser_bow_rare.png", + "stats": { + "damage": 62, + "criticalChance": 0.3, + "attackRate": 4 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/lasercrossbow/laser_crossbow_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/lasercrossbow/laser_crossbow_common.json new file mode 100644 index 0000000..1e84817 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/lasercrossbow/laser_crossbow_common.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "laser_crossbow_common", + "texture": "assets/gso/textures/weapon/lasercrossbow/laser_crossbow_common.png", + "stats": { + "damage": 48, + "criticalChance": 0.15, + "attackRate": 2 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/lasercrossbow/laser_crossbow_epic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/lasercrossbow/laser_crossbow_epic.json new file mode 100644 index 0000000..93692b6 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/lasercrossbow/laser_crossbow_epic.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "laser_crossbow_epic", + "texture": "assets/gso/textures/weapon/lasercrossbow/laser_crossbow_epic.png", + "stats": { + "damage": 110, + "criticalChance": 0.4, + "attackRate": 3 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/lasercrossbow/laser_crossbow_rare.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/lasercrossbow/laser_crossbow_rare.json new file mode 100644 index 0000000..be4921c --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/lasercrossbow/laser_crossbow_rare.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "laser_crossbow_rare", + "texture": "assets/gso/textures/weapon/lasercrossbow/laser_crossbow_rare.png", + "stats": { + "damage": 72, + "criticalChance": 0.25, + "attackRate": 3 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/pistol/pistol_marksman_rare.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/pistol/pistol_marksman_rare.json new file mode 100644 index 0000000..7f03a00 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/pistol/pistol_marksman_rare.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "pistol_marksman_rare", + "texture": "assets/gso/textures/weapon/pistol/pistol_marksman_rare.png", + "stats": { + "damage": 28, + "criticalChance": 0.1, + "attackRate": 5 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/pistol/pistol_overcharge_epic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/pistol/pistol_overcharge_epic.json new file mode 100644 index 0000000..d7206b6 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/pistol/pistol_overcharge_epic.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "pistol_overcharge_epic", + "texture": "assets/gso/textures/weapon/pistol/pistol_overcharge_epic.png", + "stats": { + "damage": 42, + "criticalChance": 0.18, + "attackRate": 6 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/pistol/pistol_standard_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/pistol/pistol_standard_common.json new file mode 100644 index 0000000..4d219d6 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/pistol/pistol_standard_common.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "pistol_standard_common", + "texture": "assets/gso/textures/weapon/pistol/pistol_standard_common.png", + "stats": { + "damage": 18, + "criticalChance": 0.05, + "attackRate": 4 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/plasmacutter/plasma_cutter_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/plasmacutter/plasma_cutter_common.json new file mode 100644 index 0000000..2bda647 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/plasmacutter/plasma_cutter_common.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "plasma_cutter_common", + "texture": "assets/gso/textures/weapon/plasmacutter/plasma_cutter_common.png", + "stats": { + "damage": 60, + "criticalChance": 0.12, + "attackRate": 3 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/plasmacutter/plasma_cutter_epic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/plasmacutter/plasma_cutter_epic.json new file mode 100644 index 0000000..f6700d1 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/plasmacutter/plasma_cutter_epic.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "plasma_cutter_epic", + "texture": "assets/gso/textures/weapon/plasmacutter/plasma_cutter_epic.png", + "stats": { + "damage": 135, + "criticalChance": 0.35, + "attackRate": 5 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/plasmacutter/plasma_cutter_rare.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/plasmacutter/plasma_cutter_rare.json new file mode 100644 index 0000000..0cc02a0 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/plasmacutter/plasma_cutter_rare.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "plasma_cutter_rare", + "texture": "assets/gso/textures/weapon/plasmacutter/plasma_cutter_rare.png", + "stats": { + "damage": 90, + "criticalChance": 0.2, + "attackRate": 4 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/rocketlauncher/rocket_launcher_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/rocketlauncher/rocket_launcher_common.json new file mode 100644 index 0000000..cdb832c --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/rocketlauncher/rocket_launcher_common.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "rocket_launcher_common", + "texture": "assets/gso/textures/weapon/rocketlauncher/rocket_launcher_common.png", + "stats": { + "damage": 140, + "criticalChance": 0.1, + "attackRate": 1 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/rocketlauncher/rocket_launcher_epic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/rocketlauncher/rocket_launcher_epic.json new file mode 100644 index 0000000..9d80a49 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/rocketlauncher/rocket_launcher_epic.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "rocket_launcher_epic", + "texture": "assets/gso/textures/weapon/rocketlauncher/rocket_launcher_epic.png", + "stats": { + "damage": 320, + "criticalChance": 0.25, + "attackRate": 2 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/rocketlauncher/rocket_launcher_rare.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/rocketlauncher/rocket_launcher_rare.json new file mode 100644 index 0000000..d9f7bb0 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/rocketlauncher/rocket_launcher_rare.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "rocket_launcher_rare", + "texture": "assets/gso/textures/weapon/rocketlauncher/rocket_launcher_rare.png", + "stats": { + "damage": 220, + "criticalChance": 0.18, + "attackRate": 1 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/sniper/sniper_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/sniper/sniper_common.json new file mode 100644 index 0000000..f5f3cac --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/sniper/sniper_common.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "sniper_common", + "texture": "assets/gso/textures/weapon/sniper/sniper_common.png", + "stats": { + "damage": 90, + "criticalChance": 0.2, + "attackRate": 1 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/sniper/sniper_epic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/sniper/sniper_epic.json new file mode 100644 index 0000000..6789b70 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/sniper/sniper_epic.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "sniper_epic", + "texture": "assets/gso/textures/weapon/sniper/sniper_epic.png", + "stats": { + "damage": 190, + "criticalChance": 0.45, + "attackRate": 2 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/sniper/sniper_rare.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/sniper/sniper_rare.json new file mode 100644 index 0000000..bbbd9e4 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapon/sniper/sniper_rare.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "sniper_rare", + "texture": "assets/gso/textures/weapon/sniper/sniper_rare.png", + "stats": { + "damage": 135, + "criticalChance": 0.3, + "attackRate": 1 + } + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/assault_rifle_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/assault_rifle_common.json new file mode 100644 index 0000000..48e24b9 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/assault_rifle_common.json @@ -0,0 +1,27 @@ +{ + "templates": { + "weapon": { + "id": "assault_rifle_common", + "name": "Assault Rifle", + "type": "weapon", + "rarity": "common", + "price": 2000, + "currency": "credits", + "description": "Rapid-fire assault rifle for sustained combat", + "texture": "assets/gso/textures/weapons/assaultrifle/assault_rifle_common.png", + "stats": { + "damage": 28, + "criticalChance": 0.07, + "attackRate": 8 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 5 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/assault_rifle_rare.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/assault_rifle_rare.json new file mode 100644 index 0000000..0c9f3a5 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/assault_rifle_rare.json @@ -0,0 +1,27 @@ +{ + "templates": { + "weapon": { + "id": "assault_rifle_rare", + "name": "Heavy Assault Rifle", + "type": "weapon", + "rarity": "rare", + "price": 8000, + "currency": "credits", + "description": "Military-grade assault rifle with enhanced stopping power", + "texture": "assets/gso/textures/weapons/assaultrifle/assault_rifle_rare.png", + "stats": { + "damage": 45, + "criticalChance": 0.09, + "attackRate": 8 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 12 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/laser_bow_epic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/laser_bow_epic.json new file mode 100644 index 0000000..2675b5e --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/laser_bow_epic.json @@ -0,0 +1,26 @@ +{ + "templates": { + "weapon": { + "id": "laser_bow_epic", + "name": "Laser Bow", + "type": "weapon", + "rarity": "epic", + "price": 200, + "currency": "gems", + "description": "Ancient-style energy bow channelling pure laser beams", + "texture": "assets/gso/textures/weapons/laserbow/laser_bow_epic.png", + "stats": { + "damage": 80, + "criticalChance": 0.2, + "attackRate": 2 + }, + "categories": [ + "shop" + ], + "requirements": { + "level": 20 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/pistol_marksman_rare.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/pistol_marksman_rare.json new file mode 100644 index 0000000..210e167 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/pistol_marksman_rare.json @@ -0,0 +1,27 @@ +{ + "templates": { + "weapon": { + "id": "pistol_marksman_rare", + "name": "Marksman Pistol", + "type": "weapon", + "rarity": "rare", + "price": 5000, + "currency": "credits", + "description": "Precision-engineered for long-range accuracy", + "texture": "assets/gso/textures/weapons/pistol/pistol_marksman_rare.png", + "stats": { + "damage": 38, + "criticalChance": 0.12, + "attackRate": 3 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 8 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/pistol_standard_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/pistol_standard_common.json new file mode 100644 index 0000000..061a06e --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/pistol_standard_common.json @@ -0,0 +1,27 @@ +{ + "templates": { + "weapon": { + "id": "pistol_standard_common", + "name": "Standard Pistol", + "type": "weapon", + "rarity": "common", + "price": 800, + "currency": "credits", + "description": "A reliable sidearm for any situation", + "texture": "assets/gso/textures/weapons/pistol/pistol_standard_common.png", + "stats": { + "damage": 22, + "criticalChance": 0.06, + "attackRate": 4 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 2 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/plasma_cutter_rare.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/plasma_cutter_rare.json new file mode 100644 index 0000000..a35d703 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/plasma_cutter_rare.json @@ -0,0 +1,27 @@ +{ + "templates": { + "weapon": { + "id": "plasma_cutter_rare", + "name": "Plasma Cutter", + "type": "weapon", + "rarity": "rare", + "price": 12000, + "currency": "credits", + "description": "Superheated plasma blade that melts through armour", + "texture": "assets/gso/textures/weapons/plasmacutter/plasma_cutter_rare.png", + "stats": { + "damage": 60, + "criticalChance": 0.1, + "attackRate": 2 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 15 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/sniper_common.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/sniper_common.json new file mode 100644 index 0000000..4d2790b --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/sniper_common.json @@ -0,0 +1,27 @@ +{ + "templates": { + "weapon": { + "id": "sniper_common", + "name": "Sniper Rifle", + "type": "weapon", + "rarity": "common", + "price": 3000, + "currency": "credits", + "description": "Long-range precision weapon for eliminating distant targets", + "texture": "assets/gso/textures/weapons/sniper/sniper_common.png", + "stats": { + "damage": 55, + "criticalChance": 0.15, + "attackRate": 1 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 6 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/starter_blaster.json b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/starter_blaster.json new file mode 100644 index 0000000..693791d --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/items/weapons/starter_blaster.json @@ -0,0 +1,27 @@ +{ + "templates": { + "weapon": { + "id": "starter_blaster", + "name": "Starter Blaster", + "type": "weapon", + "rarity": "common", + "price": 0, + "currency": "credits", + "description": "Standard-issue blaster for new recruits", + "texture": "assets/gso/textures/weapons/pistol/starter_blaster.png", + "stats": { + "damage": 18, + "criticalChance": 0.05, + "attackRate": 4 + }, + "categories": [ + "starter", + "dungeon_loot" + ], + "requirements": { + "level": 1 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/missions/faction_missions.json b/Galaxy-Strike-Online-main/GameServer/data/gso/missions/faction_missions.json new file mode 100644 index 0000000..825aef6 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/missions/faction_missions.json @@ -0,0 +1,12 @@ +{ + "missions": [ + { "id": "fed_patrol_alpha", "faction": "federation", "name": "Patrol Route Alpha", "type": "patrol", "difficulty": 1, "minLevel": 1, "duration": 300, "rewards": { "credits": 200, "xp": 100, "reputation": { "federation": 50 } }, "desc": "Patrol the outer rim of Sector 7 for Federation security." }, + { "id": "fed_escort_convoy", "faction": "federation", "name": "Escort Trade Convoy", "type": "escort", "difficulty": 2, "minLevel": 5, "duration": 600, "rewards": { "credits": 500, "xp": 250, "reputation": { "federation": 80, "merchant_guild": 30 } }, "desc": "Protect a Federation supply convoy through hostile space." }, + { "id": "fed_hunt_pirate", "faction": "federation", "name": "Pirate Bounty Hunt", "type": "combat", "difficulty": 3, "minLevel": 8, "duration": 900, "rewards": { "credits": 800, "xp": 400, "reputation": { "federation": 120, "pirate_syndicate": -80 } }, "desc": "Eliminate a wanted pirate commander for a Federation bounty." }, + { "id": "mg_trade_run", "faction": "merchant_guild", "name": "Express Trade Run", "type": "transport", "difficulty": 1, "minLevel": 1, "duration": 300, "rewards": { "credits": 300, "xp": 80, "metal": 200, "reputation": { "merchant_guild": 40 } }, "desc": "Deliver urgently needed goods between Guild stations." }, + { "id": "mg_rare_cargo", "faction": "merchant_guild", "name": "Rare Materials Haul", "type": "transport", "difficulty": 3, "minLevel": 10, "duration": 1200, "rewards": { "credits": 1500, "xp": 600, "crystal": 300, "reputation": { "merchant_guild": 150 } }, "desc": "Transport a shipment of rare crystals. Guard it well." }, + { "id": "pir_raid_outpost", "faction": "pirate_syndicate", "name": "Raid Rival Outpost", "type": "combat", "difficulty": 3, "minLevel": 6, "duration": 600, "requires_rep": -200, "rewards": { "credits": 600, "xp": 300, "metal": 400, "reputation": { "pirate_syndicate": 100, "federation": -60 } }, "desc": "Strike an enemy faction outpost for the Syndicate." }, + { "id": "rog_ai_recon", "faction": "rogue_ai", "name": "Neural Net Recon", "type": "explore", "difficulty": 4, "minLevel": 15, "duration": 1800, "requires_rep": 100, "rewards": { "credits": 2000, "xp": 1000, "darkMatter": 20, "reputation": { "rogue_ai": 200 } }, "desc": "Investigate a Rogue AI data node deep in uncharted space." }, + { "id": "void_offering", "faction": "void_cult", "name": "The Offering", "type": "special", "difficulty": 5, "minLevel": 20, "duration": 3600, "requires_rep": 0, "rewards": { "credits": 3000, "xp": 2000, "darkMatter": 50, "reputation": { "void_cult": 300 } }, "desc": "Make a donation to the Void Cult's mysterious ritual site." } + ] +} diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_craft_3.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_craft_3.json new file mode 100644 index 0000000..7c664d6 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_craft_3.json @@ -0,0 +1,46 @@ +{ + "id": "d_craft_3", + "name": "Quick Craft", + "category": "daily", + "repeatable": true, + "timeWindow": { + "type": "daily", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 2, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_craft", + "type": "CRAFT_TOOLS", + "target": 3, + "params": { + "toolId": "any" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 300 + }, + { + "type": "item", + "items": [ + { + "itemId": "scrap_pack_small", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_daily_craft", + "summary": "Craft 3 items of any type." + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_customize_ship.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_customize_ship.json new file mode 100644 index 0000000..7e8d380 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_customize_ship.json @@ -0,0 +1,44 @@ +{ + "id": "d_customize_ship", + "name": "Fresh Paint", + "category": "daily", + "repeatable": true, + "timeWindow": { + "type": "daily", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 1, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_ship", + "type": "CUSTOMIZE_SHIP", + "target": 1, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 200 + }, + { + "type": "item", + "items": [ + { + "itemId": "paint_kit_random", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_daily_ship", + "summary": "Customize your ship." + } +} diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_decor_10.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_decor_10.json new file mode 100644 index 0000000..93cb472 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_decor_10.json @@ -0,0 +1,46 @@ +{ + "id": "d_decor_10", + "name": "Interior Designer", + "category": "daily", + "repeatable": true, + "timeWindow": { + "type": "daily", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 2, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_decor", + "type": "PLACE_DECORATIONS", + "target": 10, + "params": { + "location": "ship_or_base" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 350 + }, + { + "type": "item", + "items": [ + { + "itemId": "deco_token", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_daily_decor", + "summary": "Place 10 decorations." + } +} diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_dungeons_3.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_dungeons_3.json new file mode 100644 index 0000000..b0e20ac --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_dungeons_3.json @@ -0,0 +1,44 @@ +{ + "id": "d_dungeons_3", + "name": "Dungeon Hopping", + "category": "daily", + "repeatable": true, + "timeWindow": { + "type": "daily", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 3, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_dungeons", + "type": "COMPLETE_DUNGEON", + "target": 3, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 500 + }, + { + "type": "item", + "items": [ + { + "itemId": "health_kit_small", + "qty": 3 + } + ] + } + ], + "ui": { + "icon": "quest_daily_dungeon", + "summary": "Complete 3 dungeons." + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_explore_30.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_explore_30.json new file mode 100644 index 0000000..42d1bc3 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_explore_30.json @@ -0,0 +1,35 @@ +{ + "id": "d_explore_30", + "name": "Daily Survey", + "category": "daily", + "repeatable": true, + "timeWindow": { + "type": "daily", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 1, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_explore", + "type": "TRAVEL_METERS_PLANET", + "target": 30, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 200 + } + ], + "ui": { + "icon": "quest_daily_explore", + "summary": "Explore 30 sectors." + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_kill_pirates_10.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_kill_pirates_10.json new file mode 100644 index 0000000..8009f69 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_kill_pirates_10.json @@ -0,0 +1,46 @@ +{ + "id": "d_kill_pirates_10", + "name": "Pirate Bounty", + "category": "daily", + "repeatable": true, + "timeWindow": { + "type": "daily", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 2, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_kill", + "type": "DEFEAT_ENEMY", + "target": 10, + "params": { + "enemyId": "space_pirate" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 400 + }, + { + "type": "item", + "items": [ + { + "itemId": "iron_ore", + "qty": 10 + } + ] + } + ], + "ui": { + "icon": "quest_daily_combat", + "summary": "Defeat 10 space pirates." + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_login_5.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_login_5.json new file mode 100644 index 0000000..13bd966 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_login_5.json @@ -0,0 +1,44 @@ +{ + "id": "d_login_5", + "name": "Frequent Flyer", + "category": "daily", + "repeatable": true, + "timeWindow": { + "type": "daily", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 1, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_login", + "type": "LOGIN", + "target": 1, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 100 + }, + { + "type": "item", + "items": [ + { + "itemId": "fuel_cell", + "qty": 2 + } + ] + } + ], + "ui": { + "icon": "quest_daily_login", + "summary": "Log in today." + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_smelt_5.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_smelt_5.json new file mode 100644 index 0000000..7d2d568 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_smelt_5.json @@ -0,0 +1,46 @@ +{ + "id": "d_smelt_5", + "name": "Smelting Run", + "category": "daily", + "repeatable": true, + "timeWindow": { + "type": "daily", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 2, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_smelt", + "type": "CRAFT_TOOLS", + "target": 5, + "params": { + "toolId": "ingot_any" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 250 + }, + { + "type": "item", + "items": [ + { + "itemId": "bandage", + "qty": 5 + } + ] + } + ], + "ui": { + "icon": "quest_daily_smelt", + "summary": "Smelt 5 ingots." + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_travel_planet_1000.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_travel_planet_1000.json new file mode 100644 index 0000000..191270f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/daily/d_travel_planet_1000.json @@ -0,0 +1,37 @@ +{ + "id": "d_travel_planet_1000", + "name": "Planetary Errands", + "category": "daily", + "repeatable": true, + "timeWindow": { + "type": "daily", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 1, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_travel", + "type": "TRAVEL_METERS_PLANET", + "target": 1000, + "params": { + "planetId": "any" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 250 + } + ], + "ui": { + "icon": "quest_daily_walk", + "summary": "Travel 1,000 meters on any planet." + } +} diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch1_001.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch1_001.json new file mode 100644 index 0000000..d13290d --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch1_001.json @@ -0,0 +1,78 @@ +{ + "id": "ms_ch1_001", + "name": "First Steps Into the Stars", + "category": "main_story", + "chapter": "1", + "repeatable": false, + "timeWindow": { + "type": "none", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 1, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_travel", + "type": "TRAVEL_METERS_PLANET", + "target": 500, + "params": { + "planetId": "terra-1" + }, + "progress": 0, + "complete": false + }, + { + "id": "obj_customize", + "type": "CUSTOMIZE_CHARACTER", + "target": 1, + "params": {}, + "progress": 0, + "complete": false + }, + { + "id": "obj_craft", + "type": "CRAFT_TOOLS", + "target": 2, + "params": { + "toolId": "basic_tool_any" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 500 + }, + { + "type": "money", + "amount": 10 + }, + { + "type": "item", + "items": [ + { + "itemId": "starter_crate", + "qty": 1 + }, + { + "itemId": "fuel_cell", + "qty": 5 + } + ] + }, + { + "type": "item", + "itemId": "wp_nebula_dawn", + "quantity": 1 + } + ], + "ui": { + "icon": "quest_star", + "summary": "Travel, gear up, and personalise your new life." + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch1_002.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch1_002.json new file mode 100644 index 0000000..5b60296 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch1_002.json @@ -0,0 +1,65 @@ +{ + "id": "ms_ch1_002", + "name": "Into the Unknown", + "category": "main_story", + "chapter": "1", + "repeatable": false, + "timeWindow": { + "type": "none", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [ + "ms_ch1_001" + ], + "playerLevelMin": 2, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_dungeon", + "type": "COMPLETE_DUNGEON", + "target": 1, + "params": { + "dungeonId": "pirate_lair" + }, + "progress": 0, + "complete": false + }, + { + "id": "obj_credits", + "type": "EARN_CREDITS", + "target": 500, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 800 + }, + { + "type": "money", + "amount": 20 + }, + { + "type": "item", + "items": [ + { + "itemId": "pistol_standard_common", + "qty": 1 + }, + { + "itemId": "health_kit_small", + "qty": 5 + } + ] + } + ], + "ui": { + "icon": "quest_combat", + "summary": "Clear the pirate lair and earn your keep." + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch1_003.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch1_003.json new file mode 100644 index 0000000..9eee795 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch1_003.json @@ -0,0 +1,63 @@ +{ + "id": "ms_ch1_003", + "name": "The Art of Craft", + "category": "main_story", + "chapter": "1", + "repeatable": false, + "timeWindow": { + "type": "none", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [ + "ms_ch1_002" + ], + "playerLevelMin": 3, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_smelt", + "type": "CRAFT_TOOLS", + "target": 5, + "params": { + "toolId": "ingot_any" + }, + "progress": 0, + "complete": false + }, + { + "id": "obj_alloy", + "type": "CRAFT_TOOLS", + "target": 1, + "params": { + "toolId": "steel_alloy" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 1000 + }, + { + "type": "money", + "amount": 25 + }, + { + "type": "item", + "items": [ + { + "itemId": "body_basic_common", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_craft", + "summary": "Master the basics of smelting and alloying." + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch2_001.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch2_001.json new file mode 100644 index 0000000..bf62fa7 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch2_001.json @@ -0,0 +1,67 @@ +{ + "id": "ms_ch2_001", + "name": "Alien Incursion", + "category": "main_story", + "chapter": "2", + "repeatable": false, + "timeWindow": { + "type": "none", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [ + "ms_ch1_003" + ], + "playerLevelMin": 5, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_alien_dungeon", + "type": "COMPLETE_DUNGEON", + "target": 1, + "params": { + "dungeonId": "alien_ruins" + }, + "progress": 0, + "complete": false + }, + { + "id": "obj_kill_guardians", + "type": "DEFEAT_ENEMY", + "target": 10, + "params": { + "enemyId": "alien_guardian" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 2000 + }, + { + "type": "money", + "amount": 50 + }, + { + "type": "item", + "items": [ + { + "itemId": "assault_rifle_common", + "qty": 1 + }, + { + "itemId": "health_kit_large", + "qty": 3 + } + ] + } + ], + "ui": { + "icon": "quest_alien", + "summary": "Investigate the alien ruins and push back the incursion." + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch2_002.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch2_002.json new file mode 100644 index 0000000..8019a88 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch2_002.json @@ -0,0 +1,63 @@ +{ + "id": "ms_ch2_002", + "name": "Power Core", + "category": "main_story", + "chapter": "2", + "repeatable": false, + "timeWindow": { + "type": "none", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [ + "ms_ch2_001" + ], + "playerLevelMin": 7, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_circuits", + "type": "CRAFT_TOOLS", + "target": 3, + "params": { + "toolId": "circuit_any" + }, + "progress": 0, + "complete": false + }, + { + "id": "obj_ai_vault", + "type": "COMPLETE_DUNGEON", + "target": 1, + "params": { + "dungeonId": "corrupted_vault" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 3500 + }, + { + "type": "money", + "amount": 80 + }, + { + "type": "item", + "items": [ + { + "itemId": "body_plated_rare", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_tech", + "summary": "Craft advanced circuits and storm the corrupted AI vault." + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch2_003.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch2_003.json new file mode 100644 index 0000000..fa5f0c5 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch2_003.json @@ -0,0 +1,63 @@ +{ + "id": "ms_ch2_003", + "name": "The Void Calls", + "category": "main_story", + "chapter": "2", + "repeatable": false, + "timeWindow": { + "type": "none", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [ + "ms_ch2_002" + ], + "playerLevelMin": 10, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_void", + "type": "COMPLETE_DUNGEON", + "target": 1, + "params": { + "dungeonId": "nebula_anomaly" + }, + "progress": 0, + "complete": false + }, + { + "id": "obj_void_crystal", + "type": "GATHER_RESOURCE", + "target": 3, + "params": { + "itemId": "void_crystal" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 6000 + }, + { + "type": "money", + "amount": 150 + }, + { + "type": "item", + "items": [ + { + "itemId": "laser_bow_epic", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_void", + "summary": "Enter the nebula anomaly and harvest void crystals." + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch3_001.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch3_001.json new file mode 100644 index 0000000..530f948 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch3_001.json @@ -0,0 +1,63 @@ +{ + "id": "ms_ch3_001", + "name": "Dreadnought Rising", + "category": "main_story", + "chapter": "3", + "repeatable": false, + "timeWindow": { + "type": "none", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [ + "ms_ch2_003" + ], + "playerLevelMin": 15, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_hull", + "type": "CRAFT_TOOLS", + "target": 1, + "params": { + "toolId": "heavy_hull" + }, + "progress": 0, + "complete": false + }, + { + "id": "obj_void_rift", + "type": "COMPLETE_DUNGEON", + "target": 1, + "params": { + "dungeonId": "void_rift" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 12000 + }, + { + "type": "money", + "amount": 300 + }, + { + "type": "item", + "items": [ + { + "itemId": "phantom_epic", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_dread", + "summary": "Build a hull worthy of the void and conquer the rift." + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch3_002.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch3_002.json new file mode 100644 index 0000000..c25aa01 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/main_story/ms_ch3_002.json @@ -0,0 +1,72 @@ +{ + "id": "ms_ch3_002", + "name": "Galaxy Strike", + "category": "main_story", + "chapter": "3", + "repeatable": false, + "timeWindow": { + "type": "none", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [ + "ms_ch3_001" + ], + "playerLevelMin": 20, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_dread", + "type": "CRAFT_TOOLS", + "target": 1, + "params": { + "toolId": "dreadframe_hull" + }, + "progress": 0, + "complete": false + }, + { + "id": "obj_final", + "type": "COMPLETE_DUNGEON", + "target": 3, + "params": { + "dungeonId": "void_rift" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 25000 + }, + { + "type": "money", + "amount": 500 + }, + { + "type": "item", + "items": [ + { + "itemId": "dreadnought_legendary", + "qty": 1 + }, + { + "itemId": "xp_booster", + "qty": 3 + } + ] + }, + { + "type": "item", + "itemId": "room_throne", + "quantity": 1 + } + ], + "ui": { + "icon": "quest_final", + "summary": "Forge the ultimate hull and strike the heart of the void." + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/manifest.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/manifest.json new file mode 100644 index 0000000..5ba250d --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/manifest.json @@ -0,0 +1,12 @@ +{ + "counts_by_category": { + "main_story": 8, + "daily": 6, + "weekly": 5, + "monthly": 4 + }, + "categoryIndexes": "data/gso/category/quests/", + "notes": [ + "One JSON per quest. Category index files in data/gso/category/quests/.json" + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_craft_100.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_craft_100.json new file mode 100644 index 0000000..8b20746 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_craft_100.json @@ -0,0 +1,50 @@ +{ + "id": "m_craft_100", + "name": "Master Craftsman", + "category": "monthly", + "repeatable": true, + "timeWindow": { + "type": "monthly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 5, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_craft", + "type": "CRAFT_TOOLS", + "target": 100, + "params": { + "toolId": "any" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 12000 + }, + { + "type": "money", + "amount": 200 + }, + { + "type": "item", + "items": [ + { + "itemId": "body_plated_rare", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_monthly_craft", + "summary": "Craft 100 items this month." + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_decor_100.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_decor_100.json new file mode 100644 index 0000000..805e706 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_decor_100.json @@ -0,0 +1,46 @@ +{ + "id": "m_decor_100", + "name": "Base Beautification", + "category": "monthly", + "repeatable": true, + "timeWindow": { + "type": "monthly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 3, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_decor", + "type": "PLACE_DECORATIONS", + "target": 100, + "params": { + "location": "base" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 8000 + }, + { + "type": "item", + "items": [ + { + "itemId": "deco_bundle_large", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_monthly_decor", + "summary": "Place 100 decorations in your base." + } +} diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_dungeons_50.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_dungeons_50.json new file mode 100644 index 0000000..52df2cc --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_dungeons_50.json @@ -0,0 +1,52 @@ +{ + "id": "m_dungeons_50", + "name": "Legendary Dungeon Diver", + "category": "monthly", + "repeatable": true, + "timeWindow": { + "type": "monthly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 6, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_dungeons", + "type": "COMPLETE_DUNGEON", + "target": 50, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 15000 + }, + { + "type": "money", + "amount": 300 + }, + { + "type": "item", + "items": [ + { + "itemId": "xp_booster", + "qty": 3 + }, + { + "itemId": "credit_multiplier", + "qty": 3 + } + ] + } + ], + "ui": { + "icon": "quest_monthly_dungeon", + "summary": "Complete 50 dungeons this month." + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_guild_fight_10.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_guild_fight_10.json new file mode 100644 index 0000000..4cf189c --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_guild_fight_10.json @@ -0,0 +1,48 @@ +{ + "id": "m_guild_fight_10", + "name": "Guild Warpath", + "category": "monthly", + "repeatable": true, + "timeWindow": { + "type": "monthly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 8, + "guildRequired": true + }, + "objectives": [ + { + "id": "obj_guild_fight", + "type": "FIGHT_GUILD", + "target": 10, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 25000 + }, + { + "type": "money", + "amount": 100 + }, + { + "type": "item", + "items": [ + { + "itemId": "guild_trophy", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_monthly_guild", + "summary": "Win 10 guild battles this month." + } +} diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_hire_5.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_hire_5.json new file mode 100644 index 0000000..b7dfc68 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_hire_5.json @@ -0,0 +1,50 @@ +{ + "id": "m_hire_5", + "name": "Private Army", + "category": "monthly", + "repeatable": true, + "timeWindow": { + "type": "monthly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 7, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_hire", + "type": "HIRE_MERCENARIES", + "target": 5, + "params": { + "fleetTier": "any" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 15000 + }, + { + "type": "money", + "amount": 75 + }, + { + "type": "item", + "items": [ + { + "itemId": "merc_fleet_skin", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_monthly_merc", + "summary": "Hire 5 mercenary fleets this month." + } +} diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_lightyears_100.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_lightyears_100.json new file mode 100644 index 0000000..fb146e6 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_lightyears_100.json @@ -0,0 +1,44 @@ +{ + "id": "m_lightyears_100", + "name": "Long Haul", + "category": "monthly", + "repeatable": true, + "timeWindow": { + "type": "monthly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 5, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_ly", + "type": "TRAVEL_LIGHTYEARS", + "target": 100, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 5000 + }, + { + "type": "item", + "items": [ + { + "itemId": "warp_core_fragment", + "qty": 2 + } + ] + } + ], + "ui": { + "icon": "quest_monthly", + "summary": "Travel 100 light years this month." + } +} diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_raids_20.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_raids_20.json new file mode 100644 index 0000000..4226622 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_raids_20.json @@ -0,0 +1,48 @@ +{ + "id": "m_raids_20", + "name": "Monthly Raider", + "category": "monthly", + "repeatable": true, + "timeWindow": { + "type": "monthly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 5, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_raids", + "type": "FIGHT_RAIDS", + "target": 20, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 8000 + }, + { + "type": "money", + "amount": 150 + }, + { + "type": "item", + "items": [ + { + "itemId": "plasma_cutter_rare", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_monthly", + "summary": "Complete 20 raids this month." + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_void_run.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_void_run.json new file mode 100644 index 0000000..3a08bdf --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/monthly/m_void_run.json @@ -0,0 +1,50 @@ +{ + "id": "m_void_run", + "name": "Void Conqueror", + "category": "monthly", + "repeatable": true, + "timeWindow": { + "type": "monthly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 12, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_void", + "type": "COMPLETE_DUNGEON", + "target": 5, + "params": { + "dungeonId": "void_rift" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 25000 + }, + { + "type": "money", + "amount": 500 + }, + { + "type": "item", + "items": [ + { + "itemId": "dreadnought_legendary", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_monthly_void", + "summary": "Conquer the void rift 5 times." + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_craft_20.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_craft_20.json new file mode 100644 index 0000000..b912850 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_craft_20.json @@ -0,0 +1,55 @@ +{ + "id": "w_craft_20", + "name": "Crafting Spree", + "category": "weekly", + "repeatable": true, + "timeWindow": { + "type": "weekly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 3, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_craft", + "type": "CRAFT_TOOLS", + "target": 20, + "params": { + "toolId": "any" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 2000 + }, + { + "type": "money", + "amount": 40 + }, + { + "type": "item", + "items": [ + { + "itemId": "xp_booster", + "qty": 1 + } + ] + }, + { + "type": "item", + "itemId": "wp_golden_empire", + "quantity": 1 + } + ], + "ui": { + "icon": "quest_weekly_craft", + "summary": "Craft 20 items this week." + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_dungeons_10.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_dungeons_10.json new file mode 100644 index 0000000..d939ad4 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_dungeons_10.json @@ -0,0 +1,48 @@ +{ + "id": "w_dungeons_10", + "name": "Dungeon Crawler", + "category": "weekly", + "repeatable": true, + "timeWindow": { + "type": "weekly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 4, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_dungeons", + "type": "COMPLETE_DUNGEON", + "target": 10, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 3000 + }, + { + "type": "money", + "amount": 60 + }, + { + "type": "item", + "items": [ + { + "itemId": "credit_multiplier", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_weekly_dungeon", + "summary": "Complete 10 dungeons this week." + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_explore_planets_3.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_explore_planets_3.json new file mode 100644 index 0000000..00ab846 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_explore_planets_3.json @@ -0,0 +1,48 @@ +{ + "id": "w_explore_planets_3", + "name": "New Horizons", + "category": "weekly", + "repeatable": true, + "timeWindow": { + "type": "weekly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 3, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_planets", + "type": "EXPLORE_NEW_PLANETS", + "target": 3, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 1800 + }, + { + "type": "money", + "amount": 20 + }, + { + "type": "item", + "items": [ + { + "itemId": "nav_chip_mk1", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_weekly_planets", + "summary": "Explore 3 new planets this week." + } +} diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_friends_5.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_friends_5.json new file mode 100644 index 0000000..2cd8c7e --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_friends_5.json @@ -0,0 +1,39 @@ +{ + "id": "w_friends_5", + "name": "Build Your Network", + "category": "weekly", + "repeatable": true, + "timeWindow": { + "type": "weekly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 2, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_friends", + "type": "ADD_FRIENDS", + "target": 5, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 1500 + }, + { + "type": "money", + "amount": 15 + } + ], + "ui": { + "icon": "quest_weekly_social", + "summary": "Add 5 friends." + } +} diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_gather_50.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_gather_50.json new file mode 100644 index 0000000..5d5a733 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_gather_50.json @@ -0,0 +1,50 @@ +{ + "id": "w_gather_50", + "name": "Resource Rush", + "category": "weekly", + "repeatable": true, + "timeWindow": { + "type": "weekly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 2, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_gather", + "type": "GATHER_RESOURCE", + "target": 50, + "params": { + "itemId": "any_ore" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 1500 + }, + { + "type": "money", + "amount": 30 + }, + { + "type": "item", + "items": [ + { + "itemId": "scrap_pack_small", + "qty": 5 + } + ] + } + ], + "ui": { + "icon": "quest_weekly_gather", + "summary": "Gather 50 ores this week." + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_guild_fight_3.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_guild_fight_3.json new file mode 100644 index 0000000..17d2807 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_guild_fight_3.json @@ -0,0 +1,44 @@ +{ + "id": "w_guild_fight_3", + "name": "Guild Skirmishes", + "category": "weekly", + "repeatable": true, + "timeWindow": { + "type": "weekly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 6, + "guildRequired": true + }, + "objectives": [ + { + "id": "obj_guild_fight", + "type": "FIGHT_GUILD", + "target": 3, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 3500 + }, + { + "type": "item", + "items": [ + { + "itemId": "guild_rep", + "qty": 50 + } + ] + } + ], + "ui": { + "icon": "quest_weekly_guild", + "summary": "Fight 3 other guilds this week." + } +} diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_lightyears_25.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_lightyears_25.json new file mode 100644 index 0000000..d4ac7fd --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_lightyears_25.json @@ -0,0 +1,44 @@ +{ + "id": "w_lightyears_25", + "name": "Interstellar Route", + "category": "weekly", + "repeatable": true, + "timeWindow": { + "type": "weekly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 4, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_ly", + "type": "TRAVEL_LIGHTYEARS", + "target": 25, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 2200 + }, + { + "type": "item", + "items": [ + { + "itemId": "fuel_cell", + "qty": 10 + } + ] + } + ], + "ui": { + "icon": "quest_weekly_space", + "summary": "Travel 25 light years." + } +} diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_raids_3.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_raids_3.json new file mode 100644 index 0000000..bdb0fe6 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_raids_3.json @@ -0,0 +1,48 @@ +{ + "id": "w_raids_3", + "name": "Raid Week", + "category": "weekly", + "repeatable": true, + "timeWindow": { + "type": "weekly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 3, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_raids", + "type": "FIGHT_RAIDS", + "target": 3, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 1200 + }, + { + "type": "money", + "amount": 25 + }, + { + "type": "item", + "items": [ + { + "itemId": "energy_boost_large", + "qty": 2 + } + ] + } + ], + "ui": { + "icon": "quest_weekly", + "summary": "Complete 3 raids." + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_survive_hardcore_7.json b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_survive_hardcore_7.json new file mode 100644 index 0000000..5fdcae6 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/quests/weekly/w_survive_hardcore_7.json @@ -0,0 +1,39 @@ +{ + "id": "w_survive_hardcore_7", + "name": "Hardcore Survivor", + "category": "weekly", + "repeatable": true, + "timeWindow": { + "type": "weekly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 5, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_hardcore", + "type": "SURVIVE_DUNGEON_HARDCORE", + "target": 7, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 4000 + }, + { + "type": "money", + "amount": 80 + } + ], + "ui": { + "icon": "quest_weekly_hardcore", + "summary": "Survive 7 hardcore dungeon encounters." + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/.gitkeep b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/ablative_plating.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/ablative_plating.json new file mode 100644 index 0000000..2658432 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/ablative_plating.json @@ -0,0 +1,18 @@ +{ + "path": "alloys/ablative_plating", + "recipe": { + "inputs": { + "ceramic_composite": 1, + "carbon_ingot": 1 + }, + "output": { + "ablative_plating": 1 + }, + "alloy_time_seconds": 40 + }, + "craft": { + "type": "alloys", + "subCategory": "misc", + "id": "alloys:misc:alloys_ablative_plating" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/aerospace_aluminum.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/aerospace_aluminum.json new file mode 100644 index 0000000..883ac76 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/aerospace_aluminum.json @@ -0,0 +1,18 @@ +{ + "path": "alloys/aerospace_aluminum", + "recipe": { + "inputs": { + "aluminum_ingot": 2, + "magnesium_ingot": 1 + }, + "output": { + "aerospace_aluminum": 1 + }, + "alloy_time_seconds": 25 + }, + "craft": { + "type": "alloys", + "subCategory": "misc", + "id": "alloys:misc:alloys_aerospace_aluminum" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/ballistic_alloy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/ballistic_alloy.json new file mode 100644 index 0000000..7867299 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/ballistic_alloy.json @@ -0,0 +1,18 @@ +{ + "path": "alloys/ballistic_alloy", + "recipe": { + "inputs": { + "tungsten_ingot": 1, + "steel_alloy": 1 + }, + "output": { + "ballistic_alloy": 1 + }, + "alloy_time_seconds": 45 + }, + "craft": { + "type": "alloys", + "subCategory": "chemical", + "id": "alloys:chemical:alloys_ballistic_alloy" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/carbon_titanium_composite.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/carbon_titanium_composite.json new file mode 100644 index 0000000..6d0068a --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/carbon_titanium_composite.json @@ -0,0 +1,18 @@ +{ + "path": "alloys/carbon_titanium_composite", + "recipe": { + "inputs": { + "titanium_alloy": 2, + "carbon_ingot": 3 + }, + "output": { + "carbon_titanium_composite": 1 + }, + "alloy_time_seconds": 60 + }, + "craft": { + "type": "alloys", + "subCategory": "chemical", + "id": "alloys:chemical:alloys_carbon_titanium_composite" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/chromium_steel.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/chromium_steel.json new file mode 100644 index 0000000..a859067 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/chromium_steel.json @@ -0,0 +1,18 @@ +{ + "path": "alloys/chromium_steel", + "recipe": { + "inputs": { + "steel_alloy": 1, + "chromium_ingot": 1 + }, + "output": { + "chromium_steel": 1 + }, + "alloy_time_seconds": 30 + }, + "craft": { + "type": "alloys", + "subCategory": "misc", + "id": "alloys:misc:alloys_chromium_steel" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/chrono_alloy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/chrono_alloy.json new file mode 100644 index 0000000..a400c0f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/chrono_alloy.json @@ -0,0 +1,19 @@ +{ + "path": "alloys/chrono_alloy", + "recipe": { + "inputs": { + "phase_alloy": 1, + "chronite_ore": 2, + "titanium_alloy": 1 + }, + "output": { + "chrono_alloy": 1 + }, + "alloy_time_seconds": 150 + }, + "craft": { + "type": "alloys", + "subCategory": "misc", + "id": "alloys:misc:alloys_chrono_alloy" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/dimensional_alloy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/dimensional_alloy.json new file mode 100644 index 0000000..f3335e2 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/dimensional_alloy.json @@ -0,0 +1,19 @@ +{ + "path": "alloys/dimensional_alloy", + "recipe": { + "inputs": { + "neutronium_composite": 1, + "chrono_alloy": 1, + "void_crystal": 2 + }, + "output": { + "dimensional_alloy": 1 + }, + "alloy_time_seconds": 360 + }, + "craft": { + "type": "alloys", + "subCategory": "misc", + "id": "alloys:misc:alloys_dimensional_alloy" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/flux_core_alloy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/flux_core_alloy.json new file mode 100644 index 0000000..288e942 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/flux_core_alloy.json @@ -0,0 +1,19 @@ +{ + "path": "alloys/flux_core_alloy", + "recipe": { + "inputs": { + "steel_alloy": 2, + "flux_crystal": 1, + "chromium_ingot": 1 + }, + "output": { + "flux_core_alloy": 1 + }, + "alloy_time_seconds": 50 + }, + "craft": { + "type": "alloys", + "subCategory": "misc", + "id": "alloys:misc:alloys_flux_core_alloy" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/fusion_rated_alloy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/fusion_rated_alloy.json new file mode 100644 index 0000000..e8c666c --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/fusion_rated_alloy.json @@ -0,0 +1,18 @@ +{ + "path": "alloys/fusion_rated_alloy", + "recipe": { + "inputs": { + "titanium_alloy": 1, + "radiation_shield_alloy": 1 + }, + "output": { + "fusion_rated_alloy": 1 + }, + "alloy_time_seconds": 70 + }, + "craft": { + "type": "alloys", + "subCategory": "chemical", + "id": "alloys:chemical:alloys_fusion_rated_alloy" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/neutronium_composite.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/neutronium_composite.json new file mode 100644 index 0000000..d344a3c --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/neutronium_composite.json @@ -0,0 +1,19 @@ +{ + "path": "alloys/neutronium_composite", + "recipe": { + "inputs": { + "neutronium_ingot": 1, + "carbon_titanium_composite": 1, + "voidsteel": 1 + }, + "output": { + "neutronium_composite": 1 + }, + "alloy_time_seconds": 240 + }, + "craft": { + "type": "alloys", + "subCategory": "misc", + "id": "alloys:misc:alloys_neutronium_composite" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/phase_alloy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/phase_alloy.json new file mode 100644 index 0000000..27ec9a7 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/phase_alloy.json @@ -0,0 +1,18 @@ +{ + "path": "alloys/phase_alloy", + "recipe": { + "inputs": { + "titanium_alloy": 2, + "phase_crystal": 1 + }, + "output": { + "phase_alloy": 1 + }, + "alloy_time_seconds": 80 + }, + "craft": { + "type": "alloys", + "subCategory": "misc", + "id": "alloys:misc:alloys_phase_alloy" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/reactive_armor_alloy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/reactive_armor_alloy.json new file mode 100644 index 0000000..43bd60d --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/reactive_armor_alloy.json @@ -0,0 +1,18 @@ +{ + "path": "alloys/reactive_armor_alloy", + "recipe": { + "inputs": { + "ballistic_alloy": 1, + "explosive_compound": 1 + }, + "output": { + "reactive_armor_alloy": 1 + }, + "alloy_time_seconds": 60 + }, + "craft": { + "type": "alloys", + "subCategory": "chemical", + "id": "alloys:chemical:alloys_reactive_armor_alloy" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/reinforced_steel.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/reinforced_steel.json new file mode 100644 index 0000000..38c5026 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/reinforced_steel.json @@ -0,0 +1,18 @@ +{ + "path": "alloys/reinforced_steel", + "recipe": { + "inputs": { + "steel_alloy": 2, + "titanium_ingot": 1 + }, + "output": { + "reinforced_steel": 1 + }, + "alloy_time_seconds": 35 + }, + "craft": { + "type": "alloys", + "subCategory": "chemical", + "id": "alloys:chemical:alloys_reinforced_steel" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/steel_alloy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/steel_alloy.json new file mode 100644 index 0000000..66ca448 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/steel_alloy.json @@ -0,0 +1,18 @@ +{ + "path": "alloys/steel_alloy", + "recipe": { + "inputs": { + "iron_ingot": 2, + "carbon_ingot": 1 + }, + "output": { + "steel_alloy": 1 + }, + "alloy_time_seconds": 20 + }, + "craft": { + "type": "alloys", + "subCategory": "chemical", + "id": "alloys:chemical:alloys_steel_alloy" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/superconductive_alloy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/superconductive_alloy.json new file mode 100644 index 0000000..8107699 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/superconductive_alloy.json @@ -0,0 +1,19 @@ +{ + "path": "alloys/superconductive_alloy", + "recipe": { + "inputs": { + "copper_ingot": 1, + "silver_ingot": 1, + "flux_crystal": 1 + }, + "output": { + "superconductive_alloy": 1 + }, + "alloy_time_seconds": 55 + }, + "craft": { + "type": "alloys", + "subCategory": "chemical", + "id": "alloys:chemical:alloys_superconductive_alloy" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/titanium_alloy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/titanium_alloy.json new file mode 100644 index 0000000..cd0bcf2 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/titanium_alloy.json @@ -0,0 +1,18 @@ +{ + "path": "alloys/titanium_alloy", + "recipe": { + "inputs": { + "titanium_ingot": 2, + "carbon_ingot": 1 + }, + "output": { + "titanium_alloy": 1 + }, + "alloy_time_seconds": 30 + }, + "craft": { + "type": "alloys", + "subCategory": "chemical", + "id": "alloys:chemical:alloys_titanium_alloy" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/tungsten_steel.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/tungsten_steel.json new file mode 100644 index 0000000..ac03212 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/tungsten_steel.json @@ -0,0 +1,18 @@ +{ + "path": "alloys/tungsten_steel", + "recipe": { + "inputs": { + "steel_alloy": 1, + "tungsten_ingot": 1 + }, + "output": { + "tungsten_steel": 1 + }, + "alloy_time_seconds": 40 + }, + "craft": { + "type": "alloys", + "subCategory": "misc", + "id": "alloys:misc:alloys_tungsten_steel" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/voidsteel.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/voidsteel.json new file mode 100644 index 0000000..707d525 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/alloys/voidsteel.json @@ -0,0 +1,19 @@ +{ + "path": "alloys/voidsteel", + "recipe": { + "inputs": { + "darksteel_ingot": 2, + "void_crystal": 1, + "steel_alloy": 1 + }, + "output": { + "voidsteel": 1 + }, + "alloy_time_seconds": 90 + }, + "craft": { + "type": "alloys", + "subCategory": "misc", + "id": "alloys:misc:alloys_voidsteel" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/armours/body_basic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/armours/body_basic.json new file mode 100644 index 0000000..2847133 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/armours/body_basic.json @@ -0,0 +1,18 @@ +{ + "path": "armours/body_basic", + "recipe": { + "inputs": { + "steel_alloy": 4, + "bandage": 2 + }, + "output": { + "body_basic_common": 1 + }, + "craft_time_seconds": 60 + }, + "craft": { + "type": "armours", + "subCategory": "body", + "id": "armours:body:armours_body_basic" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/armours/body_plated.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/armours/body_plated.json new file mode 100644 index 0000000..bb957a5 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/armours/body_plated.json @@ -0,0 +1,18 @@ +{ + "path": "armours/body_plated", + "recipe": { + "inputs": { + "carbon_titanium_composite": 4, + "shield_control_circuit": 1 + }, + "output": { + "body_plated_rare": 1 + }, + "craft_time_seconds": 180 + }, + "craft": { + "type": "armours", + "subCategory": "body", + "id": "armours:body:armours_body_plated" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/armours/boots_basic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/armours/boots_basic.json new file mode 100644 index 0000000..9edd980 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/armours/boots_basic.json @@ -0,0 +1,18 @@ +{ + "path": "armours/boots_basic", + "recipe": { + "inputs": { + "steel_alloy": 2, + "iron_ingot": 1 + }, + "output": { + "boots_basic_common": 1 + }, + "craft_time_seconds": 30 + }, + "craft": { + "type": "armours", + "subCategory": "boots", + "id": "armours:boots:armours_boots_basic" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/armours/gloves_basic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/armours/gloves_basic.json new file mode 100644 index 0000000..fd09e48 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/armours/gloves_basic.json @@ -0,0 +1,18 @@ +{ + "path": "armours/gloves_basic", + "recipe": { + "inputs": { + "iron_ingot": 2, + "bandage": 1 + }, + "output": { + "gloves_basic_common": 1 + }, + "craft_time_seconds": 30 + }, + "craft": { + "type": "armours", + "subCategory": "hands", + "id": "armours:hands:armours_gloves_basic" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/armours/helmet_basic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/armours/helmet_basic.json new file mode 100644 index 0000000..8ea4197 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/armours/helmet_basic.json @@ -0,0 +1,18 @@ +{ + "path": "armours/helmet_basic", + "recipe": { + "inputs": { + "steel_alloy": 2, + "basic_circuit": 1 + }, + "output": { + "helmet_basic_common": 1 + }, + "craft_time_seconds": 45 + }, + "craft": { + "type": "armours", + "subCategory": "helmet", + "id": "armours:helmet:armours_helmet_basic" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/armours/pants_basic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/armours/pants_basic.json new file mode 100644 index 0000000..0cb2262 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/armours/pants_basic.json @@ -0,0 +1,18 @@ +{ + "path": "armours/pants_basic", + "recipe": { + "inputs": { + "steel_alloy": 3, + "iron_ingot": 1 + }, + "output": { + "pants_basic_common": 1 + }, + "craft_time_seconds": 45 + }, + "craft": { + "type": "armours", + "subCategory": "pants", + "id": "armours:pants:armours_pants_basic" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/ai_core_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/ai_core_circuit.json new file mode 100644 index 0000000..84ff449 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/ai_core_circuit.json @@ -0,0 +1,18 @@ +{ + "path": "circuits/ai_core_circuit", + "recipe": { + "inputs": { + "quantum_circuit": 2, + "superconductive_alloy": 1 + }, + "output": { + "ai_core_circuit": 1 + }, + "craft_time_seconds": 90 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_ai_core_circuit" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/basic_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/basic_circuit.json new file mode 100644 index 0000000..a40b958 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/basic_circuit.json @@ -0,0 +1,18 @@ +{ + "path": "circuits/basic_circuit", + "recipe": { + "inputs": { + "copper_ingot": 1, + "carbon_ingot": 1 + }, + "output": { + "basic_circuit": 1 + }, + "craft_time_seconds": 10 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_basic_circuit" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/control_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/control_circuit.json new file mode 100644 index 0000000..8a8285c --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/control_circuit.json @@ -0,0 +1,18 @@ +{ + "path": "circuits/control_circuit", + "recipe": { + "inputs": { + "logic_circuit": 1, + "copper_ingot": 2 + }, + "output": { + "control_circuit": 1 + }, + "craft_time_seconds": 20 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_control_circuit" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/dimensional_logic_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/dimensional_logic_circuit.json new file mode 100644 index 0000000..3ba64ea --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/dimensional_logic_circuit.json @@ -0,0 +1,19 @@ +{ + "path": "circuits/dimensional_logic_circuit", + "recipe": { + "inputs": { + "quantum_circuit": 1, + "dimensional_alloy": 1, + "void_crystal": 1 + }, + "output": { + "dimensional_logic_circuit": 1 + }, + "craft_time_seconds": 300 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_dimensional_logic_circuit" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/high_density_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/high_density_circuit.json new file mode 100644 index 0000000..4854397 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/high_density_circuit.json @@ -0,0 +1,18 @@ +{ + "path": "circuits/high_density_circuit", + "recipe": { + "inputs": { + "logic_circuit": 2, + "silver_ingot": 1 + }, + "output": { + "high_density_circuit": 1 + }, + "craft_time_seconds": 30 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_high_density_circuit" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/logic_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/logic_circuit.json new file mode 100644 index 0000000..e7b1bbc --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/logic_circuit.json @@ -0,0 +1,18 @@ +{ + "path": "circuits/logic_circuit", + "recipe": { + "inputs": { + "basic_circuit": 1, + "silicon_wafer": 1 + }, + "output": { + "logic_circuit": 1 + }, + "craft_time_seconds": 15 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_logic_circuit" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/navigation_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/navigation_circuit.json new file mode 100644 index 0000000..4123b5a --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/navigation_circuit.json @@ -0,0 +1,18 @@ +{ + "path": "circuits/navigation_circuit", + "recipe": { + "inputs": { + "control_circuit": 2, + "silver_ingot": 1 + }, + "output": { + "navigation_circuit": 1 + }, + "craft_time_seconds": 30 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_navigation_circuit" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/power_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/power_circuit.json new file mode 100644 index 0000000..f1ad77d --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/power_circuit.json @@ -0,0 +1,19 @@ +{ + "path": "circuits/power_circuit", + "recipe": { + "inputs": { + "control_circuit": 1, + "iron_ingot": 1, + "magnetic_coil": 1 + }, + "output": { + "power_circuit": 1 + }, + "craft_time_seconds": 25 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_power_circuit" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/processor_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/processor_circuit.json new file mode 100644 index 0000000..3cdcffa --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/processor_circuit.json @@ -0,0 +1,19 @@ +{ + "path": "circuits/processor_circuit", + "recipe": { + "inputs": { + "logic_circuit": 2, + "silicon_wafer": 2, + "gold_ingot": 1 + }, + "output": { + "processor_circuit": 1 + }, + "craft_time_seconds": 40 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_processor_circuit" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/quantum_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/quantum_circuit.json new file mode 100644 index 0000000..b3cb46d --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/quantum_circuit.json @@ -0,0 +1,19 @@ +{ + "path": "circuits/quantum_circuit", + "recipe": { + "inputs": { + "processor_circuit": 1, + "flux_crystal": 1, + "titanium_ingot": 1 + }, + "output": { + "quantum_circuit": 1 + }, + "craft_time_seconds": 90 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_quantum_circuit" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/reactor_control_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/reactor_control_circuit.json new file mode 100644 index 0000000..dc70fcf --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/reactor_control_circuit.json @@ -0,0 +1,18 @@ +{ + "path": "circuits/reactor_control_circuit", + "recipe": { + "inputs": { + "processor_circuit": 2, + "flux_core_alloy": 1 + }, + "output": { + "reactor_control_circuit": 1 + }, + "craft_time_seconds": 55 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_reactor_control_circuit" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/sensor_array_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/sensor_array_circuit.json new file mode 100644 index 0000000..e3f5a60 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/sensor_array_circuit.json @@ -0,0 +1,18 @@ +{ + "path": "circuits/sensor_array_circuit", + "recipe": { + "inputs": { + "high_density_circuit": 1, + "silver_ingot": 1 + }, + "output": { + "sensor_array_circuit": 1 + }, + "craft_time_seconds": 25 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_sensor_array_circuit" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/sentient_core_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/sentient_core_circuit.json new file mode 100644 index 0000000..d1665e7 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/sentient_core_circuit.json @@ -0,0 +1,19 @@ +{ + "path": "circuits/sentient_core_circuit", + "recipe": { + "inputs": { + "ai_core_circuit": 2, + "chrono_alloy": 1, + "voidsteel": 1 + }, + "output": { + "sentient_core_circuit": 1 + }, + "craft_time_seconds": 180 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_sentient_core_circuit" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/shield_control_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/shield_control_circuit.json new file mode 100644 index 0000000..c453f28 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/shield_control_circuit.json @@ -0,0 +1,18 @@ +{ + "path": "circuits/shield_control_circuit", + "recipe": { + "inputs": { + "control_circuit": 2, + "magnetic_coil": 2 + }, + "output": { + "shield_control_circuit": 1 + }, + "craft_time_seconds": 50 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_shield_control_circuit" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/stealth_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/stealth_circuit.json new file mode 100644 index 0000000..3b86817 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/stealth_circuit.json @@ -0,0 +1,18 @@ +{ + "path": "circuits/stealth_circuit", + "recipe": { + "inputs": { + "processor_circuit": 1, + "void_residue": 1 + }, + "output": { + "stealth_circuit": 1 + }, + "craft_time_seconds": 60 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_stealth_circuit" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/warp_navigation_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/warp_navigation_circuit.json new file mode 100644 index 0000000..930ab45 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/warp_navigation_circuit.json @@ -0,0 +1,18 @@ +{ + "path": "circuits/warp_navigation_circuit", + "recipe": { + "inputs": { + "navigation_circuit": 2, + "chrono_alloy": 1 + }, + "output": { + "warp_navigation_circuit": 1 + }, + "craft_time_seconds": 75 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_warp_navigation_circuit" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/weapon_control_circuit.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/weapon_control_circuit.json new file mode 100644 index 0000000..fdebaee --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/circuits/weapon_control_circuit.json @@ -0,0 +1,18 @@ +{ + "path": "circuits/weapon_control_circuit", + "recipe": { + "inputs": { + "control_circuit": 2, + "power_circuit": 1 + }, + "output": { + "weapon_control_circuit": 1 + }, + "craft_time_seconds": 50 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_weapon_control_circuit" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/consumables/energy_boost_small.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/consumables/energy_boost_small.json new file mode 100644 index 0000000..58afca8 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/consumables/energy_boost_small.json @@ -0,0 +1,18 @@ +{ + "path": "consumables/energy_boost_small", + "recipe": { + "inputs": { + "flux_crystal": 1, + "copper_ingot": 1 + }, + "output": { + "energy_boost_small": 1 + }, + "craft_time_seconds": 15 + }, + "craft": { + "type": "consumables", + "subCategory": "misc", + "id": "consumables:misc:consumables_energy_boost_small" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/consumables/fuel_cell.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/consumables/fuel_cell.json new file mode 100644 index 0000000..ba48ac6 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/consumables/fuel_cell.json @@ -0,0 +1,18 @@ +{ + "path": "consumables/fuel_cell", + "recipe": { + "inputs": { + "iron_ingot": 1, + "magnetic_coil": 1 + }, + "output": { + "fuel_cell": 3 + }, + "craft_time_seconds": 20 + }, + "craft": { + "type": "consumables", + "subCategory": "misc", + "id": "consumables:misc:consumables_fuel_cell" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/consumables/health_kit_large.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/consumables/health_kit_large.json new file mode 100644 index 0000000..6e8ceef --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/consumables/health_kit_large.json @@ -0,0 +1,18 @@ +{ + "path": "consumables/health_kit_large", + "recipe": { + "inputs": { + "bandage": 5, + "alien_fauna_sample": 3 + }, + "output": { + "health_kit_large": 1 + }, + "craft_time_seconds": 30 + }, + "craft": { + "type": "consumables", + "subCategory": "misc", + "id": "consumables:misc:consumables_health_kit_large" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/consumables/health_kit_small.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/consumables/health_kit_small.json new file mode 100644 index 0000000..0636738 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/consumables/health_kit_small.json @@ -0,0 +1,18 @@ +{ + "path": "consumables/health_kit_small", + "recipe": { + "inputs": { + "bandage": 2, + "alien_fauna_sample": 1 + }, + "output": { + "health_kit_small": 1 + }, + "craft_time_seconds": 15 + }, + "craft": { + "type": "consumables", + "subCategory": "misc", + "id": "consumables:misc:consumables_health_kit_small" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/crafting_tabs.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/crafting_tabs.json new file mode 100644 index 0000000..08c45e8 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/crafting_tabs.json @@ -0,0 +1,212 @@ +{ + "craftingTypes": [ + { + "id": "smelting", + "displayName": "Smelting", + "tabs": [ + { + "id": "ingot", + "displayName": "Ingots", + "recipeFilter": { + "type": "smelting", + "subCategory": "ingot" + }, + "recipesDir": "data/gso/recipes/smelting", + "iconTexture": "assets/gso/textures/ui/crafting/smelting/ingot.png" + } + ] + }, + { + "id": "alloys", + "displayName": "Alloys", + "tabs": [ + { + "id": "chemical", + "displayName": "Chemical", + "recipeFilter": { + "type": "alloys", + "subCategory": "chemical" + }, + "recipesDir": "data/gso/recipes/alloys", + "iconTexture": "assets/gso/textures/ui/crafting/alloys/chemical.png" + }, + { + "id": "misc", + "displayName": "Misc", + "recipeFilter": { + "type": "alloys", + "subCategory": "misc" + }, + "recipesDir": "data/gso/recipes/alloys", + "iconTexture": "assets/gso/textures/ui/crafting/alloys/misc.png" + } + ] + }, + { + "id": "circuits", + "displayName": "Circuits", + "tabs": [ + { + "id": "electronics", + "displayName": "Electronics", + "recipeFilter": { + "type": "circuits", + "subCategory": "electronics" + }, + "recipesDir": "data/gso/recipes/circuits", + "iconTexture": "assets/gso/textures/ui/crafting/circuits/electronics.png" + } + ] + }, + { + "id": "hull_sections", + "displayName": "Hull Sections", + "tabs": [ + { + "id": "misc", + "displayName": "Misc", + "recipeFilter": { + "type": "hull_sections", + "subCategory": "misc" + }, + "recipesDir": "data/gso/recipes/hull_sections", + "iconTexture": "assets/gso/textures/ui/crafting/hull_sections/misc.png" + } + ] + }, + { + "id": "hulls", + "displayName": "Hulls", + "tabs": [ + { + "id": "misc", + "displayName": "Misc", + "recipeFilter": { + "type": "hulls", + "subCategory": "misc" + }, + "recipesDir": "data/gso/recipes/hulls", + "iconTexture": "assets/gso/textures/ui/crafting/hulls/misc.png" + } + ] + }, + { + "id": "weapons", + "displayName": "Weapons", + "tabs": [ + { + "id": "pistol", + "displayName": "Pistols", + "recipeFilter": { + "type": "weapons", + "subCategory": "pistol" + }, + "recipesDir": "data/gso/recipes/weapons", + "iconTexture": "assets/gso/textures/ui/crafting/weapons/pistol.png" + }, + { + "id": "rifle", + "displayName": "Rifles", + "recipeFilter": { + "type": "weapons", + "subCategory": "rifle" + }, + "recipesDir": "data/gso/recipes/weapons", + "iconTexture": "assets/gso/textures/ui/crafting/weapons/rifle.png" + }, + { + "id": "sniper", + "displayName": "Snipers", + "recipeFilter": { + "type": "weapons", + "subCategory": "sniper" + }, + "recipesDir": "data/gso/recipes/weapons", + "iconTexture": "assets/gso/textures/ui/crafting/weapons/sniper.png" + }, + { + "id": "special", + "displayName": "Special", + "recipeFilter": { + "type": "weapons", + "subCategory": "special" + }, + "recipesDir": "data/gso/recipes/weapons", + "iconTexture": "assets/gso/textures/ui/crafting/weapons/special.png" + } + ] + }, + { + "id": "armours", + "displayName": "Armour", + "tabs": [ + { + "id": "body", + "displayName": "Body", + "recipeFilter": { + "type": "armours", + "subCategory": "body" + }, + "recipesDir": "data/gso/recipes/armours", + "iconTexture": "assets/gso/textures/ui/crafting/armours/body.png" + }, + { + "id": "helmet", + "displayName": "Helmet", + "recipeFilter": { + "type": "armours", + "subCategory": "helmet" + }, + "recipesDir": "data/gso/recipes/armours", + "iconTexture": "assets/gso/textures/ui/crafting/armours/helmet.png" + }, + { + "id": "boots", + "displayName": "Boots", + "recipeFilter": { + "type": "armours", + "subCategory": "boots" + }, + "recipesDir": "data/gso/recipes/armours", + "iconTexture": "assets/gso/textures/ui/crafting/armours/boots.png" + }, + { + "id": "hands", + "displayName": "Gloves", + "recipeFilter": { + "type": "armours", + "subCategory": "hands" + }, + "recipesDir": "data/gso/recipes/armours", + "iconTexture": "assets/gso/textures/ui/crafting/armours/hands.png" + }, + { + "id": "pants", + "displayName": "Pants", + "recipeFilter": { + "type": "armours", + "subCategory": "pants" + }, + "recipesDir": "data/gso/recipes/armours", + "iconTexture": "assets/gso/textures/ui/crafting/armours/pants.png" + } + ] + }, + { + "id": "consumables", + "displayName": "Consumables", + "tabs": [ + { + "id": "misc", + "displayName": "Misc", + "recipeFilter": { + "type": "consumables", + "subCategory": "misc" + }, + "recipesDir": "data/gso/recipes/consumables", + "iconTexture": "assets/gso/textures/ui/crafting/consumables/misc.png" + } + ] + } + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/alien_protein.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/alien_protein.json new file mode 100644 index 0000000..ce1a31d --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/alien_protein.json @@ -0,0 +1,17 @@ +{ + "path": "food_organics/alien_protein", + "recipe": { + "inputs": { + "alien_fauna_sample": 1 + }, + "output": { + "alien_protein": 2 + }, + "harvest_time_seconds": 45 + }, + "craft": { + "type": "food_organics", + "subCategory": "misc", + "id": "food_organics:misc:food_organics_alien_protein" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/bread.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/bread.json new file mode 100644 index 0000000..00d55bb --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/bread.json @@ -0,0 +1,17 @@ +{ + "path": "food_organics/bread", + "recipe": { + "inputs": { + "dough": 1 + }, + "output": { + "bread": 1 + }, + "cook_time_seconds": 25 + }, + "craft": { + "type": "food_organics", + "subCategory": "food", + "id": "food_organics:food:food_organics_bread" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/cooked_fish.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/cooked_fish.json new file mode 100644 index 0000000..758edc5 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/cooked_fish.json @@ -0,0 +1,17 @@ +{ + "path": "food_organics/cooked_fish", + "recipe": { + "inputs": { + "fish_meat": 1 + }, + "output": { + "cooked_fish": 1 + }, + "cook_time_seconds": 15 + }, + "craft": { + "type": "food_organics", + "subCategory": "misc", + "id": "food_organics:misc:food_organics_cooked_fish" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/cooked_meat.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/cooked_meat.json new file mode 100644 index 0000000..ef171d6 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/cooked_meat.json @@ -0,0 +1,17 @@ +{ + "path": "food_organics/cooked_meat", + "recipe": { + "inputs": { + "raw_meat": 1 + }, + "output": { + "cooked_meat": 1 + }, + "cook_time_seconds": 20 + }, + "craft": { + "type": "food_organics", + "subCategory": "misc", + "id": "food_organics:misc:food_organics_cooked_meat" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/dough.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/dough.json new file mode 100644 index 0000000..27254b2 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/dough.json @@ -0,0 +1,18 @@ +{ + "path": "food_organics/dough", + "recipe": { + "inputs": { + "flour": 1, + "water": 1 + }, + "output": { + "dough": 1 + }, + "craft_time_seconds": 10 + }, + "craft": { + "type": "food_organics", + "subCategory": "misc", + "id": "food_organics:misc:food_organics_dough" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/fish_meat.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/fish_meat.json new file mode 100644 index 0000000..e6e37e1 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/fish_meat.json @@ -0,0 +1,17 @@ +{ + "path": "food_organics/fish_meat", + "recipe": { + "inputs": { + "fish": 1 + }, + "output": { + "fish_meat": 2 + }, + "harvest_time_seconds": 20 + }, + "craft": { + "type": "food_organics", + "subCategory": "food", + "id": "food_organics:food:food_organics_fish_meat" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/flour.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/flour.json new file mode 100644 index 0000000..972aa2e --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/flour.json @@ -0,0 +1,17 @@ +{ + "path": "food_organics/flour", + "recipe": { + "inputs": { + "grain": 2 + }, + "output": { + "flour": 1 + }, + "craft_time_seconds": 20 + }, + "craft": { + "type": "food_organics", + "subCategory": "food", + "id": "food_organics:food:food_organics_flour" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/grain.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/grain.json new file mode 100644 index 0000000..72f13bc --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/grain.json @@ -0,0 +1,17 @@ +{ + "path": "food_organics/grain", + "recipe": { + "inputs": { + "grain_crop": 1 + }, + "output": { + "grain": 4 + }, + "harvest_time_seconds": 30 + }, + "craft": { + "type": "food_organics", + "subCategory": "food", + "id": "food_organics:food:food_organics_grain" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/hydroponic_growth_medium.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/hydroponic_growth_medium.json new file mode 100644 index 0000000..f80784b --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/hydroponic_growth_medium.json @@ -0,0 +1,19 @@ +{ + "path": "food_organics/hydroponic_growth_medium", + "recipe": { + "inputs": { + "bio_pulp": 1, + "water": 1, + "nutrient_paste": 1 + }, + "output": { + "hydroponic_growth_medium": 1 + }, + "craft_time_seconds": 40 + }, + "craft": { + "type": "food_organics", + "subCategory": "misc", + "id": "food_organics:misc:food_organics_hydroponic_growth_medium" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/luxury_meal.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/luxury_meal.json new file mode 100644 index 0000000..bc5b9cc --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/luxury_meal.json @@ -0,0 +1,19 @@ +{ + "path": "food_organics/luxury_meal", + "recipe": { + "inputs": { + "cooked_meat": 1, + "raw_fruits": 1, + "vegetable_stew": 1 + }, + "output": { + "luxury_meal": 1 + }, + "cook_time_seconds": 60 + }, + "craft": { + "type": "food_organics", + "subCategory": "misc", + "id": "food_organics:misc:food_organics_luxury_meal" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/meat_stew.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/meat_stew.json new file mode 100644 index 0000000..1bbc79d --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/meat_stew.json @@ -0,0 +1,19 @@ +{ + "path": "food_organics/meat_stew", + "recipe": { + "inputs": { + "raw_meat": 1, + "raw_vegetables": 1, + "water": 1 + }, + "output": { + "meat_stew": 1 + }, + "cook_time_seconds": 35 + }, + "craft": { + "type": "food_organics", + "subCategory": "food", + "id": "food_organics:food:food_organics_meat_stew" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/military_ration.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/military_ration.json new file mode 100644 index 0000000..de01869 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/military_ration.json @@ -0,0 +1,18 @@ +{ + "path": "food_organics/military_ration", + "recipe": { + "inputs": { + "ration_pack": 2, + "nutrient_paste": 1 + }, + "output": { + "military_ration": 1 + }, + "craft_time_seconds": 30 + }, + "craft": { + "type": "food_organics", + "subCategory": "misc", + "id": "food_organics:misc:food_organics_military_ration" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/nutrient_paste.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/nutrient_paste.json new file mode 100644 index 0000000..2d721ee --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/nutrient_paste.json @@ -0,0 +1,18 @@ +{ + "path": "food_organics/nutrient_paste", + "recipe": { + "inputs": { + "bio_pulp": 1, + "water": 1 + }, + "output": { + "nutrient_paste": 2 + }, + "craft_time_seconds": 15 + }, + "craft": { + "type": "food_organics", + "subCategory": "misc", + "id": "food_organics:misc:food_organics_nutrient_paste" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/protein_bar.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/protein_bar.json new file mode 100644 index 0000000..8090f65 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/protein_bar.json @@ -0,0 +1,18 @@ +{ + "path": "food_organics/protein_bar", + "recipe": { + "inputs": { + "alien_protein": 1, + "nutrient_paste": 1 + }, + "output": { + "protein_bar": 2 + }, + "craft_time_seconds": 20 + }, + "craft": { + "type": "food_organics", + "subCategory": "misc", + "id": "food_organics:misc:food_organics_protein_bar" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/ration_pack.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/ration_pack.json new file mode 100644 index 0000000..7ec2e9a --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/ration_pack.json @@ -0,0 +1,18 @@ +{ + "path": "food_organics/ration_pack", + "recipe": { + "inputs": { + "protein_bar": 1, + "bread": 1 + }, + "output": { + "ration_pack": 1 + }, + "craft_time_seconds": 15 + }, + "craft": { + "type": "food_organics", + "subCategory": "food", + "id": "food_organics:food:food_organics_ration_pack" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/raw_fruits.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/raw_fruits.json new file mode 100644 index 0000000..7f65568 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/raw_fruits.json @@ -0,0 +1,17 @@ +{ + "path": "food_organics/raw_fruits", + "recipe": { + "inputs": { + "fruit_plant": 1 + }, + "output": { + "raw_fruits": 3 + }, + "harvest_time_seconds": 20 + }, + "craft": { + "type": "food_organics", + "subCategory": "misc", + "id": "food_organics:misc:food_organics_raw_fruits" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/raw_meat.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/raw_meat.json new file mode 100644 index 0000000..36713ce --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/raw_meat.json @@ -0,0 +1,17 @@ +{ + "path": "food_organics/raw_meat", + "recipe": { + "inputs": { + "animal_carcass": 1 + }, + "output": { + "raw_meat": 4 + }, + "harvest_time_seconds": 40 + }, + "craft": { + "type": "food_organics", + "subCategory": "misc", + "id": "food_organics:misc:food_organics_raw_meat" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/raw_vegetables.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/raw_vegetables.json new file mode 100644 index 0000000..799cd1b --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/raw_vegetables.json @@ -0,0 +1,17 @@ +{ + "path": "food_organics/raw_vegetables", + "recipe": { + "inputs": { + "vegetable_plant": 1 + }, + "output": { + "raw_vegetables": 3 + }, + "harvest_time_seconds": 25 + }, + "craft": { + "type": "food_organics", + "subCategory": "misc", + "id": "food_organics:misc:food_organics_raw_vegetables" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/vegetable_stew.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/vegetable_stew.json new file mode 100644 index 0000000..fc35890 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/vegetable_stew.json @@ -0,0 +1,18 @@ +{ + "path": "food_organics/vegetable_stew", + "recipe": { + "inputs": { + "raw_vegetables": 2, + "water": 1 + }, + "output": { + "vegetable_stew": 1 + }, + "cook_time_seconds": 30 + }, + "craft": { + "type": "food_organics", + "subCategory": "food", + "id": "food_organics:food:food_organics_vegetable_stew" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/water.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/water.json new file mode 100644 index 0000000..ee57bf2 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/food_organics/water.json @@ -0,0 +1,17 @@ +{ + "path": "food_organics/water", + "recipe": { + "inputs": { + "ice_chunk": 1 + }, + "output": { + "water": 2 + }, + "process_time_seconds": 10 + }, + "craft": { + "type": "food_organics", + "subCategory": "liquid", + "id": "food_organics:liquid:food_organics_water" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hull_sections/advanced_hull_section.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hull_sections/advanced_hull_section.json new file mode 100644 index 0000000..eb6995e --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hull_sections/advanced_hull_section.json @@ -0,0 +1,18 @@ +{ + "path": "hull_sections/advanced_hull_section", + "recipe": { + "inputs": { + "flux_core_alloy": 3, + "processor_circuit": 1 + }, + "output": { + "advanced_hull_section": 1 + }, + "construction_time_seconds": 240 + }, + "craft": { + "type": "hull_sections", + "subCategory": "misc", + "id": "hull_sections:misc:hull_sections_advanced_hull_section" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hull_sections/armor_hull_section.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hull_sections/armor_hull_section.json new file mode 100644 index 0000000..d89833a --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hull_sections/armor_hull_section.json @@ -0,0 +1,18 @@ +{ + "path": "hull_sections/armor_hull_section", + "recipe": { + "inputs": { + "carbon_titanium_composite": 3, + "shield_control_circuit": 1 + }, + "output": { + "armor_hull_section": 1 + }, + "construction_time_seconds": 180 + }, + "craft": { + "type": "hull_sections", + "subCategory": "misc", + "id": "hull_sections:misc:hull_sections_armor_hull_section" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hull_sections/exotic_hull_section.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hull_sections/exotic_hull_section.json new file mode 100644 index 0000000..148fb09 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hull_sections/exotic_hull_section.json @@ -0,0 +1,18 @@ +{ + "path": "hull_sections/exotic_hull_section", + "recipe": { + "inputs": { + "voidsteel": 3, + "quantum_circuit": 1 + }, + "output": { + "exotic_hull_section": 1 + }, + "construction_time_seconds": 360 + }, + "craft": { + "type": "hull_sections", + "subCategory": "misc", + "id": "hull_sections:misc:hull_sections_exotic_hull_section" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hull_sections/heavy_hull_section.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hull_sections/heavy_hull_section.json new file mode 100644 index 0000000..13d8a1f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hull_sections/heavy_hull_section.json @@ -0,0 +1,18 @@ +{ + "path": "hull_sections/heavy_hull_section", + "recipe": { + "inputs": { + "titanium_alloy": 4, + "power_circuit": 1 + }, + "output": { + "heavy_hull_section": 1 + }, + "construction_time_seconds": 120 + }, + "craft": { + "type": "hull_sections", + "subCategory": "misc", + "id": "hull_sections:misc:hull_sections_heavy_hull_section" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hull_sections/light_hull_section.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hull_sections/light_hull_section.json new file mode 100644 index 0000000..070e1ab --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hull_sections/light_hull_section.json @@ -0,0 +1,18 @@ +{ + "path": "hull_sections/light_hull_section", + "recipe": { + "inputs": { + "steel_alloy": 4, + "basic_circuit": 1 + }, + "output": { + "light_hull_section": 1 + }, + "construction_time_seconds": 60 + }, + "craft": { + "type": "hull_sections", + "subCategory": "misc", + "id": "hull_sections:misc:hull_sections_light_hull_section" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hull_sections/standard_hull_section.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hull_sections/standard_hull_section.json new file mode 100644 index 0000000..9a2c446 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hull_sections/standard_hull_section.json @@ -0,0 +1,18 @@ +{ + "path": "hull_sections/standard_hull_section", + "recipe": { + "inputs": { + "reinforced_steel": 4, + "control_circuit": 1 + }, + "output": { + "standard_hull_section": 1 + }, + "construction_time_seconds": 90 + }, + "craft": { + "type": "hull_sections", + "subCategory": "misc", + "id": "hull_sections:misc:hull_sections_standard_hull_section" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/ablative_armor_hull.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/ablative_armor_hull.json new file mode 100644 index 0000000..1f60759 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/ablative_armor_hull.json @@ -0,0 +1,15 @@ +{ + "path": "hulls/ablative_armor_hull", + "recipe": { + "inputs": { + "armor_hull_section": 24, + "ablative_plating": 16 + }, + "construction_time_seconds": 680 + }, + "craft": { + "type": "hulls", + "subCategory": "plating", + "id": "hulls:plating:hulls_ablative_armor_hull" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/advanced_hull.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/advanced_hull.json new file mode 100644 index 0000000..9f4e161 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/advanced_hull.json @@ -0,0 +1,18 @@ +{ + "path": "hulls/advanced_hull", + "recipe": { + "inputs": { + "advanced_hull_section": 8, + "flux_core_alloy": 4 + }, + "output": { + "advanced_hull": 1 + }, + "construction_time_seconds": 900 + }, + "craft": { + "type": "hulls", + "subCategory": "misc", + "id": "hulls:misc:hulls_advanced_hull" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/armor_hull.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/armor_hull.json new file mode 100644 index 0000000..15a827c --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/armor_hull.json @@ -0,0 +1,18 @@ +{ + "path": "hulls/armor_hull", + "recipe": { + "inputs": { + "armor_hull_section": 8, + "carbon_titanium_composite": 4 + }, + "output": { + "armor_hull": 1 + }, + "construction_time_seconds": 600 + }, + "craft": { + "type": "hulls", + "subCategory": "misc", + "id": "hulls:misc:hulls_armor_hull" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/chrono_stabilized_hull.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/chrono_stabilized_hull.json new file mode 100644 index 0000000..c45e756 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/chrono_stabilized_hull.json @@ -0,0 +1,15 @@ +{ + "path": "hulls/chrono_stabilized_hull", + "recipe": { + "inputs": { + "advanced_hull_section": 30, + "chrono_alloy": 16 + }, + "construction_time_seconds": 1100 + }, + "craft": { + "type": "hulls", + "subCategory": "module", + "id": "hulls:module:hulls_chrono_stabilized_hull" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/dimensional_anchor_hull.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/dimensional_anchor_hull.json new file mode 100644 index 0000000..2a15205 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/dimensional_anchor_hull.json @@ -0,0 +1,15 @@ +{ + "path": "hulls/dimensional_anchor_hull", + "recipe": { + "inputs": { + "exotic_hull_section": 48, + "dimensional_alloy": 24 + }, + "construction_time_seconds": 1500 + }, + "craft": { + "type": "hulls", + "subCategory": "module", + "id": "hulls:module:hulls_dimensional_anchor_hull" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/dreadframe_hull.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/dreadframe_hull.json new file mode 100644 index 0000000..6dbd350 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/dreadframe_hull.json @@ -0,0 +1,19 @@ +{ + "path": "hulls/dreadframe_hull", + "recipe": { + "inputs": { + "advanced_hull_section": 12, + "neutronium_composite": 4, + "dimensional_alloy": 2 + }, + "output": { + "dreadframe_hull": 1 + }, + "construction_time_seconds": 3600 + }, + "craft": { + "type": "hulls", + "subCategory": "misc", + "id": "hulls:misc:hulls_dreadframe_hull" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/heavy_hull.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/heavy_hull.json new file mode 100644 index 0000000..4e6bcdd --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/heavy_hull.json @@ -0,0 +1,18 @@ +{ + "path": "hulls/heavy_hull", + "recipe": { + "inputs": { + "heavy_hull_section": 10, + "titanium_alloy": 4 + }, + "output": { + "heavy_hull": 1 + }, + "construction_time_seconds": 480 + }, + "craft": { + "type": "hulls", + "subCategory": "misc", + "id": "hulls:misc:hulls_heavy_hull" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/industrial_hull.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/industrial_hull.json new file mode 100644 index 0000000..062b023 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/industrial_hull.json @@ -0,0 +1,15 @@ +{ + "path": "hulls/industrial_hull", + "recipe": { + "inputs": { + "heavy_hull_section": 24, + "reinforced_steel": 8 + }, + "construction_time_seconds": 600 + }, + "craft": { + "type": "hulls", + "subCategory": "misc", + "id": "hulls:misc:hulls_industrial_hull" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/lightframe_hull.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/lightframe_hull.json new file mode 100644 index 0000000..493d2ac --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/lightframe_hull.json @@ -0,0 +1,18 @@ +{ + "path": "hulls/lightframe_hull", + "recipe": { + "inputs": { + "light_hull_section": 8, + "carbon_titanium_composite": 2 + }, + "output": { + "lightframe_hull": 1 + }, + "construction_time_seconds": 180 + }, + "craft": { + "type": "hulls", + "subCategory": "misc", + "id": "hulls:misc:hulls_lightframe_hull" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/phase_locked_hull.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/phase_locked_hull.json new file mode 100644 index 0000000..a97f65a --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/phase_locked_hull.json @@ -0,0 +1,19 @@ +{ + "path": "hulls/phase_locked_hull", + "recipe": { + "inputs": { + "exotic_hull_section": 6, + "phase_alloy": 4, + "phase_crystal": 2 + }, + "output": { + "phase_locked_hull": 1 + }, + "construction_time_seconds": 1500 + }, + "craft": { + "type": "hulls", + "subCategory": "misc", + "id": "hulls:misc:hulls_phase_locked_hull" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/plasma_resistant_hull.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/plasma_resistant_hull.json new file mode 100644 index 0000000..6f17c05 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/plasma_resistant_hull.json @@ -0,0 +1,15 @@ +{ + "path": "hulls/plasma_resistant_hull", + "recipe": { + "inputs": { + "advanced_hull_section": 28, + "plasma_channel_alloy": 10 + }, + "construction_time_seconds": 840 + }, + "craft": { + "type": "hulls", + "subCategory": "misc", + "id": "hulls:misc:hulls_plasma_resistant_hull" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/reinforced_plating_hull.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/reinforced_plating_hull.json new file mode 100644 index 0000000..ce44870 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/reinforced_plating_hull.json @@ -0,0 +1,15 @@ +{ + "path": "hulls/reinforced_plating_hull", + "recipe": { + "inputs": { + "armor_hull_section": 32, + "ballistic_alloy": 12 + }, + "construction_time_seconds": 720 + }, + "craft": { + "type": "hulls", + "subCategory": "plating", + "id": "hulls:plating:hulls_reinforced_plating_hull" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/standard_hull.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/standard_hull.json new file mode 100644 index 0000000..b5fcc41 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/standard_hull.json @@ -0,0 +1,18 @@ +{ + "path": "hulls/standard_hull", + "recipe": { + "inputs": { + "standard_hull_section": 8, + "reinforced_steel": 4 + }, + "output": { + "standard_hull": 1 + }, + "construction_time_seconds": 300 + }, + "craft": { + "type": "hulls", + "subCategory": "misc", + "id": "hulls:misc:hulls_standard_hull" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/standard_structural_hull.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/standard_structural_hull.json new file mode 100644 index 0000000..12f17de --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/standard_structural_hull.json @@ -0,0 +1,15 @@ +{ + "path": "hulls/standard_structural_hull", + "recipe": { + "inputs": { + "standard_hull_section": 20, + "reinforced_steel": 4 + }, + "construction_time_seconds": 420 + }, + "craft": { + "type": "hulls", + "subCategory": "misc", + "id": "hulls:misc:hulls_standard_structural_hull" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/superconductive_hull.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/superconductive_hull.json new file mode 100644 index 0000000..3e74b93 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/superconductive_hull.json @@ -0,0 +1,15 @@ +{ + "path": "hulls/superconductive_hull", + "recipe": { + "inputs": { + "advanced_hull_section": 18, + "superconductive_alloy": 12 + }, + "construction_time_seconds": 780 + }, + "craft": { + "type": "hulls", + "subCategory": "misc", + "id": "hulls:misc:hulls_superconductive_hull" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/titanium_aerospace_hull.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/titanium_aerospace_hull.json new file mode 100644 index 0000000..055d074 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/titanium_aerospace_hull.json @@ -0,0 +1,15 @@ +{ + "path": "hulls/titanium_aerospace_hull", + "recipe": { + "inputs": { + "advanced_hull_section": 20, + "carbon_titanium_composite": 6 + }, + "construction_time_seconds": 540 + }, + "craft": { + "type": "hulls", + "subCategory": "misc", + "id": "hulls:misc:hulls_titanium_aerospace_hull" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/voidsteel_hull.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/voidsteel_hull.json new file mode 100644 index 0000000..777fbc2 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/hulls/voidsteel_hull.json @@ -0,0 +1,18 @@ +{ + "path": "hulls/voidsteel_hull", + "recipe": { + "inputs": { + "exotic_hull_section": 8, + "voidsteel": 4 + }, + "output": { + "voidsteel_hull": 1 + }, + "construction_time_seconds": 1200 + }, + "craft": { + "type": "hulls", + "subCategory": "misc", + "id": "hulls:misc:hulls_voidsteel_hull" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/manifest.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/manifest.json new file mode 100644 index 0000000..1af0276 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/manifest.json @@ -0,0 +1,21 @@ +{ + "counts_by_group": { + "smelting": 10, + "alloys": 10, + "circuits": 10, + "hull_sections": 6, + "hulls": 8, + "weapons": 5, + "armours": 6, + "consumables": 4 + }, + "craftingTabs": { + "file": "data/gso/recipes/crafting_tabs.json", + "notes": [ + "Filter recipes by recipe.craft.type and recipe.craft.subCategory" + ] + }, + "notes": [ + "Recipes are grouped into data/gso/recipes//. Each file preserves {path, recipe, craft}." + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/bandage.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/bandage.json new file mode 100644 index 0000000..8652bed --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/bandage.json @@ -0,0 +1,18 @@ +{ + "path": "organics/bandage", + "recipe": { + "inputs": { + "plant_cloth": 1, + "resin": 1 + }, + "output": { + "bandage": 2 + }, + "craft_time_seconds": 10 + }, + "craft": { + "type": "organics", + "subCategory": "misc", + "id": "organics:misc:organics_bandage" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/bio_composite_material.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/bio_composite_material.json new file mode 100644 index 0000000..bee0947 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/bio_composite_material.json @@ -0,0 +1,19 @@ +{ + "path": "organics/bio_composite_material", + "recipe": { + "inputs": { + "resin": 1, + "plant_fiber": 1, + "carbon_ingot": 1 + }, + "output": { + "bio_composite_material": 1 + }, + "craft_time_seconds": 45 + }, + "craft": { + "type": "organics", + "subCategory": "chemical", + "id": "organics:chemical:organics_bio_composite_material" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/bio_pulp.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/bio_pulp.json new file mode 100644 index 0000000..f2a508f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/bio_pulp.json @@ -0,0 +1,17 @@ +{ + "path": "organics/bio_pulp", + "recipe": { + "inputs": { + "plant_matter": 3 + }, + "output": { + "bio_pulp": 1 + }, + "craft_time_seconds": 30 + }, + "craft": { + "type": "organics", + "subCategory": "bio", + "id": "organics:bio:organics_bio_pulp" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/medical_pack.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/medical_pack.json new file mode 100644 index 0000000..991d42e --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/medical_pack.json @@ -0,0 +1,18 @@ +{ + "path": "organics/medical_pack", + "recipe": { + "inputs": { + "bandage": 2, + "bio_pulp": 1 + }, + "output": { + "medical_pack": 1 + }, + "craft_time_seconds": 30 + }, + "craft": { + "type": "organics", + "subCategory": "misc", + "id": "organics:misc:organics_medical_pack" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/organic_insulation.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/organic_insulation.json new file mode 100644 index 0000000..8aadd4a --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/organic_insulation.json @@ -0,0 +1,18 @@ +{ + "path": "organics/organic_insulation", + "recipe": { + "inputs": { + "plant_cloth": 1, + "bio_pulp": 1 + }, + "output": { + "organic_insulation": 1 + }, + "craft_time_seconds": 35 + }, + "craft": { + "type": "organics", + "subCategory": "bio", + "id": "organics:bio:organics_organic_insulation" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/plant_cloth.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/plant_cloth.json new file mode 100644 index 0000000..1f0fecc --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/plant_cloth.json @@ -0,0 +1,17 @@ +{ + "path": "organics/plant_cloth", + "recipe": { + "inputs": { + "plant_fiber": 2 + }, + "output": { + "plant_cloth": 1 + }, + "craft_time_seconds": 20 + }, + "craft": { + "type": "organics", + "subCategory": "fabric", + "id": "organics:fabric:organics_plant_cloth" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/plant_fiber.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/plant_fiber.json new file mode 100644 index 0000000..8a6610c --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/plant_fiber.json @@ -0,0 +1,17 @@ +{ + "path": "organics/plant_fiber", + "recipe": { + "inputs": { + "plant_matter": 2 + }, + "output": { + "plant_fiber": 1 + }, + "harvest_time_seconds": 15 + }, + "craft": { + "type": "organics", + "subCategory": "fabric", + "id": "organics:fabric:organics_plant_fiber" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/processed_wood.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/processed_wood.json new file mode 100644 index 0000000..b81ac52 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/processed_wood.json @@ -0,0 +1,17 @@ +{ + "path": "organics/processed_wood", + "recipe": { + "inputs": { + "raw_wood": 2 + }, + "output": { + "processed_wood": 1 + }, + "craft_time_seconds": 20 + }, + "craft": { + "type": "organics", + "subCategory": "misc", + "id": "organics:misc:organics_processed_wood" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/raw_wood.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/raw_wood.json new file mode 100644 index 0000000..62bf685 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/raw_wood.json @@ -0,0 +1,17 @@ +{ + "path": "organics/raw_wood", + "recipe": { + "inputs": { + "tree": 1 + }, + "output": { + "raw_wood": 4 + }, + "harvest_time_seconds": 20 + }, + "craft": { + "type": "organics", + "subCategory": "misc", + "id": "organics:misc:organics_raw_wood" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/resin.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/resin.json new file mode 100644 index 0000000..780f01a --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/resin.json @@ -0,0 +1,17 @@ +{ + "path": "organics/resin", + "recipe": { + "inputs": { + "tree_sap": 2 + }, + "output": { + "resin": 1 + }, + "craft_time_seconds": 25 + }, + "craft": { + "type": "organics", + "subCategory": "chemical", + "id": "organics:chemical:organics_resin" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/tree_sap.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/tree_sap.json new file mode 100644 index 0000000..98614f2 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/tree_sap.json @@ -0,0 +1,17 @@ +{ + "path": "organics/tree_sap", + "recipe": { + "inputs": { + "tree": 1 + }, + "output": { + "tree_sap": 1 + }, + "harvest_time_seconds": 25 + }, + "craft": { + "type": "organics", + "subCategory": "misc", + "id": "organics:misc:organics_tree_sap" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/wood_planks.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/wood_planks.json new file mode 100644 index 0000000..c2181ea --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/wood_planks.json @@ -0,0 +1,17 @@ +{ + "path": "organics/wood_planks", + "recipe": { + "inputs": { + "processed_wood": 1 + }, + "output": { + "wood_planks": 4 + }, + "craft_time_seconds": 15 + }, + "craft": { + "type": "organics", + "subCategory": "wood", + "id": "organics:wood:organics_wood_planks" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/wooden_components.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/wooden_components.json new file mode 100644 index 0000000..7f73700 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/organics/wooden_components.json @@ -0,0 +1,18 @@ +{ + "path": "organics/wooden_components", + "recipe": { + "inputs": { + "wood_planks": 2, + "resin": 1 + }, + "output": { + "wooden_components": 1 + }, + "craft_time_seconds": 25 + }, + "craft": { + "type": "organics", + "subCategory": "misc", + "id": "organics:misc:organics_wooden_components" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/aluminum_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/aluminum_ingot.json new file mode 100644 index 0000000..9658e33 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/aluminum_ingot.json @@ -0,0 +1,17 @@ +{ + "path": "smelting/aluminum_ingot", + "recipe": { + "inputs": { + "bauxite_ore": 3 + }, + "output": { + "aluminum_ingot": 1 + }, + "smelt_time_seconds": 20 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_aluminum_ingot" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/carbon_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/carbon_ingot.json new file mode 100644 index 0000000..4d0f570 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/carbon_ingot.json @@ -0,0 +1,18 @@ +{ + "path": "smelting/carbon_ingot", + "recipe": { + "inputs": { + "iron_ore": 1, + "chromium_ore": 1 + }, + "output": { + "carbon_ingot": 1 + }, + "smelt_time_seconds": 20 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_carbon_ingot" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/chromium_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/chromium_ingot.json new file mode 100644 index 0000000..7e157d0 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/chromium_ingot.json @@ -0,0 +1,17 @@ +{ + "path": "smelting/chromium_ingot", + "recipe": { + "inputs": { + "chromium_ore": 2 + }, + "output": { + "chromium_ingot": 1 + }, + "smelt_time_seconds": 25 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_chromium_ingot" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/chronium_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/chronium_ingot.json new file mode 100644 index 0000000..83cbaa2 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/chronium_ingot.json @@ -0,0 +1,17 @@ +{ + "path": "smelting/chronium_ingot", + "recipe": { + "inputs": { + "chronite_ore": 3 + }, + "output": { + "chronium_ingot": 1 + }, + "smelt_time_seconds": 75 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_chronium_ingot" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/copper_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/copper_ingot.json new file mode 100644 index 0000000..99c24a7 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/copper_ingot.json @@ -0,0 +1,17 @@ +{ + "path": "smelting/copper_ingot", + "recipe": { + "inputs": { + "copper_ore": 2 + }, + "output": { + "copper_ingot": 1 + }, + "smelt_time_seconds": 15 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_copper_ingot" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/darksteel_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/darksteel_ingot.json new file mode 100644 index 0000000..955a00e --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/darksteel_ingot.json @@ -0,0 +1,18 @@ +{ + "path": "smelting/darksteel_ingot", + "recipe": { + "inputs": { + "dark_iron_ore": 2, + "iron_ore": 1 + }, + "output": { + "darksteel_ingot": 1 + }, + "smelt_time_seconds": 35 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_darksteel_ingot" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/gold_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/gold_ingot.json new file mode 100644 index 0000000..2084d24 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/gold_ingot.json @@ -0,0 +1,17 @@ +{ + "path": "smelting/gold_ingot", + "recipe": { + "inputs": { + "gold_ore": 2 + }, + "output": { + "gold_ingot": 1 + }, + "smelt_time_seconds": 20 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_gold_ingot" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/iron_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/iron_ingot.json new file mode 100644 index 0000000..3414468 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/iron_ingot.json @@ -0,0 +1,17 @@ +{ + "path": "smelting/iron_ingot", + "recipe": { + "inputs": { + "iron_ore": 2 + }, + "output": { + "iron_ingot": 1 + }, + "smelt_time_seconds": 15 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_iron_ingot" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/magnesium_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/magnesium_ingot.json new file mode 100644 index 0000000..87cc4cf --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/magnesium_ingot.json @@ -0,0 +1,17 @@ +{ + "path": "smelting/magnesium_ingot", + "recipe": { + "inputs": { + "magnesium_ore": 2 + }, + "output": { + "magnesium_ingot": 1 + }, + "smelt_time_seconds": 18 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_magnesium_ingot" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/neutronium_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/neutronium_ingot.json new file mode 100644 index 0000000..611a49b --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/neutronium_ingot.json @@ -0,0 +1,17 @@ +{ + "path": "smelting/neutronium_ingot", + "recipe": { + "inputs": { + "neutronium_shard": 3 + }, + "output": { + "neutronium_ingot": 1 + }, + "smelt_time_seconds": 120 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_neutronium_ingot" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/platinum_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/platinum_ingot.json new file mode 100644 index 0000000..822951e --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/platinum_ingot.json @@ -0,0 +1,17 @@ +{ + "path": "smelting/platinum_ingot", + "recipe": { + "inputs": { + "platinum_ore": 2 + }, + "output": { + "platinum_ingot": 1 + }, + "smelt_time_seconds": 40 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_platinum_ingot" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/silver_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/silver_ingot.json new file mode 100644 index 0000000..3f371d9 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/silver_ingot.json @@ -0,0 +1,17 @@ +{ + "path": "smelting/silver_ingot", + "recipe": { + "inputs": { + "silver_ore": 2 + }, + "output": { + "silver_ingot": 1 + }, + "smelt_time_seconds": 25 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_silver_ingot" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/titanium_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/titanium_ingot.json new file mode 100644 index 0000000..3b324ee --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/titanium_ingot.json @@ -0,0 +1,17 @@ +{ + "path": "smelting/titanium_ingot", + "recipe": { + "inputs": { + "titanium_ore": 2 + }, + "output": { + "titanium_ingot": 1 + }, + "smelt_time_seconds": 25 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_titanium_ingot" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/tungsten_ingot.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/tungsten_ingot.json new file mode 100644 index 0000000..9e81f54 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/smelting/tungsten_ingot.json @@ -0,0 +1,17 @@ +{ + "path": "smelting/tungsten_ingot", + "recipe": { + "inputs": { + "tungsten_ore": 2 + }, + "output": { + "tungsten_ingot": 1 + }, + "smelt_time_seconds": 30 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_tungsten_ingot" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/body_exotic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/body_exotic.json new file mode 100644 index 0000000..91704d1 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/body_exotic.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/body_suits/body_exotic", + "recipe": { + "inputs": { + "phase_alloy": 4, + "dimensional_alloy": 2, + "sentient_core_circuit": 1 + }, + "output": { + "body_exotic": 1 + }, + "craft_time_seconds": 220 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_body_suits_body_exotic" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/body_light.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/body_light.json new file mode 100644 index 0000000..d179e1e --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/body_light.json @@ -0,0 +1,18 @@ +{ + "path": "spacesuit_parts/body_suits/body_light", + "recipe": { + "inputs": { + "aluminum_alloy": 3, + "organic_insulation": 2 + }, + "output": { + "body_light": 1 + }, + "craft_time_seconds": 90 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_body_suits_body_light" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/body_military.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/body_military.json new file mode 100644 index 0000000..2b56e1f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/body_military.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/body_suits/body_military", + "recipe": { + "inputs": { + "darksteel_ingot": 4, + "voidsteel": 2, + "shield_control_circuit": 1 + }, + "output": { + "body_military": 1 + }, + "craft_time_seconds": 160 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_body_suits_body_military" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/body_standard.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/body_standard.json new file mode 100644 index 0000000..dbb20ac --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/body_standard.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/body_suits/body_standard", + "recipe": { + "inputs": { + "titanium_alloy": 4, + "carbon_nanotube_alloy": 2, + "organic_insulation": 2 + }, + "output": { + "body_standard": 1 + }, + "craft_time_seconds": 120 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_body_suits_body_standard" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/boots_light.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/boots_light.json new file mode 100644 index 0000000..38589e8 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/boots_light.json @@ -0,0 +1,18 @@ +{ + "path": "spacesuit_parts/boots/boots_light", + "recipe": { + "inputs": { + "aluminum_alloy": 1, + "rubber_polymer": 1 + }, + "output": { + "boots_light": 1 + }, + "craft_time_seconds": 40 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_boots_boots_light" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/boots_magnetic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/boots_magnetic.json new file mode 100644 index 0000000..e721e8f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/boots_magnetic.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/boots/boots_magnetic", + "recipe": { + "inputs": { + "tungsten_alloy": 1, + "magnetic_coil": 1, + "control_circuit": 1 + }, + "output": { + "boots_magnetic": 1 + }, + "craft_time_seconds": 70 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_boots_boots_magnetic" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/boots_military.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/boots_military.json new file mode 100644 index 0000000..83229a2 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/boots_military.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/boots/boots_military", + "recipe": { + "inputs": { + "darksteel_ingot": 1, + "servo_alloy": 1, + "weapon_control_circuit": 1 + }, + "output": { + "boots_military": 1 + }, + "craft_time_seconds": 90 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_boots_boots_military" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/boots_standard.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/boots_standard.json new file mode 100644 index 0000000..b8b4195 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/boots_standard.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/boots/boots_standard", + "recipe": { + "inputs": { + "titanium_alloy": 1, + "bio_composite_material": 1, + "control_circuit": 1 + }, + "output": { + "boots_standard": 1 + }, + "craft_time_seconds": 55 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_boots_boots_standard" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/gloves_industrial.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/gloves_industrial.json new file mode 100644 index 0000000..583d793 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/gloves_industrial.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/gloves/gloves_industrial", + "recipe": { + "inputs": { + "titanium_alloy": 1, + "bio_composite_material": 1, + "control_circuit": 1 + }, + "output": { + "gloves_industrial": 1 + }, + "craft_time_seconds": 60 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_gloves_gloves_industrial" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/gloves_light.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/gloves_light.json new file mode 100644 index 0000000..5123544 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/gloves_light.json @@ -0,0 +1,18 @@ +{ + "path": "spacesuit_parts/gloves/gloves_light", + "recipe": { + "inputs": { + "flex_alloy": 1, + "plant_cloth": 1 + }, + "output": { + "gloves_light": 1 + }, + "craft_time_seconds": 30 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_gloves_gloves_light" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/gloves_military.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/gloves_military.json new file mode 100644 index 0000000..f240b34 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/gloves_military.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/gloves/gloves_military", + "recipe": { + "inputs": { + "darksteel_ingot": 1, + "servo_alloy": 1, + "weapon_control_circuit": 1 + }, + "output": { + "gloves_military": 1 + }, + "craft_time_seconds": 80 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_gloves_gloves_military" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/gloves_standard.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/gloves_standard.json new file mode 100644 index 0000000..6d873f6 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/gloves_standard.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/gloves/gloves_standard", + "recipe": { + "inputs": { + "flex_alloy": 1, + "bio_composite_material": 1, + "basic_circuit": 1 + }, + "output": { + "gloves_standard": 1 + }, + "craft_time_seconds": 45 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_gloves_gloves_standard" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/helmet_exotic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/helmet_exotic.json new file mode 100644 index 0000000..7e8049d --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/helmet_exotic.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/helmets/helmet_exotic", + "recipe": { + "inputs": { + "phase_alloy": 2, + "crystal_glass": 1, + "quantum_circuit": 1 + }, + "output": { + "helmet_exotic": 1 + }, + "craft_time_seconds": 120 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_helmets_helmet_exotic" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/helmet_light.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/helmet_light.json new file mode 100644 index 0000000..63f7bff --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/helmet_light.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/helmets/helmet_light", + "recipe": { + "inputs": { + "aluminum_alloy": 2, + "glass_composite": 1, + "basic_circuit": 1 + }, + "output": { + "helmet_light": 1 + }, + "craft_time_seconds": 45 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_helmets_helmet_light" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/helmet_military.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/helmet_military.json new file mode 100644 index 0000000..0f9ed82 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/helmet_military.json @@ -0,0 +1,20 @@ +{ + "path": "spacesuit_parts/helmets/helmet_military", + "recipe": { + "inputs": { + "darksteel_ingot": 2, + "transparent_alloy": 1, + "sensor_array_circuit": 1, + "shield_control_circuit": 1 + }, + "output": { + "helmet_military": 1 + }, + "craft_time_seconds": 90 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_helmets_helmet_military" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/helmet_standard.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/helmet_standard.json new file mode 100644 index 0000000..78de778 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/helmet_standard.json @@ -0,0 +1,20 @@ +{ + "path": "spacesuit_parts/helmets/helmet_standard", + "recipe": { + "inputs": { + "titanium_alloy": 2, + "glass_composite": 1, + "sensor_array_circuit": 1, + "organic_insulation": 1 + }, + "output": { + "helmet_standard": 1 + }, + "craft_time_seconds": 60 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_helmets_helmet_standard" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/life_support_basic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/life_support_basic.json new file mode 100644 index 0000000..af74776 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/life_support_basic.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/life_support_backpacks/life_support_basic", + "recipe": { + "inputs": { + "aluminum_alloy": 2, + "power_circuit": 1, + "bio_pulp": 1 + }, + "output": { + "life_support_basic": 1 + }, + "craft_time_seconds": 100 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_life_support_backpacks_life_support_basic" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/life_support_exotic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/life_support_exotic.json new file mode 100644 index 0000000..e09dad4 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/life_support_exotic.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/life_support_backpacks/life_support_exotic", + "recipe": { + "inputs": { + "phase_alloy": 3, + "quantum_circuit": 1, + "bio_composite_material": 2 + }, + "output": { + "life_support_exotic": 1 + }, + "craft_time_seconds": 260 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_life_support_backpacks_life_support_exotic" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/life_support_military.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/life_support_military.json new file mode 100644 index 0000000..f2c1672 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/life_support_military.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/life_support_backpacks/life_support_military", + "recipe": { + "inputs": { + "darksteel_ingot": 3, + "reactor_control_circuit": 1, + "organic_insulation": 2 + }, + "output": { + "life_support_military": 1 + }, + "craft_time_seconds": 200 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_life_support_backpacks_life_support_military" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/life_support_standard.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/life_support_standard.json new file mode 100644 index 0000000..3b8fbef --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/life_support_standard.json @@ -0,0 +1,20 @@ +{ + "path": "spacesuit_parts/life_support_backpacks/life_support_standard", + "recipe": { + "inputs": { + "titanium_alloy": 3, + "power_circuit": 1, + "control_circuit": 1, + "organic_insulation": 1 + }, + "output": { + "life_support_standard": 1 + }, + "craft_time_seconds": 150 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_life_support_backpacks_life_support_standard" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/pants_exotic.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/pants_exotic.json new file mode 100644 index 0000000..a22b886 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/pants_exotic.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/pants/pants_exotic", + "recipe": { + "inputs": { + "phase_alloy": 2, + "bio_composite_material": 1, + "dimensional_logic_circuit": 1 + }, + "output": { + "pants_exotic": 1 + }, + "craft_time_seconds": 120 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_pants_pants_exotic" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/pants_heavy.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/pants_heavy.json new file mode 100644 index 0000000..7bb61a9 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/pants_heavy.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/pants/pants_heavy", + "recipe": { + "inputs": { + "tungsten_alloy": 2, + "carbon_nanotube_alloy": 1, + "organic_insulation": 1 + }, + "output": { + "pants_heavy": 1 + }, + "craft_time_seconds": 90 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_pants_pants_heavy" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/pants_light.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/pants_light.json new file mode 100644 index 0000000..48072ea --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/pants_light.json @@ -0,0 +1,18 @@ +{ + "path": "spacesuit_parts/pants/pants_light", + "recipe": { + "inputs": { + "aluminum_alloy": 2, + "organic_insulation": 1 + }, + "output": { + "pants_light": 1 + }, + "craft_time_seconds": 50 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_pants_pants_light" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/pants_standard.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/pants_standard.json new file mode 100644 index 0000000..52b5c94 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/spacesuit_parts/pants_standard.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/pants/pants_standard", + "recipe": { + "inputs": { + "titanium_alloy": 2, + "bio_composite_material": 1, + "organic_insulation": 1 + }, + "output": { + "pants_standard": 1 + }, + "craft_time_seconds": 70 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_pants_pants_standard" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/weapons/assault_rifle.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/weapons/assault_rifle.json new file mode 100644 index 0000000..bb7783d --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/weapons/assault_rifle.json @@ -0,0 +1,19 @@ +{ + "path": "weapons/assault_rifle", + "recipe": { + "inputs": { + "reinforced_steel": 4, + "weapon_control_circuit": 1, + "power_circuit": 1 + }, + "output": { + "assault_rifle_common": 1 + }, + "craft_time_seconds": 90 + }, + "craft": { + "type": "weapons", + "subCategory": "rifle", + "id": "weapons:rifle:weapons_assault_rifle" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/weapons/plasma_cutter.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/weapons/plasma_cutter.json new file mode 100644 index 0000000..dcd854b --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/weapons/plasma_cutter.json @@ -0,0 +1,19 @@ +{ + "path": "weapons/plasma_cutter", + "recipe": { + "inputs": { + "flux_core_alloy": 3, + "weapon_control_circuit": 1, + "flux_crystal": 2 + }, + "output": { + "plasma_cutter_rare": 1 + }, + "craft_time_seconds": 180 + }, + "craft": { + "type": "weapons", + "subCategory": "special", + "id": "weapons:special:weapons_plasma_cutter" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/weapons/sniper_rifle.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/weapons/sniper_rifle.json new file mode 100644 index 0000000..1f60c90 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/weapons/sniper_rifle.json @@ -0,0 +1,19 @@ +{ + "path": "weapons/sniper_rifle", + "recipe": { + "inputs": { + "titanium_alloy": 3, + "processor_circuit": 1, + "magnetic_coil": 2 + }, + "output": { + "sniper_common": 1 + }, + "craft_time_seconds": 120 + }, + "craft": { + "type": "weapons", + "subCategory": "sniper", + "id": "weapons:sniper:weapons_sniper_rifle" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/weapons/standard_pistol.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/weapons/standard_pistol.json new file mode 100644 index 0000000..e9d8244 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/weapons/standard_pistol.json @@ -0,0 +1,18 @@ +{ + "path": "weapons/standard_pistol", + "recipe": { + "inputs": { + "steel_alloy": 3, + "control_circuit": 1 + }, + "output": { + "pistol_standard_common": 1 + }, + "craft_time_seconds": 45 + }, + "craft": { + "type": "weapons", + "subCategory": "pistol", + "id": "weapons:pistol:weapons_standard_pistol" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/weapons/starter_blaster.json b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/weapons/starter_blaster.json new file mode 100644 index 0000000..0c4a340 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/recipes/weapons/starter_blaster.json @@ -0,0 +1,18 @@ +{ + "path": "weapons/starter_blaster", + "recipe": { + "inputs": { + "iron_ingot": 5, + "basic_circuit": 1 + }, + "output": { + "starter_blaster": 1 + }, + "craft_time_seconds": 30 + }, + "craft": { + "type": "weapons", + "subCategory": "pistol", + "id": "weapons:pistol:weapons_starter_blaster" + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/skills/combat.json b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/combat.json new file mode 100644 index 0000000..fdc4eb5 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/combat.json @@ -0,0 +1,48 @@ +[ + { + "id": "weapons_mastery", + "name": "Weapons Mastery", + "description": "Increases damage and accuracy with all weapons", + "category": "combat", + "maxLevel": 100, + "experiencePerLevel": 1000, + "defaultUnlocked": true, + "icon": "fa-crosshairs", + "bonuses": { "damage": 2, "accuracy": 1, "criticalChance": 0.5 } + }, + { + "id": "defense_training", + "name": "Defense Training", + "description": "Improves damage reduction and resistance", + "category": "combat", + "maxLevel": 100, + "experiencePerLevel": 1000, + "defaultUnlocked": true, + "icon": "fa-shield-alt", + "bonuses": { "damageReduction": 1.5, "resistance": 1, "health": 5 } + }, + { + "id": "speed_reflexes", + "name": "Speed & Reflexes", + "description": "Enhances movement speed and reaction time", + "category": "combat", + "maxLevel": 100, + "experiencePerLevel": 1000, + "defaultUnlocked": false, + "requiredLevel": 5, + "icon": "fa-bolt", + "bonuses": { "speed": 2, "dodgeChance": 0.8, "initiative": 1 } + }, + { + "id": "tactical_analysis", + "name": "Tactical Analysis", + "description": "Reveals enemy weaknesses and improves accuracy", + "category": "combat", + "maxLevel": 100, + "experiencePerLevel": 1200, + "defaultUnlocked": false, + "requiredLevel": 10, + "icon": "fa-brain", + "bonuses": { "criticalDamage": 0.05, "attack": 1 } + } +] diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/skills/combat/defense_training.json b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/combat/defense_training.json new file mode 100644 index 0000000..73e1a96 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/combat/defense_training.json @@ -0,0 +1,15 @@ +{ + "id": "defense_training", + "name": "Defense Training", + "category": "combat", + "description": "Improves damage reduction and resistance", + "maxLevel": 100, + "experiencePerLevel": 1000, + "defaultUnlocked": true, + "icon": "fa-shield-alt", + "bonuses": { + "damageReduction": 1.5, + "resistance": 1, + "health": 5 + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/skills/combat/piloting.json b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/combat/piloting.json new file mode 100644 index 0000000..ef4a60f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/combat/piloting.json @@ -0,0 +1,15 @@ +{ + "id": "piloting", + "name": "Piloting", + "category": "combat", + "description": "Advanced ship handling and evasive manoeuvres", + "maxLevel": 100, + "experiencePerLevel": 1000, + "defaultUnlocked": true, + "icon": "fa-rocket", + "bonuses": { + "speed": 2, + "criticalChance": 0.005, + "dodgeChance": 0.5 + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/skills/combat/speed_reflexes.json b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/combat/speed_reflexes.json new file mode 100644 index 0000000..262cd64 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/combat/speed_reflexes.json @@ -0,0 +1,15 @@ +{ + "id": "speed_reflexes", + "name": "Speed & Reflexes", + "category": "combat", + "description": "Enhances movement speed and reaction time", + "maxLevel": 100, + "experiencePerLevel": 1000, + "defaultUnlocked": true, + "icon": "fa-bolt", + "bonuses": { + "speed": 2, + "dodgeChance": 0.8, + "initiative": 1 + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/skills/combat/tactical_analysis.json b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/combat/tactical_analysis.json new file mode 100644 index 0000000..2cc04fd --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/combat/tactical_analysis.json @@ -0,0 +1,15 @@ +{ + "id": "tactical_analysis", + "name": "Tactical Analysis", + "category": "combat", + "description": "Reveals enemy weaknesses and improves critical strikes", + "maxLevel": 100, + "experiencePerLevel": 1200, + "defaultUnlocked": false, + "requiredLevel": 5, + "icon": "fa-brain", + "bonuses": { + "criticalDamage": 0.05, + "attack": 1 + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/skills/combat/weapons_mastery.json b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/combat/weapons_mastery.json new file mode 100644 index 0000000..22ae3ef --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/combat/weapons_mastery.json @@ -0,0 +1,15 @@ +{ + "id": "weapons_mastery", + "name": "Weapons Mastery", + "category": "combat", + "description": "Increases damage and accuracy with all weapons", + "maxLevel": 100, + "experiencePerLevel": 1000, + "defaultUnlocked": true, + "icon": "fa-crosshairs", + "bonuses": { + "damage": 2, + "accuracy": 1, + "criticalChance": 0.5 + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/skills/crafting.json b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/crafting.json new file mode 100644 index 0000000..5e60722 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/crafting.json @@ -0,0 +1,48 @@ +[ + { + "id": "weapons_crafting", + "name": "Weapons Crafting", + "description": "Ability to craft and improve weapons", + "category": "crafting", + "maxLevel": 100, + "experiencePerLevel": 800, + "defaultUnlocked": true, + "icon": "fa-hammer", + "bonuses": { "craftingSpeed": 2, "craftingSuccess": 1, "rareChance": 0.5 } + }, + { + "id": "armor_crafting", + "name": "Armor Crafting", + "description": "Ability to craft and improve armor", + "category": "crafting", + "maxLevel": 100, + "experiencePerLevel": 800, + "defaultUnlocked": true, + "icon": "fa-hard-hat", + "bonuses": { "craftingSpeed": 2, "durabilityBonus": 3, "rareChance": 0.5 } + }, + { + "id": "resource_extraction", + "name": "Resource Extraction", + "description": "Better resource gathering and efficiency", + "category": "crafting", + "maxLevel": 100, + "experiencePerLevel": 700, + "defaultUnlocked": false, + "requiredLevel": 4, + "icon": "fa-gem", + "bonuses": { "resourceBonus": 0.15, "findResources": 0.1 } + }, + { + "id": "engineering", + "name": "Engineering", + "description": "Advanced ship modifications and systems", + "category": "crafting", + "maxLevel": 100, + "experiencePerLevel": 1000, + "defaultUnlocked": false, + "requiredLevel": 7, + "icon": "fa-cogs", + "bonuses": { "shipUpgrades": 0.2, "systemEfficiency": 0.1 } + } +] diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/skills/crafting/armor_crafting.json b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/crafting/armor_crafting.json new file mode 100644 index 0000000..823d7e0 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/crafting/armor_crafting.json @@ -0,0 +1,15 @@ +{ + "id": "armor_crafting", + "name": "Armor Crafting", + "category": "crafting", + "description": "Forge protective armour plating and shields", + "maxLevel": 100, + "experiencePerLevel": 800, + "defaultUnlocked": true, + "icon": "fa-shield-halved", + "bonuses": { + "craftingBonus": 0.1, + "armorStats": 0.05, + "durabilityBonus": 3 + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/skills/crafting/general_crafting.json b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/crafting/general_crafting.json new file mode 100644 index 0000000..18cd89f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/crafting/general_crafting.json @@ -0,0 +1,14 @@ +{ + "id": "general_crafting", + "name": "General Crafting", + "category": "crafting", + "description": "Foundation crafting skills for all item types", + "maxLevel": 100, + "experiencePerLevel": 800, + "defaultUnlocked": true, + "icon": "fa-hammer", + "bonuses": { + "craftingBonus": 0.05, + "craftingSpeed": 1 + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/skills/crafting/resource_extraction.json b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/crafting/resource_extraction.json new file mode 100644 index 0000000..a8a5f8e --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/crafting/resource_extraction.json @@ -0,0 +1,15 @@ +{ + "id": "resource_extraction", + "name": "Resource Extraction", + "category": "crafting", + "description": "Improved ore yields and mining efficiency", + "maxLevel": 100, + "experiencePerLevel": 700, + "defaultUnlocked": false, + "requiredLevel": 4, + "icon": "fa-gem", + "bonuses": { + "resourceBonus": 0.15, + "findResources": 0.1 + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/skills/crafting/smelting_mastery.json b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/crafting/smelting_mastery.json new file mode 100644 index 0000000..0b66184 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/crafting/smelting_mastery.json @@ -0,0 +1,15 @@ +{ + "id": "smelting_mastery", + "name": "Smelting Mastery", + "category": "crafting", + "description": "Faster smelting and higher-quality ingot output", + "maxLevel": 100, + "experiencePerLevel": 800, + "defaultUnlocked": false, + "requiredLevel": 7, + "icon": "fa-fire", + "bonuses": { + "smeltingSpeed": 2, + "ingotBonus": 0.1 + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/skills/crafting/weapons_crafting.json b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/crafting/weapons_crafting.json new file mode 100644 index 0000000..b5515a6 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/crafting/weapons_crafting.json @@ -0,0 +1,15 @@ +{ + "id": "weapons_crafting", + "name": "Weapons Crafting", + "category": "crafting", + "description": "Craft and upgrade ship weapons systems", + "maxLevel": 100, + "experiencePerLevel": 800, + "defaultUnlocked": true, + "icon": "fa-gun", + "bonuses": { + "craftingBonus": 0.1, + "weaponStats": 0.05, + "rareChance": 0.5 + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/skills/manifest.json b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/manifest.json new file mode 100644 index 0000000..5401aaf --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/manifest.json @@ -0,0 +1,10 @@ +{ + "counts_by_category": { + "combat": 5, + "science": 5, + "crafting": 5 + }, + "notes": [ + "One JSON per skill. Grouped into data/gso/skills//.json" + ] +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/skills/science.json b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/science.json new file mode 100644 index 0000000..237f554 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/science.json @@ -0,0 +1,49 @@ +[ + { + "id": "energy_manipulation", + "name": "Energy Manipulation", + "description": "Better energy control and regeneration", + "category": "science", + "maxLevel": 100, + "experiencePerLevel": 1000, + "defaultUnlocked": true, + "icon": "fa-bolt", + "bonuses": { "maxEnergy": 10, "energyRegeneration": 0.1 } + }, + { + "id": "alien_technology", + "name": "Alien Technology", + "description": "Understanding and using alien artifacts", + "category": "science", + "maxLevel": 100, + "experiencePerLevel": 1200, + "defaultUnlocked": false, + "requiredLevel": 3, + "icon": "fa-atom", + "bonuses": { "findRarity": 0.05, "itemValue": 0.1 } + }, + { + "id": "quantum_physics", + "name": "Quantum Physics", + "description": "Advanced quantum mechanics for better equipment", + "category": "science", + "maxLevel": 100, + "experiencePerLevel": 1500, + "defaultUnlocked": false, + "requiredLevel": 8, + "icon": "fa-microscope", + "bonuses": { "criticalDamage": 0.1, "attack": 3 } + }, + { + "id": "bio_engineering", + "name": "Bio-Engineering", + "description": "Biological enhancements and healing", + "category": "science", + "maxLevel": 100, + "experiencePerLevel": 1100, + "defaultUnlocked": false, + "requiredLevel": 6, + "icon": "fa-dna", + "bonuses": { "maxHealth": 15, "healthRegeneration": 0.05 } + } +] diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/skills/science/alien_technology.json b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/science/alien_technology.json new file mode 100644 index 0000000..68d7378 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/science/alien_technology.json @@ -0,0 +1,15 @@ +{ + "id": "alien_technology", + "name": "Alien Technology", + "category": "science", + "description": "Understanding and reverse-engineering alien artefacts", + "maxLevel": 100, + "experiencePerLevel": 1500, + "defaultUnlocked": false, + "requiredLevel": 3, + "icon": "fa-atom", + "bonuses": { + "findRarity": 0.05, + "itemValue": 0.1 + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/skills/science/bio_engineering.json b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/science/bio_engineering.json new file mode 100644 index 0000000..9807acc --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/science/bio_engineering.json @@ -0,0 +1,15 @@ +{ + "id": "bio_engineering", + "name": "Bio-Engineering", + "category": "science", + "description": "Biological enhancements and accelerated healing", + "maxLevel": 100, + "experiencePerLevel": 1500, + "defaultUnlocked": false, + "requiredLevel": 6, + "icon": "fa-dna", + "bonuses": { + "maxHealth": 15, + "healthRegeneration": 0.05 + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/skills/science/energy_manipulation.json b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/science/energy_manipulation.json new file mode 100644 index 0000000..5c24024 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/science/energy_manipulation.json @@ -0,0 +1,14 @@ +{ + "id": "energy_manipulation", + "name": "Energy Manipulation", + "category": "science", + "description": "Better energy control and faster regeneration", + "maxLevel": 100, + "experiencePerLevel": 1000, + "defaultUnlocked": true, + "icon": "fa-bolt", + "bonuses": { + "maxEnergy": 10, + "energyRegeneration": 0.1 + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/skills/science/engineering.json b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/science/engineering.json new file mode 100644 index 0000000..b26331b --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/science/engineering.json @@ -0,0 +1,14 @@ +{ + "id": "engineering", + "name": "Engineering", + "category": "science", + "description": "Technical skills for ship components and machinery", + "maxLevel": 100, + "experiencePerLevel": 1000, + "defaultUnlocked": true, + "icon": "fa-wrench", + "bonuses": { + "craftingBonus": 0.08, + "shipStats": 0.03 + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/gso/skills/science/quantum_physics.json b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/science/quantum_physics.json new file mode 100644 index 0000000..256441a --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/data/gso/skills/science/quantum_physics.json @@ -0,0 +1,15 @@ +{ + "id": "quantum_physics", + "name": "Quantum Physics", + "category": "science", + "description": "Advanced quantum mechanics applied to equipment", + "maxLevel": 100, + "experiencePerLevel": 2000, + "defaultUnlocked": false, + "requiredLevel": 8, + "icon": "fa-microscope", + "bonuses": { + "criticalDamage": 0.1, + "attack": 3 + } +} \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/GameServer/data/mods.db b/Galaxy-Strike-Online-main/GameServer/data/mods.db new file mode 100644 index 0000000000000000000000000000000000000000..48ea0169e6211b5976ee6c0cd9941e9c8fff94c7 GIT binary patch literal 40960 zcmeI&J#X7a7{GBdaU@HDkSvC_<~-R#oB&29BTH17HdM=!Ls3XQ5iEJGiRdjPl{Q0% z#z3;>LuBZ;>DMVb^+<-I=r^QAfC9vy040+5Lf-G5J4rYVU-tY+inCcTaU$_dd#LHU z_Cg3v)7I2pS9`X_=kcCxdUkNj`y|5_IT!a_AKyZP&})3OrvcX!fJot zGej{>Y}dt?&G$sNZyAS%DNfApaoao;G3bkZqi3jQblSsCd*8_K zH0pO>j*P7MmnUpJuT`3Zmah3zPyQOd8LKaowzG(4$+=xruy>2+_1|Ars?EJU{e3sl z4Q23F21%iLAkSnVr>+dIzgjJ5U!P}1{k422EaOLO)jV(7TW7qGY5G;X#oTiu>DlV* z@xJO`t9xuHql5OSXNk_pRD_ltUmRNPba7hxhkKgD}Couj`$Wo3{|YgY0K)|Kz@Bk<(>cS2gi=cak67IxU$%I zcCXSr+`K6^^1e%lOHx%>Ik{VDK7Op9rUNA%XQ5QA@enDVt`%b8QsVa1naCYgvUOqV z)#k>A{;`@xRUA*@okHpO%72cb_*TW96X_(9rIJH}L0C zRzY4)PUG1+pJ}u4x|?V5r(xubRi>{S*m=Q!H6P@~&it{o=T3BfQ)}FF>AnsZlO#Pa z=gGNLbL-UgWq2vcolVdDpOcGOY;E1CG@ozW5?f&sRpDLrak?IVq&DK0hw%#=0tg_0 z00IagfB*srAbWgQe_6Yib|HWO0tg_000IagfB*srAdm>K{!btvfB*sr zAbfB*srAb { + const fullPath = path.join(__dirname, dir); + if (!fs.existsSync(fullPath)) { + fs.mkdirSync(fullPath, { recursive: true }); + console.log(`Created directory: ${dir}`); + } +}); + +// Generate simple SVG placeholder +function generateSVG(width, height, color, text, filename) { + const svg = ` + + + + ${text} + +`; + + return svg; +} + +// Ships - Blue theme +const ships = [ + 'starter_cruiser_common', + 'starter_cruiser_uncommon', + 'starter_cruiser_rare', + 'advanced_fighter_common', + 'advanced_fighter_uncommon', + 'advanced_fighter_rare', + 'heavy_battleship_common', + 'heavy_battleship_uncommon', + 'heavy_battleship_rare', + 'stealth_ship_common', + 'stealth_ship_uncommon', + 'stealth_ship_rare' +]; + +// Weapons - Red theme +const weapons = [ + 'laser_cannon_common', + 'laser_cannon_uncommon', + 'laser_cannon_rare', + 'plasma_rifle_common', + 'plasma_rifle_uncommon', + 'plasma_rifle_rare', + 'missile_launcher_common', + 'missile_launcher_uncommon', + 'missile_launcher_rare', + 'railgun_common', + 'railgun_uncommon', + 'railgun_rare' +]; + +// Armors - Green theme +const armors = [ + 'light_armor_common', + 'light_armor_uncommon', + 'light_armor_rare', + 'medium_armor_common', + 'medium_armor_uncommon', + 'medium_armor_rare', + 'heavy_armor_common', + 'heavy_armor_uncommon', + 'heavy_armor_rare', + 'energy_shield_common', + 'energy_shield_uncommon', + 'energy_shield_rare' +]; + +// Materials - Gray theme +const materials = [ + 'metal_scraps', + 'energy_crystal', + 'plastic_parts', + 'electronic_components', + 'rare_earth_metal', + 'quantum_core', + 'nanomaterials', + 'dark_matter_fragment', + 'solar_panel', + 'cooling_system', + 'power_cell', + 'hull_plating' +]; + +// Consumables - Purple theme +const consumables = [ + 'health_pack', + 'energy_boost', + 'shield_recharge', + 'speed_boost', + 'repair_kit', + 'ammo_pack', + 'stealth_device', + 'scanner_boost', + 'experience_boost', + 'credit_multiplier', + 'lucky_charm', + 'emergency_beacon' +]; + +// Cosmetics - Gold theme +const cosmetics = [ + 'cool_paint_job', + 'neon_lights', + 'custom_decal', + 'golden_trim', + 'carbon_fiber', + 'chrome_finish', + 'matte_black', + 'camo_pattern', + 'flame_design', + 'electric_aura', + 'ice_effect', + 'rainbow_sparkle' +]; + +// Generate all images +console.log('Generating ship images...'); +ships.forEach(shipId => { + const rarity = shipId.split('_').pop(); + const color = rarity === 'common' ? '#3498db' : rarity === 'uncommon' ? '#2ecc71' : '#f39c12'; + const svg = generateSVG(80, 80, color, shipId.replace(/_/g, ' ').toUpperCase(), shipId); + fs.writeFileSync(path.join(__dirname, `assets/images/ships/${shipId}.png`), svg); + console.log(`Created: ${shipId}.png`); +}); + +console.log('Generating weapon images...'); +weapons.forEach(weaponId => { + const rarity = weaponId.split('_').pop(); + const color = rarity === 'common' ? '#e74c3c' : rarity === 'uncommon' ? '#c0392b' : '#8e44ad'; + const svg = generateSVG(64, 64, color, weaponId.replace(/_/g, ' ').toUpperCase(), weaponId); + fs.writeFileSync(path.join(__dirname, `assets/images/weapons/${weaponId}.png`), svg); + console.log(`Created: ${weaponId}.png`); +}); + +console.log('Generating armor images...'); +armors.forEach(armorId => { + const rarity = armorId.split('_').pop(); + const color = rarity === 'common' ? '#27ae60' : rarity === 'uncommon' ? '#16a085' : '#2c3e50'; + const svg = generateSVG(64, 64, color, armorId.replace(/_/g, ' ').toUpperCase(), armorId); + fs.writeFileSync(path.join(__dirname, `assets/images/armors/${armorId}.png`), svg); + console.log(`Created: ${armorId}.png`); +}); + +console.log('Generating material images...'); +materials.forEach(materialId => { + const svg = generateSVG(48, 48, '#7f8c8d', materialId.replace(/_/g, ' ').toUpperCase(), materialId); + fs.writeFileSync(path.join(__dirname, `assets/images/items/materials/${materialId}.png`), svg); + console.log(`Created: ${materialId}.png`); +}); + +console.log('Generating consumable images...'); +consumables.forEach(consumableId => { + const svg = generateSVG(48, 48, '#9b59b6', consumableId.replace(/_/g, ' ').toUpperCase(), consumableId); + fs.writeFileSync(path.join(__dirname, `assets/images/items/consumables/${consumableId}.png`), svg); + console.log(`Created: ${consumableId}.png`); +}); + +console.log('Generating cosmetic images...'); +cosmetics.forEach(cosmeticId => { + const svg = generateSVG(64, 64, '#f1c40f', cosmeticId.replace(/_/g, ' ').toUpperCase(), cosmeticId); + fs.writeFileSync(path.join(__dirname, `assets/images/items/cosmetics/${cosmeticId}.png`), svg); + console.log(`Created: ${cosmeticId}.png`); +}); + +// Generate UI placeholder +console.log('Generating UI placeholder...'); +const placeholderSvg = generateSVG(80, 80, '#95a5a6', 'PLACEHOLDER', 'placeholder'); +fs.writeFileSync(path.join(__dirname, 'assets/images/ui/placeholder.png'), placeholderSvg); + +// Generate some UI icons +const icons = ['coin', 'gem', 'heart', 'shield', 'star', 'lock', 'unlock', 'settings']; +icons.forEach(iconId => { + const svg = generateSVG(32, 32, '#34495e', iconId.toUpperCase(), iconId); + fs.writeFileSync(path.join(__dirname, `assets/images/ui/icons/${iconId}.png`), svg); + console.log(`Created icon: ${iconId}.png`); +}); + +console.log('\n🎉 All placeholder images generated successfully!'); +console.log('📁 Total images created:'); +console.log(` Ships: ${ships.length}`); +console.log(` Weapons: ${weapons.length}`); +console.log(` Armors: ${armors.length}`); +console.log(` Materials: ${materials.length}`); +console.log(` Consumables: ${consumables.length}`); +console.log(` Cosmetics: ${cosmetics.length}`); +console.log(` UI Icons: ${icons.length + 1} (including placeholder)`); +console.log('\n🚀 Your shop should now display all items with placeholder images!'); diff --git a/Galaxy-Strike-Online-main/GameServer/middleware/errorHandler.js b/Galaxy-Strike-Online-main/GameServer/middleware/errorHandler.js new file mode 100644 index 0000000..1de2fd0 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/middleware/errorHandler.js @@ -0,0 +1,40 @@ +const logger = require('../utils/logger'); + +const errorHandler = (err, req, res, next) => { + let error = { ...err }; + error.message = err.message; + + // Log error + logger.error(err); + + // Mongoose bad ObjectId + if (err.name === 'CastError') { + const message = 'Resource not found'; + error = { message, statusCode: 404 }; + } + + // Mongoose duplicate key + if (err.code === 11000) { + const message = 'Duplicate field value entered'; + error = { message, statusCode: 400 }; + } + + // Mongoose validation error + if (err.name === 'ValidationError') { + const message = Object.values(err.errors).map(val => val.message); + error = { message, statusCode: 400 }; + } + + res.status(error.statusCode || 500).json({ + success: false, + error: error.message || 'Server Error' + }); +}; + +const notFound = (req, res, next) => { + const error = new Error(`Not found - ${req.originalUrl}`); + res.status(404); + next(error); +}; + +module.exports = { errorHandler, notFound }; diff --git a/Galaxy-Strike-Online-main/GameServer/models/PlayerData.js b/Galaxy-Strike-Online-main/GameServer/models/PlayerData.js new file mode 100644 index 0000000..9169c97 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/models/PlayerData.js @@ -0,0 +1,181 @@ +const mongoose = require('mongoose'); + +const playerDataSchema = new mongoose.Schema({ + userId: { + type: String, + required: true, + unique: true + }, + username: { + type: String, + required: true + }, + // Player stats + stats: { + level: { type: Number, default: 1 }, + experience: { type: Number, default: 0 }, + totalExperience: { type: Number, default: 0 }, + credits: { type: Number, default: 1000 }, + gems: { type: Number, default: 0 }, + skillPoints: { type: Number, default: 0 }, + totalKills: { type: Number, default: 0 }, + dungeonsCleared: { type: Number, default: 0 }, + questsCompleted: { type: Number, default: 0 }, + playTime: { type: Number, default: 0 }, + lastLogin: { type: Date, default: Date.now } + }, + // Game systems data + inventory: { + items: [{ type: mongoose.Schema.Types.Mixed }], + maxSize: { type: Number, default: 50 } + }, + ship: { + type: mongoose.Schema.Types.Mixed, + default: null + }, + base: { + type: mongoose.Schema.Types.Mixed, + default: null + }, + quests: { + active: [{ type: mongoose.Schema.Types.Mixed }], + completed: [{ type: String }] + }, + skills: { + type: mongoose.Schema.Types.Mixed, + default: {} + }, + idleSystem: { + type: mongoose.Schema.Types.Mixed, + default: {} + }, + dungeonSystem: { + type: mongoose.Schema.Types.Mixed, + default: {} + }, + crafting: { + type: mongoose.Schema.Types.Mixed, + default: {} + }, + // Starbase customisation — wallpapers and room unlocks + starbase: { + type: mongoose.Schema.Types.Mixed, + default: { + wallpaper: null, + ownedWallpapers: [], + unlockedRooms: [], + } + }, + // NPC Faction Reputation (GDD §15.3) — scale -2000 to +2000 + reputation: { type: mongoose.Schema.Types.Mixed, default: null }, + + // Fleet missions (GDD §8.3) + fleetMissions: { type: mongoose.Schema.Types.Mixed, default: [] }, + + // Ship construction queue (GDD §7.4) + shipQueue: { type: mongoose.Schema.Types.Mixed, default: [] }, + + // Alliance membership (GDD §12) + allianceId: { type: String, default: null }, + allianceTag: { type: String, default: null }, + allianceName: { type: String, default: null }, + allianceRank: { type: String, default: null }, + + // Core resources — Metal, Gas, Crystal, Energy Cells, Dark Matter (GDD §5) + resources: { + type: mongoose.Schema.Types.Mixed, + default: { metal: 500, gas: 200, crystal: 100, energyCells: 300, darkMatter: 0, lastTick: null } + }, + // Building slots — { buildingId: { level, buildQueue: { startedAt, completesAt } | null } } + buildings: { + type: mongoose.Schema.Types.Mixed, + default: { command_center: { level: 1, buildQueue: null } } + }, + // Galaxy map — explored sectors and home sector + exploredSectors: { type: [String], default: ['15_10'] }, + homeSector: { type: String, default: '15_10' }, + // Research progress + research: { + type: mongoose.Schema.Types.Mixed, + default: { completed: [], inProgress: null, effects: {} } + }, + // Server-specific data + currentServerId: { + type: String, + default: null + }, + lastServerJoin: { + type: Date, + default: null + }, + totalPlayTime: { + type: Number, + default: 0 + } +}, { + timestamps: true +}); + +// Indexes for performance +playerDataSchema.index({ username: 1 }); +playerDataSchema.index({ 'stats.level': 1 }); + +// Note: userId field already has unique: true which creates an index automatically + +// Methods +// GDD §3.2: XP_required(L) = 500 × L^1.65 +function xpForLevel(level) { return Math.floor(500 * Math.pow(level, 1.65)); } + +playerDataSchema.methods.addExperience = function(amount) { + this.stats.experience = (this.stats.experience || 0) + amount; + this.stats.totalExperience = (this.stats.totalExperience || 0) + amount; + // Level-up loop + let leveled = false; + while (this.stats.experience >= xpForLevel(this.stats.level + 1)) { + this.stats.experience -= xpForLevel(this.stats.level + 1); + this.stats.level += 1; + this.stats.skillPoints = (this.stats.skillPoints || 0) + 1; // +1 skill point per level + leveled = true; + } + this.stats.experienceToNextLevel = xpForLevel(this.stats.level + 1); + return { newXP: this.stats.experience, level: this.stats.level, leveled }; +}; + +playerDataSchema.methods.addCredits = function(amount) { + this.stats.credits += amount; + return this.stats.credits; +}; + +playerDataSchema.methods.updatePlayTime = function() { + const sessionTime = Date.now() - (this.lastLogin || Date.now()); + this.stats.playTime += sessionTime; + this.totalPlayTime += sessionTime; + this.lastLogin = new Date(); + return sessionTime; +}; + +playerDataSchema.methods.joinServer = function(serverId) { + this.currentServerId = serverId; + this.lastServerJoin = new Date(); + this.lastLogin = new Date(); +}; + +playerDataSchema.methods.leaveServer = function() { + console.log('[PLAYER DATA] leaveServer called for:', this.username); + + // Update play time before leaving + const sessionTime = Date.now() - (this.lastLogin || Date.now()); + this.stats.playTime += sessionTime; + this.totalPlayTime += sessionTime; + this.lastLogin = new Date(); + + // Clear server association + this.currentServerId = null; + this.lastServerJoin = null; + + console.log('[PLAYER DATA] Updated play time:', sessionTime, 'Total play time:', this.totalPlayTime); + + return sessionTime; +}; + +module.exports = mongoose.model('PlayerData', playerDataSchema); diff --git a/Galaxy-Strike-Online-main/GameServer/package-lock.json b/Galaxy-Strike-Online-main/GameServer/package-lock.json new file mode 100644 index 0000000..6580117 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/package-lock.json @@ -0,0 +1,3293 @@ +{ + "name": "galaxystrikeonline-game-server", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "galaxystrikeonline-game-server", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "compression": "^1.7.4", + "cors": "^2.8.5", + "dotenv": "^16.3.1", + "express": "^4.18.2", + "helmet": "^7.1.0", + "jsonwebtoken": "^9.0.2", + "mongoose": "^8.0.3", + "socket.io": "^4.7.4", + "sqlite3": "^5.0.2", + "winston": "^3.11.0" + }, + "devDependencies": { + "nodemon": "^3.0.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", + "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", + "license": "MIT", + "dependencies": { + "@so-ric/colorspace": "^1.1.6", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.4.tgz", + "integrity": "sha512-p7X/ytJDIdwUfFL/CLOhKgdfJe1Fa8uw9seJYvdOmnP9JBWGWHW69HkOixXS6Wy9yvGf1MbhcS6lVmrhy4jm2g==", + "license": "MIT", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, + "node_modules/@so-ric/colorspace": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", + "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", + "license": "MIT", + "dependencies": { + "color": "^5.0.2", + "text-hex": "1.0.x" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "25.0.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz", + "integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "license": "MIT" + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "license": "ISC" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "license": "MIT", + "optional": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "license": "ISC" + }, + "node_modules/are-we-there-yet": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", + "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/are-we-there-yet/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/are-we-there-yet/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT", + "optional": true + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "license": "MIT", + "optional": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha512-OorbnJVPII4DuUKbjARAe8u8EfqOmkEEaSFIyoQ7OjTHn6kafxWl0wLgoZ2rXaYd7MyLcDaU4TmhfxtwgcccMQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "inherits": "~2.0.0" + }, + "engines": { + "node": "0.4 || >=0.5.8" + } + }, + "node_modules/body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bson": { + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "license": "Apache-2.0", + "optional": true + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz", + "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==", + "license": "MIT", + "dependencies": { + "color-convert": "^3.1.3", + "color-string": "^2.1.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/color-convert": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz", + "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=14.6" + } + }, + "node_modules/color-name": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", + "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/color-string": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz", + "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "optional": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "license": "ISC" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "license": "MIT", + "optional": true, + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "license": "MIT" + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "license": "MIT", + "optional": true, + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/engine.io": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", + "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", + "license": "MIT", + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT", + "optional": true + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "optional": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT", + "optional": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT", + "optional": true + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "license": "MIT" + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "license": "ISC", + "dependencies": { + "minipass": "^2.6.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "license": "MIT", + "optional": true, + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC", + "optional": true + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "license": "ISC", + "optional": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "license": "MIT", + "optional": true, + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "license": "ISC" + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/helmet": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.2.0.tgz", + "integrity": "sha512-ZRiwvN089JfMXokizgqEPXsl2Guk094yExfoDXR0cBYWxtBbaSww/w+vT4WEJsBW2iTUi1GgZ6swmoug3Oy4Xw==", + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/ignore-walk": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz", + "integrity": "sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==", + "license": "ISC", + "dependencies": { + "minimatch": "^3.0.4" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "license": "MIT", + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "license": "MIT", + "optional": true + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC", + "optional": true + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "license": "MIT", + "optional": true + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "license": "MIT", + "optional": true + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "license": "(AFL-2.1 OR BSD-3-Clause)", + "optional": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT", + "optional": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "license": "ISC", + "optional": true + }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "license": "MIT", + "optional": true, + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/kareem": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "license": "MIT", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/logform/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT" + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "license": "ISC", + "dependencies": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "node_modules/minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "license": "MIT", + "dependencies": { + "minipass": "^2.9.0" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mongodb": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.20.0.tgz", + "integrity": "sha512-Tl6MEIU3K4Rq3TSHd+sZQqRBoGlFsOgNrH5ltAcFBV62Re3Fd+FcaVf8uSEQFOJ51SDowDVttBTONMfoYWrWlQ==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.3.0", + "bson": "^6.10.4", + "mongodb-connection-string-url": "^3.0.2" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.3.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, + "node_modules/mongoose": { + "version": "8.20.4", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.20.4.tgz", + "integrity": "sha512-o4ABeT3IEk1Z4dGt3XjHJ0x9OjyWvakC1+btPpzWqCovqyidKGdbB05/g87cdh7AuWXFQKHOxt+L/OZOBps4hw==", + "license": "MIT", + "dependencies": { + "bson": "^6.10.4", + "kareem": "2.6.3", + "mongodb": "~6.20.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "engines": { + "node": ">=16.20.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mongoose/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "license": "MIT", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/mquery/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mquery/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/needle": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz", + "integrity": "sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==", + "license": "MIT", + "dependencies": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/needle/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "license": "MIT" + }, + "node_modules/node-gyp": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", + "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", + "license": "MIT", + "optional": true, + "dependencies": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/node-gyp/node_modules/semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha512-mfmm3/H9+67MCVix1h+IXTpDwL6710LyHuk7+cWC9T1mE0qz4iHhh6r4hU2wrIT9iTsAAC2XQRvfblL028cpLw==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/node-pre-gyp": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz", + "integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==", + "deprecated": "Please upgrade to @mapbox/node-pre-gyp: the non-scoped node-pre-gyp package is deprecated and only the @mapbox scoped package will recieve updates in the future", + "license": "BSD-3-Clause", + "dependencies": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/node-pre-gyp/node_modules/nopt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", + "license": "ISC", + "dependencies": { + "abbrev": "1", + "osenv": "^0.1.4" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, + "node_modules/node-pre-gyp/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/node-pre-gyp/node_modules/tar": { + "version": "4.4.19", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", + "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", + "license": "ISC", + "dependencies": { + "chownr": "^1.1.4", + "fs-minipass": "^1.2.7", + "minipass": "^2.9.0", + "minizlib": "^1.3.3", + "mkdirp": "^0.5.5", + "safe-buffer": "^5.2.1", + "yallist": "^3.1.1" + }, + "engines": { + "node": ">=4.5" + } + }, + "node_modules/nodemon": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.11.tgz", + "integrity": "sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/nodemon/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==", + "license": "ISC", + "optional": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-bundled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", + "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "license": "ISC" + }, + "node_modules/npm-packlist": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", + "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", + "license": "ISC", + "dependencies": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "license": "MIT", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "license": "MIT", + "optional": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "license": "MIT", + "optional": true, + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request/node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.4.tgz", + "integrity": "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", + "license": "MIT" + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "license": "MIT", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-adapter/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/sqlite3": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.0.2.tgz", + "integrity": "sha512-1SdTNo+BVU211Xj1csWa8lV6KM0CtucDwRyA0VHl91wEH1Mgh7RxUpI4rVvG7OhHrzCSGaVyW5g8vKvlrk9DJA==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "node-addon-api": "^3.0.0", + "node-pre-gyp": "^0.11.0" + }, + "optionalDependencies": { + "node-gyp": "3.x" + }, + "peerDependencies": { + "node-gyp": "3.x" + }, + "peerDependenciesMeta": { + "node-gyp": { + "optional": true + } + } + }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "license": "MIT", + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tar": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", + "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", + "deprecated": "This version of tar is no longer supported, and will not receive security updates. Please upgrade asap.", + "license": "ISC", + "optional": true, + "dependencies": { + "block-stream": "*", + "fstream": "^1.0.12", + "inherits": "2" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "license": "Unlicense", + "optional": true + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "license": "MIT", + "optional": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/winston": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz", + "integrity": "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==", + "license": "MIT", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.8", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "license": "MIT", + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" + } + } +} diff --git a/Galaxy-Strike-Online-main/GameServer/package.json b/Galaxy-Strike-Online-main/GameServer/package.json new file mode 100644 index 0000000..0e6623f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/package.json @@ -0,0 +1,39 @@ +{ + "name": "galaxystrikeonline-game-server", + "version": "1.0.0", + "description": "Galaxy Strike Online - Game Server (Real-time Multiplayer)", + "keywords": [ + "game", + "server", + "mmorpg", + "multiplayer", + "websocket" + ], + "license": "MIT", + "author": "Korvarix Studios", + "type": "commonjs", + "main": "server.js", + "scripts": { + "start": "node server.js", + "dev": "nodemon server.js", + "debug": "node --inspect server.js" + }, + "dependencies": { + "compression": "^1.7.4", + "cors": "^2.8.5", + "dotenv": "^16.3.1", + "express": "^4.18.2", + "helmet": "^7.1.0", + "jsonwebtoken": "^9.0.2", + "mongoose": "^8.0.3", + "socket.io": "^4.7.4", + "sqlite3": "^5.0.2", + "winston": "^3.11.0" + }, + "devDependencies": { + "nodemon": "^3.0.2" + }, + "engines": { + "node": ">=18.0.0" + } +} diff --git a/Galaxy-Strike-Online-main/GameServer/routes/base.js b/Galaxy-Strike-Online-main/GameServer/routes/base.js new file mode 100644 index 0000000..e973390 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/routes/base.js @@ -0,0 +1,243 @@ +const express = require('express'); +const BaseSystem = require('../systems/BaseSystem'); + +const router = express.Router(); + +// Initialize base system +const baseSystem = new BaseSystem(); + +// Get player base +router.get('/player/:userId', async (req, res) => { + try { + const { userId } = req.params; + const base = baseSystem.getPlayerBase(userId); + + res.json({ + success: true, + base + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get base statistics +router.get('/stats/:userId', async (req, res) => { + try { + const { userId } = req.params; + const stats = baseSystem.getBaseStatistics(userId); + + res.json({ + success: true, + stats + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Add experience to base +router.post('/experience/:userId', async (req, res) => { + try { + const { userId } = req.params; + const { amount } = req.body; + + if (!amount || amount <= 0) { + return res.status(400).json({ + success: false, + error: 'Valid experience amount required' + }); + } + + const result = baseSystem.addExperience(userId, amount); + + res.json({ + success: true, + result + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Build room +router.post('/build/:userId', async (req, res) => { + try { + const { userId } = req.params; + const { roomType, level = 1 } = req.body; + + if (!roomType) { + return res.status(400).json({ + success: false, + error: 'Room type required' + }); + } + + const result = baseSystem.buildRoom(userId, roomType, level); + + res.json({ + success: true, + result + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Upgrade room +router.post('/upgrade/:userId', async (req, res) => { + try { + const { userId } = req.params; + const { roomType } = req.body; + + if (!roomType) { + return res.status(400).json({ + success: false, + error: 'Room type required' + }); + } + + const result = baseSystem.upgradeRoom(userId, roomType); + + res.json({ + success: true, + result + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Demolish room +router.post('/demolish/:userId', async (req, res) => { + try { + const { userId } = req.params; + const { roomType } = req.body; + + if (!roomType) { + return res.status(400).json({ + success: false, + error: 'Room type required' + }); + } + + const result = baseSystem.demolishRoom(userId, roomType); + + res.json({ + success: true, + result + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Add decoration +router.post('/decoration/add/:userId', async (req, res) => { + try { + const { userId } = req.params; + const { decorationType } = req.body; + + if (!decorationType) { + return res.status(400).json({ + success: false, + error: 'Decoration type required' + }); + } + + const result = baseSystem.addDecoration(userId, decorationType); + + res.json({ + success: true, + result + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Remove decoration +router.post('/decoration/remove/:userId', async (req, res) => { + try { + const { userId } = req.params; + const { decorationIndex } = req.body; + + if (decorationIndex === undefined || decorationIndex === null) { + return res.status(400).json({ + success: false, + error: 'Decoration index required' + }); + } + + const result = baseSystem.removeDecoration(userId, decorationIndex); + + res.json({ + success: true, + result + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get available rooms +router.get('/rooms/available/:userId', async (req, res) => { + try { + const { userId } = req.params; + const rooms = baseSystem.getAvailableRooms(userId); + + res.json({ + success: true, + rooms + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get room upgrades +router.get('/rooms/upgrades/:userId', async (req, res) => { + try { + const { userId } = req.params; + const upgrades = baseSystem.getRoomUpgrades(userId); + + res.json({ + success: true, + upgrades + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +module.exports = router; diff --git a/Galaxy-Strike-Online-main/GameServer/routes/crafting.js b/Galaxy-Strike-Online-main/GameServer/routes/crafting.js new file mode 100644 index 0000000..010af20 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/routes/crafting.js @@ -0,0 +1,212 @@ +const express = require('express'); +const CraftingSystem = require('../systems/CraftingSystem'); + +const router = express.Router(); + +// Initialize crafting system +const craftingSystem = new CraftingSystem(); + +// Get all recipes +router.get('/recipes', async (req, res) => { + try { + const recipes = craftingSystem.getAllRecipes(); + res.json({ + success: true, + recipes + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get recipe by ID +router.get('/recipes/:id', async (req, res) => { + try { + const { id } = req.params; + const recipe = craftingSystem.getRecipe(id); + + if (!recipe) { + return res.status(404).json({ + success: false, + error: 'Recipe not found' + }); + } + + res.json({ + success: true, + recipe + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get recipes by type +router.get('/recipes/type/:type', async (req, res) => { + try { + const { type } = req.params; + const recipes = craftingSystem.getRecipesByType(type); + + res.json({ + success: true, + recipes + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get player crafting data +router.get('/player/:userId', async (req, res) => { + try { + const { userId } = req.params; + const playerData = craftingSystem.getPlayerData(userId); + + res.json({ + success: true, + playerData + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Check if can craft +router.post('/can-craft/:userId/:recipeId', async (req, res) => { + try { + const { userId, recipeId } = req.params; + const { inventory } = req.body; + + if (!inventory) { + return res.status(400).json({ + success: false, + error: 'Inventory data required' + }); + } + + const canCraft = craftingSystem.canCraft(userId, recipeId, inventory); + + res.json({ + success: true, + canCraft + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Craft item +router.post('/craft/:userId/:recipeId', async (req, res) => { + try { + const { userId, recipeId } = req.params; + const { inventory } = req.body; + + if (!inventory) { + return res.status(400).json({ + success: false, + error: 'Inventory data required' + }); + } + + const result = await craftingSystem.craftItem(userId, recipeId, inventory); + + res.json({ + success: true, + result + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Learn recipe +router.post('/learn/:userId/:recipeId', async (req, res) => { + try { + const { userId, recipeId } = req.params; + const result = craftingSystem.learnRecipe(userId, recipeId); + + res.json({ + success: true, + result + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get player known recipes +router.get('/known/:userId', async (req, res) => { + try { + const { userId } = req.params; + const recipes = craftingSystem.getPlayerKnownRecipes(userId); + + res.json({ + success: true, + recipes + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get crafting stats +router.get('/stats/:userId', async (req, res) => { + try { + const { userId } = req.params; + const stats = craftingSystem.getCraftingStats(userId); + + res.json({ + success: true, + stats + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Discover recipe +router.post('/discover/:userId/:recipeId', async (req, res) => { + try { + const { userId, recipeId } = req.params; + const result = craftingSystem.discoverRecipe(userId, recipeId); + + res.json({ + success: true, + result + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +module.exports = router; diff --git a/Galaxy-Strike-Online-main/GameServer/routes/dungeons.js b/Galaxy-Strike-Online-main/GameServer/routes/dungeons.js new file mode 100644 index 0000000..29a42bf --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/routes/dungeons.js @@ -0,0 +1,380 @@ +const express = require('express'); +const DungeonSystem = require('../systems/DungeonSystem'); + +const router = express.Router(); + +// Initialize dungeon system +const dungeonSystem = new DungeonSystem(); + +// Get all dungeons +router.get('/', async (req, res) => { + try { + const dungeons = dungeonSystem.getAllDungeons(); + res.json({ + success: true, + dungeons + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get dungeons by difficulty +router.get('/difficulty/:difficulty', async (req, res) => { + try { + const { difficulty } = req.params; + const dungeons = dungeonSystem.getDungeonsByDifficulty(difficulty); + + res.json({ + success: true, + dungeons + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get available dungeons for player +router.get('/available/:userId', async (req, res) => { + try { + const { userId } = req.params; + const { playerLevel = 1, playerCount = 1 } = req.query; + const dungeons = dungeonSystem.getAvailableDungeons(parseInt(playerLevel), parseInt(playerCount)); + + res.json({ + success: true, + dungeons + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Create dungeon instance +router.post('/create/:dungeonId/:creatorId', async (req, res) => { + try { + const { dungeonId, creatorId } = req.params; + const { playerIds = [] } = req.body; + + const instance = dungeonSystem.createInstance(dungeonId, creatorId, playerIds); + + res.json({ + success: true, + instance + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get dungeon instance +router.get('/instance/:instanceId', async (req, res) => { + try { + const { instanceId } = req.params; + const instance = dungeonSystem.getInstance(instanceId); + + if (!instance) { + return res.status(404).json({ + success: false, + error: 'Instance not found' + }); + } + + res.json({ + success: true, + instance + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get player's current instance +router.get('/player/:userId', async (req, res) => { + try { + const { userId } = req.params; + const instance = dungeonSystem.getPlayerInstance(userId); + + if (!instance) { + return res.status(404).json({ + success: false, + error: 'No active instance found' + }); + } + + res.json({ + success: true, + instance + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Join dungeon instance +router.post('/join/:instanceId/:userId', async (req, res) => { + try { + const { instanceId, userId } = req.params; + const instance = dungeonSystem.joinInstance(instanceId, userId); + + res.json({ + success: true, + instance + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Leave dungeon instance +router.post('/leave/:userId', async (req, res) => { + try { + const { userId } = req.params; + const instance = dungeonSystem.leaveInstance(userId); + + res.json({ + success: true, + instance + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Start encounter +router.post('/encounter/start/:instanceId/:userId', async (req, res) => { + try { + const { instanceId, userId } = req.params; + const result = dungeonSystem.startEncounter(instanceId, userId); + + res.json({ + success: true, + result + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Complete encounter +router.post('/encounter/complete/:instanceId/:userId', async (req, res) => { + try { + const { instanceId, userId } = req.params; + const { result } = req.body; + + if (!result) { + return res.status(400).json({ + success: false, + error: 'Encounter result required' + }); + } + + const encounterResult = dungeonSystem.completeEncounter(instanceId, userId, result); + + res.json({ + success: true, + result: encounterResult + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Complete dungeon +router.post('/complete/:instanceId', async (req, res) => { + try { + const { instanceId } = req.params; + const result = dungeonSystem.completeDungeon(instanceId); + + res.json({ + success: true, + result + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get dungeon statistics +router.get('/stats/:userId', async (req, res) => { + try { + const { userId } = req.params; + const stats = dungeonSystem.getDungeonStatistics(userId); + + res.json({ + success: true, + stats + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Cleanup expired instances +router.post('/cleanup', async (req, res) => { + try { + const cleanedCount = dungeonSystem.cleanupExpiredInstances(); + + res.json({ + success: true, + cleanedCount + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// 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; diff --git a/Galaxy-Strike-Online-main/GameServer/routes/idle.js b/Galaxy-Strike-Online-main/GameServer/routes/idle.js new file mode 100644 index 0000000..f6381a8 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/routes/idle.js @@ -0,0 +1,282 @@ +const express = require('express'); +const IdleSystem = require('../systems/IdleSystem'); + +const router = express.Router(); + +// Initialize idle system +const idleSystem = new IdleSystem(); + +// Calculate offline progress +router.post('/progress/:userId', async (req, res) => { + try { + const { userId } = req.params; + const progress = idleSystem.calculateOfflineProgress(userId); + + res.json({ + success: true, + progress: { + offlineTime: progress.offlineTime, + offlineSeconds: progress.offlineSeconds, + rewards: progress.rewards, + productionRates: progress.productionRates + } + }); + + } catch (error) { + res.status(500).json({ + success: false, + error: 'Internal server error' + }); + } +}); + +// Apply idle rewards to player data +router.post('/rewards/apply/:userId', async (req, res) => { + try { + const { userId } = req.params; + const { playerData } = req.body; + + if (!playerData) { + return res.status(400).json({ + success: false, + error: 'Player data required' + }); + } + + const progress = idleSystem.applyIdleRewards(userId, playerData); + + res.json({ + success: true, + progress, + updatedPlayerData: playerData + }); + + } catch (error) { + res.status(500).json({ + success: false, + error: 'Internal server error' + }); + } +}); + +// Get production rates +router.get('/production/:userId', async (req, res) => { + try { + const { userId } = req.params; + const productionRates = idleSystem.getProductionRates(userId); + + res.json({ + success: true, + productionRates + }); + + } catch (error) { + res.status(500).json({ + success: false, + error: 'Internal server error' + }); + } +}); + +// Update production rates +router.post('/production/update/:userId', async (req, res) => { + try { + const { userId } = req.params; + const { rates } = req.body; + + if (!rates) { + return res.status(400).json({ + success: false, + error: 'Production rates required' + }); + } + + idleSystem.updateProductionRates(userId, rates); + const updatedRates = idleSystem.getProductionRates(userId); + + res.json({ + success: true, + productionRates: updatedRates + }); + + } catch (error) { + res.status(500).json({ + success: false, + error: 'Internal server error' + }); + } +}); + +// Update bonuses +router.post('/bonuses/update/:userId', async (req, res) => { + try { + const { userId } = req.params; + const { bonuses } = req.body; + + if (!bonuses) { + return res.status(400).json({ + success: false, + error: 'Bonuses required' + }); + } + + const updatedRates = idleSystem.updateBonuses(userId, bonuses); + + res.json({ + success: true, + productionRates: updatedRates + }); + + } catch (error) { + res.status(500).json({ + success: false, + error: 'Internal server error' + }); + } +}); + +// Get achievements +router.get('/achievements/:userId', async (req, res) => { + try { + const { userId } = req.params; + const achievements = idleSystem.getAchievements(userId); + + res.json({ + success: true, + achievements + }); + + } catch (error) { + res.status(500).json({ + success: false, + error: 'Internal server error' + }); + } +}); + +// Get idle stats +router.get('/stats/:userId', async (req, res) => { + try { + const { userId } = req.params; + const stats = idleSystem.getIdleStats(userId); + + res.json({ + success: true, + stats + }); + + } catch (error) { + res.status(500).json({ + success: false, + error: 'Internal server error' + }); + } +}); + +// Calculate income for specific time +router.post('/income/calculate/:userId', async (req, res) => { + try { + const { userId } = req.params; + const { timeInSeconds } = req.body; + + if (!timeInSeconds || timeInSeconds <= 0) { + return res.status(400).json({ + success: false, + error: 'Valid time in seconds required' + }); + } + + const income = idleSystem.calculateIncomeForTime(userId, timeInSeconds); + + res.json({ + success: true, + timeInSeconds, + income + }); + + } catch (error) { + res.status(500).json({ + success: false, + error: 'Internal server error' + }); + } +}); + +// Get time until milestone +router.get('/milestone/:userId', async (req, res) => { + try { + const { userId } = req.params; + const targetCredits = parseInt(req.query.target) || 10000; + const timeUntil = idleSystem.getTimeUntilMilestone(userId, targetCredits); + + res.json({ + success: true, + targetCredits, + timeUntil + }); + + } catch (error) { + res.status(500).json({ + success: false, + error: 'Internal server error' + }); + } +}); + +// Update last active time +router.post('/active/:userId', async (req, res) => { + try { + const { userId } = req.params; + idleSystem.updateLastActive(userId); + + res.json({ + success: true, + lastActive: new Date().toISOString() + }); + + } catch (error) { + res.status(500).json({ + success: false, + error: 'Internal server error' + }); + } +}); + +// Get global idle statistics +router.get('/global', async (req, res) => { + try { + const globalStats = idleSystem.getGlobalStats(); + + res.json({ + success: true, + globalStats + }); + + } catch (error) { + res.status(500).json({ + success: false, + error: 'Internal server error' + }); + } +}); + +// Reset player data (admin only) +router.post('/reset/:userId', async (req, res) => { + try { + const { userId } = req.params; + idleSystem.resetPlayerData(userId); + + res.json({ + success: true, + message: 'Player idle data reset successfully' + }); + + } catch (error) { + res.status(500).json({ + success: false, + error: 'Internal server error' + }); + } +}); + +module.exports = router; diff --git a/Galaxy-Strike-Online-main/GameServer/routes/quests.js b/Galaxy-Strike-Online-main/GameServer/routes/quests.js new file mode 100644 index 0000000..c6e7d36 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/routes/quests.js @@ -0,0 +1,232 @@ +const express = require('express'); +const QuestSystem = require('../systems/QuestSystem'); + +const router = express.Router(); + +// Initialize quest system +const questSystem = new QuestSystem(); + +// Get all quests +router.get('/', async (req, res) => { + try { + const quests = questSystem.getAllQuests(); + res.json({ + success: true, + quests + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get quests by type +router.get('/type/:type', async (req, res) => { + try { + const { type } = req.params; + const quests = questSystem.getQuestsByType(type); + + res.json({ + success: true, + quests + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get quests by difficulty +router.get('/difficulty/:difficulty', async (req, res) => { + try { + const { difficulty } = req.params; + const quests = questSystem.getQuestsByDifficulty(difficulty); + + res.json({ + success: true, + quests + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get available quests for player +router.get('/available/:userId', async (req, res) => { + try { + const { userId } = req.params; + const quests = questSystem.getAvailableQuests(userId); + + res.json({ + success: true, + quests + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Start quest +router.post('/start/:userId/:questId', async (req, res) => { + try { + const { userId, questId } = req.params; + const result = questSystem.startQuest(userId, questId); + + res.json({ + success: true, + result + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Update quest progress +router.post('/progress/:userId/:questId', async (req, res) => { + try { + const { userId, questId } = req.params; + const { progressUpdates } = req.body; + + if (!progressUpdates) { + return res.status(400).json({ + success: false, + error: 'Progress updates required' + }); + } + + const result = questSystem.updateQuestProgress(userId, questId, progressUpdates); + + res.json({ + success: true, + result + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Complete quest +router.post('/complete/:userId/:questId', async (req, res) => { + try { + const { userId, questId } = req.params; + const result = questSystem.completeQuest(userId, questId); + + res.json({ + success: true, + result + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get player active quests +router.get('/active/:userId', async (req, res) => { + try { + const { userId } = req.params; + const quests = questSystem.getPlayerActiveQuests(userId); + + res.json({ + success: true, + quests + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get player completed quests +router.get('/completed/:userId', async (req, res) => { + try { + const { userId } = req.params; + const quests = questSystem.getPlayerCompletedQuests(userId); + + res.json({ + success: true, + quests + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get quest statistics +router.get('/stats/:userId', async (req, res) => { + try { + const { userId } = req.params; + const stats = questSystem.getQuestStatistics(userId); + + res.json({ + success: true, + stats + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Reset daily quests +router.post('/reset/daily/:userId', async (req, res) => { + try { + const { userId } = req.params; + const result = questSystem.resetDailyQuests(userId); + + res.json({ + success: true, + result + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Reset weekly quests +router.post('/reset/weekly/:userId', async (req, res) => { + try { + const { userId } = req.params; + const result = questSystem.resetWeeklyQuests(userId); + + res.json({ + success: true, + result + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +module.exports = router; diff --git a/Galaxy-Strike-Online-main/GameServer/routes/ships.js b/Galaxy-Strike-Online-main/GameServer/routes/ships.js new file mode 100644 index 0000000..c4684f7 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/routes/ships.js @@ -0,0 +1,452 @@ +const express = require('express'); +const ShipSystem = require('../systems/ShipSystem'); + +const router = express.Router(); + +// Initialize ship system +const shipSystem = new ShipSystem(); + +// Get all ship templates +router.get('/templates', async (req, res) => { + try { + const templates = shipSystem.getAllShipTemplates(); + res.json({ + success: true, + templates + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get ship templates by class +router.get('/templates/class/:shipClass', async (req, res) => { + try { + const { shipClass } = req.params; + const templates = shipSystem.getShipTemplatesByClass(shipClass); + + res.json({ + success: true, + templates + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get ship templates by rarity +router.get('/templates/rarity/:rarity', async (req, res) => { + try { + const { rarity } = req.params; + const templates = shipSystem.getShipTemplatesByRarity(rarity); + + res.json({ + success: true, + templates + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get available ships for player +router.get('/available/:userId', async (req, res) => { + try { + const { userId } = req.params; + const { playerLevel = 1 } = req.query; + const ships = shipSystem.getAvailableShips(userId, parseInt(playerLevel)); + + res.json({ + success: true, + ships + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get player ships +router.get('/player/:userId', async (req, res) => { + try { + const { userId } = req.params; + const ships = shipSystem.getPlayerShips(userId); + + res.json({ + success: true, + ships: ships.map(ship => ({ + id: ship.id, + name: ship.name, + class: ship.class, + rarity: ship.rarity, + level: ship.level, + experience: ship.experience, + requiredExp: ship.requiredExp, + stats: ship.stats, + maxStats: ship.maxStats, + slots: ship.slots, + equippedItems: ship.equippedItems, + texture: ship.texture, + status: ship.status, + createdAt: ship.createdAt, + lastUsed: ship.lastUsed + })) + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get specific ship +router.get('/player/:userId/:shipId', async (req, res) => { + try { + const { userId, shipId } = req.params; + const ship = shipSystem.getShip(userId, shipId); + + if (!ship) { + return res.status(404).json({ + success: false, + error: 'Ship not found' + }); + } + + res.json({ + success: true, + ship: { + id: ship.id, + name: ship.name, + class: ship.class, + rarity: ship.rarity, + level: ship.level, + experience: ship.experience, + requiredExp: ship.requiredExp, + stats: ship.stats, + maxStats: ship.maxStats, + slots: ship.slots, + equippedItems: ship.equippedItems, + texture: ship.texture, + status: ship.status, + createdAt: ship.createdAt, + lastUsed: ship.lastUsed + } + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get current ship +router.get('/current/:userId', async (req, res) => { + try { + const { userId } = req.params; + const currentShip = shipSystem.getCurrentShip(userId); + + if (!currentShip) { + return res.status(404).json({ + success: false, + error: 'No current ship found' + }); + } + + res.json({ + success: true, + ship: { + id: currentShip.id, + name: currentShip.name, + class: currentShip.class, + rarity: currentShip.rarity, + level: currentShip.level, + experience: currentShip.experience, + requiredExp: currentShip.requiredExp, + stats: currentShip.stats, + maxStats: currentShip.maxStats, + slots: currentShip.slots, + equippedItems: currentShip.equippedItems, + texture: currentShip.texture, + status: currentShip.status + } + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Create new ship +router.post('/create/:userId', async (req, res) => { + try { + const { userId } = req.params; + const { templateId, name, level = 1 } = req.body; + + if (!templateId) { + return res.status(400).json({ + success: false, + error: 'Ship template ID required' + }); + } + + const ship = shipSystem.createShip(userId, templateId, { name, level }); + const playerShips = shipSystem.getPlayerShips(userId); + playerShips.push(ship); + + res.json({ + success: true, + ship: { + id: ship.id, + name: ship.name, + class: ship.class, + rarity: ship.rarity, + level: ship.level, + experience: ship.experience, + requiredExp: ship.requiredExp, + stats: ship.stats, + maxStats: ship.maxStats, + slots: ship.slots, + equippedItems: ship.equippedItems, + texture: ship.texture, + status: ship.status, + createdAt: ship.createdAt + } + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Set current ship +router.post('/current/:userId/:shipId', async (req, res) => { + try { + const { userId, shipId } = req.params; + const ship = shipSystem.setCurrentShip(userId, shipId); + + res.json({ + success: true, + ship: { + id: ship.id, + name: ship.name, + class: ship.class, + level: ship.level, + stats: ship.stats, + status: ship.status, + lastUsed: ship.lastUsed + } + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Add experience to ship +router.post('/experience/:userId/:shipId', async (req, res) => { + try { + const { userId, shipId } = req.params; + const { amount } = req.body; + + if (!amount || amount <= 0) { + return res.status(400).json({ + success: false, + error: 'Valid experience amount required' + }); + } + + const result = shipSystem.addExperience(userId, shipId, amount); + + res.json({ + success: true, + result: { + experienceGained: result.experienceGained, + levelsGained: result.levelsGained, + newLevel: result.newLevel, + newStats: result.newStats + } + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Upgrade ship +router.post('/upgrade/:userId/:shipId', async (req, res) => { + try { + const { userId, shipId } = req.params; + const { upgradeType } = req.body; + + if (!upgradeType) { + return res.status(400).json({ + success: false, + error: 'Upgrade type required' + }); + } + + const result = shipSystem.upgradeShip(userId, shipId, upgradeType); + + res.json({ + success: true, + result: { + upgradeType: result.upgradeType, + newStats: result.newStats + } + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Equip item to ship +router.post('/equip/:userId/:shipId', async (req, res) => { + try { + const { userId, shipId } = req.params; + const { slot, itemId } = req.body; + + if (!slot || !itemId) { + return res.status(400).json({ + success: false, + error: 'Slot and item ID required' + }); + } + + const result = shipSystem.equipItem(userId, shipId, slot, itemId); + + res.json({ + success: true, + result: { + slot: result.slot, + itemId: result.itemId, + equippedItems: result.equippedItems + } + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Unequip item from ship +router.post('/unequip/:userId/:shipId', async (req, res) => { + try { + const { userId, shipId } = req.params; + const { slot } = req.body; + + if (!slot) { + return res.status(400).json({ + success: false, + error: 'Slot required' + }); + } + + const result = shipSystem.unequipItem(userId, shipId, slot); + + res.json({ + success: true, + result: { + slot: result.slot, + itemId: result.itemId, + equippedItems: result.equippedItems + } + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Repair ship +router.post('/repair/:userId/:shipId', async (req, res) => { + try { + const { userId, shipId } = req.params; + const { amount } = req.body; + + const result = shipSystem.repairShip(userId, shipId, amount); + + res.json({ + success: true, + result: { + health: result.health, + maxHealth: result.maxHealth, + repaired: result.repaired + } + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Delete ship +router.delete('/:userId/:shipId', async (req, res) => { + try { + const { userId, shipId } = req.params; + const deletedShip = shipSystem.deleteShip(userId, shipId); + + res.json({ + success: true, + deletedShip: { + id: deletedShip.id, + name: deletedShip.name, + class: deletedShip.class + } + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get ship statistics +router.get('/stats/:userId', async (req, res) => { + try { + const { userId } = req.params; + const stats = shipSystem.getPlayerShipStats(userId); + + res.json({ + success: true, + stats + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +module.exports = router; diff --git a/Galaxy-Strike-Online-main/GameServer/routes/skills.js b/Galaxy-Strike-Online-main/GameServer/routes/skills.js new file mode 100644 index 0000000..e650c0f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/routes/skills.js @@ -0,0 +1,187 @@ +const express = require('express'); +const SkillSystem = require('../systems/SkillSystem'); + +const router = express.Router(); + +// Initialize skill system +const skillSystem = new SkillSystem(); + +// Get all skills +router.get('/', async (req, res) => { + try { + const skills = skillSystem.getAllSkills(); + res.json({ + success: true, + skills + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get skills by category +router.get('/category/:category', async (req, res) => { + try { + const { category } = req.params; + const skills = skillSystem.getSkillsByCategory(category); + + res.json({ + success: true, + skills + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get player skills +router.get('/player/:userId', async (req, res) => { + try { + const { userId } = req.params; + const skills = skillSystem.getPlayerSkills(userId); + + res.json({ + success: true, + skills + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Add experience to skill +router.post('/experience/:userId/:skillId', async (req, res) => { + try { + const { userId, skillId } = req.params; + const { amount } = req.body; + + if (!amount || amount <= 0) { + return res.status(400).json({ + success: false, + error: 'Valid experience amount required' + }); + } + + const result = skillSystem.addExperience(userId, skillId, amount); + + res.json({ + success: true, + result + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Unlock skill +router.post('/unlock/:userId/:skillId', async (req, res) => { + try { + const { userId, skillId } = req.params; + const result = skillSystem.unlockSkill(userId, skillId); + + res.json({ + success: true, + result + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get skill bonuses +router.get('/bonuses/:userId', async (req, res) => { + try { + const { userId } = req.params; + const bonuses = skillSystem.getSkillBonuses(userId); + + res.json({ + success: true, + bonuses + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Award skill points +router.post('/points/award/:userId', async (req, res) => { + try { + const { userId } = req.params; + const { amount } = req.body; + + if (!amount || amount <= 0) { + return res.status(400).json({ + success: false, + error: 'Valid point amount required' + }); + } + + const result = skillSystem.awardSkillPoints(userId, amount); + + res.json({ + success: true, + result + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Allocate skill point +router.post('/points/allocate/:userId/:skillId', async (req, res) => { + try { + const { userId, skillId } = req.params; + const result = skillSystem.allocateSkillPoint(userId, skillId); + + res.json({ + success: true, + result + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +// Get skill statistics +router.get('/stats/:userId', async (req, res) => { + try { + const { userId } = req.params; + const stats = skillSystem.getSkillStatistics(userId); + + res.json({ + success: true, + stats + }); + } catch (error) { + res.status(500).json({ + success: false, + error: error.message + }); + } +}); + +module.exports = router; diff --git a/Galaxy-Strike-Online-main/GameServer/server.js b/Galaxy-Strike-Online-main/GameServer/server.js new file mode 100644 index 0000000..8b8b4ee --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/server.js @@ -0,0 +1,2498 @@ +/** + * Game Server - Based on Client LocalServer Infrastructure + * Handles real-time multiplayer game instances and gameplay + */ + +const express = require('express'); +const http = require('http'); +const socketIo = require('socket.io'); +const cors = require('cors'); +const helmet = require('helmet'); +const compression = require('compression'); +const mongoose = require('mongoose'); +const fs = require('fs'); +const path = require('path'); +require('dotenv').config(); + +const logger = require('./utils/logger'); +const connectDB = require('./config/database'); +const PlayerData = require('./models/PlayerData'); + +// Import server systems for player initialization +const QuestSystem = require('./systems/QuestSystem'); +const SkillSystem = require('./systems/SkillSystem'); +const DungeonSystem = require('./systems/DungeonSystem'); +const CraftingSystem = require('./systems/CraftingSystem'); +const IdleSystem = require('./systems/IdleSystem'); +const ItemSystem = require('./systems/ItemSystem'); +const ContentLoader = require('./systems/ContentLoader'); +const FleetSystem = require('./systems/FleetSystem'); +const { ResourceSystem, RESOURCE_CONFIG, RESOURCE_TYPES } = require('./systems/ResourceSystem'); +const MissionSystem = require('./systems/MissionSystem'); +const { AllianceSystem } = require('./systems/AllianceSystem'); +const { MarketSystem } = require('./systems/MarketSystem'); +const SocialSystem = require('./systems/SocialSystem'); +const { ReputationSystem } = require('./systems/ReputationSystem'); +const GalaxyEventSystem = require('./systems/GalaxyEventSystem'); +const { SeasonSystem } = require('./systems/SeasonSystem'); +const GalaxySystem = require('./systems/GalaxySystem'); +const { ResearchSystem } = require('./systems/ResearchSystem'); + +// ── Load all JSON game content before systems are initialised ── +const contentLoader = new ContentLoader(); +contentLoader._loadSkills(); +contentLoader._loadEnemies(); +contentLoader._loadDungeons(); +contentLoader._loadItems(); +contentLoader._loadRecipes(); +contentLoader._loadQuests(); +contentLoader._loaded = true; +console.log(`[SERVER] Content loaded — ${contentLoader.skills.size} skills, ${contentLoader.items.size} items, ${contentLoader.recipes.size} recipes, ${contentLoader.quests.size} quests, ${contentLoader.dungeons.size} dungeons, ${contentLoader.enemies.size} enemies`); + +// Initialize server systems — each receives contentLoader as its data source +const questSystem = new QuestSystem(contentLoader); +const skillSystem = new SkillSystem(contentLoader); +const dungeonSystem = new DungeonSystem(contentLoader); +const craftingSystem = new CraftingSystem(contentLoader); +const idleSystem = new IdleSystem(); +const itemSystem = new ItemSystem(contentLoader); + +const fleetSystem = new FleetSystem(contentLoader); +const resourceSystem = new ResourceSystem(); +const missionSystem = new MissionSystem(); +const allianceSystem = new AllianceSystem(); +const marketSystem = new MarketSystem(); +const socialSystem = new SocialSystem(); +const reputationSystem = new ReputationSystem(); +const galaxyEventSystem = new GalaxyEventSystem(); +const seasonSystem = new SeasonSystem(); +const galaxySystem = new GalaxySystem(); +const researchSystem = new ResearchSystem(); +// Set server URL for ItemSystem +const SERVER_URL = process.env.SERVER_URL || 'https://dev.gameserver.galaxystrike.online'; +itemSystem.setServerUrl(SERVER_URL); + +const app = express(); +const server = http.createServer(app); +const io = socketIo(server, { + cors: { + origin: [ + "http://localhost:3000", + "http://127.0.0.1:3000", + "file://", + "https://dev.galaxystrike.online", + "https://galaxystrike.online" + ], + methods: ["GET", "POST"], + credentials: true + } +}); + +// Pass io instance to systems that need it +dungeonSystem.setIO(io); + +// Game state +const gameInstances = new Map(); +const connectedClients = new Map(); + +// Server-side shop item lookup using ItemSystem +function findShopItem(itemId) { + return itemSystem.findShopItem(itemId); +} + +// Middleware +app.use(cors({ + origin: [ + "http://localhost:3000", + "http://127.0.0.1:3000", + "file://", + "https://dev.galaxystrike.online", + "https://galaxystrike.online" + ], + credentials: true +})); +app.use(express.json({ limit: '10mb' })); + +// Serve static files from the client directory +app.use(express.static(path.join(__dirname, '../Client'))); + +// Serve ships from server-side storage +app.use('/images/ships', (req, res, next) => { + const requestedPath = req.path; + const serverImagePath = path.join(__dirname, 'assets/images/ships', requestedPath); + + console.log('[IMAGE SERVER] Ship requested:', requestedPath); + + if (fs.existsSync(serverImagePath)) { + res.sendFile(serverImagePath); + } else { + const placeholderPath = path.join(__dirname, 'assets/images/ui/placeholder.png'); + res.sendFile(placeholderPath); + } +}); + +// Serve weapons from server-side storage +app.use('/images/weapons', (req, res, next) => { + const requestedPath = req.path; + const serverImagePath = path.join(__dirname, 'assets/images/weapons', requestedPath); + + console.log('[IMAGE SERVER] Weapon requested:', requestedPath); + + if (fs.existsSync(serverImagePath)) { + res.sendFile(serverImagePath); + } else { + const placeholderPath = path.join(__dirname, 'assets/images/ui/placeholder.png'); + res.sendFile(placeholderPath); + } +}); + +// Serve armors from server-side storage +app.use('/images/armors', (req, res, next) => { + const requestedPath = req.path; + const serverImagePath = path.join(__dirname, 'assets/images/armors', requestedPath); + + console.log('[IMAGE SERVER] Armor requested:', requestedPath); + + if (fs.existsSync(serverImagePath)) { + res.sendFile(serverImagePath); + } else { + const placeholderPath = path.join(__dirname, 'assets/images/ui/placeholder.png'); + res.sendFile(placeholderPath); + } +}); + +// Serve other items (materials, consumables, cosmetics) from server-side storage +app.use('/images/items', (req, res, next) => { + const requestedPath = req.path; + const serverImagePath = path.join(__dirname, 'assets/images/items', requestedPath); + + console.log('[IMAGE SERVER] Item requested:', requestedPath); + + if (fs.existsSync(serverImagePath)) { + res.sendFile(serverImagePath); + } else { + const placeholderPath = path.join(__dirname, 'assets/images/ui/placeholder.png'); + res.sendFile(placeholderPath); + } +}); + +// Serve UI elements and icons from server-side storage +app.use('/images/ui', (req, res, next) => { + const requestedPath = req.path; + const serverImagePath = path.join(__dirname, 'assets/images/ui', requestedPath); + + if (fs.existsSync(serverImagePath)) { + res.sendFile(serverImagePath); + } else { + res.status(404).send('UI asset not found'); + } +}); +app.use(express.urlencoded({ extended: true })); + +// Health check endpoint +app.get('/health', (req, res) => { + res.status(200).json({ + status: 'OK', + timestamp: new Date().toISOString(), + uptime: process.uptime(), + mode: 'game-server', + activeInstances: gameInstances.size, + connectedClients: connectedClients.size + }); +}); + +// API version endpoint +app.get('/api/ssc/version', (req, res) => { + res.status(200).json({ + version: '1.0.0', + service: 'galaxystrikeonline-game-server', + timestamp: new Date().toISOString(), + mode: 'multiplayer' + }); +}); + +// Shop API endpoints +app.get('/api/shop/items', (req, res) => { + try { + res.status(200).json(itemSystem.buildShopResponse()); + } catch (error) { + console.error('[GAME SERVER] Error fetching shop items:', error); + res.status(500).json({ success: false, error: 'Failed to fetch shop items' }); + } +}); + +app.get('/api/shop/items/:category', (req, res) => { + try { + const { category } = req.params; + res.status(200).json({ + success: true, + category, + items: itemSystem.getRandomItemsByCategory(category), + timestamp: new Date().toISOString() + }); + } catch (error) { + console.error('[GAME SERVER] Error fetching category items:', error); + res.status(500).json({ success: false, error: 'Failed to fetch category items' }); + } +}); + +app.get('/api/items/:itemId', (req, res) => { + try { + const { itemId } = req.params; + + // Find item across all categories + const allItems = itemSystem.getAllItems(); + let item = null; + + for (const [category, items] of Object.entries(allItems)) { + item = items.find(i => i.id === itemId); + if (item) break; + } + + if (!item) { + return res.status(404).json({ + success: false, + error: 'Item not found' + }); + } + + res.status(200).json({ + success: true, + item: item, + timestamp: new Date().toISOString() + }); + } catch (error) { + console.error('[GAME SERVER] Error fetching item details:', error); + res.status(500).json({ + success: false, + error: 'Failed to fetch item details' + }); + } +}); + +// Game data endpoints (similar to LocalServer) +app.post('/api/game/player/:id/save', (req, res) => { + const playerId = req.params.id; + const playerData = req.body; + + // Store player data in game instance + if (connectedClients.has(playerId)) { + connectedClients.get(playerId).playerData = playerData; + + // Broadcast to other players in same instance + const instanceId = connectedClients.get(playerId).instanceId; + if (gameInstances.has(instanceId)) { + io.to(instanceId).emit('playerDataUpdated', { + playerId, + timestamp: Date.now() + }); + } + } + + res.status(200).json({ + success: true, + message: 'Player data saved to game server' + }); +}); + +app.get('/api/game/player/:id', (req, res) => { + const playerId = req.params.id; + + if (connectedClients.has(playerId)) { + const playerData = connectedClients.get(playerId).playerData; + res.status(200).json({ + success: true, + player: playerData + }); + } else { + res.status(404).json({ + success: false, + error: 'Player not connected to game server' + }); + } +}); + +// Game system routes +app.use('/api/crafting', require('./routes/crafting')); +app.use('/api/quests', require('./routes/quests')); +app.use('/api/skills', require('./routes/skills')); +app.use('/api/ships', require('./routes/ships')); +app.use('/api/idle', require('./routes/idle')); +app.use('/api/dungeons', require('./routes/dungeons')); +app.use('/api/base', require('./routes/base')); + +// Socket.IO handlers (based on LocalServer) +io.on('connection', (socket) => { + console.log('[GAME SERVER] === NEW CLIENT CONNECTION ==='); + console.log('[GAME SERVER] Client connected:', socket.id); + + // ── Rate limiter: GDD §18.4 — max 20 events/sec per socket ────────── + { + const RL_MAX = 20, RL_WIN = 1000; + let rlCount = 0, rlReset = Date.now() + RL_WIN; + const _origOn = socket.on.bind(socket); + const SYSTEM_EVENTS = new Set(['connect','disconnect','error','reconnect']); + socket.on = function(event, handler) { + if (SYSTEM_EVENTS.has(event)) return _origOn(event, handler); + return _origOn(event, function(...args) { + const now = Date.now(); + if (now > rlReset) { rlCount = 0; rlReset = now + RL_WIN; } + if (++rlCount > RL_MAX) { + console.warn(`[RATE_LIMIT] ${socket.id} dropped '${event}' (${rlCount}/s)`); + return; + } + handler.apply(this, args); + }); + }; + } + connectedClients.set(socket.id, { + connectedAt: Date.now(), + playerData: null, + instanceId: null + }); + + console.log('[GAME SERVER] Waiting for authentication from:', socket.id); + + // Update player count on API server when new player connects + updatePlayerCountOnAPI(); + + // Add timeout for authentication + const authTimeout = setTimeout(() => { + const clientData = connectedClients.get(socket.id); + if (clientData && !clientData.userId) { + console.log('[GAME SERVER] Authentication timeout for:', socket.id); + socket.emit('authenticated', { + success: false, + error: 'Authentication timeout' + }); + socket.disconnect(); + } + }, 10000); // 10 seconds timeout + + // Clear timeout when authenticated + socket.on('authenticated', () => { + clearTimeout(authTimeout); + }); + + // Log all incoming events for debugging + socket.onAny((eventName, ...args) => { + if (eventName !== 'ping' && eventName !== 'pong') { + console.log(`[GAME SERVER] Event received: ${eventName} from ${socket.id}`, args); + } + }); + + // Authentication (similar to LocalServer) + socket.on('authenticate', async (data) => { + console.log('[GAME SERVER] Authenticating client:', socket.id, data); + + try { + // Check database connection first + if (mongoose.connection.readyState !== 1) { + console.error('[GAME SERVER] Database not connected, authentication failed'); + socket.emit('authenticated', { + success: false, + error: 'Database not available' + }); + return; + } + + console.log('[GAME SERVER] Database is connected, proceeding with authentication'); + + // Load player data from database + const playerData = await loadPlayerData(data.userId || socket.id, data.username || 'Game Player'); + + if (playerData) { + console.log('[GAME SERVER] Player data loaded successfully:', playerData.username); + + // Store player data in client connection + const clientData = connectedClients.get(socket.id); + if (clientData) { + clientData.playerData = playerData; + clientData.userId = playerData.userId; + clientData.username = playerData.username; + } else { + console.error('[GAME SERVER] No client data found for socket:', socket.id); + } + + // Join server + playerData.joinServer(`devgame-server-${PORT}`); + + // Update last login time + if (!playerData.stats) playerData.stats = {}; + playerData.stats.lastLogin = new Date().toISOString(); + + // Ensure all required fields exist (for existing players) + if (playerData.stats.totalExperience === undefined) playerData.stats.totalExperience = playerData.stats.experience || 0; + if (playerData.stats.gems === undefined || playerData.stats.gems === 0) playerData.stats.gems = 50; // Restore gems if wiped + if (playerData.stats.skillPoints === undefined) playerData.stats.skillPoints = 0; + if (playerData.stats.totalKills === undefined) playerData.stats.totalKills = 0; + if (playerData.stats.questsCompleted === undefined) playerData.stats.questsCompleted = 0; + + // Ensure idle system production rates are initialized + idleSystem.initializePlayerData(playerData.userId); + console.log('[GAME SERVER] Idle system initialized for player:', playerData.username); + + await savePlayerData(playerData.userId, playerData); + + // Auto-collect any completed fleet missions (GDD §8.3) + try { + if (playerData.fleetMissions?.length > 0) { + const mResults = missionSystem.collectMissions(playerData, resourceSystem); + if (mResults.length > 0) { + await savePlayerData(playerData.userId, playerData); + mResults.forEach(r => socket.emit('mission_completed', r)); + } + } + } catch(me) { console.error('[AUTH MISSION COLLECT]', me.message); } + + // Calculate pending offline rewards to show on dashboard + const pendingOffline = idleSystem.calculateOfflineRewards(playerData.userId); + if (pendingOffline.offlineTime > 0) { + playerData.stats.offlineTime = pendingOffline.offlineTime; + playerData.stats.offlineCredits = pendingOffline.rewards?.credits || 0; + } else { + playerData.stats.offlineTime = 0; + playerData.stats.offlineCredits = 0; + } + + // In production, validate with API server + socket.emit('authenticated', { + success: true, + user: { + id: playerData.userId, + username: playerData.username, + token: 'game-token-' + Date.now() + }, + playerData: { + ...playerData.toObject(), + serverTimestamp: Date.now(), + serverTimezone: 'UTC' + } + }); + + console.log(`[GAME SERVER] ${playerData.username} authenticated with Level ${playerData.stats.level}`); + } else { + console.error('[GAME SERVER] Failed to load player data'); + socket.emit('authenticated', { + success: false, + error: 'Failed to load player data' + }); + } + } catch (error) { + console.error('[GAME SERVER] Authentication error:', error); + console.error('[GAME INITIALIZER] Error stack:', error.stack); + socket.emit('authenticated', { + success: false, + error: 'Authentication failed: ' + error.message + }); + } + }); + + // Game data events (similar to LocalServer) + socket.on('saveGameData', async (data) => { + console.log('[GAME SERVER] Saving game data for:', socket.id); + + const clientData = connectedClients.get(socket.id); + if (clientData && clientData.userId) { + try { + // Validate the data before saving + if (!data || typeof data !== 'object') { + console.warn('[GAME SERVER] Invalid game data received, skipping save'); + socket.emit('gameDataSaved', { success: false, error: 'Invalid game data' }); + return; + } + + // Update player data with new game data + const updatedPlayerData = { ...clientData.playerData, ...data }; + clientData.playerData = updatedPlayerData; + + // Save to database + const success = await savePlayerData(clientData.userId, updatedPlayerData); + + socket.emit('gameDataSaved', { + success: success, + message: success ? 'Game saved to server!' : 'Failed to save to server' + }); + + console.log(`[GAME SERVER] Saved game data for ${clientData.username}`); + } catch (error) { + console.error('[GAME SERVER] Error saving game data:', error); + socket.emit('gameDataSaved', { success: false, error: 'Failed to save data' }); + } + } else { + console.warn('[GAME SERVER] No client data or user ID found for saveGameData'); + socket.emit('gameDataSaved', { success: false, error: 'Player not authenticated' }); + } + }); + + socket.on('loadGameData', (data) => { + console.log('[GAME SERVER] Loading game data for:', socket.id); + + const playerData = connectedClients.get(socket.id)?.playerData || {}; + + socket.emit('gameDataLoaded', { + success: true, + data: playerData + }); + }); + + // Test idle system manually + socket.on('testIdleRewards', (data) => { + console.log('[GAME SERVER] Testing idle rewards for:', socket.id); + + const clientData = connectedClients.get(socket.id); + if (clientData && clientData.userId) { + const onlineRewards = idleSystem.generateOnlineIdleRewards(clientData.userId, 10000); + console.log('[GAME SERVER] Test idle rewards:', onlineRewards); + + socket.emit('testIdleRewards', { + rewards: onlineRewards, + productionRates: idleSystem.playerProductionRates.get(clientData.userId) + }); + } else { + socket.emit('testIdleRewards', { error: 'Not authenticated' }); + } + }); + + // Shop and item system events + socket.on('ping', (data) => { + console.log('[GAME SERVER] Ping received from:', socket.id, data); + socket.emit('pong', { + timestamp: Date.now(), + received: data.timestamp, + serverTime: new Date().toISOString() + }); + }); + + socket.on('getShopItems', () => { + try { + socket.emit('shopItemsReceived', itemSystem.buildShopResponse()); + } catch (error) { + console.error('[GAME SERVER] getShopItems error:', error); + socket.emit('shopItemsReceived', { success: false, error: 'Failed to load shop items' }); + } + }); + + socket.on('getShopCategory', (data) => { + try { + const { category } = data || {}; + if (!category) { + socket.emit('shopCategoryReceived', { success: false, error: 'Category required' }); + return; + } + socket.emit('shopCategoryReceived', { + success: true, + category, + items: itemSystem.getRandomItemsByCategory(category), + timestamp: new Date().toISOString() + }); + } catch (error) { + console.error('[GAME SERVER] getShopCategory error:', error); + socket.emit('shopCategoryReceived', { success: false, error: 'Failed to load category items' }); + } + }); + + socket.on('getItemDetails', (data) => { + const { itemId } = data || {}; + if (!itemId) { + socket.emit('itemDetailsReceived', { success: false, error: 'Item ID required' }); + return; + } + socket.emit('itemDetailsReceived', itemSystem.buildItemDetailResponse(itemId)); + }); + + // Game-specific events + socket.on('joinGameInstance', (data) => { + const { instanceId } = data; + + if (!gameInstances.has(instanceId)) { + gameInstances.set(instanceId, { + id: instanceId, + players: new Set(), + createdAt: Date.now() + }); + } + + const instance = gameInstances.get(instanceId); + instance.players.add(socket.id); + + connectedClients.get(socket.id).instanceId = instanceId; + socket.join(instanceId); + + socket.emit('joinedGameInstance', { + instanceId, + playerCount: instance.players.size + }); + + // Notify other players + socket.to(instanceId).emit('playerJoinedInstance', { + playerId: socket.id, + playerCount: instance.players.size + }); + }); + + socket.on('leaveGameInstance', (data) => { + const clientData = connectedClients.get(socket.id); + if (clientData && clientData.instanceId) { + const instance = gameInstances.get(clientData.instanceId); + if (instance) { + instance.players.delete(socket.id); + socket.leave(clientData.instanceId); + + // Clean up empty instances + if (instance.players.size === 0) { + gameInstances.delete(clientData.instanceId); + } + + // Notify other players + socket.to(clientData.instanceId).emit('playerLeftInstance', { + playerId: socket.id, + playerCount: instance.players.size + }); + } + + clientData.instanceId = null; + } + }); + + socket.on('gameAction', (data) => { + const clientData = connectedClients.get(socket.id); + if (clientData && clientData.instanceId) { + // Broadcast game action to other players in same instance + socket.to(clientData.instanceId).emit('gameAction', { + playerId: socket.id, + action: data, + timestamp: Date.now() + }); + } + }); + + // Idle rewards events + socket.on('claimOfflineRewards', async (data) => { + console.log('[GAME SERVER] Claiming offline rewards for:', socket.id); + + const clientData = connectedClients.get(socket.id); + if (!clientData || !clientData.userId) { + socket.emit('offlineRewardsClaimed', { success: false, error: 'Not authenticated' }); + return; + } + + try { + const playerData = await loadPlayerData(clientData.userId, clientData.username || 'Player'); + const offlineRewards = idleSystem.calculateOfflineRewards(clientData.userId); + + if (offlineRewards.offlineTime > 0 && offlineRewards.rewards.credits > 0) { + // Apply rewards to player + playerData.stats.credits = (playerData.stats.credits || 0) + offlineRewards.rewards.credits; + playerData.stats.experience = (playerData.stats.experience || 0) + offlineRewards.rewards.experience; + + // Update idle system data + if (!playerData.idleSystem) playerData.idleSystem = {}; + playerData.idleSystem.lastActive = new Date().toISOString(); + playerData.idleSystem.totalOfflineTime += offlineRewards.offlineTime; + playerData.idleSystem.totalIdleCredits += offlineRewards.rewards.credits; + + await savePlayerData(clientData.userId, playerData); + + socket.emit('offlineRewardsClaimed', { + success: true, + rewards: offlineRewards.rewards, + offlineTime: offlineRewards.offlineTime + }); + + console.log(`[GAME SERVER] Offline rewards claimed for ${clientData.username}: ${offlineRewards.rewards.credits} credits`); + } else { + socket.emit('offlineRewardsClaimed', { + success: true, + rewards: { credits: 0, experience: 0, energy: 0 }, + offlineTime: 0 + }); + } + } catch (error) { + console.error('[GAME SERVER] Error claiming offline rewards:', error); + socket.emit('offlineRewardsClaimed', { success: false, error: 'Failed to claim rewards' }); + } + }); + + // Shop purchase events + socket.on('purchaseItem', async (data) => { + const clientData = connectedClients.get(socket.id); + if (!clientData?.userId) { + socket.emit('purchaseCompleted', { success: false, error: 'Not authenticated' }); + return; + } + + try { + const { itemId, quantity = 1 } = data || {}; + if (!itemId) { + socket.emit('purchaseCompleted', { success: false, error: 'Item ID required' }); + return; + } + + const playerData = await loadPlayerData(clientData.userId, clientData.username || 'Player'); + if (!playerData) { + socket.emit('purchaseCompleted', { success: false, error: 'Failed to load player data' }); + return; + } + + // Server-side validation: item must exist and be in the shop + const item = itemSystem.findShopItem(itemId); + if (!item) { + socket.emit('purchaseCompleted', { success: false, error: 'Item not found in shop' }); + return; + } + + // Block re-purchase of already-owned cosmetics and decorations + if (item.type === 'cosmetic' && playerData.ownedCosmetics?.includes(item.id)) { + socket.emit('purchaseCompleted', { success: false, error: 'You already own this cosmetic' }); + return; + } + if (item.type === 'decoration') { + const sb = playerData.starbase || {}; + if (item.subtype === 'wallpaper' && sb.ownedWallpapers?.includes(item.id)) { + socket.emit('purchaseCompleted', { success: false, error: 'You already own this wallpaper' }); + return; + } + if (item.subtype === 'room_unlock' && item.roomId && sb.unlockedRooms?.includes(item.roomId)) { + socket.emit('purchaseCompleted', { success: false, error: 'You already own this room' }); + return; + } + } + + // Validate affordability + const validation = itemSystem.validatePurchase(item, playerData.stats, quantity); + if (!validation.valid) { + socket.emit('purchaseCompleted', { success: false, error: validation.error }); + return; + } + + // Apply purchase (deducts currency + grants item in playerData) + const purchaseSummary = itemSystem.applyPurchase(item, playerData, quantity); + + await savePlayerData(clientData.userId, playerData); + clientData.playerData = playerData; + + socket.emit('purchaseCompleted', { + success: true, + item: itemSystem.buildItemDetailResponse(item.id).item, + quantity, + totalCost: purchaseSummary.totalCost, + currency: purchaseSummary.currency, + newBalance: purchaseSummary.newBalance + }); + + console.log(`[GAME SERVER] Purchase: ${clientData.username} bought ${item.name} x${quantity} for ${purchaseSummary.totalCost} ${purchaseSummary.currency}`); + broadcastEconomyUpdate(socket.id); + + } catch (error) { + console.error('[GAME SERVER] purchaseItem error:', error); + socket.emit('purchaseCompleted', { success: false, error: 'Purchase failed: ' + error.message }); + } + }); + + // ── Starbase customisation ────────────────────────────────────────────── + socket.on('get_starbase_data', async () => { + const client = connectedClients.get(socket.id); + if (!client?.userId) { socket.emit('starbase_data', { success: false }); return; } + try { + const pd = await loadPlayerData(client.userId, client.username || 'Player'); + const sb = pd?.starbase || { wallpaper: null, ownedWallpapers: [], unlockedRooms: [] }; + const catalog = itemSystem.getAllItems().filter(i => i.type === 'decoration'); + socket.emit('starbase_data', { success: true, starbase: sb, catalog }); + } catch (err) { + console.error('[GAME SERVER] get_starbase_data error:', err); + socket.emit('starbase_data', { success: false, error: err.message }); + } + }); + + socket.on('set_wallpaper', async (data) => { + const { wallpaperId, roomId } = data || {}; + const client = connectedClients.get(socket.id); + if (!client?.userId) { socket.emit('wallpaper_set', { success: false, error: 'Not authenticated' }); return; } + try { + const pd = await loadPlayerData(client.userId, client.username || 'Player'); + if (!pd) { socket.emit('wallpaper_set', { success: false, error: 'Player not found' }); return; } + if (!pd.starbase) pd.starbase = { wallpaper: null, ownedWallpapers: [], unlockedRooms: [] }; + if (wallpaperId === null) { + if (roomId) { + if (!pd.starbase.roomWallpapers) pd.starbase.roomWallpapers = {}; + delete pd.starbase.roomWallpapers[roomId]; + } else { + pd.starbase.wallpaper = null; + } + } else { + if (!pd.starbase.ownedWallpapers?.includes(wallpaperId)) { + socket.emit('wallpaper_set', { success: false, error: 'Wallpaper not owned' }); return; + } + if (roomId) { + if (!pd.starbase.roomWallpapers) pd.starbase.roomWallpapers = {}; + pd.starbase.roomWallpapers[roomId] = wallpaperId; + } else { + pd.starbase.wallpaper = wallpaperId; + } + } + await savePlayerData(client.userId, pd); + client.playerData = pd; + socket.emit('wallpaper_set', { success: true, starbase: pd.starbase }); + } catch (err) { + console.error('[GAME SERVER] set_wallpaper error:', err); + socket.emit('wallpaper_set', { success: false, error: err.message }); + } + }); + + socket.on('get_skills', () => { + console.log('[GAME SERVER] Sending skill definitions to:', socket.id); + try { + const skills = skillSystem.getAllSkills(); + socket.emit('skills_data', skills); + } catch (error) { + console.error('[GAME SERVER] Error sending skills:', error); + socket.emit('skills_data', []); + } + }); + + socket.on('get_recipes', () => { + console.log('[GAME SERVER] Sending crafting recipes to:', socket.id); + try { + const recipes = craftingSystem.getAllRecipes(); + socket.emit('recipes_data', recipes); + } catch (error) { + console.error('[GAME SERVER] Error sending recipes:', error); + socket.emit('recipes_data', []); + } + }); + + socket.on('get_dungeons', () => { + console.log('[GAME SERVER] Sending dungeons data to:', socket.id); + const dungeons = dungeonSystem.getDungeonsGroupedByDifficulty(); + socket.emit('dungeons_data', dungeons); + }); + + socket.on('get_enemy_templates', () => { + // Client expects { [id]: enemyTemplate } — convert array to keyed object + const arr = dungeonSystem.getEnemyTemplates(); + const templates = {}; + for (const e of arr) templates[e.id] = e; + socket.emit('enemy_templates_data', templates); + }); + + // Economy System Packet Handlers + socket.on('get_economy_data', () => { + console.log('[GAME SERVER] Sending economy data to:', socket.id); + const clientData = connectedClients.get(socket.id); + if (clientData && clientData.playerData) { + const economyData = { + credits: clientData.playerData.stats.credits || 0, + gems: clientData.playerData.stats.gems || 0 + }; + socket.emit('economy_data', economyData); + } + }); + + // Function to broadcast economy updates to specific client + function broadcastEconomyUpdate(socketId) { + const clientData = connectedClients.get(socketId); + if (clientData && clientData.playerData) { + const economyData = { + credits: clientData.playerData.stats.credits || 0, + gems: clientData.playerData.stats.gems || 0 + }; + io.to(socketId).emit('economy_data', economyData); + console.log('[GAME SERVER] Broadcasted economy update to:', socketId, economyData); + } + } + + // ════════════════════════════════════════════════════════════════════════ + // BUILDINGS / BASE HANDLERS + // ════════════════════════════════════════════════════════════════════════ + + // Building definitions (in-memory; extend to JSON data later) + const BUILDING_DEFS = { + // GDD §6.2 — buildings cost metal/gas/crystal in addition to credits + command_center: { name:'Command Center', maxLevel:20, baseCost:{credits:0, metal:100 }, timeBase:60, effects:{buildSlots:1}, icon:'fa-satellite-dish', description:'Gates all other building levels.' }, + mining_facility: { name:'Mining Facility', maxLevel:20, baseCost:{credits:0, metal:300 }, timeBase:90, effects:{metalPerHr:100}, icon:'fa-industry', description:'+15% metal/hr per level.' }, + gas_extractor: { name:'Gas Extractor', maxLevel:20, baseCost:{credits:0, metal:200, gas:100 }, timeBase:90, effects:{gasPerHr:80}, icon:'fa-cloud', description:'+15% gas/hr per level.' }, + power_reactor: { name:'Power Reactor', maxLevel:20, baseCost:{credits:0, metal:400, crystal:50 }, timeBase:120, effects:{energyPerHr:200}, icon:'fa-bolt', description:'+200 energy/hr per level.' }, + shipyard: { name:'Shipyard', maxLevel:15, baseCost:{credits:500, metal:1000, gas:300 }, timeBase:180, effects:{buildSpeed:10}, icon:'fa-anchor', description:'Build ships; each level +10% speed.' }, + research_lab: { name:'Research Lab', maxLevel:20, baseCost:{credits:300, metal:600, crystal:100 }, timeBase:150, effects:{researchSpeed:8}, icon:'fa-flask', description:'+8% research speed per level.' }, + defense_platform: { name:'Defense Platform', maxLevel:10, baseCost:{credits:0, metal:500, crystal:150 }, timeBase:120, effects:{baseDPS:50}, icon:'fa-shield-alt', description:'+50 auto-defense DPS per level.' }, + storage_depot: { name:'Storage Depot', maxLevel:15, baseCost:{credits:0, metal:200 }, timeBase:60, effects:{storageBonus:2000}, icon:'fa-warehouse', description:'+2000 storage cap per level.' }, + sensor_array: { name:'Sensor Array', maxLevel:10, baseCost:{credits:200, metal:300, crystal:50 }, timeBase:90, effects:{sensorRange:1}, icon:'fa-broadcast-tower',description:'Reveals sectors; level 7+ enables interdiction.' }, + hangar_bay: { name:'Hangar Bay', maxLevel:10, baseCost:{credits:500, metal:1500, gas:400 }, timeBase:200, effects:{fleetSlots:1}, icon:'fa-shuttle-space', description:'Level 2/4/7/10 unlock extra fleet slots.' }, + trade_port: { name:'Trade Port', maxLevel:10, baseCost:{credits:2500}, timeBase:180, effects:{tradeIncome:5}, icon:'fa-ship', description:'+5% market income per level.' }, + shield_generator: { name:'Shield Generator', maxLevel:10, baseCost:{credits:4000}, timeBase:240, effects:{shieldDuration:8}, icon:'fa-circle-notch', description:'Deployable base shield +8hrs per level.' }, + crystal_refinery: { name:'Crystal Refinery', maxLevel:20, baseCost:{credits:900}, timeBase:100, effects:{crystalPerHr:60}, icon:'fa-gem', description:'+12% crystal/hr per level.' }, + }; + + function getBuildingCost(defId, currentLevel) { + const def = BUILDING_DEFS[defId]; + if (!def) return null; + const mult = Math.pow(1.6, currentLevel); // exponential cost scaling + return { credits: Math.floor((def.baseCost.credits || 0) * mult) }; + } + + function getBuildingBuildTime(defId, currentLevel) { + const def = BUILDING_DEFS[defId]; + if (!def) return 60; + return Math.floor(def.timeBase * Math.pow(1.8, currentLevel)); + } + + socket.on('get_base_data', async () => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return socket.emit('base_data', { success: false, error: 'Not authenticated' }); + const pd = client.playerData; + // Init buildings if missing + if (!pd.buildings) pd.buildings = { command_center: { level: 1, buildQueue: null } }; + // Check for completed builds + const now = Date.now(); + let changed = false; + for (const [id, bld] of Object.entries(pd.buildings)) { + if (bld.buildQueue && now >= bld.buildQueue.completesAt) { + bld.level = (bld.level || 1) + 1; + bld.buildQueue = null; + changed = true; + socket.emit('building_upgraded', { success: true, buildingId: id, newLevel: bld.level }); + } + } + if (changed) await savePlayerData(pd.userId, pd); + const buildings = Object.entries(pd.buildings).map(([id, bld]) => { + const def = BUILDING_DEFS[id] || {}; + const nextCost = getBuildingCost(id, bld.level || 1); + const nextTime = getBuildingBuildTime(id, bld.level || 1); + return { id, name: def.name || id, level: bld.level || 1, maxLevel: def.maxLevel || 10, + icon: def.icon || 'fa-building', description: def.description || '', + buildQueue: bld.buildQueue || null, nextCost, nextTime, effects: def.effects || {} }; + }); + // Available to build (not yet built) + const available = Object.entries(BUILDING_DEFS) + .filter(([id]) => !pd.buildings[id]) + .map(([id, def]) => ({ id, name: def.name, icon: def.icon, description: def.description, + maxLevel: def.maxLevel, effects: def.effects, cost: getBuildingCost(id, 0), buildTime: getBuildingBuildTime(id, 0) })); + socket.emit('base_data', { success: true, buildings, available }); + } catch (err) { + console.error('[BASE] get_base_data error:', err); + socket.emit('base_data', { success: false, error: err.message }); + } + }); + + socket.on('construct_building', async ({ buildingId }) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const pd = client.playerData; + if (!pd.buildings) pd.buildings = {}; + if (pd.buildings[buildingId]) return socket.emit('building_constructed', { success: false, error: 'Already built' }); + const def = BUILDING_DEFS[buildingId]; + if (!def) return socket.emit('building_constructed', { success: false, error: 'Unknown building' }); + const cost = getBuildingCost(buildingId, 0); + + // Deduct credits + if ((pd.stats.credits || 0) < (cost.credits||0)) return socket.emit('building_constructed', { success: false, error: `Need ${cost.credits} credits` }); + pd.stats.credits -= (cost.credits||0); + + // Deduct resources (GDD §6.2) + resourceSystem.initResources(pd); + const resCost = {}; + if (cost.metal) resCost.metal = cost.metal; + if (cost.gas) resCost.gas = cost.gas; + if (cost.crystal) resCost.crystal = cost.crystal; + if (Object.keys(resCost).length > 0) { + try { resourceSystem.spend(pd, resCost); } catch(e) { pd.stats.credits += (cost.credits||0); return socket.emit('building_constructed',{success:false,error:e.message}); } + } + + const buildTime = getBuildingBuildTime(buildingId, 0) * 1000; + pd.buildings[buildingId] = { level: 0, buildQueue: { startedAt: Date.now(), completesAt: Date.now() + buildTime } }; + await savePlayerData(pd.userId, pd); + socket.emit('building_constructed', { success: true, buildingId, completesAt: Date.now() + buildTime }); + socket.emit('economy_data', { credits: pd.stats.credits, gems: pd.stats.gems }); + socket.emit('resource_update',{resources:pd.resources,rates:resourceSystem.getProductionRates(pd),caps:resourceSystem.getStorageCaps(pd)}); + } catch (err) { + socket.emit('building_constructed', { success: false, error: err.message }); + } + }); + + socket.on('upgrade_building', async ({ buildingId }) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const pd = client.playerData; + const bld = pd.buildings?.[buildingId]; + if (!bld) return socket.emit('building_upgraded', { success: false, error: 'Building not found' }); + if (bld.buildQueue) return socket.emit('building_upgraded', { success: false, error: 'Already building' }); + const def = BUILDING_DEFS[buildingId]; + if (bld.level >= (def?.maxLevel || 10)) return socket.emit('building_upgraded', { success: false, error: 'Already max level' }); + const cost = getBuildingCost(buildingId, bld.level); + + if ((pd.stats.credits || 0) < (cost.credits||0)) return socket.emit('building_upgraded', { success: false, error: `Need ${cost.credits} credits` }); + pd.stats.credits -= (cost.credits||0); + + resourceSystem.initResources(pd); + const resCost = {}; + if (cost.metal) resCost.metal = cost.metal; + if (cost.gas) resCost.gas = cost.gas; + if (cost.crystal) resCost.crystal = cost.crystal; + if (Object.keys(resCost).length > 0) { + try { resourceSystem.spend(pd, resCost); } catch(e) { pd.stats.credits += (cost.credits||0); return socket.emit('building_upgraded',{success:false,error:e.message}); } + } + + const buildTime = getBuildingBuildTime(buildingId, bld.level) * 1000; + bld.buildQueue = { startedAt: Date.now(), completesAt: Date.now() + buildTime }; + await savePlayerData(pd.userId, pd); + socket.emit('building_upgraded', { success: true, buildingId, completesAt: Date.now() + buildTime }); + socket.emit('economy_data', { credits: pd.stats.credits, gems: pd.stats.gems }); + socket.emit('resource_update',{resources:pd.resources,rates:resourceSystem.getProductionRates(pd),caps:resourceSystem.getStorageCaps(pd)}); + } catch (err) { + socket.emit('building_upgraded', { success: false, error: err.message }); + } + }); + + socket.on('start_dungeon', (data) => { + console.log('[GAME SERVER] Starting dungeon for:', socket.id, data); + try { + const { dungeonId, userId } = data; + + // Check if dungeon is one-time only and already completed + const dungeon = dungeonSystem.getDungeon(dungeonId); + if (dungeon && dungeon.oneTimeOnly) { + const completedDungeons = dungeonSystem.getPlayerCompletedDungeons(userId); + if (completedDungeons.includes(dungeonId)) { + socket.emit('dungeon_started', { + success: false, + error: 'This dungeon can only be completed once per character.' + }); + return; + } + } + + const instance = dungeonSystem.createInstance(dungeonId, userId, []); + socket.emit('dungeon_started', { instance }); + } catch (error) { + console.error('[GAME SERVER] Error starting dungeon:', error); + socket.emit('dungeon_started', { success: false, error: error.message }); + } + }); + + socket.on('process_encounter', (data) => { + console.log('[GAME SERVER] Processing encounter for:', socket.id, data); + try { + const { instanceId, userId } = data; + const result = dungeonSystem.startEncounter(instanceId, userId); + + // Auto-complete combat for enemies with 0 attack + if (result.encounter.enemies && result.encounter.enemies.length > 0) { + const allZeroAttack = result.encounter.enemies.every(enemy => enemy.attack === 0); + if (allZeroAttack) { + console.log('[GAME SERVER] Auto-combat: All enemies have 0 attack, completing encounter'); + const completionResult = dungeonSystem.completeEncounter(instanceId, userId, { victory: true }); + socket.emit('encounter_completed', { + success: true, + rewards: completionResult.rewards, + nextEncounter: completionResult.nextEncounter, + encounterIndex: completionResult.encounterIndex + }); + return; + } + } + + socket.emit('encounter_data', { + encounter: result.encounter, + encounterIndex: result.encounterIndex, + instance: result.instance + }); + } catch (error) { + console.error('[GAME SERVER] Error processing encounter:', error); + socket.emit('encounter_data', { success: false, error: error.message }); + } + }); + + socket.on('complete_dungeon', async (data) => { + console.log('[GAME SERVER] Completing dungeon for:', socket.id, data); + try { + const { instanceId, userId } = data; + const client = connectedClients.get(socket.id); + const result = dungeonSystem.completeDungeon(instanceId); + + // Grant item rewards to player inventory/starbase + if (result.success && result.rewards?.length && client?.userId) { + try { + const playerData = await loadPlayerData(client.userId, client.username || 'Player'); + if (playerData) { + for (const reward of result.rewards) { + const item = itemSystem.getItem(reward.itemId); + if (!item) continue; + itemSystem.applyPurchase(item, playerData, reward.quantity || 1); + } + await savePlayerData(client.userId, playerData); + client.playerData = playerData; + } + } catch (grantErr) { + console.error('[GAME SERVER] Failed to grant dungeon rewards:', grantErr.message); + } + } + + // Log to combat log (GDD §9.5) + socialSystem.addCombatLogEntry(playerData.userId, { + type: 'dungeon', outcome: 'win', + enemy: dungeon?.name || instanceId, + xpGained: rewards?.experience || 0, + timestamp: new Date() + }).catch(()=>{}); + socket.emit('dungeon_completed', { rewards: result }); + } catch (error) { + console.error('[GAME SERVER] Error completing dungeon:', error); + socket.emit('dungeon_completed', { success: false, error: error.message }); + } + }); + + socket.on('get_quests', (data) => { + console.log('[GAME SERVER] Getting quests for:', socket.id); + try { + const userId = connectedClients.get(socket.id)?.userId || 'anonymous'; + const playerQD = questSystem.getPlayerQuests(userId); + + // Build quests with per-player progress merged in + const buildList = (category) => + questSystem.getQuestsByCategory(category).map(q => { + const activeState = playerQD.active[q.id]; + return { + ...q, + status: playerQD.completed.includes(q.id) + ? 'completed' + : activeState + ? 'active' + : (q.prerequisites?.playerLevelMin || 1) <= 1 ? 'available' : 'locked', + objectives: q.objectives.map(obj => ({ + ...obj, + progress: activeState?.objectives?.[obj.id]?.progress ?? 0, + complete: activeState?.objectives?.[obj.id]?.complete ?? false, + })), + }; + }); + + socket.emit('quests_data', { + success: true, + mainQuests: buildList('main_story'), + dailyQuests: buildList('daily'), + weeklyQuests: buildList('weekly'), + monthlyQuests:buildList('monthly'), + playerState: playerQD, + }); + } catch (error) { + console.error('[GAME SERVER] Error getting quests:', error); + socket.emit('quests_data', { success: false, error: error.message }); + } + }); + + // ── quest_completed: a system or action triggers a quest completion ──── + // Note: io.on doesn't exist; use a proper socket-level handler instead. + // Clients can emit 'complete_quest' to finish a quest; the server validates & pushes result. + socket.on('complete_quest', async (data) => { + const { questId } = data || {}; + const client = connectedClients.get(socket.id); + if (!client?.userId) { + socket.emit('quest_completed', { success: false, error: 'Not authenticated' }); + return; + } + try { + const quest = questSystem.getQuest(questId); + if (!quest) { + socket.emit('quest_completed', { success: false, error: 'Quest not found' }); + return; + } + const result = questSystem.completeQuest(client.userId, questId, quest.rewards); + // Resolve reward amounts into flat numbers for the client + const rewardCredits = quest.rewards + ? quest.rewards.filter(r => r.type === 'coin').reduce((s, r) => s + (r.amount || 0), 0) + : 0; + const rewardMoney = quest.rewards + ? quest.rewards.filter(r => r.type === 'money').reduce((s, r) => s + (r.amount || 0), 0) + : 0; + // Credit the player + grant item rewards + if (client.playerData) { + if (rewardCredits > 0) { + client.playerData.stats.credits = (client.playerData.stats.credits || 0) + rewardCredits; + } + client.playerData.stats.questsCompleted = (client.playerData.stats.questsCompleted || 0) + 1; + + // Grant item rewards (decorations, consumables, etc.) + const itemRewards = quest.rewards?.filter(r => r.type === 'item') || []; + for (const reward of itemRewards) { + const item = itemSystem.getItem(reward.itemId); + if (item) itemSystem.applyPurchase(item, client.playerData, reward.quantity || 1); + } + + await savePlayerData(client.playerData.userId, client.playerData); + } + socket.emit('quest_completed', { + success: true, + questId, + questName: quest.name, + rewards: { credits: rewardCredits, money: rewardMoney, items: quest.rewards?.filter(r => r.type === 'item') || [] }, + }); + // Push updated economy to client + socket.emit('economy_data', { + credits: client.playerData?.stats?.credits || 0, + gems: client.playerData?.stats?.gems || 0, + }); + } catch (err) { + console.error('[GAME SERVER] complete_quest error:', err); + socket.emit('quest_completed', { success: false, error: err.message }); + } + }); + + + socket.on('next_room', (data) => { + console.log('[GAME SERVER] Moving to next room for:', socket.id, data); + try { + const { instanceId, userId } = data; + const result = dungeonSystem.moveToNextRoom(instanceId, userId); + socket.emit('next_room_data', result); + } catch (error) { + console.error('[GAME SERVER] Error moving to next room:', error); + socket.emit('next_room_data', { success: false, error: error.message }); + } + }); + + socket.on('get_dungeon_status', (data) => { + console.log('[GAME SERVER] Getting dungeon status for:', socket.id, data); + try { + const { userId } = data; + const instance = dungeonSystem.getPlayerInstance(userId); + if (instance) { + socket.emit('dungeon_status', { + hasActiveDungeon: true, + currentInstance: instance + }); + } else { + socket.emit('dungeon_status', { + hasActiveDungeon: false, + currentInstance: null + }); + } + } catch (error) { + console.error('[GAME SERVER] Error getting dungeon status:', error); + socket.emit('dungeon_status', { success: false, error: error.message }); + } + }); + + // ── joinServer: client emits after connecting ────────────────────────── + socket.on('joinServer', (data) => { + const { serverId, userId, username } = data || {}; + console.log(`[GAME SERVER] joinServer from ${username} (${userId}) for server ${serverId}`); + // Join a socket.io room named after the serverId so we can broadcast to it + if (serverId) socket.join(serverId); + const client = connectedClients.get(socket.id); + if (client) { client.serverId = serverId; } + // Notify room that a player joined + socket.to(serverId).emit('playerJoined', { userId, username }); + }); + + // ── getPlayerList ─────────────────────────────────────────────────────── + socket.on('getPlayerList', (data) => { + const { serverId } = data || {}; + const players = []; + for (const [sid, cd] of connectedClients) { + if (cd.serverId === serverId && cd.username) { + players.push({ userId: cd.userId, username: cd.username }); + } + } + socket.emit('playerList', { players }); + }); + + // ── chatMessage: relay to everyone in the server room ────────────────── + socket.on('chatMessage', (data) => { + const client = connectedClients.get(socket.id); + const serverId = client?.serverId; + const payload = { + userId: client?.userId || data.userId, + username: client?.username || data.username || 'Unknown', + message: data.message || '', + timestamp: Date.now(), + }; + if (serverId) { + io.to(serverId).emit('chatMessage', payload); + } else { + socket.emit('chatMessage', payload); // echo back to sender only + } + }); + + // ── updatePlayerStats: client-reported stat delta (server validates) ──── + socket.on('updatePlayerStats', async (data) => { + const client = connectedClients.get(socket.id); + if (!client?.playerData) { + socket.emit('player_stat_update', { success: false, error: 'Not authenticated' }); + return; + } + try { + const pd = client.playerData; + // Only trust non-economy fields from client; economy is server-authoritative + const allowed = ['playTime']; + for (const key of allowed) { + if (data.playerStats?.[key] !== undefined) { + pd.stats[key] = data.playerStats[key]; + } + } + await savePlayerData(pd.userId, pd); + socket.emit('player_stat_update', { success: true, stats: pd.stats }); + } catch (err) { + console.error('[GAME SERVER] updatePlayerStats error:', err); + socket.emit('player_stat_update', { success: false, error: err.message }); + } + }); + + // ── exit_dungeon: player voluntarily leaves a dungeon ────────────────── + socket.on('exit_dungeon', (data) => { + console.log('[GAME SERVER] exit_dungeon for:', socket.id); + try { + const { instanceId, userId } = data || {}; + if (instanceId) dungeonSystem.abandonInstance(instanceId); + socket.emit('dungeon_exited', { success: true }); + } catch (err) { + console.error('[GAME SERVER] exit_dungeon error:', err); + socket.emit('dungeon_exited', { success: false, error: err.message }); + } + }); + + // ── get_room_types: dungeon room type definitions ─────────────────────── + socket.on('get_room_types', () => { + socket.emit('room_types_data', dungeonSystem.roomTypes || {}); + }); + + // ════════════════════════════════════════════════════════════════════════ + // SEASON SYSTEM (GDD §20.3) + // ════════════════════════════════════════════════════════════════════════ + + socket.on('get_season', () => { + const data = seasonSystem.getCurrentSeason(); + if (data.active) { + const client = connectedClients.get(socket.id); + if (client?.playerData) data.myScore = seasonSystem.getSeasonScore(client.playerData); + } + socket.emit('season_data', data); + }); + + // ════════════════════════════════════════════════════════════════════════ + // GALAXY EVENTS (GDD §20.2) + // ════════════════════════════════════════════════════════════════════════ + + socket.on('get_galaxy_event', () => { + socket.emit('galaxy_event_data', galaxyEventSystem.getEventData()); + }); + + socket.on('claim_event_reward', async () => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const result = galaxyEventSystem.claimEventReward(client.playerData); + if (result.success) await savePlayerData(client.playerData.userId, client.playerData); + socket.emit('event_reward_result', result); + } catch(err) { socket.emit('event_reward_result',{success:false,error:err.message}); } + }); + + // ════════════════════════════════════════════════════════════════════════ + // REPUTATION HANDLERS (GDD §15.3) + // ════════════════════════════════════════════════════════════════════════ + + socket.on('get_reputation', async () => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + reputationSystem.initReputation(client.playerData); + socket.emit('reputation_data', { success:true, reputations: reputationSystem.getReputationData(client.playerData) }); + } catch(err) { socket.emit('reputation_data',{success:false,error:err.message}); } + }); + + // ════════════════════════════════════════════════════════════════════════ + // SOCIAL: FRIENDS + COMBAT LOG (GDD §17.2, §9.5) + // ════════════════════════════════════════════════════════════════════════ + + socket.on('get_friends', async () => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const data = await socialSystem.getFriendsList(client.playerData.userId, client.playerData.username, connectedClients); + socket.emit('friends_data', { success:true, ...data }); + } catch(err) { socket.emit('friends_data',{success:false,error:err.message}); } + }); + + socket.on('add_friend', async ({ username }={}) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const result = await socialSystem.sendFriendRequest(client.playerData.userId, client.playerData.username, username, connectedClients); + socket.emit('friend_request_sent', { success:true, targetName: result.targetName }); + // Notify target if online + for (const [sid, cd] of connectedClients.entries()) { + if (cd.userId === result.targetId) io.to(sid).emit('friend_request', { fromId: client.playerData.userId, fromName: client.playerData.username }); + } + } catch(err) { socket.emit('friend_request_sent',{success:false,error:err.message}); } + }); + + socket.on('accept_friend', async ({ fromId, fromName }={}) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + await socialSystem.acceptFriendRequest(client.playerData.userId, client.playerData.username, fromId, fromName); + socket.emit('friend_accepted', { success:true, friendId: fromId, friendName: fromName }); + for (const [sid, cd] of connectedClients.entries()) { + if (cd.userId === fromId) io.to(sid).emit('friend_accepted', { friendId: client.playerData.userId, friendName: client.playerData.username }); + } + } catch(err) { socket.emit('friend_accepted',{success:false,error:err.message}); } + }); + + socket.on('remove_friend', async ({ friendId }={}) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + await socialSystem.removeFriend(client.playerData.userId, friendId); + socket.emit('friend_removed', { success:true }); + } catch(err) { socket.emit('friend_removed',{success:false,error:err.message}); } + }); + + socket.on('send_gift', async ({ targetId, amount }={}) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const result = await socialSystem.sendGift(client.playerData, targetId, amount); + await savePlayerData(client.playerData.userId, client.playerData); + socket.emit('gift_sent', { success:true, amount }); + socket.emit('economy_data',{credits:client.playerData.stats.credits,gems:client.playerData.stats.gems}); + // Give credits to recipient if online + for (const [sid, cd] of connectedClients.entries()) { + if (cd.userId === targetId) { + cd.playerData.stats.credits = (cd.playerData.stats.credits||0) + amount; + io.to(sid).emit('gift_received', { fromName: client.playerData.username, amount }); + io.to(sid).emit('economy_data',{credits:cd.playerData.stats.credits,gems:cd.playerData.stats.gems}); + await savePlayerData(cd.userId, cd.playerData); + } + } + } catch(err) { socket.emit('gift_sent',{success:false,error:err.message}); } + }); + + socket.on('get_combat_log', async () => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const log = await socialSystem.getCombatLog(client.playerData.userId); + socket.emit('combat_log_data', { success:true, log }); + } catch(err) { socket.emit('combat_log_data',{success:false,error:err.message}); } + }); + + // ════════════════════════════════════════════════════════════════════════ + // PLAYER MARKET HANDLERS (GDD §14) + // ════════════════════════════════════════════════════════════════════════ + + socket.on('get_market', async ({ itemId, category } = {}) => { + try { + const listings = await marketSystem.getListings({ itemId, category }); + const tradeRes = marketSystem.getTradeableResources(); + socket.emit('market_data', { success:true, listings, tradeableResources: tradeRes }); + } catch(err) { socket.emit('market_data',{success:false,error:err.message}); } + }); + + socket.on('get_my_listings', async () => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const listings = await marketSystem.getMyListings(client.playerData.userId); + socket.emit('my_listings', { success:true, listings }); + } catch(err) { socket.emit('my_listings',{success:false,error:err.message}); } + }); + + socket.on('list_resource', async (data={}) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + resourceSystem.initResources(client.playerData); + const result = await marketSystem.listResource(client.playerData, data); + await savePlayerData(client.playerData.userId, client.playerData); + socket.emit('list_result', { success:true, listing:result.listing, listingFee:result.listingFee }); + socket.emit('resource_update',{resources:client.playerData.resources,rates:resourceSystem.getProductionRates(client.playerData),caps:resourceSystem.getStorageCaps(client.playerData)}); + } catch(err) { socket.emit('list_result',{success:false,error:err.message}); } + }); + + socket.on('list_item', async (data={}) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const result = await marketSystem.listItem(client.playerData, data); + await savePlayerData(client.playerData.userId, client.playerData); + socket.emit('list_result', { success:true, listing:result.listing, listingFee:result.listingFee }); + } catch(err) { socket.emit('list_result',{success:false,error:err.message}); } + }); + + socket.on('buy_listing', async ({ listingId }={}) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const result = await marketSystem.buyListing(client.playerData, listingId); + // Credit seller if online + for (const [sid, cd] of connectedClients.entries()) { + if (cd.userId === result.listing.sellerId) { + cd.playerData.stats.credits = (cd.playerData.stats.credits||0) + result.proceeds; + io.to(sid).emit('economy_data',{credits:cd.playerData.stats.credits,gems:cd.playerData.stats.gems}); + io.to(sid).emit('market_sale', { listingId, amount: result.proceeds }); + await savePlayerData(cd.userId, cd.playerData); + } + } + await savePlayerData(client.playerData.userId, client.playerData); + socket.emit('buy_result',{success:true,listingId,proceeds:result.proceeds}); + socket.emit('economy_data',{credits:client.playerData.stats.credits,gems:client.playerData.stats.gems}); + } catch(err) { socket.emit('buy_result',{success:false,error:err.message}); } + }); + + socket.on('cancel_listing', async ({ listingId }={}) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + await marketSystem.cancelListing(client.playerData, listingId); + await savePlayerData(client.playerData.userId, client.playerData); + socket.emit('cancel_listing_result',{success:true}); + } catch(err) { socket.emit('cancel_listing_result',{success:false,error:err.message}); } + }); + + // ════════════════════════════════════════════════════════════════════════ + // ALLIANCE HANDLERS (GDD §12) + // ════════════════════════════════════════════════════════════════════════ + + socket.on('get_alliance', async () => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const pd = client.playerData; + const allianceData = pd.allianceId ? await allianceSystem.getAllianceData(pd.allianceId) : null; + socket.emit('alliance_data', { success:true, alliance:allianceData, playerRank: pd.allianceRank, playerAllianceId: pd.allianceId }); + } catch(err) { socket.emit('alliance_data',{success:false,error:err.message}); } + }); + + socket.on('search_alliances', async ({ query }={}) => { + try { + const results = await allianceSystem.searchAlliances(query); + socket.emit('alliance_search_results', { success:true, results }); + } catch(err) { socket.emit('alliance_search_results',{success:false,error:err.message}); } + }); + + socket.on('create_alliance', async ({ name, tag, description }={}) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const alliance = await allianceSystem.createAlliance(client.playerData, { name, tag, description }); + await savePlayerData(client.playerData.userId, client.playerData); + socket.emit('alliance_created', { success:true, alliance }); + socket.emit('economy_data', { credits: client.playerData.stats.credits, gems: client.playerData.stats.gems }); + } catch(err) { socket.emit('alliance_created',{success:false,error:err.message}); } + }); + + socket.on('join_alliance', async ({ allianceId }={}) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const alliance = await allianceSystem.joinAlliance(client.playerData, allianceId); + await savePlayerData(client.playerData.userId, client.playerData); + socket.emit('alliance_joined', { success:true, alliance }); + } catch(err) { socket.emit('alliance_joined',{success:false,error:err.message}); } + }); + + socket.on('leave_alliance', async () => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + await allianceSystem.leaveAlliance(client.playerData); + await savePlayerData(client.playerData.userId, client.playerData); + socket.emit('alliance_left', { success:true }); + } catch(err) { socket.emit('alliance_left',{success:false,error:err.message}); } + }); + + socket.on('alliance_deposit', async (data={}) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const warehouse = await allianceSystem.depositWarehouse(client.playerData, data); + await savePlayerData(client.playerData.userId, client.playerData); + socket.emit('alliance_warehouse_update', { success:true, warehouse }); + socket.emit('resource_update',{resources:client.playerData.resources,rates:resourceSystem.getProductionRates(client.playerData),caps:resourceSystem.getStorageCaps(client.playerData)}); + } catch(err) { socket.emit('alliance_warehouse_update',{success:false,error:err.message}); } + }); + + socket.on('alliance_withdraw', async (data={}) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const warehouse = await allianceSystem.withdrawWarehouse(client.playerData, data); + await savePlayerData(client.playerData.userId, client.playerData); + socket.emit('alliance_warehouse_update', { success:true, warehouse }); + } catch(err) { socket.emit('alliance_warehouse_update',{success:false,error:err.message}); } + }); + + // ════════════════════════════════════════════════════════════════════════ + // FLEET MISSION HANDLERS (GDD §8.3) + // ════════════════════════════════════════════════════════════════════════ + + socket.on('get_missions', async () => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const pd = client.playerData; + // Auto-complete any finished missions + const results = missionSystem.collectMissions(pd, resourceSystem); + if (results.length > 0) { + await savePlayerData(pd.userId, pd); + for (const r of results) socket.emit('mission_completed', r); + } + socket.emit('missions_data', missionSystem.getMissionsForPlayer(pd)); + } catch(err) { console.error('[MISSIONS]', err); } + }); + + socket.on('start_mission', async ({ missionType, fleetShipIds } = {}) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const mission = missionSystem.startMission(client.playerData, { missionType, fleetShipIds }); + await savePlayerData(client.playerData.userId, client.playerData); + socket.emit('mission_started', { success: true, mission }); + socket.emit('missions_data', missionSystem.getMissionsForPlayer(client.playerData)); + } catch(err) { + socket.emit('mission_started', { success: false, error: err.message }); + } + }); + + socket.on('collect_missions', async () => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const pd = client.playerData; + const results = missionSystem.collectMissions(pd, resourceSystem); + await savePlayerData(pd.userId, pd); + socket.emit('missions_collected', { results, missions: missionSystem.getMissionsForPlayer(pd) }); + if (results.some(r => r.rewards?.metal || r.rewards?.gas)) { + socket.emit('resource_update', { resources:pd.resources, rates:resourceSystem.getProductionRates(pd), caps:resourceSystem.getStorageCaps(pd) }); + } + } catch(err) { console.error('[COLLECT_MISSIONS]', err); } + }); + + socket.on('get_faction_missions', async () => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const pd = client.playerData; + // Auto-collect completed faction missions first + const results = missionSystem.collectFactionMissions(pd, resourceSystem, reputationSystem); + if (results.length > 0) { + await savePlayerData(pd.userId, pd); + results.forEach(r => socket.emit('mission_completed', r)); + } + socket.emit('faction_missions_data', { + success: true, + available: missionSystem.getAvailableFactionMissions(pd), + active: (pd.fleetMissions||[]).filter(m => m.type==='faction'), + }); + } catch(err) { socket.emit('faction_missions_data',{success:false,error:err.message}); } + }); + + socket.on('start_faction_mission', async ({ missionId }={}) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const job = missionSystem.startFactionMission(client.playerData, missionId); + await savePlayerData(client.playerData.userId, client.playerData); + socket.emit('faction_mission_started', { success:true, job }); + } catch(err) { socket.emit('faction_mission_started',{success:false,error:err.message}); } + }); + + socket.on('recall_mission', async ({ missionId } = {}) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + missionSystem.recallMission(client.playerData, missionId); + await savePlayerData(client.playerData.userId, client.playerData); + socket.emit('missions_data', missionSystem.getMissionsForPlayer(client.playerData)); + } catch(err) { socket.emit('recall_mission_result', { success:false, error:err.message }); } + }); + + // ════════════════════════════════════════════════════════════════════════ + // RESOURCE HANDLERS (GDD §5) + // ════════════════════════════════════════════════════════════════════════ + + socket.on('get_resources', async () => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + resourceSystem.initResources(client.playerData); + // Run a tick to credit any offline production + resourceSystem.tick(client.playerData); + await savePlayerData(client.playerData.userId, client.playerData); + socket.emit('resource_update', { + resources: client.playerData.resources, + rates: resourceSystem.getProductionRates(client.playerData), + caps: resourceSystem.getStorageCaps(client.playerData), + config: resourceSystem.getConfig(), + }); + } catch (err) { + console.error('[RESOURCES] get_resources error:', err); + } + }); + + // ════════════════════════════════════════════════════════════════════════ + // SHIP CONSTRUCTION QUEUE (GDD §7.4) + // ════════════════════════════════════════════════════════════════════════ + + const SHIP_BLUEPRINTS = { + starter_cruiser: { name:'Starter Cruiser', icon:'🚀', rarity:'common', hull:100, attack:10, defense:5, speed:15, buildTime:30, metalCost:200, gasCost:50, crystalCost:0, level:1 }, + light_fighter: { name:'Light Fighter', icon:'✈', rarity:'uncommon', hull:80, attack:18, defense:3, speed:25, buildTime:60, metalCost:400, gasCost:150, crystalCost:50, level:3 }, + heavy_bomber: { name:'Heavy Bomber', icon:'💣', rarity:'uncommon', hull:160, attack:25, defense:8, speed:10, buildTime:120, metalCost:800, gasCost:200, crystalCost:100, level:5 }, + destroyer: { name:'Destroyer', icon:'⚔', rarity:'rare', hull:200, attack:30, defense:15, speed:18, buildTime:300, metalCost:1500, gasCost:500, crystalCost:300, level:8 }, + heavy_cruiser: { name:'Heavy Cruiser', icon:'🛸', rarity:'rare', hull:350, attack:40, defense:25, speed:12, buildTime:600, metalCost:3000, gasCost:1000, crystalCost:800, level:12 }, + battleship: { name:'Battleship', icon:'🌟', rarity:'epic', hull:600, attack:60, defense:45, speed:8, buildTime:1800, metalCost:8000, gasCost:3000, crystalCost:2000,level:20 }, + }; + + function getShipyardLevel(playerData) { + return playerData.buildings?.shipyard?.level || 0; + } + function getShipQueueMax(playerData) { + const sy = getShipyardLevel(playerData); + return sy >= 1 ? Math.min(3, 1 + Math.floor(sy / 3)) : 0; + } + function checkShipQueue(playerData) { + const q = playerData.shipQueue || []; + const now = Date.now(); + let changed = false; + const completed = []; + const remaining = []; + for (const job of q) { + if (job.completesAt <= now) { + // Add ship to inventory + const bp = SHIP_BLUEPRINTS[job.shipId]; + if (bp) { + if (!playerData.inventory) playerData.inventory = []; + playerData.inventory.push({ + id: job.shipId + '_' + Date.now(), + type: 'ship', shipId: job.shipId, + name: bp.name, icon: bp.icon, rarity: bp.rarity, + stats: { hull: bp.hull, maxHull: bp.hull, attack: bp.attack, defense: bp.defense, speed: bp.speed, currentHull: bp.hull } + }); + completed.push({ shipId: job.shipId, name: bp.name }); + changed = true; + } + } else { + remaining.push(job); + } + } + playerData.shipQueue = remaining; + return { changed, completed }; + } + + socket.on('get_shipyard', async () => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const pd = client.playerData; + checkShipQueue(pd); + const ships = (pd.inventory || []).filter(i => i.type === 'ship'); + socket.emit('shipyard_data', { + success: true, + blueprints: SHIP_BLUEPRINTS, + queue: pd.shipQueue || [], + ships, + shipyardLevel: getShipyardLevel(pd), + queueMax: getShipQueueMax(pd), + activeShipId: pd.stats?.activeShipId, + resources: pd.resources, + }); + } catch(err) { console.error('[SHIPYARD]', err); } + }); + + socket.on('build_ship', async ({ shipId } = {}) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const pd = client.playerData; + const bp = SHIP_BLUEPRINTS[shipId]; + if (!bp) return socket.emit('build_ship_result', { success:false, error:'Unknown blueprint' }); + + const syLevel = getShipyardLevel(pd); + if (syLevel < 1) return socket.emit('build_ship_result', { success:false, error:'Build a Shipyard first (Base → Buildings)' }); + if ((pd.stats?.level || 1) < bp.level) return socket.emit('build_ship_result', { success:false, error:`Requires Commander Level ${bp.level}` }); + + const q = pd.shipQueue || []; + const qMax = getShipQueueMax(pd); + if (q.length >= qMax) return socket.emit('build_ship_result', { success:false, error:`Queue full (${qMax} slots). Upgrade Shipyard for more.` }); + + // Deduct resources + resourceSystem.initResources(pd); + try { + resourceSystem.spend(pd, { metal: bp.metalCost, gas: bp.gasCost, crystal: bp.crystalCost }); + } catch(e) { return socket.emit('build_ship_result', { success:false, error: e.message }); } + + const now = Date.now(); + const job = { shipId, name:bp.name, icon:bp.icon, startedAt:now, completesAt: now + bp.buildTime*1000 }; + pd.shipQueue = [...q, job]; + await savePlayerData(pd.userId, pd); + + socket.emit('build_ship_result', { success:true, job, resources:pd.resources }); + socket.emit('resource_update', { resources:pd.resources, rates:resourceSystem.getProductionRates(pd), caps:resourceSystem.getStorageCaps(pd) }); + } catch(err) { console.error('[BUILD_SHIP]', err); socket.emit('build_ship_result',{success:false,error:'Server error'}); } + }); + + socket.on('cancel_ship_build', async ({ shipId, startedAt } = {}) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const pd = client.playerData; + const idx = (pd.shipQueue||[]).findIndex(j => j.shipId===shipId && j.startedAt===startedAt); + if (idx < 0) return socket.emit('cancel_ship_result',{success:false,error:'Job not found'}); + const job = pd.shipQueue[idx]; + const bp = SHIP_BLUEPRINTS[job.shipId]; + // Refund 75% + if (bp) resourceSystem.add(pd, { metal:Math.floor(bp.metalCost*.75), gas:Math.floor(bp.gasCost*.75), crystal:Math.floor(bp.crystalCost*.75) }); + pd.shipQueue.splice(idx,1); + await savePlayerData(pd.userId, pd); + socket.emit('cancel_ship_result',{success:true}); + socket.emit('resource_update',{resources:pd.resources,rates:resourceSystem.getProductionRates(pd),caps:resourceSystem.getStorageCaps(pd)}); + } catch(err) { console.error('[CANCEL_SHIP]',err); } + }); + + // ════════════════════════════════════════════════════════════════════════ + // LEADERBOARD HANDLER + // ════════════════════════════════════════════════════════════════════════ + + socket.on('get_leaderboard', async ({ category = 'level' } = {}) => { + try { + const VALID_CATEGORIES = ['level', 'credits', 'questsCompleted', 'dungeonsCleared', 'totalKills']; + if (!VALID_CATEGORIES.includes(category)) { + return socket.emit('leaderboard_data', { success: false, error: 'Invalid category' }); + } + + // Query top 20 players for the requested stat + const sortField = `stats.${category}`; + const players = await PlayerData.find( + { [`stats.${category}`]: { $exists: true, $gt: 0 } }, + { 'stats.username': 1, username: 1, [`stats.${category}`]: 1, 'stats.level': 1, updatedAt: 1 } + ) + .sort({ [sortField]: -1 }) + .limit(20) + .lean(); + + const myUserId = connectedClients.get(socket.id)?.userId; + const entries = players.map((p, i) => ({ + rank: i + 1, + username: p.username || p.stats?.username || 'Unknown Commander', + value: p.stats?.[category] || 0, + level: p.stats?.level || 1, + isMe: p.userId === myUserId || p._id?.toString() === myUserId, + })); + + socket.emit('leaderboard_data', { success: true, category, entries }); + } catch (err) { + console.error('[LEADERBOARD] get_leaderboard error:', err); + socket.emit('leaderboard_data', { success: false, error: err.message }); + } + }); + + // ════════════════════════════════════════════════════════════════════════ + // FLEET HANDLERS + // ════════════════════════════════════════════════════════════════════════ + + socket.on('get_fleet_data', async () => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return socket.emit('fleet_data', { success: false, error: 'Not authenticated' }); + // Auto-give starter ship if inventory has no ships + const ships = (client.playerData.inventory || []).filter(i => i.type === 'ship'); + if (ships.length === 0) { + const starterShip = { + id: `ship_starter_${Date.now()}`, + name: 'Starter Cruiser', + type: 'ship', + rarity: 'common', + texture: 'assets/gso/textures/ships/starter_cruiser_common.png', + stats: { attack: 10, defense: 5, speed: 10, hull: 100, maxHull: 100, currentHull: 100 }, + level: 1, equipped: false, + }; + client.playerData.inventory = client.playerData.inventory || []; + client.playerData.inventory.push(starterShip); + client.playerData.stats.activeShipId = starterShip.id; + await savePlayerData(client.playerData.userId, client.playerData); + } + const fleetData = fleetSystem.getFleetData(client.playerData); + const templates = fleetSystem.getAllShipTemplates(); + socket.emit('fleet_data', { success: true, ...fleetData, templates }); + } catch (err) { + console.error('[FLEET] get_fleet_data error:', err); + socket.emit('fleet_data', { success: false, error: err.message }); + } + }); + + socket.on('set_active_ship', async ({ shipId }) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const stats = fleetSystem.setActiveShip(client.playerData, shipId); + await savePlayerData(client.playerData.userId, client.playerData); + socket.emit('active_ship_set', { success: true, shipId, stats }); + } catch (err) { + socket.emit('active_ship_set', { success: false, error: err.message }); + } + }); + + socket.on('repair_ship', async ({ shipId }) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const credits = client.playerData.stats?.credits || 0; + const result = fleetSystem.repairShip(client.playerData, shipId, credits); + client.playerData.stats.credits -= result.repairCost; + await savePlayerData(client.playerData.userId, client.playerData); + socket.emit('ship_repaired', { success: true, shipId, repairCost: result.repairCost }); + socket.emit('economy_data', { credits: client.playerData.stats.credits, gems: client.playerData.stats.gems }); + } catch (err) { + socket.emit('ship_repaired', { success: false, error: err.message }); + } + }); + + // ════════════════════════════════════════════════════════════════════════ + // GALAXY MAP HANDLERS + // ════════════════════════════════════════════════════════════════════════ + + socket.on('get_galaxy_map', async () => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + // Seed home sector if first time + if (!client.playerData.homeSector) { + client.playerData.homeSector = '15_10'; + client.playerData.exploredSectors = ['15_10']; + await savePlayerData(client.playerData.userId, client.playerData); + } + const visibleSectors = galaxySystem.getVisibleSectors(client.playerData); + socket.emit('galaxy_map_data', { + success: true, + sectors: visibleSectors, + homeSector: client.playerData.homeSector, + gridW: galaxySystem.GRID_W, + gridH: galaxySystem.GRID_H, + }); + } catch (err) { + console.error('[GALAXY] get_galaxy_map error:', err); + socket.emit('galaxy_map_data', { success: false, error: err.message }); + } + }); + + socket.on('explore_sector', async ({ sectorId }) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const sector = galaxySystem.exploreSector(sectorId, client.playerData); + await savePlayerData(client.playerData.userId, client.playerData); + // XP reward for first explore + const xpGain = 50 + sector.threat * 10; + client.playerData.stats.experience = (client.playerData.stats.experience || 0) + xpGain; + socket.emit('sector_explored', { success: true, sector, xpGain }); + // Push updated visible sectors + const visibleSectors = galaxySystem.getVisibleSectors(client.playerData); + socket.emit('galaxy_map_data', { + success: true, + sectors: visibleSectors, + homeSector: client.playerData.homeSector, + gridW: galaxySystem.GRID_W, + gridH: galaxySystem.GRID_H, + }); + } catch (err) { + socket.emit('sector_explored', { success: false, error: err.message }); + } + }); + + // ════════════════════════════════════════════════════════════════════════ + // RESEARCH HANDLERS + // ════════════════════════════════════════════════════════════════════════ + + socket.on('get_research_data', async () => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + // Check for completed research first + const justCompleted = researchSystem.checkCompletion(client.playerData); + if (justCompleted) { + await savePlayerData(client.playerData.userId, client.playerData); + socket.emit('research_completed', { tech: justCompleted }); + } + const available = researchSystem.getAvailableResearch(client.playerData); + socket.emit('research_data', { + success: true, + research: available, + inProgress: client.playerData.research?.inProgress || null, + completed: client.playerData.research?.completed || [], + effects: client.playerData.research?.effects || {}, + }); + } catch (err) { + console.error('[RESEARCH] get_research_data error:', err); + socket.emit('research_data', { success: false, error: err.message }); + } + }); + + socket.on('start_research', async ({ techId }) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const result = researchSystem.startResearch(client.playerData, techId); + await savePlayerData(client.playerData.userId, client.playerData); + socket.emit('research_started', { success: true, tech: result.tech, completesAt: result.completesAt }); + socket.emit('economy_data', { credits: client.playerData.stats.credits, gems: client.playerData.stats.gems }); + } catch (err) { + socket.emit('research_started', { success: false, error: err.message }); + } + }); + + socket.on('cancel_research', async () => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const tech = researchSystem.cancelResearch(client.playerData); + await savePlayerData(client.playerData.userId, client.playerData); + socket.emit('research_cancelled', { success: true, tech }); + socket.emit('economy_data', { credits: client.playerData.stats.credits, gems: client.playerData.stats.gems }); + } catch (err) { + socket.emit('research_cancelled', { success: false, error: err.message }); + } + }); + + socket.on('disconnect', async () => { + console.log('[GAME SERVER] Client disconnected:', socket.id); + + const clientData = connectedClients.get(socket.id); + if (clientData) { + // Save player data before disconnect + if (clientData.playerData && clientData.userId) { + try { + // Final resource tick to capture any partial production + resourceSystem.initResources(clientData.playerData); + resourceSystem.tick(clientData.playerData); + clientData.playerData.leaveServer(); + await savePlayerData(clientData.userId, clientData.playerData); + console.log(`[GAME SERVER] Saved data for ${clientData.username} on disconnect`); + } catch (error) { + console.error('[GAME SERVER] Error saving data on disconnect:', error); + } + } + + // Handle game instance cleanup + if (clientData.instanceId) { + const instance = gameInstances.get(clientData.instanceId); + if (instance) { + instance.players.delete(socket.id); + + // Clean up empty instances + if (instance.players.size === 0) { + gameInstances.delete(clientData.instanceId); + } + + // Notify other players + socket.to(clientData.instanceId).emit('playerLeftInstance', { + playerId: socket.id, + playerCount: instance.players.size + }); + } + } + } + + connectedClients.delete(socket.id); + + // Update player count on API server + updatePlayerCountOnAPI(); + }); +}); + +// Load player data from database +async function loadPlayerData(userId, username) { + try { + // Check if database is connected + if (mongoose.connection.readyState !== 1) { + console.error('[GAME SERVER] Database not connected, cannot load player data'); + return null; + } + + let playerData = await PlayerData.findOne({ userId }); + + if (!playerData) { + // Create new player data with initialized systems + console.log(`[GAME SERVER] Creating new player data for ${username} with system initialization`); + + // Initialize player data in all systems + const questData = questSystem.initializePlayerData(userId); + const skillData = skillSystem.initializePlayerData(userId); + const craftingData = craftingSystem.initializePlayerData(userId); + + // For dungeons, initialize with available dungeons + const availableDungeons = dungeonSystem.getAllDungeons(); + const dungeonData = { + availableDungeons: availableDungeons.map(d => ({ + id: d.id, + name: d.name, + difficulty: d.difficulty, + minLevel: d.minLevel || 1, + maxPlayers: d.maxPlayers || 4, + description: d.description + })), + completedDungeons: [], + currentInstance: null, + dungeonProgress: {} + }; + + playerData = new PlayerData({ + userId, + username, + resources: { metal: 500, gas: 200, crystal: 100, energyCells: 300, darkMatter: 0, lastTick: Date.now() }, + stats: { + level: 1, + experience: 0, + totalExperience: 0, + credits: 1000, + gems: 50, // Give some starting gems + skillPoints: 0, + totalKills: 0, + dungeonsCleared: 0, + questsCompleted: 0, + playTime: 0, + lastLogin: new Date() + }, + // Initialize with system data + quests: { + active: Array.from(questData.activeQuests.values()), + completed: Array.from(questData.completedQuests.keys()) + }, + skills: skillData, + dungeonSystem: dungeonData, + crafting: craftingData + }); + await playerData.save(); + console.log(`[GAME SERVER] Created new player data for ${username} with ${questData.activeQuests.size} active quests`); + } else { + // Auto-migration for existing players - check for missing data + console.log(`[GAME SERVER] Loading existing player data for ${username} (Level ${playerData.stats.level})`); + + let migrated = false; + let migrationLog = []; + + // Always categorize quests to ensure proper format + console.log(`[GAME SERVER] Categorizing quests for ${username}`); + console.log(`[GAME SERVER] Current playerData.quests before categorization:`, playerData.quests); + + const questData = questSystem.getPlayerData(userId); + console.log(`[GAME SERVER] QuestSystem data:`, { + activeQuestsCount: questData.activeQuests.size, + completedQuestsCount: questData.completedQuests.size, + activeQuests: Array.from(questData.activeQuests.values()) + }); + + // Categorize quests by type for client + const activeQuests = Array.from(questData.activeQuests.values()); + const mainQuests = activeQuests.filter(quest => quest.type === 'main'); + const dailyQuests = activeQuests.filter(quest => quest.type === 'daily'); + const weeklyQuests = activeQuests.filter(quest => quest.type === 'weekly'); + const tutorialQuests = activeQuests.filter(quest => quest.type === 'tutorial'); + + playerData.quests = { + main: mainQuests, + daily: dailyQuests, + weekly: weeklyQuests, + tutorial: tutorialQuests, + active: activeQuests, + completed: Array.from(questData.completedQuests.keys()) + }; + + console.log(`[GAME SERVER] Quest categorization complete:`, { + main: mainQuests.length, + daily: dailyQuests.length, + weekly: weeklyQuests.length, + tutorial: tutorialQuests.length, + total: activeQuests.length + }); + + console.log(`[GAME SERVER] Final playerData.quests after categorization:`, playerData.quests); + + // IMPORTANT: Save the updated quest data to database + try { + await savePlayerData(userId, playerData); + console.log(`[GAME SERVER] Saved categorized quest data for ${username}`); + } catch (error) { + console.error(`[GAME SERVER] Failed to save quest data for ${username}:`, error); + } + + // Migrate skills if missing or empty + if (!playerData.skills || Object.keys(playerData.skills).length === 0) { + console.log(`[GAME SERVER] Migrating skills for ${username}`); + const skillData = skillSystem.initializePlayerData(userId); + playerData.skills = skillData; + migrated = true; + migrationLog.push('Added skill data'); + } + + // Migrate dungeons if missing or empty + if (!playerData.dungeonSystem || !playerData.dungeonSystem.availableDungeons || + (playerData.dungeonSystem.availableDungeons && playerData.dungeonSystem.availableDungeons.length === 0)) { + console.log(`[GAME SERVER] Migrating dungeons for ${username}`); + const availableDungeons = dungeonSystem.getAllDungeons(); + playerData.dungeonSystem = { + availableDungeons: availableDungeons.map(d => ({ + id: d.id, + name: d.name, + difficulty: d.difficulty, + minLevel: d.minLevel || 1, + maxPlayers: d.maxPlayers || 4, + description: d.description + })), + completedDungeons: [], + currentInstance: null, + dungeonProgress: {} + }; + migrated = true; + migrationLog.push(`Added ${availableDungeons.length} available dungeons`); + } + + // Migrate crafting if missing or empty (this field might not exist at all) + if (!playerData.crafting || Object.keys(playerData.crafting).length === 0) { + console.log(`[GAME SERVER] Migrating crafting for ${username}`); + const craftingData = craftingSystem.initializePlayerData(userId); + playerData.crafting = craftingData; + migrated = true; + migrationLog.push('Added crafting data'); + } + + // Initialize idle system data + const idleData = idleSystem.initializePlayerData(userId); + if (!playerData.idleSystem) { + playerData.idleSystem = { + lastActive: new Date().toISOString(), + totalOfflineTime: 0, + totalIdleCredits: 0 + }; + migrated = true; + migrationLog.push('Added idle system data'); + } + + // Save if migration occurred + if (migrated) { + await playerData.save(); + console.log(`[GAME SERVER] Migration completed for ${username}: ${migrationLog.join(', ')}`); + } + + // Update existing player login info + playerData.username = username; + playerData.lastLogin = new Date(); + await playerData.save(); + } + + return playerData; + } catch (error) { + console.error('[GAME SERVER] Error loading player data:', error); + return null; + } +} + +// Save player data to database +async function savePlayerData(userId, playerData) { + try { + // Check if database is connected + if (mongoose.connection.readyState !== 1) { + console.error('[GAME SERVER] Database not connected, cannot save player data'); + return false; + } + + await PlayerData.findOneAndUpdate( + { userId }, + playerData, + { upsert: true, new: true } + ); + console.log(`[GAME SERVER] Saved player data for ${playerData.username}`); + return true; + } catch (error) { + console.error('[GAME SERVER] Error saving player data:', error); + return false; + } +} + +// Update player count on API server +async function updatePlayerCountOnAPI() { + try { + const apiServerUrl = 'http://localhost:3001'; + const currentPlayers = connectedClients.size; + + console.log(`[GAME SERVER] Updating player count: ${currentPlayers} players`); + + const response = await fetch(`${apiServerUrl}/api/servers/update-status/devgame-server-3002`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + currentPlayers: currentPlayers, + status: currentPlayers > 0 ? 'active' : 'waiting' + }) + }); + + if (response.ok) { + const result = await response.json(); + console.log(`[GAME SERVER] Player count updated:`, result); + } else { + console.error(`[GAME SERVER] Failed to update player count: ${response.status}`); + } + } catch (error) { + console.error('[GAME SERVER] Error updating player count:', error); + } +} + +// Start server +const PORT = process.env.PORT || 3002; + +async function startServer() { + try { + console.log('[GAME SERVER] Attempting to connect to database...'); + + // Connect to database + await connectDB(); + + console.log('[GAME SERVER] Database connection established'); + + server.listen(PORT, async () => { + console.log(`[GAME SERVER] Game server running on port ${PORT}`); + console.log(`[GAME SERVER] Socket.IO ready for multiplayer connections`); + + // Register with API server + await registerWithAPIServer(); + + // Start periodic player count updates + setInterval(updatePlayerCountOnAPI, 30000); // Update every 30 seconds + + // Start online idle rewards generation (every 10 seconds) + setInterval(async () => { + console.log('[GAME SERVER] Idle reward timer triggered - checking', connectedClients.size, 'connected clients'); + + for (const [socketId, clientData] of connectedClients.entries()) { + if (clientData.userId && clientData.playerData) { + try { + console.log('[GAME SERVER] Processing idle rewards for client:', socketId, 'user:', clientData.username); + // Update playTime for active players + const sessionTime = clientData.playerData.updatePlayTime(); + console.log(`[GAME SERVER] Updated playTime for ${clientData.username}: +${sessionTime}ms, Total: ${clientData.playerData.stats.playTime}ms`); + + // Send playTime update to client + io.to(socketId).emit('playTimeUpdated', { + playTime: clientData.playerData.stats.playTime, + sessionTime: sessionTime + }); + + const onlineRewards = idleSystem.generateOnlineIdleRewards(clientData.userId, 10000); // 10 seconds + + console.log('[GAME SERVER] Generated online rewards for', clientData.username, ':', onlineRewards); + + if (onlineRewards.credits > 0) { + // Update player data with online rewards + clientData.playerData.stats.credits = (clientData.playerData.stats.credits || 0) + onlineRewards.credits; + clientData.playerData.stats.experience = (clientData.playerData.stats.experience || 0) + onlineRewards.experience; + + console.log('[GAME SERVER] Applied idle rewards - Credits:', onlineRewards.credits, 'New balance:', clientData.playerData.stats.credits); + + // Send update to client + io.to(socketId).emit('onlineIdleRewards', { + credits: onlineRewards.credits, + experience: onlineRewards.experience, + newBalance: clientData.playerData.stats.credits, + playTime: clientData.playerData.stats.playTime + }); + + console.log('[GAME SERVER] Sent onlineIdleRewards to client:', socketId); + } else { + console.log('[GAME SERVER] No idle rewards generated for', clientData.username); + } + } catch (error) { + console.error(`[GAME SERVER] Error generating online idle rewards for ${socketId}:`, error); + } + } + } + }, 10000); // Every 10 seconds + + // ── Market Tick: expire old listings every 15 min (GDD §18.2) ──────────── + setInterval(async () => { + try { const n = await marketSystem.expireListings(); if(n>0) console.log(`[MARKET TICK] Expired ${n} listings`); } + catch(e) { console.error('[MARKET TICK]', e.message); } + }, 15 * 60 * 1000); + + // ── Auto-collect missions on login (handled in get_missions) ───────────── + + // ── Economy Tick: resource production every 60s (GDD §18.2) ──────────── + setInterval(async () => { + for (const [socketId, clientData] of connectedClients.entries()) { + if (!clientData.userId || !clientData.playerData) continue; + try { + resourceSystem.initResources(clientData.playerData); + const { produced, capped } = resourceSystem.tick(clientData.playerData); + if (Object.keys(produced).length > 0) { + io.to(socketId).emit('resource_update', { + resources: clientData.playerData.resources, + produced, + capped, + rates: resourceSystem.getProductionRates(clientData.playerData), + caps: resourceSystem.getStorageCaps(clientData.playerData), + }); + } + } catch (err) { + console.error('[ECONOMY TICK] Error:', err.message); + } + } + }, 60000); // GDD §18.2: 60-second economy tick + + }); + } catch (error) { + console.error('[GAME SERVER] Failed to start server:', error); + process.exit(1); + } +} + +// Register this GameServer with the API server +async function registerWithAPIServer() { + try { + // Force use of local API server for development + const apiServerUrl = 'http://localhost:3001'; + const gameServerUrl = 'https://dev.gameserver.galaxystrike.online'; + + const serverData = { + serverId: `devgame-server-${PORT}`, + name: 'Dev Game Server', + type: 'public', // Must be 'public' or 'private' according to model + region: 'Europe', + maxPlayers: 100, // Hardcoded to allow 100 players + currentPlayers: 0, + gameServerUrl: gameServerUrl, + owner: { + userId: 'developer', + username: 'Developer' + } + }; + + console.log(`[GAME SERVER] Registering with API server at ${apiServerUrl}`); + console.log(`[GAME SERVER] Server data:`, serverData); + + const response = await fetch(`${apiServerUrl}/api/servers/register`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(serverData) + }); + + if (response.ok) { + const result = await response.json(); + console.log(`[GAME SERVER] Successfully registered with API server:`, result); + } else { + const errorText = await response.text(); + console.error(`[GAME SERVER] Failed to register with API server: ${response.status}`); + console.error(`[GAME SERVER] Error response:`, errorText); + } + } catch (error) { + console.error('[GAME SERVER] Error registering with API server:', error); + } +} + +startServer(); + +module.exports = { app, server, io, gameInstances, connectedClients }; diff --git a/Galaxy-Strike-Online-main/GameServer/services/ServerRegistrationService.js b/Galaxy-Strike-Online-main/GameServer/services/ServerRegistrationService.js new file mode 100644 index 0000000..38e9c09 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/services/ServerRegistrationService.js @@ -0,0 +1,161 @@ +const logger = require('../utils/logger'); + +class ServerRegistrationService { + constructor(gameServerUrl, apiUrl, serverName, serverRegion, maxPlayers = 10) { + this.gameServerUrl = gameServerUrl; + this.apiUrl = apiUrl; + this.serverName = serverName; + this.serverRegion = serverRegion; + this.maxPlayers = maxPlayers; + this.serverId = `gameserver_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; + this.registrationInterval = null; + this.isRegistered = false; + this.getCurrentPlayerCount = null; // Callback to get current player count + } + + setPlayerCountCallback(callback) { + this.getCurrentPlayerCount = callback; + } + + async registerWithAPI() { + try { + logger.info(`[SERVER REGISTRATION] Registering server ${this.serverId} with API at ${this.apiUrl}`); + + const currentPlayers = this.getCurrentPlayerCount ? this.getCurrentPlayerCount() : 0; + + const serverData = { + serverId: this.serverId, + name: this.serverName, + type: 'public', + region: this.serverRegion, + maxPlayers: this.maxPlayers, + currentPlayers: currentPlayers, + gameServerUrl: this.gameServerUrl, + owner: { + userId: 'system', + username: 'Game Server' + } + }; + + logger.info(`[SERVER REGISTRATION] Registering with ${currentPlayers} current players`); + + const response = await fetch(`${this.apiUrl}/api/servers/register`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(serverData) + }); + + if (response.ok) { + const result = await response.json(); + logger.info(`[SERVER REGISTRATION] Server registered successfully:`, result); + this.isRegistered = true; + return true; + } else { + const error = await response.text(); + logger.error(`[SERVER REGISTRATION] Failed to register server: ${error}`); + return false; + } + } catch (error) { + logger.error(`[SERVER REGISTRATION] Error registering server:`, error); + return false; + } + } + + async updateServerStatus(currentPlayers, status) { + if (!this.isRegistered) { + logger.warn(`[SERVER REGISTRATION] Cannot update status - server not registered`); + return false; + } + + try { + logger.info(`[SERVER REGISTRATION] Updating server ${this.serverId} status:`, { + currentPlayers, + status + }); + + const response = await fetch(`${this.apiUrl}/api/servers/update-status/${this.serverId}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ currentPlayers, status }) + }); + + if (response.ok) { + const result = await response.json(); + logger.info(`[SERVER REGISTRATION] Status updated successfully:`, result); + return true; + } else { + const error = await response.text(); + logger.error(`[SERVER REGISTRATION] Failed to update status: ${error}`); + return false; + } + } catch (error) { + logger.error(`[SERVER REGISTRATION] Error updating server status:`, error); + return false; + } + } + + async updatePlayerCount(playerCount) { + return await this.updateServerStatus(playerCount, 'active'); + } + + startHeartbeat() { + // Register immediately + this.registerWithAPI(); + + // Set up periodic registration updates (every 30 seconds) + this.registrationInterval = setInterval(async () => { + await this.registerWithAPI(); + }, 30000); + + logger.info(`[SERVER REGISTRATION] Heartbeat started for server ${this.serverId}`); + } + + stopHeartbeat() { + if (this.registrationInterval) { + clearInterval(this.registrationInterval); + this.registrationInterval = null; + logger.info(`[SERVER REGISTRATION] Heartbeat stopped for server ${this.serverId}`); + } + } + + async unregisterWithAPI() { + try { + logger.info(`[SERVER REGISTRATION] Unregistering server ${this.serverId} from API at ${this.apiUrl}`); + + const response = await fetch(`${this.apiUrl}/api/servers/unregister/${this.serverId}`, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json' + } + }); + + if (response.ok) { + const result = await response.json(); + logger.info(`[SERVER REGISTRATION] Server unregistered successfully:`, result); + this.isRegistered = false; + return true; + } else { + const error = await response.text(); + logger.error(`[SERVER REGISTRATION] Failed to unregister server: ${error}`); + return false; + } + } catch (error) { + logger.error(`[SERVER REGISTRATION] Error unregistering server:`, error); + return false; + } + } + + getServerId() { + return this.serverId; + } + + isServerRegistered() { + return this.isRegistered; + } +} + +module.exports = ServerRegistrationService; diff --git a/Galaxy-Strike-Online-main/GameServer/socket/socketHandlers.js b/Galaxy-Strike-Online-main/GameServer/socket/socketHandlers.js new file mode 100644 index 0000000..c4456ea --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/socket/socketHandlers.js @@ -0,0 +1,393 @@ +/** + * Socket Handlers - Manages real-time socket connections for game server + */ + +const jwt = require('jsonwebtoken'); +const logger = require('../utils/logger'); +const { getGameSystem } = require('../systems/GameSystem'); + +class SocketHandlers { + constructor(io, gameServers, connectedPlayers) { + this.io = io; + this.gameServers = gameServers; + this.connectedPlayers = connectedPlayers; // Track actual player connections + this.gameSystem = null; + + // Track connected users to prevent duplicate connections + this.connectedUsers = new Map(); // userId -> socket.id + this.userSockets = new Map(); // socket.id -> userId + + // Add connection cleanup interval + this.startConnectionCleanup(); + } + + startConnectionCleanup() { + // Clean up stale connections every 30 seconds + setInterval(() => { + this.cleanupStaleConnections(); + }, 30000); + } + + cleanupStaleConnections() { + logger.info(`[GAME SERVER] Checking ${this.connectedUsers.size} active connections...`); + logger.info(`[GAME SERVER] Current tracked players: ${Array.from(this.connectedPlayers)}`); + + let playersRemoved = 0; + for (const [userId, socketId] of this.connectedUsers.entries()) { + const socket = this.io.sockets.sockets.get(socketId); + if (!socket || !socket.connected) { + logger.warn(`[GAME SERVER] Cleaning up stale connection for user ${userId} (socket: ${socketId})`); + this.connectedUsers.delete(userId); + this.userSockets.delete(socketId); + if (this.connectedPlayers.has(userId)) { + this.connectedPlayers.delete(userId); + playersRemoved++; + logger.info(`[GAME SERVER] Removed stale player ${userId}. Players removed: ${playersRemoved}`); + } + } + } + + logger.info(`[GAME SERVER] Cleanup complete. Players removed: ${playersRemoved}, Total players now: ${this.connectedPlayers.size}`); + + // Update player count on API if players were removed + if (playersRemoved > 0 && this.serverRegistration) { + logger.info(`[GAME SERVER] Updating API player count after cleanup to: ${this.connectedPlayers.size}`); + this.serverRegistration.updatePlayerCount(this.connectedPlayers.size); + } + } + + async initializeGameSystem() { + const { initializeGameSystems } = require('../systems/GameSystem'); + this.gameSystem = await initializeGameSystems(); + } + + handleConnection(socket) { + logger.info(`Game Server: Socket connected - ${socket.id}`); + + // Authentication middleware + socket.use(async (packet, next) => { + try { + const token = socket.handshake.auth.token; + if (!token) { + return next(new Error('Authentication required')); + } + + // Verify JWT token + const decoded = jwt.verify(token, process.env.JWT_SECRET || 'fallback_secret'); + socket.userId = decoded.userId; + socket.email = decoded.email; + + // Check if user is already connected from another client + const existingSocketId = this.connectedUsers.get(decoded.userId); + const wasAlreadyConnected = this.connectedPlayers.has(decoded.userId); + + logger.info(`[GAME SERVER] User ${decoded.userId} (${decoded.email}) connecting. Was already connected: ${wasAlreadyConnected}, Existing socket: ${existingSocketId}`); + + if (existingSocketId && existingSocketId !== socket.id) { + logger.warn(`[GAME SERVER] User ${decoded.userId} attempting to connect from multiple clients. Disconnecting previous client.`); + logger.warn(`[GAME SERVER] Existing socket: ${existingSocketId}, New socket: ${socket.id}`); + + // Disconnect the previous client + const previousSocket = this.io.sockets.sockets.get(existingSocketId); + if (previousSocket) { + logger.info(`[GAME SERVER] Force disconnecting previous socket: ${existingSocketId}`); + previousSocket.emit('force_disconnect', { + reason: 'Another client connected with your account', + newSocketId: socket.id + }); + previousSocket.disconnect(true); + } else { + logger.warn(`[GAME SERVER] Previous socket ${existingSocketId} not found in active connections`); + // Clean up the stale mapping + this.connectedUsers.delete(decoded.userId); + this.userSockets.delete(existingSocketId); + if (!wasAlreadyConnected) { + this.connectedPlayers.delete(decoded.userId); + } + } + } else { + logger.info(`[GAME SERVER] New connection for user ${decoded.userId} (socket: ${socket.id})`); + } + + // Store user connection + this.connectedUsers.set(decoded.userId, socket.id); + this.userSockets.set(socket.id, decoded.userId); + + // Add to connected players tracking only if not already there + const wasNotAlreadyConnected = !this.connectedPlayers.has(decoded.userId); + this.connectedPlayers.add(decoded.userId); + + logger.info(`[GAME SERVER] User ${decoded.userId} added to tracking. Was not already connected: ${wasNotAlreadyConnected}, Total players: ${this.connectedPlayers.size}`); + + // Update player count on API only if this is a new unique user + if (this.serverRegistration && wasNotAlreadyConnected) { + logger.info(`[GAME SERVER] Updating API player count to: ${this.connectedPlayers.size}`); + this.serverRegistration.updatePlayerCount(this.connectedPlayers.size); + } + + next(); + } catch (error) { + logger.error(`Socket authentication failed: ${error.message}`); + socket.emit('authError', { error: 'Authentication failed' }); + socket.disconnect(); + } + }); + + // Event handlers + socket.on('joinServer', (data) => this.handleJoinServer(socket, data)); + socket.on('leaveServer', (data) => this.handleLeaveServer(socket, data)); + socket.on('gameAction', (data) => this.handleGameAction(socket, data)); + socket.on('chatMessage', (data) => this.handleChatMessage(socket, data)); + socket.on('getPlayerList', (data) => this.handleGetPlayerList(socket, data)); + socket.on('disconnect', () => this.handleDisconnect(socket)); + } + + async handleJoinServer(socket, data) { + try { + const { serverId, userId, username } = data; + + // Verify user matches socket authentication + if (socket.userId !== userId) { + socket.emit('error', { message: 'User authentication mismatch' }); + return; + } + + // Create or get game instance + let gameInstance = this.gameSystem.getGameInstance(serverId); + if (!gameInstance) { + // This should ideally be handled by the API server + // But for now, create a basic game instance + gameInstance = this.gameSystem.createGameInstance({ + id: serverId, + name: `Game ${serverId}`, + type: 'public', + region: 'us-east', + maxPlayers: 10 + }); + } + + // Join player to game instance + const playerData = { + userId: userId, + username: username, + currentShip: data.currentShip, + stats: data.stats + }; + + const joinedGame = this.gameSystem.joinGameInstance(socket, serverId, playerData); + + if (joinedGame) { + // Notify player of successful join + socket.emit('joinedServer', { + serverId: serverId, + gameInstance: { + id: joinedGame.id, + name: joinedGame.name + } + }); + + // Notify other players + socket.to(`game_${gameId}`).emit('playerJoined', { + userId: socket.userId, + username: socket.email || 'Player' + }); + + logger.info(`Player ${socket.userId} joined game ${gameId}`); + } else { + socket.emit('error', { message: 'Failed to join game' }); + } + } catch (error) { + logger.error(`Join game error: ${error.message}`); + socket.emit('error', { message: 'Failed to join game' }); + } + } + + async handleLeaveGame(socket, data) { + try { + const { gameId } = data; + + const leftGame = this.gameSystem.leaveGameInstance(socket, gameId); + + if (leftGame) { + // Notify player of successful leave + socket.emit('leftGame', { gameId: gameId }); + + // Notify other players + socket.to(`game_${gameId}`).emit('playerLeft', { + userId: socket.userId, + currentPlayers: leftGame.currentPlayers + }); + + logger.info(`Player left game ${gameId}`); + } else { + socket.emit('error', { message: 'Failed to leave game' }); + } + + } catch (error) { + logger.error(`Leave game error: ${error.message}`); + socket.emit('error', { message: 'Failed to leave game' }); + } + } + + async handleGameAction(socket, data) { + try { + const { type, actionData } = data; + + // Handle game action through game system + const success = this.gameSystem.handlePlayerAction(socket, type, actionData); + + if (!success) { + socket.emit('error', { message: 'Failed to process game action' }); + } + + } catch (error) { + logger.error(`Game action error: ${error.message}`); + socket.emit('error', { message: 'Failed to process game action' }); + } + } + + async handleChatMessage(socket, data) { + try { + const { message } = data; + + // Handle chat through game system + const success = this.gameSystem.handlePlayerChat(socket, { message }); + + if (!success) { + socket.emit('error', { message: 'Failed to send chat message' }); + } + + } catch (error) { + logger.error(`Chat message error: ${error.message}`); + socket.emit('error', { message: 'Failed to send chat message' }); + } + } + + async handleGetPlayerList(socket, data) { + try { + const { serverId } = data; + this.sendPlayerList(socket, serverId); + } catch (error) { + logger.error(`Get player list error: ${error.message}`); + socket.emit('error', { message: 'Failed to get player list' }); + } + } + + async sendPlayerList(socket, serverId) { + const gameInstance = this.gameSystem.getGameInstance(serverId); + + if (gameInstance) { + const players = Array.from(gameInstance.players.values()).map(player => ({ + userId: player.userId, + username: player.username, + joinedAt: player.joinedAt, + isReady: player.isReady, + stats: player.stats + })); + + socket.emit('playerList', { + serverId: serverId, + players: players, + currentPlayers: gameInstance.currentPlayers, + maxPlayers: gameInstance.maxPlayers + }); + } else { + socket.emit('error', { message: 'Game instance not found' }); + } + } + + async handleDisconnect(socket) { + try { + logger.info(`Game Server: Socket disconnected - ${socket.id}`); + + // Get user ID from socket + const userId = this.userSockets.get(socket.id); + if (userId) { + logger.info(`[GAME SERVER] User ${userId} disconnecting (socket: ${socket.id})`); + + // Remove from tracking maps + this.connectedUsers.delete(userId); + this.userSockets.delete(socket.id); + + // Remove from connected players tracking + const wasTracked = this.connectedPlayers.has(userId); + this.connectedPlayers.delete(userId); + + logger.info(`[GAME SERVER] User ${userId} removed from tracking. Was tracked: ${wasTracked}, Total players: ${this.connectedPlayers.size}`); + + // Update player count on API only if user was being tracked + if (this.serverRegistration && wasTracked) { + logger.info(`[GAME SERVER] Updating API player count to: ${this.connectedPlayers.size}`); + this.serverRegistration.updatePlayerCount(this.connectedPlayers.size); + } + + // Get player connection info + const connection = this.gameSystem.getPlayerConnection(socket.id); + if (connection && connection.gameId) { + const gameId = connection.gameId; + + // Remove player from game + const success = this.gameSystem.removePlayerFromGame(gameId, userId); + + if (success) { + // Notify other players in the game + const game = this.gameSystem.getGame(gameId); + if (game) { + this.io.to(gameId).emit('playerLeft', { + userId, + username: socket.email || 'Unknown', + gameId, + currentPlayers: game.currentPlayers + }); + } + + logger.info(`Player ${userId} disconnected from game ${gameId}`); + } + + if (success) { + // Notify other players + this.io.to(`game_${gameId}`).emit('playerLeft', { + userId: socket.userId, + currentPlayers: game.currentPlayers + }); + logger.info(`Player ${socket.userId} disconnected from game ${gameId}`); + } + } + + logger.info(`[GAME SERVER] User ${userId} fully disconnected and cleaned up`); + } else { + logger.warn(`[GAME SERVER] Unknown socket ${socket.id} disconnected without user mapping`); + } + } catch (error) { + logger.error(`[GAME SERVER] Disconnect error: ${error.message}`); + } + } + + // Server management methods + getServerStatus() { + const gameInstances = this.gameSystem.getAllGameInstances(); + + return { + activeServers: gameInstances.length, + connectedPlayers: this.connectedPlayers.size, + }; + } + + getConnectedUsers() { + return Array.from(this.connectedUsers.keys()); + } + + getUserCount() { + return this.connectedUsers.size; + } + + broadcastToAll(event, data) { + this.io.emit(event, data); + } + + broadcastToServer(serverId, event, data) { + this.io.to(`game_${serverId}`).emit(event, data); + } +} + +module.exports = SocketHandlers; diff --git a/Galaxy-Strike-Online-main/GameServer/systems/AllianceSystem.js b/Galaxy-Strike-Online-main/GameServer/systems/AllianceSystem.js new file mode 100644 index 0000000..dd7842c --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/systems/AllianceSystem.js @@ -0,0 +1,177 @@ +/** + * Galaxy Strike Online — Alliance System (GDD §12) + * - Alliance creation (Quantum Relay level 1, 10,000 credits founding fee) + * - Ranks: Founder, Officer, Veteran, Member + * - Alliance Warehouse: shared resource pool + * - Alliance Research stubs (phase 2) + */ + +const mongoose = require('mongoose'); + +const allianceSchema = new mongoose.Schema({ + allianceId: { type: String, required: true, unique: true }, + name: { type: String, required: true, unique: true }, + tag: { type: String, required: true, unique: true, maxlength: 4 }, + founderId: { type: String, required: true }, + founderName: { type: String, default: '' }, + description: { type: String, default: '' }, + createdAt: { type: Date, default: Date.now }, + members: [{ + userId: String, + username: String, + rank: { type: String, enum: ['founder','officer','veteran','member'], default: 'member' }, + joinedAt: { type: Date, default: Date.now }, + }], + // GDD §12.3 Alliance Warehouse + warehouse: { + metal: { type: Number, default: 0 }, + gas: { type: Number, default: 0 }, + crystal: { type: Number, default: 0 }, + energyCells: { type: Number, default: 0 }, + credits: { type: Number, default: 0 }, + maxPerResource: { type: Number, default: 50000 }, + }, + warehouseLog: [{ type: mongoose.Schema.Types.Mixed }], + // Alliance research (stubbed for phase 2) + research: { type: mongoose.Schema.Types.Mixed, default: {} }, + maxMembers: { type: Number, default: 50 }, + isRecruiting: { type: Boolean, default: true }, +}, { timestamps: true }); + +const Alliance = mongoose.model('Alliance', allianceSchema); + +const RANKS = ['founder', 'officer', 'veteran', 'member']; +const RANK_PERMS = { + founder: ['kick', 'promote', 'demote', 'deposit', 'withdraw', 'edit', 'disband'], + officer: ['kick_member', 'promote_veteran', 'deposit', 'withdraw'], + veteran: ['deposit', 'withdraw_limited'], + member: ['deposit'], +}; + +class AllianceSystem { + constructor() { this.Alliance = Alliance; } + + async createAlliance(playerData, { name, tag, description = '' }) { + if (!name || name.length < 3 || name.length > 24) throw new Error('Alliance name must be 3–24 characters'); + if (!tag || tag.length < 2 || tag.length > 4) throw new Error('Alliance tag must be 2–4 characters'); + if (playerData.stats.credits < 10000) throw new Error('Founding fee is 10,000 credits'); + if (playerData.allianceId) throw new Error('You are already in an alliance'); + + // Check Quantum Relay (if building exists) + const qrLevel = playerData.buildings?.quantum_relay?.level || 0; + // Allow creation even without relay in early access — just warn + // if (qrLevel < 1) throw new Error('Quantum Relay level 1 required to found an alliance'); + + const allianceId = 'alliance_' + Date.now() + '_' + Math.random().toString(36).slice(2,6); + const alliance = new Alliance({ + allianceId, + name: name.trim(), + tag: tag.toUpperCase().trim(), + founderId: playerData.userId, + founderName: playerData.username, + description: description.trim(), + members: [{ userId: playerData.userId, username: playerData.username, rank: 'founder', joinedAt: new Date() }], + }); + await alliance.save(); + + playerData.stats.credits -= 10000; + playerData.allianceId = allianceId; + playerData.allianceTag = alliance.tag; + playerData.allianceName = alliance.name; + playerData.allianceRank = 'founder'; + return alliance; + } + + async joinAlliance(playerData, allianceId) { + if (playerData.allianceId) throw new Error('Leave your current alliance first'); + const alliance = await Alliance.findOne({ allianceId }); + if (!alliance) throw new Error('Alliance not found'); + if (!alliance.isRecruiting) throw new Error('Alliance is not recruiting'); + if (alliance.members.length >= alliance.maxMembers) throw new Error('Alliance is full'); + + alliance.members.push({ userId: playerData.userId, username: playerData.username, rank: 'member', joinedAt: new Date() }); + await alliance.save(); + + playerData.allianceId = allianceId; + playerData.allianceTag = alliance.tag; + playerData.allianceName = alliance.name; + playerData.allianceRank = 'member'; + return alliance; + } + + async leaveAlliance(playerData) { + if (!playerData.allianceId) throw new Error('Not in an alliance'); + const alliance = await Alliance.findOne({ allianceId: playerData.allianceId }); + if (!alliance) { playerData.allianceId = null; return; } + + if (alliance.founderId === playerData.userId && alliance.members.length > 1) { + throw new Error('Transfer leadership before leaving'); + } + alliance.members = alliance.members.filter(m => m.userId !== playerData.userId); + if (alliance.members.length === 0) { + await Alliance.deleteOne({ allianceId: playerData.allianceId }); + } else { + await alliance.save(); + } + playerData.allianceId = null; + playerData.allianceTag = null; + playerData.allianceName = null; + playerData.allianceRank = null; + } + + async depositWarehouse(playerData, { resource, amount }) { + if (!playerData.allianceId) throw new Error('Not in an alliance'); + if (!['metal','gas','crystal','energyCells','credits'].includes(resource)) throw new Error('Invalid resource'); + amount = Math.floor(amount); + if (amount <= 0) throw new Error('Amount must be positive'); + + const alliance = await Alliance.findOne({ allianceId: playerData.allianceId }); + if (!alliance) throw new Error('Alliance not found'); + + // Deduct from player + const resKey = resource === 'credits' ? null : resource; + if (resource === 'credits') { + if ((playerData.stats.credits || 0) < amount) throw new Error('Insufficient credits'); + playerData.stats.credits -= amount; + } else { + if (!playerData.resources || (playerData.resources[resource] || 0) < amount) throw new Error(`Insufficient ${resource}`); + playerData.resources[resource] -= amount; + } + + alliance.warehouse[resource] = Math.min((alliance.warehouse[resource] || 0) + amount, alliance.warehouse.maxPerResource); + alliance.warehouseLog.push({ userId: playerData.userId, username: playerData.username, action:'deposit', resource, amount, at: new Date() }); + if (alliance.warehouseLog.length > 200) alliance.warehouseLog = alliance.warehouseLog.slice(-200); + await alliance.save(); + return alliance.warehouse; + } + + async withdrawWarehouse(playerData, { resource, amount }) { + if (!playerData.allianceId) throw new Error('Not in an alliance'); + const rank = playerData.allianceRank || 'member'; + if (!RANK_PERMS[rank]?.some(p => p.startsWith('withdraw'))) throw new Error('Insufficient rank to withdraw'); + + const alliance = await Alliance.findOne({ allianceId: playerData.allianceId }); + if (!alliance) throw new Error('Alliance not found'); + amount = Math.floor(amount); + if ((alliance.warehouse[resource] || 0) < amount) throw new Error('Insufficient in warehouse'); + + alliance.warehouse[resource] -= amount; + if (resource === 'credits') playerData.stats.credits = (playerData.stats.credits || 0) + amount; + else { if (!playerData.resources) playerData.resources = {}; playerData.resources[resource] = (playerData.resources[resource]||0) + amount; } + alliance.warehouseLog.push({ userId: playerData.userId, username: playerData.username, action:'withdraw', resource, amount, at: new Date() }); + await alliance.save(); + return alliance.warehouse; + } + + async getAllianceData(allianceId) { + return Alliance.findOne({ allianceId }).lean(); + } + + async searchAlliances(query = '') { + const q = query.trim(); + const filter = q ? { $or: [{ name: new RegExp(q,'i') }, { tag: new RegExp(q,'i') }] } : {}; + return Alliance.find(filter, { name:1, tag:1, description:1, allianceId:1, 'members':1, isRecruiting:1, maxMembers:1 }).limit(20).lean(); + } +} + +module.exports = { AllianceSystem, Alliance }; diff --git a/Galaxy-Strike-Online-main/GameServer/systems/BaseSystem.js b/Galaxy-Strike-Online-main/GameServer/systems/BaseSystem.js new file mode 100644 index 0000000..0e15c07 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/systems/BaseSystem.js @@ -0,0 +1,512 @@ +/** + * Galaxy Strike Online - Server Base System + * Manages player base building and customization + */ + +class BaseSystem { + constructor() { + this.playerBases = new Map(); // userId -> base data + + // Room types + this.roomTypes = { + command_center: { + name: 'Command Center', + description: 'Central control room for your operations', + size: 'large', + powerCost: 20, + storageBonus: 0, + buildCost: 0, + requiredLevel: 1, + maxLevel: 1, + icon: 'fa-satellite-dish' + }, + barracks: { + name: 'Barracks', + description: 'Housing for crew and allies', + size: 'medium', + powerCost: 10, + storageBonus: 100, + buildCost: 500, + requiredLevel: 2, + maxLevel: 5, + icon: 'fa-home' + }, + laboratory: { + name: 'Laboratory', + description: 'Research facility for new technologies', + size: 'medium', + powerCost: 15, + storageBonus: 50, + buildCost: 1000, + requiredLevel: 3, + maxLevel: 5, + icon: 'fa-flask' + }, + workshop: { + name: 'Workshop', + description: 'Crafting and equipment modification station', + size: 'medium', + powerCost: 12, + storageBonus: 80, + buildCost: 800, + requiredLevel: 2, + maxLevel: 5, + icon: 'fa-hammer' + }, + storage_bay: { + name: 'Storage Bay', + description: 'Additional storage for resources and items', + size: 'large', + powerCost: 5, + storageBonus: 500, + buildCost: 300, + requiredLevel: 1, + maxLevel: 10, + icon: 'fa-warehouse' + }, + power_generator: { + name: 'Power Generator', + description: 'Generates power for base operations', + size: 'small', + powerCost: -25, // Generates power + storageBonus: 0, + buildCost: 400, + requiredLevel: 2, + maxLevel: 5, + icon: 'fa-bolt' + }, + defense_turret: { + name: 'Defense Turret', + description: 'Automated defense system', + size: 'small', + powerCost: 8, + storageBonus: 0, + buildCost: 600, + requiredLevel: 4, + maxLevel: 8, + icon: 'fa-crosshairs' + }, + medical_bay: { + name: 'Medical Bay', + description: 'Healing station for crew', + size: 'medium', + powerCost: 10, + storageBonus: 30, + buildCost: 750, + requiredLevel: 3, + maxLevel: 5, + icon: 'fa-heartbeat' + }, + communication_array: { + name: 'Communication Array', + description: 'Enhances communication and scanning', + size: 'large', + powerCost: 15, + storageBonus: 20, + buildCost: 1200, + requiredLevel: 5, + maxLevel: 3, + icon: 'fa-broadcast-tower' + }, + refinery: { + name: 'Refinery', + description: 'Processes raw materials into refined resources', + size: 'large', + powerCost: 20, + storageBonus: 100, + buildCost: 2000, + requiredLevel: 6, + maxLevel: 5, + icon: 'fa-industry' + } + }; + + // Decoration types + this.decorationTypes = { + trophy_case: { + name: 'Trophy Case', + description: 'Display your achievements', + buildCost: 100, + icon: 'fa-trophy' + }, + statue: { + name: 'Hero Statue', + description: 'Commemorative statue', + buildCost: 500, + icon: 'fa-monument' + }, + garden: { + name: 'Garden', + description: 'Decorative garden area', + buildCost: 200, + icon: 'fa-tree' + }, + fountain: { + name: 'Fountain', + description: 'Decorative water feature', + buildCost: 300, + icon: 'fa-water' + }, + banner: { + name: 'Banner', + description: 'Display your faction colors', + buildCost: 150, + icon: 'fa-flag' + } + }; + } + + initializePlayerBase(userId) { + if (!this.playerBases.has(userId)) { + this.playerBases.set(userId, { + name: 'Command Center Alpha', + level: 1, + experience: 0, + experienceToNext: 500, + rooms: [ + { + type: 'command_center', + level: 1, + builtAt: new Date().toISOString(), + lastUpgraded: null + } + ], + decorations: [], + power: 100, + maxPower: 100, + storage: 1000, + maxStorage: 1000, + defense: 10, + maxDefense: 10, + crewCapacity: 5, + maxCrewCapacity: 5, + researchBonus: 0, + craftingBonus: 0, + createdAt: new Date().toISOString(), + lastActive: new Date().toISOString() + }); + } + return this.playerBases.get(userId); + } + + getPlayerBase(userId) { + return this.playerBases.get(userId) || this.initializePlayerBase(userId); + } + + addExperience(userId, amount) { + const base = this.getPlayerBase(userId); + base.experience += amount; + + let levelsGained = 0; + const oldLevel = base.level; + + // Check for level up + while (base.experience >= base.experienceToNext) { + base.experience -= base.experienceToNext; + base.level += 1; + levelsGained++; + + // Update experience needed for next level + base.experienceToNext = this.getExperienceNeeded(base.level); + + // Apply level up bonuses + this.applyLevelUpBonuses(base); + } + + base.lastActive = new Date().toISOString(); + + return { + experienceGained: amount, + levelsGained, + newLevel: base.level, + experienceToNext: base.experienceToNext + }; + } + + getExperienceNeeded(level) { + // Exponential experience curve + return Math.floor(500 * Math.pow(1.5, level - 1)); + } + + applyLevelUpBonuses(base) { + // Increase base stats on level up + base.maxPower += 20; + base.maxStorage += 200; + base.maxDefense += 5; + base.maxCrewCapacity += 2; + + // Restore to max values + base.power = Math.min(base.power, base.maxPower); + base.storage = Math.min(base.storage, base.maxStorage); + base.defense = Math.min(base.defense, base.maxDefense); + base.crewCapacity = Math.min(base.crewCapacity, base.maxCrewCapacity); + } + + buildRoom(userId, roomType, level = 1) { + const base = this.getPlayerBase(userId); + const roomTemplate = this.roomTypes[roomType]; + + if (!roomTemplate) { + throw new Error('Invalid room type'); + } + + // Check requirements + if (base.level < roomTemplate.requiredLevel) { + throw new Error('Base level too low'); + } + + // Check if room already exists + const existingRoom = base.rooms.find(room => room.type === roomType); + if (existingRoom) { + throw new Error('Room already exists'); + } + + // Check power capacity + const totalPowerCost = this.calculateTotalPowerCost(base); + if (totalPowerCost + roomTemplate.powerCost > base.maxPower) { + throw new Error('Insufficient power capacity'); + } + + // Add room + const room = { + type: roomType, + level, + builtAt: new Date().toISOString(), + lastUpgraded: null + }; + + base.rooms.push(room); + this.updateBaseStats(base); + + return { + success: true, + room, + newStats: { + power: base.power, + maxPower: base.maxPower, + storage: base.storage, + maxStorage: base.maxStorage, + defense: base.defense, + maxDefense: base.maxDefense + } + }; + } + + upgradeRoom(userId, roomType) { + const base = this.getPlayerBase(userId); + const roomTemplate = this.roomTypes[roomType]; + const room = base.rooms.find(room => room.type === roomType); + + if (!room) { + throw new Error('Room not found'); + } + + if (room.level >= roomTemplate.maxLevel) { + throw new Error('Room already at max level'); + } + + // Upgrade room + room.level += 1; + room.lastUpgraded = new Date().toISOString(); + + this.updateBaseStats(base); + + return { + success: true, + room, + newStats: { + power: base.power, + maxPower: base.maxPower, + storage: base.storage, + maxStorage: base.maxStorage, + defense: base.defense, + maxDefense: base.maxDefense + } + }; + } + + demolishRoom(userId, roomType) { + const base = this.getPlayerBase(userId); + const roomIndex = base.rooms.findIndex(room => room.type === roomType); + + if (roomIndex === -1) { + throw new Error('Room not found'); + } + + const room = base.rooms[roomIndex]; + + // Cannot demolish command center + if (room.type === 'command_center') { + throw new Error('Cannot demolish command center'); + } + + // Remove room + base.rooms.splice(roomIndex, 1); + this.updateBaseStats(base); + + return { + success: true, + demolishedRoom: room + }; + } + + addDecoration(userId, decorationType) { + const base = this.getPlayerBase(userId); + const decorationTemplate = this.decorationTypes[decorationType]; + + if (!decorationTemplate) { + throw new Error('Invalid decoration type'); + } + + const decoration = { + type: decorationType, + placedAt: new Date().toISOString() + }; + + base.decorations.push(decoration); + + return { + success: true, + decoration + }; + } + + removeDecoration(userId, decorationIndex) { + const base = this.getPlayerBase(userId); + + if (decorationIndex < 0 || decorationIndex >= base.decorations.length) { + throw new Error('Invalid decoration index'); + } + + const removedDecoration = base.decorations.splice(decorationIndex, 1)[0]; + + return { + success: true, + removedDecoration + }; + } + + calculateTotalPowerCost(base) { + let totalCost = 0; + + for (const room of base.rooms) { + const roomTemplate = this.roomTypes[room.type]; + totalCost += roomTemplate.powerCost * room.level; + } + + return totalCost; + } + + updateBaseStats(base) { + // Reset to base values + base.power = 100; + base.maxPower = 100; + base.storage = 1000; + base.maxStorage = 1000; + base.defense = 10; + base.maxDefense = 10; + base.crewCapacity = 5; + base.maxCrewCapacity = 5; + base.researchBonus = 0; + base.craftingBonus = 0; + + // Apply room bonuses + for (const room of base.rooms) { + const roomTemplate = this.roomTypes[room.type]; + + base.maxPower += roomTemplate.powerCost * room.level; + base.maxStorage += roomTemplate.storageBonus * room.level; + base.maxDefense += roomTemplate.powerCost * room.level * 0.5; // Defense rooms give defense bonus + + if (room.type === 'barracks') { + base.maxCrewCapacity += 5 * room.level; + } + + if (room.type === 'laboratory') { + base.researchBonus += 10 * room.level; + } + + if (room.type === 'workshop') { + base.craftingBonus += 5 * room.level; + } + } + + // Apply level bonuses + base.maxPower += 20 * (base.level - 1); + base.maxStorage += 200 * (base.level - 1); + base.maxDefense += 5 * (base.level - 1); + base.maxCrewCapacity += 2 * (base.level - 1); + } + + getBaseStatistics(userId) { + const base = this.getPlayerBase(userId); + + return { + name: base.name, + level: base.level, + experience: base.experience, + experienceToNext: base.experienceToNext, + rooms: base.rooms.length, + decorations: base.decorations.length, + power: base.power, + maxPower: base.maxPower, + storage: base.storage, + maxStorage: base.maxStorage, + defense: base.defense, + maxDefense: base.maxDefense, + crewCapacity: base.crewCapacity, + maxCrewCapacity: base.maxCrewCapacity, + researchBonus: base.researchBonus, + craftingBonus: base.craftingBonus, + totalPowerCost: this.calculateTotalPowerCost(base), + powerEfficiency: base.maxPower > 0 ? (base.power / base.maxPower) * 100 : 0, + storageEfficiency: base.maxStorage > 0 ? (base.storage / base.maxStorage) * 100 : 0 + }; + } + + getAvailableRooms(userId) { + const base = this.getPlayerBase(userId); + const availableRooms = []; + + for (const [roomType, template] of Object.entries(this.roomTypes)) { + // Skip if already built + if (base.rooms.find(room => room.type === roomType)) { + continue; + } + + // Check level requirement + if (base.level >= template.requiredLevel) { + availableRooms.push({ + type: roomType, + ...template, + canAfford: true // In a real implementation, check player credits + }); + } + } + + return availableRooms; + } + + getRoomUpgrades(userId) { + const base = this.getPlayerBase(userId); + const upgradableRooms = []; + + for (const room of base.rooms) { + const template = this.roomTypes[room.type]; + + if (room.level < template.maxLevel) { + upgradableRooms.push({ + type: room.type, + currentLevel: room.level, + maxLevel: template.maxLevel, + nextLevelCost: template.buildCost * room.level, + template + }); + } + } + + return upgradableRooms; + } +} + +module.exports = BaseSystem; diff --git a/Galaxy-Strike-Online-main/GameServer/systems/ContentLoader.js b/Galaxy-Strike-Online-main/GameServer/systems/ContentLoader.js new file mode 100644 index 0000000..76c73a8 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/systems/ContentLoader.js @@ -0,0 +1,305 @@ +/** + * Galaxy Strike Online — ContentLoader + * Single source of truth for all game content. + * Reads from data/gso/ JSON files at startup; never hardcodes data. + * + * Usage: + * const loader = new ContentLoader(); + * await loader.load(); + * const skills = loader.getAllSkills(); + * const recipes = loader.getAllRecipes(); + * // etc. + */ + +const fs = require('fs'); +const path = require('path'); + +const DATA_ROOT = path.join(__dirname, '..', 'data', 'gso'); + +class ContentLoader { + constructor() { + // Raw maps — populated by load() + this.skills = new Map(); // id -> skill + this.enemies = new Map(); // id -> enemy + this.dungeons = new Map(); // id -> dungeon + this.recipes = new Map(); // id -> recipe + this.items = new Map(); // id -> item + this.quests = new Map(); // id -> quest + this.craftingTabs = []; + this.questCategories = new Map(); // category -> category index + + this._loaded = false; + } + + // ───────────────────────────────────────────────────────────────── + // Public bootstrap + // ───────────────────────────────────────────────────────────────── + + async load() { + if (this._loaded) return; + console.log('[CONTENT LOADER] Loading all game content from data/gso/…'); + + this._loadSkills(); + this._loadEnemies(); + this._loadDungeons(); + this._loadItems(); + this._loadRecipes(); + this._loadQuests(); + + this._loaded = true; + + console.log('[CONTENT LOADER] Done.'); + console.log(` Skills: ${this.skills.size}`); + console.log(` Enemies: ${this.enemies.size}`); + console.log(` Dungeons: ${this.dungeons.size}`); + console.log(` Items: ${this.items.size}`); + console.log(` Recipes: ${this.recipes.size}`); + console.log(` Quests: ${this.quests.size}`); + } + + // ───────────────────────────────────────────────────────────────── + // Skills + // ───────────────────────────────────────────────────────────────── + + _loadSkills() { + const root = path.join(DATA_ROOT, 'skills'); + for (const cat of this._subdirs(root)) { + for (const file of this._jsonFiles(path.join(root, cat))) { + const skill = this._readJson(path.join(root, cat, file)); + if (skill && skill.id) { + this.skills.set(skill.id, skill); + } + } + } + } + + getAllSkills() { return Array.from(this.skills.values()); } + getSkill(id) { return this.skills.get(id) || null; } + getSkillsByCategory(cat) { return this.getAllSkills().filter(s => s.category === cat); } + + // ───────────────────────────────────────────────────────────────── + // Enemies + // ───────────────────────────────────────────────────────────────── + + _loadEnemies() { + const root = path.join(DATA_ROOT, 'enemies'); + for (const file of this._jsonFiles(root)) { + const enemy = this._readJson(path.join(root, file)); + if (enemy && enemy.id) this.enemies.set(enemy.id, enemy); + } + } + + getAllEnemies() { return Array.from(this.enemies.values()); } + getEnemy(id) { return this.enemies.get(id) || null; } + getEnemiesByRarity(r) { return this.getAllEnemies().filter(e => e.rarity === r); } + + // ───────────────────────────────────────────────────────────────── + // Dungeons + // ───────────────────────────────────────────────────────────────── + + _loadDungeons() { + const root = path.join(DATA_ROOT, 'dungeons'); + for (const file of this._jsonFiles(root)) { + const dungeon = this._readJson(path.join(root, file)); + if (dungeon && dungeon.id) this.dungeons.set(dungeon.id, dungeon); + } + } + + getAllDungeons() { return Array.from(this.dungeons.values()); } + getDungeon(id) { return this.dungeons.get(id) || null; } + getDungeonsByDifficulty(d) { return this.getAllDungeons().filter(dn => dn.difficulty === d); } + getDungeonsGroupedByDifficulty() { + const order = ['easy','medium','hard','extreme']; + const out = {}; + for (const dn of this.getAllDungeons()) { + if (!out[dn.difficulty]) out[dn.difficulty] = []; + out[dn.difficulty].push(dn); + } + return out; + } + getAvailableDungeons(playerLevel = 1) { + return this.getAllDungeons().filter(d => d.minLevel <= playerLevel); + } + + // ───────────────────────────────────────────────────────────────── + // Items — multi-type, multi-subdir + // ───────────────────────────────────────────────────────────────── + + _loadItems() { + const root = path.join(DATA_ROOT, 'items'); + this._walkJsonDir(root, (filePath) => { + const data = this._readJson(filePath); + if (!data || !data.templates) return; + + // Each file has { templates: { : { ...itemData } } } + for (const [type, itemData] of Object.entries(data.templates)) { + if (itemData && itemData.id) { + this.items.set(itemData.id, { ...itemData, type }); + } + } + }); + } + + getAllItems() { return Array.from(this.items.values()); } + getItem(id) { return this.items.get(id) || null; } + getItemsByType(type) { return this.getAllItems().filter(i => i.type === type); } + getShopItems() { + return this.getAllItems().filter(i => + Array.isArray(i.categories) && i.categories.includes('shop') + ); + } + getShopItemsByCategory() { + const out = {}; + for (const item of this.getShopItems()) { + const t = item.type; + if (!out[t]) out[t] = []; + out[t].push(item); + } + return out; + } + + // ───────────────────────────────────────────────────────────────── + // Recipes + // ───────────────────────────────────────────────────────────────── + + _loadRecipes() { + const root = path.join(DATA_ROOT, 'recipes'); + + // crafting_tabs.json + const tabsFile = path.join(root, 'crafting_tabs.json'); + if (fs.existsSync(tabsFile)) { + this.craftingTabs = this._readJson(tabsFile)?.craftingTypes || []; + } + + // One JSON per recipe, grouped into subdirs + this._walkJsonDir(root, (filePath) => { + if (filePath.endsWith('crafting_tabs.json')) return; + if (filePath.endsWith('manifest.json')) return; + + const data = this._readJson(filePath); + if (!data || !data.recipe) return; + + // Derive a stable ID from the craft.id field or the path + const id = data.craft?.id || data.path?.replace(/\//g, ':') || path.basename(filePath, '.json'); + + // Normalise inputs → array format expected by client + const inputs = data.recipe.inputs || {}; + const materials = Object.entries(inputs).map(([itemId, qty]) => ({ id: itemId, quantity: qty })); + + // Output item id + const outputObj = data.recipe.output || {}; + const outputId = Object.keys(outputObj)[0] || null; + const outputQty = outputId ? outputObj[outputId] : 0; + + // Time — any *_time_seconds key + const timeKey = Object.keys(data.recipe).find(k => k.endsWith('_time_seconds')); + const craftingTime = timeKey ? (data.recipe[timeKey] * 1000) : 5000; + + this.recipes.set(id, { + id, + path: data.path || '', + name: outputId ? this._itemName(outputId) : id, + type: data.craft?.type || 'misc', + category: data.craft?.type || 'misc', + subCategory: data.craft?.subCategory || 'misc', + craftId: data.craft?.id || id, + materials, + output: outputId, + outputQty, + craftingTime, + experience: Math.round(craftingTime / 200), // scaled default + requirements: {}, + unlocked: false, + rawRecipe: data.recipe + }); + }); + } + + getAllRecipes() { return Array.from(this.recipes.values()); } + getRecipe(id) { return this.recipes.get(id) || null; } + getRecipesByType(type) { return this.getAllRecipes().filter(r => r.type === type); } + getRecipesByCategory(cat) { return this.getAllRecipes().filter(r => r.category === cat); } + getCraftingTabs() { return this.craftingTabs; } + + // ───────────────────────────────────────────────────────────────── + // Quests + // ───────────────────────────────────────────────────────────────── + + _loadQuests() { + const root = path.join(DATA_ROOT, 'quests'); + for (const cat of this._subdirs(root)) { + for (const file of this._jsonFiles(path.join(root, cat))) { + const quest = this._readJson(path.join(root, cat, file)); + if (quest && quest.id) this.quests.set(quest.id, quest); + } + } + + // Category index files + const catRoot = path.join(DATA_ROOT, 'category', 'quests'); + if (fs.existsSync(catRoot)) { + for (const file of this._jsonFiles(catRoot)) { + const cat = this._readJson(path.join(catRoot, file)); + if (cat && cat.tabId) this.questCategories.set(cat.tabId, cat); + } + } + } + + getAllQuests() { return Array.from(this.quests.values()); } + getQuest(id) { return this.quests.get(id) || null; } + getQuestsByCategory(cat) { return this.getAllQuests().filter(q => q.category === cat); } + getQuestCategories() { return Array.from(this.questCategories.values()); } + getQuestsGroupedByCategory() { + const out = {}; + for (const q of this.getAllQuests()) { + if (!out[q.category]) out[q.category] = []; + out[q.category].push(q); + } + return out; + } + + // ───────────────────────────────────────────────────────────────── + // Helpers + // ───────────────────────────────────────────────────────────────── + + _readJson(filePath) { + try { + return JSON.parse(fs.readFileSync(filePath, 'utf8')); + } catch (e) { + console.warn(`[CONTENT LOADER] Failed to read ${filePath}:`, e.message); + return null; + } + } + + _subdirs(dir) { + if (!fs.existsSync(dir)) return []; + return fs.readdirSync(dir).filter(f => { + try { return fs.statSync(path.join(dir, f)).isDirectory(); } catch { return false; } + }); + } + + _jsonFiles(dir) { + if (!fs.existsSync(dir)) return []; + return fs.readdirSync(dir).filter(f => f.endsWith('.json')); + } + + /** Recursively walk a directory and call cb(filePath) for every .json file */ + _walkJsonDir(dir, cb) { + if (!fs.existsSync(dir)) return; + for (const entry of fs.readdirSync(dir)) { + const full = path.join(dir, entry); + const stat = fs.statSync(full); + if (stat.isDirectory()) { + this._walkJsonDir(full, cb); + } else if (entry.endsWith('.json') && !entry.endsWith('manifest.json')) { + cb(full); + } + } + } + + /** Best-effort human name from an item id (snake_case → Title Case) */ + _itemName(id) { + return id.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase()); + } +} + +module.exports = ContentLoader; diff --git a/Galaxy-Strike-Online-main/GameServer/systems/CraftingSystem.js b/Galaxy-Strike-Online-main/GameServer/systems/CraftingSystem.js new file mode 100644 index 0000000..61e2669 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/systems/CraftingSystem.js @@ -0,0 +1,30 @@ +/** + * CraftingSystem — thin wrapper around ContentLoader for recipe definitions. + */ +class CraftingSystem { + constructor(contentLoader) { + this.loader = contentLoader; + this.playerCrafting = new Map(); + } + + getAllRecipes() { return this.loader.getAllRecipes(); } + getRecipe(id) { return this.loader.getRecipe(id); } + getRecipesByType(type) { return this.loader.getRecipesByType(type); } + getCraftingTabs() { return this.loader.getCraftingTabs(); } + + initializePlayerData(userId) { + if (this.playerCrafting.has(userId)) return this.playerCrafting.get(userId); + const data = { skill: 1, experience: 0, knownRecipes: new Set(), totalCrafted: 0 }; + this.playerCrafting.set(userId, data); + return data; + } + + getPlayerCrafting(userId) { return this.initializePlayerData(userId); } + + recordCraft(userId, recipeId) { + const pd = this.initializePlayerData(userId); + pd.totalCrafted++; + pd.knownRecipes.add(recipeId); + } +} +module.exports = CraftingSystem; diff --git a/Galaxy-Strike-Online-main/GameServer/systems/DungeonSystem.js b/Galaxy-Strike-Online-main/GameServer/systems/DungeonSystem.js new file mode 100644 index 0000000..61fccc4 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/systems/DungeonSystem.js @@ -0,0 +1,171 @@ +/** + * DungeonSystem — wraps ContentLoader for dungeon/enemy defs + manages instances. + */ +class DungeonSystem { + constructor(contentLoader) { + this.loader = contentLoader; + this.io = null; + this.instances = new Map(); // instanceId -> instance + this.playerInstances = new Map(); // userId -> instanceId + + this.roomTypes = { + entrance: { name: 'Entrance', enemies: 0, rewards: false }, + corridor: { name: 'Corridor', enemies: 1, rewards: false }, + chamber: { name: 'Chamber', enemies: 2, rewards: true }, + treasure: { name: 'Treasure Room',enemies: 0, rewards: true }, + boss: { name: 'Boss Room', enemies: 1, rewards: true, isBoss: true }, + exit: { name: 'Exit', enemies: 0, rewards: false } + }; + } + + setIO(io) { this.io = io; } + + // ── Content queries (delegated to ContentLoader) ────────────────── + getAllDungeons() { return this.loader.getAllDungeons(); } + getDungeon(id) { return this.loader.getDungeon(id); } + getDungeonsByDifficulty(d) { return this.loader.getDungeonsByDifficulty(d); } + getDungeonsGroupedByDifficulty() { return this.loader.getDungeonsGroupedByDifficulty(); } + getAvailableDungeons(lvl) { return this.loader.getAvailableDungeons(lvl); } + getEnemyTemplates() { return this.loader.getAllEnemies(); } + getEnemy(id) { return this.loader.getEnemy(id); } + + // ── Instance management ─────────────────────────────────────────── + createInstance(dungeonId, userId, _partyIds = []) { + const def = this.loader.getDungeon(dungeonId); + if (!def) return null; + + const [minR, maxR] = def.roomCount || [4, 6]; + const roomCount = Math.floor(Math.random() * (maxR - minR + 1)) + minR; + const instanceId = `${userId}_${dungeonId}_${Date.now()}`; + + const instance = { + instanceId, + dungeonId, + userId, + startTime: Date.now(), + currentRoom: 0, + totalRooms: roomCount, + rooms: this._generateRooms(def, roomCount), + completed: false, + failed: false + }; + + this.instances.set(instanceId, instance); + this.playerInstances.set(userId, instanceId); + return instance; + } + + getPlayerInstance(userId) { + const id = this.playerInstances.get(userId); + return id ? this.instances.get(id) || null : null; + } + + getPlayerCompletedDungeons(_userId) { + // Could be persisted to DB; return empty array for now + return []; + } + + startEncounter(instanceId, userId) { + const instance = this.instances.get(instanceId); + if (!instance) return { success: false, error: 'Instance not found' }; + const room = instance.rooms[instance.currentRoom]; + if (!room) return { success: false, error: 'No more rooms' }; + return { success: true, room, currentRoom: instance.currentRoom, totalRooms: instance.totalRooms }; + } + + completeEncounter(instanceId, userId, result = {}) { + const instance = this.instances.get(instanceId); + if (!instance) return { success: false, error: 'Instance not found' }; + + const room = instance.rooms[instance.currentRoom]; + if (room) room.cleared = true; + instance.currentRoom++; + + const isComplete = instance.currentRoom >= instance.totalRooms; + const rewards = isComplete ? this._generateRewards(instance.dungeonId) : []; + if (isComplete) instance.completed = true; + + return { success: true, room, rewards, completed: isComplete }; + } + + moveToNextRoom(instanceId, userId) { + const instance = this.instances.get(instanceId); + if (!instance) return { success: false, error: 'Instance not found' }; + const room = instance.rooms[instance.currentRoom]; + return { success: true, room: room || null, currentRoom: instance.currentRoom, totalRooms: instance.totalRooms }; + } + + completeDungeon(instanceId) { + const instance = this.instances.get(instanceId); + if (!instance) return { success: false, error: 'Instance not found' }; + + const rewards = this._generateRewards(instance.dungeonId); + const def = this.loader.getDungeon(instance.dungeonId); + + this.instances.delete(instanceId); + this.playerInstances.delete(instance.userId); + + return { success: true, rewards, dungeonName: def?.name, timeMs: Date.now() - instance.startTime }; + } + + // ── Internals ───────────────────────────────────────────────────── + _generateRooms(def, count) { + const rooms = []; + const enemyPool = def.enemyPool || []; + + for (let i = 0; i < count; i++) { + const isFirst = i === 0; + const isLast = i === count - 1; + const isBoss = i === count - 2 && count > 2; + + let type = 'corridor', enemies = []; + + if (isFirst) type = 'entrance'; + else if (isLast) type = 'exit'; + else if (isBoss) { + type = 'boss'; + const bossId = enemyPool[enemyPool.length - 1] || enemyPool[0]; + const tmpl = this.loader.getEnemy(bossId); + if (tmpl) enemies.push({ ...tmpl, health: Math.round(tmpl.health * 2), attack: Math.round(tmpl.attack * 1.5) }); + } else { + const roll = Math.random(); + if (roll < 0.2) type = 'treasure'; + else if (roll < 0.5) type = 'chamber'; + else type = 'corridor'; + + const ec = this.roomTypes[type]?.enemies || 0; + for (let e = 0; e < ec; e++) { + const eid = enemyPool[Math.floor(Math.random() * enemyPool.length)]; + const tmpl = this.loader.getEnemy(eid); + if (tmpl) enemies.push({ ...tmpl }); + } + } + + rooms.push({ index: i, type, enemies, cleared: false, rewards: [] }); + } + return rooms; + } + + _generateRewards(dungeonId) { + const def = this.loader.getDungeon(dungeonId); + if (!def?.lootTable) return []; + + const rewards = []; + const totalWeight = def.lootTable.reduce((s, e) => s + (e.weight || 0), 0); + const picks = Math.floor(Math.random() * 3) + 1; + + for (let i = 0; i < picks; i++) { + let roll = Math.random() * totalWeight; + for (const entry of def.lootTable) { + roll -= entry.weight; + if (roll <= 0) { + const qty = Math.floor(Math.random() * (entry.qtyMax - entry.qtyMin + 1)) + entry.qtyMin; + rewards.push({ itemId: entry.itemId, quantity: qty }); + break; + } + } + } + return rewards; + } +} +module.exports = DungeonSystem; diff --git a/Galaxy-Strike-Online-main/GameServer/systems/FleetSystem.js b/Galaxy-Strike-Online-main/GameServer/systems/FleetSystem.js new file mode 100644 index 0000000..6061c96 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/systems/FleetSystem.js @@ -0,0 +1,53 @@ +/** + * Galaxy Strike Online - Fleet System + * Manages player fleets, ship roster, and galaxy movement + */ +class FleetSystem { + constructor(contentLoader) { + this.contentLoader = contentLoader; + } + + /** Return all ship templates from ContentLoader */ + getAllShipTemplates() { + const items = this.contentLoader.getAllItems ? this.contentLoader.getAllItems() : []; + return items.filter(i => i.type === 'ship'); + } + + /** Build fleet summary from playerData */ + getFleetData(playerData) { + const ships = playerData.inventory?.filter(i => i.type === 'ship') || []; + const activeShipId = playerData.stats?.activeShipId || null; + return { + ships, + activeShipId: activeShipId || (ships[0]?.id ?? null), + maxFleetSize: 5 + Math.floor((playerData.stats?.level || 1) / 10), + }; + } + + /** Set active ship — returns updated stats block */ + setActiveShip(playerData, shipId) { + const ship = (playerData.inventory || []).find(i => i.id === shipId && i.type === 'ship'); + if (!ship) throw new Error('Ship not found in inventory'); + playerData.stats = playerData.stats || {}; + playerData.stats.activeShipId = shipId; + // Apply ship stats to player combat stats + playerData.stats.attack = (playerData.stats.baseAttack || 10) + (ship.stats?.attack || 0); + playerData.stats.defense = (playerData.stats.baseDefense || 5) + (ship.stats?.defense || 0); + playerData.stats.speed = (playerData.stats.baseSpeed || 10) + (ship.stats?.speed || 0); + return playerData.stats; + } + + /** Repair a ship (full restore of hull) */ + repairShip(playerData, shipId, creditsAvailable) { + const ship = (playerData.inventory || []).find(i => i.id === shipId && i.type === 'ship'); + if (!ship) throw new Error('Ship not found'); + const missingHull = (ship.stats?.maxHull || ship.stats?.hull || 100) - (ship.stats?.currentHull || ship.stats?.hull || 100); + const repairCost = Math.ceil(missingHull * 2); + if (creditsAvailable < repairCost) throw new Error(`Need ${repairCost} credits to repair`); + ship.stats = ship.stats || {}; + ship.stats.currentHull = ship.stats.maxHull || ship.stats.hull || 100; + return { repairCost, ship }; + } +} + +module.exports = FleetSystem; diff --git a/Galaxy-Strike-Online-main/GameServer/systems/GalaxyEventSystem.js b/Galaxy-Strike-Online-main/GameServer/systems/GalaxyEventSystem.js new file mode 100644 index 0000000..08ad2c8 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/systems/GalaxyEventSystem.js @@ -0,0 +1,122 @@ +/** + * Galaxy Strike Online — Galaxy Events (GDD §20.2) + * Types: Alien Invasion, Void Surge, Sector Domination, Rogue AI Fleet Boss + * Events rotate on a schedule and reward all participating players. + */ + +const EVENT_TYPES = { + alien_invasion: { + name: 'Alien Invasion', + icon: '👾', + desc: 'A massive alien fleet has entered the galaxy. Coordinate with other commanders to repel the threat.', + durationHours: 24, + rewardPool: { credits: 50000, xp: 2000, darkMatter: 50 }, + minParticipants: 1, + }, + void_surge: { + name: 'Void Surge', + icon: '🌀', + desc: 'Dark matter concentrations spike across multiple sectors. Mine bonus Dark Matter while the surge lasts.', + durationHours: 12, + rewardPool: { credits: 20000, xp: 1000, darkMatter: 200 }, + minParticipants: 1, + bonuses: { darkMatterMultiplier: 3 }, + }, + rogue_ai_boss: { + name: 'Rogue AI Fleet Boss', + icon: '🤖', + desc: 'The Rogue AI Collective has deployed a capital-class vessel. Destroy it for legendary loot including Dreadnought Blueprints.', + durationHours: 48, + rewardPool: { credits: 100000, xp: 5000, darkMatter: 100, loot: ['dreadnought_blueprint'] }, + minParticipants: 5, + }, + sector_domination: { + name: 'Sector Domination', + icon: '⚔', + desc: 'Alliance territory season is active. The alliance controlling the most sectors at the end wins exclusive skins and titles.', + durationHours: 720, // 30 days + rewardPool: { credits: 200000, exclusiveSkin: true }, + minParticipants: 10, + }, +}; + +class GalaxyEventSystem { + constructor() { + this.activeEvent = null; + this.participants = new Map(); // userId -> contribution score + this.eventHistory = []; + this._scheduleNext(); + } + + _scheduleNext() { + // Rotate through events every 6 hours in dev; configurable in prod + const keys = Object.keys(EVENT_TYPES); + const idx = this.eventHistory.length % keys.length; + const type = keys[idx]; + const def = EVENT_TYPES[type]; + + const now = Date.now(); + this.activeEvent = { + id: 'event_' + now, + type, + ...def, + startedAt: now, + endsAt: now + def.durationHours * 3600 * 1000, + participantCount: 0, + status: 'active', + }; + this.participants.clear(); + + // Schedule end + setTimeout(() => this._endEvent(), def.durationHours * 3600 * 1000); + console.log(`[GALAXY EVENT] Started: ${def.name} — ends in ${def.durationHours}h`); + } + + _endEvent() { + if (!this.activeEvent) return; + this.activeEvent.status = 'ended'; + this.eventHistory.push({ ...this.activeEvent, endedAt: Date.now() }); + this.activeEvent = null; + // Schedule next after 30 min break + setTimeout(() => this._scheduleNext(), 30 * 60 * 1000); + } + + participate(userId, contributionDelta = 1) { + if (!this.activeEvent || this.activeEvent.status !== 'active') return false; + const current = this.participants.get(userId) || 0; + this.participants.set(userId, current + contributionDelta); + this.activeEvent.participantCount = this.participants.size; + return true; + } + + getEventData() { + if (!this.activeEvent) return { active: false }; + const now = Date.now(); + const remaining = Math.max(0, this.activeEvent.endsAt - now); + const h = Math.floor(remaining / 3600000); + const m = Math.floor((remaining % 3600000) / 60000); + return { + active: true, + event: { + ...this.activeEvent, + remainingMs: remaining, + etaLabel: h > 0 ? `${h}h ${m}m remaining` : `${m}m remaining`, + topParticipants: [...this.participants.entries()] + .sort((a,b) => b[1]-a[1]).slice(0,10) + .map(([uid, score], i) => ({ rank: i+1, userId: uid, score })), + } + }; + } + + claimEventReward(playerData) { + const score = this.participants.get(playerData.userId) || 0; + if (score === 0) return { success: false, error: 'You have not participated in this event.' }; + const event = this.activeEvent || {}; + const pool = event.rewardPool || {}; + if (pool.credits) playerData.stats.credits = (playerData.stats.credits||0) + pool.credits; + if (pool.xp) playerData.addExperience?.(pool.xp); + return { success: true, rewards: pool }; + } +} + +module.exports = GalaxyEventSystem; diff --git a/Galaxy-Strike-Online-main/GameServer/systems/GalaxySystem.js b/Galaxy-Strike-Online-main/GameServer/systems/GalaxySystem.js new file mode 100644 index 0000000..361b8c7 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/systems/GalaxySystem.js @@ -0,0 +1,112 @@ +/** + * Galaxy Strike Online - Galaxy System + * Procedurally generates and manages sectors + */ +class GalaxySystem { + constructor() { + this.GRID_W = 30; + this.GRID_H = 20; + this.TYPES = ['empty','empty','empty','asteroid','asteroid','trade_hub','npc_territory','ruins','void_rift']; + this._sectors = null; + } + + /** Lazy-generate the galaxy grid once */ + getSectors() { + if (!this._sectors) this._sectors = this._generate(); + return this._sectors; + } + + _generate() { + const sectors = {}; + for (let y = 0; y < this.GRID_H; y++) { + for (let x = 0; x < this.GRID_W; x++) { + const id = `${x}_${y}`; + const distFromCenter = Math.hypot(x - 15, y - 10) / 18; + // Richer regions further from core + const typeRoll = Math.random(); + let type = 'empty'; + if (typeRoll > 0.70) type = 'asteroid'; + if (typeRoll > 0.88) type = 'npc_territory'; + if (typeRoll > 0.94) type = 'trade_hub'; + if (typeRoll > 0.97 && distFromCenter > 0.4) type = 'ruins'; + if (typeRoll > 0.99 && distFromCenter > 0.6) type = 'void_rift'; + + const richness = Math.random(); + sectors[id] = { + id, x, y, type, + name: this._sectorName(x, y), + threat: Math.min(10, Math.floor(distFromCenter * 10 + Math.random() * 3)), + richness: type === 'asteroid' ? (0.3 + richness * 0.7) : 0, + owner: null, + explored: false, + }; + } + } + // Always make start sector safe + sectors['15_10'] = { id:'15_10', x:15, y:10, type:'trade_hub', name:'New Haven', threat:0, richness:0, owner:null, explored:true }; + return sectors; + } + + _sectorName(x, y) { + const prefixes = ['Alpha','Beta','Gamma','Delta','Sigma','Tau','Zeta','Omega','Nova','Vega','Lyra','Cygni']; + const suffixes = ['Prime','Station','Reach','Deep','Expanse','Crossing','Rift','Gate','Void','Fields']; + const seed = (x * 31 + y * 17) % (prefixes.length * suffixes.length); + return prefixes[seed % prefixes.length] + ' ' + suffixes[Math.floor(seed / prefixes.length) % suffixes.length]; + } + + /** Returns the sector or null */ + getSector(id) { + return this.getSectors()[id] || null; + } + + /** Mark a sector explored by a player */ + exploreSector(sectorId, playerData) { + const sector = this.getSector(sectorId); + if (!sector) throw new Error('Sector not found'); + const explored = playerData.exploredSectors || []; + if (!explored.includes(sectorId)) { + explored.push(sectorId); + playerData.exploredSectors = explored; + } + return sector; + } + + /** Return list of sectors visible to player (explored + adjacent) */ + getVisibleSectors(playerData) { + const explored = new Set(playerData.exploredSectors || ['15_10']); + const visible = new Set(explored); + // Reveal adjacent sectors to all explored + for (const id of explored) { + const [x, y] = id.split('_').map(Number); + for (const [dx, dy] of [[-1,0],[1,0],[0,-1],[0,1]]) { + const nid = `${x+dx}_${y+dy}`; + if (this.getSector(nid)) visible.add(nid); + } + } + const all = this.getSectors(); + return Array.from(visible).map(id => { + const s = { ...all[id] }; + s.explored = explored.has(id); + if (!s.explored) { + // Fog: hide details of unexplored but adjacent sectors + s.name = '???'; + s.richness = 0; + s.owner = null; + } + return s; + }); + } + + /** Player claims/sets base in a sector */ + claimSector(sectorId, playerData) { + const sector = this.getSector(sectorId); + if (!sector) throw new Error('Sector not found'); + if (sector.type === 'void_rift') throw new Error('Cannot claim Void Rift sectors'); + sector.owner = playerData.userId; + this.exploreSector(sectorId, playerData); + playerData.homeSector = sectorId; + return sector; + } +} + +module.exports = GalaxySystem; diff --git a/Galaxy-Strike-Online-main/GameServer/systems/GameSystem.js b/Galaxy-Strike-Online-main/GameServer/systems/GameSystem.js new file mode 100644 index 0000000..603b0a8 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/systems/GameSystem.js @@ -0,0 +1,284 @@ +/** + * Game System - Manages game instances and real-time multiplayer logic + */ + +const logger = require('../utils/logger'); + +class GameSystem { + constructor() { + this.gameInstances = new Map(); // serverId -> game instance + this.playerConnections = new Map(); // socketId -> player data + } + + async initializeGameSystems() { + logger.info('Initializing Game Server systems...'); + + // Initialize any game systems needed + this.initializeEconomySystem(); + this.initializeDungeonSystem(); + + logger.info('Game Server systems initialized successfully'); + } + + initializeEconomySystem() { + // Economy system initialization for game server + logger.info('Economy System initialized for Game Server'); + } + + initializeDungeonSystem() { + // Dungeon system initialization for game server + logger.info('Dungeon System initialized for Game Server'); + } + + createGameInstance(serverData) { + const gameId = serverData.id; + + const gameInstance = { + id: gameId, + name: serverData.name, + type: serverData.type, + region: serverData.region, + maxPlayers: serverData.maxPlayers, + currentPlayers: 0, + players: new Map(), // socketId -> player data + gameState: { + active: false, + startTime: null, + currentDungeon: null, + enemies: new Map(), + items: new Map(), + environment: null + }, + createdAt: new Date(), + lastActivity: new Date() + }; + + this.gameInstances.set(gameId, gameInstance); + logger.info(`Game instance created: ${gameId} - ${serverData.name}`); + + return gameInstance; + } + + joinGameInstance(socket, serverId, playerData) { + const gameInstance = this.gameInstances.get(serverId); + + if (!gameInstance) { + logger.error(`Game instance not found: ${serverId}`); + return false; + } + + if (gameInstance.currentPlayers >= gameInstance.maxPlayers) { + logger.warn(`Game instance full: ${serverId}`); + return false; + } + + // Add player to game instance + gameInstance.players.set(socket.id, { + socketId: socket.id, + userId: playerData.userId, + username: playerData.username, + joinedAt: new Date(), + isReady: false, + ship: playerData.currentShip || null, + stats: playerData.stats || { level: 1, health: 100 } + }); + + // Track player connection + this.playerConnections.set(socket.id, { + gameId: serverId, + socket: socket, + data: playerData + }); + + gameInstance.currentPlayers++; + gameInstance.lastActivity = new Date(); + + // Join socket to game room + socket.join(`game_${serverId}`); + + logger.info(`Player ${playerData.username} joined game instance ${serverId}`); + + return gameInstance; + } + + leaveGameInstance(socket, serverId) { + const gameInstance = this.gameInstances.get(serverId); + + if (!gameInstance) { + logger.error(`Game instance not found: ${serverId}`); + return false; + } + + const player = gameInstance.players.get(socket.id); + if (player) { + gameInstance.players.delete(socket.id); + gameInstance.currentPlayers--; + gameInstance.lastActivity = new Date(); + + // Leave socket room + socket.leave(`game_${serverId}`); + + // Remove player connection tracking + this.playerConnections.delete(socket.id); + + logger.info(`Player ${player.username} left game instance ${serverId}`); + + // Clean up empty game instances + if (gameInstance.currentPlayers === 0) { + this.cleanupGameInstance(serverId); + } + + return true; + } + + return false; + } + + cleanupGameInstance(serverId) { + const gameInstance = this.gameInstances.get(serverId); + + if (gameInstance && gameInstance.currentPlayers === 0) { + this.gameInstances.delete(serverId); + logger.info(`Cleaned up empty game instance: ${serverId}`); + } + } + + getGameInstance(serverId) { + return this.gameInstances.get(serverId); + } + + getAllGameInstances() { + return Array.from(this.gameInstances.values()).map(instance => ({ + id: instance.id, + name: instance.name, + type: instance.type, + region: instance.region, + currentPlayers: instance.currentPlayers, + maxPlayers: instance.maxPlayers, + gameState: instance.gameState.active ? 'active' : 'waiting', + createdAt: instance.createdAt + })); + } + + getPlayerConnection(socketId) { + return this.playerConnections.get(socketId); + } + + // Game action handlers + handlePlayerAction(socket, actionType, actionData) { + const connection = this.playerConnections.get(socket.id); + if (!connection) { + logger.warn(`No connection found for socket: ${socket.id}`); + return false; + } + + const gameInstance = this.gameInstances.get(connection.gameId); + if (!gameInstance) { + logger.warn(`No game instance found for player: ${socket.id}`); + return false; + } + + const player = gameInstance.players.get(socket.id); + if (!player) { + logger.warn(`Player not found in game instance: ${socket.id}`); + return false; + } + + // Handle different action types + switch (actionType) { + case 'move': + return this.handlePlayerMove(gameInstance, player, actionData); + case 'attack': + return this.handlePlayerAttack(gameInstance, player, actionData); + case 'interact': + return this.handlePlayerInteract(gameInstance, player, actionData); + case 'chat': + return this.handlePlayerChat(gameInstance, player, actionData); + default: + logger.warn(`Unknown action type: ${actionType}`); + return false; + } + } + + handlePlayerMove(gameInstance, player, moveData) { + // Update player position + player.position = moveData.position; + gameInstance.lastActivity = new Date(); + + // Broadcast movement to other players in the game + const socket = this.playerConnections.get(player.socketId)?.socket; + if (socket) { + socket.to(`game_${gameInstance.id}`).emit('playerMoved', { + playerId: player.socketId, + username: player.username, + position: moveData.position + }); + } + + return true; + } + + handlePlayerAttack(gameInstance, player, attackData) { + // Handle combat logic + logger.info(`Player ${player.username} attacked in game ${gameInstance.id}`); + + // Broadcast attack to other players + const socket = this.playerConnections.get(player.socketId)?.socket; + if (socket) { + socket.to(`game_${gameInstance.id}`).emit('playerAttacked', { + playerId: player.socketId, + username: player.username, + target: attackData.target, + damage: attackData.damage + }); + } + + return true; + } + + handlePlayerInteract(gameInstance, player, interactData) { + // Handle interaction logic (picking up items, activating objects, etc.) + logger.info(`Player ${player.username} interacted in game ${gameInstance.id}`); + + return true; + } + + handlePlayerChat(gameInstance, player, chatData) { + // Handle chat messages + const message = { + playerId: player.socketId, + username: player.username, + message: chatData.message, + timestamp: new Date() + }; + + // Broadcast chat to all players in the game + const socket = this.playerConnections.get(player.socketId)?.socket; + if (socket) { + socket.to(`game_${gameInstance.id}`).emit('chatMessage', message); + } + + return true; + } +} + +// Singleton instance +let gameSystem = null; + +async function initializeGameSystems() { + if (!gameSystem) { + gameSystem = new GameSystem(); + await gameSystem.initializeGameSystems(); + } + return gameSystem; +} + +function getGameSystem() { + return gameSystem; +} + +module.exports = { + GameSystem, + initializeGameSystems, + getGameSystem +}; diff --git a/Galaxy-Strike-Online-main/GameServer/systems/IdleSystem.js b/Galaxy-Strike-Online-main/GameServer/systems/IdleSystem.js new file mode 100644 index 0000000..938a822 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/systems/IdleSystem.js @@ -0,0 +1,323 @@ +/** + * Galaxy Strike Online - Server Idle System + * Manages offline progression and idle mechanics + */ + +class IdleSystem { + constructor() { + // Idle settings + 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: 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 + this.offlineRewards = { + credits: 0, + experience: 0, + energy: 0, + items: [] + }; + + // Idle bonuses + this.bonuses = { + premium: 1.0, + guild: 1.0, + research: 1.0 + }; + + // Idle achievements + this.achievements = { + totalOfflineTime: 0, + maxOfflineSession: 0, + totalIdleCredits: 0, + totalIdleExperience: 0 + }; + } + + initializePlayerData(userId) { + console.log('[IDLE SYSTEM] initializePlayerData called for userId:', userId); + console.log('[IDLE SYSTEM] Current playerProductionRates size:', this.playerProductionRates.size); + + if (!this.playerLastActive.has(userId)) { + console.log('[IDLE SYSTEM] Initializing new player data for:', userId); + this.playerLastActive.set(userId, Date.now()); + this.playerProductionRates.set(userId, { ...this.defaultProductionRates }); + console.log('[IDLE SYSTEM] Set production rates for', userId, ':', this.defaultProductionRates); + this.playerAchievements.set(userId, { + totalOfflineTime: 0, + maxOfflineSession: 0, + totalIdleCredits: 0, + totalIdleExperience: 0 + }); + console.log('[IDLE SYSTEM] Player data initialized successfully for:', userId); + } else { + console.log('[IDLE SYSTEM] Player data already exists for:', userId); + const existingRates = this.playerProductionRates.get(userId); + console.log('[IDLE SYSTEM] Existing production rates:', existingRates); + } + } + + updateLastActive(userId) { + this.playerLastActive.set(userId, Date.now()); + } + + updateProductionRates(userId, rates) { + this.playerProductionRates.set(userId, { + ...this.defaultProductionRates, + ...rates + }); + } + + calculateOfflineProgress(userId) { + this.initializePlayerData(userId); + + const lastActive = this.playerLastActive.get(userId); + const currentTime = Date.now(); + const offlineTime = currentTime - lastActive; + + // Cap offline time to maximum + const cappedOfflineTime = Math.min(offlineTime, this.maxOfflineTime); + + if (cappedOfflineTime <= 0) { + return { + offlineTime: 0, + rewards: { + credits: 0, + experience: 0, + energy: 0, + items: [] + } + }; + } + + const productionRates = this.playerProductionRates.get(userId); + const achievements = this.playerAchievements.get(userId); + + // Calculate offline rewards using offline rates + const offlineSeconds = Math.floor(cappedOfflineTime / 1000); + const offlineRates = this.offlineProductionRates; + const rewards = { + credits: Math.floor(offlineRates.credits * offlineSeconds), + experience: Math.floor(offlineRates.experience * offlineSeconds), + energy: Math.floor(offlineRates.energy * offlineSeconds), + items: [] + }; + + // Update achievements + achievements.totalOfflineTime += cappedOfflineTime; + achievements.maxOfflineSession = Math.max(achievements.maxOfflineSession, cappedOfflineTime); + achievements.totalIdleCredits += rewards.credits; + achievements.totalIdleExperience += rewards.experience; + + // Update last active time + this.playerLastActive.set(userId, currentTime); + + return { + offlineTime: cappedOfflineTime, + offlineSeconds, + rewards, + productionRates + }; + } + + applyIdleRewards(userId, playerData) { + const progress = this.calculateOfflineProgress(userId); + + if (progress.offlineTime > 0) { + // Apply rewards to player data + if (playerData.stats) { + playerData.stats.credits = (playerData.stats.credits || 0) + progress.rewards.credits; + playerData.stats.experience = (playerData.stats.experience || 0) + progress.rewards.experience; + } + + if (playerData.resources) { + playerData.resources.energy = (playerData.resources.energy || 0) + progress.rewards.energy; + } + + // Add items if any + if (progress.rewards.items && progress.rewards.items.length > 0) { + if (!playerData.inventory) { + playerData.inventory = []; + } + playerData.inventory.push(...progress.rewards.items); + } + } + + return progress; + } + + getProductionRates(userId) { + this.initializePlayerData(userId); + return this.playerProductionRates.get(userId); + } + + updateBonuses(userId, bonuses) { + this.initializePlayerData(userId); + const currentRates = this.playerProductionRates.get(userId); + + // Apply bonuses to production rates + const bonusMultiplier = + (bonuses.premium || this.defaultBonuses.premium) * + (bonuses.guild || this.defaultBonuses.guild) * + (bonuses.research || this.defaultBonuses.research); + + const updatedRates = {}; + for (const [resource, baseRate] of Object.entries(this.defaultProductionRates)) { + updatedRates[resource] = Math.floor(baseRate * bonusMultiplier); + } + + this.playerProductionRates.set(userId, updatedRates); + return updatedRates; + } + + getAchievements(userId) { + this.initializePlayerData(userId); + return this.playerAchievements.get(userId); + } + + getIdleStats(userId) { + this.initializePlayerData(userId); + const achievements = this.playerAchievements.get(userId); + const productionRates = this.playerProductionRates.get(userId); + + return { + lastActive: this.playerLastActive.get(userId), + productionRates, + achievements, + maxOfflineTime: this.maxOfflineTime, + currentOfflineTime: Date.now() - this.playerLastActive.get(userId) + }; + } + + // Calculate idle income for a specific time period + calculateIncomeForTime(userId, timeInSeconds) { + this.initializePlayerData(userId); + const productionRates = this.playerProductionRates.get(userId); + + return { + credits: Math.floor(productionRates.credits * timeInSeconds), + experience: Math.floor(productionRates.experience * timeInSeconds), + energy: Math.floor(productionRates.energy * timeInSeconds) + }; + } + + // Get time until next milestone + getTimeUntilMilestone(userId, targetCredits = 10000) { + this.initializePlayerData(userId); + const productionRates = this.playerProductionRates.get(userId); + const currentOfflineTime = Date.now() - this.playerLastActive.get(userId); + + if (productionRates.credits <= 0) { + return { seconds: Infinity, hours: Infinity }; + } + + const creditsPerSecond = productionRates.credits; + const secondsNeeded = Math.ceil(targetCredits / creditsPerSecond); + + return { + seconds: secondsNeeded, + minutes: Math.ceil(secondsNeeded / 60), + hours: Math.ceil(secondsNeeded / 3600) + }; + } + + // Process idle rewards for multiple users (batch processing) + processBatchIdleRewards(userIds) { + const results = new Map(); + + for (const userId of userIds) { + try { + const progress = this.calculateOfflineProgress(userId); + results.set(userId, { + success: true, + progress + }); + } catch (error) { + results.set(userId, { + success: false, + error: error.message + }); + } + } + + return results; + } + + // Generate online idle rewards (called periodically while player is online) + generateOnlineIdleRewards(userId, deltaTimeMs) { + console.log('[IDLE SYSTEM] generateOnlineIdleRewards called for userId:', userId, 'deltaTimeMs:', deltaTimeMs); + + const productionRates = this.playerProductionRates.get(userId); + console.log('[IDLE SYSTEM] Production rates for', userId, ':', productionRates); + + if (!productionRates) { + console.log('[IDLE SYSTEM] No production rates found for', userId, '- returning 0 rewards'); + return { + credits: 0, + experience: 0, + energy: 0 + }; + } + + const deltaTimeSeconds = deltaTimeMs / 1000; + console.log('[IDLE SYSTEM] Delta time seconds:', deltaTimeSeconds); + console.log('[IDLE SYSTEM] Raw calculation:', productionRates.credits, '*', deltaTimeSeconds, '=', productionRates.credits * deltaTimeSeconds); + + const creditsBeforeFloor = productionRates.credits * deltaTimeSeconds; + const rewards = { + credits: Math.floor(creditsBeforeFloor), + experience: Math.floor(productionRates.experience * deltaTimeSeconds), + energy: Math.floor(productionRates.energy * deltaTimeSeconds) + }; + + console.log('[IDLE SYSTEM] Credits before floor:', creditsBeforeFloor, 'after floor:', rewards.credits); + console.log('[IDLE SYSTEM] Calculated rewards for', userId, ':', rewards); + return rewards; + } + + // Reset idle data for a user + resetPlayerData(userId) { + this.playerLastActive.delete(userId); + this.playerProductionRates.delete(userId); + this.playerAchievements.delete(userId); + } + + // Get global idle statistics + getGlobalStats() { + const totalPlayers = this.playerLastActive.size; + let totalOfflineTime = 0; + let totalIdleCredits = 0; + let totalIdleExperience = 0; + + for (const achievements of this.playerAchievements.values()) { + totalOfflineTime += achievements.totalOfflineTime; + totalIdleCredits += achievements.totalIdleCredits; + totalIdleExperience += achievements.totalIdleExperience; + } + + return { + totalPlayers, + totalOfflineTime, + totalIdleCredits, + totalIdleExperience, + averageOfflineTime: totalPlayers > 0 ? totalOfflineTime / totalPlayers : 0 + }; + } +} + +module.exports = IdleSystem; diff --git a/Galaxy-Strike-Online-main/GameServer/systems/ItemSystem.js b/Galaxy-Strike-Online-main/GameServer/systems/ItemSystem.js new file mode 100644 index 0000000..c52880f --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/systems/ItemSystem.js @@ -0,0 +1,210 @@ +/** + * Galaxy Strike Online — Server Item System + * All item/shop data comes from ContentLoader (data/gso/items/). + * No hardcoded item data lives here. + */ + +class ItemSystem { + constructor(contentLoader = null) { + this.serverUrl = process.env.SERVER_URL || 'http://localhost:3002'; + this._loader = contentLoader; + + if (contentLoader) { + console.log(`[ITEM SYSTEM] ${contentLoader.items.size} items available from ContentLoader`); + } else { + console.warn('[ITEM SYSTEM] No ContentLoader provided — item catalog is empty'); + } + } + + setServerUrl(url) { this.serverUrl = url; } + + // ── Catalog access ───────────────────────────────────────────────────── + + getAllItems() { return this._loader ? this._loader.getAllItems() : []; } + getItem(id) { return this._loader ? this._loader.getItem(id) : null; } + getItemsByType(type) { return this._loader ? this._loader.getItemsByType(type) : []; } + + /** + * Returns all items that include 'shop' in their categories array. + * Grouped by item type: { ship: [...], weapon: [...], ... } + */ + getShopItemsByCategory() { + const out = {}; + for (const item of this.getAllItems()) { + if (!Array.isArray(item.categories) || !item.categories.includes('shop')) continue; + const t = item.type || 'misc'; + if (!out[t]) out[t] = []; + out[t].push(this._normaliseForClient(item)); + } + return out; + } + + /** + * Flat array of all shop items. + * Called as getRandomShopItems() by legacy server.js code — returns the same thing, + * just grouped (matching the expected { category: [items] } shape). + */ + getRandomShopItems() { + return this.getShopItemsByCategory(); + } + + /** + * All shop items for a single type/category. + * Called as getRandomItemsByCategory(category) by legacy server.js code. + */ + getRandomItemsByCategory(category) { + return this.getAllItems() + .filter(i => + Array.isArray(i.categories) && i.categories.includes('shop') && + i.type === category + ) + .map(i => this._normaliseForClient(i)); + } + + // ── Shop helpers ─────────────────────────────────────────────────────── + + /** + * Find an item that is available in the shop. + * Returns null if the item doesn't exist or isn't in the shop. + */ + findShopItem(itemId) { + const item = this.getItem(itemId); + if (!item) return null; + if (!Array.isArray(item.categories) || !item.categories.includes('shop')) return null; + return item; + } + + /** + * Validate a purchase against player wallet. + * Returns { valid: true } or { valid: false, error: '...' } + */ + validatePurchase(item, playerStats, quantity = 1) { + const currency = item.currency || 'credits'; + const totalCost = (item.price || 0) * quantity; + + if (currency === 'credits' && (playerStats.credits || 0) < totalCost) { + return { valid: false, error: 'Not enough credits' }; + } + if (currency === 'gems' && (playerStats.gems || 0) < totalCost) { + return { valid: false, error: 'Not enough gems' }; + } + return { valid: true, totalCost, currency }; + } + + /** + * Apply a purchase to playerData (mutates directly, like the original server did). + * Returns the purchase summary for the response payload. + */ + applyPurchase(item, playerData, quantity = 1) { + const currency = item.currency || 'credits'; + const totalCost = (item.price || 0) * quantity; + + // Deduct currency + if (currency === 'credits') playerData.stats.credits = (playerData.stats.credits || 0) - totalCost; + if (currency === 'gems') playerData.stats.gems = (playerData.stats.gems || 0) - totalCost; + + // Grant item + switch (item.type) { + case 'decoration': + if (!playerData.starbase) playerData.starbase = { wallpaper: null, ownedWallpapers: [], unlockedRooms: [] }; + if (item.subtype === 'wallpaper') { + if (!playerData.starbase.ownedWallpapers) playerData.starbase.ownedWallpapers = []; + if (!playerData.starbase.ownedWallpapers.includes(item.id)) { + playerData.starbase.ownedWallpapers.push(item.id); + } + } else if (item.subtype === 'room_unlock') { + if (!playerData.starbase.unlockedRooms) playerData.starbase.unlockedRooms = []; + if (item.roomId && !playerData.starbase.unlockedRooms.includes(item.roomId)) { + playerData.starbase.unlockedRooms.push(item.roomId); + } + } + break; + + case 'ship': + if (!playerData.ownedShips) playerData.ownedShips = []; + if (!playerData.ownedShips.includes(item.id)) playerData.ownedShips.push(item.id); + break; + + case 'cosmetic': + if (!playerData.ownedCosmetics) playerData.ownedCosmetics = []; + if (!playerData.ownedCosmetics.includes(item.id)) playerData.ownedCosmetics.push(item.id); + break; + + case 'weapon': + case 'armour': + case 'consumable': + case 'material': + case 'hullPlating': + default: + if (!playerData.inventory) playerData.inventory = { items: [] }; + if (!playerData.inventory.items) playerData.inventory.items = []; + // Stack consumables/materials; unique otherwise + const existing = playerData.inventory.items.find(i => i.id === item.id && item.stackable); + if (existing) { + existing.quantity = (existing.quantity || 1) + quantity; + } else { + playerData.inventory.items.push({ + id: item.id, + name: item.name, + type: item.type, + quantity: quantity, + acquired: new Date().toISOString() + }); + } + break; + } + + return { + totalCost, + currency, + newBalance: currency === 'credits' ? playerData.stats.credits : playerData.stats.gems + }; + } + + // ── Packet builder helpers ───────────────────────────────────────────── + + /** Builds the full shopItemsReceived payload */ + buildShopResponse() { + return { + success: true, + shopItems: this.getShopItemsByCategory(), + timestamp: new Date().toISOString() + }; + } + + /** Builds the itemDetailsReceived payload */ + buildItemDetailResponse(itemId) { + const item = this.getItem(itemId); + if (!item) return { success: false, error: `Item '${itemId}' not found` }; + return { success: true, item: this._normaliseForClient(item) }; + } + + // ── Internal normalisation ───────────────────────────────────────────── + + /** + * Normalise an item object into the flat shape the client expects. + * Handles both bare-item format (what ContentLoader returns after unwrapping templates) + * and the raw wrapped format { templates: { : {...} } }. + */ + _normaliseForClient(item) { + return { + id: item.id, + name: item.name || item.id, + type: item.type || 'misc', + rarity: item.rarity || 'common', + price: item.price || 0, + currency: item.currency || 'credits', + description: item.description || '', + texture: item.texture || '', + texturePath: item.texture || '', + stats: item.stats || {}, + categories: item.categories || [], + requirements:item.requirements || {}, + stackable: item.stackable ?? false, + maxStack: item.maxStack || 1, + meta: item.meta || {} + }; + } +} + +module.exports = ItemSystem; diff --git a/Galaxy-Strike-Online-main/GameServer/systems/MarketSystem.js b/Galaxy-Strike-Online-main/GameServer/systems/MarketSystem.js new file mode 100644 index 0000000..8bb3509 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/systems/MarketSystem.js @@ -0,0 +1,206 @@ +/** + * Galaxy Strike Online — Player Market (GDD §14) + * Types: Auction, Direct Sale + * 2% listing fee; 24/48/72-hour durations + * Tradeable: resources, modules, blueprints, consumables, cosmetics + */ + +const mongoose = require('mongoose'); + +const listingSchema = new mongoose.Schema({ + listingId: { type: String, required: true, unique: true }, + sellerId: { type: String, required: true }, + sellerName: { type: String, default: '' }, + itemType: { type: String, required: true }, // 'resource'|'item' + itemId: { type: String, required: true }, // resource key or item.id + itemName: { type: String, default: '' }, + itemIcon: { type: String, default: '📦' }, + quantity: { type: Number, default: 1 }, + pricePerUnit: { type: Number, required: true }, + totalPrice: { type: Number, required: true }, + currency: { type: String, default: 'credits' }, + listedAt: { type: Date, default: Date.now }, + expiresAt: { type: Date, required: true }, + isActive: { type: Boolean, default: true }, + buyerId: { type: String, default: null }, + soldAt: { type: Date, default: null }, + durationHours: { type: Number, default: 24 }, +}, { timestamps: true }); + +listingSchema.index({ isActive: 1, expiresAt: 1 }); +listingSchema.index({ sellerId: 1 }); +listingSchema.index({ itemId: 1, isActive: 1 }); + +const Listing = mongoose.model('MarketListing', listingSchema); + +const LISTING_FEE_PCT = 0.02; // 2% GDD §14.3 +const MAX_LISTINGS = 10; // per player (base) +const VALID_DURATIONS = [24, 48, 72]; + +const TRADEABLE_RESOURCES = { + metal: { name:'Metal', icon:'⚙', minPrice:1, maxPrice:100 }, + gas: { name:'Gas', icon:'☁', minPrice:1, maxPrice:100 }, + crystal: { name:'Crystal', icon:'💎', minPrice:2, maxPrice:200 }, + energyCells: { name:'Energy Cells',icon:'⚡', minPrice:1, maxPrice:80 }, + darkMatter: { name:'Dark Matter', icon:'✦', minPrice:10, maxPrice:2000 }, +}; + +class MarketSystem { + constructor() { this.Listing = Listing; } + + /** List a resource on the market */ + async listResource(playerData, { resource, quantity, pricePerUnit, durationHours = 24 }) { + if (!TRADEABLE_RESOURCES[resource]) throw new Error('Resource not tradeable'); + quantity = Math.floor(quantity); + pricePerUnit = Math.floor(pricePerUnit); + durationHours = VALID_DURATIONS.includes(durationHours) ? durationHours : 24; + + if (quantity < 1) throw new Error('Quantity must be ≥ 1'); + if (pricePerUnit < 1) throw new Error('Price must be ≥ 1 credit'); + + // Check median manipulation (stub — skip if no data) + const res = playerData.resources || {}; + if ((res[resource] || 0) < quantity) throw new Error(`Insufficient ${resource}: have ${res[resource]||0}, need ${quantity}`); + + // Count existing listings + const myListings = await Listing.countDocuments({ sellerId: playerData.userId, isActive: true }); + if (myListings >= MAX_LISTINGS) throw new Error(`Max ${MAX_LISTINGS} active listings`); + + // Deduct resources + res[resource] -= quantity; + + // Listing fee in credits (2%) + const totalPrice = pricePerUnit * quantity; + const listingFee = Math.ceil(totalPrice * LISTING_FEE_PCT); + if ((playerData.stats?.credits || 0) < listingFee) throw new Error(`Listing fee: ${listingFee} credits`); + playerData.stats.credits -= listingFee; + + const cfg = TRADEABLE_RESOURCES[resource]; + const listing = new Listing({ + listingId: 'listing_' + Date.now() + '_' + Math.random().toString(36).slice(2, 6), + sellerId: playerData.userId, + sellerName: playerData.username, + itemType: 'resource', + itemId: resource, + itemName: cfg.name, + itemIcon: cfg.icon, + quantity, + pricePerUnit, + totalPrice, + currency: 'credits', + expiresAt: new Date(Date.now() + durationHours * 3600 * 1000), + durationHours, + }); + await listing.save(); + return { listing, listingFee }; + } + + /** List an inventory item */ + async listItem(playerData, { itemId, pricePerUnit, durationHours = 24 }) { + const inventory = playerData.inventory || []; + const idx = inventory.findIndex(i => i.id === itemId); + if (idx < 0) throw new Error('Item not found in inventory'); + const item = inventory[idx]; + if (item.type === 'ship' && item.id === playerData.stats?.activeShipId) throw new Error('Cannot list your active ship'); + + pricePerUnit = Math.floor(pricePerUnit); + durationHours = VALID_DURATIONS.includes(durationHours) ? durationHours : 24; + + const myListings = await Listing.countDocuments({ sellerId: playerData.userId, isActive: true }); + if (myListings >= MAX_LISTINGS) throw new Error(`Max ${MAX_LISTINGS} active listings`); + + const listingFee = Math.ceil(pricePerUnit * LISTING_FEE_PCT); + if ((playerData.stats?.credits || 0) < listingFee) throw new Error(`Listing fee: ${listingFee} credits`); + playerData.stats.credits -= listingFee; + inventory.splice(idx, 1); + + const listing = new Listing({ + listingId: 'listing_' + Date.now() + '_' + Math.random().toString(36).slice(2, 6), + sellerId: playerData.userId, + sellerName: playerData.username, + itemType: 'item', + itemId: item.id, + itemName: item.name || item.id, + itemIcon: item.icon || '📦', + quantity: 1, + pricePerUnit, + totalPrice: pricePerUnit, + currency: 'credits', + expiresAt: new Date(Date.now() + durationHours * 3600 * 1000), + durationHours, + _itemData: JSON.stringify(item), + }); + await listing.save(); + return { listing, listingFee }; + } + + /** Buy a listing */ + async buyListing(buyerData, listingId) { + const listing = await Listing.findOne({ listingId, isActive: true }); + if (!listing) throw new Error('Listing not found or already sold'); + if (listing.expiresAt < new Date()) throw new Error('Listing expired'); + if (listing.sellerId === buyerData.userId) throw new Error('Cannot buy your own listing'); + if ((buyerData.stats?.credits || 0) < listing.totalPrice) throw new Error(`Need ${listing.totalPrice} credits, have ${buyerData.stats?.credits||0}`); + + buyerData.stats.credits -= listing.totalPrice; + + // Transfer goods to buyer + if (listing.itemType === 'resource') { + if (!buyerData.resources) buyerData.resources = {}; + buyerData.resources[listing.itemId] = (buyerData.resources[listing.itemId] || 0) + listing.quantity; + } else { + if (!buyerData.inventory) buyerData.inventory = []; + try { buyerData.inventory.push(JSON.parse(listing._itemData)); } catch(e) { /* fallback */ } + } + + listing.isActive = false; + listing.buyerId = buyerData.userId; + listing.soldAt = new Date(); + await listing.save(); + + // Return sale proceeds to seller (handled async — seller gets credits on next login or via event) + return { listing, proceeds: listing.totalPrice }; + } + + /** Cancel own listing — returns resources/item minus fee (no fee refund per GDD) */ + async cancelListing(playerData, listingId) { + const listing = await Listing.findOne({ listingId, sellerId: playerData.userId, isActive: true }); + if (!listing) throw new Error('Listing not found'); + + listing.isActive = false; + await listing.save(); + + // Return goods + if (listing.itemType === 'resource') { + if (!playerData.resources) playerData.resources = {}; + playerData.resources[listing.itemId] = (playerData.resources[listing.itemId] || 0) + listing.quantity; + } else { + if (!playerData.inventory) playerData.inventory = []; + try { playerData.inventory.push(JSON.parse(listing._itemData)); } catch(e) { /* skip */ } + } + return listing; + } + + async getListings({ itemId, category, limit = 40 } = {}) { + const filter = { isActive: true, expiresAt: { $gt: new Date() } }; + if (itemId) filter.itemId = itemId; + if (category) filter.itemType = category; + return Listing.find(filter).sort({ pricePerUnit: 1 }).limit(limit).lean(); + } + + async getMyListings(userId) { + return Listing.find({ sellerId: userId, isActive: true }).sort({ listedAt: -1 }).lean(); + } + + async expireListings() { + const now = new Date(); + const expired = await Listing.find({ isActive: true, expiresAt: { $lt: now } }); + // TODO: return resources to sellers (deferred — done on next login) + await Listing.updateMany({ isActive: true, expiresAt: { $lt: now } }, { $set: { isActive: false } }); + return expired.length; + } + + getTradeableResources() { return TRADEABLE_RESOURCES; } +} + +module.exports = { MarketSystem, Listing }; diff --git a/Galaxy-Strike-Online-main/GameServer/systems/MissionSystem.js b/Galaxy-Strike-Online-main/GameServer/systems/MissionSystem.js new file mode 100644 index 0000000..8a40a35 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/systems/MissionSystem.js @@ -0,0 +1,237 @@ +/** + * Galaxy Strike Online — Mission System (GDD §8.3, §15) + * Fleet Missions: Attack, Mine, Patrol, Transport + * Reward formula: base_reward = difficulty × 100 × (1 + level × 0.02) + */ + +const MISSION_TYPES = { + patrol: { + label: 'Patrol', icon: '🛡', + desc: 'Patrol a sector — passive XP and chance of intercepting pirates.', + baseDurationMin: 3, baseDurationMax: 5, // minutes + rewards: (level) => ({ xp: Math.floor(50 * (1 + level * 0.02)), credits: Math.floor(30 * (1 + level * 0.02)) }), + riskFactor: 0.05, + }, + mine: { + label: 'Mining Run', icon: '⛏', + desc: 'Send fleet to mine asteroids in a sector. Returns Metal and Gas.', + baseDurationMin: 5, baseDurationMax: 10, + rewards: (level) => ({ + xp: Math.floor(30 * (1 + level * 0.02)), + metal: Math.floor(200 * (1 + level * 0.05)), + gas: Math.floor(80 * (1 + level * 0.05)), + }), + riskFactor: 0.02, + }, + transport: { + label: 'Trade Run', icon: '🚚', + desc: 'Transport cargo between sectors for credits.', + baseDurationMin: 8, baseDurationMax: 15, + rewards: (level) => ({ xp: Math.floor(40 * (1 + level * 0.02)), credits: Math.floor(150 * (1 + level * 0.03)) }), + riskFactor: 0.01, + }, + attack: { + label: 'Raid', icon: '⚔', + desc: 'Raid a pirate outpost. High rewards, high risk.', + baseDurationMin: 10, baseDurationMax: 20, + rewards: (level) => ({ + xp: Math.floor(120 * (1 + level * 0.02)), + credits: Math.floor(200 * (1 + level * 0.03)), + loot: true, + }), + riskFactor: 0.25, + }, + explore: { + label: 'Explore', icon: '🔭', + desc: 'Explore an unknown sector for intel and resources.', + baseDurationMin: 6, baseDurationMax: 12, + rewards: (level) => ({ xp: Math.floor(80 * (1 + level * 0.02)), darkMatter: Math.floor(2 + Math.floor(level / 5)) }), + riskFactor: 0.10, + }, +}; + +// Random loot table for raid missions +const RAID_LOOT = ['repair_kit','warp_token','shield_charge','metal_cache','crystal_shard']; + +class MissionSystem { + constructor() { + this.MISSION_TYPES = MISSION_TYPES; + } + + /** Start a fleet mission */ + startMission(playerData, { missionType, fleetShipIds = [] }) { + if (!MISSION_TYPES[missionType]) throw new Error('Unknown mission type: ' + missionType); + const type = MISSION_TYPES[missionType]; + const level = playerData.stats?.level || 1; + + // Need at least one ship + const ships = (playerData.inventory || []).filter(i => i.type === 'ship' && fleetShipIds.includes(i.id)); + if (ships.length === 0) throw new Error('Select at least one ship for the mission.'); + + // Ships can't be on multiple missions + const activeMissions = playerData.fleetMissions || []; + const busyShips = new Set(activeMissions.flatMap(m => m.shipIds || [])); + const conflicting = fleetShipIds.filter(id => busyShips.has(id)); + if (conflicting.length > 0) throw new Error('Some ships are already on a mission.'); + + const durationMin = type.baseDurationMin + Math.random() * (type.baseDurationMax - type.baseDurationMin); + const durationMs = Math.round(durationMin * 60 * 1000); + const now = Date.now(); + + const mission = { + id: 'mission_' + now + '_' + Math.random().toString(36).slice(2, 7), + type: missionType, + label: type.label, + icon: type.icon, + shipIds: fleetShipIds, + startedAt: now, + completesAt: now + durationMs, + level, + status: 'active', + }; + + playerData.fleetMissions = [...activeMissions, mission]; + return mission; + } + + /** Collect completed missions, apply rewards, return results */ + collectMissions(playerData, resourceSystem) { + const now = Date.now(); + const active = playerData.fleetMissions || []; + const done = active.filter(m => m.completesAt <= now && m.status === 'active'); + const remaining = active.filter(m => m.completesAt > now || m.status !== 'active'); + playerData.fleetMissions = remaining; + + const results = []; + for (const mission of done) { + const type = MISSION_TYPES[mission.type]; + if (!type) continue; + const rewards = type.rewards(mission.level || 1); + + // Risk roll — small chance mission fails + const failed = Math.random() < type.riskFactor; + if (failed) { + results.push({ mission, success: false, message: `Mission failed! Fleet was intercepted.` }); + continue; + } + + // Apply XP + credits + if (rewards.xp) playerData.stats.experience = (playerData.stats.experience || 0) + rewards.xp; + if (rewards.credits) playerData.stats.credits = (playerData.stats.credits || 0) + rewards.credits; + + // Apply resource rewards + const resGains = {}; + if (rewards.metal) resGains.metal = rewards.metal; + if (rewards.gas) resGains.gas = rewards.gas; + if (rewards.crystal) resGains.crystal = rewards.crystal; + if (rewards.darkMatter) resGains.darkMatter = rewards.darkMatter; + if (Object.keys(resGains).length > 0 && resourceSystem) { + resourceSystem.initResources(playerData); + resourceSystem.add(playerData, resGains); + } + + // Random loot for raids + const lootItem = rewards.loot ? RAID_LOOT[Math.floor(Math.random() * RAID_LOOT.length)] : null; + if (lootItem) { + if (!playerData.inventory) playerData.inventory = []; + playerData.inventory.push({ id: lootItem + '_' + Date.now(), type: 'consumable', name: lootItem.replace('_', ' '), icon: '📦', description: 'Mission loot' }); + } + + results.push({ mission, success: true, rewards, lootItem, message: `${type.label} completed successfully!` }); + } + return results; + } + + /** Recall a mission early (no rewards, ships return instantly) */ + recallMission(playerData, missionId) { + const idx = (playerData.fleetMissions || []).findIndex(m => m.id === missionId); + if (idx < 0) throw new Error('Mission not found'); + playerData.fleetMissions.splice(idx, 1); + } + + getMissionTypes() { return MISSION_TYPES; } + + getMissionsForPlayer(playerData) { + return { + active: (playerData.fleetMissions || []).filter(m => m.status === 'active'), + types: MISSION_TYPES, + }; + } +} + +module.exports = MissionSystem; + +// ── Faction NPC Missions (GDD §15.1) ───────────────────────────────────── +const fs = require('fs'); +const path = require('path'); + +let FACTION_MISSIONS = []; +try { + const p = path.join(__dirname, '../data/gso/missions/faction_missions.json'); + FACTION_MISSIONS = JSON.parse(fs.readFileSync(p, 'utf8')).missions; +} catch(e) { /* data not loaded */ } + +MissionSystem.prototype.getAvailableFactionMissions = function(playerData) { + const level = playerData.stats?.level || 1; + const rep = playerData.reputation || {}; + return FACTION_MISSIONS.filter(m => { + if (m.minLevel > level) return false; + if (m.requires_rep !== undefined) { + const fRep = rep[m.faction] || 0; + if (fRep < m.requires_rep) return false; + } + return true; + }); +}; + +MissionSystem.prototype.startFactionMission = function(playerData, missionId) { + const mission = FACTION_MISSIONS.find(m => m.id === missionId); + if (!mission) throw new Error('Faction mission not found: ' + missionId); + const level = playerData.stats?.level || 1; + if (mission.minLevel > level) throw new Error(`Requires Commander Level ${mission.minLevel}`); + + const now = Date.now(); + const job = { + id: 'fmission_' + now, + type: 'faction', + factionMissionId: mission.id, + label: mission.name, + icon: '📋', + faction: mission.faction, + startedAt: now, + completesAt: now + mission.duration * 1000, + status: 'active', + rewards: mission.rewards, + }; + playerData.fleetMissions = [...(playerData.fleetMissions||[]), job]; + return job; +}; + +MissionSystem.prototype.collectFactionMissions = function(playerData, resourceSystem, reputationSystem) { + const now = Date.now(); + const done = (playerData.fleetMissions||[]).filter(m => m.type==='faction' && m.completesAt <= now && m.status==='active'); + playerData.fleetMissions = (playerData.fleetMissions||[]).filter(m => !(m.type==='faction' && m.completesAt <= now && m.status==='active')); + + const results = []; + for (const job of done) { + const r = job.rewards || {}; + if (r.credits) playerData.stats.credits = (playerData.stats.credits||0) + r.credits; + if (r.xp) playerData.addExperience?.(r.xp); + if (r.metal || r.gas || r.crystal || r.darkMatter) { + resourceSystem?.initResources(playerData); + const gains = {}; + if (r.metal) gains.metal = r.metal; + if (r.gas) gains.gas = r.gas; + if (r.crystal) gains.crystal = r.crystal; + if (r.darkMatter) gains.darkMatter = r.darkMatter; + resourceSystem?.add(playerData, gains); + } + if (r.reputation && reputationSystem) { + for (const [factionId, delta] of Object.entries(r.reputation)) { + reputationSystem.adjustReputation(playerData, factionId, delta); + } + } + results.push({ mission: job, success: true, rewards: r }); + } + return results; +}; diff --git a/Galaxy-Strike-Online-main/GameServer/systems/QuestSystem.js b/Galaxy-Strike-Online-main/GameServer/systems/QuestSystem.js new file mode 100644 index 0000000..ac6e76e --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/systems/QuestSystem.js @@ -0,0 +1,61 @@ +/** + * QuestSystem — thin wrapper around ContentLoader for quest definitions. + */ +class QuestSystem { + constructor(contentLoader) { + this.loader = contentLoader; + this.playerQuests = new Map(); + } + + getAllQuests() { return this.loader.getAllQuests(); } + getQuest(id) { return this.loader.getQuest(id); } + getQuestsByCategory(cat) { return this.loader.getQuestsByCategory(cat); } + getQuestCategories() { return this.loader.getQuestCategories(); } + + initializePlayerData(userId) { + if (this.playerQuests.has(userId)) return this.playerQuests.get(userId); + const data = { active: {}, completed: [], failed: [] }; + this.playerQuests.set(userId, data); + return data; + } + + getPlayerData(userId) { return this.initializePlayerData(userId); } + getPlayerQuests(userId) { return this.initializePlayerData(userId); } + + startQuest(userId, questId) { + const quest = this.loader.getQuest(questId); + if (!quest) return { success: false, error: 'Quest not found' }; + const pd = this.initializePlayerData(userId); + if (pd.active[questId]) return { success: false, error: 'Already active' }; + const objectives = {}; + for (const obj of (quest.objectives || [])) { + objectives[obj.id] = { progress: 0, complete: false }; + } + pd.active[questId] = { startedAt: Date.now(), objectives }; + return { success: true, quest }; + } + + completeQuest(userId, questId, rewards) { + const pd = this.initializePlayerData(userId); + delete pd.active[questId]; + if (!pd.completed.includes(questId)) pd.completed.push(questId); + return { success: true, questId, rewards }; + } + + updateObjective(userId, questId, objectiveId, amount = 1) { + const pd = this.initializePlayerData(userId); + const state = pd.active[questId]; + if (!state) return null; + const quest = this.loader.getQuest(questId); + const objDef = quest?.objectives?.find(o => o.id === objectiveId); + if (!objDef) return null; + const obj = state.objectives[objectiveId] || { progress: 0, complete: false }; + obj.progress = Math.min(obj.progress + amount, objDef.target); + obj.complete = obj.progress >= objDef.target; + state.objectives[objectiveId] = obj; + const allDone = quest.objectives.every(o => state.objectives[o.id]?.complete); + if (allDone) return this.completeQuest(userId, questId, quest.rewards); + return { questId, objectiveId, progress: obj.progress, completed: false }; + } +} +module.exports = QuestSystem; diff --git a/Galaxy-Strike-Online-main/GameServer/systems/ReputationSystem.js b/Galaxy-Strike-Online-main/GameServer/systems/ReputationSystem.js new file mode 100644 index 0000000..1e691ef --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/systems/ReputationSystem.js @@ -0,0 +1,66 @@ +/** + * Galaxy Strike Online — NPC Reputation System (GDD §15.3) + * Scale: -2000 to +2000 per faction + * Factions: Federation, Pirate Syndicate, Merchant Guild, Rogue AI, Void Cult + */ + +const FACTIONS = { + federation: { name: 'Federation', icon: '🌐', color: '#42a5f5', desc: 'The governing body of settled space.' }, + pirate_syndicate:{ name: 'Pirate Syndicate', icon: '💀', color: '#ef5350', desc: 'Organised raiders — hostile by default.' }, + merchant_guild: { name: 'Merchant Guild', icon: '💼', color: '#ffd700', desc: 'Trade consortium controlling major routes.' }, + rogue_ai: { name: 'Rogue AI Collective',icon: '🤖', color: '#ab47bc', desc: 'Autonomous machines of unknown origin.' }, + void_cult: { name: 'Void Cult', icon: '🌑', color: '#78909c', desc: 'Enigmatic zealots from deep space.' }, +}; + +const THRESHOLDS = [ + { min: 1500, label: 'Exalted', color: '#ffd700' }, + { min: 1000, label: 'Honored', color: '#4caf50' }, + { min: 500, label: 'Friendly', color: '#66bb6a' }, + { min: 1, label: 'Neutral', color: '#aaa' }, + { min: -499, label: 'Unfriendly',color: '#ff9800' }, + { min: -999, label: 'Hostile', color: '#f44336' }, + { min: -2000,label: 'Hated', color: '#b71c1c' }, +]; + +function getStanding(rep) { + for (const t of THRESHOLDS) if (rep >= t.min) return t; + return THRESHOLDS[THRESHOLDS.length - 1]; +} + +class ReputationSystem { + initReputation(playerData) { + if (playerData.reputation) return; + const rep = {}; + for (const id of Object.keys(FACTIONS)) { + rep[id] = id === 'pirate_syndicate' ? -500 : 0; // Pirates start hostile + } + playerData.reputation = rep; + } + + adjustReputation(playerData, factionId, delta) { + this.initReputation(playerData); + if (!FACTIONS[factionId]) return; + const current = playerData.reputation[factionId] || 0; + playerData.reputation[factionId] = Math.max(-2000, Math.min(2000, current + delta)); + // Opposing factions lose rep when gaining with others + const opposites = { federation: 'pirate_syndicate', pirate_syndicate: 'federation' }; + const opp = opposites[factionId]; + if (opp && delta > 0) { + playerData.reputation[opp] = Math.max(-2000, (playerData.reputation[opp]||0) - Math.floor(delta * 0.5)); + } + return playerData.reputation[factionId]; + } + + getReputationData(playerData) { + this.initReputation(playerData); + return Object.entries(FACTIONS).map(([id, f]) => { + const rep = playerData.reputation[id] || 0; + const standing = getStanding(rep); + return { id, ...f, reputation: rep, standing: standing.label, standingColor: standing.color }; + }); + } + + getFactions() { return FACTIONS; } +} + +module.exports = { ReputationSystem, FACTIONS }; diff --git a/Galaxy-Strike-Online-main/GameServer/systems/ResearchSystem.js b/Galaxy-Strike-Online-main/GameServer/systems/ResearchSystem.js new file mode 100644 index 0000000..30b6704 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/systems/ResearchSystem.js @@ -0,0 +1,110 @@ +/** + * Galaxy Strike Online - Research System + * Manages the research tree and player tech progress + */ +const RESEARCH_TREE = { + // ── Weapons ───────────────────────────────────────────────────────────── + basic_lasers: { id:'basic_lasers', name:'Basic Lasers', branch:'weapons', tier:1, cost:{credits:500, gems:0}, time:60, effects:{weaponDamage:10}, requires:[] }, + pulse_lasers: { id:'pulse_lasers', name:'Pulse Lasers', branch:'weapons', tier:2, cost:{credits:1200, gems:0}, time:180, effects:{weaponDamage:25}, requires:['basic_lasers'] }, + advanced_lasers: { id:'advanced_lasers', name:'Advanced Lasers', branch:'weapons', tier:3, cost:{credits:3000, gems:5}, time:600, effects:{weaponDamage:50}, requires:['pulse_lasers'] }, + railgun_tech: { id:'railgun_tech', name:'Railgun Tech', branch:'weapons', tier:2, cost:{credits:2000, gems:0}, time:240, effects:{armorPierce:20}, requires:['basic_lasers'] }, + smart_warheads: { id:'smart_warheads', name:'Smart Warheads', branch:'weapons', tier:4, cost:{credits:9000, gems:10},time:1200, effects:{missileBonus:30}, requires:['railgun_tech'] }, + emp_technology: { id:'emp_technology', name:'EMP Technology', branch:'weapons', tier:3, cost:{credits:4000, gems:5}, time:720, effects:{empChance:15}, requires:['pulse_lasers'] }, + // ── Engineering ───────────────────────────────────────────────────────── + structural_analysis:{ id:'structural_analysis', name:'Structural Analysis', branch:'engineering', tier:1, cost:{credits:400, gems:0}, time:60, effects:{hullBonus:10}, requires:[] }, + nano_composites: { id:'nano_composites', name:'Nano-Composites', branch:'engineering',tier:2, cost:{credits:1000, gems:0}, time:180, effects:{hullBonus:25}, requires:['structural_analysis'] }, + reactive_armor: { id:'reactive_armor', name:'Reactive Armor', branch:'engineering',tier:3, cost:{credits:3500, gems:5}, time:600, effects:{armorBonus:40}, requires:['nano_composites'] }, + improved_engines: { id:'improved_engines', name:'Improved Engines', branch:'engineering',tier:1, cost:{credits:300, gems:0}, time:60, effects:{speedBonus:10}, requires:[] }, + warp_drive: { id:'warp_drive', name:'Warp Drive', branch:'engineering',tier:5, cost:{credits:20000,gems:20},time:3600, effects:{travelSpeed:60}, requires:['improved_engines'] }, + shield_matrix: { id:'shield_matrix', name:'Shield Matrix', branch:'engineering',tier:2, cost:{credits:1200, gems:0}, time:180, effects:{shieldRegen:50}, requires:[] }, + // ── Economy ───────────────────────────────────────────────────────────── + enhanced_mining: { id:'enhanced_mining', name:'Enhanced Mining', branch:'economy', tier:1, cost:{credits:200, gems:0}, time:60, effects:{miningBonus:15}, requires:[] }, + deep_core_drills: { id:'deep_core_drills', name:'Deep-Core Drills', branch:'economy', tier:3, cost:{credits:2500, gems:5}, time:600, effects:{miningBonus:35}, requires:['enhanced_mining'] }, + trade_protocols: { id:'trade_protocols', name:'Trade Protocols', branch:'economy', tier:2, cost:{credits:600, gems:0}, time:120, effects:{tradeFeeReduction:10}, requires:[] }, + construction_eff: { id:'construction_eff', name:'Construction Efficiency', branch:'economy', tier:1, cost:{credits:350, gems:0}, time:60, effects:{buildTimeReduction:10}, requires:[] }, + mass_production: { id:'mass_production', name:'Mass Production', branch:'economy', tier:4, cost:{credits:10000,gems:10},time:1800, effects:{buildTimeReduction:35}, requires:['construction_eff'] }, + // ── Exploration ───────────────────────────────────────────────────────── + long_range_sensors:{ id:'long_range_sensors',name:'Long Range Sensors',branch:'exploration',tier:1, cost:{credits:300, gems:0}, time:60, effects:{sensorRange:2}, requires:[] }, + stealth_systems: { id:'stealth_systems', name:'Stealth Systems', branch:'exploration',tier:3, cost:{credits:5000, gems:10},time:900, effects:{stealthRating:30}, requires:['long_range_sensors'] }, + wormhole_nav: { id:'wormhole_nav', name:'Wormhole Navigation',branch:'exploration',tier:4, cost:{credits:8000, gems:15},time:1200, effects:{warpCooldown:50}, requires:['long_range_sensors'] }, + // ── Defense ───────────────────────────────────────────────────────────── + point_defense: { id:'point_defense', name:'Point Defense', branch:'defense', tier:1, cost:{credits:400, gems:0}, time:60, effects:{missileDefense:20}, requires:[] }, + heavy_armor_plating:{ id:'heavy_armor_plating', name:'Heavy Armor Plating', branch:'defense', tier:4, cost:{credits:12000,gems:15},time:1800, effects:{armorBonus:80}, requires:['reactive_armor'] }, + auto_repair: { id:'auto_repair', name:'Auto-Repair Drones', branch:'defense', tier:3, cost:{credits:4000, gems:5}, time:720, effects:{hullRegen:2}, requires:['shield_matrix'] }, +}; + +class ResearchSystem { + constructor() { + this.tree = RESEARCH_TREE; + } + + getTree() { return Object.values(this.tree); } + + /** Return what a player can currently research */ + getAvailableResearch(playerData) { + const completed = new Set(playerData.research?.completed || []); + const inProgress = playerData.research?.inProgress || null; + return Object.values(this.tree).map(tech => ({ + ...tech, + status: completed.has(tech.id) ? 'completed' + : inProgress?.techId === tech.id ? 'in_progress' + : tech.requires.every(r => completed.has(r)) ? 'available' + : 'locked', + progressPercent: inProgress?.techId === tech.id + ? Math.floor(((Date.now() - inProgress.startedAt) / (tech.time * 1000)) * 100) + : 0, + })); + } + + /** Start researching a technology */ + startResearch(playerData, techId) { + const tech = this.tree[techId]; + if (!tech) throw new Error('Unknown technology'); + + const completed = new Set(playerData.research?.completed || []); + if (completed.has(techId)) throw new Error('Already researched'); + if (playerData.research?.inProgress) throw new Error('Research already in progress'); + if (!tech.requires.every(r => completed.has(r))) throw new Error('Prerequisites not met'); + + const stats = playerData.stats || {}; + if ((stats.credits || 0) < tech.cost.credits) throw new Error(`Need ${tech.cost.credits} credits`); + if ((stats.gems || 0) < tech.cost.gems) throw new Error(`Need ${tech.cost.gems} gems`); + + stats.credits = (stats.credits || 0) - tech.cost.credits; + stats.gems = (stats.gems || 0) - tech.cost.gems; + playerData.stats = stats; + playerData.research = playerData.research || { completed: [], inProgress: null, effects: {} }; + playerData.research.inProgress = { techId, startedAt: Date.now(), completesAt: Date.now() + tech.time * 1000 }; + return { tech, completesAt: playerData.research.inProgress.completesAt }; + } + + /** Check and resolve completed research — call on tick or on request */ + checkCompletion(playerData) { + const ip = playerData.research?.inProgress; + if (!ip || Date.now() < ip.completesAt) return null; + + const tech = this.tree[ip.techId]; + playerData.research.completed = playerData.research.completed || []; + playerData.research.completed.push(ip.techId); + playerData.research.inProgress = null; + // Apply effects + playerData.research.effects = playerData.research.effects || {}; + for (const [k, v] of Object.entries(tech.effects || {})) { + playerData.research.effects[k] = (playerData.research.effects[k] || 0) + v; + } + return tech; + } + + /** Cancel in-progress research — refund 75% */ + cancelResearch(playerData) { + const ip = playerData.research?.inProgress; + if (!ip) throw new Error('No research in progress'); + const tech = this.tree[ip.techId]; + playerData.stats.credits += Math.floor(tech.cost.credits * 0.75); + playerData.stats.gems += Math.floor(tech.cost.gems * 0.75); + playerData.research.inProgress = null; + return tech; + } +} + +module.exports = { ResearchSystem, RESEARCH_TREE }; diff --git a/Galaxy-Strike-Online-main/GameServer/systems/ResourceSystem.js b/Galaxy-Strike-Online-main/GameServer/systems/ResourceSystem.js new file mode 100644 index 0000000..44eb100 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/systems/ResourceSystem.js @@ -0,0 +1,146 @@ +/** + * Galaxy Strike Online — Resource System + * GDD §5: Metal, Gas, Crystal, Energy Cells, Dark Matter + * Handles production formulas, storage caps, and economy tick integration. + */ + +const RESOURCE_TYPES = ['metal', 'gas', 'crystal', 'energyCells', 'darkMatter']; + +// GDD §5.1 base rates & storage +const RESOURCE_CONFIG = { + metal: { baseRate: 100, storageBase: 10000, icon: '⚙', color: '#9e9e9e', label: 'Metal' }, + gas: { baseRate: 60, storageBase: 5000, icon: '☁', color: '#4fc3f7', label: 'Gas' }, + crystal: { baseRate: 40, storageBase: 5000, icon: '💎', color: '#ce93d8', label: 'Crystal' }, + energyCells: { baseRate: 200, storageBase: 8000, icon: '⚡', color: '#fff176', label: 'Energy Cells' }, + darkMatter: { baseRate: 5, storageBase: 500, icon: '✦', color: '#b39ddb', label: 'Dark Matter' }, +}; + +// Building → resource production mapping (GDD §5.2, §6.2) +const BUILDING_PRODUCTION = { + mining_facility: { metal: { base: 100, perLevel: 0.15 } }, + gas_extractor: { gas: { base: 60, perLevel: 0.15 } }, + crystal_refinery: { crystal: { base: 40, perLevel: 0.12 } }, + power_reactor: { energyCells:{ base: 200, perLevel: 0.20 } }, + // Dark matter only from endgame structures +}; + +// Building → storage bonus +const BUILDING_STORAGE = { + storage_depot: { all: 2000 }, + command_center:{ all: 500 }, +}; + +class ResourceSystem { + constructor() { + this.tickIntervalMs = 60000; // 60-second economy tick per GDD §18.2 + } + + /** Initialise resources for a new player */ + initResources(playerData) { + if (playerData.resources) return; // already initialised + playerData.resources = { + metal: 500, + gas: 200, + crystal: 100, + energyCells: 300, + darkMatter: 0, + lastTick: Date.now(), + }; + } + + /** + * Calculate per-hour production rates from buildings. + * GDD formula: output = base_rate × (1 + 0.15 × level) × research_mult × sector_richness + */ + getProductionRates(playerData) { + const buildings = playerData.buildings || {}; + const research = playerData.research?.effects || {}; + const rates = { metal: 0, gas: 0, crystal: 0, energyCells: 0, darkMatter: 0 }; + + for (const [buildId, prod] of Object.entries(BUILDING_PRODUCTION)) { + const bld = buildings[buildId]; + if (!bld || bld.level < 1) continue; + for (const [res, cfg] of Object.entries(prod)) { + const researchMult = 1 + ((research[`${res}Bonus`] || research['miningBonus'] || 0) / 100); + rates[res] += Math.floor(cfg.base * (1 + cfg.perLevel * bld.level) * researchMult); + } + } + return rates; + } + + /** Calculate storage caps */ + getStorageCaps(playerData) { + const buildings = playerData.buildings || {}; + const caps = {}; + for (const [res, cfg] of Object.entries(RESOURCE_CONFIG)) { + let cap = cfg.storageBase; + for (const [buildId, bonus] of Object.entries(BUILDING_STORAGE)) { + const bld = buildings[buildId]; + if (!bld || bld.level < 1) continue; + if (bonus.all) cap += bonus.all * bld.level; + if (bonus[res]) cap += bonus[res] * bld.level; + } + caps[res] = cap; + } + return caps; + } + + /** + * Economy tick — called server-side every 60s. + * Returns { produced, capped } summary. + */ + tick(playerData) { + this.initResources(playerData); + const now = Date.now(); + const elapsed = (now - (playerData.resources.lastTick || now)) / 3600000; // hours + if (elapsed <= 0) return { produced: {}, capped: {} }; + + const rates = this.getProductionRates(playerData); + const caps = this.getStorageCaps(playerData); + const res = playerData.resources; + const produced = {}; + const capped = {}; + + for (const r of RESOURCE_TYPES) { + const gain = Math.floor(rates[r] * elapsed); + if (gain <= 0) continue; + produced[r] = gain; + const before = res[r] || 0; + res[r] = Math.min(before + gain, caps[r]); + if (res[r] === caps[r] && before + gain > caps[r]) capped[r] = true; + } + res.lastTick = now; + return { produced, capped }; + } + + /** Spend resources — throws if insufficient */ + spend(playerData, costs) { + this.initResources(playerData); + const res = playerData.resources; + for (const [r, amount] of Object.entries(costs)) { + if ((res[r] || 0) < amount) { + const label = RESOURCE_CONFIG[r]?.label || r; + throw new Error(`Insufficient ${label}: need ${amount}, have ${res[r] || 0}`); + } + } + for (const [r, amount] of Object.entries(costs)) { + res[r] = (res[r] || 0) - amount; + } + } + + /** Add resources (from mining, loot, etc.) */ + add(playerData, gains) { + this.initResources(playerData); + const caps = this.getStorageCaps(playerData); + const res = playerData.resources; + for (const [r, amount] of Object.entries(gains)) { + res[r] = Math.min((res[r] || 0) + amount, caps[r] || 999999); + } + } + + getConfig() { return RESOURCE_CONFIG; } + getTypes() { return RESOURCE_TYPES; } + getBuildingProd() { return BUILDING_PRODUCTION; } +} + +module.exports = { ResourceSystem, RESOURCE_CONFIG, RESOURCE_TYPES }; diff --git a/Galaxy-Strike-Online-main/GameServer/systems/SeasonSystem.js b/Galaxy-Strike-Online-main/GameServer/systems/SeasonSystem.js new file mode 100644 index 0000000..50b9f2d --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/systems/SeasonSystem.js @@ -0,0 +1,69 @@ +/** + * Galaxy Strike Online — Season System (GDD §20.3) + * 90-day seasons with themed content, seasonal leaderboards, and exclusive rewards. + * Season 1: "Dawn of the Void" + */ + +const SEASONS = { + 1: { + id: 1, + name: 'Dawn of the Void', + theme: 'void_cult', + icon: '🌑', + color: '#78909c', + description: 'The Void Cult stirs. Ancient relics emerge from uncharted sectors. Are you ready?', + durationDays: 90, + rewards: { + top1: { title: 'Void Champion', skin: 'void_champion_skin', credits: 50000 }, + top10: { title: 'Void Knight', skin: 'void_knight_skin', credits: 20000 }, + top100:{ title: 'Void Initiate', skin: null, credits: 5000 }, + participation: { title: 'Season 1 Veteran', credits: 1000 }, + }, + bonuses: { + darkMatterBonus: 0.25, // +25% dark matter production + voidCultRepBonus: 0.5, // +50% Void Cult reputation gain + }, + startDate: new Date('2026-01-01'), + endDate: new Date('2026-04-01'), + } +}; + +const CURRENT_SEASON_ID = 1; + +class SeasonSystem { + constructor() { + this.currentSeason = SEASONS[CURRENT_SEASON_ID]; + } + + getCurrentSeason() { + const now = new Date(); + const s = this.currentSeason; + if (!s) return { active: false }; + const daysLeft = Math.max(0, Math.ceil((s.endDate - now) / 86400000)); + const daysTotal = Math.ceil((s.endDate - s.startDate) / 86400000); + const daysDone = Math.max(0, Math.ceil((now - s.startDate) / 86400000)); + return { + active: now >= s.startDate && now <= s.endDate, + season: { ...s, daysLeft, daysTotal, daysDone, progress: Math.round(daysDone/daysTotal*100) }, + }; + } + + getSeasonBonuses() { + return this.currentSeason?.bonuses || {}; + } + + recordSeasonActivity(playerData, activityType, score = 1) { + if (!playerData.seasonProgress) playerData.seasonProgress = {}; + playerData.seasonProgress[CURRENT_SEASON_ID] = playerData.seasonProgress[CURRENT_SEASON_ID] || { score: 0, activities: {} }; + const sp = playerData.seasonProgress[CURRENT_SEASON_ID]; + sp.score += score; + sp.activities[activityType] = (sp.activities[activityType] || 0) + 1; + return sp.score; + } + + getSeasonScore(playerData) { + return playerData.seasonProgress?.[CURRENT_SEASON_ID]?.score || 0; + } +} + +module.exports = { SeasonSystem, SEASONS }; diff --git a/Galaxy-Strike-Online-main/GameServer/systems/ShipSystem.js b/Galaxy-Strike-Online-main/GameServer/systems/ShipSystem.js new file mode 100644 index 0000000..46570dd --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/systems/ShipSystem.js @@ -0,0 +1,488 @@ +/** + * Galaxy Strike Online - Server Ship System + * Manages ship data, upgrades, and combat mechanics + */ + +class ShipSystem { + constructor() { + this.shipTemplates = new Map(); + this.playerShips = new Map(); // userId -> array of ships + this.initializeShipTemplates(); + } + + initializeShipTemplates() { + // Starter Ships + this.addShipTemplate('starter_cruiser', { + name: 'Starter Cruiser', + class: 'Cruiser', + description: 'A reliable cruiser for beginners', + rarity: 'Common', + baseStats: { + health: 100, + maxHealth: 100, + attack: 10, + defense: 5, + speed: 10, + energy: 50, + maxEnergy: 50 + }, + slots: { + weapon: 2, + armor: 1, + module: 1 + }, + requirements: { + level: 1, + credits: 0 + }, + experience: 0, + requiredExp: 100, + texture: 'assets/textures/ships/starter_cruiser.png' + }); + + this.addShipTemplate('light_fighter', { + name: 'Light Fighter', + class: 'Fighter', + description: 'Fast and agile fighter ship', + rarity: 'Common', + baseStats: { + health: 80, + maxHealth: 80, + attack: 15, + defense: 3, + speed: 15, + energy: 40, + maxEnergy: 40 + }, + slots: { + weapon: 3, + armor: 0, + module: 2 + }, + requirements: { + level: 3, + credits: 1000 + }, + experience: 0, + requiredExp: 150, + texture: 'assets/textures/ships/light_fighter.png' + }); + + this.addShipTemplate('heavy_bomber', { + name: 'Heavy Bomber', + class: 'Bomber', + description: 'Slow but powerful bomber', + rarity: 'Uncommon', + baseStats: { + health: 150, + maxHealth: 150, + attack: 25, + defense: 10, + speed: 5, + energy: 60, + maxEnergy: 60 + }, + slots: { + weapon: 4, + armor: 2, + module: 1 + }, + requirements: { + level: 5, + credits: 5000 + }, + experience: 0, + requiredExp: 200, + texture: 'assets/textures/ships/heavy_bomber.png' + }); + + this.addShipTemplate('exploration_vessel', { + name: 'Exploration Vessel', + class: 'Explorer', + description: 'Versatile ship for exploration missions', + rarity: 'Rare', + baseStats: { + health: 120, + maxHealth: 120, + attack: 12, + defense: 8, + speed: 12, + energy: 80, + maxEnergy: 80 + }, + slots: { + weapon: 2, + armor: 1, + module: 3 + }, + requirements: { + level: 8, + credits: 15000 + }, + experience: 0, + requiredExp: 300, + texture: 'assets/textures/ships/exploration_vessel.png' + }); + + this.addShipTemplate('battle_cruiser', { + name: 'Battle Cruiser', + class: 'Cruiser', + description: 'Heavy combat cruiser', + rarity: 'Rare', + baseStats: { + health: 200, + maxHealth: 200, + attack: 30, + defense: 15, + speed: 8, + energy: 70, + maxEnergy: 70 + }, + slots: { + weapon: 5, + armor: 3, + module: 2 + }, + requirements: { + level: 12, + credits: 50000 + }, + experience: 0, + requiredExp: 500, + texture: 'assets/textures/ships/battle_cruiser.png' + }); + + this.addShipTemplate('flagship', { + name: 'Flagship', + class: 'Capital', + description: 'Ultimate command vessel', + rarity: 'Legendary', + baseStats: { + health: 500, + maxHealth: 500, + attack: 50, + defense: 30, + speed: 6, + energy: 150, + maxEnergy: 150 + }, + slots: { + weapon: 8, + armor: 5, + module: 4 + }, + requirements: { + level: 20, + credits: 500000 + }, + experience: 0, + requiredExp: 1000, + texture: 'assets/textures/ships/flagship.png' + }); + } + + addShipTemplate(id, template) { + this.shipTemplates.set(id, { + id, + ...template, + createdAt: new Date().toISOString() + }); + } + + getShipTemplate(id) { + return this.shipTemplates.get(id); + } + + getShipTemplatesByClass(shipClass) { + return Array.from(this.shipTemplates.values()).filter(template => template.class === shipClass); + } + + getShipTemplatesByRarity(rarity) { + return Array.from(this.shipTemplates.values()).filter(template => template.rarity === rarity); + } + + getAllShipTemplates() { + return Array.from(this.shipTemplates.values()); + } + + initializePlayerShips(userId) { + if (!this.playerShips.has(userId)) { + this.playerShips.set(userId, []); + + // Give starter ship to new players + const starterShip = this.createShip(userId, 'starter_cruiser'); + if (starterShip) { + this.playerShips.get(userId).push(starterShip); + } + } + return this.playerShips.get(userId); + } + + createShip(userId, templateId, customizations = {}) { + const template = this.shipTemplates.get(templateId); + if (!template) { + throw new Error(`Ship template not found: ${templateId}`); + } + + const ship = { + id: `ship_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, + userId, + templateId, + name: customizations.name || template.name, + class: template.class, + rarity: template.rarity, + level: customizations.level || 1, + experience: 0, + requiredExp: template.requiredExp, + stats: { ...template.baseStats }, + maxStats: { ...template.baseStats }, + slots: { ...template.slots }, + equippedItems: {}, + texture: template.texture, + status: 'active', + createdAt: new Date().toISOString(), + lastUsed: new Date().toISOString() + }; + + return ship; + } + + getPlayerShips(userId) { + return this.playerShips.get(userId) || this.initializePlayerShips(userId); + } + + getShip(userId, shipId) { + const ships = this.getPlayerShips(userId); + return ships.find(ship => ship.id === shipId); + } + + getCurrentShip(userId) { + const ships = this.getPlayerShips(userId); + return ships.find(ship => ship.status === 'active' && ship.lastUsed === Math.max(...ships.map(s => new Date(s.lastUsed).getTime()))); + } + + setCurrentShip(userId, shipId) { + const ships = this.getPlayerShips(userId); + const ship = ships.find(s => s.id === shipId); + + if (!ship) { + throw new Error('Ship not found'); + } + + // Update last used time for all ships + ships.forEach(s => { + if (s.id === shipId) { + s.status = 'active'; + s.lastUsed = new Date().toISOString(); + } else { + s.status = 'inactive'; + } + }); + + return ship; + } + + addExperience(userId, shipId, amount) { + const ship = this.getShip(userId, shipId); + if (!ship) { + throw new Error('Ship not found'); + } + + ship.experience += amount; + + // Check for level up + let levelsGained = 0; + while (ship.experience >= ship.requiredExp) { + ship.experience -= ship.requiredExp; + ship.level += 1; + levelsGained++; + + // Increase stats on level up + this.levelUpShip(ship); + + // Update required experience for next level + ship.requiredExp = this.getExperienceNeeded(ship.level); + } + + return { + experienceGained: amount, + levelsGained, + newLevel: ship.level, + newStats: ship.stats + }; + } + + levelUpShip(ship) { + const statIncrease = 1.1; // 10% increase per level + + for (const [stat, value] of Object.entries(ship.stats)) { + if (typeof value === 'number') { + ship.stats[stat] = Math.floor(value * statIncrease); + ship.maxStats[stat] = Math.floor(ship.maxStats[stat] * statIncrease); + } + } + } + + getExperienceNeeded(level) { + // Exponential experience curve + return Math.floor(100 * Math.pow(1.5, level - 1)); + } + + upgradeShip(userId, shipId, upgradeType) { + const ship = this.getShip(userId, shipId); + if (!ship) { + throw new Error('Ship not found'); + } + + switch (upgradeType) { + case 'health': + ship.stats.health = Math.floor(ship.stats.health * 1.2); + ship.stats.maxHealth = Math.floor(ship.stats.maxHealth * 1.2); + break; + case 'attack': + ship.stats.attack = Math.floor(ship.stats.attack * 1.15); + break; + case 'defense': + ship.stats.defense = Math.floor(ship.stats.defense * 1.15); + break; + case 'speed': + ship.stats.speed = Math.floor(ship.stats.speed * 1.1); + break; + case 'energy': + ship.stats.energy = Math.floor(ship.stats.energy * 1.2); + ship.stats.maxEnergy = Math.floor(ship.stats.maxEnergy * 1.2); + break; + default: + throw new Error('Invalid upgrade type'); + } + + return { + upgradeType, + newStats: ship.stats + }; + } + + equipItem(userId, shipId, slot, itemId) { + const ship = this.getShip(userId, shipId); + if (!ship) { + throw new Error('Ship not found'); + } + + if (!ship.slots[slot]) { + throw new Error('Invalid slot type'); + } + + ship.equippedItems[slot] = itemId; + + return { + slot, + itemId, + equippedItems: ship.equippedItems + }; + } + + unequipItem(userId, shipId, slot) { + const ship = this.getShip(userId, shipId); + if (!ship) { + throw new Error('Ship not found'); + } + + const itemId = ship.equippedItems[slot]; + delete ship.equippedItems[slot]; + + return { + slot, + itemId, + equippedItems: ship.equippedItems + }; + } + + repairShip(userId, shipId, amount = null) { + const ship = this.getShip(userId, shipId); + if (!ship) { + throw new Error('Ship not found'); + } + + if (amount === null) { + // Full repair + ship.stats.health = ship.stats.maxHealth; + } else { + // Partial repair + ship.stats.health = Math.min(ship.stats.health + amount, ship.stats.maxHealth); + } + + return { + health: ship.stats.health, + maxHealth: ship.stats.maxHealth, + repaired: amount || (ship.stats.maxHealth - ship.stats.health) + }; + } + + deleteShip(userId, shipId) { + const ships = this.getPlayerShips(userId); + const shipIndex = ships.findIndex(ship => ship.id === shipId); + + if (shipIndex === -1) { + throw new Error('Ship not found'); + } + + const deletedShip = ships.splice(shipIndex, 1)[0]; + + // If this was the current ship, set a new current ship + if (deletedShip.status === 'active' && ships.length > 0) { + ships[0].status = 'active'; + ships[0].lastUsed = new Date().toISOString(); + } + + return deletedShip; + } + + getShipClasses() { + const classes = new Set(); + for (const template of this.shipTemplates.values()) { + classes.add(template.class); + } + return Array.from(classes).sort(); + } + + getShipRarities() { + const rarities = new Set(); + for (const template of this.shipTemplates.values()) { + rarities.add(template.rarity); + } + return Array.from(rarities).sort(); + } + + getAvailableShips(userId, playerLevel = 1) { + return Array.from(this.shipTemplates.values()) + .filter(template => template.requirements.level <= playerLevel) + .map(template => ({ + id: template.id, + name: template.name, + class: template.class, + rarity: template.rarity, + description: template.description, + requirements: template.requirements, + baseStats: template.baseStats, + slots: template.slots, + texture: template.texture + })); + } + + getPlayerShipStats(userId) { + const ships = this.getPlayerShips(userId); + + return { + totalShips: ships.length, + activeShip: this.getCurrentShip(userId), + shipClasses: ships.reduce((acc, ship) => { + acc[ship.class] = (acc[ship.class] || 0) + 1; + return acc; + }, {}), + averageLevel: ships.length > 0 ? Math.floor(ships.reduce((sum, ship) => sum + ship.level, 0) / ships.length) : 0, + totalExperience: ships.reduce((sum, ship) => sum + ship.experience, 0) + }; + } +} + +module.exports = ShipSystem; diff --git a/Galaxy-Strike-Online-main/GameServer/systems/SkillSystem.js b/Galaxy-Strike-Online-main/GameServer/systems/SkillSystem.js new file mode 100644 index 0000000..983cced --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/systems/SkillSystem.js @@ -0,0 +1,29 @@ +/** + * SkillSystem — thin wrapper around ContentLoader for skill definitions. + * Player state is managed in-memory here. + */ +class SkillSystem { + constructor(contentLoader) { + this.loader = contentLoader; + this.playerSkills = new Map(); // userId -> player data + } + + getAllSkills() { return this.loader.getAllSkills(); } + getSkill(id) { return this.loader.getSkill(id); } + getSkillsByCategory(cat) { return this.loader.getSkillsByCategory(cat); } + + initializePlayerData(userId) { + if (this.playerSkills.has(userId)) return this.playerSkills.get(userId); + const defaultUnlocked = this.loader.getAllSkills() + .filter(s => s.defaultUnlocked) + .map(s => s.id); + const skills = {}; + for (const id of defaultUnlocked) skills[id] = { level: 1, experience: 0 }; + const data = { skillPoints: 0, skills, unlockedSkills: new Set(defaultUnlocked) }; + this.playerSkills.set(userId, data); + return data; + } + + getPlayerSkills(userId) { return this.initializePlayerData(userId); } +} +module.exports = SkillSystem; diff --git a/Galaxy-Strike-Online-main/GameServer/systems/SocialSystem.js b/Galaxy-Strike-Online-main/GameServer/systems/SocialSystem.js new file mode 100644 index 0000000..4f26dde --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/systems/SocialSystem.js @@ -0,0 +1,113 @@ +/** + * Galaxy Strike Online — Social System (GDD §17.2) + * Friends list, online status, sector tracking, gift system (≤5k credits/day) + */ + +const mongoose = require('mongoose'); + +const socialSchema = new mongoose.Schema({ + userId: { type: String, required: true, unique: true }, + username: { type: String, default: '' }, + friends: [{ userId: String, username: String, addedAt: Date }], + friendRequests: [{ // pending incoming + fromId: String, fromName: String, sentAt: { type: Date, default: Date.now } + }], + giftsGivenToday: { type: Number, default: 0 }, + lastGiftReset: { type: Date, default: Date.now }, + // Combat log entries (GDD §9.5) — last 50 + combatLog: [{ type: mongoose.Schema.Types.Mixed }], +}, { timestamps: true }); + +const SocialData = mongoose.model('SocialData', socialSchema); + +const DAILY_GIFT_LIMIT = 5000; + +class SocialSystem { + constructor() { this.SocialData = SocialData; } + + async getOrCreate(userId, username) { + let doc = await SocialData.findOne({ userId }); + if (!doc) { doc = new SocialData({ userId, username }); await doc.save(); } + return doc; + } + + async sendFriendRequest(senderId, senderName, targetUsername, onlinePlayers) { + // Find target in online players first, then DB + let targetId = null, targetName = null; + for (const [, cd] of onlinePlayers) { + if (cd.username?.toLowerCase() === targetUsername.toLowerCase()) { + targetId = cd.userId; targetName = cd.username; break; + } + } + if (!targetId) { + const pd = await SocialData.findOne({ username: new RegExp('^' + targetUsername + '$', 'i') }); + if (pd) { targetId = pd.userId; targetName = pd.username; } + } + if (!targetId) throw new Error(`Player "${targetUsername}" not found`); + if (targetId === senderId) throw new Error('Cannot add yourself'); + + const targetDoc = await this.getOrCreate(targetId, targetName); + if (targetDoc.friends.some(f => f.userId === senderId)) throw new Error('Already friends'); + if (targetDoc.friendRequests.some(r => r.fromId === senderId)) throw new Error('Request already sent'); + + targetDoc.friendRequests.push({ fromId: senderId, fromName: senderName }); + await targetDoc.save(); + return { targetId, targetName }; + } + + async acceptFriendRequest(userId, username, fromId, fromName) { + const myDoc = await this.getOrCreate(userId, username); + const theirDoc = await this.getOrCreate(fromId, fromName); + const idx = myDoc.friendRequests.findIndex(r => r.fromId === fromId); + if (idx < 0) throw new Error('No pending request from that player'); + myDoc.friendRequests.splice(idx, 1); + if (!myDoc.friends.some(f => f.userId === fromId)) myDoc.friends.push({ userId: fromId, username: fromName, addedAt: new Date() }); + if (!theirDoc.friends.some(f => f.userId === userId)) theirDoc.friends.push({ userId, username, addedAt: new Date() }); + await myDoc.save(); await theirDoc.save(); + } + + async removeFriend(userId, friendId) { + await SocialData.updateOne({ userId }, { $pull: { friends: { userId: friendId } } }); + await SocialData.updateOne({ userId: friendId }, { $pull: { friends: { userId } } }); + } + + async sendGift(senderData, targetId, amount) { + if (amount > DAILY_GIFT_LIMIT || amount <= 0) throw new Error(`Gift must be 1–${DAILY_GIFT_LIMIT} credits`); + const myDoc = await this.getOrCreate(senderData.userId, senderData.username); + // Reset daily counter if needed + const today = new Date(); today.setHours(0,0,0,0); + if (myDoc.lastGiftReset < today) { myDoc.giftsGivenToday = 0; myDoc.lastGiftReset = today; } + if (myDoc.giftsGivenToday + amount > DAILY_GIFT_LIMIT) throw new Error(`Daily gift limit is ${DAILY_GIFT_LIMIT} credits`); + if ((senderData.stats?.credits || 0) < amount) throw new Error('Insufficient credits'); + + senderData.stats.credits -= amount; + myDoc.giftsGivenToday += amount; + await myDoc.save(); + return { targetId, amount }; + } + + async getFriendsList(userId, username, onlinePlayers) { + const doc = await this.getOrCreate(userId, username); + const onlineIds = new Set(); + for (const [, cd] of onlinePlayers) if (cd.userId) onlineIds.add(cd.userId); + return { + friends: doc.friends.map(f => ({ ...f.toObject?.() || f, online: onlineIds.has(f.userId) })), + requests: doc.friendRequests, + }; + } + + async addCombatLogEntry(userId, entry) { + await SocialData.findOneAndUpdate( + { userId }, + { $push: { combatLog: { $each: [entry], $slice: -50 } } }, + { upsert: true } + ); + } + + async getCombatLog(userId) { + const doc = await SocialData.findOne({ userId }); + return (doc?.combatLog || []).reverse(); + } +} + +module.exports = SocialSystem; diff --git a/Galaxy-Strike-Online-main/GameServer/utils/logger.js b/Galaxy-Strike-Online-main/GameServer/utils/logger.js new file mode 100644 index 0000000..dbd08a5 --- /dev/null +++ b/Galaxy-Strike-Online-main/GameServer/utils/logger.js @@ -0,0 +1,27 @@ +const winston = require('winston'); + +const logger = winston.createLogger({ + level: process.env.LOG_LEVEL || 'info', + format: winston.format.combine( + winston.format.timestamp(), + winston.format.errors({ stack: true }), + winston.format.json() + ), + defaultMeta: { service: 'galaxystrikeonline-game-server' }, + transports: [ + new winston.transports.File({ filename: 'logs/error.log', level: 'error' }), + new winston.transports.File({ filename: 'logs/combined.log' }) + ] +}); + +// If not in production, also log to console +if (process.env.NODE_ENV !== 'production') { + logger.add(new winston.transports.Console({ + format: winston.format.combine( + winston.format.colorize(), + winston.format.simple() + ) + })); +} + +module.exports = logger; diff --git a/Galaxy-Strike-Online-main/LICENSE b/Galaxy-Strike-Online-main/LICENSE new file mode 100644 index 0000000..433f005 --- /dev/null +++ b/Galaxy-Strike-Online-main/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Korvarix + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Galaxy-Strike-Online-main/Wiki/SETUP_GUIDE.html b/Galaxy-Strike-Online-main/Wiki/SETUP_GUIDE.html new file mode 100644 index 0000000..6a9c219 --- /dev/null +++ b/Galaxy-Strike-Online-main/Wiki/SETUP_GUIDE.html @@ -0,0 +1,256 @@ + + + + + + Galaxy Strike Online - Complete Setup Guide + + + +
+

Galaxy Strike Online - Complete Setup Guide

+

This comprehensive guide will walk you through setting up the entire Galaxy Strike Online project from scratch.

+ +

Table of Contents

+
    +
  1. Prerequisites
  2. +
  3. Project Download
  4. +
  5. Server Setup
  6. +
  7. Client Setup
  8. +
  9. Database Setup
  10. +
  11. Running the Application
  12. +
  13. Testing
  14. +
  15. Troubleshooting
  16. +
+ +

1. Prerequisites

+
+

Required Software:

+
    +
  • Node.js 18.0.0 or higher - Download from nodejs.org
  • +
  • MongoDB - Local installation or MongoDB Atlas account
  • +
  • Git - For version control (optional but recommended)
  • +
  • Code Editor - VS Code recommended
  • +
+
+ +
+ Important: Make sure Node.js is version 18.0.0 or higher. Check with node --version +
+ +

2. Project Download

+
+

Option A: Clone from Git Repository

+
git clone <repository-url>
+cd galaxystrikeonline
+
+ +
+

Option B: Download ZIP File

+
    +
  1. Download the project ZIP file
  2. +
  3. Extract to your desired location
  4. +
  5. Navigate to the project directory
  6. +
+
+ +

3. Server Setup

+
+

Step 1: Navigate to Server Directory

+
cd Server
+
+ +
+

Step 2: Install Dependencies

+
npm install
+
+ +
+

Step 3: Create Environment File

+
cp .env.example .env
+
+ +
+

Step 4: Configure Environment Variables

+

Edit the .env file with your configuration:

+
PORT=3001
+MONGODB_URI=mongodb://localhost:27017/galaxystrikeonline
+JWT_SECRET=your_jwt_secret_key_here
+CLIENT_URL=http://localhost:3000
+
+ +
+ Important: Replace your_jwt_secret_key_here with a secure random string. +
+ +
+

Step 5: Database Migration

+
npm run migrate
+
+ +
+

Step 6: Seed Database

+
npm run seed
+
+ +

4. Client Setup

+
+

Step 1: Navigate to Website Directory

+
cd ../Website
+
+ +
+

Step 2: Install Dependencies

+
npm install
+
+ +
+

Step 3: Create Environment File

+
cp .env.example .env
+
+ +
+

Step 4: Configure Environment Variables

+

Edit the .env file:

+
VITE_API_BASE_URL=http://localhost:3001/api
+VITE_SOCKET_URL=http://localhost:3001
+
+ +

6. Running the Application

+
+

Step 1: Start MongoDB

+
    +
  • Local: Start MongoDB service
  • +
  • Atlas: Ensure cluster is running
  • +
+
+ +
+

Step 2: Start Server

+
cd Server
+npm run dev
+

Server will start on port 3001

+
+ +
+

Step 3: Start Client

+
cd ../Website
+npm run dev
+

Client will start on port 3000

+
+ +
+

Step 4: Access Application

+

Open browser and navigate to: http://localhost:3000

+
+ +

8. Troubleshooting

+
+

Common Issues:

+ +

1. MongoDB Connection Error

+
    +
  • Ensure MongoDB is running
  • +
  • Check connection string in .env file
  • +
  • Verify network connectivity
  • +
+ +

2. Port Already in Use

+
    +
  • Check if ports 3000 or 3001 are occupied
  • +
  • Use netstat -ano | findstr :3000
  • +
  • Kill conflicting processes
  • +
+ +

3. Module Installation Errors

+
    +
  • Clear npm cache: npm cache clean --force
  • +
  • Delete node_modules and package-lock.json
  • +
  • Run npm install again
  • +
+
+ +
+

Success Indicators:

+
    +
  • Server starts without errors on port 3001
  • +
  • Client starts without errors on port 3000
  • +
  • Can register and login successfully
  • +
  • Socket.IO connection established
  • +
+
+
+ + \ No newline at end of file diff --git a/Galaxy-Strike-Online-main/config/xp-progression.js b/Galaxy-Strike-Online-main/config/xp-progression.js new file mode 100644 index 0000000..3c49d8a --- /dev/null +++ b/Galaxy-Strike-Online-main/config/xp-progression.js @@ -0,0 +1,250 @@ +/** + * Galaxy Strike Online - XP Progression Configuration + * Math settings from https://math.korvarix.com/ + * + * Decoded configuration parameters: + * - Current Level: 1 + * - Max Level: 50 + * - Formula parameters with multiple progression curves enabled + */ + +const XP_CONFIG = { + // Basic settings + currentLevel: 1, + maxLevel: 50, + + // Formula parameters (decoded from the website) + formula: { + // Linear progression parameters + linearBase: 500, // lb: 500 + linearExponent: 2.2, // le: 2.2 + + // Base curve parameters + curveBase: 12.5, // kb: 12.5 + curveExponent: 1.4, // ke: 1.4 + + // Multiplier parameters + multiplier: 0.11667, // mqm: 0.11667 + squareMultiplier: 0.05, // sqm: 0.05 + divisionMultiplier: 0.125, // dm: 0.125 + rootMultiplier: 0.25, // rm: 0.25 + constant: 0.5, // kt: 0.5 + + // Threshold parameters + multiplierThreshold: 15, // mqt: 15 + squareThreshold: 10, // sqt: 10 + divisionThreshold: 45 // dt: 45 + }, + + // Enabled formula types + enabled: { + linear: true, // l: true + curve: true, // k: true + multiplier: true, // mq: true + square: true, // sq: true + division: true, // d: true + root: true // r: true + }, + + // Activity XP rewards (based on website estimates) + activities: { + combat: { + tutorial: 10, + easy: 25, + normal: 50, + hard: 100, + extreme: 200, + legendary: 500, + mythic: 1000 + }, + quests: { + main: 200, + side: 50, + daily_easy: 25, + daily_medium: 75, + daily_hard: 150, + daily_extreme: 300, + weekly_basic: 200, + weekly_medium: 500, + weekly_hard: 800, + weekly_extreme: 1200, + weekly_legendary: 2000, + weekly_mythic: 3000 + }, + dungeons: { + tutorial: 50, + easy: 100, + medium: 250, + hard: 500, + extreme: 1000, + legendary: 2000, + mythic: 5000 + }, + raids: { + weekly: 1000, + monthly: 5000 + } + } +}; + +/** + * Calculate XP required for a specific level + * Uses the complex formula from the website configuration + * @param {number} level - The level to calculate XP for + * @returns {number} XP required for this level + */ +function calculateXPForLevel(level) { + if (level <= 1) return 0; + if (level > XP_CONFIG.maxLevel) return Infinity; + + const f = XP_CONFIG.formula; + const enabled = XP_CONFIG.enabled; + let xp = 0; + + // Base linear progression: lb * level^le + if (enabled.linear) { + xp += f.linearBase * Math.pow(level, f.linearExponent); + } + + // Curve progression: kb * level^ke + if (enabled.curve) { + xp += f.curveBase * Math.pow(level, f.curveExponent); + } + + // Multiplier progression (after threshold) + if (enabled.multiplier && level > f.multiplierThreshold) { + const multiplierBonus = (level - f.multiplierThreshold) * f.multiplier; + xp *= (1 + multiplierBonus); + } + + // Square progression (after threshold) + if (enabled.square && level > f.squareThreshold) { + const squareBonus = Math.pow(level - f.squareThreshold, 2) * f.squareMultiplier; + xp += squareBonus; + } + + // Division progression (after threshold) + if (enabled.division && level > f.divisionThreshold) { + const divisionBonus = (level - f.divisionThreshold) * f.divisionMultiplier; + xp *= (1 + divisionBonus); + } + + // Root progression + if (enabled.root) { + const rootBonus = Math.sqrt(level) * f.rootMultiplier; + xp += rootBonus; + } + + // Add constant + xp += f.constant; + + return Math.floor(xp); +} + +/** + * Calculate total XP needed to reach from level 1 to target level + * @param {number} targetLevel - The target level + * @returns {number} Total XP needed + */ +function calculateTotalXPToLevel(targetLevel) { + let totalXP = 0; + for (let level = 2; level <= targetLevel; level++) { + totalXP += calculateXPForLevel(level); + } + return totalXP; +} + +/** + * Calculate XP remaining until next level up + * @param {number} currentLevel - Current player level + * @param {number} currentXP - Current player XP + * @returns {number} XP needed for next level + */ +function calculateXPToNextLevel(currentLevel, currentXP) { + if (currentLevel >= XP_CONFIG.maxLevel) return 0; + + const xpForNextLevel = calculateXPForLevel(currentLevel + 1); + const totalXPToCurrentLevel = calculateTotalXPToLevel(currentLevel); + + return Math.max(0, xpForNextLevel - currentXP); +} + +/** + * Get level from total XP + * @param {number} totalXP - Total accumulated XP + * @returns {object} Level info { level, xpIntoLevel, xpToNext, progress } + */ +function getLevelFromXP(totalXP) { + let level = 1; + let cumulativeXP = 0; + + // Find current level + while (level < XP_CONFIG.maxLevel) { + const xpForNextLevel = calculateXPForLevel(level + 1); + if (cumulativeXP + xpForNextLevel > totalXP) { + break; + } + cumulativeXP += xpForNextLevel; + level++; + } + + const xpIntoLevel = totalXP - cumulativeXP; + const xpToNext = calculateXPToNextLevel(level, totalXP); + const progress = xpToNext > 0 ? xpIntoLevel / (xpIntoLevel + xpToNext) : 1; + + return { + level, + xpIntoLevel, + xpToNext, + progress: Math.min(1, Math.max(0, progress)) + }; +} + +/** + * Generate XP progression table for all levels + * @returns {Array} Array of level data + */ +function generateProgressionTable() { + const table = []; + let cumulativeXP = 0; + + for (let level = 1; level <= XP_CONFIG.maxLevel; level++) { + const xpForThisLevel = level === 1 ? 0 : calculateXPForLevel(level); + const xpToNext = level < XP_CONFIG.maxLevel ? calculateXPForLevel(level + 1) : 0; + + table.push({ + level, + xpForThisLevel, + xpToNext, + cumulativeXP, + totalXPToLevel: cumulativeXP + }); + + cumulativeXP += xpForThisLevel; + } + + return table; +} + +// Export for use in both client and server +if (typeof module !== 'undefined' && module.exports) { + // Node.js (server) + module.exports = { + XP_CONFIG, + calculateXPForLevel, + calculateTotalXPToLevel, + calculateXPToNextLevel, + getLevelFromXP, + generateProgressionTable + }; +} else { + // Browser (client) + window.XPProgression = { + XP_CONFIG, + calculateXPForLevel, + calculateTotalXPToLevel, + calculateXPToNextLevel, + getLevelFromXP, + generateProgressionTable + }; +} diff --git a/Galaxy-Strike-Online-main/docs/Server-Modding-Guide.md b/Galaxy-Strike-Online-main/docs/Server-Modding-Guide.md new file mode 100644 index 0000000..b6f52a7 --- /dev/null +++ b/Galaxy-Strike-Online-main/docs/Server-Modding-Guide.md @@ -0,0 +1,376 @@ +# Server Modding Guide + +This guide explains how to create and install mods for the Galaxy Strike Online server. + +## Overview + +The Galaxy Strike Online server supports modding through a local SQLite database that stores mod data and assets. Mods can add new ships, items, quests, and game systems to the server. + +## Mod Directory Structure + +All mods are stored in the `GameServer/mods/` directory. Each mod has its own folder with the following structure: + +``` +GameServer/mods/ +├── your-mod-name/ +│ ├── mod.json # Mod manifest file +│ └── assets/ # Mod assets folder +│ ├── ships/ # Ship definitions +│ ├── items/ # Item definitions +│ ├── quests/ # Quest definitions +│ └── systems/ # Game system modifications +``` + +## Mod Manifest (mod.json) + +Every mod must include a `mod.json` file in its root directory with the following structure: + +```json +{ + "name": "Your Mod Name", + "version": "1.0.0", + "author": "Your Name", + "description": "A brief description of your mod", + "dependencies": ["other-mod-name"], + "config": { + "setting1": "value1", + "setting2": 42 + } +} +``` + +### Required Fields +- `name` - Unique identifier for your mod +- `version` - Version string (semantic versioning recommended) +- `author` - Mod author name + +### Optional Fields +- `description` - Brief description of what your mod does +- `dependencies` - Array of mod names that must be loaded first +- `config` - Configuration options for your mod + +## Asset Types + +### Ships + +Place ship definitions in `assets/ships/` as JSON files: + +```json +{ + "id": "custom_fighter", + "name": "Custom Fighter", + "description": "A powerful custom fighter ship", + "rarity": "rare", + "stats": { + "health": 150, + "maxHealth": 150, + "attack": 25, + "defense": 15, + "speed": 20, + "criticalChance": 0.10, + "criticalDamage": 2.0 + }, + "abilities": [ + { + "id": "laser_burst", + "name": "Laser Burst", + "damage": 50, + "cooldown": 5 + } + ], + "requirements": { + "level": 10, + "credits": 5000 + } +} +``` + +### Items + +Place item definitions in `assets/items/` as JSON files: + +```json +{ + "id": "custom_blaster", + "name": "Custom Blaster", + "description": "A powerful custom blaster", + "type": "weapon", + "rarity": "epic", + "stats": { + "attack": 30, + "criticalChance": 0.15, + "criticalDamage": 2.5 + }, + "requirements": { + "level": 15, + "class": "fighter" + }, + "value": 2500 +} +``` + +### Quests + +Place quest definitions in `assets/quests/` as JSON files: + +```json +{ + "id": "custom_mission", + "name": "Custom Mission", + "description": "A special custom mission", + "type": "combat", + "difficulty": "medium", + "objectives": [ + { + "type": "defeat_enemies", + "target": "enemy_fighter", + "count": 10 + }, + { + "type": "collect_items", + "target": "energy_crystal", + "count": 5 + } + ], + "rewards": { + "experience": 1000, + "credits": 2000, + "items": ["custom_blaster"] + }, + "requirements": { + "level": 12, + "completed_quests": ["basic_training"] + } +} +``` + +### Systems + +Place system modifications in `assets/systems/` as JSON files: + +```json +{ + "id": "custom_combat_system", + "name": "Custom Combat System", + "description": "Modifies combat mechanics", + "type": "combat", + "modifications": { + "damage_formula": "base_damage * (1 + critical_chance * critical_damage)", + "speed_bonus": 1.2, + "defense_reduction": 0.9 + }, + "conditions": { + "player_level": 20, + "active_mods": ["advanced_combat"] + } +} +``` + +## Installation + +### Automatic Installation + +1. Create your mod folder in `GameServer/mods/` +2. Add your `mod.json` manifest and assets +3. Restart the GameServer or use the reload endpoint: + ```bash + curl -X POST "http://localhost:3001/api/mods/reload" \ + -H "Authorization: Bearer YOUR_TOKEN" + ``` + +### Manual Installation via API + +You can also manage mods through the API endpoints: + +#### List all mods +```bash +curl -X GET "http://localhost:3001/api/mods" \ + -H "Authorization: Bearer YOUR_TOKEN" +``` + +#### Enable a mod +```bash +curl -X POST "http://localhost:3001/api/mods/1/enable" \ + -H "Authorization: Bearer YOUR_TOKEN" +``` + +#### Disable a mod +```bash +curl -X POST "http://localhost:3001/api/mods/1/disable" \ + -H "Authorization: Bearer YOUR_TOKEN" +``` + +#### Set mod load order +```bash +curl -X POST "http://localhost:3001/api/mods/load-order" \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"modIds": [1, 3, 2]}' +``` + +## Mod Development Best Practices + +### 1. Use Unique IDs +Always use unique IDs for your assets to avoid conflicts: +```json +{ + "id": "yourmodname_ship_name", + "name": "Ship Name" +} +``` + +### 2. Version Your Mods +Use semantic versioning (major.minor.patch): +- `1.0.0` - Initial release +- `1.1.0` - New features +- `1.1.1` - Bug fixes +- `2.0.0` - Breaking changes + +### 3. Handle Dependencies +If your mod depends on other mods, specify them in `mod.json`: +```json +{ + "dependencies": ["base_game_expansion", "common_items_pack"] +} +``` + +### 4. Test Thoroughly +- Test your mod with and without other popular mods +- Test different player levels and progressions +- Verify all assets load correctly + +### 5. Provide Documentation +Include a README.md in your mod folder with: +- Installation instructions +- Configuration options +- Known issues +- Changelog + +## Configuration + +### Server Preferences +Set server-wide mod preferences via API: +```bash +curl -X POST "http://localhost:3001/api/mods/preferences/server" \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"key": "max_mods_per_player", "value": 10}' +``` + +### Mod-Specific Configuration +Access your mod's configuration in-game: +```javascript +const modConfig = await ModService.getModConfig('your-mod-name'); +``` + +## Troubleshooting + +### Common Issues + +1. **Mod not loading** + - Check `mod.json` syntax + - Verify all required fields are present + - Check server logs for error messages + +2. **Assets not appearing** + - Verify file paths are correct + - Check JSON syntax in asset files + - Ensure assets are in the correct subdirectories + +3. **Load order issues** + - Set explicit load order via API + - Check dependencies are satisfied + - Verify dependent mods are enabled + +4. **Performance issues** + - Limit the number of assets per mod + - Optimize JSON structures + - Use efficient algorithms in system modifications + +### Debug Mode + +Enable debug logging by setting the LOG_LEVEL environment variable: +```bash +LOG_LEVEL=debug npm start +``` + +## API Reference + +### Authentication +All mod management endpoints require JWT authentication. Include your token in the Authorization header: +``` +Authorization: Bearer YOUR_JWT_TOKEN +``` + +### Endpoints + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/api/mods` | List all mods | +| GET | `/api/mods/:id` | Get specific mod | +| POST | `/api/mods/:id/enable` | Enable mod | +| POST | `/api/mods/:id/disable` | Disable mod | +| GET | `/api/mods/assets/:type` | Get assets by type | +| GET | `/api/mods/assets/:type/:assetId` | Get specific asset | +| GET | `/api/mods/load-order` | Get load order | +| POST | `/api/mods/load-order` | Set load order | +| GET | `/api/mods/preferences/server` | Get server preferences | +| POST | `/api/mods/preferences/server` | Set server preference | +| POST | `/api/mods/reload` | Reload mods from filesystem | + +## Example Mod + +Here's a complete example of a simple mod that adds a new ship: + +### File Structure +``` +GameServer/mods/ +└── simple_ship_pack/ + ├── mod.json + └── assets/ + └── ships/ + └── scout_ship.json +``` + +### mod.json +```json +{ + "name": "Simple Ship Pack", + "version": "1.0.0", + "author": "Example Author", + "description": "Adds a simple scout ship to the game" +} +``` + +### assets/ships/scout_ship.json +```json +{ + "id": "simple_ship_pack_scout", + "name": "Scout Ship", + "description": "A fast and agile scout ship", + "rarity": "common", + "stats": { + "health": 80, + "maxHealth": 80, + "attack": 15, + "defense": 8, + "speed": 25, + "criticalChance": 0.08, + "criticalDamage": 1.8 + }, + "abilities": [ + { + "id": "quick_scan", + "name": "Quick Scan", + "description": "Reveals nearby enemies", + "cooldown": 10 + } + ], + "requirements": { + "level": 5, + "credits": 1000 + } +} +``` + +This mod will be automatically loaded when the server starts and will add the Scout Ship to the game, available for players to purchase at level 5. diff --git a/GameServer/data/gso/category/quests/daily.json b/GameServer/data/gso/category/quests/daily.json new file mode 100644 index 0000000..16ed319 --- /dev/null +++ b/GameServer/data/gso/category/quests/daily.json @@ -0,0 +1,31 @@ +{ + "tabId": "daily", + "title": "Dailies", + "description": "Resets daily. Small rewards, fast goals.", + "quests": [ + { + "id": "d_craft_3", + "name": "Quick Craft" + }, + { + "id": "d_dungeons_3", + "name": "Dungeon Hopping" + }, + { + "id": "d_explore_30", + "name": "Daily Survey" + }, + { + "id": "d_login_5", + "name": "Frequent Flyer" + }, + { + "id": "d_smelt_5", + "name": "Smelting Run" + }, + { + "id": "d_kill_pirates_10", + "name": "Pirate Bounty" + } + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/category/quests/main_story.json b/GameServer/data/gso/category/quests/main_story.json new file mode 100644 index 0000000..6e15a7f --- /dev/null +++ b/GameServer/data/gso/category/quests/main_story.json @@ -0,0 +1,39 @@ +{ + "tabId": "main_story", + "title": "Main Story", + "description": "The core story of Galaxy Strike Online.", + "quests": [ + { + "id": "ms_ch1_001", + "name": "First Steps Into the Stars" + }, + { + "id": "ms_ch1_002", + "name": "Into the Unknown" + }, + { + "id": "ms_ch1_003", + "name": "The Art of Craft" + }, + { + "id": "ms_ch2_001", + "name": "Alien Incursion" + }, + { + "id": "ms_ch2_002", + "name": "Power Core" + }, + { + "id": "ms_ch2_003", + "name": "The Void Calls" + }, + { + "id": "ms_ch3_001", + "name": "Dreadnought Rising" + }, + { + "id": "ms_ch3_002", + "name": "Galaxy Strike" + } + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/category/quests/monthly.json b/GameServer/data/gso/category/quests/monthly.json new file mode 100644 index 0000000..8f9c115 --- /dev/null +++ b/GameServer/data/gso/category/quests/monthly.json @@ -0,0 +1,23 @@ +{ + "tabId": "monthly", + "title": "Monthlies", + "description": "The ultimate monthly challenges.", + "quests": [ + { + "id": "m_raids_20", + "name": "Monthly Raider" + }, + { + "id": "m_dungeons_50", + "name": "Legendary Dungeon Diver" + }, + { + "id": "m_craft_100", + "name": "Master Craftsman" + }, + { + "id": "m_void_run", + "name": "Void Conqueror" + } + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/category/quests/weekly.json b/GameServer/data/gso/category/quests/weekly.json new file mode 100644 index 0000000..2bafb4a --- /dev/null +++ b/GameServer/data/gso/category/quests/weekly.json @@ -0,0 +1,27 @@ +{ + "tabId": "weekly", + "title": "Weeklies", + "description": "Resets weekly. Bigger challenges, better rewards.", + "quests": [ + { + "id": "w_raids_3", + "name": "Raid Week" + }, + { + "id": "w_craft_20", + "name": "Crafting Spree" + }, + { + "id": "w_dungeons_10", + "name": "Dungeon Crawler" + }, + { + "id": "w_survive_hardcore_7", + "name": "Hardcore Survivor" + }, + { + "id": "w_gather_50", + "name": "Resource Rush" + } + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/dungeons/alien_ruins.json b/GameServer/data/gso/dungeons/alien_ruins.json new file mode 100644 index 0000000..155a83d --- /dev/null +++ b/GameServer/data/gso/dungeons/alien_ruins.json @@ -0,0 +1,50 @@ +{ + "id": "alien_ruins", + "name": "Alien Ruins", + "category": "exploration", + "description": "Ancient alien structures filled with mysterious technology", + "difficulty": "medium", + "minLevel": 3, + "roomCount": [ + 5, + 8 + ], + "energyCost": 20, + "rewardMultiplier": 1.2, + "enemyPool": [ + "alien_guardian", + "ancient_drone", + "crystal_golem" + ], + "bossId": "crystal_golem", + "rewards": { + "creditsMin": 200, + "creditsMax": 500, + "experienceMin": 150, + "experienceMax": 300 + }, + "ui": { + "icon": "fa-monument", + "color": "#4488cc" + }, + "lootTable": [ + { + "itemId": "wp_alien_ruins", + "weight": 8, + "qtyMin": 1, + "qtyMax": 1 + }, + { + "itemId": "energy_boost_small", + "weight": 60, + "qtyMin": 1, + "qtyMax": 3 + }, + { + "itemId": "health_kit_small", + "weight": 60, + "qtyMin": 1, + "qtyMax": 2 + } + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/dungeons/asteroid_mine.json b/GameServer/data/gso/dungeons/asteroid_mine.json new file mode 100644 index 0000000..8164701 --- /dev/null +++ b/GameServer/data/gso/dungeons/asteroid_mine.json @@ -0,0 +1,50 @@ +{ + "id": "asteroid_mine", + "name": "Asteroid Mine", + "category": "exploration", + "description": "Abandoned mining facilities in asteroid fields", + "difficulty": "easy", + "minLevel": 2, + "roomCount": [ + 4, + 7 + ], + "energyCost": 10, + "rewardMultiplier": 0.8, + "enemyPool": [ + "mining_drone", + "rock_creature", + "space_pirate" + ], + "bossId": "mining_drone", + "rewards": { + "creditsMin": 60, + "creditsMax": 180, + "experienceMin": 50, + "experienceMax": 120 + }, + "ui": { + "icon": "fa-meteor", + "color": "#888844" + }, + "lootTable": [ + { + "itemId": "wp_arctic_station", + "weight": 8, + "qtyMin": 1, + "qtyMax": 1 + }, + { + "itemId": "fuel_cell", + "weight": 60, + "qtyMin": 1, + "qtyMax": 5 + }, + { + "itemId": "health_kit_small", + "weight": 50, + "qtyMin": 1, + "qtyMax": 3 + } + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/dungeons/corrupted_vault.json b/GameServer/data/gso/dungeons/corrupted_vault.json new file mode 100644 index 0000000..b1ca63f --- /dev/null +++ b/GameServer/data/gso/dungeons/corrupted_vault.json @@ -0,0 +1,56 @@ +{ + "id": "corrupted_vault", + "name": "Corrupted AI Vault", + "category": "combat", + "description": "Malfunctioning AI facilities with corrupted security systems", + "difficulty": "hard", + "minLevel": 5, + "roomCount": [ + 6, + 9 + ], + "energyCost": 25, + "rewardMultiplier": 1.5, + "enemyPool": [ + "security_drone", + "corrupted_ai", + "virus_program" + ], + "bossId": "corrupted_ai", + "rewards": { + "creditsMin": 400, + "creditsMax": 900, + "experienceMin": 300, + "experienceMax": 600 + }, + "ui": { + "icon": "fa-microchip", + "color": "#44cc44" + }, + "lootTable": [ + { + "itemId": "room_vault", + "weight": 12, + "qtyMin": 1, + "qtyMax": 1 + }, + { + "itemId": "wp_void_black", + "weight": 10, + "qtyMin": 1, + "qtyMax": 1 + }, + { + "itemId": "credit_multiplier", + "weight": 40, + "qtyMin": 1, + "qtyMax": 1 + }, + { + "itemId": "xp_booster", + "weight": 40, + "qtyMin": 1, + "qtyMax": 2 + } + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/dungeons/nebula_anomaly.json b/GameServer/data/gso/dungeons/nebula_anomaly.json new file mode 100644 index 0000000..f831df5 --- /dev/null +++ b/GameServer/data/gso/dungeons/nebula_anomaly.json @@ -0,0 +1,50 @@ +{ + "id": "nebula_anomaly", + "name": "Nebula Anomaly", + "category": "endgame", + "description": "Strange energy anomalies deep in uncharted space", + "difficulty": "extreme", + "minLevel": 8, + "roomCount": [ + 7, + 10 + ], + "energyCost": 30, + "rewardMultiplier": 2.0, + "enemyPool": [ + "energy_being", + "phase_shifter", + "quantum_entity" + ], + "bossId": "quantum_entity", + "rewards": { + "creditsMin": 800, + "creditsMax": 2000, + "experienceMin": 600, + "experienceMax": 1200 + }, + "ui": { + "icon": "fa-star", + "color": "#cc44ff" + }, + "lootTable": [ + { + "itemId": "wp_nebula_dawn", + "weight": 10, + "qtyMin": 1, + "qtyMax": 1 + }, + { + "itemId": "wp_deep_ocean", + "weight": 8, + "qtyMin": 1, + "qtyMax": 1 + }, + { + "itemId": "xp_booster", + "weight": 60, + "qtyMin": 1, + "qtyMax": 2 + } + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/dungeons/pirate_lair.json b/GameServer/data/gso/dungeons/pirate_lair.json new file mode 100644 index 0000000..cb7c4b3 --- /dev/null +++ b/GameServer/data/gso/dungeons/pirate_lair.json @@ -0,0 +1,50 @@ +{ + "id": "pirate_lair", + "name": "Pirate Lair", + "category": "combat", + "description": "Dangerous pirate hideouts packed with loot", + "difficulty": "easy", + "minLevel": 1, + "roomCount": [ + 4, + 6 + ], + "energyCost": 15, + "rewardMultiplier": 1.0, + "enemyPool": [ + "space_pirate", + "pirate_captain", + "security_drone" + ], + "bossId": "pirate_captain", + "rewards": { + "creditsMin": 100, + "creditsMax": 300, + "experienceMin": 80, + "experienceMax": 160 + }, + "ui": { + "icon": "fa-skull-crossbones", + "color": "#cc4444" + }, + "lootTable": [ + { + "itemId": "wp_lava_forge", + "weight": 6, + "qtyMin": 1, + "qtyMax": 1 + }, + { + "itemId": "health_kit_large", + "weight": 40, + "qtyMin": 1, + "qtyMax": 2 + }, + { + "itemId": "fuel_cell", + "weight": 50, + "qtyMin": 1, + "qtyMax": 3 + } + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/dungeons/void_rift.json b/GameServer/data/gso/dungeons/void_rift.json new file mode 100644 index 0000000..9ba4c70 --- /dev/null +++ b/GameServer/data/gso/dungeons/void_rift.json @@ -0,0 +1,56 @@ +{ + "id": "void_rift", + "name": "Void Rift", + "category": "endgame", + "description": "A tear in space-time inhabited by entities beyond comprehension", + "difficulty": "extreme", + "minLevel": 12, + "roomCount": [ + 8, + 12 + ], + "energyCost": 40, + "rewardMultiplier": 2.5, + "enemyPool": [ + "quantum_entity", + "energy_being", + "phase_shifter" + ], + "bossId": "quantum_entity", + "rewards": { + "creditsMin": 1500, + "creditsMax": 4000, + "experienceMin": 1000, + "experienceMax": 2000 + }, + "ui": { + "icon": "fa-circle-radiation", + "color": "#ff4488" + }, + "lootTable": [ + { + "itemId": "wp_void_rift", + "weight": 5, + "qtyMin": 1, + "qtyMax": 1 + }, + { + "itemId": "room_vault", + "weight": 3, + "qtyMin": 1, + "qtyMax": 1 + }, + { + "itemId": "xp_booster", + "weight": 50, + "qtyMin": 1, + "qtyMax": 2 + }, + { + "itemId": "credit_multiplier", + "weight": 30, + "qtyMin": 1, + "qtyMax": 1 + } + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/enemies/alien_guardian.json b/GameServer/data/gso/enemies/alien_guardian.json new file mode 100644 index 0000000..d9400d8 --- /dev/null +++ b/GameServer/data/gso/enemies/alien_guardian.json @@ -0,0 +1,15 @@ +{ + "id": "alien_guardian", + "name": "Alien Guardian", + "rarity": "common", + "health": 50, + "attack": 8, + "defense": 5, + "speed": 6, + "experience": 25, + "credits": 15, + "dropTable": [ + "alien_fauna_sample", + "iron_ore" + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/enemies/ancient_drone.json b/GameServer/data/gso/enemies/ancient_drone.json new file mode 100644 index 0000000..02cade1 --- /dev/null +++ b/GameServer/data/gso/enemies/ancient_drone.json @@ -0,0 +1,15 @@ +{ + "id": "ancient_drone", + "name": "Ancient Drone", + "rarity": "uncommon", + "health": 30, + "attack": 12, + "defense": 2, + "speed": 9, + "experience": 20, + "credits": 10, + "dropTable": [ + "basic_circuit", + "alien_fauna_sample" + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/enemies/corrupted_ai.json b/GameServer/data/gso/enemies/corrupted_ai.json new file mode 100644 index 0000000..772a234 --- /dev/null +++ b/GameServer/data/gso/enemies/corrupted_ai.json @@ -0,0 +1,15 @@ +{ + "id": "corrupted_ai", + "name": "Corrupted AI", + "rarity": "rare", + "health": 60, + "attack": 20, + "defense": 2, + "speed": 15, + "experience": 40, + "credits": 30, + "dropTable": [ + "processor_circuit", + "logic_circuit" + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/enemies/crystal_golem.json b/GameServer/data/gso/enemies/crystal_golem.json new file mode 100644 index 0000000..1452316 --- /dev/null +++ b/GameServer/data/gso/enemies/crystal_golem.json @@ -0,0 +1,15 @@ +{ + "id": "crystal_golem", + "name": "Crystal Golem", + "rarity": "rare", + "health": 80, + "attack": 6, + "defense": 10, + "speed": 4, + "experience": 35, + "credits": 25, + "dropTable": [ + "flux_crystal", + "titanium_ore" + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/enemies/energy_being.json b/GameServer/data/gso/enemies/energy_being.json new file mode 100644 index 0000000..f30e729 --- /dev/null +++ b/GameServer/data/gso/enemies/energy_being.json @@ -0,0 +1,15 @@ +{ + "id": "energy_being", + "name": "Energy Being", + "rarity": "epic", + "health": 55, + "attack": 22, + "defense": 3, + "speed": 18, + "experience": 45, + "credits": 35, + "dropTable": [ + "void_crystal", + "quantum_circuit" + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/enemies/mining_drone.json b/GameServer/data/gso/enemies/mining_drone.json new file mode 100644 index 0000000..3cb8875 --- /dev/null +++ b/GameServer/data/gso/enemies/mining_drone.json @@ -0,0 +1,15 @@ +{ + "id": "mining_drone", + "name": "Mining Drone", + "rarity": "common", + "health": 20, + "attack": 8, + "defense": 3, + "speed": 5, + "experience": 12, + "credits": 8, + "dropTable": [ + "iron_ore", + "copper_ore" + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/enemies/phase_shifter.json b/GameServer/data/gso/enemies/phase_shifter.json new file mode 100644 index 0000000..c61f026 --- /dev/null +++ b/GameServer/data/gso/enemies/phase_shifter.json @@ -0,0 +1,15 @@ +{ + "id": "phase_shifter", + "name": "Phase Shifter", + "rarity": "epic", + "health": 50, + "attack": 25, + "defense": 4, + "speed": 22, + "experience": 55, + "credits": 45, + "dropTable": [ + "phase_crystal", + "void_crystal" + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/enemies/pirate_captain.json b/GameServer/data/gso/enemies/pirate_captain.json new file mode 100644 index 0000000..ebbb472 --- /dev/null +++ b/GameServer/data/gso/enemies/pirate_captain.json @@ -0,0 +1,15 @@ +{ + "id": "pirate_captain", + "name": "Pirate Captain", + "rarity": "uncommon", + "health": 40, + "attack": 15, + "defense": 6, + "speed": 12, + "experience": 30, + "credits": 20, + "dropTable": [ + "steel_alloy", + "iron_ore" + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/enemies/quantum_entity.json b/GameServer/data/gso/enemies/quantum_entity.json new file mode 100644 index 0000000..f0055a3 --- /dev/null +++ b/GameServer/data/gso/enemies/quantum_entity.json @@ -0,0 +1,16 @@ +{ + "id": "quantum_entity", + "name": "Quantum Entity", + "rarity": "legendary", + "health": 70, + "attack": 35, + "defense": 5, + "speed": 20, + "experience": 60, + "credits": 50, + "dropTable": [ + "void_crystal", + "neutronium_shard", + "dimensional_logic_circuit" + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/enemies/rock_creature.json b/GameServer/data/gso/enemies/rock_creature.json new file mode 100644 index 0000000..503b92f --- /dev/null +++ b/GameServer/data/gso/enemies/rock_creature.json @@ -0,0 +1,15 @@ +{ + "id": "rock_creature", + "name": "Rock Creature", + "rarity": "common", + "health": 30, + "attack": 7, + "defense": 8, + "speed": 3, + "experience": 14, + "credits": 9, + "dropTable": [ + "iron_ore", + "carbon_ingot" + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/enemies/security_drone.json b/GameServer/data/gso/enemies/security_drone.json new file mode 100644 index 0000000..8b0b47f --- /dev/null +++ b/GameServer/data/gso/enemies/security_drone.json @@ -0,0 +1,15 @@ +{ + "id": "security_drone", + "name": "Security Drone", + "rarity": "uncommon", + "health": 35, + "attack": 14, + "defense": 4, + "speed": 10, + "experience": 22, + "credits": 15, + "dropTable": [ + "basic_circuit", + "copper_ore" + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/enemies/space_pirate.json b/GameServer/data/gso/enemies/space_pirate.json new file mode 100644 index 0000000..938491f --- /dev/null +++ b/GameServer/data/gso/enemies/space_pirate.json @@ -0,0 +1,15 @@ +{ + "id": "space_pirate", + "name": "Space Pirate", + "rarity": "common", + "health": 25, + "attack": 10, + "defense": 3, + "speed": 8, + "experience": 15, + "credits": 12, + "dropTable": [ + "iron_ore", + "bandage" + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/enemies/training_drone.json b/GameServer/data/gso/enemies/training_drone.json new file mode 100644 index 0000000..5a7086d --- /dev/null +++ b/GameServer/data/gso/enemies/training_drone.json @@ -0,0 +1,12 @@ +{ + "id": "training_drone", + "name": "Training Drone", + "rarity": "common", + "health": 10, + "attack": 5, + "defense": 2, + "speed": 6, + "experience": 5, + "credits": 3, + "dropTable": [] +} \ No newline at end of file diff --git a/GameServer/data/gso/enemies/virus_program.json b/GameServer/data/gso/enemies/virus_program.json new file mode 100644 index 0000000..9edeac6 --- /dev/null +++ b/GameServer/data/gso/enemies/virus_program.json @@ -0,0 +1,15 @@ +{ + "id": "virus_program", + "name": "Virus Program", + "rarity": "rare", + "health": 45, + "attack": 18, + "defense": 1, + "speed": 20, + "experience": 38, + "credits": 28, + "dropTable": [ + "processor_circuit", + "stealth_circuit" + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/items/.gitkeep b/GameServer/data/gso/items/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/GameServer/data/gso/items/armour/backpack/backpack_basic_common.json b/GameServer/data/gso/items/armour/backpack/backpack_basic_common.json new file mode 100644 index 0000000..915caec --- /dev/null +++ b/GameServer/data/gso/items/armour/backpack/backpack_basic_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "backpack_basic_common", + "texture": "assets/gso/textures/armour/backpack/backpack_basic_common.png", + "stats": { + "health": 20, + "resistance": 0.03, + "defenceRating": 1, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/armour/backpack/backpack_field_rare.json b/GameServer/data/gso/items/armour/backpack/backpack_field_rare.json new file mode 100644 index 0000000..36eb7fd --- /dev/null +++ b/GameServer/data/gso/items/armour/backpack/backpack_field_rare.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "backpack_field_rare", + "texture": "assets/gso/textures/armour/backpack/backpack_field_rare.png", + "stats": { + "health": 40, + "resistance": 0.08, + "defenceRating": 3, + "reflectChance": 0.01 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/armour/backpack/backpack_reactor_epic.json b/GameServer/data/gso/items/armour/backpack/backpack_reactor_epic.json new file mode 100644 index 0000000..07d86c6 --- /dev/null +++ b/GameServer/data/gso/items/armour/backpack/backpack_reactor_epic.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "backpack_reactor_epic", + "texture": "assets/gso/textures/armour/backpack/backpack_reactor_epic.png", + "stats": { + "health": 70, + "resistance": 0.15, + "defenceRating": 6, + "reflectChance": 0.03 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/armour/body/body_basic_common.json b/GameServer/data/gso/items/armour/body/body_basic_common.json new file mode 100644 index 0000000..461b4cb --- /dev/null +++ b/GameServer/data/gso/items/armour/body/body_basic_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "body_basic_common", + "texture": "assets/gso/textures/armour/body/body_basic_common.png", + "stats": { + "health": 60, + "resistance": 0.1, + "defenceRating": 4, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/armour/body/body_exosuit_epic.json b/GameServer/data/gso/items/armour/body/body_exosuit_epic.json new file mode 100644 index 0000000..7f70a3f --- /dev/null +++ b/GameServer/data/gso/items/armour/body/body_exosuit_epic.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "body_exosuit_epic", + "texture": "assets/gso/textures/armour/body/body_exosuit_epic.png", + "stats": { + "health": 180, + "resistance": 0.3, + "defenceRating": 14, + "reflectChance": 0.05 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/armour/body/body_plated_rare.json b/GameServer/data/gso/items/armour/body/body_plated_rare.json new file mode 100644 index 0000000..3d724e8 --- /dev/null +++ b/GameServer/data/gso/items/armour/body/body_plated_rare.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "body_plated_rare", + "texture": "assets/gso/textures/armour/body/body_plated_rare.png", + "stats": { + "health": 110, + "resistance": 0.2, + "defenceRating": 8, + "reflectChance": 0.02 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/armour/boots/boots_assault_rare.json b/GameServer/data/gso/items/armour/boots/boots_assault_rare.json new file mode 100644 index 0000000..a4b641a --- /dev/null +++ b/GameServer/data/gso/items/armour/boots/boots_assault_rare.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "boots_assault_rare", + "texture": "assets/gso/textures/armour/boots/boots_assault_rare.png", + "stats": { + "health": 40, + "resistance": 0.12, + "defenceRating": 4, + "reflectChance": 0.01 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/armour/boots/boots_basic_common.json b/GameServer/data/gso/items/armour/boots/boots_basic_common.json new file mode 100644 index 0000000..110c2a5 --- /dev/null +++ b/GameServer/data/gso/items/armour/boots/boots_basic_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "boots_basic_common", + "texture": "assets/gso/textures/armour/boots/boots_basic_common.png", + "stats": { + "health": 20, + "resistance": 0.05, + "defenceRating": 2, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/armour/boots/boots_gravity_epic.json b/GameServer/data/gso/items/armour/boots/boots_gravity_epic.json new file mode 100644 index 0000000..ea5a086 --- /dev/null +++ b/GameServer/data/gso/items/armour/boots/boots_gravity_epic.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "boots_gravity_epic", + "texture": "assets/gso/textures/armour/boots/boots_gravity_epic.png", + "stats": { + "health": 65, + "resistance": 0.2, + "defenceRating": 7, + "reflectChance": 0.025 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/armour/hands/hands_basic_common.json b/GameServer/data/gso/items/armour/hands/hands_basic_common.json new file mode 100644 index 0000000..bbf01e6 --- /dev/null +++ b/GameServer/data/gso/items/armour/hands/hands_basic_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "hands_basic_common", + "texture": "assets/gso/textures/armour/hands/hands_basic_common.png", + "stats": { + "health": 15, + "resistance": 0.04, + "defenceRating": 1, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/armour/hands/hands_combat_rare.json b/GameServer/data/gso/items/armour/hands/hands_combat_rare.json new file mode 100644 index 0000000..4b698e2 --- /dev/null +++ b/GameServer/data/gso/items/armour/hands/hands_combat_rare.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "hands_combat_rare", + "texture": "assets/gso/textures/armour/hands/hands_combat_rare.png", + "stats": { + "health": 30, + "resistance": 0.1, + "defenceRating": 3, + "reflectChance": 0.01 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/armour/hands/hands_elite_epic.json b/GameServer/data/gso/items/armour/hands/hands_elite_epic.json new file mode 100644 index 0000000..69a6ba1 --- /dev/null +++ b/GameServer/data/gso/items/armour/hands/hands_elite_epic.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "hands_elite_epic", + "texture": "assets/gso/textures/armour/hands/hands_elite_epic.png", + "stats": { + "health": 50, + "resistance": 0.18, + "defenceRating": 6, + "reflectChance": 0.025 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/armour/helmet/helmet_basic_common.json b/GameServer/data/gso/items/armour/helmet/helmet_basic_common.json new file mode 100644 index 0000000..88ea0a0 --- /dev/null +++ b/GameServer/data/gso/items/armour/helmet/helmet_basic_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "helmet_basic_common", + "texture": "assets/gso/textures/armour/helmet/helmet_basic_common.png", + "stats": { + "health": 25, + "resistance": 0.05, + "defenceRating": 2, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/armour/helmet/helmet_reinforced_rare.json b/GameServer/data/gso/items/armour/helmet/helmet_reinforced_rare.json new file mode 100644 index 0000000..557fb37 --- /dev/null +++ b/GameServer/data/gso/items/armour/helmet/helmet_reinforced_rare.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "helmet_reinforced_rare", + "texture": "assets/gso/textures/armour/helmet/helmet_reinforced_rare.png", + "stats": { + "health": 45, + "resistance": 0.12, + "defenceRating": 5, + "reflectChance": 0.01 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/armour/helmet/helmet_tactical_epic.json b/GameServer/data/gso/items/armour/helmet/helmet_tactical_epic.json new file mode 100644 index 0000000..46d37de --- /dev/null +++ b/GameServer/data/gso/items/armour/helmet/helmet_tactical_epic.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "helmet_tactical_epic", + "texture": "assets/gso/textures/armour/helmet/helmet_tactical_epic.png", + "stats": { + "health": 70, + "resistance": 0.2, + "defenceRating": 8, + "reflectChance": 0.03 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/armour/pants/pants_basic_common.json b/GameServer/data/gso/items/armour/pants/pants_basic_common.json new file mode 100644 index 0000000..87e7248 --- /dev/null +++ b/GameServer/data/gso/items/armour/pants/pants_basic_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "pants_basic_common", + "texture": "assets/gso/textures/armour/pants/pants_basic_common.png", + "stats": { + "health": 30, + "resistance": 0.06, + "defenceRating": 2, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/armour/pants/pants_exo_epic.json b/GameServer/data/gso/items/armour/pants/pants_exo_epic.json new file mode 100644 index 0000000..3cdba58 --- /dev/null +++ b/GameServer/data/gso/items/armour/pants/pants_exo_epic.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "pants_exo_epic", + "texture": "assets/gso/textures/armour/pants/pants_exo_epic.png", + "stats": { + "health": 85, + "resistance": 0.22, + "defenceRating": 9, + "reflectChance": 0.03 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/armour/pants/pants_reinforced_rare.json b/GameServer/data/gso/items/armour/pants/pants_reinforced_rare.json new file mode 100644 index 0000000..6710535 --- /dev/null +++ b/GameServer/data/gso/items/armour/pants/pants_reinforced_rare.json @@ -0,0 +1,14 @@ +{ + "templates": { + "armour": { + "id": "pants_reinforced_rare", + "texture": "assets/gso/textures/armour/pants/pants_reinforced_rare.png", + "stats": { + "health": 55, + "resistance": 0.14, + "defenceRating": 5, + "reflectChance": 0.015 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/armours/backpack_basic_common.json b/GameServer/data/gso/items/armours/backpack_basic_common.json new file mode 100644 index 0000000..229ac4f --- /dev/null +++ b/GameServer/data/gso/items/armours/backpack_basic_common.json @@ -0,0 +1,30 @@ +{ + "templates": { + "armour": { + "id": "backpack_basic_common", + "name": "Basic Backpack", + "type": "armour", + "slot": "backpack", + "rarity": "common", + "price": 500, + "currency": "credits", + "description": "Lightweight backpack with extra inventory slots", + "texture": "assets/gso/textures/armour/backpack/backpack_basic_common.png", + "stats": { + "health": 10, + "resistance": 0.02, + "defenceRating": 1, + "reflectChance": 0.0, + "extraSlots": 4 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 1 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/armours/body_basic_common.json b/GameServer/data/gso/items/armours/body_basic_common.json new file mode 100644 index 0000000..09c508b --- /dev/null +++ b/GameServer/data/gso/items/armours/body_basic_common.json @@ -0,0 +1,29 @@ +{ + "templates": { + "armour": { + "id": "body_basic_common", + "name": "Basic Body Armour", + "type": "armour", + "slot": "body", + "rarity": "common", + "price": 600, + "currency": "credits", + "description": "Standard-issue protection for rookie pilots", + "texture": "assets/gso/textures/armour/body/body_basic_common.png", + "stats": { + "health": 60, + "resistance": 0.1, + "defenceRating": 4, + "reflectChance": 0.0 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 1 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/armours/body_exosuit_epic.json b/GameServer/data/gso/items/armours/body_exosuit_epic.json new file mode 100644 index 0000000..826754b --- /dev/null +++ b/GameServer/data/gso/items/armours/body_exosuit_epic.json @@ -0,0 +1,28 @@ +{ + "templates": { + "armour": { + "id": "body_exosuit_epic", + "name": "Exosuit", + "type": "armour", + "slot": "body", + "rarity": "epic", + "price": 300, + "currency": "gems", + "description": "Powered exosuit that amplifies strength and endurance", + "texture": "assets/gso/textures/armour/body/body_exosuit_epic.png", + "stats": { + "health": 220, + "resistance": 0.35, + "defenceRating": 22, + "reflectChance": 0.1 + }, + "categories": [ + "shop" + ], + "requirements": { + "level": 20 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/armours/body_plated_rare.json b/GameServer/data/gso/items/armours/body_plated_rare.json new file mode 100644 index 0000000..002370f --- /dev/null +++ b/GameServer/data/gso/items/armours/body_plated_rare.json @@ -0,0 +1,29 @@ +{ + "templates": { + "armour": { + "id": "body_plated_rare", + "name": "Plated Combat Suit", + "type": "armour", + "slot": "body", + "rarity": "rare", + "price": 7000, + "currency": "credits", + "description": "Heavy-duty plated suit for frontline fighters", + "texture": "assets/gso/textures/armour/body/body_plated_rare.png", + "stats": { + "health": 120, + "resistance": 0.2, + "defenceRating": 12, + "reflectChance": 0.05 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 10 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/armours/boots_basic_common.json b/GameServer/data/gso/items/armours/boots_basic_common.json new file mode 100644 index 0000000..dc376ea --- /dev/null +++ b/GameServer/data/gso/items/armours/boots_basic_common.json @@ -0,0 +1,29 @@ +{ + "templates": { + "armour": { + "id": "boots_basic_common", + "name": "Basic Boots", + "type": "armour", + "slot": "boots", + "rarity": "common", + "price": 350, + "currency": "credits", + "description": "Sturdy magnetic boots for zero-gravity environments", + "texture": "assets/gso/textures/armour/boots/boots_basic_common.png", + "stats": { + "health": 20, + "resistance": 0.05, + "defenceRating": 2, + "reflectChance": 0.0 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 1 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/armours/gloves_basic_common.json b/GameServer/data/gso/items/armours/gloves_basic_common.json new file mode 100644 index 0000000..f0be1aa --- /dev/null +++ b/GameServer/data/gso/items/armours/gloves_basic_common.json @@ -0,0 +1,29 @@ +{ + "templates": { + "armour": { + "id": "gloves_basic_common", + "name": "Basic Gloves", + "type": "armour", + "slot": "hands", + "rarity": "common", + "price": 300, + "currency": "credits", + "description": "Reinforced tactical gloves with grip enhancement", + "texture": "assets/gso/textures/armour/hands/hands_basic_common.png", + "stats": { + "health": 15, + "resistance": 0.05, + "defenceRating": 2, + "reflectChance": 0.0 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 1 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/armours/helmet_basic_common.json b/GameServer/data/gso/items/armours/helmet_basic_common.json new file mode 100644 index 0000000..e6a52d9 --- /dev/null +++ b/GameServer/data/gso/items/armours/helmet_basic_common.json @@ -0,0 +1,29 @@ +{ + "templates": { + "armour": { + "id": "helmet_basic_common", + "name": "Basic Helmet", + "type": "armour", + "slot": "helmet", + "rarity": "common", + "price": 400, + "currency": "credits", + "description": "Hardened composite helmet protecting the head", + "texture": "assets/gso/textures/armour/helmet/helmet_basic_common.png", + "stats": { + "health": 30, + "resistance": 0.08, + "defenceRating": 3, + "reflectChance": 0.0 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 1 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/armours/helmet_reinforced_rare.json b/GameServer/data/gso/items/armours/helmet_reinforced_rare.json new file mode 100644 index 0000000..947dd9e --- /dev/null +++ b/GameServer/data/gso/items/armours/helmet_reinforced_rare.json @@ -0,0 +1,29 @@ +{ + "templates": { + "armour": { + "id": "helmet_reinforced_rare", + "name": "Reinforced Helmet", + "type": "armour", + "slot": "helmet", + "rarity": "rare", + "price": 4500, + "currency": "credits", + "description": "Reinforced titanium helmet with HUD integration", + "texture": "assets/gso/textures/armour/helmet/helmet_reinforced_rare.png", + "stats": { + "health": 70, + "resistance": 0.18, + "defenceRating": 9, + "reflectChance": 0.03 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 8 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/armours/pants_basic_common.json b/GameServer/data/gso/items/armours/pants_basic_common.json new file mode 100644 index 0000000..ae2e92d --- /dev/null +++ b/GameServer/data/gso/items/armours/pants_basic_common.json @@ -0,0 +1,29 @@ +{ + "templates": { + "armour": { + "id": "pants_basic_common", + "name": "Basic Leg Guards", + "type": "armour", + "slot": "pants", + "rarity": "common", + "price": 350, + "currency": "credits", + "description": "Lightweight leg armour for mobile pilots", + "texture": "assets/gso/textures/armour/pants/pants_basic_common.png", + "stats": { + "health": 25, + "resistance": 0.07, + "defenceRating": 2, + "reflectChance": 0.0 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 1 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/consumables/credit_multiplier.json b/GameServer/data/gso/items/consumables/credit_multiplier.json new file mode 100644 index 0000000..a5b87f6 --- /dev/null +++ b/GameServer/data/gso/items/consumables/credit_multiplier.json @@ -0,0 +1,26 @@ +{ + "templates": { + "consumable": { + "id": "credit_multiplier", + "name": "Credit Multiplier", + "type": "consumable", + "rarity": "rare", + "price": 50, + "currency": "gems", + "description": "Doubles credits earned for 1 hour", + "texture": "assets/gso/textures/consumables/credit_multiplier.png", + "effect": { + "creditMultiplier": 2, + "durationMinutes": 60 + }, + "categories": [ + "shop" + ], + "requirements": { + "level": 1 + }, + "stackable": true, + "maxStack": 5 + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/consumables/energy_boost_large.json b/GameServer/data/gso/items/consumables/energy_boost_large.json new file mode 100644 index 0000000..a066e9a --- /dev/null +++ b/GameServer/data/gso/items/consumables/energy_boost_large.json @@ -0,0 +1,25 @@ +{ + "templates": { + "consumable": { + "id": "energy_boost_large", + "name": "Max Energy Boost", + "type": "consumable", + "rarity": "uncommon", + "price": 100, + "currency": "credits", + "description": "Restores full energy", + "texture": "assets/gso/textures/consumables/energy_boost_large.png", + "effect": { + "energy": 999 + }, + "categories": [ + "shop" + ], + "requirements": { + "level": 1 + }, + "stackable": true, + "maxStack": 10 + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/consumables/energy_boost_small.json b/GameServer/data/gso/items/consumables/energy_boost_small.json new file mode 100644 index 0000000..d50fca8 --- /dev/null +++ b/GameServer/data/gso/items/consumables/energy_boost_small.json @@ -0,0 +1,26 @@ +{ + "templates": { + "consumable": { + "id": "energy_boost_small", + "name": "Energy Boost", + "type": "consumable", + "rarity": "common", + "price": 30, + "currency": "credits", + "description": "Restores 50 energy", + "texture": "assets/gso/textures/consumables/energy_boost_small.png", + "effect": { + "energy": 50 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 1 + }, + "stackable": true, + "maxStack": 20 + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/consumables/fuel_cell.json b/GameServer/data/gso/items/consumables/fuel_cell.json new file mode 100644 index 0000000..99f6d8d --- /dev/null +++ b/GameServer/data/gso/items/consumables/fuel_cell.json @@ -0,0 +1,27 @@ +{ + "templates": { + "consumable": { + "id": "fuel_cell", + "name": "Fuel Cell", + "type": "consumable", + "rarity": "common", + "price": 20, + "currency": "credits", + "description": "Restores 25 energy \u2014 handy for long dungeon runs", + "texture": "assets/gso/textures/consumables/fuel_cell.png", + "effect": { + "energy": 25 + }, + "categories": [ + "shop", + "dungeon_loot", + "starter" + ], + "requirements": { + "level": 1 + }, + "stackable": true, + "maxStack": 50 + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/consumables/health_kit_large.json b/GameServer/data/gso/items/consumables/health_kit_large.json new file mode 100644 index 0000000..a1208f6 --- /dev/null +++ b/GameServer/data/gso/items/consumables/health_kit_large.json @@ -0,0 +1,26 @@ +{ + "templates": { + "consumable": { + "id": "health_kit_large", + "name": "Large Health Kit", + "type": "consumable", + "rarity": "uncommon", + "price": 150, + "currency": "credits", + "description": "Restores full health", + "texture": "assets/gso/textures/consumables/health_kit_large.png", + "effect": { + "heal": 999 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 1 + }, + "stackable": true, + "maxStack": 10 + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/consumables/health_kit_small.json b/GameServer/data/gso/items/consumables/health_kit_small.json new file mode 100644 index 0000000..e98ad52 --- /dev/null +++ b/GameServer/data/gso/items/consumables/health_kit_small.json @@ -0,0 +1,26 @@ +{ + "templates": { + "consumable": { + "id": "health_kit_small", + "name": "Small Health Kit", + "type": "consumable", + "rarity": "common", + "price": 50, + "currency": "credits", + "description": "Restores 50 health", + "texture": "assets/gso/textures/consumables/health_kit_small.png", + "effect": { + "heal": 50 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 1 + }, + "stackable": true, + "maxStack": 20 + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/consumables/scrap_pack_small.json b/GameServer/data/gso/items/consumables/scrap_pack_small.json new file mode 100644 index 0000000..82fa4cd --- /dev/null +++ b/GameServer/data/gso/items/consumables/scrap_pack_small.json @@ -0,0 +1,35 @@ +{ + "templates": { + "consumable": { + "id": "scrap_pack_small", + "name": "Small Scrap Pack", + "type": "consumable", + "rarity": "common", + "price": 0, + "currency": "credits", + "description": "A bundle of mixed crafting scraps", + "texture": "assets/gso/textures/consumables/scrap_pack_small.png", + "effect": { + "grantItems": [ + { + "id": "iron_ore", + "qty": 3 + }, + { + "id": "copper_ore", + "qty": 2 + } + ] + }, + "categories": [ + "quest_reward", + "dungeon_loot" + ], + "requirements": { + "level": 1 + }, + "stackable": true, + "maxStack": 10 + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/consumables/starter_crate.json b/GameServer/data/gso/items/consumables/starter_crate.json new file mode 100644 index 0000000..de40f73 --- /dev/null +++ b/GameServer/data/gso/items/consumables/starter_crate.json @@ -0,0 +1,39 @@ +{ + "templates": { + "consumable": { + "id": "starter_crate", + "name": "Starter Crate", + "type": "consumable", + "rarity": "uncommon", + "price": 0, + "currency": "credits", + "description": "A crate of starter supplies for new recruits", + "texture": "assets/gso/textures/consumables/starter_crate.png", + "effect": { + "grantItems": [ + { + "id": "health_kit_small", + "qty": 3 + }, + { + "id": "energy_boost_small", + "qty": 3 + }, + { + "id": "fuel_cell", + "qty": 5 + } + ] + }, + "categories": [ + "quest_reward", + "starter" + ], + "requirements": { + "level": 1 + }, + "stackable": true, + "maxStack": 5 + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/consumables/xp_booster.json b/GameServer/data/gso/items/consumables/xp_booster.json new file mode 100644 index 0000000..76bb9c8 --- /dev/null +++ b/GameServer/data/gso/items/consumables/xp_booster.json @@ -0,0 +1,26 @@ +{ + "templates": { + "consumable": { + "id": "xp_booster", + "name": "XP Booster", + "type": "consumable", + "rarity": "rare", + "price": 75, + "currency": "gems", + "description": "Doubles XP gained for 30 minutes", + "texture": "assets/gso/textures/consumables/xp_booster.png", + "effect": { + "xpMultiplier": 2, + "durationMinutes": 30 + }, + "categories": [ + "shop" + ], + "requirements": { + "level": 1 + }, + "stackable": true, + "maxStack": 5 + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/decorations/room_armory.json b/GameServer/data/gso/items/decorations/room_armory.json new file mode 100644 index 0000000..0d5b312 --- /dev/null +++ b/GameServer/data/gso/items/decorations/room_armory.json @@ -0,0 +1,10 @@ +{ + "templates": { "decoration": { + "id": "room_armory", "name": "Armory Wing", "type": "decoration", + "subtype": "room_unlock", "rarity": "rare", "price": 5000, "currency": "credits", + "description": "Unlock the Armory Wing of your starbase. Houses weapon racks, armour stands, and a personal combat trainer.", + "texture": "assets/gso/textures/decorations/room_armory.png", + "roomId": "left_wing", + "categories": ["shop", "decoration"], "stackable": false + }} +} diff --git a/GameServer/data/gso/items/decorations/room_operations.json b/GameServer/data/gso/items/decorations/room_operations.json new file mode 100644 index 0000000..bf343ef --- /dev/null +++ b/GameServer/data/gso/items/decorations/room_operations.json @@ -0,0 +1,10 @@ +{ + "templates": { "decoration": { + "id": "room_operations", "name": "Operations Centre", "type": "decoration", + "subtype": "room_unlock", "rarity": "epic", "price": 200, "currency": "gems", + "description": "Unlock the Operations Centre. A private command room for coordinating missions and monitoring your empire.", + "texture": "assets/gso/textures/decorations/room_operations.png", + "roomId": "operations", + "categories": ["shop", "decoration"], "stackable": false + }} +} diff --git a/GameServer/data/gso/items/decorations/room_research_lab.json b/GameServer/data/gso/items/decorations/room_research_lab.json new file mode 100644 index 0000000..7340a5f --- /dev/null +++ b/GameServer/data/gso/items/decorations/room_research_lab.json @@ -0,0 +1,10 @@ +{ + "templates": { "decoration": { + "id": "room_research_lab", "name": "Research Lab", "type": "decoration", + "subtype": "room_unlock", "rarity": "rare", "price": 6000, "currency": "credits", + "description": "Unlock the Research Lab. Advanced crafting terminals and science equipment for developing new technologies.", + "texture": "assets/gso/textures/decorations/room_research_lab.png", + "roomId": "right_wing", + "categories": ["shop", "decoration"], "stackable": false + }} +} diff --git a/GameServer/data/gso/items/decorations/room_throne.json b/GameServer/data/gso/items/decorations/room_throne.json new file mode 100644 index 0000000..8c8e9eb --- /dev/null +++ b/GameServer/data/gso/items/decorations/room_throne.json @@ -0,0 +1,10 @@ +{ + "templates": { "decoration": { + "id": "room_throne", "name": "Throne Room", "type": "decoration", + "subtype": "room_unlock", "rarity": "legendary", "price": 0, "currency": "credits", + "description": "A legendary throne room earned only by those who have completed the Commander's Story Arc.", + "texture": "assets/gso/textures/decorations/room_throne.png", + "roomId": "throne_room", + "categories": ["quest_reward", "decoration"], "stackable": false + }} +} diff --git a/GameServer/data/gso/items/decorations/room_vault.json b/GameServer/data/gso/items/decorations/room_vault.json new file mode 100644 index 0000000..aff7bb3 --- /dev/null +++ b/GameServer/data/gso/items/decorations/room_vault.json @@ -0,0 +1,10 @@ +{ + "templates": { "decoration": { + "id": "room_vault", "name": "Commander's Vault", "type": "decoration", + "subtype": "room_unlock", "rarity": "epic", "price": 0, "currency": "credits", + "description": "A legendary vault room — only accessible to those who have conquered the Corrupted Vault dungeon.", + "texture": "assets/gso/textures/decorations/room_vault.png", + "roomId": "commanders_vault", + "categories": ["dungeon_loot", "decoration"], "stackable": false + }} +} diff --git a/GameServer/data/gso/items/decorations/wp_alien_ruins.json b/GameServer/data/gso/items/decorations/wp_alien_ruins.json new file mode 100644 index 0000000..eb82739 --- /dev/null +++ b/GameServer/data/gso/items/decorations/wp_alien_ruins.json @@ -0,0 +1,12 @@ +{ + "templates": { "decoration": { + "id": "wp_alien_ruins", "name": "Alien Ruins", "type": "decoration", + "subtype": "wallpaper", "rarity": "epic", "price": 0, "currency": "credits", + "description": "Cryptic green glyphs on ancient stone. Salvaged from the Alien Ruins dungeon.", + "texture": "assets/gso/textures/decorations/wp_alien_ruins.png", + "preview": { "floorColorEven": "#0a1508", "floorColorOdd": "#081208", + "wallColor": "#44ff88", "wallColorLeft": "#0a1e0a", "wallColorRight": "#081808", "wallColorTop": "#102a10", + "doorColor": "#88ffaa", "doorFrameColor": "#44cc66" }, + "categories": ["dungeon_loot", "decoration"], "stackable": false + }} +} diff --git a/GameServer/data/gso/items/decorations/wp_arctic_station.json b/GameServer/data/gso/items/decorations/wp_arctic_station.json new file mode 100644 index 0000000..1283292 --- /dev/null +++ b/GameServer/data/gso/items/decorations/wp_arctic_station.json @@ -0,0 +1,12 @@ +{ + "templates": { "decoration": { + "id": "wp_arctic_station", "name": "Arctic Station", "type": "decoration", + "subtype": "wallpaper", "rarity": "uncommon", "price": 1000, "currency": "credits", + "description": "Crisp whites and icy blues. A frozen outpost at the edge of the galaxy.", + "texture": "assets/gso/textures/decorations/wp_arctic_station.png", + "preview": { "floorColorEven": "#0e1820", "floorColorOdd": "#0c1418", + "wallColor": "#aaddff", "wallColorLeft": "#0c1828", "wallColorRight": "#0a1420", "wallColorTop": "#122030", + "doorColor": "#ddeeff", "doorFrameColor": "#88ccff" }, + "categories": ["shop", "decoration"], "stackable": false + }} +} diff --git a/GameServer/data/gso/items/decorations/wp_deep_ocean.json b/GameServer/data/gso/items/decorations/wp_deep_ocean.json new file mode 100644 index 0000000..b490ba2 --- /dev/null +++ b/GameServer/data/gso/items/decorations/wp_deep_ocean.json @@ -0,0 +1,12 @@ +{ + "templates": { "decoration": { + "id": "wp_deep_ocean", "name": "Deep Ocean", "type": "decoration", + "subtype": "wallpaper", "rarity": "uncommon", "price": 900, "currency": "credits", + "description": "Bioluminescent blues and teals. Like a starbase built beneath an alien sea.", + "texture": "assets/gso/textures/decorations/wp_deep_ocean.png", + "preview": { "floorColorEven": "#081820", "floorColorOdd": "#061418", + "wallColor": "#00cccc", "wallColorLeft": "#062030", "wallColorRight": "#041820", "wallColorTop": "#083040", + "doorColor": "#00ffee", "doorFrameColor": "#00bbcc" }, + "categories": ["shop", "decoration"], "stackable": false + }} +} diff --git a/GameServer/data/gso/items/decorations/wp_golden_empire.json b/GameServer/data/gso/items/decorations/wp_golden_empire.json new file mode 100644 index 0000000..58a1e50 --- /dev/null +++ b/GameServer/data/gso/items/decorations/wp_golden_empire.json @@ -0,0 +1,12 @@ +{ + "templates": { "decoration": { + "id": "wp_golden_empire", "name": "Golden Empire", "type": "decoration", + "subtype": "wallpaper", "rarity": "epic", "price": 150, "currency": "gems", + "description": "Opulent golds and deep bronzes. Reserved for commanders of the highest rank.", + "texture": "assets/gso/textures/decorations/wp_golden_empire.png", + "preview": { "floorColorEven": "#1a1200", "floorColorOdd": "#140e00", + "wallColor": "#ffcc00", "wallColorLeft": "#221500", "wallColorRight": "#1a1000", "wallColorTop": "#2e1c00", + "doorColor": "#ffee88", "doorFrameColor": "#ddaa00" }, + "categories": ["shop", "decoration"], "stackable": false + }} +} diff --git a/GameServer/data/gso/items/decorations/wp_lava_forge.json b/GameServer/data/gso/items/decorations/wp_lava_forge.json new file mode 100644 index 0000000..70e1e62 --- /dev/null +++ b/GameServer/data/gso/items/decorations/wp_lava_forge.json @@ -0,0 +1,12 @@ +{ + "templates": { "decoration": { + "id": "wp_lava_forge", "name": "Lava Forge", "type": "decoration", + "subtype": "wallpaper", "rarity": "rare", "price": 2000, "currency": "credits", + "description": "Volcanic orange and molten red. A station carved from the heart of a dying star.", + "texture": "assets/gso/textures/decorations/wp_lava_forge.png", + "preview": { "floorColorEven": "#1e0a00", "floorColorOdd": "#180800", + "wallColor": "#ff4400", "wallColorLeft": "#2a0800", "wallColorRight": "#200600", "wallColorTop": "#350a00", + "doorColor": "#ffaa00", "doorFrameColor": "#ff6600" }, + "categories": ["shop", "decoration"], "stackable": false + }} +} diff --git a/GameServer/data/gso/items/decorations/wp_nebula_dawn.json b/GameServer/data/gso/items/decorations/wp_nebula_dawn.json new file mode 100644 index 0000000..fb87312 --- /dev/null +++ b/GameServer/data/gso/items/decorations/wp_nebula_dawn.json @@ -0,0 +1,12 @@ +{ + "templates": { "decoration": { + "id": "wp_nebula_dawn", "name": "Nebula Dawn", "type": "decoration", + "subtype": "wallpaper", "rarity": "uncommon", "price": 800, "currency": "credits", + "description": "Soft pink and purple nebula clouds. A peaceful dawn among the stars.", + "texture": "assets/gso/textures/decorations/wp_nebula_dawn.png", + "preview": { "floorColorEven": "#1a1228", "floorColorOdd": "#150e20", + "wallColor": "#cc66ff", "wallColorLeft": "#1a0a28", "wallColorRight": "#140820", "wallColorTop": "#220d35", + "doorColor": "#ff88ff", "doorFrameColor": "#cc44cc" }, + "categories": ["shop", "decoration"], "stackable": false + }} +} diff --git a/GameServer/data/gso/items/decorations/wp_void_black.json b/GameServer/data/gso/items/decorations/wp_void_black.json new file mode 100644 index 0000000..4632c99 --- /dev/null +++ b/GameServer/data/gso/items/decorations/wp_void_black.json @@ -0,0 +1,12 @@ +{ + "templates": { "decoration": { + "id": "wp_void_black", "name": "Void Black", "type": "decoration", + "subtype": "wallpaper", "rarity": "rare", "price": 1500, "currency": "credits", + "description": "Absolute darkness. The walls absorb all light — only edges glow.", + "texture": "assets/gso/textures/decorations/wp_void_black.png", + "preview": { "floorColorEven": "#060608", "floorColorOdd": "#040406", + "wallColor": "#444455", "wallColorLeft": "#0a0a0f", "wallColorRight": "#060608", "wallColorTop": "#111118", + "doorColor": "#8888ff", "doorFrameColor": "#6666cc" }, + "categories": ["shop", "decoration"], "stackable": false + }} +} diff --git a/GameServer/data/gso/items/decorations/wp_void_rift.json b/GameServer/data/gso/items/decorations/wp_void_rift.json new file mode 100644 index 0000000..2321673 --- /dev/null +++ b/GameServer/data/gso/items/decorations/wp_void_rift.json @@ -0,0 +1,12 @@ +{ + "templates": { "decoration": { + "id": "wp_void_rift", "name": "Void Rift", "type": "decoration", + "subtype": "wallpaper", "rarity": "legendary", "price": 0, "currency": "credits", + "description": "Fractured space-time tears through the walls. Only found deep in the Void Rift.", + "texture": "assets/gso/textures/decorations/wp_void_rift.png", + "preview": { "floorColorEven": "#060010", "floorColorOdd": "#04000c", + "wallColor": "#8800ff", "wallColorLeft": "#0c0020", "wallColorRight": "#080018", "wallColorTop": "#100028", + "doorColor": "#cc44ff", "doorFrameColor": "#9900ee" }, + "categories": ["dungeon_loot", "decoration"], "stackable": false + }} +} diff --git a/GameServer/data/gso/items/hullPlating/misc/ballistic_alloy_common.json b/GameServer/data/gso/items/hullPlating/misc/ballistic_alloy_common.json new file mode 100644 index 0000000..d646b25 --- /dev/null +++ b/GameServer/data/gso/items/hullPlating/misc/ballistic_alloy_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "ballistic_alloy_common", + "texture": "assets/gso/textures/hullPlating/misc/ballistic_alloy.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/hullPlating/misc/carbon_titanium_composite_common.json b/GameServer/data/gso/items/hullPlating/misc/carbon_titanium_composite_common.json new file mode 100644 index 0000000..238220e --- /dev/null +++ b/GameServer/data/gso/items/hullPlating/misc/carbon_titanium_composite_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "carbon_titanium_composite_common", + "texture": "assets/gso/textures/hullPlating/misc/carbon_titanium_composite.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/hullPlating/misc/chrono_alloy_common.json b/GameServer/data/gso/items/hullPlating/misc/chrono_alloy_common.json new file mode 100644 index 0000000..c24c500 --- /dev/null +++ b/GameServer/data/gso/items/hullPlating/misc/chrono_alloy_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "chrono_alloy_common", + "texture": "assets/gso/textures/hullPlating/misc/chrono_alloy.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/hullPlating/misc/dimensional_alloy_common.json b/GameServer/data/gso/items/hullPlating/misc/dimensional_alloy_common.json new file mode 100644 index 0000000..bd6d924 --- /dev/null +++ b/GameServer/data/gso/items/hullPlating/misc/dimensional_alloy_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "dimensional_alloy_common", + "texture": "assets/gso/textures/hullPlating/misc/dimensional_alloy.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/hullPlating/misc/flux_core_alloy_common.json b/GameServer/data/gso/items/hullPlating/misc/flux_core_alloy_common.json new file mode 100644 index 0000000..79c38ac --- /dev/null +++ b/GameServer/data/gso/items/hullPlating/misc/flux_core_alloy_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "flux_core_alloy_common", + "texture": "assets/gso/textures/hullPlating/misc/flux_core_alloy.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/hullPlating/misc/neutronium_composite_common.json b/GameServer/data/gso/items/hullPlating/misc/neutronium_composite_common.json new file mode 100644 index 0000000..5ee776e --- /dev/null +++ b/GameServer/data/gso/items/hullPlating/misc/neutronium_composite_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "neutronium_composite_common", + "texture": "assets/gso/textures/hullPlating/misc/neutronium_composite.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/hullPlating/misc/phase_alloy_common.json b/GameServer/data/gso/items/hullPlating/misc/phase_alloy_common.json new file mode 100644 index 0000000..e598e36 --- /dev/null +++ b/GameServer/data/gso/items/hullPlating/misc/phase_alloy_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "phase_alloy_common", + "texture": "assets/gso/textures/hullPlating/misc/phase_alloy.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/hullPlating/misc/plasma_channel_alloy_common.json b/GameServer/data/gso/items/hullPlating/misc/plasma_channel_alloy_common.json new file mode 100644 index 0000000..9e6a70f --- /dev/null +++ b/GameServer/data/gso/items/hullPlating/misc/plasma_channel_alloy_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "plasma_channel_alloy_common", + "texture": "assets/gso/textures/hullPlating/misc/plasma_channel_alloy.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/hullPlating/misc/reinforced_steel_common.json b/GameServer/data/gso/items/hullPlating/misc/reinforced_steel_common.json new file mode 100644 index 0000000..f987a2c --- /dev/null +++ b/GameServer/data/gso/items/hullPlating/misc/reinforced_steel_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "reinforced_steel_common", + "texture": "assets/gso/textures/hullPlating/misc/reinforced_steel.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/hullPlating/misc/superconductive_alloy_common.json b/GameServer/data/gso/items/hullPlating/misc/superconductive_alloy_common.json new file mode 100644 index 0000000..b6f44cb --- /dev/null +++ b/GameServer/data/gso/items/hullPlating/misc/superconductive_alloy_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "superconductive_alloy_common", + "texture": "assets/gso/textures/hullPlating/misc/superconductive_alloy.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/hullPlating/misc/voidsteel_common.json b/GameServer/data/gso/items/hullPlating/misc/voidsteel_common.json new file mode 100644 index 0000000..27bbd6d --- /dev/null +++ b/GameServer/data/gso/items/hullPlating/misc/voidsteel_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "voidsteel_common", + "texture": "assets/gso/textures/hullPlating/misc/voidsteel.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/hullPlating/plating/ablative_plating_common.json b/GameServer/data/gso/items/hullPlating/plating/ablative_plating_common.json new file mode 100644 index 0000000..096cc7c --- /dev/null +++ b/GameServer/data/gso/items/hullPlating/plating/ablative_plating_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "ablative_plating_common", + "texture": "assets/gso/textures/hullPlating/plating/ablative_plating.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/hullPlating/section/advanced_hull_section_common.json b/GameServer/data/gso/items/hullPlating/section/advanced_hull_section_common.json new file mode 100644 index 0000000..d83b4c0 --- /dev/null +++ b/GameServer/data/gso/items/hullPlating/section/advanced_hull_section_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "advanced_hull_section_common", + "texture": "assets/gso/textures/hullPlating/section/advanced_hull_section.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/hullPlating/section/armor_hull_section_common.json b/GameServer/data/gso/items/hullPlating/section/armor_hull_section_common.json new file mode 100644 index 0000000..06f0106 --- /dev/null +++ b/GameServer/data/gso/items/hullPlating/section/armor_hull_section_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "armor_hull_section_common", + "texture": "assets/gso/textures/hullPlating/section/armor_hull_section.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/hullPlating/section/exotic_hull_section_common.json b/GameServer/data/gso/items/hullPlating/section/exotic_hull_section_common.json new file mode 100644 index 0000000..40f22f6 --- /dev/null +++ b/GameServer/data/gso/items/hullPlating/section/exotic_hull_section_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "exotic_hull_section_common", + "texture": "assets/gso/textures/hullPlating/section/exotic_hull_section.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/hullPlating/section/heavy_hull_section_common.json b/GameServer/data/gso/items/hullPlating/section/heavy_hull_section_common.json new file mode 100644 index 0000000..9be3680 --- /dev/null +++ b/GameServer/data/gso/items/hullPlating/section/heavy_hull_section_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "heavy_hull_section_common", + "texture": "assets/gso/textures/hullPlating/section/heavy_hull_section.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/hullPlating/section/light_hull_section_common.json b/GameServer/data/gso/items/hullPlating/section/light_hull_section_common.json new file mode 100644 index 0000000..72d22a2 --- /dev/null +++ b/GameServer/data/gso/items/hullPlating/section/light_hull_section_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "light_hull_section_common", + "texture": "assets/gso/textures/hullPlating/section/light_hull_section.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/hullPlating/section/standard_hull_section_common.json b/GameServer/data/gso/items/hullPlating/section/standard_hull_section_common.json new file mode 100644 index 0000000..731d0a8 --- /dev/null +++ b/GameServer/data/gso/items/hullPlating/section/standard_hull_section_common.json @@ -0,0 +1,14 @@ +{ + "templates": { + "hullPlating": { + "id": "standard_hull_section_common", + "texture": "assets/gso/textures/hullPlating/section/standard_hull_section.png", + "stats": { + "health": 0, + "resistance": 0.0, + "defenceRating": 0, + "reflectChance": 0.0 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/manifest.json b/GameServer/data/gso/items/manifest.json new file mode 100644 index 0000000..1beaf29 --- /dev/null +++ b/GameServer/data/gso/items/manifest.json @@ -0,0 +1,20 @@ +{ + "counts_by_type": { + "ships": 8, + "weapons": 8, + "armours": 9, + "consumables": 9, + "materials": { + "ore": 9, + "ingot": 10, + "chemical": 10, + "electronics": 10, + "misc": 10 + } + }, + "notes": [ + "One JSON per unique item. Grouped into data/gso/items///", + "Texture paths point into assets/gso/textures///.png", + "Server loads all items via ContentLoader.loadAllItems()" + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/bio/bio_pulp.json b/GameServer/data/gso/items/materials/bio/bio_pulp.json new file mode 100644 index 0000000..9694a7d --- /dev/null +++ b/GameServer/data/gso/items/materials/bio/bio_pulp.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "bio_pulp_common", + "texture": "assets/gso/textures/materials/bio/bio_pulp.png", + "stats": [], + "meta": { + "materialCategory": "bio" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/bio/organic_insulation.json b/GameServer/data/gso/items/materials/bio/organic_insulation.json new file mode 100644 index 0000000..77b72e7 --- /dev/null +++ b/GameServer/data/gso/items/materials/bio/organic_insulation.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "organic_insulation_common", + "texture": "assets/gso/textures/materials/bio/organic_insulation.png", + "stats": [], + "meta": { + "materialCategory": "bio" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/bio/plant_matter.json b/GameServer/data/gso/items/materials/bio/plant_matter.json new file mode 100644 index 0000000..901dafd --- /dev/null +++ b/GameServer/data/gso/items/materials/bio/plant_matter.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "plant_matter_common", + "texture": "assets/gso/textures/materials/bio/plant_matter.png", + "stats": [], + "meta": { + "materialCategory": "bio" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/chemical/aluminum_alloy.json b/GameServer/data/gso/items/materials/chemical/aluminum_alloy.json new file mode 100644 index 0000000..3a475d7 --- /dev/null +++ b/GameServer/data/gso/items/materials/chemical/aluminum_alloy.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "aluminum_alloy_common", + "texture": "assets/gso/textures/materials/chemical/aluminum_alloy.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/chemical/bio_composite_material.json b/GameServer/data/gso/items/materials/chemical/bio_composite_material.json new file mode 100644 index 0000000..f7f90da --- /dev/null +++ b/GameServer/data/gso/items/materials/chemical/bio_composite_material.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "bio_composite_material_common", + "texture": "assets/gso/textures/materials/chemical/bio_composite_material.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/chemical/carbon_nanotube_alloy.json b/GameServer/data/gso/items/materials/chemical/carbon_nanotube_alloy.json new file mode 100644 index 0000000..70cb067 --- /dev/null +++ b/GameServer/data/gso/items/materials/chemical/carbon_nanotube_alloy.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "carbon_nanotube_alloy_common", + "texture": "assets/gso/textures/materials/chemical/carbon_nanotube_alloy.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/chemical/carbon_titanium_composite.json b/GameServer/data/gso/items/materials/chemical/carbon_titanium_composite.json new file mode 100644 index 0000000..a69db9d --- /dev/null +++ b/GameServer/data/gso/items/materials/chemical/carbon_titanium_composite.json @@ -0,0 +1,16 @@ +{ + "templates": { + "materials": { + "id": "carbon_titanium_composite", + "name": "Carbon-Titanium Composite", + "meta": { + "materialCategory": "chemical" + }, + "texture": "assets/gso/textures/materials/chemical/carbon_titanium_composite.png", + "price": 500, + "rarity": "rare", + "stackable": true, + "maxStack": 200 + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/chemical/ceramic_composite.json b/GameServer/data/gso/items/materials/chemical/ceramic_composite.json new file mode 100644 index 0000000..ef39f66 --- /dev/null +++ b/GameServer/data/gso/items/materials/chemical/ceramic_composite.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "ceramic_composite_common", + "texture": "assets/gso/textures/materials/chemical/ceramic_composite.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/chemical/chrono_alloy.json b/GameServer/data/gso/items/materials/chemical/chrono_alloy.json new file mode 100644 index 0000000..00dd70d --- /dev/null +++ b/GameServer/data/gso/items/materials/chemical/chrono_alloy.json @@ -0,0 +1,16 @@ +{ + "templates": { + "materials": { + "id": "chrono_alloy", + "name": "Chrono Alloy", + "meta": { + "materialCategory": "chemical" + }, + "texture": "assets/gso/textures/materials/chemical/chrono_alloy.png", + "price": 2000, + "rarity": "legendary", + "stackable": true, + "maxStack": 50 + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/chemical/dimensional_alloy.json b/GameServer/data/gso/items/materials/chemical/dimensional_alloy.json new file mode 100644 index 0000000..25e33f4 --- /dev/null +++ b/GameServer/data/gso/items/materials/chemical/dimensional_alloy.json @@ -0,0 +1,16 @@ +{ + "templates": { + "materials": { + "id": "dimensional_alloy", + "name": "Dimensional Alloy", + "meta": { + "materialCategory": "chemical" + }, + "texture": "assets/gso/textures/materials/chemical/dimensional_alloy.png", + "price": 5000, + "rarity": "legendary", + "stackable": true, + "maxStack": 25 + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/chemical/explosive_compound.json b/GameServer/data/gso/items/materials/chemical/explosive_compound.json new file mode 100644 index 0000000..dfd4c0c --- /dev/null +++ b/GameServer/data/gso/items/materials/chemical/explosive_compound.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "explosive_compound_common", + "texture": "assets/gso/textures/materials/chemical/explosive_compound.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/chemical/flex_alloy.json b/GameServer/data/gso/items/materials/chemical/flex_alloy.json new file mode 100644 index 0000000..84ba999 --- /dev/null +++ b/GameServer/data/gso/items/materials/chemical/flex_alloy.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "flex_alloy_common", + "texture": "assets/gso/textures/materials/chemical/flex_alloy.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/chemical/flux_core_alloy.json b/GameServer/data/gso/items/materials/chemical/flux_core_alloy.json new file mode 100644 index 0000000..b9cebdd --- /dev/null +++ b/GameServer/data/gso/items/materials/chemical/flux_core_alloy.json @@ -0,0 +1,16 @@ +{ + "templates": { + "materials": { + "id": "flux_core_alloy", + "name": "Flux Core Alloy", + "meta": { + "materialCategory": "chemical" + }, + "texture": "assets/gso/textures/materials/chemical/flux_core_alloy.png", + "price": 600, + "rarity": "rare", + "stackable": true, + "maxStack": 200 + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/chemical/fusion_rated_alloy.json b/GameServer/data/gso/items/materials/chemical/fusion_rated_alloy.json new file mode 100644 index 0000000..6a1c270 --- /dev/null +++ b/GameServer/data/gso/items/materials/chemical/fusion_rated_alloy.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "fusion_rated_alloy_common", + "texture": "assets/gso/textures/materials/chemical/fusion_rated_alloy.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/chemical/glass_composite.json b/GameServer/data/gso/items/materials/chemical/glass_composite.json new file mode 100644 index 0000000..71ec3dc --- /dev/null +++ b/GameServer/data/gso/items/materials/chemical/glass_composite.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "glass_composite_common", + "texture": "assets/gso/textures/materials/chemical/glass_composite.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/chemical/neutronium_composite.json b/GameServer/data/gso/items/materials/chemical/neutronium_composite.json new file mode 100644 index 0000000..5ccf2f4 --- /dev/null +++ b/GameServer/data/gso/items/materials/chemical/neutronium_composite.json @@ -0,0 +1,16 @@ +{ + "templates": { + "materials": { + "id": "neutronium_composite", + "name": "Neutronium Composite", + "meta": { + "materialCategory": "chemical" + }, + "texture": "assets/gso/textures/materials/chemical/neutronium_composite.png", + "price": 3000, + "rarity": "legendary", + "stackable": true, + "maxStack": 50 + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/chemical/phase_alloy.json b/GameServer/data/gso/items/materials/chemical/phase_alloy.json new file mode 100644 index 0000000..1f01059 --- /dev/null +++ b/GameServer/data/gso/items/materials/chemical/phase_alloy.json @@ -0,0 +1,16 @@ +{ + "templates": { + "materials": { + "id": "phase_alloy", + "name": "Phase Alloy", + "meta": { + "materialCategory": "chemical" + }, + "texture": "assets/gso/textures/materials/chemical/phase_alloy.png", + "price": 800, + "rarity": "epic", + "stackable": true, + "maxStack": 100 + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/chemical/radiation_shield_alloy.json b/GameServer/data/gso/items/materials/chemical/radiation_shield_alloy.json new file mode 100644 index 0000000..2cfea79 --- /dev/null +++ b/GameServer/data/gso/items/materials/chemical/radiation_shield_alloy.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "radiation_shield_alloy_common", + "texture": "assets/gso/textures/materials/chemical/radiation_shield_alloy.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/chemical/reactive_armor_alloy.json b/GameServer/data/gso/items/materials/chemical/reactive_armor_alloy.json new file mode 100644 index 0000000..93ac7fe --- /dev/null +++ b/GameServer/data/gso/items/materials/chemical/reactive_armor_alloy.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "reactive_armor_alloy_common", + "texture": "assets/gso/textures/materials/chemical/reactive_armor_alloy.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/chemical/reinforced_steel.json b/GameServer/data/gso/items/materials/chemical/reinforced_steel.json new file mode 100644 index 0000000..85cc041 --- /dev/null +++ b/GameServer/data/gso/items/materials/chemical/reinforced_steel.json @@ -0,0 +1,16 @@ +{ + "templates": { + "materials": { + "id": "reinforced_steel", + "name": "Reinforced Steel", + "meta": { + "materialCategory": "chemical" + }, + "texture": "assets/gso/textures/materials/chemical/reinforced_steel.png", + "price": 120, + "rarity": "common", + "stackable": true, + "maxStack": 500 + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/chemical/resin.json b/GameServer/data/gso/items/materials/chemical/resin.json new file mode 100644 index 0000000..97e5611 --- /dev/null +++ b/GameServer/data/gso/items/materials/chemical/resin.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "resin_common", + "texture": "assets/gso/textures/materials/chemical/resin.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/chemical/rubber_polymer.json b/GameServer/data/gso/items/materials/chemical/rubber_polymer.json new file mode 100644 index 0000000..577cc78 --- /dev/null +++ b/GameServer/data/gso/items/materials/chemical/rubber_polymer.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "rubber_polymer_common", + "texture": "assets/gso/textures/materials/chemical/rubber_polymer.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/chemical/servo_alloy.json b/GameServer/data/gso/items/materials/chemical/servo_alloy.json new file mode 100644 index 0000000..2d20cd4 --- /dev/null +++ b/GameServer/data/gso/items/materials/chemical/servo_alloy.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "servo_alloy_common", + "texture": "assets/gso/textures/materials/chemical/servo_alloy.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/chemical/steel_alloy.json b/GameServer/data/gso/items/materials/chemical/steel_alloy.json new file mode 100644 index 0000000..84b7c43 --- /dev/null +++ b/GameServer/data/gso/items/materials/chemical/steel_alloy.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "steel_alloy_common", + "texture": "assets/gso/textures/materials/chemical/steel_alloy.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/chemical/titanium_alloy.json b/GameServer/data/gso/items/materials/chemical/titanium_alloy.json new file mode 100644 index 0000000..3ef1d08 --- /dev/null +++ b/GameServer/data/gso/items/materials/chemical/titanium_alloy.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "titanium_alloy_common", + "texture": "assets/gso/textures/materials/chemical/titanium_alloy.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/chemical/transparent_alloy.json b/GameServer/data/gso/items/materials/chemical/transparent_alloy.json new file mode 100644 index 0000000..888adba --- /dev/null +++ b/GameServer/data/gso/items/materials/chemical/transparent_alloy.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "transparent_alloy_common", + "texture": "assets/gso/textures/materials/chemical/transparent_alloy.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/chemical/tungsten_alloy.json b/GameServer/data/gso/items/materials/chemical/tungsten_alloy.json new file mode 100644 index 0000000..1ff843a --- /dev/null +++ b/GameServer/data/gso/items/materials/chemical/tungsten_alloy.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "tungsten_alloy_common", + "texture": "assets/gso/textures/materials/chemical/tungsten_alloy.png", + "stats": [], + "meta": { + "materialCategory": "chemical" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/chemical/voidsteel.json b/GameServer/data/gso/items/materials/chemical/voidsteel.json new file mode 100644 index 0000000..20464b1 --- /dev/null +++ b/GameServer/data/gso/items/materials/chemical/voidsteel.json @@ -0,0 +1,16 @@ +{ + "templates": { + "materials": { + "id": "voidsteel", + "name": "Voidsteel", + "meta": { + "materialCategory": "chemical" + }, + "texture": "assets/gso/textures/materials/chemical/voidsteel.png", + "price": 1200, + "rarity": "epic", + "stackable": true, + "maxStack": 100 + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/electronics/ai_core_circuit.json b/GameServer/data/gso/items/materials/electronics/ai_core_circuit.json new file mode 100644 index 0000000..3aaa251 --- /dev/null +++ b/GameServer/data/gso/items/materials/electronics/ai_core_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "ai_core_circuit_common", + "texture": "assets/gso/textures/materials/electronics/ai_core_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/electronics/basic_circuit.json b/GameServer/data/gso/items/materials/electronics/basic_circuit.json new file mode 100644 index 0000000..785c0a7 --- /dev/null +++ b/GameServer/data/gso/items/materials/electronics/basic_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "basic_circuit_common", + "texture": "assets/gso/textures/materials/electronics/basic_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/electronics/control_circuit.json b/GameServer/data/gso/items/materials/electronics/control_circuit.json new file mode 100644 index 0000000..65d1d4a --- /dev/null +++ b/GameServer/data/gso/items/materials/electronics/control_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "control_circuit_common", + "texture": "assets/gso/textures/materials/electronics/control_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/electronics/dimensional_logic_circuit.json b/GameServer/data/gso/items/materials/electronics/dimensional_logic_circuit.json new file mode 100644 index 0000000..126c3fc --- /dev/null +++ b/GameServer/data/gso/items/materials/electronics/dimensional_logic_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "dimensional_logic_circuit_common", + "texture": "assets/gso/textures/materials/electronics/dimensional_logic_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/electronics/high_density_circuit.json b/GameServer/data/gso/items/materials/electronics/high_density_circuit.json new file mode 100644 index 0000000..6aa4e33 --- /dev/null +++ b/GameServer/data/gso/items/materials/electronics/high_density_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "high_density_circuit_common", + "texture": "assets/gso/textures/materials/electronics/high_density_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/electronics/logic_circuit.json b/GameServer/data/gso/items/materials/electronics/logic_circuit.json new file mode 100644 index 0000000..47a1720 --- /dev/null +++ b/GameServer/data/gso/items/materials/electronics/logic_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "logic_circuit_common", + "texture": "assets/gso/textures/materials/electronics/logic_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/electronics/navigation_circuit.json b/GameServer/data/gso/items/materials/electronics/navigation_circuit.json new file mode 100644 index 0000000..92b938d --- /dev/null +++ b/GameServer/data/gso/items/materials/electronics/navigation_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "navigation_circuit_common", + "texture": "assets/gso/textures/materials/electronics/navigation_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/electronics/power_circuit.json b/GameServer/data/gso/items/materials/electronics/power_circuit.json new file mode 100644 index 0000000..88b7ad2 --- /dev/null +++ b/GameServer/data/gso/items/materials/electronics/power_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "power_circuit_common", + "texture": "assets/gso/textures/materials/electronics/power_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/electronics/processor_circuit.json b/GameServer/data/gso/items/materials/electronics/processor_circuit.json new file mode 100644 index 0000000..155c515 --- /dev/null +++ b/GameServer/data/gso/items/materials/electronics/processor_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "processor_circuit_common", + "texture": "assets/gso/textures/materials/electronics/processor_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/electronics/quantum_circuit.json b/GameServer/data/gso/items/materials/electronics/quantum_circuit.json new file mode 100644 index 0000000..c5c7fa9 --- /dev/null +++ b/GameServer/data/gso/items/materials/electronics/quantum_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "quantum_circuit_common", + "texture": "assets/gso/textures/materials/electronics/quantum_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/electronics/reactor_control_circuit.json b/GameServer/data/gso/items/materials/electronics/reactor_control_circuit.json new file mode 100644 index 0000000..4087bde --- /dev/null +++ b/GameServer/data/gso/items/materials/electronics/reactor_control_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "reactor_control_circuit_common", + "texture": "assets/gso/textures/materials/electronics/reactor_control_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/electronics/sensor_array_circuit.json b/GameServer/data/gso/items/materials/electronics/sensor_array_circuit.json new file mode 100644 index 0000000..e07e563 --- /dev/null +++ b/GameServer/data/gso/items/materials/electronics/sensor_array_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "sensor_array_circuit_common", + "texture": "assets/gso/textures/materials/electronics/sensor_array_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/electronics/sentient_core_circuit.json b/GameServer/data/gso/items/materials/electronics/sentient_core_circuit.json new file mode 100644 index 0000000..0db17ab --- /dev/null +++ b/GameServer/data/gso/items/materials/electronics/sentient_core_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "sentient_core_circuit_common", + "texture": "assets/gso/textures/materials/electronics/sentient_core_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/electronics/shield_control_circuit.json b/GameServer/data/gso/items/materials/electronics/shield_control_circuit.json new file mode 100644 index 0000000..e1dd4b1 --- /dev/null +++ b/GameServer/data/gso/items/materials/electronics/shield_control_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "shield_control_circuit_common", + "texture": "assets/gso/textures/materials/electronics/shield_control_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/electronics/stealth_circuit.json b/GameServer/data/gso/items/materials/electronics/stealth_circuit.json new file mode 100644 index 0000000..3345f32 --- /dev/null +++ b/GameServer/data/gso/items/materials/electronics/stealth_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "stealth_circuit_common", + "texture": "assets/gso/textures/materials/electronics/stealth_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/electronics/warp_navigation_circuit.json b/GameServer/data/gso/items/materials/electronics/warp_navigation_circuit.json new file mode 100644 index 0000000..ee3be9b --- /dev/null +++ b/GameServer/data/gso/items/materials/electronics/warp_navigation_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "warp_navigation_circuit_common", + "texture": "assets/gso/textures/materials/electronics/warp_navigation_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/electronics/weapon_control_circuit.json b/GameServer/data/gso/items/materials/electronics/weapon_control_circuit.json new file mode 100644 index 0000000..c0b8707 --- /dev/null +++ b/GameServer/data/gso/items/materials/electronics/weapon_control_circuit.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "weapon_control_circuit_common", + "texture": "assets/gso/textures/materials/electronics/weapon_control_circuit.png", + "stats": [], + "meta": { + "materialCategory": "electronics" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/fabric/plant_cloth.json b/GameServer/data/gso/items/materials/fabric/plant_cloth.json new file mode 100644 index 0000000..5f8be11 --- /dev/null +++ b/GameServer/data/gso/items/materials/fabric/plant_cloth.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "plant_cloth_common", + "texture": "assets/gso/textures/materials/fabric/plant_cloth.png", + "stats": [], + "meta": { + "materialCategory": "fabric" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/fabric/plant_fiber.json b/GameServer/data/gso/items/materials/fabric/plant_fiber.json new file mode 100644 index 0000000..2b3b757 --- /dev/null +++ b/GameServer/data/gso/items/materials/fabric/plant_fiber.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "plant_fiber_common", + "texture": "assets/gso/textures/materials/fabric/plant_fiber.png", + "stats": [], + "meta": { + "materialCategory": "fabric" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/food/bread.json b/GameServer/data/gso/items/materials/food/bread.json new file mode 100644 index 0000000..d53913a --- /dev/null +++ b/GameServer/data/gso/items/materials/food/bread.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "bread_common", + "texture": "assets/gso/textures/materials/food/bread.png", + "stats": [], + "meta": { + "materialCategory": "food" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/food/fish.json b/GameServer/data/gso/items/materials/food/fish.json new file mode 100644 index 0000000..de1c1dd --- /dev/null +++ b/GameServer/data/gso/items/materials/food/fish.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "fish_common", + "texture": "assets/gso/textures/materials/food/fish.png", + "stats": [], + "meta": { + "materialCategory": "food" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/food/fish_meat.json b/GameServer/data/gso/items/materials/food/fish_meat.json new file mode 100644 index 0000000..cbbca71 --- /dev/null +++ b/GameServer/data/gso/items/materials/food/fish_meat.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "fish_meat_common", + "texture": "assets/gso/textures/materials/food/fish_meat.png", + "stats": [], + "meta": { + "materialCategory": "food" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/food/flour.json b/GameServer/data/gso/items/materials/food/flour.json new file mode 100644 index 0000000..51214b6 --- /dev/null +++ b/GameServer/data/gso/items/materials/food/flour.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "flour_common", + "texture": "assets/gso/textures/materials/food/flour.png", + "stats": [], + "meta": { + "materialCategory": "food" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/food/fruit_plant.json b/GameServer/data/gso/items/materials/food/fruit_plant.json new file mode 100644 index 0000000..2dd83c4 --- /dev/null +++ b/GameServer/data/gso/items/materials/food/fruit_plant.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "fruit_plant_common", + "texture": "assets/gso/textures/materials/food/fruit_plant.png", + "stats": [], + "meta": { + "materialCategory": "food" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/food/grain.json b/GameServer/data/gso/items/materials/food/grain.json new file mode 100644 index 0000000..7c217eb --- /dev/null +++ b/GameServer/data/gso/items/materials/food/grain.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "grain_common", + "texture": "assets/gso/textures/materials/food/grain.png", + "stats": [], + "meta": { + "materialCategory": "food" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/food/grain_crop.json b/GameServer/data/gso/items/materials/food/grain_crop.json new file mode 100644 index 0000000..4d94bf6 --- /dev/null +++ b/GameServer/data/gso/items/materials/food/grain_crop.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "grain_crop_common", + "texture": "assets/gso/textures/materials/food/grain_crop.png", + "stats": [], + "meta": { + "materialCategory": "food" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/food/meat_stew.json b/GameServer/data/gso/items/materials/food/meat_stew.json new file mode 100644 index 0000000..7708a18 --- /dev/null +++ b/GameServer/data/gso/items/materials/food/meat_stew.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "meat_stew_common", + "texture": "assets/gso/textures/materials/food/meat_stew.png", + "stats": [], + "meta": { + "materialCategory": "food" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/food/ration_pack.json b/GameServer/data/gso/items/materials/food/ration_pack.json new file mode 100644 index 0000000..080b4cd --- /dev/null +++ b/GameServer/data/gso/items/materials/food/ration_pack.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "ration_pack_common", + "texture": "assets/gso/textures/materials/food/ration_pack.png", + "stats": [], + "meta": { + "materialCategory": "food" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/food/vegetable_plant.json b/GameServer/data/gso/items/materials/food/vegetable_plant.json new file mode 100644 index 0000000..617b2cc --- /dev/null +++ b/GameServer/data/gso/items/materials/food/vegetable_plant.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "vegetable_plant_common", + "texture": "assets/gso/textures/materials/food/vegetable_plant.png", + "stats": [], + "meta": { + "materialCategory": "food" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/food/vegetable_stew.json b/GameServer/data/gso/items/materials/food/vegetable_stew.json new file mode 100644 index 0000000..d701e38 --- /dev/null +++ b/GameServer/data/gso/items/materials/food/vegetable_stew.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "vegetable_stew_common", + "texture": "assets/gso/textures/materials/food/vegetable_stew.png", + "stats": [], + "meta": { + "materialCategory": "food" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/ingot/aluminum_ingot.json b/GameServer/data/gso/items/materials/ingot/aluminum_ingot.json new file mode 100644 index 0000000..ce236e0 --- /dev/null +++ b/GameServer/data/gso/items/materials/ingot/aluminum_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "aluminum_ingot_common", + "texture": "assets/gso/textures/materials/ingot/aluminum_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/ingot/carbon_ingot.json b/GameServer/data/gso/items/materials/ingot/carbon_ingot.json new file mode 100644 index 0000000..e4b6c3b --- /dev/null +++ b/GameServer/data/gso/items/materials/ingot/carbon_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "carbon_ingot_common", + "texture": "assets/gso/textures/materials/ingot/carbon_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/ingot/chromium_ingot.json b/GameServer/data/gso/items/materials/ingot/chromium_ingot.json new file mode 100644 index 0000000..8d7e546 --- /dev/null +++ b/GameServer/data/gso/items/materials/ingot/chromium_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "chromium_ingot_common", + "texture": "assets/gso/textures/materials/ingot/chromium_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/ingot/chronium_ingot.json b/GameServer/data/gso/items/materials/ingot/chronium_ingot.json new file mode 100644 index 0000000..cdcf576 --- /dev/null +++ b/GameServer/data/gso/items/materials/ingot/chronium_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "chronium_ingot_common", + "texture": "assets/gso/textures/materials/ingot/chronium_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/ingot/copper_ingot.json b/GameServer/data/gso/items/materials/ingot/copper_ingot.json new file mode 100644 index 0000000..e138c02 --- /dev/null +++ b/GameServer/data/gso/items/materials/ingot/copper_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "copper_ingot_common", + "texture": "assets/gso/textures/materials/ingot/copper_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/ingot/darksteel_ingot.json b/GameServer/data/gso/items/materials/ingot/darksteel_ingot.json new file mode 100644 index 0000000..7f1f87f --- /dev/null +++ b/GameServer/data/gso/items/materials/ingot/darksteel_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "darksteel_ingot_common", + "texture": "assets/gso/textures/materials/ingot/darksteel_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/ingot/gold_ingot.json b/GameServer/data/gso/items/materials/ingot/gold_ingot.json new file mode 100644 index 0000000..5daff67 --- /dev/null +++ b/GameServer/data/gso/items/materials/ingot/gold_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "gold_ingot_common", + "texture": "assets/gso/textures/materials/ingot/gold_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/ingot/iron_ingot.json b/GameServer/data/gso/items/materials/ingot/iron_ingot.json new file mode 100644 index 0000000..fc720b8 --- /dev/null +++ b/GameServer/data/gso/items/materials/ingot/iron_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "iron_ingot_common", + "texture": "assets/gso/textures/materials/ingot/iron_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/ingot/magnesium_ingot.json b/GameServer/data/gso/items/materials/ingot/magnesium_ingot.json new file mode 100644 index 0000000..2aa68f8 --- /dev/null +++ b/GameServer/data/gso/items/materials/ingot/magnesium_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "magnesium_ingot_common", + "texture": "assets/gso/textures/materials/ingot/magnesium_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/ingot/neutronium_ingot.json b/GameServer/data/gso/items/materials/ingot/neutronium_ingot.json new file mode 100644 index 0000000..c41551b --- /dev/null +++ b/GameServer/data/gso/items/materials/ingot/neutronium_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "neutronium_ingot_common", + "texture": "assets/gso/textures/materials/ingot/neutronium_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/ingot/platinum_ingot.json b/GameServer/data/gso/items/materials/ingot/platinum_ingot.json new file mode 100644 index 0000000..f0facc6 --- /dev/null +++ b/GameServer/data/gso/items/materials/ingot/platinum_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "platinum_ingot_common", + "texture": "assets/gso/textures/materials/ingot/platinum_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/ingot/silver_ingot.json b/GameServer/data/gso/items/materials/ingot/silver_ingot.json new file mode 100644 index 0000000..c1bf929 --- /dev/null +++ b/GameServer/data/gso/items/materials/ingot/silver_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "silver_ingot_common", + "texture": "assets/gso/textures/materials/ingot/silver_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/ingot/titanium_ingot.json b/GameServer/data/gso/items/materials/ingot/titanium_ingot.json new file mode 100644 index 0000000..bee6654 --- /dev/null +++ b/GameServer/data/gso/items/materials/ingot/titanium_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "titanium_ingot_common", + "texture": "assets/gso/textures/materials/ingot/titanium_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/ingot/tungsten_ingot.json b/GameServer/data/gso/items/materials/ingot/tungsten_ingot.json new file mode 100644 index 0000000..04cd018 --- /dev/null +++ b/GameServer/data/gso/items/materials/ingot/tungsten_ingot.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "tungsten_ingot_common", + "texture": "assets/gso/textures/materials/ingot/tungsten_ingot.png", + "stats": [], + "meta": { + "materialCategory": "ingot" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/liquid/water.json b/GameServer/data/gso/items/materials/liquid/water.json new file mode 100644 index 0000000..a582e19 --- /dev/null +++ b/GameServer/data/gso/items/materials/liquid/water.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "water_common", + "texture": "assets/gso/textures/materials/liquid/water.png", + "stats": [], + "meta": { + "materialCategory": "liquid" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/aerospace_aluminum.json b/GameServer/data/gso/items/materials/misc/aerospace_aluminum.json new file mode 100644 index 0000000..44e03d4 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/aerospace_aluminum.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "aerospace_aluminum_common", + "texture": "assets/gso/textures/materials/misc/aerospace_aluminum.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/alien_fauna_sample.json b/GameServer/data/gso/items/materials/misc/alien_fauna_sample.json new file mode 100644 index 0000000..12c4fca --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/alien_fauna_sample.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "alien_fauna_sample_common", + "texture": "assets/gso/textures/materials/misc/alien_fauna_sample.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/alien_protein.json b/GameServer/data/gso/items/materials/misc/alien_protein.json new file mode 100644 index 0000000..e2f0032 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/alien_protein.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "alien_protein_common", + "texture": "assets/gso/textures/materials/misc/alien_protein.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/animal_carcass.json b/GameServer/data/gso/items/materials/misc/animal_carcass.json new file mode 100644 index 0000000..a5f7989 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/animal_carcass.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "animal_carcass_common", + "texture": "assets/gso/textures/materials/misc/animal_carcass.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/bandage.json b/GameServer/data/gso/items/materials/misc/bandage.json new file mode 100644 index 0000000..99b667c --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/bandage.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "bandage_common", + "texture": "assets/gso/textures/materials/misc/bandage.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/body_exotic.json b/GameServer/data/gso/items/materials/misc/body_exotic.json new file mode 100644 index 0000000..1ac7c69 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/body_exotic.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "body_exotic_common", + "texture": "assets/gso/textures/materials/misc/body_exotic.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/body_light.json b/GameServer/data/gso/items/materials/misc/body_light.json new file mode 100644 index 0000000..47f0e18 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/body_light.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "body_light_common", + "texture": "assets/gso/textures/materials/misc/body_light.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/body_military.json b/GameServer/data/gso/items/materials/misc/body_military.json new file mode 100644 index 0000000..ed810d9 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/body_military.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "body_military_common", + "texture": "assets/gso/textures/materials/misc/body_military.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/body_standard.json b/GameServer/data/gso/items/materials/misc/body_standard.json new file mode 100644 index 0000000..865e876 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/body_standard.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "body_standard_common", + "texture": "assets/gso/textures/materials/misc/body_standard.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/boots_light.json b/GameServer/data/gso/items/materials/misc/boots_light.json new file mode 100644 index 0000000..cd3f5a8 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/boots_light.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "boots_light_common", + "texture": "assets/gso/textures/materials/misc/boots_light.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/boots_magnetic.json b/GameServer/data/gso/items/materials/misc/boots_magnetic.json new file mode 100644 index 0000000..b773376 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/boots_magnetic.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "boots_magnetic_common", + "texture": "assets/gso/textures/materials/misc/boots_magnetic.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/boots_military.json b/GameServer/data/gso/items/materials/misc/boots_military.json new file mode 100644 index 0000000..7b16a29 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/boots_military.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "boots_military_common", + "texture": "assets/gso/textures/materials/misc/boots_military.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/boots_standard.json b/GameServer/data/gso/items/materials/misc/boots_standard.json new file mode 100644 index 0000000..502e2a7 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/boots_standard.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "boots_standard_common", + "texture": "assets/gso/textures/materials/misc/boots_standard.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/carbon_shale.json b/GameServer/data/gso/items/materials/misc/carbon_shale.json new file mode 100644 index 0000000..4f0011f --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/carbon_shale.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "carbon_shale_common", + "texture": "assets/gso/textures/materials/misc/carbon_shale.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/chromium_steel.json b/GameServer/data/gso/items/materials/misc/chromium_steel.json new file mode 100644 index 0000000..5a38bd2 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/chromium_steel.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "chromium_steel_common", + "texture": "assets/gso/textures/materials/misc/chromium_steel.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/cooked_fish.json b/GameServer/data/gso/items/materials/misc/cooked_fish.json new file mode 100644 index 0000000..04fad5b --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/cooked_fish.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "cooked_fish_common", + "texture": "assets/gso/textures/materials/misc/cooked_fish.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/cooked_meat.json b/GameServer/data/gso/items/materials/misc/cooked_meat.json new file mode 100644 index 0000000..f744a82 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/cooked_meat.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "cooked_meat_common", + "texture": "assets/gso/textures/materials/misc/cooked_meat.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/crystal_glass.json b/GameServer/data/gso/items/materials/misc/crystal_glass.json new file mode 100644 index 0000000..ed0dd5c --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/crystal_glass.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "crystal_glass_common", + "texture": "assets/gso/textures/materials/misc/crystal_glass.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/dough.json b/GameServer/data/gso/items/materials/misc/dough.json new file mode 100644 index 0000000..eaba2dd --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/dough.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "dough_common", + "texture": "assets/gso/textures/materials/misc/dough.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/flux_crystal.json b/GameServer/data/gso/items/materials/misc/flux_crystal.json new file mode 100644 index 0000000..9bc919f --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/flux_crystal.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "flux_crystal_common", + "texture": "assets/gso/textures/materials/misc/flux_crystal.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/gloves_industrial.json b/GameServer/data/gso/items/materials/misc/gloves_industrial.json new file mode 100644 index 0000000..b734c4b --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/gloves_industrial.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "gloves_industrial_common", + "texture": "assets/gso/textures/materials/misc/gloves_industrial.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/gloves_light.json b/GameServer/data/gso/items/materials/misc/gloves_light.json new file mode 100644 index 0000000..90fb50d --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/gloves_light.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "gloves_light_common", + "texture": "assets/gso/textures/materials/misc/gloves_light.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/gloves_military.json b/GameServer/data/gso/items/materials/misc/gloves_military.json new file mode 100644 index 0000000..b6df73e --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/gloves_military.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "gloves_military_common", + "texture": "assets/gso/textures/materials/misc/gloves_military.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/gloves_standard.json b/GameServer/data/gso/items/materials/misc/gloves_standard.json new file mode 100644 index 0000000..37f29e8 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/gloves_standard.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "gloves_standard_common", + "texture": "assets/gso/textures/materials/misc/gloves_standard.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/helmet_exotic.json b/GameServer/data/gso/items/materials/misc/helmet_exotic.json new file mode 100644 index 0000000..ff48b87 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/helmet_exotic.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "helmet_exotic_common", + "texture": "assets/gso/textures/materials/misc/helmet_exotic.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/helmet_light.json b/GameServer/data/gso/items/materials/misc/helmet_light.json new file mode 100644 index 0000000..79da9f9 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/helmet_light.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "helmet_light_common", + "texture": "assets/gso/textures/materials/misc/helmet_light.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/helmet_military.json b/GameServer/data/gso/items/materials/misc/helmet_military.json new file mode 100644 index 0000000..d47135c --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/helmet_military.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "helmet_military_common", + "texture": "assets/gso/textures/materials/misc/helmet_military.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/helmet_standard.json b/GameServer/data/gso/items/materials/misc/helmet_standard.json new file mode 100644 index 0000000..1211052 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/helmet_standard.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "helmet_standard_common", + "texture": "assets/gso/textures/materials/misc/helmet_standard.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/hydroponic_growth_medium.json b/GameServer/data/gso/items/materials/misc/hydroponic_growth_medium.json new file mode 100644 index 0000000..82b2306 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/hydroponic_growth_medium.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "hydroponic_growth_medium_common", + "texture": "assets/gso/textures/materials/misc/hydroponic_growth_medium.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/ice_chunk.json b/GameServer/data/gso/items/materials/misc/ice_chunk.json new file mode 100644 index 0000000..dca1c4d --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/ice_chunk.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "ice_chunk_common", + "texture": "assets/gso/textures/materials/misc/ice_chunk.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/iron_ore_common.json b/GameServer/data/gso/items/materials/misc/iron_ore_common.json new file mode 100644 index 0000000..e382e63 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/iron_ore_common.json @@ -0,0 +1,16 @@ +{ + "templates": { + "materials": { + "id": "iron_ore_common", + "name": "Raw Iron Chunk", + "meta": { + "materialCategory": "misc" + }, + "texture": "assets/gso/textures/materials/misc/iron_ore.png", + "price": 5, + "rarity": "common", + "stackable": true, + "maxStack": 999 + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/life_support_basic.json b/GameServer/data/gso/items/materials/misc/life_support_basic.json new file mode 100644 index 0000000..0bacdba --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/life_support_basic.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "life_support_basic_common", + "texture": "assets/gso/textures/materials/misc/life_support_basic.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/life_support_exotic.json b/GameServer/data/gso/items/materials/misc/life_support_exotic.json new file mode 100644 index 0000000..6b7e48d --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/life_support_exotic.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "life_support_exotic_common", + "texture": "assets/gso/textures/materials/misc/life_support_exotic.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/life_support_military.json b/GameServer/data/gso/items/materials/misc/life_support_military.json new file mode 100644 index 0000000..99e6d10 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/life_support_military.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "life_support_military_common", + "texture": "assets/gso/textures/materials/misc/life_support_military.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/life_support_standard.json b/GameServer/data/gso/items/materials/misc/life_support_standard.json new file mode 100644 index 0000000..e595921 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/life_support_standard.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "life_support_standard_common", + "texture": "assets/gso/textures/materials/misc/life_support_standard.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/luxury_meal.json b/GameServer/data/gso/items/materials/misc/luxury_meal.json new file mode 100644 index 0000000..c172420 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/luxury_meal.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "luxury_meal_common", + "texture": "assets/gso/textures/materials/misc/luxury_meal.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/magnetic_coil.json b/GameServer/data/gso/items/materials/misc/magnetic_coil.json new file mode 100644 index 0000000..6821764 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/magnetic_coil.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "magnetic_coil_common", + "texture": "assets/gso/textures/materials/misc/magnetic_coil.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/medical_pack.json b/GameServer/data/gso/items/materials/misc/medical_pack.json new file mode 100644 index 0000000..a383c69 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/medical_pack.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "medical_pack_common", + "texture": "assets/gso/textures/materials/misc/medical_pack.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/military_ration.json b/GameServer/data/gso/items/materials/misc/military_ration.json new file mode 100644 index 0000000..0d32df0 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/military_ration.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "military_ration_common", + "texture": "assets/gso/textures/materials/misc/military_ration.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/neutronium_shard.json b/GameServer/data/gso/items/materials/misc/neutronium_shard.json new file mode 100644 index 0000000..ac6591e --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/neutronium_shard.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "neutronium_shard_common", + "texture": "assets/gso/textures/materials/misc/neutronium_shard.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/nutrient_paste.json b/GameServer/data/gso/items/materials/misc/nutrient_paste.json new file mode 100644 index 0000000..5f7ba9f --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/nutrient_paste.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "nutrient_paste_common", + "texture": "assets/gso/textures/materials/misc/nutrient_paste.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/pants_exotic.json b/GameServer/data/gso/items/materials/misc/pants_exotic.json new file mode 100644 index 0000000..f321099 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/pants_exotic.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "pants_exotic_common", + "texture": "assets/gso/textures/materials/misc/pants_exotic.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/pants_heavy.json b/GameServer/data/gso/items/materials/misc/pants_heavy.json new file mode 100644 index 0000000..4e7b70a --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/pants_heavy.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "pants_heavy_common", + "texture": "assets/gso/textures/materials/misc/pants_heavy.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/pants_light.json b/GameServer/data/gso/items/materials/misc/pants_light.json new file mode 100644 index 0000000..e54f1b0 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/pants_light.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "pants_light_common", + "texture": "assets/gso/textures/materials/misc/pants_light.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/pants_standard.json b/GameServer/data/gso/items/materials/misc/pants_standard.json new file mode 100644 index 0000000..8bbd979 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/pants_standard.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "pants_standard_common", + "texture": "assets/gso/textures/materials/misc/pants_standard.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/phase_crystal.json b/GameServer/data/gso/items/materials/misc/phase_crystal.json new file mode 100644 index 0000000..0365dbb --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/phase_crystal.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "phase_crystal_common", + "texture": "assets/gso/textures/materials/misc/phase_crystal.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/processed_wood.json b/GameServer/data/gso/items/materials/misc/processed_wood.json new file mode 100644 index 0000000..c856c4a --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/processed_wood.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "processed_wood_common", + "texture": "assets/gso/textures/materials/misc/processed_wood.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/protein_bar.json b/GameServer/data/gso/items/materials/misc/protein_bar.json new file mode 100644 index 0000000..c1e9309 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/protein_bar.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "protein_bar_common", + "texture": "assets/gso/textures/materials/misc/protein_bar.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/raw_fruits.json b/GameServer/data/gso/items/materials/misc/raw_fruits.json new file mode 100644 index 0000000..2c89214 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/raw_fruits.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "raw_fruits_common", + "texture": "assets/gso/textures/materials/misc/raw_fruits.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/raw_meat.json b/GameServer/data/gso/items/materials/misc/raw_meat.json new file mode 100644 index 0000000..04380f6 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/raw_meat.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "raw_meat_common", + "texture": "assets/gso/textures/materials/misc/raw_meat.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/raw_vegetables.json b/GameServer/data/gso/items/materials/misc/raw_vegetables.json new file mode 100644 index 0000000..5eced33 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/raw_vegetables.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "raw_vegetables_common", + "texture": "assets/gso/textures/materials/misc/raw_vegetables.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/raw_wood.json b/GameServer/data/gso/items/materials/misc/raw_wood.json new file mode 100644 index 0000000..e823203 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/raw_wood.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "raw_wood_common", + "texture": "assets/gso/textures/materials/misc/raw_wood.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/silicon_wafer.json b/GameServer/data/gso/items/materials/misc/silicon_wafer.json new file mode 100644 index 0000000..bd28e1b --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/silicon_wafer.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "silicon_wafer_common", + "texture": "assets/gso/textures/materials/misc/silicon_wafer.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/tree.json b/GameServer/data/gso/items/materials/misc/tree.json new file mode 100644 index 0000000..f0c9fd8 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/tree.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "tree_common", + "texture": "assets/gso/textures/materials/misc/tree.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/tree_sap.json b/GameServer/data/gso/items/materials/misc/tree_sap.json new file mode 100644 index 0000000..2acae8e --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/tree_sap.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "tree_sap_common", + "texture": "assets/gso/textures/materials/misc/tree_sap.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/tungsten_steel.json b/GameServer/data/gso/items/materials/misc/tungsten_steel.json new file mode 100644 index 0000000..072f02c --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/tungsten_steel.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "tungsten_steel_common", + "texture": "assets/gso/textures/materials/misc/tungsten_steel.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/void_crystal.json b/GameServer/data/gso/items/materials/misc/void_crystal.json new file mode 100644 index 0000000..2959095 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/void_crystal.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "void_crystal_common", + "texture": "assets/gso/textures/materials/misc/void_crystal.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/void_residue.json b/GameServer/data/gso/items/materials/misc/void_residue.json new file mode 100644 index 0000000..51b1851 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/void_residue.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "void_residue_common", + "texture": "assets/gso/textures/materials/misc/void_residue.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/misc/wooden_components.json b/GameServer/data/gso/items/materials/misc/wooden_components.json new file mode 100644 index 0000000..7e524b4 --- /dev/null +++ b/GameServer/data/gso/items/materials/misc/wooden_components.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "wooden_components_common", + "texture": "assets/gso/textures/materials/misc/wooden_components.png", + "stats": [], + "meta": { + "materialCategory": "misc" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/ore/bauxite_ore.json b/GameServer/data/gso/items/materials/ore/bauxite_ore.json new file mode 100644 index 0000000..67239c3 --- /dev/null +++ b/GameServer/data/gso/items/materials/ore/bauxite_ore.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "bauxite_ore_common", + "texture": "assets/gso/textures/materials/ore/bauxite_ore.png", + "stats": [], + "meta": { + "materialCategory": "ore" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/ore/chromium_ore.json b/GameServer/data/gso/items/materials/ore/chromium_ore.json new file mode 100644 index 0000000..4b2dfe6 --- /dev/null +++ b/GameServer/data/gso/items/materials/ore/chromium_ore.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "chromium_ore_common", + "texture": "assets/gso/textures/materials/ore/chromium_ore.png", + "stats": [], + "meta": { + "materialCategory": "ore" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/ore/chronite_ore.json b/GameServer/data/gso/items/materials/ore/chronite_ore.json new file mode 100644 index 0000000..8b3b6cc --- /dev/null +++ b/GameServer/data/gso/items/materials/ore/chronite_ore.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "chronite_ore_common", + "texture": "assets/gso/textures/materials/ore/chronite_ore.png", + "stats": [], + "meta": { + "materialCategory": "ore" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/ore/copper_ore.json b/GameServer/data/gso/items/materials/ore/copper_ore.json new file mode 100644 index 0000000..ae7c850 --- /dev/null +++ b/GameServer/data/gso/items/materials/ore/copper_ore.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "copper_ore_common", + "texture": "assets/gso/textures/materials/ore/copper_ore.png", + "stats": [], + "meta": { + "materialCategory": "ore" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/ore/dark_iron_ore.json b/GameServer/data/gso/items/materials/ore/dark_iron_ore.json new file mode 100644 index 0000000..4a5b5b2 --- /dev/null +++ b/GameServer/data/gso/items/materials/ore/dark_iron_ore.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "dark_iron_ore_common", + "texture": "assets/gso/textures/materials/ore/dark_iron_ore.png", + "stats": [], + "meta": { + "materialCategory": "ore" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/ore/gold_ore.json b/GameServer/data/gso/items/materials/ore/gold_ore.json new file mode 100644 index 0000000..463cd2b --- /dev/null +++ b/GameServer/data/gso/items/materials/ore/gold_ore.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "gold_ore_common", + "texture": "assets/gso/textures/materials/ore/gold_ore.png", + "stats": [], + "meta": { + "materialCategory": "ore" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/ore/iron_ore.json b/GameServer/data/gso/items/materials/ore/iron_ore.json new file mode 100644 index 0000000..bbd8d8a --- /dev/null +++ b/GameServer/data/gso/items/materials/ore/iron_ore.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "iron_ore_common", + "texture": "assets/gso/textures/materials/ore/iron_ore.png", + "stats": [], + "meta": { + "materialCategory": "ore" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/ore/magnesium_ore.json b/GameServer/data/gso/items/materials/ore/magnesium_ore.json new file mode 100644 index 0000000..47f162e --- /dev/null +++ b/GameServer/data/gso/items/materials/ore/magnesium_ore.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "magnesium_ore_common", + "texture": "assets/gso/textures/materials/ore/magnesium_ore.png", + "stats": [], + "meta": { + "materialCategory": "ore" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/ore/platinum_ore.json b/GameServer/data/gso/items/materials/ore/platinum_ore.json new file mode 100644 index 0000000..bd044b6 --- /dev/null +++ b/GameServer/data/gso/items/materials/ore/platinum_ore.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "platinum_ore_common", + "texture": "assets/gso/textures/materials/ore/platinum_ore.png", + "stats": [], + "meta": { + "materialCategory": "ore" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/ore/silver_ore.json b/GameServer/data/gso/items/materials/ore/silver_ore.json new file mode 100644 index 0000000..93a220f --- /dev/null +++ b/GameServer/data/gso/items/materials/ore/silver_ore.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "silver_ore_common", + "texture": "assets/gso/textures/materials/ore/silver_ore.png", + "stats": [], + "meta": { + "materialCategory": "ore" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/ore/titanium_ore.json b/GameServer/data/gso/items/materials/ore/titanium_ore.json new file mode 100644 index 0000000..045c5ca --- /dev/null +++ b/GameServer/data/gso/items/materials/ore/titanium_ore.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "titanium_ore_common", + "texture": "assets/gso/textures/materials/ore/titanium_ore.png", + "stats": [], + "meta": { + "materialCategory": "ore" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/ore/tungsten_ore.json b/GameServer/data/gso/items/materials/ore/tungsten_ore.json new file mode 100644 index 0000000..56ee49f --- /dev/null +++ b/GameServer/data/gso/items/materials/ore/tungsten_ore.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "tungsten_ore_common", + "texture": "assets/gso/textures/materials/ore/tungsten_ore.png", + "stats": [], + "meta": { + "materialCategory": "ore" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/stone/fluxstone.json b/GameServer/data/gso/items/materials/stone/fluxstone.json new file mode 100644 index 0000000..073b760 --- /dev/null +++ b/GameServer/data/gso/items/materials/stone/fluxstone.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "fluxstone_common", + "texture": "assets/gso/textures/materials/stone/fluxstone.png", + "stats": [], + "meta": { + "materialCategory": "stone" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/materials/wood/wood_planks.json b/GameServer/data/gso/items/materials/wood/wood_planks.json new file mode 100644 index 0000000..bdc7092 --- /dev/null +++ b/GameServer/data/gso/items/materials/wood/wood_planks.json @@ -0,0 +1,12 @@ +{ + "templates": { + "materials": { + "id": "wood_planks_common", + "texture": "assets/gso/textures/materials/wood/wood_planks.png", + "stats": [], + "meta": { + "materialCategory": "wood" + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/ships/dreadnought_legendary.json b/GameServer/data/gso/items/ships/dreadnought_legendary.json new file mode 100644 index 0000000..425ef64 --- /dev/null +++ b/GameServer/data/gso/items/ships/dreadnought_legendary.json @@ -0,0 +1,27 @@ +{ + "templates": { + "ship": { + "id": "dreadnought_legendary", + "name": "Dreadnought", + "type": "ship", + "rarity": "legendary", + "price": 500, + "currency": "gems", + "description": "The most powerful ship ever built \u2014 a living fortress", + "texture": "assets/gso/textures/ships/dreadnought_legendary.png", + "stats": { + "attack": 60, + "speed": 5, + "defense": 55, + "hull": 500 + }, + "categories": [ + "shop" + ], + "requirements": { + "level": 30 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/ships/heavy_cruiser_rare.json b/GameServer/data/gso/items/ships/heavy_cruiser_rare.json new file mode 100644 index 0000000..c2e989b --- /dev/null +++ b/GameServer/data/gso/items/ships/heavy_cruiser_rare.json @@ -0,0 +1,28 @@ +{ + "templates": { + "ship": { + "id": "heavy_cruiser_rare", + "name": "Heavy Cruiser", + "type": "ship", + "rarity": "rare", + "price": 35000, + "currency": "credits", + "description": "Powerful heavy cruiser with superior firepower and armour", + "texture": "assets/gso/textures/ships/heavy_cruiser_rare.png", + "stats": { + "attack": 28, + "speed": 8, + "defense": 25, + "hull": 200 + }, + "categories": [ + "shop", + "dungeon_reward" + ], + "requirements": { + "level": 12 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/ships/interceptor_common.json b/GameServer/data/gso/items/ships/interceptor_common.json new file mode 100644 index 0000000..4bc2757 --- /dev/null +++ b/GameServer/data/gso/items/ships/interceptor_common.json @@ -0,0 +1,28 @@ +{ + "templates": { + "ship": { + "id": "interceptor_common", + "name": "Interceptor", + "type": "ship", + "rarity": "common", + "price": 8000, + "currency": "credits", + "description": "Fast attack ship for hit-and-run tactics", + "texture": "assets/gso/textures/ships/interceptor_common.png", + "stats": { + "attack": 12, + "speed": 18, + "defense": 8, + "hull": 80 + }, + "categories": [ + "shop", + "dungeon_reward" + ], + "requirements": { + "level": 3 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/ships/interceptor_uncommon.json b/GameServer/data/gso/items/ships/interceptor_uncommon.json new file mode 100644 index 0000000..52561bb --- /dev/null +++ b/GameServer/data/gso/items/ships/interceptor_uncommon.json @@ -0,0 +1,28 @@ +{ + "templates": { + "ship": { + "id": "interceptor_uncommon", + "name": "Interceptor II", + "type": "ship", + "rarity": "uncommon", + "price": 18000, + "currency": "credits", + "description": "Enhanced interceptor with improved weapons", + "texture": "assets/gso/textures/ships/interceptor_uncommon.png", + "stats": { + "attack": 15, + "speed": 22, + "defense": 10, + "hull": 95 + }, + "categories": [ + "shop", + "dungeon_reward" + ], + "requirements": { + "level": 7 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/ships/phantom_epic.json b/GameServer/data/gso/items/ships/phantom_epic.json new file mode 100644 index 0000000..4e6f6b6 --- /dev/null +++ b/GameServer/data/gso/items/ships/phantom_epic.json @@ -0,0 +1,27 @@ +{ + "templates": { + "ship": { + "id": "phantom_epic", + "name": "Phantom", + "type": "ship", + "rarity": "epic", + "price": 80000, + "currency": "credits", + "description": "A stealth vessel that strikes from the shadows", + "texture": "assets/gso/textures/ships/phantom_epic.png", + "stats": { + "attack": 35, + "speed": 30, + "defense": 14, + "hull": 130 + }, + "categories": [ + "shop" + ], + "requirements": { + "level": 20 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/ships/starter_cruiser_common.json b/GameServer/data/gso/items/ships/starter_cruiser_common.json new file mode 100644 index 0000000..8a7f3d4 --- /dev/null +++ b/GameServer/data/gso/items/ships/starter_cruiser_common.json @@ -0,0 +1,28 @@ +{ + "templates": { + "ship": { + "id": "starter_cruiser_common", + "name": "Starter Cruiser", + "type": "ship", + "rarity": "common", + "price": 5000, + "currency": "credits", + "description": "Reliable starter cruiser for new pilots", + "texture": "assets/gso/textures/ships/starter_cruiser_common.png", + "stats": { + "attack": 15, + "speed": 10, + "defense": 12, + "hull": 100 + }, + "categories": [ + "shop", + "dungeon_reward" + ], + "requirements": { + "level": 1 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/ships/starter_cruiser_rare.json b/GameServer/data/gso/items/ships/starter_cruiser_rare.json new file mode 100644 index 0000000..f2fd32f --- /dev/null +++ b/GameServer/data/gso/items/ships/starter_cruiser_rare.json @@ -0,0 +1,28 @@ +{ + "templates": { + "ship": { + "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/gso/textures/ships/starter_cruiser_rare.png", + "stats": { + "attack": 22, + "speed": 15, + "defense": 18, + "hull": 150 + }, + "categories": [ + "shop", + "dungeon_reward" + ], + "requirements": { + "level": 10 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/ships/starter_cruiser_uncommon.json b/GameServer/data/gso/items/ships/starter_cruiser_uncommon.json new file mode 100644 index 0000000..73a6e15 --- /dev/null +++ b/GameServer/data/gso/items/ships/starter_cruiser_uncommon.json @@ -0,0 +1,28 @@ +{ + "templates": { + "ship": { + "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/gso/textures/ships/starter_cruiser_uncommon.png", + "stats": { + "attack": 18, + "speed": 12, + "defense": 15, + "hull": 120 + }, + "categories": [ + "shop", + "dungeon_reward" + ], + "requirements": { + "level": 5 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapon/assaultrifle/assault_rifle_common.json b/GameServer/data/gso/items/weapon/assaultrifle/assault_rifle_common.json new file mode 100644 index 0000000..3fff28b --- /dev/null +++ b/GameServer/data/gso/items/weapon/assaultrifle/assault_rifle_common.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "assault_rifle_common", + "texture": "assets/gso/textures/weapon/assaultrifle/assault_rifle_common.png", + "stats": { + "damage": 26, + "criticalChance": 0.04, + "attackRate": 6 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapon/assaultrifle/assault_rifle_epic.json b/GameServer/data/gso/items/weapon/assaultrifle/assault_rifle_epic.json new file mode 100644 index 0000000..b9964bb --- /dev/null +++ b/GameServer/data/gso/items/weapon/assaultrifle/assault_rifle_epic.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "assault_rifle_epic", + "texture": "assets/gso/textures/weapon/assaultrifle/assault_rifle_epic.png", + "stats": { + "damage": 56, + "criticalChance": 0.14, + "attackRate": 8 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapon/assaultrifle/assault_rifle_rare.json b/GameServer/data/gso/items/weapon/assaultrifle/assault_rifle_rare.json new file mode 100644 index 0000000..4bef56d --- /dev/null +++ b/GameServer/data/gso/items/weapon/assaultrifle/assault_rifle_rare.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "assault_rifle_rare", + "texture": "assets/gso/textures/weapon/assaultrifle/assault_rifle_rare.png", + "stats": { + "damage": 38, + "criticalChance": 0.08, + "attackRate": 7 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapon/dualpistol/dual_pistol_common.json b/GameServer/data/gso/items/weapon/dualpistol/dual_pistol_common.json new file mode 100644 index 0000000..12d45fb --- /dev/null +++ b/GameServer/data/gso/items/weapon/dualpistol/dual_pistol_common.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "dual_pistol_common", + "texture": "assets/gso/textures/weapon/dualpistol/dual_pistol_common.png", + "stats": { + "damage": 14, + "criticalChance": 0.06, + "attackRate": 7 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapon/dualpistol/dual_pistol_epic.json b/GameServer/data/gso/items/weapon/dualpistol/dual_pistol_epic.json new file mode 100644 index 0000000..541f57a --- /dev/null +++ b/GameServer/data/gso/items/weapon/dualpistol/dual_pistol_epic.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "dual_pistol_epic", + "texture": "assets/gso/textures/weapon/dualpistol/dual_pistol_epic.png", + "stats": { + "damage": 34, + "criticalChance": 0.2, + "attackRate": 11 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapon/dualpistol/dual_pistol_rare.json b/GameServer/data/gso/items/weapon/dualpistol/dual_pistol_rare.json new file mode 100644 index 0000000..bb91d2c --- /dev/null +++ b/GameServer/data/gso/items/weapon/dualpistol/dual_pistol_rare.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "dual_pistol_rare", + "texture": "assets/gso/textures/weapon/dualpistol/dual_pistol_rare.png", + "stats": { + "damage": 22, + "criticalChance": 0.12, + "attackRate": 9 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapon/laserbow/laser_bow_common.json b/GameServer/data/gso/items/weapon/laserbow/laser_bow_common.json new file mode 100644 index 0000000..f421632 --- /dev/null +++ b/GameServer/data/gso/items/weapon/laserbow/laser_bow_common.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "laser_bow_common", + "texture": "assets/gso/textures/weapon/laserbow/laser_bow_common.png", + "stats": { + "damage": 40, + "criticalChance": 0.18, + "attackRate": 3 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapon/laserbow/laser_bow_epic.json b/GameServer/data/gso/items/weapon/laserbow/laser_bow_epic.json new file mode 100644 index 0000000..63c290a --- /dev/null +++ b/GameServer/data/gso/items/weapon/laserbow/laser_bow_epic.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "laser_bow_epic", + "texture": "assets/gso/textures/weapon/laserbow/laser_bow_epic.png", + "stats": { + "damage": 95, + "criticalChance": 0.45, + "attackRate": 4 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapon/laserbow/laser_bow_rare.json b/GameServer/data/gso/items/weapon/laserbow/laser_bow_rare.json new file mode 100644 index 0000000..1a5813b --- /dev/null +++ b/GameServer/data/gso/items/weapon/laserbow/laser_bow_rare.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "laser_bow_rare", + "texture": "assets/gso/textures/weapon/laserbow/laser_bow_rare.png", + "stats": { + "damage": 62, + "criticalChance": 0.3, + "attackRate": 4 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapon/lasercrossbow/laser_crossbow_common.json b/GameServer/data/gso/items/weapon/lasercrossbow/laser_crossbow_common.json new file mode 100644 index 0000000..1e84817 --- /dev/null +++ b/GameServer/data/gso/items/weapon/lasercrossbow/laser_crossbow_common.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "laser_crossbow_common", + "texture": "assets/gso/textures/weapon/lasercrossbow/laser_crossbow_common.png", + "stats": { + "damage": 48, + "criticalChance": 0.15, + "attackRate": 2 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapon/lasercrossbow/laser_crossbow_epic.json b/GameServer/data/gso/items/weapon/lasercrossbow/laser_crossbow_epic.json new file mode 100644 index 0000000..93692b6 --- /dev/null +++ b/GameServer/data/gso/items/weapon/lasercrossbow/laser_crossbow_epic.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "laser_crossbow_epic", + "texture": "assets/gso/textures/weapon/lasercrossbow/laser_crossbow_epic.png", + "stats": { + "damage": 110, + "criticalChance": 0.4, + "attackRate": 3 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapon/lasercrossbow/laser_crossbow_rare.json b/GameServer/data/gso/items/weapon/lasercrossbow/laser_crossbow_rare.json new file mode 100644 index 0000000..be4921c --- /dev/null +++ b/GameServer/data/gso/items/weapon/lasercrossbow/laser_crossbow_rare.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "laser_crossbow_rare", + "texture": "assets/gso/textures/weapon/lasercrossbow/laser_crossbow_rare.png", + "stats": { + "damage": 72, + "criticalChance": 0.25, + "attackRate": 3 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapon/pistol/pistol_marksman_rare.json b/GameServer/data/gso/items/weapon/pistol/pistol_marksman_rare.json new file mode 100644 index 0000000..7f03a00 --- /dev/null +++ b/GameServer/data/gso/items/weapon/pistol/pistol_marksman_rare.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "pistol_marksman_rare", + "texture": "assets/gso/textures/weapon/pistol/pistol_marksman_rare.png", + "stats": { + "damage": 28, + "criticalChance": 0.1, + "attackRate": 5 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapon/pistol/pistol_overcharge_epic.json b/GameServer/data/gso/items/weapon/pistol/pistol_overcharge_epic.json new file mode 100644 index 0000000..d7206b6 --- /dev/null +++ b/GameServer/data/gso/items/weapon/pistol/pistol_overcharge_epic.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "pistol_overcharge_epic", + "texture": "assets/gso/textures/weapon/pistol/pistol_overcharge_epic.png", + "stats": { + "damage": 42, + "criticalChance": 0.18, + "attackRate": 6 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapon/pistol/pistol_standard_common.json b/GameServer/data/gso/items/weapon/pistol/pistol_standard_common.json new file mode 100644 index 0000000..4d219d6 --- /dev/null +++ b/GameServer/data/gso/items/weapon/pistol/pistol_standard_common.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "pistol_standard_common", + "texture": "assets/gso/textures/weapon/pistol/pistol_standard_common.png", + "stats": { + "damage": 18, + "criticalChance": 0.05, + "attackRate": 4 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapon/plasmacutter/plasma_cutter_common.json b/GameServer/data/gso/items/weapon/plasmacutter/plasma_cutter_common.json new file mode 100644 index 0000000..2bda647 --- /dev/null +++ b/GameServer/data/gso/items/weapon/plasmacutter/plasma_cutter_common.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "plasma_cutter_common", + "texture": "assets/gso/textures/weapon/plasmacutter/plasma_cutter_common.png", + "stats": { + "damage": 60, + "criticalChance": 0.12, + "attackRate": 3 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapon/plasmacutter/plasma_cutter_epic.json b/GameServer/data/gso/items/weapon/plasmacutter/plasma_cutter_epic.json new file mode 100644 index 0000000..f6700d1 --- /dev/null +++ b/GameServer/data/gso/items/weapon/plasmacutter/plasma_cutter_epic.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "plasma_cutter_epic", + "texture": "assets/gso/textures/weapon/plasmacutter/plasma_cutter_epic.png", + "stats": { + "damage": 135, + "criticalChance": 0.35, + "attackRate": 5 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapon/plasmacutter/plasma_cutter_rare.json b/GameServer/data/gso/items/weapon/plasmacutter/plasma_cutter_rare.json new file mode 100644 index 0000000..0cc02a0 --- /dev/null +++ b/GameServer/data/gso/items/weapon/plasmacutter/plasma_cutter_rare.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "plasma_cutter_rare", + "texture": "assets/gso/textures/weapon/plasmacutter/plasma_cutter_rare.png", + "stats": { + "damage": 90, + "criticalChance": 0.2, + "attackRate": 4 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapon/rocketlauncher/rocket_launcher_common.json b/GameServer/data/gso/items/weapon/rocketlauncher/rocket_launcher_common.json new file mode 100644 index 0000000..cdb832c --- /dev/null +++ b/GameServer/data/gso/items/weapon/rocketlauncher/rocket_launcher_common.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "rocket_launcher_common", + "texture": "assets/gso/textures/weapon/rocketlauncher/rocket_launcher_common.png", + "stats": { + "damage": 140, + "criticalChance": 0.1, + "attackRate": 1 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapon/rocketlauncher/rocket_launcher_epic.json b/GameServer/data/gso/items/weapon/rocketlauncher/rocket_launcher_epic.json new file mode 100644 index 0000000..9d80a49 --- /dev/null +++ b/GameServer/data/gso/items/weapon/rocketlauncher/rocket_launcher_epic.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "rocket_launcher_epic", + "texture": "assets/gso/textures/weapon/rocketlauncher/rocket_launcher_epic.png", + "stats": { + "damage": 320, + "criticalChance": 0.25, + "attackRate": 2 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapon/rocketlauncher/rocket_launcher_rare.json b/GameServer/data/gso/items/weapon/rocketlauncher/rocket_launcher_rare.json new file mode 100644 index 0000000..d9f7bb0 --- /dev/null +++ b/GameServer/data/gso/items/weapon/rocketlauncher/rocket_launcher_rare.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "rocket_launcher_rare", + "texture": "assets/gso/textures/weapon/rocketlauncher/rocket_launcher_rare.png", + "stats": { + "damage": 220, + "criticalChance": 0.18, + "attackRate": 1 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapon/sniper/sniper_common.json b/GameServer/data/gso/items/weapon/sniper/sniper_common.json new file mode 100644 index 0000000..f5f3cac --- /dev/null +++ b/GameServer/data/gso/items/weapon/sniper/sniper_common.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "sniper_common", + "texture": "assets/gso/textures/weapon/sniper/sniper_common.png", + "stats": { + "damage": 90, + "criticalChance": 0.2, + "attackRate": 1 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapon/sniper/sniper_epic.json b/GameServer/data/gso/items/weapon/sniper/sniper_epic.json new file mode 100644 index 0000000..6789b70 --- /dev/null +++ b/GameServer/data/gso/items/weapon/sniper/sniper_epic.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "sniper_epic", + "texture": "assets/gso/textures/weapon/sniper/sniper_epic.png", + "stats": { + "damage": 190, + "criticalChance": 0.45, + "attackRate": 2 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapon/sniper/sniper_rare.json b/GameServer/data/gso/items/weapon/sniper/sniper_rare.json new file mode 100644 index 0000000..bbbd9e4 --- /dev/null +++ b/GameServer/data/gso/items/weapon/sniper/sniper_rare.json @@ -0,0 +1,13 @@ +{ + "templates": { + "weapon": { + "id": "sniper_rare", + "texture": "assets/gso/textures/weapon/sniper/sniper_rare.png", + "stats": { + "damage": 135, + "criticalChance": 0.3, + "attackRate": 1 + } + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapons/assault_rifle_common.json b/GameServer/data/gso/items/weapons/assault_rifle_common.json new file mode 100644 index 0000000..48e24b9 --- /dev/null +++ b/GameServer/data/gso/items/weapons/assault_rifle_common.json @@ -0,0 +1,27 @@ +{ + "templates": { + "weapon": { + "id": "assault_rifle_common", + "name": "Assault Rifle", + "type": "weapon", + "rarity": "common", + "price": 2000, + "currency": "credits", + "description": "Rapid-fire assault rifle for sustained combat", + "texture": "assets/gso/textures/weapons/assaultrifle/assault_rifle_common.png", + "stats": { + "damage": 28, + "criticalChance": 0.07, + "attackRate": 8 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 5 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapons/assault_rifle_rare.json b/GameServer/data/gso/items/weapons/assault_rifle_rare.json new file mode 100644 index 0000000..0c9f3a5 --- /dev/null +++ b/GameServer/data/gso/items/weapons/assault_rifle_rare.json @@ -0,0 +1,27 @@ +{ + "templates": { + "weapon": { + "id": "assault_rifle_rare", + "name": "Heavy Assault Rifle", + "type": "weapon", + "rarity": "rare", + "price": 8000, + "currency": "credits", + "description": "Military-grade assault rifle with enhanced stopping power", + "texture": "assets/gso/textures/weapons/assaultrifle/assault_rifle_rare.png", + "stats": { + "damage": 45, + "criticalChance": 0.09, + "attackRate": 8 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 12 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapons/laser_bow_epic.json b/GameServer/data/gso/items/weapons/laser_bow_epic.json new file mode 100644 index 0000000..2675b5e --- /dev/null +++ b/GameServer/data/gso/items/weapons/laser_bow_epic.json @@ -0,0 +1,26 @@ +{ + "templates": { + "weapon": { + "id": "laser_bow_epic", + "name": "Laser Bow", + "type": "weapon", + "rarity": "epic", + "price": 200, + "currency": "gems", + "description": "Ancient-style energy bow channelling pure laser beams", + "texture": "assets/gso/textures/weapons/laserbow/laser_bow_epic.png", + "stats": { + "damage": 80, + "criticalChance": 0.2, + "attackRate": 2 + }, + "categories": [ + "shop" + ], + "requirements": { + "level": 20 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapons/pistol_marksman_rare.json b/GameServer/data/gso/items/weapons/pistol_marksman_rare.json new file mode 100644 index 0000000..210e167 --- /dev/null +++ b/GameServer/data/gso/items/weapons/pistol_marksman_rare.json @@ -0,0 +1,27 @@ +{ + "templates": { + "weapon": { + "id": "pistol_marksman_rare", + "name": "Marksman Pistol", + "type": "weapon", + "rarity": "rare", + "price": 5000, + "currency": "credits", + "description": "Precision-engineered for long-range accuracy", + "texture": "assets/gso/textures/weapons/pistol/pistol_marksman_rare.png", + "stats": { + "damage": 38, + "criticalChance": 0.12, + "attackRate": 3 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 8 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapons/pistol_standard_common.json b/GameServer/data/gso/items/weapons/pistol_standard_common.json new file mode 100644 index 0000000..061a06e --- /dev/null +++ b/GameServer/data/gso/items/weapons/pistol_standard_common.json @@ -0,0 +1,27 @@ +{ + "templates": { + "weapon": { + "id": "pistol_standard_common", + "name": "Standard Pistol", + "type": "weapon", + "rarity": "common", + "price": 800, + "currency": "credits", + "description": "A reliable sidearm for any situation", + "texture": "assets/gso/textures/weapons/pistol/pistol_standard_common.png", + "stats": { + "damage": 22, + "criticalChance": 0.06, + "attackRate": 4 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 2 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapons/plasma_cutter_rare.json b/GameServer/data/gso/items/weapons/plasma_cutter_rare.json new file mode 100644 index 0000000..a35d703 --- /dev/null +++ b/GameServer/data/gso/items/weapons/plasma_cutter_rare.json @@ -0,0 +1,27 @@ +{ + "templates": { + "weapon": { + "id": "plasma_cutter_rare", + "name": "Plasma Cutter", + "type": "weapon", + "rarity": "rare", + "price": 12000, + "currency": "credits", + "description": "Superheated plasma blade that melts through armour", + "texture": "assets/gso/textures/weapons/plasmacutter/plasma_cutter_rare.png", + "stats": { + "damage": 60, + "criticalChance": 0.1, + "attackRate": 2 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 15 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapons/sniper_common.json b/GameServer/data/gso/items/weapons/sniper_common.json new file mode 100644 index 0000000..4d2790b --- /dev/null +++ b/GameServer/data/gso/items/weapons/sniper_common.json @@ -0,0 +1,27 @@ +{ + "templates": { + "weapon": { + "id": "sniper_common", + "name": "Sniper Rifle", + "type": "weapon", + "rarity": "common", + "price": 3000, + "currency": "credits", + "description": "Long-range precision weapon for eliminating distant targets", + "texture": "assets/gso/textures/weapons/sniper/sniper_common.png", + "stats": { + "damage": 55, + "criticalChance": 0.15, + "attackRate": 1 + }, + "categories": [ + "shop", + "dungeon_loot" + ], + "requirements": { + "level": 6 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/items/weapons/starter_blaster.json b/GameServer/data/gso/items/weapons/starter_blaster.json new file mode 100644 index 0000000..693791d --- /dev/null +++ b/GameServer/data/gso/items/weapons/starter_blaster.json @@ -0,0 +1,27 @@ +{ + "templates": { + "weapon": { + "id": "starter_blaster", + "name": "Starter Blaster", + "type": "weapon", + "rarity": "common", + "price": 0, + "currency": "credits", + "description": "Standard-issue blaster for new recruits", + "texture": "assets/gso/textures/weapons/pistol/starter_blaster.png", + "stats": { + "damage": 18, + "criticalChance": 0.05, + "attackRate": 4 + }, + "categories": [ + "starter", + "dungeon_loot" + ], + "requirements": { + "level": 1 + }, + "stackable": false + } + } +} \ No newline at end of file diff --git a/GameServer/data/gso/quests/daily/d_craft_3.json b/GameServer/data/gso/quests/daily/d_craft_3.json new file mode 100644 index 0000000..7c664d6 --- /dev/null +++ b/GameServer/data/gso/quests/daily/d_craft_3.json @@ -0,0 +1,46 @@ +{ + "id": "d_craft_3", + "name": "Quick Craft", + "category": "daily", + "repeatable": true, + "timeWindow": { + "type": "daily", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 2, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_craft", + "type": "CRAFT_TOOLS", + "target": 3, + "params": { + "toolId": "any" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 300 + }, + { + "type": "item", + "items": [ + { + "itemId": "scrap_pack_small", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_daily_craft", + "summary": "Craft 3 items of any type." + } +} \ No newline at end of file diff --git a/GameServer/data/gso/quests/daily/d_customize_ship.json b/GameServer/data/gso/quests/daily/d_customize_ship.json new file mode 100644 index 0000000..7e8d380 --- /dev/null +++ b/GameServer/data/gso/quests/daily/d_customize_ship.json @@ -0,0 +1,44 @@ +{ + "id": "d_customize_ship", + "name": "Fresh Paint", + "category": "daily", + "repeatable": true, + "timeWindow": { + "type": "daily", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 1, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_ship", + "type": "CUSTOMIZE_SHIP", + "target": 1, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 200 + }, + { + "type": "item", + "items": [ + { + "itemId": "paint_kit_random", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_daily_ship", + "summary": "Customize your ship." + } +} diff --git a/GameServer/data/gso/quests/daily/d_decor_10.json b/GameServer/data/gso/quests/daily/d_decor_10.json new file mode 100644 index 0000000..93cb472 --- /dev/null +++ b/GameServer/data/gso/quests/daily/d_decor_10.json @@ -0,0 +1,46 @@ +{ + "id": "d_decor_10", + "name": "Interior Designer", + "category": "daily", + "repeatable": true, + "timeWindow": { + "type": "daily", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 2, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_decor", + "type": "PLACE_DECORATIONS", + "target": 10, + "params": { + "location": "ship_or_base" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 350 + }, + { + "type": "item", + "items": [ + { + "itemId": "deco_token", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_daily_decor", + "summary": "Place 10 decorations." + } +} diff --git a/GameServer/data/gso/quests/daily/d_dungeons_3.json b/GameServer/data/gso/quests/daily/d_dungeons_3.json new file mode 100644 index 0000000..b0e20ac --- /dev/null +++ b/GameServer/data/gso/quests/daily/d_dungeons_3.json @@ -0,0 +1,44 @@ +{ + "id": "d_dungeons_3", + "name": "Dungeon Hopping", + "category": "daily", + "repeatable": true, + "timeWindow": { + "type": "daily", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 3, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_dungeons", + "type": "COMPLETE_DUNGEON", + "target": 3, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 500 + }, + { + "type": "item", + "items": [ + { + "itemId": "health_kit_small", + "qty": 3 + } + ] + } + ], + "ui": { + "icon": "quest_daily_dungeon", + "summary": "Complete 3 dungeons." + } +} \ No newline at end of file diff --git a/GameServer/data/gso/quests/daily/d_explore_30.json b/GameServer/data/gso/quests/daily/d_explore_30.json new file mode 100644 index 0000000..42d1bc3 --- /dev/null +++ b/GameServer/data/gso/quests/daily/d_explore_30.json @@ -0,0 +1,35 @@ +{ + "id": "d_explore_30", + "name": "Daily Survey", + "category": "daily", + "repeatable": true, + "timeWindow": { + "type": "daily", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 1, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_explore", + "type": "TRAVEL_METERS_PLANET", + "target": 30, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 200 + } + ], + "ui": { + "icon": "quest_daily_explore", + "summary": "Explore 30 sectors." + } +} \ No newline at end of file diff --git a/GameServer/data/gso/quests/daily/d_kill_pirates_10.json b/GameServer/data/gso/quests/daily/d_kill_pirates_10.json new file mode 100644 index 0000000..8009f69 --- /dev/null +++ b/GameServer/data/gso/quests/daily/d_kill_pirates_10.json @@ -0,0 +1,46 @@ +{ + "id": "d_kill_pirates_10", + "name": "Pirate Bounty", + "category": "daily", + "repeatable": true, + "timeWindow": { + "type": "daily", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 2, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_kill", + "type": "DEFEAT_ENEMY", + "target": 10, + "params": { + "enemyId": "space_pirate" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 400 + }, + { + "type": "item", + "items": [ + { + "itemId": "iron_ore", + "qty": 10 + } + ] + } + ], + "ui": { + "icon": "quest_daily_combat", + "summary": "Defeat 10 space pirates." + } +} \ No newline at end of file diff --git a/GameServer/data/gso/quests/daily/d_login_5.json b/GameServer/data/gso/quests/daily/d_login_5.json new file mode 100644 index 0000000..13bd966 --- /dev/null +++ b/GameServer/data/gso/quests/daily/d_login_5.json @@ -0,0 +1,44 @@ +{ + "id": "d_login_5", + "name": "Frequent Flyer", + "category": "daily", + "repeatable": true, + "timeWindow": { + "type": "daily", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 1, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_login", + "type": "LOGIN", + "target": 1, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 100 + }, + { + "type": "item", + "items": [ + { + "itemId": "fuel_cell", + "qty": 2 + } + ] + } + ], + "ui": { + "icon": "quest_daily_login", + "summary": "Log in today." + } +} \ No newline at end of file diff --git a/GameServer/data/gso/quests/daily/d_smelt_5.json b/GameServer/data/gso/quests/daily/d_smelt_5.json new file mode 100644 index 0000000..7d2d568 --- /dev/null +++ b/GameServer/data/gso/quests/daily/d_smelt_5.json @@ -0,0 +1,46 @@ +{ + "id": "d_smelt_5", + "name": "Smelting Run", + "category": "daily", + "repeatable": true, + "timeWindow": { + "type": "daily", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 2, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_smelt", + "type": "CRAFT_TOOLS", + "target": 5, + "params": { + "toolId": "ingot_any" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 250 + }, + { + "type": "item", + "items": [ + { + "itemId": "bandage", + "qty": 5 + } + ] + } + ], + "ui": { + "icon": "quest_daily_smelt", + "summary": "Smelt 5 ingots." + } +} \ No newline at end of file diff --git a/GameServer/data/gso/quests/daily/d_travel_planet_1000.json b/GameServer/data/gso/quests/daily/d_travel_planet_1000.json new file mode 100644 index 0000000..191270f --- /dev/null +++ b/GameServer/data/gso/quests/daily/d_travel_planet_1000.json @@ -0,0 +1,37 @@ +{ + "id": "d_travel_planet_1000", + "name": "Planetary Errands", + "category": "daily", + "repeatable": true, + "timeWindow": { + "type": "daily", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 1, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_travel", + "type": "TRAVEL_METERS_PLANET", + "target": 1000, + "params": { + "planetId": "any" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 250 + } + ], + "ui": { + "icon": "quest_daily_walk", + "summary": "Travel 1,000 meters on any planet." + } +} diff --git a/GameServer/data/gso/quests/main_story/ms_ch1_001.json b/GameServer/data/gso/quests/main_story/ms_ch1_001.json new file mode 100644 index 0000000..d13290d --- /dev/null +++ b/GameServer/data/gso/quests/main_story/ms_ch1_001.json @@ -0,0 +1,78 @@ +{ + "id": "ms_ch1_001", + "name": "First Steps Into the Stars", + "category": "main_story", + "chapter": "1", + "repeatable": false, + "timeWindow": { + "type": "none", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 1, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_travel", + "type": "TRAVEL_METERS_PLANET", + "target": 500, + "params": { + "planetId": "terra-1" + }, + "progress": 0, + "complete": false + }, + { + "id": "obj_customize", + "type": "CUSTOMIZE_CHARACTER", + "target": 1, + "params": {}, + "progress": 0, + "complete": false + }, + { + "id": "obj_craft", + "type": "CRAFT_TOOLS", + "target": 2, + "params": { + "toolId": "basic_tool_any" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 500 + }, + { + "type": "money", + "amount": 10 + }, + { + "type": "item", + "items": [ + { + "itemId": "starter_crate", + "qty": 1 + }, + { + "itemId": "fuel_cell", + "qty": 5 + } + ] + }, + { + "type": "item", + "itemId": "wp_nebula_dawn", + "quantity": 1 + } + ], + "ui": { + "icon": "quest_star", + "summary": "Travel, gear up, and personalise your new life." + } +} \ No newline at end of file diff --git a/GameServer/data/gso/quests/main_story/ms_ch1_002.json b/GameServer/data/gso/quests/main_story/ms_ch1_002.json new file mode 100644 index 0000000..5b60296 --- /dev/null +++ b/GameServer/data/gso/quests/main_story/ms_ch1_002.json @@ -0,0 +1,65 @@ +{ + "id": "ms_ch1_002", + "name": "Into the Unknown", + "category": "main_story", + "chapter": "1", + "repeatable": false, + "timeWindow": { + "type": "none", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [ + "ms_ch1_001" + ], + "playerLevelMin": 2, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_dungeon", + "type": "COMPLETE_DUNGEON", + "target": 1, + "params": { + "dungeonId": "pirate_lair" + }, + "progress": 0, + "complete": false + }, + { + "id": "obj_credits", + "type": "EARN_CREDITS", + "target": 500, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 800 + }, + { + "type": "money", + "amount": 20 + }, + { + "type": "item", + "items": [ + { + "itemId": "pistol_standard_common", + "qty": 1 + }, + { + "itemId": "health_kit_small", + "qty": 5 + } + ] + } + ], + "ui": { + "icon": "quest_combat", + "summary": "Clear the pirate lair and earn your keep." + } +} \ No newline at end of file diff --git a/GameServer/data/gso/quests/main_story/ms_ch1_003.json b/GameServer/data/gso/quests/main_story/ms_ch1_003.json new file mode 100644 index 0000000..9eee795 --- /dev/null +++ b/GameServer/data/gso/quests/main_story/ms_ch1_003.json @@ -0,0 +1,63 @@ +{ + "id": "ms_ch1_003", + "name": "The Art of Craft", + "category": "main_story", + "chapter": "1", + "repeatable": false, + "timeWindow": { + "type": "none", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [ + "ms_ch1_002" + ], + "playerLevelMin": 3, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_smelt", + "type": "CRAFT_TOOLS", + "target": 5, + "params": { + "toolId": "ingot_any" + }, + "progress": 0, + "complete": false + }, + { + "id": "obj_alloy", + "type": "CRAFT_TOOLS", + "target": 1, + "params": { + "toolId": "steel_alloy" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 1000 + }, + { + "type": "money", + "amount": 25 + }, + { + "type": "item", + "items": [ + { + "itemId": "body_basic_common", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_craft", + "summary": "Master the basics of smelting and alloying." + } +} \ No newline at end of file diff --git a/GameServer/data/gso/quests/main_story/ms_ch2_001.json b/GameServer/data/gso/quests/main_story/ms_ch2_001.json new file mode 100644 index 0000000..bf62fa7 --- /dev/null +++ b/GameServer/data/gso/quests/main_story/ms_ch2_001.json @@ -0,0 +1,67 @@ +{ + "id": "ms_ch2_001", + "name": "Alien Incursion", + "category": "main_story", + "chapter": "2", + "repeatable": false, + "timeWindow": { + "type": "none", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [ + "ms_ch1_003" + ], + "playerLevelMin": 5, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_alien_dungeon", + "type": "COMPLETE_DUNGEON", + "target": 1, + "params": { + "dungeonId": "alien_ruins" + }, + "progress": 0, + "complete": false + }, + { + "id": "obj_kill_guardians", + "type": "DEFEAT_ENEMY", + "target": 10, + "params": { + "enemyId": "alien_guardian" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 2000 + }, + { + "type": "money", + "amount": 50 + }, + { + "type": "item", + "items": [ + { + "itemId": "assault_rifle_common", + "qty": 1 + }, + { + "itemId": "health_kit_large", + "qty": 3 + } + ] + } + ], + "ui": { + "icon": "quest_alien", + "summary": "Investigate the alien ruins and push back the incursion." + } +} \ No newline at end of file diff --git a/GameServer/data/gso/quests/main_story/ms_ch2_002.json b/GameServer/data/gso/quests/main_story/ms_ch2_002.json new file mode 100644 index 0000000..8019a88 --- /dev/null +++ b/GameServer/data/gso/quests/main_story/ms_ch2_002.json @@ -0,0 +1,63 @@ +{ + "id": "ms_ch2_002", + "name": "Power Core", + "category": "main_story", + "chapter": "2", + "repeatable": false, + "timeWindow": { + "type": "none", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [ + "ms_ch2_001" + ], + "playerLevelMin": 7, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_circuits", + "type": "CRAFT_TOOLS", + "target": 3, + "params": { + "toolId": "circuit_any" + }, + "progress": 0, + "complete": false + }, + { + "id": "obj_ai_vault", + "type": "COMPLETE_DUNGEON", + "target": 1, + "params": { + "dungeonId": "corrupted_vault" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 3500 + }, + { + "type": "money", + "amount": 80 + }, + { + "type": "item", + "items": [ + { + "itemId": "body_plated_rare", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_tech", + "summary": "Craft advanced circuits and storm the corrupted AI vault." + } +} \ No newline at end of file diff --git a/GameServer/data/gso/quests/main_story/ms_ch2_003.json b/GameServer/data/gso/quests/main_story/ms_ch2_003.json new file mode 100644 index 0000000..fa5f0c5 --- /dev/null +++ b/GameServer/data/gso/quests/main_story/ms_ch2_003.json @@ -0,0 +1,63 @@ +{ + "id": "ms_ch2_003", + "name": "The Void Calls", + "category": "main_story", + "chapter": "2", + "repeatable": false, + "timeWindow": { + "type": "none", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [ + "ms_ch2_002" + ], + "playerLevelMin": 10, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_void", + "type": "COMPLETE_DUNGEON", + "target": 1, + "params": { + "dungeonId": "nebula_anomaly" + }, + "progress": 0, + "complete": false + }, + { + "id": "obj_void_crystal", + "type": "GATHER_RESOURCE", + "target": 3, + "params": { + "itemId": "void_crystal" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 6000 + }, + { + "type": "money", + "amount": 150 + }, + { + "type": "item", + "items": [ + { + "itemId": "laser_bow_epic", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_void", + "summary": "Enter the nebula anomaly and harvest void crystals." + } +} \ No newline at end of file diff --git a/GameServer/data/gso/quests/main_story/ms_ch3_001.json b/GameServer/data/gso/quests/main_story/ms_ch3_001.json new file mode 100644 index 0000000..530f948 --- /dev/null +++ b/GameServer/data/gso/quests/main_story/ms_ch3_001.json @@ -0,0 +1,63 @@ +{ + "id": "ms_ch3_001", + "name": "Dreadnought Rising", + "category": "main_story", + "chapter": "3", + "repeatable": false, + "timeWindow": { + "type": "none", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [ + "ms_ch2_003" + ], + "playerLevelMin": 15, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_hull", + "type": "CRAFT_TOOLS", + "target": 1, + "params": { + "toolId": "heavy_hull" + }, + "progress": 0, + "complete": false + }, + { + "id": "obj_void_rift", + "type": "COMPLETE_DUNGEON", + "target": 1, + "params": { + "dungeonId": "void_rift" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 12000 + }, + { + "type": "money", + "amount": 300 + }, + { + "type": "item", + "items": [ + { + "itemId": "phantom_epic", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_dread", + "summary": "Build a hull worthy of the void and conquer the rift." + } +} \ No newline at end of file diff --git a/GameServer/data/gso/quests/main_story/ms_ch3_002.json b/GameServer/data/gso/quests/main_story/ms_ch3_002.json new file mode 100644 index 0000000..c25aa01 --- /dev/null +++ b/GameServer/data/gso/quests/main_story/ms_ch3_002.json @@ -0,0 +1,72 @@ +{ + "id": "ms_ch3_002", + "name": "Galaxy Strike", + "category": "main_story", + "chapter": "3", + "repeatable": false, + "timeWindow": { + "type": "none", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [ + "ms_ch3_001" + ], + "playerLevelMin": 20, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_dread", + "type": "CRAFT_TOOLS", + "target": 1, + "params": { + "toolId": "dreadframe_hull" + }, + "progress": 0, + "complete": false + }, + { + "id": "obj_final", + "type": "COMPLETE_DUNGEON", + "target": 3, + "params": { + "dungeonId": "void_rift" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 25000 + }, + { + "type": "money", + "amount": 500 + }, + { + "type": "item", + "items": [ + { + "itemId": "dreadnought_legendary", + "qty": 1 + }, + { + "itemId": "xp_booster", + "qty": 3 + } + ] + }, + { + "type": "item", + "itemId": "room_throne", + "quantity": 1 + } + ], + "ui": { + "icon": "quest_final", + "summary": "Forge the ultimate hull and strike the heart of the void." + } +} \ No newline at end of file diff --git a/GameServer/data/gso/quests/manifest.json b/GameServer/data/gso/quests/manifest.json new file mode 100644 index 0000000..5ba250d --- /dev/null +++ b/GameServer/data/gso/quests/manifest.json @@ -0,0 +1,12 @@ +{ + "counts_by_category": { + "main_story": 8, + "daily": 6, + "weekly": 5, + "monthly": 4 + }, + "categoryIndexes": "data/gso/category/quests/", + "notes": [ + "One JSON per quest. Category index files in data/gso/category/quests/.json" + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/quests/monthly/m_craft_100.json b/GameServer/data/gso/quests/monthly/m_craft_100.json new file mode 100644 index 0000000..8b20746 --- /dev/null +++ b/GameServer/data/gso/quests/monthly/m_craft_100.json @@ -0,0 +1,50 @@ +{ + "id": "m_craft_100", + "name": "Master Craftsman", + "category": "monthly", + "repeatable": true, + "timeWindow": { + "type": "monthly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 5, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_craft", + "type": "CRAFT_TOOLS", + "target": 100, + "params": { + "toolId": "any" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 12000 + }, + { + "type": "money", + "amount": 200 + }, + { + "type": "item", + "items": [ + { + "itemId": "body_plated_rare", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_monthly_craft", + "summary": "Craft 100 items this month." + } +} \ No newline at end of file diff --git a/GameServer/data/gso/quests/monthly/m_decor_100.json b/GameServer/data/gso/quests/monthly/m_decor_100.json new file mode 100644 index 0000000..805e706 --- /dev/null +++ b/GameServer/data/gso/quests/monthly/m_decor_100.json @@ -0,0 +1,46 @@ +{ + "id": "m_decor_100", + "name": "Base Beautification", + "category": "monthly", + "repeatable": true, + "timeWindow": { + "type": "monthly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 3, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_decor", + "type": "PLACE_DECORATIONS", + "target": 100, + "params": { + "location": "base" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 8000 + }, + { + "type": "item", + "items": [ + { + "itemId": "deco_bundle_large", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_monthly_decor", + "summary": "Place 100 decorations in your base." + } +} diff --git a/GameServer/data/gso/quests/monthly/m_dungeons_50.json b/GameServer/data/gso/quests/monthly/m_dungeons_50.json new file mode 100644 index 0000000..52df2cc --- /dev/null +++ b/GameServer/data/gso/quests/monthly/m_dungeons_50.json @@ -0,0 +1,52 @@ +{ + "id": "m_dungeons_50", + "name": "Legendary Dungeon Diver", + "category": "monthly", + "repeatable": true, + "timeWindow": { + "type": "monthly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 6, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_dungeons", + "type": "COMPLETE_DUNGEON", + "target": 50, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 15000 + }, + { + "type": "money", + "amount": 300 + }, + { + "type": "item", + "items": [ + { + "itemId": "xp_booster", + "qty": 3 + }, + { + "itemId": "credit_multiplier", + "qty": 3 + } + ] + } + ], + "ui": { + "icon": "quest_monthly_dungeon", + "summary": "Complete 50 dungeons this month." + } +} \ No newline at end of file diff --git a/GameServer/data/gso/quests/monthly/m_guild_fight_10.json b/GameServer/data/gso/quests/monthly/m_guild_fight_10.json new file mode 100644 index 0000000..4cf189c --- /dev/null +++ b/GameServer/data/gso/quests/monthly/m_guild_fight_10.json @@ -0,0 +1,48 @@ +{ + "id": "m_guild_fight_10", + "name": "Guild Warpath", + "category": "monthly", + "repeatable": true, + "timeWindow": { + "type": "monthly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 8, + "guildRequired": true + }, + "objectives": [ + { + "id": "obj_guild_fight", + "type": "FIGHT_GUILD", + "target": 10, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 25000 + }, + { + "type": "money", + "amount": 100 + }, + { + "type": "item", + "items": [ + { + "itemId": "guild_trophy", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_monthly_guild", + "summary": "Win 10 guild battles this month." + } +} diff --git a/GameServer/data/gso/quests/monthly/m_hire_5.json b/GameServer/data/gso/quests/monthly/m_hire_5.json new file mode 100644 index 0000000..b7dfc68 --- /dev/null +++ b/GameServer/data/gso/quests/monthly/m_hire_5.json @@ -0,0 +1,50 @@ +{ + "id": "m_hire_5", + "name": "Private Army", + "category": "monthly", + "repeatable": true, + "timeWindow": { + "type": "monthly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 7, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_hire", + "type": "HIRE_MERCENARIES", + "target": 5, + "params": { + "fleetTier": "any" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 15000 + }, + { + "type": "money", + "amount": 75 + }, + { + "type": "item", + "items": [ + { + "itemId": "merc_fleet_skin", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_monthly_merc", + "summary": "Hire 5 mercenary fleets this month." + } +} diff --git a/GameServer/data/gso/quests/monthly/m_lightyears_100.json b/GameServer/data/gso/quests/monthly/m_lightyears_100.json new file mode 100644 index 0000000..fb146e6 --- /dev/null +++ b/GameServer/data/gso/quests/monthly/m_lightyears_100.json @@ -0,0 +1,44 @@ +{ + "id": "m_lightyears_100", + "name": "Long Haul", + "category": "monthly", + "repeatable": true, + "timeWindow": { + "type": "monthly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 5, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_ly", + "type": "TRAVEL_LIGHTYEARS", + "target": 100, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 5000 + }, + { + "type": "item", + "items": [ + { + "itemId": "warp_core_fragment", + "qty": 2 + } + ] + } + ], + "ui": { + "icon": "quest_monthly", + "summary": "Travel 100 light years this month." + } +} diff --git a/GameServer/data/gso/quests/monthly/m_raids_20.json b/GameServer/data/gso/quests/monthly/m_raids_20.json new file mode 100644 index 0000000..4226622 --- /dev/null +++ b/GameServer/data/gso/quests/monthly/m_raids_20.json @@ -0,0 +1,48 @@ +{ + "id": "m_raids_20", + "name": "Monthly Raider", + "category": "monthly", + "repeatable": true, + "timeWindow": { + "type": "monthly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 5, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_raids", + "type": "FIGHT_RAIDS", + "target": 20, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 8000 + }, + { + "type": "money", + "amount": 150 + }, + { + "type": "item", + "items": [ + { + "itemId": "plasma_cutter_rare", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_monthly", + "summary": "Complete 20 raids this month." + } +} \ No newline at end of file diff --git a/GameServer/data/gso/quests/monthly/m_void_run.json b/GameServer/data/gso/quests/monthly/m_void_run.json new file mode 100644 index 0000000..3a08bdf --- /dev/null +++ b/GameServer/data/gso/quests/monthly/m_void_run.json @@ -0,0 +1,50 @@ +{ + "id": "m_void_run", + "name": "Void Conqueror", + "category": "monthly", + "repeatable": true, + "timeWindow": { + "type": "monthly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 12, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_void", + "type": "COMPLETE_DUNGEON", + "target": 5, + "params": { + "dungeonId": "void_rift" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 25000 + }, + { + "type": "money", + "amount": 500 + }, + { + "type": "item", + "items": [ + { + "itemId": "dreadnought_legendary", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_monthly_void", + "summary": "Conquer the void rift 5 times." + } +} \ No newline at end of file diff --git a/GameServer/data/gso/quests/weekly/w_craft_20.json b/GameServer/data/gso/quests/weekly/w_craft_20.json new file mode 100644 index 0000000..b912850 --- /dev/null +++ b/GameServer/data/gso/quests/weekly/w_craft_20.json @@ -0,0 +1,55 @@ +{ + "id": "w_craft_20", + "name": "Crafting Spree", + "category": "weekly", + "repeatable": true, + "timeWindow": { + "type": "weekly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 3, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_craft", + "type": "CRAFT_TOOLS", + "target": 20, + "params": { + "toolId": "any" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 2000 + }, + { + "type": "money", + "amount": 40 + }, + { + "type": "item", + "items": [ + { + "itemId": "xp_booster", + "qty": 1 + } + ] + }, + { + "type": "item", + "itemId": "wp_golden_empire", + "quantity": 1 + } + ], + "ui": { + "icon": "quest_weekly_craft", + "summary": "Craft 20 items this week." + } +} \ No newline at end of file diff --git a/GameServer/data/gso/quests/weekly/w_dungeons_10.json b/GameServer/data/gso/quests/weekly/w_dungeons_10.json new file mode 100644 index 0000000..d939ad4 --- /dev/null +++ b/GameServer/data/gso/quests/weekly/w_dungeons_10.json @@ -0,0 +1,48 @@ +{ + "id": "w_dungeons_10", + "name": "Dungeon Crawler", + "category": "weekly", + "repeatable": true, + "timeWindow": { + "type": "weekly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 4, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_dungeons", + "type": "COMPLETE_DUNGEON", + "target": 10, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 3000 + }, + { + "type": "money", + "amount": 60 + }, + { + "type": "item", + "items": [ + { + "itemId": "credit_multiplier", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_weekly_dungeon", + "summary": "Complete 10 dungeons this week." + } +} \ No newline at end of file diff --git a/GameServer/data/gso/quests/weekly/w_explore_planets_3.json b/GameServer/data/gso/quests/weekly/w_explore_planets_3.json new file mode 100644 index 0000000..00ab846 --- /dev/null +++ b/GameServer/data/gso/quests/weekly/w_explore_planets_3.json @@ -0,0 +1,48 @@ +{ + "id": "w_explore_planets_3", + "name": "New Horizons", + "category": "weekly", + "repeatable": true, + "timeWindow": { + "type": "weekly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 3, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_planets", + "type": "EXPLORE_NEW_PLANETS", + "target": 3, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 1800 + }, + { + "type": "money", + "amount": 20 + }, + { + "type": "item", + "items": [ + { + "itemId": "nav_chip_mk1", + "qty": 1 + } + ] + } + ], + "ui": { + "icon": "quest_weekly_planets", + "summary": "Explore 3 new planets this week." + } +} diff --git a/GameServer/data/gso/quests/weekly/w_friends_5.json b/GameServer/data/gso/quests/weekly/w_friends_5.json new file mode 100644 index 0000000..2cd8c7e --- /dev/null +++ b/GameServer/data/gso/quests/weekly/w_friends_5.json @@ -0,0 +1,39 @@ +{ + "id": "w_friends_5", + "name": "Build Your Network", + "category": "weekly", + "repeatable": true, + "timeWindow": { + "type": "weekly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 2, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_friends", + "type": "ADD_FRIENDS", + "target": 5, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 1500 + }, + { + "type": "money", + "amount": 15 + } + ], + "ui": { + "icon": "quest_weekly_social", + "summary": "Add 5 friends." + } +} diff --git a/GameServer/data/gso/quests/weekly/w_gather_50.json b/GameServer/data/gso/quests/weekly/w_gather_50.json new file mode 100644 index 0000000..5d5a733 --- /dev/null +++ b/GameServer/data/gso/quests/weekly/w_gather_50.json @@ -0,0 +1,50 @@ +{ + "id": "w_gather_50", + "name": "Resource Rush", + "category": "weekly", + "repeatable": true, + "timeWindow": { + "type": "weekly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 2, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_gather", + "type": "GATHER_RESOURCE", + "target": 50, + "params": { + "itemId": "any_ore" + }, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 1500 + }, + { + "type": "money", + "amount": 30 + }, + { + "type": "item", + "items": [ + { + "itemId": "scrap_pack_small", + "qty": 5 + } + ] + } + ], + "ui": { + "icon": "quest_weekly_gather", + "summary": "Gather 50 ores this week." + } +} \ No newline at end of file diff --git a/GameServer/data/gso/quests/weekly/w_guild_fight_3.json b/GameServer/data/gso/quests/weekly/w_guild_fight_3.json new file mode 100644 index 0000000..17d2807 --- /dev/null +++ b/GameServer/data/gso/quests/weekly/w_guild_fight_3.json @@ -0,0 +1,44 @@ +{ + "id": "w_guild_fight_3", + "name": "Guild Skirmishes", + "category": "weekly", + "repeatable": true, + "timeWindow": { + "type": "weekly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 6, + "guildRequired": true + }, + "objectives": [ + { + "id": "obj_guild_fight", + "type": "FIGHT_GUILD", + "target": 3, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 3500 + }, + { + "type": "item", + "items": [ + { + "itemId": "guild_rep", + "qty": 50 + } + ] + } + ], + "ui": { + "icon": "quest_weekly_guild", + "summary": "Fight 3 other guilds this week." + } +} diff --git a/GameServer/data/gso/quests/weekly/w_lightyears_25.json b/GameServer/data/gso/quests/weekly/w_lightyears_25.json new file mode 100644 index 0000000..d4ac7fd --- /dev/null +++ b/GameServer/data/gso/quests/weekly/w_lightyears_25.json @@ -0,0 +1,44 @@ +{ + "id": "w_lightyears_25", + "name": "Interstellar Route", + "category": "weekly", + "repeatable": true, + "timeWindow": { + "type": "weekly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 4, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_ly", + "type": "TRAVEL_LIGHTYEARS", + "target": 25, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 2200 + }, + { + "type": "item", + "items": [ + { + "itemId": "fuel_cell", + "qty": 10 + } + ] + } + ], + "ui": { + "icon": "quest_weekly_space", + "summary": "Travel 25 light years." + } +} diff --git a/GameServer/data/gso/quests/weekly/w_raids_3.json b/GameServer/data/gso/quests/weekly/w_raids_3.json new file mode 100644 index 0000000..bdb0fe6 --- /dev/null +++ b/GameServer/data/gso/quests/weekly/w_raids_3.json @@ -0,0 +1,48 @@ +{ + "id": "w_raids_3", + "name": "Raid Week", + "category": "weekly", + "repeatable": true, + "timeWindow": { + "type": "weekly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 3, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_raids", + "type": "FIGHT_RAIDS", + "target": 3, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 1200 + }, + { + "type": "money", + "amount": 25 + }, + { + "type": "item", + "items": [ + { + "itemId": "energy_boost_large", + "qty": 2 + } + ] + } + ], + "ui": { + "icon": "quest_weekly", + "summary": "Complete 3 raids." + } +} \ No newline at end of file diff --git a/GameServer/data/gso/quests/weekly/w_survive_hardcore_7.json b/GameServer/data/gso/quests/weekly/w_survive_hardcore_7.json new file mode 100644 index 0000000..5fdcae6 --- /dev/null +++ b/GameServer/data/gso/quests/weekly/w_survive_hardcore_7.json @@ -0,0 +1,39 @@ +{ + "id": "w_survive_hardcore_7", + "name": "Hardcore Survivor", + "category": "weekly", + "repeatable": true, + "timeWindow": { + "type": "weekly", + "resetAtUtc": "00:00" + }, + "prerequisites": { + "questsCompleted": [], + "playerLevelMin": 5, + "guildRequired": false + }, + "objectives": [ + { + "id": "obj_hardcore", + "type": "SURVIVE_DUNGEON_HARDCORE", + "target": 7, + "params": {}, + "progress": 0, + "complete": false + } + ], + "rewards": [ + { + "type": "coin", + "amount": 4000 + }, + { + "type": "money", + "amount": 80 + } + ], + "ui": { + "icon": "quest_weekly_hardcore", + "summary": "Survive 7 hardcore dungeon encounters." + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/.gitkeep b/GameServer/data/gso/recipes/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/GameServer/data/gso/recipes/alloys/ablative_plating.json b/GameServer/data/gso/recipes/alloys/ablative_plating.json new file mode 100644 index 0000000..2658432 --- /dev/null +++ b/GameServer/data/gso/recipes/alloys/ablative_plating.json @@ -0,0 +1,18 @@ +{ + "path": "alloys/ablative_plating", + "recipe": { + "inputs": { + "ceramic_composite": 1, + "carbon_ingot": 1 + }, + "output": { + "ablative_plating": 1 + }, + "alloy_time_seconds": 40 + }, + "craft": { + "type": "alloys", + "subCategory": "misc", + "id": "alloys:misc:alloys_ablative_plating" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/alloys/aerospace_aluminum.json b/GameServer/data/gso/recipes/alloys/aerospace_aluminum.json new file mode 100644 index 0000000..883ac76 --- /dev/null +++ b/GameServer/data/gso/recipes/alloys/aerospace_aluminum.json @@ -0,0 +1,18 @@ +{ + "path": "alloys/aerospace_aluminum", + "recipe": { + "inputs": { + "aluminum_ingot": 2, + "magnesium_ingot": 1 + }, + "output": { + "aerospace_aluminum": 1 + }, + "alloy_time_seconds": 25 + }, + "craft": { + "type": "alloys", + "subCategory": "misc", + "id": "alloys:misc:alloys_aerospace_aluminum" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/alloys/ballistic_alloy.json b/GameServer/data/gso/recipes/alloys/ballistic_alloy.json new file mode 100644 index 0000000..7867299 --- /dev/null +++ b/GameServer/data/gso/recipes/alloys/ballistic_alloy.json @@ -0,0 +1,18 @@ +{ + "path": "alloys/ballistic_alloy", + "recipe": { + "inputs": { + "tungsten_ingot": 1, + "steel_alloy": 1 + }, + "output": { + "ballistic_alloy": 1 + }, + "alloy_time_seconds": 45 + }, + "craft": { + "type": "alloys", + "subCategory": "chemical", + "id": "alloys:chemical:alloys_ballistic_alloy" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/alloys/carbon_titanium_composite.json b/GameServer/data/gso/recipes/alloys/carbon_titanium_composite.json new file mode 100644 index 0000000..6d0068a --- /dev/null +++ b/GameServer/data/gso/recipes/alloys/carbon_titanium_composite.json @@ -0,0 +1,18 @@ +{ + "path": "alloys/carbon_titanium_composite", + "recipe": { + "inputs": { + "titanium_alloy": 2, + "carbon_ingot": 3 + }, + "output": { + "carbon_titanium_composite": 1 + }, + "alloy_time_seconds": 60 + }, + "craft": { + "type": "alloys", + "subCategory": "chemical", + "id": "alloys:chemical:alloys_carbon_titanium_composite" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/alloys/chromium_steel.json b/GameServer/data/gso/recipes/alloys/chromium_steel.json new file mode 100644 index 0000000..a859067 --- /dev/null +++ b/GameServer/data/gso/recipes/alloys/chromium_steel.json @@ -0,0 +1,18 @@ +{ + "path": "alloys/chromium_steel", + "recipe": { + "inputs": { + "steel_alloy": 1, + "chromium_ingot": 1 + }, + "output": { + "chromium_steel": 1 + }, + "alloy_time_seconds": 30 + }, + "craft": { + "type": "alloys", + "subCategory": "misc", + "id": "alloys:misc:alloys_chromium_steel" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/alloys/chrono_alloy.json b/GameServer/data/gso/recipes/alloys/chrono_alloy.json new file mode 100644 index 0000000..a400c0f --- /dev/null +++ b/GameServer/data/gso/recipes/alloys/chrono_alloy.json @@ -0,0 +1,19 @@ +{ + "path": "alloys/chrono_alloy", + "recipe": { + "inputs": { + "phase_alloy": 1, + "chronite_ore": 2, + "titanium_alloy": 1 + }, + "output": { + "chrono_alloy": 1 + }, + "alloy_time_seconds": 150 + }, + "craft": { + "type": "alloys", + "subCategory": "misc", + "id": "alloys:misc:alloys_chrono_alloy" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/alloys/dimensional_alloy.json b/GameServer/data/gso/recipes/alloys/dimensional_alloy.json new file mode 100644 index 0000000..f3335e2 --- /dev/null +++ b/GameServer/data/gso/recipes/alloys/dimensional_alloy.json @@ -0,0 +1,19 @@ +{ + "path": "alloys/dimensional_alloy", + "recipe": { + "inputs": { + "neutronium_composite": 1, + "chrono_alloy": 1, + "void_crystal": 2 + }, + "output": { + "dimensional_alloy": 1 + }, + "alloy_time_seconds": 360 + }, + "craft": { + "type": "alloys", + "subCategory": "misc", + "id": "alloys:misc:alloys_dimensional_alloy" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/alloys/flux_core_alloy.json b/GameServer/data/gso/recipes/alloys/flux_core_alloy.json new file mode 100644 index 0000000..288e942 --- /dev/null +++ b/GameServer/data/gso/recipes/alloys/flux_core_alloy.json @@ -0,0 +1,19 @@ +{ + "path": "alloys/flux_core_alloy", + "recipe": { + "inputs": { + "steel_alloy": 2, + "flux_crystal": 1, + "chromium_ingot": 1 + }, + "output": { + "flux_core_alloy": 1 + }, + "alloy_time_seconds": 50 + }, + "craft": { + "type": "alloys", + "subCategory": "misc", + "id": "alloys:misc:alloys_flux_core_alloy" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/alloys/fusion_rated_alloy.json b/GameServer/data/gso/recipes/alloys/fusion_rated_alloy.json new file mode 100644 index 0000000..e8c666c --- /dev/null +++ b/GameServer/data/gso/recipes/alloys/fusion_rated_alloy.json @@ -0,0 +1,18 @@ +{ + "path": "alloys/fusion_rated_alloy", + "recipe": { + "inputs": { + "titanium_alloy": 1, + "radiation_shield_alloy": 1 + }, + "output": { + "fusion_rated_alloy": 1 + }, + "alloy_time_seconds": 70 + }, + "craft": { + "type": "alloys", + "subCategory": "chemical", + "id": "alloys:chemical:alloys_fusion_rated_alloy" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/alloys/neutronium_composite.json b/GameServer/data/gso/recipes/alloys/neutronium_composite.json new file mode 100644 index 0000000..d344a3c --- /dev/null +++ b/GameServer/data/gso/recipes/alloys/neutronium_composite.json @@ -0,0 +1,19 @@ +{ + "path": "alloys/neutronium_composite", + "recipe": { + "inputs": { + "neutronium_ingot": 1, + "carbon_titanium_composite": 1, + "voidsteel": 1 + }, + "output": { + "neutronium_composite": 1 + }, + "alloy_time_seconds": 240 + }, + "craft": { + "type": "alloys", + "subCategory": "misc", + "id": "alloys:misc:alloys_neutronium_composite" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/alloys/phase_alloy.json b/GameServer/data/gso/recipes/alloys/phase_alloy.json new file mode 100644 index 0000000..27ec9a7 --- /dev/null +++ b/GameServer/data/gso/recipes/alloys/phase_alloy.json @@ -0,0 +1,18 @@ +{ + "path": "alloys/phase_alloy", + "recipe": { + "inputs": { + "titanium_alloy": 2, + "phase_crystal": 1 + }, + "output": { + "phase_alloy": 1 + }, + "alloy_time_seconds": 80 + }, + "craft": { + "type": "alloys", + "subCategory": "misc", + "id": "alloys:misc:alloys_phase_alloy" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/alloys/reactive_armor_alloy.json b/GameServer/data/gso/recipes/alloys/reactive_armor_alloy.json new file mode 100644 index 0000000..43bd60d --- /dev/null +++ b/GameServer/data/gso/recipes/alloys/reactive_armor_alloy.json @@ -0,0 +1,18 @@ +{ + "path": "alloys/reactive_armor_alloy", + "recipe": { + "inputs": { + "ballistic_alloy": 1, + "explosive_compound": 1 + }, + "output": { + "reactive_armor_alloy": 1 + }, + "alloy_time_seconds": 60 + }, + "craft": { + "type": "alloys", + "subCategory": "chemical", + "id": "alloys:chemical:alloys_reactive_armor_alloy" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/alloys/reinforced_steel.json b/GameServer/data/gso/recipes/alloys/reinforced_steel.json new file mode 100644 index 0000000..38c5026 --- /dev/null +++ b/GameServer/data/gso/recipes/alloys/reinforced_steel.json @@ -0,0 +1,18 @@ +{ + "path": "alloys/reinforced_steel", + "recipe": { + "inputs": { + "steel_alloy": 2, + "titanium_ingot": 1 + }, + "output": { + "reinforced_steel": 1 + }, + "alloy_time_seconds": 35 + }, + "craft": { + "type": "alloys", + "subCategory": "chemical", + "id": "alloys:chemical:alloys_reinforced_steel" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/alloys/steel_alloy.json b/GameServer/data/gso/recipes/alloys/steel_alloy.json new file mode 100644 index 0000000..66ca448 --- /dev/null +++ b/GameServer/data/gso/recipes/alloys/steel_alloy.json @@ -0,0 +1,18 @@ +{ + "path": "alloys/steel_alloy", + "recipe": { + "inputs": { + "iron_ingot": 2, + "carbon_ingot": 1 + }, + "output": { + "steel_alloy": 1 + }, + "alloy_time_seconds": 20 + }, + "craft": { + "type": "alloys", + "subCategory": "chemical", + "id": "alloys:chemical:alloys_steel_alloy" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/alloys/superconductive_alloy.json b/GameServer/data/gso/recipes/alloys/superconductive_alloy.json new file mode 100644 index 0000000..8107699 --- /dev/null +++ b/GameServer/data/gso/recipes/alloys/superconductive_alloy.json @@ -0,0 +1,19 @@ +{ + "path": "alloys/superconductive_alloy", + "recipe": { + "inputs": { + "copper_ingot": 1, + "silver_ingot": 1, + "flux_crystal": 1 + }, + "output": { + "superconductive_alloy": 1 + }, + "alloy_time_seconds": 55 + }, + "craft": { + "type": "alloys", + "subCategory": "chemical", + "id": "alloys:chemical:alloys_superconductive_alloy" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/alloys/titanium_alloy.json b/GameServer/data/gso/recipes/alloys/titanium_alloy.json new file mode 100644 index 0000000..cd0bcf2 --- /dev/null +++ b/GameServer/data/gso/recipes/alloys/titanium_alloy.json @@ -0,0 +1,18 @@ +{ + "path": "alloys/titanium_alloy", + "recipe": { + "inputs": { + "titanium_ingot": 2, + "carbon_ingot": 1 + }, + "output": { + "titanium_alloy": 1 + }, + "alloy_time_seconds": 30 + }, + "craft": { + "type": "alloys", + "subCategory": "chemical", + "id": "alloys:chemical:alloys_titanium_alloy" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/alloys/tungsten_steel.json b/GameServer/data/gso/recipes/alloys/tungsten_steel.json new file mode 100644 index 0000000..ac03212 --- /dev/null +++ b/GameServer/data/gso/recipes/alloys/tungsten_steel.json @@ -0,0 +1,18 @@ +{ + "path": "alloys/tungsten_steel", + "recipe": { + "inputs": { + "steel_alloy": 1, + "tungsten_ingot": 1 + }, + "output": { + "tungsten_steel": 1 + }, + "alloy_time_seconds": 40 + }, + "craft": { + "type": "alloys", + "subCategory": "misc", + "id": "alloys:misc:alloys_tungsten_steel" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/alloys/voidsteel.json b/GameServer/data/gso/recipes/alloys/voidsteel.json new file mode 100644 index 0000000..707d525 --- /dev/null +++ b/GameServer/data/gso/recipes/alloys/voidsteel.json @@ -0,0 +1,19 @@ +{ + "path": "alloys/voidsteel", + "recipe": { + "inputs": { + "darksteel_ingot": 2, + "void_crystal": 1, + "steel_alloy": 1 + }, + "output": { + "voidsteel": 1 + }, + "alloy_time_seconds": 90 + }, + "craft": { + "type": "alloys", + "subCategory": "misc", + "id": "alloys:misc:alloys_voidsteel" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/armours/body_basic.json b/GameServer/data/gso/recipes/armours/body_basic.json new file mode 100644 index 0000000..2847133 --- /dev/null +++ b/GameServer/data/gso/recipes/armours/body_basic.json @@ -0,0 +1,18 @@ +{ + "path": "armours/body_basic", + "recipe": { + "inputs": { + "steel_alloy": 4, + "bandage": 2 + }, + "output": { + "body_basic_common": 1 + }, + "craft_time_seconds": 60 + }, + "craft": { + "type": "armours", + "subCategory": "body", + "id": "armours:body:armours_body_basic" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/armours/body_plated.json b/GameServer/data/gso/recipes/armours/body_plated.json new file mode 100644 index 0000000..bb957a5 --- /dev/null +++ b/GameServer/data/gso/recipes/armours/body_plated.json @@ -0,0 +1,18 @@ +{ + "path": "armours/body_plated", + "recipe": { + "inputs": { + "carbon_titanium_composite": 4, + "shield_control_circuit": 1 + }, + "output": { + "body_plated_rare": 1 + }, + "craft_time_seconds": 180 + }, + "craft": { + "type": "armours", + "subCategory": "body", + "id": "armours:body:armours_body_plated" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/armours/boots_basic.json b/GameServer/data/gso/recipes/armours/boots_basic.json new file mode 100644 index 0000000..9edd980 --- /dev/null +++ b/GameServer/data/gso/recipes/armours/boots_basic.json @@ -0,0 +1,18 @@ +{ + "path": "armours/boots_basic", + "recipe": { + "inputs": { + "steel_alloy": 2, + "iron_ingot": 1 + }, + "output": { + "boots_basic_common": 1 + }, + "craft_time_seconds": 30 + }, + "craft": { + "type": "armours", + "subCategory": "boots", + "id": "armours:boots:armours_boots_basic" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/armours/gloves_basic.json b/GameServer/data/gso/recipes/armours/gloves_basic.json new file mode 100644 index 0000000..fd09e48 --- /dev/null +++ b/GameServer/data/gso/recipes/armours/gloves_basic.json @@ -0,0 +1,18 @@ +{ + "path": "armours/gloves_basic", + "recipe": { + "inputs": { + "iron_ingot": 2, + "bandage": 1 + }, + "output": { + "gloves_basic_common": 1 + }, + "craft_time_seconds": 30 + }, + "craft": { + "type": "armours", + "subCategory": "hands", + "id": "armours:hands:armours_gloves_basic" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/armours/helmet_basic.json b/GameServer/data/gso/recipes/armours/helmet_basic.json new file mode 100644 index 0000000..8ea4197 --- /dev/null +++ b/GameServer/data/gso/recipes/armours/helmet_basic.json @@ -0,0 +1,18 @@ +{ + "path": "armours/helmet_basic", + "recipe": { + "inputs": { + "steel_alloy": 2, + "basic_circuit": 1 + }, + "output": { + "helmet_basic_common": 1 + }, + "craft_time_seconds": 45 + }, + "craft": { + "type": "armours", + "subCategory": "helmet", + "id": "armours:helmet:armours_helmet_basic" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/armours/pants_basic.json b/GameServer/data/gso/recipes/armours/pants_basic.json new file mode 100644 index 0000000..0cb2262 --- /dev/null +++ b/GameServer/data/gso/recipes/armours/pants_basic.json @@ -0,0 +1,18 @@ +{ + "path": "armours/pants_basic", + "recipe": { + "inputs": { + "steel_alloy": 3, + "iron_ingot": 1 + }, + "output": { + "pants_basic_common": 1 + }, + "craft_time_seconds": 45 + }, + "craft": { + "type": "armours", + "subCategory": "pants", + "id": "armours:pants:armours_pants_basic" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/circuits/ai_core_circuit.json b/GameServer/data/gso/recipes/circuits/ai_core_circuit.json new file mode 100644 index 0000000..84ff449 --- /dev/null +++ b/GameServer/data/gso/recipes/circuits/ai_core_circuit.json @@ -0,0 +1,18 @@ +{ + "path": "circuits/ai_core_circuit", + "recipe": { + "inputs": { + "quantum_circuit": 2, + "superconductive_alloy": 1 + }, + "output": { + "ai_core_circuit": 1 + }, + "craft_time_seconds": 90 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_ai_core_circuit" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/circuits/basic_circuit.json b/GameServer/data/gso/recipes/circuits/basic_circuit.json new file mode 100644 index 0000000..a40b958 --- /dev/null +++ b/GameServer/data/gso/recipes/circuits/basic_circuit.json @@ -0,0 +1,18 @@ +{ + "path": "circuits/basic_circuit", + "recipe": { + "inputs": { + "copper_ingot": 1, + "carbon_ingot": 1 + }, + "output": { + "basic_circuit": 1 + }, + "craft_time_seconds": 10 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_basic_circuit" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/circuits/control_circuit.json b/GameServer/data/gso/recipes/circuits/control_circuit.json new file mode 100644 index 0000000..8a8285c --- /dev/null +++ b/GameServer/data/gso/recipes/circuits/control_circuit.json @@ -0,0 +1,18 @@ +{ + "path": "circuits/control_circuit", + "recipe": { + "inputs": { + "logic_circuit": 1, + "copper_ingot": 2 + }, + "output": { + "control_circuit": 1 + }, + "craft_time_seconds": 20 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_control_circuit" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/circuits/dimensional_logic_circuit.json b/GameServer/data/gso/recipes/circuits/dimensional_logic_circuit.json new file mode 100644 index 0000000..3ba64ea --- /dev/null +++ b/GameServer/data/gso/recipes/circuits/dimensional_logic_circuit.json @@ -0,0 +1,19 @@ +{ + "path": "circuits/dimensional_logic_circuit", + "recipe": { + "inputs": { + "quantum_circuit": 1, + "dimensional_alloy": 1, + "void_crystal": 1 + }, + "output": { + "dimensional_logic_circuit": 1 + }, + "craft_time_seconds": 300 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_dimensional_logic_circuit" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/circuits/high_density_circuit.json b/GameServer/data/gso/recipes/circuits/high_density_circuit.json new file mode 100644 index 0000000..4854397 --- /dev/null +++ b/GameServer/data/gso/recipes/circuits/high_density_circuit.json @@ -0,0 +1,18 @@ +{ + "path": "circuits/high_density_circuit", + "recipe": { + "inputs": { + "logic_circuit": 2, + "silver_ingot": 1 + }, + "output": { + "high_density_circuit": 1 + }, + "craft_time_seconds": 30 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_high_density_circuit" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/circuits/logic_circuit.json b/GameServer/data/gso/recipes/circuits/logic_circuit.json new file mode 100644 index 0000000..e7b1bbc --- /dev/null +++ b/GameServer/data/gso/recipes/circuits/logic_circuit.json @@ -0,0 +1,18 @@ +{ + "path": "circuits/logic_circuit", + "recipe": { + "inputs": { + "basic_circuit": 1, + "silicon_wafer": 1 + }, + "output": { + "logic_circuit": 1 + }, + "craft_time_seconds": 15 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_logic_circuit" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/circuits/navigation_circuit.json b/GameServer/data/gso/recipes/circuits/navigation_circuit.json new file mode 100644 index 0000000..4123b5a --- /dev/null +++ b/GameServer/data/gso/recipes/circuits/navigation_circuit.json @@ -0,0 +1,18 @@ +{ + "path": "circuits/navigation_circuit", + "recipe": { + "inputs": { + "control_circuit": 2, + "silver_ingot": 1 + }, + "output": { + "navigation_circuit": 1 + }, + "craft_time_seconds": 30 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_navigation_circuit" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/circuits/power_circuit.json b/GameServer/data/gso/recipes/circuits/power_circuit.json new file mode 100644 index 0000000..f1ad77d --- /dev/null +++ b/GameServer/data/gso/recipes/circuits/power_circuit.json @@ -0,0 +1,19 @@ +{ + "path": "circuits/power_circuit", + "recipe": { + "inputs": { + "control_circuit": 1, + "iron_ingot": 1, + "magnetic_coil": 1 + }, + "output": { + "power_circuit": 1 + }, + "craft_time_seconds": 25 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_power_circuit" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/circuits/processor_circuit.json b/GameServer/data/gso/recipes/circuits/processor_circuit.json new file mode 100644 index 0000000..3cdcffa --- /dev/null +++ b/GameServer/data/gso/recipes/circuits/processor_circuit.json @@ -0,0 +1,19 @@ +{ + "path": "circuits/processor_circuit", + "recipe": { + "inputs": { + "logic_circuit": 2, + "silicon_wafer": 2, + "gold_ingot": 1 + }, + "output": { + "processor_circuit": 1 + }, + "craft_time_seconds": 40 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_processor_circuit" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/circuits/quantum_circuit.json b/GameServer/data/gso/recipes/circuits/quantum_circuit.json new file mode 100644 index 0000000..b3cb46d --- /dev/null +++ b/GameServer/data/gso/recipes/circuits/quantum_circuit.json @@ -0,0 +1,19 @@ +{ + "path": "circuits/quantum_circuit", + "recipe": { + "inputs": { + "processor_circuit": 1, + "flux_crystal": 1, + "titanium_ingot": 1 + }, + "output": { + "quantum_circuit": 1 + }, + "craft_time_seconds": 90 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_quantum_circuit" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/circuits/reactor_control_circuit.json b/GameServer/data/gso/recipes/circuits/reactor_control_circuit.json new file mode 100644 index 0000000..dc70fcf --- /dev/null +++ b/GameServer/data/gso/recipes/circuits/reactor_control_circuit.json @@ -0,0 +1,18 @@ +{ + "path": "circuits/reactor_control_circuit", + "recipe": { + "inputs": { + "processor_circuit": 2, + "flux_core_alloy": 1 + }, + "output": { + "reactor_control_circuit": 1 + }, + "craft_time_seconds": 55 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_reactor_control_circuit" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/circuits/sensor_array_circuit.json b/GameServer/data/gso/recipes/circuits/sensor_array_circuit.json new file mode 100644 index 0000000..e3f5a60 --- /dev/null +++ b/GameServer/data/gso/recipes/circuits/sensor_array_circuit.json @@ -0,0 +1,18 @@ +{ + "path": "circuits/sensor_array_circuit", + "recipe": { + "inputs": { + "high_density_circuit": 1, + "silver_ingot": 1 + }, + "output": { + "sensor_array_circuit": 1 + }, + "craft_time_seconds": 25 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_sensor_array_circuit" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/circuits/sentient_core_circuit.json b/GameServer/data/gso/recipes/circuits/sentient_core_circuit.json new file mode 100644 index 0000000..d1665e7 --- /dev/null +++ b/GameServer/data/gso/recipes/circuits/sentient_core_circuit.json @@ -0,0 +1,19 @@ +{ + "path": "circuits/sentient_core_circuit", + "recipe": { + "inputs": { + "ai_core_circuit": 2, + "chrono_alloy": 1, + "voidsteel": 1 + }, + "output": { + "sentient_core_circuit": 1 + }, + "craft_time_seconds": 180 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_sentient_core_circuit" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/circuits/shield_control_circuit.json b/GameServer/data/gso/recipes/circuits/shield_control_circuit.json new file mode 100644 index 0000000..c453f28 --- /dev/null +++ b/GameServer/data/gso/recipes/circuits/shield_control_circuit.json @@ -0,0 +1,18 @@ +{ + "path": "circuits/shield_control_circuit", + "recipe": { + "inputs": { + "control_circuit": 2, + "magnetic_coil": 2 + }, + "output": { + "shield_control_circuit": 1 + }, + "craft_time_seconds": 50 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_shield_control_circuit" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/circuits/stealth_circuit.json b/GameServer/data/gso/recipes/circuits/stealth_circuit.json new file mode 100644 index 0000000..3b86817 --- /dev/null +++ b/GameServer/data/gso/recipes/circuits/stealth_circuit.json @@ -0,0 +1,18 @@ +{ + "path": "circuits/stealth_circuit", + "recipe": { + "inputs": { + "processor_circuit": 1, + "void_residue": 1 + }, + "output": { + "stealth_circuit": 1 + }, + "craft_time_seconds": 60 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_stealth_circuit" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/circuits/warp_navigation_circuit.json b/GameServer/data/gso/recipes/circuits/warp_navigation_circuit.json new file mode 100644 index 0000000..930ab45 --- /dev/null +++ b/GameServer/data/gso/recipes/circuits/warp_navigation_circuit.json @@ -0,0 +1,18 @@ +{ + "path": "circuits/warp_navigation_circuit", + "recipe": { + "inputs": { + "navigation_circuit": 2, + "chrono_alloy": 1 + }, + "output": { + "warp_navigation_circuit": 1 + }, + "craft_time_seconds": 75 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_warp_navigation_circuit" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/circuits/weapon_control_circuit.json b/GameServer/data/gso/recipes/circuits/weapon_control_circuit.json new file mode 100644 index 0000000..fdebaee --- /dev/null +++ b/GameServer/data/gso/recipes/circuits/weapon_control_circuit.json @@ -0,0 +1,18 @@ +{ + "path": "circuits/weapon_control_circuit", + "recipe": { + "inputs": { + "control_circuit": 2, + "power_circuit": 1 + }, + "output": { + "weapon_control_circuit": 1 + }, + "craft_time_seconds": 50 + }, + "craft": { + "type": "circuits", + "subCategory": "electronics", + "id": "circuits:electronics:circuits_weapon_control_circuit" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/consumables/energy_boost_small.json b/GameServer/data/gso/recipes/consumables/energy_boost_small.json new file mode 100644 index 0000000..58afca8 --- /dev/null +++ b/GameServer/data/gso/recipes/consumables/energy_boost_small.json @@ -0,0 +1,18 @@ +{ + "path": "consumables/energy_boost_small", + "recipe": { + "inputs": { + "flux_crystal": 1, + "copper_ingot": 1 + }, + "output": { + "energy_boost_small": 1 + }, + "craft_time_seconds": 15 + }, + "craft": { + "type": "consumables", + "subCategory": "misc", + "id": "consumables:misc:consumables_energy_boost_small" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/consumables/fuel_cell.json b/GameServer/data/gso/recipes/consumables/fuel_cell.json new file mode 100644 index 0000000..ba48ac6 --- /dev/null +++ b/GameServer/data/gso/recipes/consumables/fuel_cell.json @@ -0,0 +1,18 @@ +{ + "path": "consumables/fuel_cell", + "recipe": { + "inputs": { + "iron_ingot": 1, + "magnetic_coil": 1 + }, + "output": { + "fuel_cell": 3 + }, + "craft_time_seconds": 20 + }, + "craft": { + "type": "consumables", + "subCategory": "misc", + "id": "consumables:misc:consumables_fuel_cell" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/consumables/health_kit_large.json b/GameServer/data/gso/recipes/consumables/health_kit_large.json new file mode 100644 index 0000000..6e8ceef --- /dev/null +++ b/GameServer/data/gso/recipes/consumables/health_kit_large.json @@ -0,0 +1,18 @@ +{ + "path": "consumables/health_kit_large", + "recipe": { + "inputs": { + "bandage": 5, + "alien_fauna_sample": 3 + }, + "output": { + "health_kit_large": 1 + }, + "craft_time_seconds": 30 + }, + "craft": { + "type": "consumables", + "subCategory": "misc", + "id": "consumables:misc:consumables_health_kit_large" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/consumables/health_kit_small.json b/GameServer/data/gso/recipes/consumables/health_kit_small.json new file mode 100644 index 0000000..0636738 --- /dev/null +++ b/GameServer/data/gso/recipes/consumables/health_kit_small.json @@ -0,0 +1,18 @@ +{ + "path": "consumables/health_kit_small", + "recipe": { + "inputs": { + "bandage": 2, + "alien_fauna_sample": 1 + }, + "output": { + "health_kit_small": 1 + }, + "craft_time_seconds": 15 + }, + "craft": { + "type": "consumables", + "subCategory": "misc", + "id": "consumables:misc:consumables_health_kit_small" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/crafting_tabs.json b/GameServer/data/gso/recipes/crafting_tabs.json new file mode 100644 index 0000000..08c45e8 --- /dev/null +++ b/GameServer/data/gso/recipes/crafting_tabs.json @@ -0,0 +1,212 @@ +{ + "craftingTypes": [ + { + "id": "smelting", + "displayName": "Smelting", + "tabs": [ + { + "id": "ingot", + "displayName": "Ingots", + "recipeFilter": { + "type": "smelting", + "subCategory": "ingot" + }, + "recipesDir": "data/gso/recipes/smelting", + "iconTexture": "assets/gso/textures/ui/crafting/smelting/ingot.png" + } + ] + }, + { + "id": "alloys", + "displayName": "Alloys", + "tabs": [ + { + "id": "chemical", + "displayName": "Chemical", + "recipeFilter": { + "type": "alloys", + "subCategory": "chemical" + }, + "recipesDir": "data/gso/recipes/alloys", + "iconTexture": "assets/gso/textures/ui/crafting/alloys/chemical.png" + }, + { + "id": "misc", + "displayName": "Misc", + "recipeFilter": { + "type": "alloys", + "subCategory": "misc" + }, + "recipesDir": "data/gso/recipes/alloys", + "iconTexture": "assets/gso/textures/ui/crafting/alloys/misc.png" + } + ] + }, + { + "id": "circuits", + "displayName": "Circuits", + "tabs": [ + { + "id": "electronics", + "displayName": "Electronics", + "recipeFilter": { + "type": "circuits", + "subCategory": "electronics" + }, + "recipesDir": "data/gso/recipes/circuits", + "iconTexture": "assets/gso/textures/ui/crafting/circuits/electronics.png" + } + ] + }, + { + "id": "hull_sections", + "displayName": "Hull Sections", + "tabs": [ + { + "id": "misc", + "displayName": "Misc", + "recipeFilter": { + "type": "hull_sections", + "subCategory": "misc" + }, + "recipesDir": "data/gso/recipes/hull_sections", + "iconTexture": "assets/gso/textures/ui/crafting/hull_sections/misc.png" + } + ] + }, + { + "id": "hulls", + "displayName": "Hulls", + "tabs": [ + { + "id": "misc", + "displayName": "Misc", + "recipeFilter": { + "type": "hulls", + "subCategory": "misc" + }, + "recipesDir": "data/gso/recipes/hulls", + "iconTexture": "assets/gso/textures/ui/crafting/hulls/misc.png" + } + ] + }, + { + "id": "weapons", + "displayName": "Weapons", + "tabs": [ + { + "id": "pistol", + "displayName": "Pistols", + "recipeFilter": { + "type": "weapons", + "subCategory": "pistol" + }, + "recipesDir": "data/gso/recipes/weapons", + "iconTexture": "assets/gso/textures/ui/crafting/weapons/pistol.png" + }, + { + "id": "rifle", + "displayName": "Rifles", + "recipeFilter": { + "type": "weapons", + "subCategory": "rifle" + }, + "recipesDir": "data/gso/recipes/weapons", + "iconTexture": "assets/gso/textures/ui/crafting/weapons/rifle.png" + }, + { + "id": "sniper", + "displayName": "Snipers", + "recipeFilter": { + "type": "weapons", + "subCategory": "sniper" + }, + "recipesDir": "data/gso/recipes/weapons", + "iconTexture": "assets/gso/textures/ui/crafting/weapons/sniper.png" + }, + { + "id": "special", + "displayName": "Special", + "recipeFilter": { + "type": "weapons", + "subCategory": "special" + }, + "recipesDir": "data/gso/recipes/weapons", + "iconTexture": "assets/gso/textures/ui/crafting/weapons/special.png" + } + ] + }, + { + "id": "armours", + "displayName": "Armour", + "tabs": [ + { + "id": "body", + "displayName": "Body", + "recipeFilter": { + "type": "armours", + "subCategory": "body" + }, + "recipesDir": "data/gso/recipes/armours", + "iconTexture": "assets/gso/textures/ui/crafting/armours/body.png" + }, + { + "id": "helmet", + "displayName": "Helmet", + "recipeFilter": { + "type": "armours", + "subCategory": "helmet" + }, + "recipesDir": "data/gso/recipes/armours", + "iconTexture": "assets/gso/textures/ui/crafting/armours/helmet.png" + }, + { + "id": "boots", + "displayName": "Boots", + "recipeFilter": { + "type": "armours", + "subCategory": "boots" + }, + "recipesDir": "data/gso/recipes/armours", + "iconTexture": "assets/gso/textures/ui/crafting/armours/boots.png" + }, + { + "id": "hands", + "displayName": "Gloves", + "recipeFilter": { + "type": "armours", + "subCategory": "hands" + }, + "recipesDir": "data/gso/recipes/armours", + "iconTexture": "assets/gso/textures/ui/crafting/armours/hands.png" + }, + { + "id": "pants", + "displayName": "Pants", + "recipeFilter": { + "type": "armours", + "subCategory": "pants" + }, + "recipesDir": "data/gso/recipes/armours", + "iconTexture": "assets/gso/textures/ui/crafting/armours/pants.png" + } + ] + }, + { + "id": "consumables", + "displayName": "Consumables", + "tabs": [ + { + "id": "misc", + "displayName": "Misc", + "recipeFilter": { + "type": "consumables", + "subCategory": "misc" + }, + "recipesDir": "data/gso/recipes/consumables", + "iconTexture": "assets/gso/textures/ui/crafting/consumables/misc.png" + } + ] + } + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/food_organics/alien_protein.json b/GameServer/data/gso/recipes/food_organics/alien_protein.json new file mode 100644 index 0000000..ce1a31d --- /dev/null +++ b/GameServer/data/gso/recipes/food_organics/alien_protein.json @@ -0,0 +1,17 @@ +{ + "path": "food_organics/alien_protein", + "recipe": { + "inputs": { + "alien_fauna_sample": 1 + }, + "output": { + "alien_protein": 2 + }, + "harvest_time_seconds": 45 + }, + "craft": { + "type": "food_organics", + "subCategory": "misc", + "id": "food_organics:misc:food_organics_alien_protein" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/food_organics/bread.json b/GameServer/data/gso/recipes/food_organics/bread.json new file mode 100644 index 0000000..00d55bb --- /dev/null +++ b/GameServer/data/gso/recipes/food_organics/bread.json @@ -0,0 +1,17 @@ +{ + "path": "food_organics/bread", + "recipe": { + "inputs": { + "dough": 1 + }, + "output": { + "bread": 1 + }, + "cook_time_seconds": 25 + }, + "craft": { + "type": "food_organics", + "subCategory": "food", + "id": "food_organics:food:food_organics_bread" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/food_organics/cooked_fish.json b/GameServer/data/gso/recipes/food_organics/cooked_fish.json new file mode 100644 index 0000000..758edc5 --- /dev/null +++ b/GameServer/data/gso/recipes/food_organics/cooked_fish.json @@ -0,0 +1,17 @@ +{ + "path": "food_organics/cooked_fish", + "recipe": { + "inputs": { + "fish_meat": 1 + }, + "output": { + "cooked_fish": 1 + }, + "cook_time_seconds": 15 + }, + "craft": { + "type": "food_organics", + "subCategory": "misc", + "id": "food_organics:misc:food_organics_cooked_fish" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/food_organics/cooked_meat.json b/GameServer/data/gso/recipes/food_organics/cooked_meat.json new file mode 100644 index 0000000..ef171d6 --- /dev/null +++ b/GameServer/data/gso/recipes/food_organics/cooked_meat.json @@ -0,0 +1,17 @@ +{ + "path": "food_organics/cooked_meat", + "recipe": { + "inputs": { + "raw_meat": 1 + }, + "output": { + "cooked_meat": 1 + }, + "cook_time_seconds": 20 + }, + "craft": { + "type": "food_organics", + "subCategory": "misc", + "id": "food_organics:misc:food_organics_cooked_meat" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/food_organics/dough.json b/GameServer/data/gso/recipes/food_organics/dough.json new file mode 100644 index 0000000..27254b2 --- /dev/null +++ b/GameServer/data/gso/recipes/food_organics/dough.json @@ -0,0 +1,18 @@ +{ + "path": "food_organics/dough", + "recipe": { + "inputs": { + "flour": 1, + "water": 1 + }, + "output": { + "dough": 1 + }, + "craft_time_seconds": 10 + }, + "craft": { + "type": "food_organics", + "subCategory": "misc", + "id": "food_organics:misc:food_organics_dough" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/food_organics/fish_meat.json b/GameServer/data/gso/recipes/food_organics/fish_meat.json new file mode 100644 index 0000000..e6e37e1 --- /dev/null +++ b/GameServer/data/gso/recipes/food_organics/fish_meat.json @@ -0,0 +1,17 @@ +{ + "path": "food_organics/fish_meat", + "recipe": { + "inputs": { + "fish": 1 + }, + "output": { + "fish_meat": 2 + }, + "harvest_time_seconds": 20 + }, + "craft": { + "type": "food_organics", + "subCategory": "food", + "id": "food_organics:food:food_organics_fish_meat" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/food_organics/flour.json b/GameServer/data/gso/recipes/food_organics/flour.json new file mode 100644 index 0000000..972aa2e --- /dev/null +++ b/GameServer/data/gso/recipes/food_organics/flour.json @@ -0,0 +1,17 @@ +{ + "path": "food_organics/flour", + "recipe": { + "inputs": { + "grain": 2 + }, + "output": { + "flour": 1 + }, + "craft_time_seconds": 20 + }, + "craft": { + "type": "food_organics", + "subCategory": "food", + "id": "food_organics:food:food_organics_flour" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/food_organics/grain.json b/GameServer/data/gso/recipes/food_organics/grain.json new file mode 100644 index 0000000..72f13bc --- /dev/null +++ b/GameServer/data/gso/recipes/food_organics/grain.json @@ -0,0 +1,17 @@ +{ + "path": "food_organics/grain", + "recipe": { + "inputs": { + "grain_crop": 1 + }, + "output": { + "grain": 4 + }, + "harvest_time_seconds": 30 + }, + "craft": { + "type": "food_organics", + "subCategory": "food", + "id": "food_organics:food:food_organics_grain" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/food_organics/hydroponic_growth_medium.json b/GameServer/data/gso/recipes/food_organics/hydroponic_growth_medium.json new file mode 100644 index 0000000..f80784b --- /dev/null +++ b/GameServer/data/gso/recipes/food_organics/hydroponic_growth_medium.json @@ -0,0 +1,19 @@ +{ + "path": "food_organics/hydroponic_growth_medium", + "recipe": { + "inputs": { + "bio_pulp": 1, + "water": 1, + "nutrient_paste": 1 + }, + "output": { + "hydroponic_growth_medium": 1 + }, + "craft_time_seconds": 40 + }, + "craft": { + "type": "food_organics", + "subCategory": "misc", + "id": "food_organics:misc:food_organics_hydroponic_growth_medium" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/food_organics/luxury_meal.json b/GameServer/data/gso/recipes/food_organics/luxury_meal.json new file mode 100644 index 0000000..bc5b9cc --- /dev/null +++ b/GameServer/data/gso/recipes/food_organics/luxury_meal.json @@ -0,0 +1,19 @@ +{ + "path": "food_organics/luxury_meal", + "recipe": { + "inputs": { + "cooked_meat": 1, + "raw_fruits": 1, + "vegetable_stew": 1 + }, + "output": { + "luxury_meal": 1 + }, + "cook_time_seconds": 60 + }, + "craft": { + "type": "food_organics", + "subCategory": "misc", + "id": "food_organics:misc:food_organics_luxury_meal" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/food_organics/meat_stew.json b/GameServer/data/gso/recipes/food_organics/meat_stew.json new file mode 100644 index 0000000..1bbc79d --- /dev/null +++ b/GameServer/data/gso/recipes/food_organics/meat_stew.json @@ -0,0 +1,19 @@ +{ + "path": "food_organics/meat_stew", + "recipe": { + "inputs": { + "raw_meat": 1, + "raw_vegetables": 1, + "water": 1 + }, + "output": { + "meat_stew": 1 + }, + "cook_time_seconds": 35 + }, + "craft": { + "type": "food_organics", + "subCategory": "food", + "id": "food_organics:food:food_organics_meat_stew" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/food_organics/military_ration.json b/GameServer/data/gso/recipes/food_organics/military_ration.json new file mode 100644 index 0000000..de01869 --- /dev/null +++ b/GameServer/data/gso/recipes/food_organics/military_ration.json @@ -0,0 +1,18 @@ +{ + "path": "food_organics/military_ration", + "recipe": { + "inputs": { + "ration_pack": 2, + "nutrient_paste": 1 + }, + "output": { + "military_ration": 1 + }, + "craft_time_seconds": 30 + }, + "craft": { + "type": "food_organics", + "subCategory": "misc", + "id": "food_organics:misc:food_organics_military_ration" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/food_organics/nutrient_paste.json b/GameServer/data/gso/recipes/food_organics/nutrient_paste.json new file mode 100644 index 0000000..2d721ee --- /dev/null +++ b/GameServer/data/gso/recipes/food_organics/nutrient_paste.json @@ -0,0 +1,18 @@ +{ + "path": "food_organics/nutrient_paste", + "recipe": { + "inputs": { + "bio_pulp": 1, + "water": 1 + }, + "output": { + "nutrient_paste": 2 + }, + "craft_time_seconds": 15 + }, + "craft": { + "type": "food_organics", + "subCategory": "misc", + "id": "food_organics:misc:food_organics_nutrient_paste" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/food_organics/protein_bar.json b/GameServer/data/gso/recipes/food_organics/protein_bar.json new file mode 100644 index 0000000..8090f65 --- /dev/null +++ b/GameServer/data/gso/recipes/food_organics/protein_bar.json @@ -0,0 +1,18 @@ +{ + "path": "food_organics/protein_bar", + "recipe": { + "inputs": { + "alien_protein": 1, + "nutrient_paste": 1 + }, + "output": { + "protein_bar": 2 + }, + "craft_time_seconds": 20 + }, + "craft": { + "type": "food_organics", + "subCategory": "misc", + "id": "food_organics:misc:food_organics_protein_bar" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/food_organics/ration_pack.json b/GameServer/data/gso/recipes/food_organics/ration_pack.json new file mode 100644 index 0000000..7ec2e9a --- /dev/null +++ b/GameServer/data/gso/recipes/food_organics/ration_pack.json @@ -0,0 +1,18 @@ +{ + "path": "food_organics/ration_pack", + "recipe": { + "inputs": { + "protein_bar": 1, + "bread": 1 + }, + "output": { + "ration_pack": 1 + }, + "craft_time_seconds": 15 + }, + "craft": { + "type": "food_organics", + "subCategory": "food", + "id": "food_organics:food:food_organics_ration_pack" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/food_organics/raw_fruits.json b/GameServer/data/gso/recipes/food_organics/raw_fruits.json new file mode 100644 index 0000000..7f65568 --- /dev/null +++ b/GameServer/data/gso/recipes/food_organics/raw_fruits.json @@ -0,0 +1,17 @@ +{ + "path": "food_organics/raw_fruits", + "recipe": { + "inputs": { + "fruit_plant": 1 + }, + "output": { + "raw_fruits": 3 + }, + "harvest_time_seconds": 20 + }, + "craft": { + "type": "food_organics", + "subCategory": "misc", + "id": "food_organics:misc:food_organics_raw_fruits" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/food_organics/raw_meat.json b/GameServer/data/gso/recipes/food_organics/raw_meat.json new file mode 100644 index 0000000..36713ce --- /dev/null +++ b/GameServer/data/gso/recipes/food_organics/raw_meat.json @@ -0,0 +1,17 @@ +{ + "path": "food_organics/raw_meat", + "recipe": { + "inputs": { + "animal_carcass": 1 + }, + "output": { + "raw_meat": 4 + }, + "harvest_time_seconds": 40 + }, + "craft": { + "type": "food_organics", + "subCategory": "misc", + "id": "food_organics:misc:food_organics_raw_meat" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/food_organics/raw_vegetables.json b/GameServer/data/gso/recipes/food_organics/raw_vegetables.json new file mode 100644 index 0000000..799cd1b --- /dev/null +++ b/GameServer/data/gso/recipes/food_organics/raw_vegetables.json @@ -0,0 +1,17 @@ +{ + "path": "food_organics/raw_vegetables", + "recipe": { + "inputs": { + "vegetable_plant": 1 + }, + "output": { + "raw_vegetables": 3 + }, + "harvest_time_seconds": 25 + }, + "craft": { + "type": "food_organics", + "subCategory": "misc", + "id": "food_organics:misc:food_organics_raw_vegetables" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/food_organics/vegetable_stew.json b/GameServer/data/gso/recipes/food_organics/vegetable_stew.json new file mode 100644 index 0000000..fc35890 --- /dev/null +++ b/GameServer/data/gso/recipes/food_organics/vegetable_stew.json @@ -0,0 +1,18 @@ +{ + "path": "food_organics/vegetable_stew", + "recipe": { + "inputs": { + "raw_vegetables": 2, + "water": 1 + }, + "output": { + "vegetable_stew": 1 + }, + "cook_time_seconds": 30 + }, + "craft": { + "type": "food_organics", + "subCategory": "food", + "id": "food_organics:food:food_organics_vegetable_stew" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/food_organics/water.json b/GameServer/data/gso/recipes/food_organics/water.json new file mode 100644 index 0000000..ee57bf2 --- /dev/null +++ b/GameServer/data/gso/recipes/food_organics/water.json @@ -0,0 +1,17 @@ +{ + "path": "food_organics/water", + "recipe": { + "inputs": { + "ice_chunk": 1 + }, + "output": { + "water": 2 + }, + "process_time_seconds": 10 + }, + "craft": { + "type": "food_organics", + "subCategory": "liquid", + "id": "food_organics:liquid:food_organics_water" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/hull_sections/advanced_hull_section.json b/GameServer/data/gso/recipes/hull_sections/advanced_hull_section.json new file mode 100644 index 0000000..eb6995e --- /dev/null +++ b/GameServer/data/gso/recipes/hull_sections/advanced_hull_section.json @@ -0,0 +1,18 @@ +{ + "path": "hull_sections/advanced_hull_section", + "recipe": { + "inputs": { + "flux_core_alloy": 3, + "processor_circuit": 1 + }, + "output": { + "advanced_hull_section": 1 + }, + "construction_time_seconds": 240 + }, + "craft": { + "type": "hull_sections", + "subCategory": "misc", + "id": "hull_sections:misc:hull_sections_advanced_hull_section" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/hull_sections/armor_hull_section.json b/GameServer/data/gso/recipes/hull_sections/armor_hull_section.json new file mode 100644 index 0000000..d89833a --- /dev/null +++ b/GameServer/data/gso/recipes/hull_sections/armor_hull_section.json @@ -0,0 +1,18 @@ +{ + "path": "hull_sections/armor_hull_section", + "recipe": { + "inputs": { + "carbon_titanium_composite": 3, + "shield_control_circuit": 1 + }, + "output": { + "armor_hull_section": 1 + }, + "construction_time_seconds": 180 + }, + "craft": { + "type": "hull_sections", + "subCategory": "misc", + "id": "hull_sections:misc:hull_sections_armor_hull_section" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/hull_sections/exotic_hull_section.json b/GameServer/data/gso/recipes/hull_sections/exotic_hull_section.json new file mode 100644 index 0000000..148fb09 --- /dev/null +++ b/GameServer/data/gso/recipes/hull_sections/exotic_hull_section.json @@ -0,0 +1,18 @@ +{ + "path": "hull_sections/exotic_hull_section", + "recipe": { + "inputs": { + "voidsteel": 3, + "quantum_circuit": 1 + }, + "output": { + "exotic_hull_section": 1 + }, + "construction_time_seconds": 360 + }, + "craft": { + "type": "hull_sections", + "subCategory": "misc", + "id": "hull_sections:misc:hull_sections_exotic_hull_section" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/hull_sections/heavy_hull_section.json b/GameServer/data/gso/recipes/hull_sections/heavy_hull_section.json new file mode 100644 index 0000000..13d8a1f --- /dev/null +++ b/GameServer/data/gso/recipes/hull_sections/heavy_hull_section.json @@ -0,0 +1,18 @@ +{ + "path": "hull_sections/heavy_hull_section", + "recipe": { + "inputs": { + "titanium_alloy": 4, + "power_circuit": 1 + }, + "output": { + "heavy_hull_section": 1 + }, + "construction_time_seconds": 120 + }, + "craft": { + "type": "hull_sections", + "subCategory": "misc", + "id": "hull_sections:misc:hull_sections_heavy_hull_section" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/hull_sections/light_hull_section.json b/GameServer/data/gso/recipes/hull_sections/light_hull_section.json new file mode 100644 index 0000000..070e1ab --- /dev/null +++ b/GameServer/data/gso/recipes/hull_sections/light_hull_section.json @@ -0,0 +1,18 @@ +{ + "path": "hull_sections/light_hull_section", + "recipe": { + "inputs": { + "steel_alloy": 4, + "basic_circuit": 1 + }, + "output": { + "light_hull_section": 1 + }, + "construction_time_seconds": 60 + }, + "craft": { + "type": "hull_sections", + "subCategory": "misc", + "id": "hull_sections:misc:hull_sections_light_hull_section" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/hull_sections/standard_hull_section.json b/GameServer/data/gso/recipes/hull_sections/standard_hull_section.json new file mode 100644 index 0000000..9a2c446 --- /dev/null +++ b/GameServer/data/gso/recipes/hull_sections/standard_hull_section.json @@ -0,0 +1,18 @@ +{ + "path": "hull_sections/standard_hull_section", + "recipe": { + "inputs": { + "reinforced_steel": 4, + "control_circuit": 1 + }, + "output": { + "standard_hull_section": 1 + }, + "construction_time_seconds": 90 + }, + "craft": { + "type": "hull_sections", + "subCategory": "misc", + "id": "hull_sections:misc:hull_sections_standard_hull_section" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/hulls/ablative_armor_hull.json b/GameServer/data/gso/recipes/hulls/ablative_armor_hull.json new file mode 100644 index 0000000..1f60759 --- /dev/null +++ b/GameServer/data/gso/recipes/hulls/ablative_armor_hull.json @@ -0,0 +1,15 @@ +{ + "path": "hulls/ablative_armor_hull", + "recipe": { + "inputs": { + "armor_hull_section": 24, + "ablative_plating": 16 + }, + "construction_time_seconds": 680 + }, + "craft": { + "type": "hulls", + "subCategory": "plating", + "id": "hulls:plating:hulls_ablative_armor_hull" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/hulls/advanced_hull.json b/GameServer/data/gso/recipes/hulls/advanced_hull.json new file mode 100644 index 0000000..9f4e161 --- /dev/null +++ b/GameServer/data/gso/recipes/hulls/advanced_hull.json @@ -0,0 +1,18 @@ +{ + "path": "hulls/advanced_hull", + "recipe": { + "inputs": { + "advanced_hull_section": 8, + "flux_core_alloy": 4 + }, + "output": { + "advanced_hull": 1 + }, + "construction_time_seconds": 900 + }, + "craft": { + "type": "hulls", + "subCategory": "misc", + "id": "hulls:misc:hulls_advanced_hull" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/hulls/armor_hull.json b/GameServer/data/gso/recipes/hulls/armor_hull.json new file mode 100644 index 0000000..15a827c --- /dev/null +++ b/GameServer/data/gso/recipes/hulls/armor_hull.json @@ -0,0 +1,18 @@ +{ + "path": "hulls/armor_hull", + "recipe": { + "inputs": { + "armor_hull_section": 8, + "carbon_titanium_composite": 4 + }, + "output": { + "armor_hull": 1 + }, + "construction_time_seconds": 600 + }, + "craft": { + "type": "hulls", + "subCategory": "misc", + "id": "hulls:misc:hulls_armor_hull" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/hulls/chrono_stabilized_hull.json b/GameServer/data/gso/recipes/hulls/chrono_stabilized_hull.json new file mode 100644 index 0000000..c45e756 --- /dev/null +++ b/GameServer/data/gso/recipes/hulls/chrono_stabilized_hull.json @@ -0,0 +1,15 @@ +{ + "path": "hulls/chrono_stabilized_hull", + "recipe": { + "inputs": { + "advanced_hull_section": 30, + "chrono_alloy": 16 + }, + "construction_time_seconds": 1100 + }, + "craft": { + "type": "hulls", + "subCategory": "module", + "id": "hulls:module:hulls_chrono_stabilized_hull" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/hulls/dimensional_anchor_hull.json b/GameServer/data/gso/recipes/hulls/dimensional_anchor_hull.json new file mode 100644 index 0000000..2a15205 --- /dev/null +++ b/GameServer/data/gso/recipes/hulls/dimensional_anchor_hull.json @@ -0,0 +1,15 @@ +{ + "path": "hulls/dimensional_anchor_hull", + "recipe": { + "inputs": { + "exotic_hull_section": 48, + "dimensional_alloy": 24 + }, + "construction_time_seconds": 1500 + }, + "craft": { + "type": "hulls", + "subCategory": "module", + "id": "hulls:module:hulls_dimensional_anchor_hull" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/hulls/dreadframe_hull.json b/GameServer/data/gso/recipes/hulls/dreadframe_hull.json new file mode 100644 index 0000000..6dbd350 --- /dev/null +++ b/GameServer/data/gso/recipes/hulls/dreadframe_hull.json @@ -0,0 +1,19 @@ +{ + "path": "hulls/dreadframe_hull", + "recipe": { + "inputs": { + "advanced_hull_section": 12, + "neutronium_composite": 4, + "dimensional_alloy": 2 + }, + "output": { + "dreadframe_hull": 1 + }, + "construction_time_seconds": 3600 + }, + "craft": { + "type": "hulls", + "subCategory": "misc", + "id": "hulls:misc:hulls_dreadframe_hull" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/hulls/heavy_hull.json b/GameServer/data/gso/recipes/hulls/heavy_hull.json new file mode 100644 index 0000000..4e6bcdd --- /dev/null +++ b/GameServer/data/gso/recipes/hulls/heavy_hull.json @@ -0,0 +1,18 @@ +{ + "path": "hulls/heavy_hull", + "recipe": { + "inputs": { + "heavy_hull_section": 10, + "titanium_alloy": 4 + }, + "output": { + "heavy_hull": 1 + }, + "construction_time_seconds": 480 + }, + "craft": { + "type": "hulls", + "subCategory": "misc", + "id": "hulls:misc:hulls_heavy_hull" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/hulls/industrial_hull.json b/GameServer/data/gso/recipes/hulls/industrial_hull.json new file mode 100644 index 0000000..062b023 --- /dev/null +++ b/GameServer/data/gso/recipes/hulls/industrial_hull.json @@ -0,0 +1,15 @@ +{ + "path": "hulls/industrial_hull", + "recipe": { + "inputs": { + "heavy_hull_section": 24, + "reinforced_steel": 8 + }, + "construction_time_seconds": 600 + }, + "craft": { + "type": "hulls", + "subCategory": "misc", + "id": "hulls:misc:hulls_industrial_hull" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/hulls/lightframe_hull.json b/GameServer/data/gso/recipes/hulls/lightframe_hull.json new file mode 100644 index 0000000..493d2ac --- /dev/null +++ b/GameServer/data/gso/recipes/hulls/lightframe_hull.json @@ -0,0 +1,18 @@ +{ + "path": "hulls/lightframe_hull", + "recipe": { + "inputs": { + "light_hull_section": 8, + "carbon_titanium_composite": 2 + }, + "output": { + "lightframe_hull": 1 + }, + "construction_time_seconds": 180 + }, + "craft": { + "type": "hulls", + "subCategory": "misc", + "id": "hulls:misc:hulls_lightframe_hull" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/hulls/phase_locked_hull.json b/GameServer/data/gso/recipes/hulls/phase_locked_hull.json new file mode 100644 index 0000000..a97f65a --- /dev/null +++ b/GameServer/data/gso/recipes/hulls/phase_locked_hull.json @@ -0,0 +1,19 @@ +{ + "path": "hulls/phase_locked_hull", + "recipe": { + "inputs": { + "exotic_hull_section": 6, + "phase_alloy": 4, + "phase_crystal": 2 + }, + "output": { + "phase_locked_hull": 1 + }, + "construction_time_seconds": 1500 + }, + "craft": { + "type": "hulls", + "subCategory": "misc", + "id": "hulls:misc:hulls_phase_locked_hull" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/hulls/plasma_resistant_hull.json b/GameServer/data/gso/recipes/hulls/plasma_resistant_hull.json new file mode 100644 index 0000000..6f17c05 --- /dev/null +++ b/GameServer/data/gso/recipes/hulls/plasma_resistant_hull.json @@ -0,0 +1,15 @@ +{ + "path": "hulls/plasma_resistant_hull", + "recipe": { + "inputs": { + "advanced_hull_section": 28, + "plasma_channel_alloy": 10 + }, + "construction_time_seconds": 840 + }, + "craft": { + "type": "hulls", + "subCategory": "misc", + "id": "hulls:misc:hulls_plasma_resistant_hull" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/hulls/reinforced_plating_hull.json b/GameServer/data/gso/recipes/hulls/reinforced_plating_hull.json new file mode 100644 index 0000000..ce44870 --- /dev/null +++ b/GameServer/data/gso/recipes/hulls/reinforced_plating_hull.json @@ -0,0 +1,15 @@ +{ + "path": "hulls/reinforced_plating_hull", + "recipe": { + "inputs": { + "armor_hull_section": 32, + "ballistic_alloy": 12 + }, + "construction_time_seconds": 720 + }, + "craft": { + "type": "hulls", + "subCategory": "plating", + "id": "hulls:plating:hulls_reinforced_plating_hull" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/hulls/standard_hull.json b/GameServer/data/gso/recipes/hulls/standard_hull.json new file mode 100644 index 0000000..b5fcc41 --- /dev/null +++ b/GameServer/data/gso/recipes/hulls/standard_hull.json @@ -0,0 +1,18 @@ +{ + "path": "hulls/standard_hull", + "recipe": { + "inputs": { + "standard_hull_section": 8, + "reinforced_steel": 4 + }, + "output": { + "standard_hull": 1 + }, + "construction_time_seconds": 300 + }, + "craft": { + "type": "hulls", + "subCategory": "misc", + "id": "hulls:misc:hulls_standard_hull" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/hulls/standard_structural_hull.json b/GameServer/data/gso/recipes/hulls/standard_structural_hull.json new file mode 100644 index 0000000..12f17de --- /dev/null +++ b/GameServer/data/gso/recipes/hulls/standard_structural_hull.json @@ -0,0 +1,15 @@ +{ + "path": "hulls/standard_structural_hull", + "recipe": { + "inputs": { + "standard_hull_section": 20, + "reinforced_steel": 4 + }, + "construction_time_seconds": 420 + }, + "craft": { + "type": "hulls", + "subCategory": "misc", + "id": "hulls:misc:hulls_standard_structural_hull" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/hulls/superconductive_hull.json b/GameServer/data/gso/recipes/hulls/superconductive_hull.json new file mode 100644 index 0000000..3e74b93 --- /dev/null +++ b/GameServer/data/gso/recipes/hulls/superconductive_hull.json @@ -0,0 +1,15 @@ +{ + "path": "hulls/superconductive_hull", + "recipe": { + "inputs": { + "advanced_hull_section": 18, + "superconductive_alloy": 12 + }, + "construction_time_seconds": 780 + }, + "craft": { + "type": "hulls", + "subCategory": "misc", + "id": "hulls:misc:hulls_superconductive_hull" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/hulls/titanium_aerospace_hull.json b/GameServer/data/gso/recipes/hulls/titanium_aerospace_hull.json new file mode 100644 index 0000000..055d074 --- /dev/null +++ b/GameServer/data/gso/recipes/hulls/titanium_aerospace_hull.json @@ -0,0 +1,15 @@ +{ + "path": "hulls/titanium_aerospace_hull", + "recipe": { + "inputs": { + "advanced_hull_section": 20, + "carbon_titanium_composite": 6 + }, + "construction_time_seconds": 540 + }, + "craft": { + "type": "hulls", + "subCategory": "misc", + "id": "hulls:misc:hulls_titanium_aerospace_hull" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/hulls/voidsteel_hull.json b/GameServer/data/gso/recipes/hulls/voidsteel_hull.json new file mode 100644 index 0000000..777fbc2 --- /dev/null +++ b/GameServer/data/gso/recipes/hulls/voidsteel_hull.json @@ -0,0 +1,18 @@ +{ + "path": "hulls/voidsteel_hull", + "recipe": { + "inputs": { + "exotic_hull_section": 8, + "voidsteel": 4 + }, + "output": { + "voidsteel_hull": 1 + }, + "construction_time_seconds": 1200 + }, + "craft": { + "type": "hulls", + "subCategory": "misc", + "id": "hulls:misc:hulls_voidsteel_hull" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/manifest.json b/GameServer/data/gso/recipes/manifest.json new file mode 100644 index 0000000..1af0276 --- /dev/null +++ b/GameServer/data/gso/recipes/manifest.json @@ -0,0 +1,21 @@ +{ + "counts_by_group": { + "smelting": 10, + "alloys": 10, + "circuits": 10, + "hull_sections": 6, + "hulls": 8, + "weapons": 5, + "armours": 6, + "consumables": 4 + }, + "craftingTabs": { + "file": "data/gso/recipes/crafting_tabs.json", + "notes": [ + "Filter recipes by recipe.craft.type and recipe.craft.subCategory" + ] + }, + "notes": [ + "Recipes are grouped into data/gso/recipes//. Each file preserves {path, recipe, craft}." + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/organics/bandage.json b/GameServer/data/gso/recipes/organics/bandage.json new file mode 100644 index 0000000..8652bed --- /dev/null +++ b/GameServer/data/gso/recipes/organics/bandage.json @@ -0,0 +1,18 @@ +{ + "path": "organics/bandage", + "recipe": { + "inputs": { + "plant_cloth": 1, + "resin": 1 + }, + "output": { + "bandage": 2 + }, + "craft_time_seconds": 10 + }, + "craft": { + "type": "organics", + "subCategory": "misc", + "id": "organics:misc:organics_bandage" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/organics/bio_composite_material.json b/GameServer/data/gso/recipes/organics/bio_composite_material.json new file mode 100644 index 0000000..bee0947 --- /dev/null +++ b/GameServer/data/gso/recipes/organics/bio_composite_material.json @@ -0,0 +1,19 @@ +{ + "path": "organics/bio_composite_material", + "recipe": { + "inputs": { + "resin": 1, + "plant_fiber": 1, + "carbon_ingot": 1 + }, + "output": { + "bio_composite_material": 1 + }, + "craft_time_seconds": 45 + }, + "craft": { + "type": "organics", + "subCategory": "chemical", + "id": "organics:chemical:organics_bio_composite_material" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/organics/bio_pulp.json b/GameServer/data/gso/recipes/organics/bio_pulp.json new file mode 100644 index 0000000..f2a508f --- /dev/null +++ b/GameServer/data/gso/recipes/organics/bio_pulp.json @@ -0,0 +1,17 @@ +{ + "path": "organics/bio_pulp", + "recipe": { + "inputs": { + "plant_matter": 3 + }, + "output": { + "bio_pulp": 1 + }, + "craft_time_seconds": 30 + }, + "craft": { + "type": "organics", + "subCategory": "bio", + "id": "organics:bio:organics_bio_pulp" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/organics/medical_pack.json b/GameServer/data/gso/recipes/organics/medical_pack.json new file mode 100644 index 0000000..991d42e --- /dev/null +++ b/GameServer/data/gso/recipes/organics/medical_pack.json @@ -0,0 +1,18 @@ +{ + "path": "organics/medical_pack", + "recipe": { + "inputs": { + "bandage": 2, + "bio_pulp": 1 + }, + "output": { + "medical_pack": 1 + }, + "craft_time_seconds": 30 + }, + "craft": { + "type": "organics", + "subCategory": "misc", + "id": "organics:misc:organics_medical_pack" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/organics/organic_insulation.json b/GameServer/data/gso/recipes/organics/organic_insulation.json new file mode 100644 index 0000000..8aadd4a --- /dev/null +++ b/GameServer/data/gso/recipes/organics/organic_insulation.json @@ -0,0 +1,18 @@ +{ + "path": "organics/organic_insulation", + "recipe": { + "inputs": { + "plant_cloth": 1, + "bio_pulp": 1 + }, + "output": { + "organic_insulation": 1 + }, + "craft_time_seconds": 35 + }, + "craft": { + "type": "organics", + "subCategory": "bio", + "id": "organics:bio:organics_organic_insulation" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/organics/plant_cloth.json b/GameServer/data/gso/recipes/organics/plant_cloth.json new file mode 100644 index 0000000..1f0fecc --- /dev/null +++ b/GameServer/data/gso/recipes/organics/plant_cloth.json @@ -0,0 +1,17 @@ +{ + "path": "organics/plant_cloth", + "recipe": { + "inputs": { + "plant_fiber": 2 + }, + "output": { + "plant_cloth": 1 + }, + "craft_time_seconds": 20 + }, + "craft": { + "type": "organics", + "subCategory": "fabric", + "id": "organics:fabric:organics_plant_cloth" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/organics/plant_fiber.json b/GameServer/data/gso/recipes/organics/plant_fiber.json new file mode 100644 index 0000000..8a6610c --- /dev/null +++ b/GameServer/data/gso/recipes/organics/plant_fiber.json @@ -0,0 +1,17 @@ +{ + "path": "organics/plant_fiber", + "recipe": { + "inputs": { + "plant_matter": 2 + }, + "output": { + "plant_fiber": 1 + }, + "harvest_time_seconds": 15 + }, + "craft": { + "type": "organics", + "subCategory": "fabric", + "id": "organics:fabric:organics_plant_fiber" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/organics/processed_wood.json b/GameServer/data/gso/recipes/organics/processed_wood.json new file mode 100644 index 0000000..b81ac52 --- /dev/null +++ b/GameServer/data/gso/recipes/organics/processed_wood.json @@ -0,0 +1,17 @@ +{ + "path": "organics/processed_wood", + "recipe": { + "inputs": { + "raw_wood": 2 + }, + "output": { + "processed_wood": 1 + }, + "craft_time_seconds": 20 + }, + "craft": { + "type": "organics", + "subCategory": "misc", + "id": "organics:misc:organics_processed_wood" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/organics/raw_wood.json b/GameServer/data/gso/recipes/organics/raw_wood.json new file mode 100644 index 0000000..62bf685 --- /dev/null +++ b/GameServer/data/gso/recipes/organics/raw_wood.json @@ -0,0 +1,17 @@ +{ + "path": "organics/raw_wood", + "recipe": { + "inputs": { + "tree": 1 + }, + "output": { + "raw_wood": 4 + }, + "harvest_time_seconds": 20 + }, + "craft": { + "type": "organics", + "subCategory": "misc", + "id": "organics:misc:organics_raw_wood" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/organics/resin.json b/GameServer/data/gso/recipes/organics/resin.json new file mode 100644 index 0000000..780f01a --- /dev/null +++ b/GameServer/data/gso/recipes/organics/resin.json @@ -0,0 +1,17 @@ +{ + "path": "organics/resin", + "recipe": { + "inputs": { + "tree_sap": 2 + }, + "output": { + "resin": 1 + }, + "craft_time_seconds": 25 + }, + "craft": { + "type": "organics", + "subCategory": "chemical", + "id": "organics:chemical:organics_resin" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/organics/tree_sap.json b/GameServer/data/gso/recipes/organics/tree_sap.json new file mode 100644 index 0000000..98614f2 --- /dev/null +++ b/GameServer/data/gso/recipes/organics/tree_sap.json @@ -0,0 +1,17 @@ +{ + "path": "organics/tree_sap", + "recipe": { + "inputs": { + "tree": 1 + }, + "output": { + "tree_sap": 1 + }, + "harvest_time_seconds": 25 + }, + "craft": { + "type": "organics", + "subCategory": "misc", + "id": "organics:misc:organics_tree_sap" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/organics/wood_planks.json b/GameServer/data/gso/recipes/organics/wood_planks.json new file mode 100644 index 0000000..c2181ea --- /dev/null +++ b/GameServer/data/gso/recipes/organics/wood_planks.json @@ -0,0 +1,17 @@ +{ + "path": "organics/wood_planks", + "recipe": { + "inputs": { + "processed_wood": 1 + }, + "output": { + "wood_planks": 4 + }, + "craft_time_seconds": 15 + }, + "craft": { + "type": "organics", + "subCategory": "wood", + "id": "organics:wood:organics_wood_planks" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/organics/wooden_components.json b/GameServer/data/gso/recipes/organics/wooden_components.json new file mode 100644 index 0000000..7f73700 --- /dev/null +++ b/GameServer/data/gso/recipes/organics/wooden_components.json @@ -0,0 +1,18 @@ +{ + "path": "organics/wooden_components", + "recipe": { + "inputs": { + "wood_planks": 2, + "resin": 1 + }, + "output": { + "wooden_components": 1 + }, + "craft_time_seconds": 25 + }, + "craft": { + "type": "organics", + "subCategory": "misc", + "id": "organics:misc:organics_wooden_components" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/smelting/aluminum_ingot.json b/GameServer/data/gso/recipes/smelting/aluminum_ingot.json new file mode 100644 index 0000000..9658e33 --- /dev/null +++ b/GameServer/data/gso/recipes/smelting/aluminum_ingot.json @@ -0,0 +1,17 @@ +{ + "path": "smelting/aluminum_ingot", + "recipe": { + "inputs": { + "bauxite_ore": 3 + }, + "output": { + "aluminum_ingot": 1 + }, + "smelt_time_seconds": 20 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_aluminum_ingot" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/smelting/carbon_ingot.json b/GameServer/data/gso/recipes/smelting/carbon_ingot.json new file mode 100644 index 0000000..4d0f570 --- /dev/null +++ b/GameServer/data/gso/recipes/smelting/carbon_ingot.json @@ -0,0 +1,18 @@ +{ + "path": "smelting/carbon_ingot", + "recipe": { + "inputs": { + "iron_ore": 1, + "chromium_ore": 1 + }, + "output": { + "carbon_ingot": 1 + }, + "smelt_time_seconds": 20 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_carbon_ingot" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/smelting/chromium_ingot.json b/GameServer/data/gso/recipes/smelting/chromium_ingot.json new file mode 100644 index 0000000..7e157d0 --- /dev/null +++ b/GameServer/data/gso/recipes/smelting/chromium_ingot.json @@ -0,0 +1,17 @@ +{ + "path": "smelting/chromium_ingot", + "recipe": { + "inputs": { + "chromium_ore": 2 + }, + "output": { + "chromium_ingot": 1 + }, + "smelt_time_seconds": 25 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_chromium_ingot" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/smelting/chronium_ingot.json b/GameServer/data/gso/recipes/smelting/chronium_ingot.json new file mode 100644 index 0000000..83cbaa2 --- /dev/null +++ b/GameServer/data/gso/recipes/smelting/chronium_ingot.json @@ -0,0 +1,17 @@ +{ + "path": "smelting/chronium_ingot", + "recipe": { + "inputs": { + "chronite_ore": 3 + }, + "output": { + "chronium_ingot": 1 + }, + "smelt_time_seconds": 75 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_chronium_ingot" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/smelting/copper_ingot.json b/GameServer/data/gso/recipes/smelting/copper_ingot.json new file mode 100644 index 0000000..99c24a7 --- /dev/null +++ b/GameServer/data/gso/recipes/smelting/copper_ingot.json @@ -0,0 +1,17 @@ +{ + "path": "smelting/copper_ingot", + "recipe": { + "inputs": { + "copper_ore": 2 + }, + "output": { + "copper_ingot": 1 + }, + "smelt_time_seconds": 15 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_copper_ingot" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/smelting/darksteel_ingot.json b/GameServer/data/gso/recipes/smelting/darksteel_ingot.json new file mode 100644 index 0000000..955a00e --- /dev/null +++ b/GameServer/data/gso/recipes/smelting/darksteel_ingot.json @@ -0,0 +1,18 @@ +{ + "path": "smelting/darksteel_ingot", + "recipe": { + "inputs": { + "dark_iron_ore": 2, + "iron_ore": 1 + }, + "output": { + "darksteel_ingot": 1 + }, + "smelt_time_seconds": 35 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_darksteel_ingot" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/smelting/gold_ingot.json b/GameServer/data/gso/recipes/smelting/gold_ingot.json new file mode 100644 index 0000000..2084d24 --- /dev/null +++ b/GameServer/data/gso/recipes/smelting/gold_ingot.json @@ -0,0 +1,17 @@ +{ + "path": "smelting/gold_ingot", + "recipe": { + "inputs": { + "gold_ore": 2 + }, + "output": { + "gold_ingot": 1 + }, + "smelt_time_seconds": 20 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_gold_ingot" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/smelting/iron_ingot.json b/GameServer/data/gso/recipes/smelting/iron_ingot.json new file mode 100644 index 0000000..3414468 --- /dev/null +++ b/GameServer/data/gso/recipes/smelting/iron_ingot.json @@ -0,0 +1,17 @@ +{ + "path": "smelting/iron_ingot", + "recipe": { + "inputs": { + "iron_ore": 2 + }, + "output": { + "iron_ingot": 1 + }, + "smelt_time_seconds": 15 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_iron_ingot" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/smelting/magnesium_ingot.json b/GameServer/data/gso/recipes/smelting/magnesium_ingot.json new file mode 100644 index 0000000..87cc4cf --- /dev/null +++ b/GameServer/data/gso/recipes/smelting/magnesium_ingot.json @@ -0,0 +1,17 @@ +{ + "path": "smelting/magnesium_ingot", + "recipe": { + "inputs": { + "magnesium_ore": 2 + }, + "output": { + "magnesium_ingot": 1 + }, + "smelt_time_seconds": 18 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_magnesium_ingot" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/smelting/neutronium_ingot.json b/GameServer/data/gso/recipes/smelting/neutronium_ingot.json new file mode 100644 index 0000000..611a49b --- /dev/null +++ b/GameServer/data/gso/recipes/smelting/neutronium_ingot.json @@ -0,0 +1,17 @@ +{ + "path": "smelting/neutronium_ingot", + "recipe": { + "inputs": { + "neutronium_shard": 3 + }, + "output": { + "neutronium_ingot": 1 + }, + "smelt_time_seconds": 120 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_neutronium_ingot" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/smelting/platinum_ingot.json b/GameServer/data/gso/recipes/smelting/platinum_ingot.json new file mode 100644 index 0000000..822951e --- /dev/null +++ b/GameServer/data/gso/recipes/smelting/platinum_ingot.json @@ -0,0 +1,17 @@ +{ + "path": "smelting/platinum_ingot", + "recipe": { + "inputs": { + "platinum_ore": 2 + }, + "output": { + "platinum_ingot": 1 + }, + "smelt_time_seconds": 40 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_platinum_ingot" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/smelting/silver_ingot.json b/GameServer/data/gso/recipes/smelting/silver_ingot.json new file mode 100644 index 0000000..3f371d9 --- /dev/null +++ b/GameServer/data/gso/recipes/smelting/silver_ingot.json @@ -0,0 +1,17 @@ +{ + "path": "smelting/silver_ingot", + "recipe": { + "inputs": { + "silver_ore": 2 + }, + "output": { + "silver_ingot": 1 + }, + "smelt_time_seconds": 25 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_silver_ingot" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/smelting/titanium_ingot.json b/GameServer/data/gso/recipes/smelting/titanium_ingot.json new file mode 100644 index 0000000..3b324ee --- /dev/null +++ b/GameServer/data/gso/recipes/smelting/titanium_ingot.json @@ -0,0 +1,17 @@ +{ + "path": "smelting/titanium_ingot", + "recipe": { + "inputs": { + "titanium_ore": 2 + }, + "output": { + "titanium_ingot": 1 + }, + "smelt_time_seconds": 25 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_titanium_ingot" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/smelting/tungsten_ingot.json b/GameServer/data/gso/recipes/smelting/tungsten_ingot.json new file mode 100644 index 0000000..9e81f54 --- /dev/null +++ b/GameServer/data/gso/recipes/smelting/tungsten_ingot.json @@ -0,0 +1,17 @@ +{ + "path": "smelting/tungsten_ingot", + "recipe": { + "inputs": { + "tungsten_ore": 2 + }, + "output": { + "tungsten_ingot": 1 + }, + "smelt_time_seconds": 30 + }, + "craft": { + "type": "smelting", + "subCategory": "ingot", + "id": "smelting:ingot:smelting_tungsten_ingot" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/spacesuit_parts/body_exotic.json b/GameServer/data/gso/recipes/spacesuit_parts/body_exotic.json new file mode 100644 index 0000000..91704d1 --- /dev/null +++ b/GameServer/data/gso/recipes/spacesuit_parts/body_exotic.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/body_suits/body_exotic", + "recipe": { + "inputs": { + "phase_alloy": 4, + "dimensional_alloy": 2, + "sentient_core_circuit": 1 + }, + "output": { + "body_exotic": 1 + }, + "craft_time_seconds": 220 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_body_suits_body_exotic" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/spacesuit_parts/body_light.json b/GameServer/data/gso/recipes/spacesuit_parts/body_light.json new file mode 100644 index 0000000..d179e1e --- /dev/null +++ b/GameServer/data/gso/recipes/spacesuit_parts/body_light.json @@ -0,0 +1,18 @@ +{ + "path": "spacesuit_parts/body_suits/body_light", + "recipe": { + "inputs": { + "aluminum_alloy": 3, + "organic_insulation": 2 + }, + "output": { + "body_light": 1 + }, + "craft_time_seconds": 90 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_body_suits_body_light" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/spacesuit_parts/body_military.json b/GameServer/data/gso/recipes/spacesuit_parts/body_military.json new file mode 100644 index 0000000..2b56e1f --- /dev/null +++ b/GameServer/data/gso/recipes/spacesuit_parts/body_military.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/body_suits/body_military", + "recipe": { + "inputs": { + "darksteel_ingot": 4, + "voidsteel": 2, + "shield_control_circuit": 1 + }, + "output": { + "body_military": 1 + }, + "craft_time_seconds": 160 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_body_suits_body_military" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/spacesuit_parts/body_standard.json b/GameServer/data/gso/recipes/spacesuit_parts/body_standard.json new file mode 100644 index 0000000..dbb20ac --- /dev/null +++ b/GameServer/data/gso/recipes/spacesuit_parts/body_standard.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/body_suits/body_standard", + "recipe": { + "inputs": { + "titanium_alloy": 4, + "carbon_nanotube_alloy": 2, + "organic_insulation": 2 + }, + "output": { + "body_standard": 1 + }, + "craft_time_seconds": 120 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_body_suits_body_standard" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/spacesuit_parts/boots_light.json b/GameServer/data/gso/recipes/spacesuit_parts/boots_light.json new file mode 100644 index 0000000..38589e8 --- /dev/null +++ b/GameServer/data/gso/recipes/spacesuit_parts/boots_light.json @@ -0,0 +1,18 @@ +{ + "path": "spacesuit_parts/boots/boots_light", + "recipe": { + "inputs": { + "aluminum_alloy": 1, + "rubber_polymer": 1 + }, + "output": { + "boots_light": 1 + }, + "craft_time_seconds": 40 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_boots_boots_light" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/spacesuit_parts/boots_magnetic.json b/GameServer/data/gso/recipes/spacesuit_parts/boots_magnetic.json new file mode 100644 index 0000000..e721e8f --- /dev/null +++ b/GameServer/data/gso/recipes/spacesuit_parts/boots_magnetic.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/boots/boots_magnetic", + "recipe": { + "inputs": { + "tungsten_alloy": 1, + "magnetic_coil": 1, + "control_circuit": 1 + }, + "output": { + "boots_magnetic": 1 + }, + "craft_time_seconds": 70 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_boots_boots_magnetic" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/spacesuit_parts/boots_military.json b/GameServer/data/gso/recipes/spacesuit_parts/boots_military.json new file mode 100644 index 0000000..83229a2 --- /dev/null +++ b/GameServer/data/gso/recipes/spacesuit_parts/boots_military.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/boots/boots_military", + "recipe": { + "inputs": { + "darksteel_ingot": 1, + "servo_alloy": 1, + "weapon_control_circuit": 1 + }, + "output": { + "boots_military": 1 + }, + "craft_time_seconds": 90 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_boots_boots_military" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/spacesuit_parts/boots_standard.json b/GameServer/data/gso/recipes/spacesuit_parts/boots_standard.json new file mode 100644 index 0000000..b8b4195 --- /dev/null +++ b/GameServer/data/gso/recipes/spacesuit_parts/boots_standard.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/boots/boots_standard", + "recipe": { + "inputs": { + "titanium_alloy": 1, + "bio_composite_material": 1, + "control_circuit": 1 + }, + "output": { + "boots_standard": 1 + }, + "craft_time_seconds": 55 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_boots_boots_standard" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/spacesuit_parts/gloves_industrial.json b/GameServer/data/gso/recipes/spacesuit_parts/gloves_industrial.json new file mode 100644 index 0000000..583d793 --- /dev/null +++ b/GameServer/data/gso/recipes/spacesuit_parts/gloves_industrial.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/gloves/gloves_industrial", + "recipe": { + "inputs": { + "titanium_alloy": 1, + "bio_composite_material": 1, + "control_circuit": 1 + }, + "output": { + "gloves_industrial": 1 + }, + "craft_time_seconds": 60 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_gloves_gloves_industrial" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/spacesuit_parts/gloves_light.json b/GameServer/data/gso/recipes/spacesuit_parts/gloves_light.json new file mode 100644 index 0000000..5123544 --- /dev/null +++ b/GameServer/data/gso/recipes/spacesuit_parts/gloves_light.json @@ -0,0 +1,18 @@ +{ + "path": "spacesuit_parts/gloves/gloves_light", + "recipe": { + "inputs": { + "flex_alloy": 1, + "plant_cloth": 1 + }, + "output": { + "gloves_light": 1 + }, + "craft_time_seconds": 30 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_gloves_gloves_light" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/spacesuit_parts/gloves_military.json b/GameServer/data/gso/recipes/spacesuit_parts/gloves_military.json new file mode 100644 index 0000000..f240b34 --- /dev/null +++ b/GameServer/data/gso/recipes/spacesuit_parts/gloves_military.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/gloves/gloves_military", + "recipe": { + "inputs": { + "darksteel_ingot": 1, + "servo_alloy": 1, + "weapon_control_circuit": 1 + }, + "output": { + "gloves_military": 1 + }, + "craft_time_seconds": 80 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_gloves_gloves_military" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/spacesuit_parts/gloves_standard.json b/GameServer/data/gso/recipes/spacesuit_parts/gloves_standard.json new file mode 100644 index 0000000..6d873f6 --- /dev/null +++ b/GameServer/data/gso/recipes/spacesuit_parts/gloves_standard.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/gloves/gloves_standard", + "recipe": { + "inputs": { + "flex_alloy": 1, + "bio_composite_material": 1, + "basic_circuit": 1 + }, + "output": { + "gloves_standard": 1 + }, + "craft_time_seconds": 45 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_gloves_gloves_standard" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/spacesuit_parts/helmet_exotic.json b/GameServer/data/gso/recipes/spacesuit_parts/helmet_exotic.json new file mode 100644 index 0000000..7e8049d --- /dev/null +++ b/GameServer/data/gso/recipes/spacesuit_parts/helmet_exotic.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/helmets/helmet_exotic", + "recipe": { + "inputs": { + "phase_alloy": 2, + "crystal_glass": 1, + "quantum_circuit": 1 + }, + "output": { + "helmet_exotic": 1 + }, + "craft_time_seconds": 120 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_helmets_helmet_exotic" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/spacesuit_parts/helmet_light.json b/GameServer/data/gso/recipes/spacesuit_parts/helmet_light.json new file mode 100644 index 0000000..63f7bff --- /dev/null +++ b/GameServer/data/gso/recipes/spacesuit_parts/helmet_light.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/helmets/helmet_light", + "recipe": { + "inputs": { + "aluminum_alloy": 2, + "glass_composite": 1, + "basic_circuit": 1 + }, + "output": { + "helmet_light": 1 + }, + "craft_time_seconds": 45 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_helmets_helmet_light" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/spacesuit_parts/helmet_military.json b/GameServer/data/gso/recipes/spacesuit_parts/helmet_military.json new file mode 100644 index 0000000..0f9ed82 --- /dev/null +++ b/GameServer/data/gso/recipes/spacesuit_parts/helmet_military.json @@ -0,0 +1,20 @@ +{ + "path": "spacesuit_parts/helmets/helmet_military", + "recipe": { + "inputs": { + "darksteel_ingot": 2, + "transparent_alloy": 1, + "sensor_array_circuit": 1, + "shield_control_circuit": 1 + }, + "output": { + "helmet_military": 1 + }, + "craft_time_seconds": 90 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_helmets_helmet_military" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/spacesuit_parts/helmet_standard.json b/GameServer/data/gso/recipes/spacesuit_parts/helmet_standard.json new file mode 100644 index 0000000..78de778 --- /dev/null +++ b/GameServer/data/gso/recipes/spacesuit_parts/helmet_standard.json @@ -0,0 +1,20 @@ +{ + "path": "spacesuit_parts/helmets/helmet_standard", + "recipe": { + "inputs": { + "titanium_alloy": 2, + "glass_composite": 1, + "sensor_array_circuit": 1, + "organic_insulation": 1 + }, + "output": { + "helmet_standard": 1 + }, + "craft_time_seconds": 60 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_helmets_helmet_standard" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/spacesuit_parts/life_support_basic.json b/GameServer/data/gso/recipes/spacesuit_parts/life_support_basic.json new file mode 100644 index 0000000..af74776 --- /dev/null +++ b/GameServer/data/gso/recipes/spacesuit_parts/life_support_basic.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/life_support_backpacks/life_support_basic", + "recipe": { + "inputs": { + "aluminum_alloy": 2, + "power_circuit": 1, + "bio_pulp": 1 + }, + "output": { + "life_support_basic": 1 + }, + "craft_time_seconds": 100 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_life_support_backpacks_life_support_basic" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/spacesuit_parts/life_support_exotic.json b/GameServer/data/gso/recipes/spacesuit_parts/life_support_exotic.json new file mode 100644 index 0000000..e09dad4 --- /dev/null +++ b/GameServer/data/gso/recipes/spacesuit_parts/life_support_exotic.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/life_support_backpacks/life_support_exotic", + "recipe": { + "inputs": { + "phase_alloy": 3, + "quantum_circuit": 1, + "bio_composite_material": 2 + }, + "output": { + "life_support_exotic": 1 + }, + "craft_time_seconds": 260 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_life_support_backpacks_life_support_exotic" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/spacesuit_parts/life_support_military.json b/GameServer/data/gso/recipes/spacesuit_parts/life_support_military.json new file mode 100644 index 0000000..f2c1672 --- /dev/null +++ b/GameServer/data/gso/recipes/spacesuit_parts/life_support_military.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/life_support_backpacks/life_support_military", + "recipe": { + "inputs": { + "darksteel_ingot": 3, + "reactor_control_circuit": 1, + "organic_insulation": 2 + }, + "output": { + "life_support_military": 1 + }, + "craft_time_seconds": 200 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_life_support_backpacks_life_support_military" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/spacesuit_parts/life_support_standard.json b/GameServer/data/gso/recipes/spacesuit_parts/life_support_standard.json new file mode 100644 index 0000000..3b8fbef --- /dev/null +++ b/GameServer/data/gso/recipes/spacesuit_parts/life_support_standard.json @@ -0,0 +1,20 @@ +{ + "path": "spacesuit_parts/life_support_backpacks/life_support_standard", + "recipe": { + "inputs": { + "titanium_alloy": 3, + "power_circuit": 1, + "control_circuit": 1, + "organic_insulation": 1 + }, + "output": { + "life_support_standard": 1 + }, + "craft_time_seconds": 150 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_life_support_backpacks_life_support_standard" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/spacesuit_parts/pants_exotic.json b/GameServer/data/gso/recipes/spacesuit_parts/pants_exotic.json new file mode 100644 index 0000000..a22b886 --- /dev/null +++ b/GameServer/data/gso/recipes/spacesuit_parts/pants_exotic.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/pants/pants_exotic", + "recipe": { + "inputs": { + "phase_alloy": 2, + "bio_composite_material": 1, + "dimensional_logic_circuit": 1 + }, + "output": { + "pants_exotic": 1 + }, + "craft_time_seconds": 120 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_pants_pants_exotic" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/spacesuit_parts/pants_heavy.json b/GameServer/data/gso/recipes/spacesuit_parts/pants_heavy.json new file mode 100644 index 0000000..7bb61a9 --- /dev/null +++ b/GameServer/data/gso/recipes/spacesuit_parts/pants_heavy.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/pants/pants_heavy", + "recipe": { + "inputs": { + "tungsten_alloy": 2, + "carbon_nanotube_alloy": 1, + "organic_insulation": 1 + }, + "output": { + "pants_heavy": 1 + }, + "craft_time_seconds": 90 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_pants_pants_heavy" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/spacesuit_parts/pants_light.json b/GameServer/data/gso/recipes/spacesuit_parts/pants_light.json new file mode 100644 index 0000000..48072ea --- /dev/null +++ b/GameServer/data/gso/recipes/spacesuit_parts/pants_light.json @@ -0,0 +1,18 @@ +{ + "path": "spacesuit_parts/pants/pants_light", + "recipe": { + "inputs": { + "aluminum_alloy": 2, + "organic_insulation": 1 + }, + "output": { + "pants_light": 1 + }, + "craft_time_seconds": 50 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_pants_pants_light" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/spacesuit_parts/pants_standard.json b/GameServer/data/gso/recipes/spacesuit_parts/pants_standard.json new file mode 100644 index 0000000..52b5c94 --- /dev/null +++ b/GameServer/data/gso/recipes/spacesuit_parts/pants_standard.json @@ -0,0 +1,19 @@ +{ + "path": "spacesuit_parts/pants/pants_standard", + "recipe": { + "inputs": { + "titanium_alloy": 2, + "bio_composite_material": 1, + "organic_insulation": 1 + }, + "output": { + "pants_standard": 1 + }, + "craft_time_seconds": 70 + }, + "craft": { + "type": "spacesuit_parts", + "subCategory": "misc", + "id": "spacesuit_parts:misc:spacesuit_parts_pants_pants_standard" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/weapons/assault_rifle.json b/GameServer/data/gso/recipes/weapons/assault_rifle.json new file mode 100644 index 0000000..bb7783d --- /dev/null +++ b/GameServer/data/gso/recipes/weapons/assault_rifle.json @@ -0,0 +1,19 @@ +{ + "path": "weapons/assault_rifle", + "recipe": { + "inputs": { + "reinforced_steel": 4, + "weapon_control_circuit": 1, + "power_circuit": 1 + }, + "output": { + "assault_rifle_common": 1 + }, + "craft_time_seconds": 90 + }, + "craft": { + "type": "weapons", + "subCategory": "rifle", + "id": "weapons:rifle:weapons_assault_rifle" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/weapons/plasma_cutter.json b/GameServer/data/gso/recipes/weapons/plasma_cutter.json new file mode 100644 index 0000000..dcd854b --- /dev/null +++ b/GameServer/data/gso/recipes/weapons/plasma_cutter.json @@ -0,0 +1,19 @@ +{ + "path": "weapons/plasma_cutter", + "recipe": { + "inputs": { + "flux_core_alloy": 3, + "weapon_control_circuit": 1, + "flux_crystal": 2 + }, + "output": { + "plasma_cutter_rare": 1 + }, + "craft_time_seconds": 180 + }, + "craft": { + "type": "weapons", + "subCategory": "special", + "id": "weapons:special:weapons_plasma_cutter" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/weapons/sniper_rifle.json b/GameServer/data/gso/recipes/weapons/sniper_rifle.json new file mode 100644 index 0000000..1f60c90 --- /dev/null +++ b/GameServer/data/gso/recipes/weapons/sniper_rifle.json @@ -0,0 +1,19 @@ +{ + "path": "weapons/sniper_rifle", + "recipe": { + "inputs": { + "titanium_alloy": 3, + "processor_circuit": 1, + "magnetic_coil": 2 + }, + "output": { + "sniper_common": 1 + }, + "craft_time_seconds": 120 + }, + "craft": { + "type": "weapons", + "subCategory": "sniper", + "id": "weapons:sniper:weapons_sniper_rifle" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/weapons/standard_pistol.json b/GameServer/data/gso/recipes/weapons/standard_pistol.json new file mode 100644 index 0000000..e9d8244 --- /dev/null +++ b/GameServer/data/gso/recipes/weapons/standard_pistol.json @@ -0,0 +1,18 @@ +{ + "path": "weapons/standard_pistol", + "recipe": { + "inputs": { + "steel_alloy": 3, + "control_circuit": 1 + }, + "output": { + "pistol_standard_common": 1 + }, + "craft_time_seconds": 45 + }, + "craft": { + "type": "weapons", + "subCategory": "pistol", + "id": "weapons:pistol:weapons_standard_pistol" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/recipes/weapons/starter_blaster.json b/GameServer/data/gso/recipes/weapons/starter_blaster.json new file mode 100644 index 0000000..0c4a340 --- /dev/null +++ b/GameServer/data/gso/recipes/weapons/starter_blaster.json @@ -0,0 +1,18 @@ +{ + "path": "weapons/starter_blaster", + "recipe": { + "inputs": { + "iron_ingot": 5, + "basic_circuit": 1 + }, + "output": { + "starter_blaster": 1 + }, + "craft_time_seconds": 30 + }, + "craft": { + "type": "weapons", + "subCategory": "pistol", + "id": "weapons:pistol:weapons_starter_blaster" + } +} \ No newline at end of file diff --git a/GameServer/data/gso/skills/combat/defense_training.json b/GameServer/data/gso/skills/combat/defense_training.json new file mode 100644 index 0000000..73e1a96 --- /dev/null +++ b/GameServer/data/gso/skills/combat/defense_training.json @@ -0,0 +1,15 @@ +{ + "id": "defense_training", + "name": "Defense Training", + "category": "combat", + "description": "Improves damage reduction and resistance", + "maxLevel": 100, + "experiencePerLevel": 1000, + "defaultUnlocked": true, + "icon": "fa-shield-alt", + "bonuses": { + "damageReduction": 1.5, + "resistance": 1, + "health": 5 + } +} \ No newline at end of file diff --git a/GameServer/data/gso/skills/combat/piloting.json b/GameServer/data/gso/skills/combat/piloting.json new file mode 100644 index 0000000..ef4a60f --- /dev/null +++ b/GameServer/data/gso/skills/combat/piloting.json @@ -0,0 +1,15 @@ +{ + "id": "piloting", + "name": "Piloting", + "category": "combat", + "description": "Advanced ship handling and evasive manoeuvres", + "maxLevel": 100, + "experiencePerLevel": 1000, + "defaultUnlocked": true, + "icon": "fa-rocket", + "bonuses": { + "speed": 2, + "criticalChance": 0.005, + "dodgeChance": 0.5 + } +} \ No newline at end of file diff --git a/GameServer/data/gso/skills/combat/speed_reflexes.json b/GameServer/data/gso/skills/combat/speed_reflexes.json new file mode 100644 index 0000000..262cd64 --- /dev/null +++ b/GameServer/data/gso/skills/combat/speed_reflexes.json @@ -0,0 +1,15 @@ +{ + "id": "speed_reflexes", + "name": "Speed & Reflexes", + "category": "combat", + "description": "Enhances movement speed and reaction time", + "maxLevel": 100, + "experiencePerLevel": 1000, + "defaultUnlocked": true, + "icon": "fa-bolt", + "bonuses": { + "speed": 2, + "dodgeChance": 0.8, + "initiative": 1 + } +} \ No newline at end of file diff --git a/GameServer/data/gso/skills/combat/tactical_analysis.json b/GameServer/data/gso/skills/combat/tactical_analysis.json new file mode 100644 index 0000000..2cc04fd --- /dev/null +++ b/GameServer/data/gso/skills/combat/tactical_analysis.json @@ -0,0 +1,15 @@ +{ + "id": "tactical_analysis", + "name": "Tactical Analysis", + "category": "combat", + "description": "Reveals enemy weaknesses and improves critical strikes", + "maxLevel": 100, + "experiencePerLevel": 1200, + "defaultUnlocked": false, + "requiredLevel": 5, + "icon": "fa-brain", + "bonuses": { + "criticalDamage": 0.05, + "attack": 1 + } +} \ No newline at end of file diff --git a/GameServer/data/gso/skills/combat/weapons_mastery.json b/GameServer/data/gso/skills/combat/weapons_mastery.json new file mode 100644 index 0000000..22ae3ef --- /dev/null +++ b/GameServer/data/gso/skills/combat/weapons_mastery.json @@ -0,0 +1,15 @@ +{ + "id": "weapons_mastery", + "name": "Weapons Mastery", + "category": "combat", + "description": "Increases damage and accuracy with all weapons", + "maxLevel": 100, + "experiencePerLevel": 1000, + "defaultUnlocked": true, + "icon": "fa-crosshairs", + "bonuses": { + "damage": 2, + "accuracy": 1, + "criticalChance": 0.5 + } +} \ No newline at end of file diff --git a/GameServer/data/gso/skills/crafting/armor_crafting.json b/GameServer/data/gso/skills/crafting/armor_crafting.json new file mode 100644 index 0000000..823d7e0 --- /dev/null +++ b/GameServer/data/gso/skills/crafting/armor_crafting.json @@ -0,0 +1,15 @@ +{ + "id": "armor_crafting", + "name": "Armor Crafting", + "category": "crafting", + "description": "Forge protective armour plating and shields", + "maxLevel": 100, + "experiencePerLevel": 800, + "defaultUnlocked": true, + "icon": "fa-shield-halved", + "bonuses": { + "craftingBonus": 0.1, + "armorStats": 0.05, + "durabilityBonus": 3 + } +} \ No newline at end of file diff --git a/GameServer/data/gso/skills/crafting/resource_extraction.json b/GameServer/data/gso/skills/crafting/resource_extraction.json new file mode 100644 index 0000000..a8a5f8e --- /dev/null +++ b/GameServer/data/gso/skills/crafting/resource_extraction.json @@ -0,0 +1,15 @@ +{ + "id": "resource_extraction", + "name": "Resource Extraction", + "category": "crafting", + "description": "Improved ore yields and mining efficiency", + "maxLevel": 100, + "experiencePerLevel": 700, + "defaultUnlocked": false, + "requiredLevel": 4, + "icon": "fa-gem", + "bonuses": { + "resourceBonus": 0.15, + "findResources": 0.1 + } +} \ No newline at end of file diff --git a/GameServer/data/gso/skills/crafting/smelting_mastery.json b/GameServer/data/gso/skills/crafting/smelting_mastery.json new file mode 100644 index 0000000..0b66184 --- /dev/null +++ b/GameServer/data/gso/skills/crafting/smelting_mastery.json @@ -0,0 +1,15 @@ +{ + "id": "smelting_mastery", + "name": "Smelting Mastery", + "category": "crafting", + "description": "Faster smelting and higher-quality ingot output", + "maxLevel": 100, + "experiencePerLevel": 800, + "defaultUnlocked": false, + "requiredLevel": 7, + "icon": "fa-fire", + "bonuses": { + "smeltingSpeed": 2, + "ingotBonus": 0.1 + } +} \ No newline at end of file diff --git a/GameServer/data/gso/skills/crafting/weapons_crafting.json b/GameServer/data/gso/skills/crafting/weapons_crafting.json new file mode 100644 index 0000000..b5515a6 --- /dev/null +++ b/GameServer/data/gso/skills/crafting/weapons_crafting.json @@ -0,0 +1,15 @@ +{ + "id": "weapons_crafting", + "name": "Weapons Crafting", + "category": "crafting", + "description": "Craft and upgrade ship weapons systems", + "maxLevel": 100, + "experiencePerLevel": 800, + "defaultUnlocked": true, + "icon": "fa-gun", + "bonuses": { + "craftingBonus": 0.1, + "weaponStats": 0.05, + "rareChance": 0.5 + } +} \ No newline at end of file diff --git a/GameServer/data/gso/skills/manifest.json b/GameServer/data/gso/skills/manifest.json new file mode 100644 index 0000000..5401aaf --- /dev/null +++ b/GameServer/data/gso/skills/manifest.json @@ -0,0 +1,10 @@ +{ + "counts_by_category": { + "combat": 5, + "science": 5, + "crafting": 5 + }, + "notes": [ + "One JSON per skill. Grouped into data/gso/skills//.json" + ] +} \ No newline at end of file diff --git a/GameServer/data/gso/skills/science/alien_technology.json b/GameServer/data/gso/skills/science/alien_technology.json new file mode 100644 index 0000000..68d7378 --- /dev/null +++ b/GameServer/data/gso/skills/science/alien_technology.json @@ -0,0 +1,15 @@ +{ + "id": "alien_technology", + "name": "Alien Technology", + "category": "science", + "description": "Understanding and reverse-engineering alien artefacts", + "maxLevel": 100, + "experiencePerLevel": 1500, + "defaultUnlocked": false, + "requiredLevel": 3, + "icon": "fa-atom", + "bonuses": { + "findRarity": 0.05, + "itemValue": 0.1 + } +} \ No newline at end of file diff --git a/GameServer/data/gso/skills/science/bio_engineering.json b/GameServer/data/gso/skills/science/bio_engineering.json new file mode 100644 index 0000000..9807acc --- /dev/null +++ b/GameServer/data/gso/skills/science/bio_engineering.json @@ -0,0 +1,15 @@ +{ + "id": "bio_engineering", + "name": "Bio-Engineering", + "category": "science", + "description": "Biological enhancements and accelerated healing", + "maxLevel": 100, + "experiencePerLevel": 1500, + "defaultUnlocked": false, + "requiredLevel": 6, + "icon": "fa-dna", + "bonuses": { + "maxHealth": 15, + "healthRegeneration": 0.05 + } +} \ No newline at end of file diff --git a/GameServer/data/gso/skills/science/energy_manipulation.json b/GameServer/data/gso/skills/science/energy_manipulation.json new file mode 100644 index 0000000..5c24024 --- /dev/null +++ b/GameServer/data/gso/skills/science/energy_manipulation.json @@ -0,0 +1,14 @@ +{ + "id": "energy_manipulation", + "name": "Energy Manipulation", + "category": "science", + "description": "Better energy control and faster regeneration", + "maxLevel": 100, + "experiencePerLevel": 1000, + "defaultUnlocked": true, + "icon": "fa-bolt", + "bonuses": { + "maxEnergy": 10, + "energyRegeneration": 0.1 + } +} \ No newline at end of file diff --git a/GameServer/data/gso/skills/science/engineering.json b/GameServer/data/gso/skills/science/engineering.json new file mode 100644 index 0000000..b26331b --- /dev/null +++ b/GameServer/data/gso/skills/science/engineering.json @@ -0,0 +1,14 @@ +{ + "id": "engineering", + "name": "Engineering", + "category": "science", + "description": "Technical skills for ship components and machinery", + "maxLevel": 100, + "experiencePerLevel": 1000, + "defaultUnlocked": true, + "icon": "fa-wrench", + "bonuses": { + "craftingBonus": 0.08, + "shipStats": 0.03 + } +} \ No newline at end of file diff --git a/GameServer/data/gso/skills/science/quantum_physics.json b/GameServer/data/gso/skills/science/quantum_physics.json new file mode 100644 index 0000000..256441a --- /dev/null +++ b/GameServer/data/gso/skills/science/quantum_physics.json @@ -0,0 +1,15 @@ +{ + "id": "quantum_physics", + "name": "Quantum Physics", + "category": "science", + "description": "Advanced quantum mechanics applied to equipment", + "maxLevel": 100, + "experiencePerLevel": 2000, + "defaultUnlocked": false, + "requiredLevel": 8, + "icon": "fa-microscope", + "bonuses": { + "criticalDamage": 0.1, + "attack": 3 + } +} \ No newline at end of file diff --git a/GameServer/models/PlayerData.js b/GameServer/models/PlayerData.js index 827e2b7..da682ec 100644 --- a/GameServer/models/PlayerData.js +++ b/GameServer/models/PlayerData.js @@ -57,6 +57,28 @@ const playerDataSchema = new mongoose.Schema({ type: mongoose.Schema.Types.Mixed, default: {} }, + // Starbase customisation — wallpapers and room unlocks + starbase: { + type: mongoose.Schema.Types.Mixed, + default: { + wallpaper: null, + ownedWallpapers: [], + unlockedRooms: [], + } + }, + // Building slots — { buildingId: { level, buildQueue: { startedAt, completesAt } | null } } + buildings: { + type: mongoose.Schema.Types.Mixed, + default: { command_center: { level: 1, buildQueue: null } } + }, + // Galaxy map — explored sectors and home sector + exploredSectors: { type: [String], default: ['15_10'] }, + homeSector: { type: String, default: '15_10' }, + // Research progress + research: { + type: mongoose.Schema.Types.Mixed, + default: { completed: [], inProgress: null, effects: {} } + }, // Server-specific data currentServerId: { type: String, diff --git a/GameServer/server.js b/GameServer/server.js index 694b3f6..1313695 100644 --- a/GameServer/server.js +++ b/GameServer/server.js @@ -19,21 +19,39 @@ const connectDB = require('./config/database'); const PlayerData = require('./models/PlayerData'); // Import server systems for player initialization -const QuestSystem = require('./systems/QuestSystem'); -const SkillSystem = require('./systems/SkillSystem'); -const DungeonSystem = require('./systems/DungeonSystem'); +const QuestSystem = require('./systems/QuestSystem'); +const SkillSystem = require('./systems/SkillSystem'); +const DungeonSystem = require('./systems/DungeonSystem'); const CraftingSystem = require('./systems/CraftingSystem'); -const IdleSystem = require('./systems/IdleSystem'); -const ItemSystem = require('./systems/ItemSystem'); +const IdleSystem = require('./systems/IdleSystem'); +const ItemSystem = require('./systems/ItemSystem'); +const ContentLoader = require('./systems/ContentLoader'); +const FleetSystem = require('./systems/FleetSystem'); +const GalaxySystem = require('./systems/GalaxySystem'); +const { ResearchSystem } = require('./systems/ResearchSystem'); -// Initialize server systems -const questSystem = new QuestSystem(); -const skillSystem = new SkillSystem(); -const dungeonSystem = new DungeonSystem(); -const craftingSystem = new CraftingSystem(); -const idleSystem = new IdleSystem(); -const itemSystem = new ItemSystem(); +// ── Load all JSON game content before systems are initialised ── +const contentLoader = new ContentLoader(); +contentLoader._loadSkills(); +contentLoader._loadEnemies(); +contentLoader._loadDungeons(); +contentLoader._loadItems(); +contentLoader._loadRecipes(); +contentLoader._loadQuests(); +contentLoader._loaded = true; +console.log(`[SERVER] Content loaded — ${contentLoader.skills.size} skills, ${contentLoader.items.size} items, ${contentLoader.recipes.size} recipes, ${contentLoader.quests.size} quests, ${contentLoader.dungeons.size} dungeons, ${contentLoader.enemies.size} enemies`); +// Initialize server systems — each receives contentLoader as its data source +const questSystem = new QuestSystem(contentLoader); +const skillSystem = new SkillSystem(contentLoader); +const dungeonSystem = new DungeonSystem(contentLoader); +const craftingSystem = new CraftingSystem(contentLoader); +const idleSystem = new IdleSystem(); +const itemSystem = new ItemSystem(contentLoader); + +const fleetSystem = new FleetSystem(contentLoader); +const galaxySystem = new GalaxySystem(); +const researchSystem = new ResearchSystem(); // Set server URL for ItemSystem const SERVER_URL = process.env.SERVER_URL || 'https://dev.gameserver.galaxystrike.online'; itemSystem.setServerUrl(SERVER_URL); @@ -63,7 +81,7 @@ const connectedClients = new Map(); // Server-side shop item lookup using ItemSystem function findShopItem(itemId) { - return itemSystem.getItem(itemId); + return itemSystem.findShopItem(itemId); } // Middleware @@ -180,38 +198,25 @@ app.get('/api/ssc/version', (req, res) => { // Shop API endpoints app.get('/api/shop/items', (req, res) => { try { - const shopItems = itemSystem.getRandomShopItems(); - res.status(200).json({ - success: true, - shopItems: shopItems, - timestamp: new Date().toISOString() - }); + res.status(200).json(itemSystem.buildShopResponse()); } catch (error) { console.error('[GAME SERVER] Error fetching shop items:', error); - res.status(500).json({ - success: false, - error: 'Failed to fetch shop items' - }); + res.status(500).json({ success: false, error: 'Failed to fetch shop items' }); } }); app.get('/api/shop/items/:category', (req, res) => { try { const { category } = req.params; - const items = itemSystem.getRandomItemsByCategory(category); - res.status(200).json({ success: true, - category: category, - items: items, + category, + items: itemSystem.getRandomItemsByCategory(category), timestamp: new Date().toISOString() }); } catch (error) { console.error('[GAME SERVER] Error fetching category items:', error); - res.status(500).json({ - success: false, - error: 'Failed to fetch category items' - }); + res.status(500).json({ success: false, error: 'Failed to fetch category items' }); } }); @@ -393,6 +398,16 @@ io.on('connection', (socket) => { await savePlayerData(playerData.userId, playerData); + // Calculate pending offline rewards to show on dashboard + const pendingOffline = idleSystem.calculateOfflineRewards(playerData.userId); + if (pendingOffline.offlineTime > 0) { + playerData.stats.offlineTime = pendingOffline.offlineTime; + playerData.stats.offlineCredits = pendingOffline.rewards?.credits || 0; + } else { + playerData.stats.offlineTime = 0; + playerData.stats.offlineCredits = 0; + } + // In production, validate with API server socket.emit('authenticated', { success: true, @@ -403,8 +418,8 @@ io.on('connection', (socket) => { }, playerData: { ...playerData.toObject(), - serverTimestamp: Date.now(), // Add server timestamp for time synchronization - serverTimezone: 'UTC' // Server operates in UTC timezone + serverTimestamp: Date.now(), + serverTimezone: 'UTC' } }); @@ -502,116 +517,41 @@ io.on('connection', (socket) => { }); }); - socket.on('getShopItems', (data) => { - console.log('[GAME SERVER] === getShopItems REQUEST START ==='); - console.log('[GAME SERVER] getShopItems request received from:', socket.id); - console.log('[GAME SERVER] Request data:', data); - console.log('[GAME SERVER] ItemSystem available:', !!itemSystem); - console.log('[GAME SERVER] ItemSystem.getRandomShopItems available:', typeof itemSystem.getRandomShopItems); - + socket.on('getShopItems', () => { try { - console.log('[GAME SERVER] Getting shop items from ItemSystem...'); - const shopItems = itemSystem.getRandomShopItems(); - console.log('[GAME SERVER] Got shop items by category:', Object.keys(shopItems)); - - // Log item counts per category - Object.entries(shopItems).forEach(([category, items]) => { - console.log(`[GAME SERVER] ${category}: ${items.length} items`); - }); - - const response = { - success: true, - shopItems: shopItems, // Changed from 'items' to 'shopItems' - timestamp: new Date().toISOString() - }; - - console.log('[GAME SERVER] Sending response:', response); - console.log('[GAME SERVER] About to emit shopItemsReceived to:', socket.id); - - socket.emit('shopItemsReceived', response); - console.log('[GAME SERVER] shopItemsReceived emitted successfully'); - console.log('[GAME SERVER] === getShopItems REQUEST COMPLETE ==='); + socket.emit('shopItemsReceived', itemSystem.buildShopResponse()); } catch (error) { - console.error('[GAME SERVER] Error sending shop items:', error); - console.error('[GAME SERVER] Error stack:', error.stack); - socket.emit('shopItemsReceived', { - success: false, - error: 'Failed to load shop items' - }); - console.log('[GAME SERVER] Error response sent'); + console.error('[GAME SERVER] getShopItems error:', error); + socket.emit('shopItemsReceived', { success: false, error: 'Failed to load shop items' }); } }); socket.on('getShopCategory', (data) => { - console.log('[GAME SERVER] getShopCategory request received from:', socket.id); - console.log('[GAME SERVER] Request data:', data); - try { - const { category } = data; + const { category } = data || {}; if (!category) { - socket.emit('shopCategoryReceived', { - success: false, - error: 'Category required' - }); + socket.emit('shopCategoryReceived', { success: false, error: 'Category required' }); return; } - - console.log('[GAME SERVER] Getting items for category:', category); - const categoryItems = itemSystem.getRandomItemsByCategory(category); - console.log('[GAME SERVER] Got', categoryItems.length, 'items for category', category); - - const response = { - success: true, - category: category, - items: categoryItems, - timestamp: new Date().toISOString() - }; - - console.log('[GAME SERVER] Sending category response:', response); - socket.emit('shopCategoryReceived', response); - console.log('[GAME SERVER] Category response sent successfully'); - } catch (error) { - console.error('[GAME SERVER] Error sending category items:', error); socket.emit('shopCategoryReceived', { - success: false, - error: 'Failed to load category items' + success: true, + category, + items: itemSystem.getRandomItemsByCategory(category), + timestamp: new Date().toISOString() }); + } catch (error) { + console.error('[GAME SERVER] getShopCategory error:', error); + socket.emit('shopCategoryReceived', { success: false, error: 'Failed to load category items' }); } }); socket.on('getItemDetails', (data) => { - const { itemId } = data; - + const { itemId } = data || {}; if (!itemId) { - socket.emit('itemDetailsReceived', { - success: false, - error: 'Item ID required' - }); + socket.emit('itemDetailsReceived', { success: false, error: 'Item ID required' }); return; } - - try { - const item = itemSystem.getItem(itemId); - - if (!item) { - socket.emit('itemDetailsReceived', { - success: false, - error: 'Item not found' - }); - return; - } - - socket.emit('itemDetailsReceived', { - success: true, - item: item - }); - } catch (error) { - console.error('[GAME SERVER] Error getting item details:', error); - socket.emit('itemDetailsReceived', { - success: false, - error: 'Failed to get item details' - }); - } + socket.emit('itemDetailsReceived', itemSystem.buildItemDetailResponse(itemId)); }); // Game-specific events @@ -729,211 +669,152 @@ io.on('connection', (socket) => { // Shop purchase events socket.on('purchaseItem', async (data) => { - console.log('[GAME SERVER] Processing purchase request:', socket.id, data); - const clientData = connectedClients.get(socket.id); - if (!clientData || !clientData.userId) { - console.log('[GAME SERVER] Purchase failed - not authenticated'); + if (!clientData?.userId) { socket.emit('purchaseCompleted', { success: false, error: 'Not authenticated' }); return; } - + try { - const { itemId, quantity = 1 } = data; - - console.log('[GAME SERVER] Purchase details:', { - itemId: itemId, - quantity: quantity, - userId: clientData.userId, - username: clientData.username - }); - + const { itemId, quantity = 1 } = data || {}; if (!itemId) { - console.log('[GAME SERVER] Purchase failed - no item ID'); socket.emit('purchaseCompleted', { success: false, error: 'Item ID required' }); return; } - - // Load current player data - console.log('[GAME SERVER] Loading player data for purchase:', { - userId: clientData.userId, - username: clientData.username - }); - + const playerData = await loadPlayerData(clientData.userId, clientData.username || 'Player'); if (!playerData) { - console.log('[GAME SERVER] Purchase failed - could not load player data'); socket.emit('purchaseCompleted', { success: false, error: 'Failed to load player data' }); return; } - console.log('[GAME SERVER] Player data loaded:', { - username: playerData.username, - credits: playerData.stats.credits, - gems: playerData.stats.gems - }); - - // Find the item in shop (server-side validation) - const item = findShopItem(itemId); + + // Server-side validation: item must exist and be in the shop + const item = itemSystem.findShopItem(itemId); if (!item) { - console.log('[GAME SERVER] Purchase failed - item not found:', itemId); socket.emit('purchaseCompleted', { success: false, error: 'Item not found in shop' }); return; } - - console.log('[GAME SERVER] Item found:', { - id: item.id, - name: item.name, - type: item.type, - price: item.price, - currency: item.currency - }); - - // Calculate total cost - const totalCost = item.price * quantity; - const currency = item.currency; - - console.log('[GAME SERVER] Cost calculation:', { - unitPrice: item.price, - quantity: quantity, - totalCost: totalCost, - currency: currency - }); - - // Check if player can afford - if (currency === 'credits' && (playerData.stats.credits || 0) < totalCost) { - console.log('[GAME SERVER] Purchase failed - insufficient credits:', { - required: totalCost, - current: playerData.stats.credits || 0 - }); - socket.emit('purchaseCompleted', { success: false, error: 'Not enough credits' }); - return; - } - - if (currency === 'gems' && (playerData.stats.gems || 0) < totalCost) { - console.log('[GAME SERVER] Purchase failed - insufficient gems:', { - required: totalCost, - current: playerData.stats.gems || 0 - }); - socket.emit('purchaseCompleted', { success: false, error: 'Not enough gems' }); - return; - } - - // Check if already owns this cosmetic - if (item.type === 'cosmetic' && playerData.ownedCosmetics && playerData.ownedCosmetics.includes(item.id)) { - console.log('[GAME SERVER] Purchase failed - already owns cosmetic:', item.id); + + // Block re-purchase of already-owned cosmetics and decorations + if (item.type === 'cosmetic' && playerData.ownedCosmetics?.includes(item.id)) { socket.emit('purchaseCompleted', { success: false, error: 'You already own this cosmetic' }); return; } - - // Process payment - console.log('[GAME SERVER] Processing payment...'); - if (currency === 'credits') { - playerData.stats.credits = (playerData.stats.credits || 0) - totalCost; - } else if (currency === 'gems') { - playerData.stats.gems = (playerData.stats.gems || 0) - totalCost; - } - - console.log('[GAME SERVER] Payment processed, new balance:', { - credits: playerData.stats.credits, - gems: playerData.stats.gems - }); - - // Give item based on type - console.log('[GAME SERVER] Adding item to inventory, type:', item.type); - switch (item.type) { - case 'ship': - if (!playerData.ownedShips) playerData.ownedShips = []; - if (!playerData.ownedShips.includes(item.id)) { - playerData.ownedShips.push(item.id); - console.log('[GAME SERVER] Ship added to owned ships:', item.id); - } else { - console.log('[GAME SERVER] Ship already owned:', item.id); - } - break; - - case 'cosmetic': - if (!playerData.ownedCosmetics) playerData.ownedCosmetics = []; - if (!playerData.ownedCosmetics.includes(item.id)) { - playerData.ownedCosmetics.push(item.id); - console.log('[GAME SERVER] Cosmetic added to owned cosmetics:', item.id); - } else { - console.log('[GAME SERVER] Cosmetic already owned:', item.id); - } - break; - - case 'consumable': - if (!playerData.inventory) playerData.inventory = { items: [] }; - playerData.inventory.items.push({ - id: item.id, - name: item.name, - type: item.type, - quantity: quantity, - acquired: new Date().toISOString() - }); - console.log('[GAME SERVER] Consumable added to inventory:', item.name); - break; - - case 'material': - if (!playerData.inventory) playerData.inventory = { items: [] }; - playerData.inventory.items.push({ - id: item.id, - name: item.name, - type: item.type, - quantity: quantity, - acquired: new Date().toISOString() - }); - console.log('[GAME SERVER] Material added to inventory:', item.name); - break; - - default: - console.warn(`[GAME SERVER] Unknown item type: ${item.type}`); - socket.emit('purchaseCompleted', { success: false, error: 'Unknown item type' }); + if (item.type === 'decoration') { + const sb = playerData.starbase || {}; + if (item.subtype === 'wallpaper' && sb.ownedWallpapers?.includes(item.id)) { + socket.emit('purchaseCompleted', { success: false, error: 'You already own this wallpaper' }); return; + } + if (item.subtype === 'room_unlock' && item.roomId && sb.unlockedRooms?.includes(item.roomId)) { + socket.emit('purchaseCompleted', { success: false, error: 'You already own this room' }); + return; + } } - - // Save updated player data - console.log('[GAME SERVER] Saving player data...'); + + // Validate affordability + const validation = itemSystem.validatePurchase(item, playerData.stats, quantity); + if (!validation.valid) { + socket.emit('purchaseCompleted', { success: false, error: validation.error }); + return; + } + + // Apply purchase (deducts currency + grants item in playerData) + const purchaseSummary = itemSystem.applyPurchase(item, playerData, quantity); + await savePlayerData(clientData.userId, playerData); - - // Update client data with new values clientData.playerData = playerData; - - // Send success response - const response = { - success: true, - item: { - id: item.id, - name: item.name, - type: item.type, - rarity: item.rarity, - description: item.description, - price: item.price, - currency: item.currency, - quantity: quantity, - stats: item.stats || {}, - texturePath: item.texturePath || item.texture - }, - quantity: quantity, - totalCost: totalCost, - currency: currency, - newBalance: currency === 'credits' ? playerData.stats.credits : playerData.stats.gems - }; - - console.log('[GAME SERVER] Sending success response:', response); - socket.emit('purchaseCompleted', response); - - console.log(`[GAME SERVER] Purchase completed for ${clientData.username}: ${item.name} x${quantity} for ${totalCost} ${currency}`); - - // Broadcast economy update to client + + socket.emit('purchaseCompleted', { + success: true, + item: itemSystem.buildItemDetailResponse(item.id).item, + quantity, + totalCost: purchaseSummary.totalCost, + currency: purchaseSummary.currency, + newBalance: purchaseSummary.newBalance + }); + + console.log(`[GAME SERVER] Purchase: ${clientData.username} bought ${item.name} x${quantity} for ${purchaseSummary.totalCost} ${purchaseSummary.currency}`); broadcastEconomyUpdate(socket.id); - + } catch (error) { - console.error('[GAME SERVER] Error processing purchase:', error); + console.error('[GAME SERVER] purchaseItem error:', error); socket.emit('purchaseCompleted', { success: false, error: 'Purchase failed: ' + error.message }); } }); - - // Dungeon System Packet Handlers + + // ── Starbase customisation ────────────────────────────────────────────── + socket.on('get_starbase_data', async () => { + const client = connectedClients.get(socket.id); + if (!client?.userId) { socket.emit('starbase_data', { success: false }); return; } + try { + const pd = await loadPlayerData(client.userId, client.username || 'Player'); + const sb = pd?.starbase || { wallpaper: null, ownedWallpapers: [], unlockedRooms: [] }; + const catalog = itemSystem.getAllItems().filter(i => i.type === 'decoration'); + socket.emit('starbase_data', { success: true, starbase: sb, catalog }); + } catch (err) { + console.error('[GAME SERVER] get_starbase_data error:', err); + socket.emit('starbase_data', { success: false, error: err.message }); + } + }); + + socket.on('set_wallpaper', async (data) => { + const { wallpaperId, roomId } = data || {}; + const client = connectedClients.get(socket.id); + if (!client?.userId) { socket.emit('wallpaper_set', { success: false, error: 'Not authenticated' }); return; } + try { + const pd = await loadPlayerData(client.userId, client.username || 'Player'); + if (!pd) { socket.emit('wallpaper_set', { success: false, error: 'Player not found' }); return; } + if (!pd.starbase) pd.starbase = { wallpaper: null, ownedWallpapers: [], unlockedRooms: [] }; + if (wallpaperId === null) { + if (roomId) { + if (!pd.starbase.roomWallpapers) pd.starbase.roomWallpapers = {}; + delete pd.starbase.roomWallpapers[roomId]; + } else { + pd.starbase.wallpaper = null; + } + } else { + if (!pd.starbase.ownedWallpapers?.includes(wallpaperId)) { + socket.emit('wallpaper_set', { success: false, error: 'Wallpaper not owned' }); return; + } + if (roomId) { + if (!pd.starbase.roomWallpapers) pd.starbase.roomWallpapers = {}; + pd.starbase.roomWallpapers[roomId] = wallpaperId; + } else { + pd.starbase.wallpaper = wallpaperId; + } + } + await savePlayerData(client.userId, pd); + client.playerData = pd; + socket.emit('wallpaper_set', { success: true, starbase: pd.starbase }); + } catch (err) { + console.error('[GAME SERVER] set_wallpaper error:', err); + socket.emit('wallpaper_set', { success: false, error: err.message }); + } + }); + + socket.on('get_skills', () => { + console.log('[GAME SERVER] Sending skill definitions to:', socket.id); + try { + const skills = skillSystem.getAllSkills(); + socket.emit('skills_data', skills); + } catch (error) { + console.error('[GAME SERVER] Error sending skills:', error); + socket.emit('skills_data', []); + } + }); + + socket.on('get_recipes', () => { + console.log('[GAME SERVER] Sending crafting recipes to:', socket.id); + try { + const recipes = craftingSystem.getAllRecipes(); + socket.emit('recipes_data', recipes); + } catch (error) { + console.error('[GAME SERVER] Error sending recipes:', error); + socket.emit('recipes_data', []); + } + }); + socket.on('get_dungeons', () => { console.log('[GAME SERVER] Sending dungeons data to:', socket.id); const dungeons = dungeonSystem.getDungeonsGroupedByDifficulty(); @@ -941,9 +822,11 @@ io.on('connection', (socket) => { }); socket.on('get_enemy_templates', () => { - console.log('[GAME SERVER] Sending enemy templates to:', socket.id); - const enemyTemplates = dungeonSystem.getEnemyTemplates(); - socket.emit('enemy_templates_data', enemyTemplates); + // Client expects { [id]: enemyTemplate } — convert array to keyed object + const arr = dungeonSystem.getEnemyTemplates(); + const templates = {}; + for (const e of arr) templates[e.id] = e; + socket.emit('enemy_templates_data', templates); }); // Economy System Packet Handlers @@ -972,6 +855,124 @@ io.on('connection', (socket) => { } } + // ════════════════════════════════════════════════════════════════════════ + // BUILDINGS / BASE HANDLERS + // ════════════════════════════════════════════════════════════════════════ + + // Building definitions (in-memory; extend to JSON data later) + const BUILDING_DEFS = { + command_center: { name:'Command Center', maxLevel:20, baseCost:{credits:0}, timeBase:60, effects:{buildSlots:1}, icon:'fa-satellite-dish', description:'Gates all other building levels.' }, + mining_facility: { name:'Mining Facility', maxLevel:20, baseCost:{credits:500}, timeBase:90, effects:{metalPerHr:100}, icon:'fa-industry', description:'+15% metal/hr per level.' }, + gas_extractor: { name:'Gas Extractor', maxLevel:20, baseCost:{credits:600}, timeBase:90, effects:{gasPerHr:80}, icon:'fa-cloud', description:'+15% gas/hr per level.' }, + power_reactor: { name:'Power Reactor', maxLevel:20, baseCost:{credits:800}, timeBase:120, effects:{energyPerHr:200}, icon:'fa-bolt', description:'+200 energy/hr per level.' }, + shipyard: { name:'Shipyard', maxLevel:15, baseCost:{credits:2000}, timeBase:180, effects:{buildSpeed:10}, icon:'fa-anchor', description:'Build ships; each level +10% speed.' }, + research_lab: { name:'Research Lab', maxLevel:20, baseCost:{credits:1500}, timeBase:150, effects:{researchSpeed:8}, icon:'fa-flask', description:'+8% research speed per level.' }, + defense_platform: { name:'Defense Platform', maxLevel:10, baseCost:{credits:1000}, timeBase:120, effects:{baseDPS:50}, icon:'fa-shield-alt', description:'+50 auto-defense DPS per level.' }, + storage_depot: { name:'Storage Depot', maxLevel:15, baseCost:{credits:400}, timeBase:60, effects:{storageBonus:2000}, icon:'fa-warehouse', description:'+2000 storage cap per level.' }, + sensor_array: { name:'Sensor Array', maxLevel:10, baseCost:{credits:700}, timeBase:90, effects:{sensorRange:1}, icon:'fa-broadcast-tower',description:'Reveals sectors; level 7+ enables interdiction.' }, + hangar_bay: { name:'Hangar Bay', maxLevel:10, baseCost:{credits:3000}, timeBase:200, effects:{fleetSlots:1}, icon:'fa-shuttle-space', description:'Level 2/4/7/10 unlock extra fleet slots.' }, + trade_port: { name:'Trade Port', maxLevel:10, baseCost:{credits:2500}, timeBase:180, effects:{tradeIncome:5}, icon:'fa-ship', description:'+5% market income per level.' }, + shield_generator: { name:'Shield Generator', maxLevel:10, baseCost:{credits:4000}, timeBase:240, effects:{shieldDuration:8}, icon:'fa-circle-notch', description:'Deployable base shield +8hrs per level.' }, + crystal_refinery: { name:'Crystal Refinery', maxLevel:20, baseCost:{credits:900}, timeBase:100, effects:{crystalPerHr:60}, icon:'fa-gem', description:'+12% crystal/hr per level.' }, + }; + + function getBuildingCost(defId, currentLevel) { + const def = BUILDING_DEFS[defId]; + if (!def) return null; + const mult = Math.pow(1.6, currentLevel); // exponential cost scaling + return { credits: Math.floor((def.baseCost.credits || 0) * mult) }; + } + + function getBuildingBuildTime(defId, currentLevel) { + const def = BUILDING_DEFS[defId]; + if (!def) return 60; + return Math.floor(def.timeBase * Math.pow(1.8, currentLevel)); + } + + socket.on('get_base_data', async () => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return socket.emit('base_data', { success: false, error: 'Not authenticated' }); + const pd = client.playerData; + // Init buildings if missing + if (!pd.buildings) pd.buildings = { command_center: { level: 1, buildQueue: null } }; + // Check for completed builds + const now = Date.now(); + let changed = false; + for (const [id, bld] of Object.entries(pd.buildings)) { + if (bld.buildQueue && now >= bld.buildQueue.completesAt) { + bld.level = (bld.level || 1) + 1; + bld.buildQueue = null; + changed = true; + socket.emit('building_upgraded', { success: true, buildingId: id, newLevel: bld.level }); + } + } + if (changed) await savePlayerData(pd.userId, pd); + const buildings = Object.entries(pd.buildings).map(([id, bld]) => { + const def = BUILDING_DEFS[id] || {}; + const nextCost = getBuildingCost(id, bld.level || 1); + const nextTime = getBuildingBuildTime(id, bld.level || 1); + return { id, name: def.name || id, level: bld.level || 1, maxLevel: def.maxLevel || 10, + icon: def.icon || 'fa-building', description: def.description || '', + buildQueue: bld.buildQueue || null, nextCost, nextTime, effects: def.effects || {} }; + }); + // Available to build (not yet built) + const available = Object.entries(BUILDING_DEFS) + .filter(([id]) => !pd.buildings[id]) + .map(([id, def]) => ({ id, name: def.name, icon: def.icon, description: def.description, + maxLevel: def.maxLevel, effects: def.effects, cost: getBuildingCost(id, 0), buildTime: getBuildingBuildTime(id, 0) })); + socket.emit('base_data', { success: true, buildings, available }); + } catch (err) { + console.error('[BASE] get_base_data error:', err); + socket.emit('base_data', { success: false, error: err.message }); + } + }); + + socket.on('construct_building', async ({ buildingId }) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const pd = client.playerData; + if (!pd.buildings) pd.buildings = {}; + if (pd.buildings[buildingId]) return socket.emit('building_constructed', { success: false, error: 'Already built' }); + const def = BUILDING_DEFS[buildingId]; + if (!def) return socket.emit('building_constructed', { success: false, error: 'Unknown building' }); + const cost = getBuildingCost(buildingId, 0); + if ((pd.stats.credits || 0) < cost.credits) return socket.emit('building_constructed', { success: false, error: `Need ${cost.credits} credits` }); + pd.stats.credits -= cost.credits; + const buildTime = getBuildingBuildTime(buildingId, 0) * 1000; + pd.buildings[buildingId] = { level: 0, buildQueue: { startedAt: Date.now(), completesAt: Date.now() + buildTime } }; + await savePlayerData(pd.userId, pd); + socket.emit('building_constructed', { success: true, buildingId, completesAt: Date.now() + buildTime }); + socket.emit('economy_data', { credits: pd.stats.credits, gems: pd.stats.gems }); + } catch (err) { + socket.emit('building_constructed', { success: false, error: err.message }); + } + }); + + socket.on('upgrade_building', async ({ buildingId }) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const pd = client.playerData; + const bld = pd.buildings?.[buildingId]; + if (!bld) return socket.emit('building_upgraded', { success: false, error: 'Building not found' }); + if (bld.buildQueue) return socket.emit('building_upgraded', { success: false, error: 'Already building' }); + const def = BUILDING_DEFS[buildingId]; + if (bld.level >= (def?.maxLevel || 10)) return socket.emit('building_upgraded', { success: false, error: 'Already max level' }); + const cost = getBuildingCost(buildingId, bld.level); + if ((pd.stats.credits || 0) < cost.credits) return socket.emit('building_upgraded', { success: false, error: `Need ${cost.credits} credits` }); + pd.stats.credits -= cost.credits; + const buildTime = getBuildingBuildTime(buildingId, bld.level) * 1000; + bld.buildQueue = { startedAt: Date.now(), completesAt: Date.now() + buildTime }; + await savePlayerData(pd.userId, pd); + socket.emit('building_upgraded', { success: true, buildingId, completesAt: Date.now() + buildTime }); + socket.emit('economy_data', { credits: pd.stats.credits, gems: pd.stats.gems }); + } catch (err) { + socket.emit('building_upgraded', { success: false, error: err.message }); + } + }); + socket.on('start_dungeon', (data) => { console.log('[GAME SERVER] Starting dungeon for:', socket.id, data); try { @@ -1031,11 +1032,31 @@ io.on('connection', (socket) => { } }); - socket.on('complete_dungeon', (data) => { + socket.on('complete_dungeon', async (data) => { console.log('[GAME SERVER] Completing dungeon for:', socket.id, data); try { const { instanceId, userId } = data; + const client = connectedClients.get(socket.id); const result = dungeonSystem.completeDungeon(instanceId); + + // Grant item rewards to player inventory/starbase + if (result.success && result.rewards?.length && client?.userId) { + try { + const playerData = await loadPlayerData(client.userId, client.username || 'Player'); + if (playerData) { + for (const reward of result.rewards) { + const item = itemSystem.getItem(reward.itemId); + if (!item) continue; + itemSystem.applyPurchase(item, playerData, reward.quantity || 1); + } + await savePlayerData(client.userId, playerData); + client.playerData = playerData; + } + } catch (grantErr) { + console.error('[GAME SERVER] Failed to grant dungeon rewards:', grantErr.message); + } + } + socket.emit('dungeon_completed', { rewards: result }); } catch (error) { console.error('[GAME SERVER] Error completing dungeon:', error); @@ -1044,31 +1065,101 @@ io.on('connection', (socket) => { }); socket.on('get_quests', (data) => { - console.log('[GAME SERVER] Getting quests for:', socket.id, data); + console.log('[GAME SERVER] Getting quests for:', socket.id); try { - // Get quest data from quest system - const questData = questSystem.getPlayerQuests(socket.userId || 'anonymous'); - socket.emit('quests_data', questData); + const userId = connectedClients.get(socket.id)?.userId || 'anonymous'; + const playerQD = questSystem.getPlayerQuests(userId); + + // Build quests with per-player progress merged in + const buildList = (category) => + questSystem.getQuestsByCategory(category).map(q => { + const activeState = playerQD.active[q.id]; + return { + ...q, + status: playerQD.completed.includes(q.id) + ? 'completed' + : activeState + ? 'active' + : (q.prerequisites?.playerLevelMin || 1) <= 1 ? 'available' : 'locked', + objectives: q.objectives.map(obj => ({ + ...obj, + progress: activeState?.objectives?.[obj.id]?.progress ?? 0, + complete: activeState?.objectives?.[obj.id]?.complete ?? false, + })), + }; + }); + + socket.emit('quests_data', { + success: true, + mainQuests: buildList('main_story'), + dailyQuests: buildList('daily'), + weeklyQuests: buildList('weekly'), + monthlyQuests:buildList('monthly'), + playerState: playerQD, + }); } catch (error) { console.error('[GAME SERVER] Error getting quests:', error); socket.emit('quests_data', { success: false, error: error.message }); } }); - // Listen for quest completion events from other systems - io.on('quest_completed', (data) => { - console.log('[GAME SERVER] Quest completion event received:', data); + // ── quest_completed: a system or action triggers a quest completion ──── + // Note: io.on doesn't exist; use a proper socket-level handler instead. + // Clients can emit 'complete_quest' to finish a quest; the server validates & pushes result. + socket.on('complete_quest', async (data) => { + const { questId } = data || {}; + const client = connectedClients.get(socket.id); + if (!client?.userId) { + socket.emit('quest_completed', { success: false, error: 'Not authenticated' }); + return; + } try { - const result = questSystem.completeQuest(data.userId, data.questId, data.rewards); - if (result.success) { - console.log('[GAME SERVER] Quest completed successfully on server:', data.questId); - } else { - console.error('[GAME SERVER] Failed to complete quest:', result.error); + const quest = questSystem.getQuest(questId); + if (!quest) { + socket.emit('quest_completed', { success: false, error: 'Quest not found' }); + return; } - } catch (error) { - console.error('[GAME SERVER] Error processing quest completion:', error); + const result = questSystem.completeQuest(client.userId, questId, quest.rewards); + // Resolve reward amounts into flat numbers for the client + const rewardCredits = quest.rewards + ? quest.rewards.filter(r => r.type === 'coin').reduce((s, r) => s + (r.amount || 0), 0) + : 0; + const rewardMoney = quest.rewards + ? quest.rewards.filter(r => r.type === 'money').reduce((s, r) => s + (r.amount || 0), 0) + : 0; + // Credit the player + grant item rewards + if (client.playerData) { + if (rewardCredits > 0) { + client.playerData.stats.credits = (client.playerData.stats.credits || 0) + rewardCredits; + } + client.playerData.stats.questsCompleted = (client.playerData.stats.questsCompleted || 0) + 1; + + // Grant item rewards (decorations, consumables, etc.) + const itemRewards = quest.rewards?.filter(r => r.type === 'item') || []; + for (const reward of itemRewards) { + const item = itemSystem.getItem(reward.itemId); + if (item) itemSystem.applyPurchase(item, client.playerData, reward.quantity || 1); + } + + await savePlayerData(client.playerData.userId, client.playerData); + } + socket.emit('quest_completed', { + success: true, + questId, + questName: quest.name, + rewards: { credits: rewardCredits, money: rewardMoney, items: quest.rewards?.filter(r => r.type === 'item') || [] }, + }); + // Push updated economy to client + socket.emit('economy_data', { + credits: client.playerData?.stats?.credits || 0, + gems: client.playerData?.stats?.gems || 0, + }); + } catch (err) { + console.error('[GAME SERVER] complete_quest error:', err); + socket.emit('quest_completed', { success: false, error: err.message }); } }); + socket.on('next_room', (data) => { console.log('[GAME SERVER] Moving to next room for:', socket.id, data); @@ -1104,6 +1195,293 @@ io.on('connection', (socket) => { } }); + // ── joinServer: client emits after connecting ────────────────────────── + socket.on('joinServer', (data) => { + const { serverId, userId, username } = data || {}; + console.log(`[GAME SERVER] joinServer from ${username} (${userId}) for server ${serverId}`); + // Join a socket.io room named after the serverId so we can broadcast to it + if (serverId) socket.join(serverId); + const client = connectedClients.get(socket.id); + if (client) { client.serverId = serverId; } + // Notify room that a player joined + socket.to(serverId).emit('playerJoined', { userId, username }); + }); + + // ── getPlayerList ─────────────────────────────────────────────────────── + socket.on('getPlayerList', (data) => { + const { serverId } = data || {}; + const players = []; + for (const [sid, cd] of connectedClients) { + if (cd.serverId === serverId && cd.username) { + players.push({ userId: cd.userId, username: cd.username }); + } + } + socket.emit('playerList', { players }); + }); + + // ── chatMessage: relay to everyone in the server room ────────────────── + socket.on('chatMessage', (data) => { + const client = connectedClients.get(socket.id); + const serverId = client?.serverId; + const payload = { + userId: client?.userId || data.userId, + username: client?.username || data.username || 'Unknown', + message: data.message || '', + timestamp: Date.now(), + }; + if (serverId) { + io.to(serverId).emit('chatMessage', payload); + } else { + socket.emit('chatMessage', payload); // echo back to sender only + } + }); + + // ── updatePlayerStats: client-reported stat delta (server validates) ──── + socket.on('updatePlayerStats', async (data) => { + const client = connectedClients.get(socket.id); + if (!client?.playerData) { + socket.emit('player_stat_update', { success: false, error: 'Not authenticated' }); + return; + } + try { + const pd = client.playerData; + // Only trust non-economy fields from client; economy is server-authoritative + const allowed = ['playTime']; + for (const key of allowed) { + if (data.playerStats?.[key] !== undefined) { + pd.stats[key] = data.playerStats[key]; + } + } + await savePlayerData(pd.userId, pd); + socket.emit('player_stat_update', { success: true, stats: pd.stats }); + } catch (err) { + console.error('[GAME SERVER] updatePlayerStats error:', err); + socket.emit('player_stat_update', { success: false, error: err.message }); + } + }); + + // ── exit_dungeon: player voluntarily leaves a dungeon ────────────────── + socket.on('exit_dungeon', (data) => { + console.log('[GAME SERVER] exit_dungeon for:', socket.id); + try { + const { instanceId, userId } = data || {}; + if (instanceId) dungeonSystem.abandonInstance(instanceId); + socket.emit('dungeon_exited', { success: true }); + } catch (err) { + console.error('[GAME SERVER] exit_dungeon error:', err); + socket.emit('dungeon_exited', { success: false, error: err.message }); + } + }); + + // ── get_room_types: dungeon room type definitions ─────────────────────── + socket.on('get_room_types', () => { + socket.emit('room_types_data', dungeonSystem.roomTypes || {}); + }); + + // ════════════════════════════════════════════════════════════════════════ + // LEADERBOARD HANDLER + // ════════════════════════════════════════════════════════════════════════ + + socket.on('get_leaderboard', async ({ category = 'level' } = {}) => { + try { + const VALID_CATEGORIES = ['level', 'credits', 'questsCompleted', 'dungeonsCleared', 'totalKills']; + if (!VALID_CATEGORIES.includes(category)) { + return socket.emit('leaderboard_data', { success: false, error: 'Invalid category' }); + } + + // Query top 20 players for the requested stat + const sortField = `stats.${category}`; + const players = await PlayerData.find( + { [`stats.${category}`]: { $exists: true, $gt: 0 } }, + { 'stats.username': 1, username: 1, [`stats.${category}`]: 1, 'stats.level': 1, updatedAt: 1 } + ) + .sort({ [sortField]: -1 }) + .limit(20) + .lean(); + + const myUserId = connectedClients.get(socket.id)?.userId; + const entries = players.map((p, i) => ({ + rank: i + 1, + username: p.username || p.stats?.username || 'Unknown Commander', + value: p.stats?.[category] || 0, + level: p.stats?.level || 1, + isMe: p.userId === myUserId || p._id?.toString() === myUserId, + })); + + socket.emit('leaderboard_data', { success: true, category, entries }); + } catch (err) { + console.error('[LEADERBOARD] get_leaderboard error:', err); + socket.emit('leaderboard_data', { success: false, error: err.message }); + } + }); + + // ════════════════════════════════════════════════════════════════════════ + // FLEET HANDLERS + // ════════════════════════════════════════════════════════════════════════ + + socket.on('get_fleet_data', async () => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return socket.emit('fleet_data', { success: false, error: 'Not authenticated' }); + // Auto-give starter ship if inventory has no ships + const ships = (client.playerData.inventory || []).filter(i => i.type === 'ship'); + if (ships.length === 0) { + const starterShip = { + id: `ship_starter_${Date.now()}`, + name: 'Starter Cruiser', + type: 'ship', + rarity: 'common', + texture: 'assets/gso/textures/ships/starter_cruiser_common.png', + stats: { attack: 10, defense: 5, speed: 10, hull: 100, maxHull: 100, currentHull: 100 }, + level: 1, equipped: false, + }; + client.playerData.inventory = client.playerData.inventory || []; + client.playerData.inventory.push(starterShip); + client.playerData.stats.activeShipId = starterShip.id; + await savePlayerData(client.playerData.userId, client.playerData); + } + const fleetData = fleetSystem.getFleetData(client.playerData); + const templates = fleetSystem.getAllShipTemplates(); + socket.emit('fleet_data', { success: true, ...fleetData, templates }); + } catch (err) { + console.error('[FLEET] get_fleet_data error:', err); + socket.emit('fleet_data', { success: false, error: err.message }); + } + }); + + socket.on('set_active_ship', async ({ shipId }) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const stats = fleetSystem.setActiveShip(client.playerData, shipId); + await savePlayerData(client.playerData.userId, client.playerData); + socket.emit('active_ship_set', { success: true, shipId, stats }); + } catch (err) { + socket.emit('active_ship_set', { success: false, error: err.message }); + } + }); + + socket.on('repair_ship', async ({ shipId }) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const credits = client.playerData.stats?.credits || 0; + const result = fleetSystem.repairShip(client.playerData, shipId, credits); + client.playerData.stats.credits -= result.repairCost; + await savePlayerData(client.playerData.userId, client.playerData); + socket.emit('ship_repaired', { success: true, shipId, repairCost: result.repairCost }); + socket.emit('economy_data', { credits: client.playerData.stats.credits, gems: client.playerData.stats.gems }); + } catch (err) { + socket.emit('ship_repaired', { success: false, error: err.message }); + } + }); + + // ════════════════════════════════════════════════════════════════════════ + // GALAXY MAP HANDLERS + // ════════════════════════════════════════════════════════════════════════ + + socket.on('get_galaxy_map', async () => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + // Seed home sector if first time + if (!client.playerData.homeSector) { + client.playerData.homeSector = '15_10'; + client.playerData.exploredSectors = ['15_10']; + await savePlayerData(client.playerData.userId, client.playerData); + } + const visibleSectors = galaxySystem.getVisibleSectors(client.playerData); + socket.emit('galaxy_map_data', { + success: true, + sectors: visibleSectors, + homeSector: client.playerData.homeSector, + gridW: galaxySystem.GRID_W, + gridH: galaxySystem.GRID_H, + }); + } catch (err) { + console.error('[GALAXY] get_galaxy_map error:', err); + socket.emit('galaxy_map_data', { success: false, error: err.message }); + } + }); + + socket.on('explore_sector', async ({ sectorId }) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const sector = galaxySystem.exploreSector(sectorId, client.playerData); + await savePlayerData(client.playerData.userId, client.playerData); + // XP reward for first explore + const xpGain = 50 + sector.threat * 10; + client.playerData.stats.experience = (client.playerData.stats.experience || 0) + xpGain; + socket.emit('sector_explored', { success: true, sector, xpGain }); + // Push updated visible sectors + const visibleSectors = galaxySystem.getVisibleSectors(client.playerData); + socket.emit('galaxy_map_data', { + success: true, + sectors: visibleSectors, + homeSector: client.playerData.homeSector, + gridW: galaxySystem.GRID_W, + gridH: galaxySystem.GRID_H, + }); + } catch (err) { + socket.emit('sector_explored', { success: false, error: err.message }); + } + }); + + // ════════════════════════════════════════════════════════════════════════ + // RESEARCH HANDLERS + // ════════════════════════════════════════════════════════════════════════ + + socket.on('get_research_data', async () => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + // Check for completed research first + const justCompleted = researchSystem.checkCompletion(client.playerData); + if (justCompleted) { + await savePlayerData(client.playerData.userId, client.playerData); + socket.emit('research_completed', { tech: justCompleted }); + } + const available = researchSystem.getAvailableResearch(client.playerData); + socket.emit('research_data', { + success: true, + research: available, + inProgress: client.playerData.research?.inProgress || null, + completed: client.playerData.research?.completed || [], + effects: client.playerData.research?.effects || {}, + }); + } catch (err) { + console.error('[RESEARCH] get_research_data error:', err); + socket.emit('research_data', { success: false, error: err.message }); + } + }); + + socket.on('start_research', async ({ techId }) => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const result = researchSystem.startResearch(client.playerData, techId); + await savePlayerData(client.playerData.userId, client.playerData); + socket.emit('research_started', { success: true, tech: result.tech, completesAt: result.completesAt }); + socket.emit('economy_data', { credits: client.playerData.stats.credits, gems: client.playerData.stats.gems }); + } catch (err) { + socket.emit('research_started', { success: false, error: err.message }); + } + }); + + socket.on('cancel_research', async () => { + try { + const client = connectedClients.get(socket.id); + if (!client?.playerData) return; + const tech = researchSystem.cancelResearch(client.playerData); + await savePlayerData(client.playerData.userId, client.playerData); + socket.emit('research_cancelled', { success: true, tech }); + socket.emit('economy_data', { credits: client.playerData.stats.credits, gems: client.playerData.stats.gems }); + } catch (err) { + socket.emit('research_cancelled', { success: false, error: err.message }); + } + }); + socket.on('disconnect', async () => { console.log('[GAME SERVER] Client disconnected:', socket.id); diff --git a/GameServer/systems/ContentLoader.js b/GameServer/systems/ContentLoader.js new file mode 100644 index 0000000..76c73a8 --- /dev/null +++ b/GameServer/systems/ContentLoader.js @@ -0,0 +1,305 @@ +/** + * Galaxy Strike Online — ContentLoader + * Single source of truth for all game content. + * Reads from data/gso/ JSON files at startup; never hardcodes data. + * + * Usage: + * const loader = new ContentLoader(); + * await loader.load(); + * const skills = loader.getAllSkills(); + * const recipes = loader.getAllRecipes(); + * // etc. + */ + +const fs = require('fs'); +const path = require('path'); + +const DATA_ROOT = path.join(__dirname, '..', 'data', 'gso'); + +class ContentLoader { + constructor() { + // Raw maps — populated by load() + this.skills = new Map(); // id -> skill + this.enemies = new Map(); // id -> enemy + this.dungeons = new Map(); // id -> dungeon + this.recipes = new Map(); // id -> recipe + this.items = new Map(); // id -> item + this.quests = new Map(); // id -> quest + this.craftingTabs = []; + this.questCategories = new Map(); // category -> category index + + this._loaded = false; + } + + // ───────────────────────────────────────────────────────────────── + // Public bootstrap + // ───────────────────────────────────────────────────────────────── + + async load() { + if (this._loaded) return; + console.log('[CONTENT LOADER] Loading all game content from data/gso/…'); + + this._loadSkills(); + this._loadEnemies(); + this._loadDungeons(); + this._loadItems(); + this._loadRecipes(); + this._loadQuests(); + + this._loaded = true; + + console.log('[CONTENT LOADER] Done.'); + console.log(` Skills: ${this.skills.size}`); + console.log(` Enemies: ${this.enemies.size}`); + console.log(` Dungeons: ${this.dungeons.size}`); + console.log(` Items: ${this.items.size}`); + console.log(` Recipes: ${this.recipes.size}`); + console.log(` Quests: ${this.quests.size}`); + } + + // ───────────────────────────────────────────────────────────────── + // Skills + // ───────────────────────────────────────────────────────────────── + + _loadSkills() { + const root = path.join(DATA_ROOT, 'skills'); + for (const cat of this._subdirs(root)) { + for (const file of this._jsonFiles(path.join(root, cat))) { + const skill = this._readJson(path.join(root, cat, file)); + if (skill && skill.id) { + this.skills.set(skill.id, skill); + } + } + } + } + + getAllSkills() { return Array.from(this.skills.values()); } + getSkill(id) { return this.skills.get(id) || null; } + getSkillsByCategory(cat) { return this.getAllSkills().filter(s => s.category === cat); } + + // ───────────────────────────────────────────────────────────────── + // Enemies + // ───────────────────────────────────────────────────────────────── + + _loadEnemies() { + const root = path.join(DATA_ROOT, 'enemies'); + for (const file of this._jsonFiles(root)) { + const enemy = this._readJson(path.join(root, file)); + if (enemy && enemy.id) this.enemies.set(enemy.id, enemy); + } + } + + getAllEnemies() { return Array.from(this.enemies.values()); } + getEnemy(id) { return this.enemies.get(id) || null; } + getEnemiesByRarity(r) { return this.getAllEnemies().filter(e => e.rarity === r); } + + // ───────────────────────────────────────────────────────────────── + // Dungeons + // ───────────────────────────────────────────────────────────────── + + _loadDungeons() { + const root = path.join(DATA_ROOT, 'dungeons'); + for (const file of this._jsonFiles(root)) { + const dungeon = this._readJson(path.join(root, file)); + if (dungeon && dungeon.id) this.dungeons.set(dungeon.id, dungeon); + } + } + + getAllDungeons() { return Array.from(this.dungeons.values()); } + getDungeon(id) { return this.dungeons.get(id) || null; } + getDungeonsByDifficulty(d) { return this.getAllDungeons().filter(dn => dn.difficulty === d); } + getDungeonsGroupedByDifficulty() { + const order = ['easy','medium','hard','extreme']; + const out = {}; + for (const dn of this.getAllDungeons()) { + if (!out[dn.difficulty]) out[dn.difficulty] = []; + out[dn.difficulty].push(dn); + } + return out; + } + getAvailableDungeons(playerLevel = 1) { + return this.getAllDungeons().filter(d => d.minLevel <= playerLevel); + } + + // ───────────────────────────────────────────────────────────────── + // Items — multi-type, multi-subdir + // ───────────────────────────────────────────────────────────────── + + _loadItems() { + const root = path.join(DATA_ROOT, 'items'); + this._walkJsonDir(root, (filePath) => { + const data = this._readJson(filePath); + if (!data || !data.templates) return; + + // Each file has { templates: { : { ...itemData } } } + for (const [type, itemData] of Object.entries(data.templates)) { + if (itemData && itemData.id) { + this.items.set(itemData.id, { ...itemData, type }); + } + } + }); + } + + getAllItems() { return Array.from(this.items.values()); } + getItem(id) { return this.items.get(id) || null; } + getItemsByType(type) { return this.getAllItems().filter(i => i.type === type); } + getShopItems() { + return this.getAllItems().filter(i => + Array.isArray(i.categories) && i.categories.includes('shop') + ); + } + getShopItemsByCategory() { + const out = {}; + for (const item of this.getShopItems()) { + const t = item.type; + if (!out[t]) out[t] = []; + out[t].push(item); + } + return out; + } + + // ───────────────────────────────────────────────────────────────── + // Recipes + // ───────────────────────────────────────────────────────────────── + + _loadRecipes() { + const root = path.join(DATA_ROOT, 'recipes'); + + // crafting_tabs.json + const tabsFile = path.join(root, 'crafting_tabs.json'); + if (fs.existsSync(tabsFile)) { + this.craftingTabs = this._readJson(tabsFile)?.craftingTypes || []; + } + + // One JSON per recipe, grouped into subdirs + this._walkJsonDir(root, (filePath) => { + if (filePath.endsWith('crafting_tabs.json')) return; + if (filePath.endsWith('manifest.json')) return; + + const data = this._readJson(filePath); + if (!data || !data.recipe) return; + + // Derive a stable ID from the craft.id field or the path + const id = data.craft?.id || data.path?.replace(/\//g, ':') || path.basename(filePath, '.json'); + + // Normalise inputs → array format expected by client + const inputs = data.recipe.inputs || {}; + const materials = Object.entries(inputs).map(([itemId, qty]) => ({ id: itemId, quantity: qty })); + + // Output item id + const outputObj = data.recipe.output || {}; + const outputId = Object.keys(outputObj)[0] || null; + const outputQty = outputId ? outputObj[outputId] : 0; + + // Time — any *_time_seconds key + const timeKey = Object.keys(data.recipe).find(k => k.endsWith('_time_seconds')); + const craftingTime = timeKey ? (data.recipe[timeKey] * 1000) : 5000; + + this.recipes.set(id, { + id, + path: data.path || '', + name: outputId ? this._itemName(outputId) : id, + type: data.craft?.type || 'misc', + category: data.craft?.type || 'misc', + subCategory: data.craft?.subCategory || 'misc', + craftId: data.craft?.id || id, + materials, + output: outputId, + outputQty, + craftingTime, + experience: Math.round(craftingTime / 200), // scaled default + requirements: {}, + unlocked: false, + rawRecipe: data.recipe + }); + }); + } + + getAllRecipes() { return Array.from(this.recipes.values()); } + getRecipe(id) { return this.recipes.get(id) || null; } + getRecipesByType(type) { return this.getAllRecipes().filter(r => r.type === type); } + getRecipesByCategory(cat) { return this.getAllRecipes().filter(r => r.category === cat); } + getCraftingTabs() { return this.craftingTabs; } + + // ───────────────────────────────────────────────────────────────── + // Quests + // ───────────────────────────────────────────────────────────────── + + _loadQuests() { + const root = path.join(DATA_ROOT, 'quests'); + for (const cat of this._subdirs(root)) { + for (const file of this._jsonFiles(path.join(root, cat))) { + const quest = this._readJson(path.join(root, cat, file)); + if (quest && quest.id) this.quests.set(quest.id, quest); + } + } + + // Category index files + const catRoot = path.join(DATA_ROOT, 'category', 'quests'); + if (fs.existsSync(catRoot)) { + for (const file of this._jsonFiles(catRoot)) { + const cat = this._readJson(path.join(catRoot, file)); + if (cat && cat.tabId) this.questCategories.set(cat.tabId, cat); + } + } + } + + getAllQuests() { return Array.from(this.quests.values()); } + getQuest(id) { return this.quests.get(id) || null; } + getQuestsByCategory(cat) { return this.getAllQuests().filter(q => q.category === cat); } + getQuestCategories() { return Array.from(this.questCategories.values()); } + getQuestsGroupedByCategory() { + const out = {}; + for (const q of this.getAllQuests()) { + if (!out[q.category]) out[q.category] = []; + out[q.category].push(q); + } + return out; + } + + // ───────────────────────────────────────────────────────────────── + // Helpers + // ───────────────────────────────────────────────────────────────── + + _readJson(filePath) { + try { + return JSON.parse(fs.readFileSync(filePath, 'utf8')); + } catch (e) { + console.warn(`[CONTENT LOADER] Failed to read ${filePath}:`, e.message); + return null; + } + } + + _subdirs(dir) { + if (!fs.existsSync(dir)) return []; + return fs.readdirSync(dir).filter(f => { + try { return fs.statSync(path.join(dir, f)).isDirectory(); } catch { return false; } + }); + } + + _jsonFiles(dir) { + if (!fs.existsSync(dir)) return []; + return fs.readdirSync(dir).filter(f => f.endsWith('.json')); + } + + /** Recursively walk a directory and call cb(filePath) for every .json file */ + _walkJsonDir(dir, cb) { + if (!fs.existsSync(dir)) return; + for (const entry of fs.readdirSync(dir)) { + const full = path.join(dir, entry); + const stat = fs.statSync(full); + if (stat.isDirectory()) { + this._walkJsonDir(full, cb); + } else if (entry.endsWith('.json') && !entry.endsWith('manifest.json')) { + cb(full); + } + } + } + + /** Best-effort human name from an item id (snake_case → Title Case) */ + _itemName(id) { + return id.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase()); + } +} + +module.exports = ContentLoader; diff --git a/GameServer/systems/CraftingSystem.js b/GameServer/systems/CraftingSystem.js index d860ed0..61e2669 100644 --- a/GameServer/systems/CraftingSystem.js +++ b/GameServer/systems/CraftingSystem.js @@ -1,277 +1,30 @@ /** - * Galaxy Strike Online - Server Crafting System - * Manages crafting recipes, materials, and item creation + * CraftingSystem — thin wrapper around ContentLoader for recipe definitions. */ - class CraftingSystem { - constructor() { - this.recipes = new Map(); - this.playerCrafting = new Map(); // userId -> crafting data - this.initializeRecipes(); + constructor(contentLoader) { + this.loader = contentLoader; + this.playerCrafting = new Map(); } - - initializeRecipes() { - // Basic weapon recipes - this.addRecipe('basic_blaster', { - name: 'Basic Blaster', - type: 'weapon', - rarity: 'common', - description: 'A simple blaster for beginners', - materials: { - 'iron_ore': 5, - 'copper_wire': 3, - 'energy_crystal': 1 - }, - results: { - 'basic_blaster': 1, - 'experience': 25 - }, - skillRequired: 1, - craftingTime: 5000 // 5 seconds - }); - - // Advanced weapon recipes - this.addRecipe('plasma_cannon', { - name: 'Plasma Cannon', - type: 'weapon', - rarity: 'rare', - description: 'A powerful plasma-based weapon', - materials: { - 'iron_ore': 15, - 'copper_wire': 10, - 'energy_crystal': 5, - 'rare_metal': 3 - }, - results: { - 'plasma_cannon': 1, - 'experience': 100 - }, - skillRequired: 10, - craftingTime: 15000 // 15 seconds - }); - - // Armor recipes - this.addRecipe('basic_armor', { - name: 'Basic Armor', - type: 'armor', - rarity: 'common', - description: 'Light protection for beginners', - materials: { - 'iron_ore': 8, - 'copper_wire': 2 - }, - results: { - 'basic_armor': 1, - 'experience': 30 - }, - skillRequired: 2, - craftingTime: 8000 // 8 seconds - }); - - // Consumable recipes - this.addRecipe('health_kit', { - name: 'Health Kit', - type: 'consumable', - rarity: 'common', - description: 'Restores health when used', - materials: { - 'iron_ore': 2, - 'energy_crystal': 1 - }, - results: { - 'health_kit': 3, - 'experience': 10 - }, - skillRequired: 1, - craftingTime: 2000 // 2 seconds - }); - } - - addRecipe(id, recipe) { - this.recipes.set(id, { - id, - ...recipe, - createdAt: new Date().toISOString() - }); - } - - getRecipe(id) { - return this.recipes.get(id); - } - - getAllRecipes() { - return Array.from(this.recipes.values()); - } - - getRecipesByType(type) { - return Array.from(this.recipes.values()).filter(recipe => recipe.type === type); - } - - getRecipesByRarity(rarity) { - return Array.from(this.recipes.values()).filter(recipe => recipe.rarity === rarity); - } - + + getAllRecipes() { return this.loader.getAllRecipes(); } + getRecipe(id) { return this.loader.getRecipe(id); } + getRecipesByType(type) { return this.loader.getRecipesByType(type); } + getCraftingTabs() { return this.loader.getCraftingTabs(); } + initializePlayerData(userId) { - if (!this.playerCrafting.has(userId)) { - this.playerCrafting.set(userId, { - skill: 1, - experience: 0, - knownRecipes: new Set(['basic_blaster', 'health_kit']), // Start with basic recipes - craftingHistory: [], - totalCrafted: 0 - }); - } - return this.playerCrafting.get(userId); + if (this.playerCrafting.has(userId)) return this.playerCrafting.get(userId); + const data = { skill: 1, experience: 0, knownRecipes: new Set(), totalCrafted: 0 }; + this.playerCrafting.set(userId, data); + return data; } - - getPlayerData(userId) { - return this.playerCrafting.get(userId) || this.initializePlayerData(userId); - } - - canCraft(userId, recipeId, playerInventory) { - const recipe = this.getRecipe(recipeId); - if (!recipe) { - return { canCraft: false, reason: 'Recipe not found' }; - } - - const playerData = this.getPlayerData(userId); - - // Check skill requirement - if (playerData.skill < recipe.skillRequired) { - return { canCraft: false, reason: 'Insufficient crafting skill' }; - } - - // Check materials - for (const [material, amount] of Object.entries(recipe.materials)) { - const playerAmount = playerInventory[material] || 0; - if (playerAmount < amount) { - return { canCraft: false, reason: `Insufficient ${material}` }; - } - } - - return { canCraft: true }; - } - - async craftItem(userId, recipeId, playerInventory) { - const canCraftResult = this.canCraft(userId, recipeId, playerInventory); - if (!canCraftResult.canCraft) { - throw new Error(canCraftResult.reason); - } - - const recipe = this.getRecipe(recipeId); - const playerData = this.getPlayerData(userId); - - // Remove materials from inventory - for (const [material, amount] of Object.entries(recipe.materials)) { - playerInventory[material] = (playerInventory[material] || 0) - amount; - } - - // Add results to inventory - for (const [item, amount] of Object.entries(recipe.results)) { - if (item !== 'experience') { - playerInventory[item] = (playerInventory[item] || 0) + amount; - } - } - - // Add experience and update stats - const experienceGained = recipe.results.experience || 0; - playerData.experience += experienceGained; - playerData.totalCrafted += 1; - - // Check for skill up - const oldSkill = playerData.skill; - const newSkill = this.calculateSkillLevel(playerData.experience); - playerData.skill = newSkill; - - // Record crafting history - playerData.craftingHistory.push({ - recipeId, - timestamp: new Date().toISOString(), - experienceGained, - skillUp: newSkill > oldSkill - }); - - // Keep only last 100 crafting records - if (playerData.craftingHistory.length > 100) { - playerData.craftingHistory = playerData.craftingHistory.slice(-100); - } - - return { - success: true, - recipe, - results: recipe.results, - experienceGained, - newSkill, - skillUp: newSkill > oldSkill, - playerInventory - }; - } - - calculateSkillLevel(experience) { - // Simple skill progression: 100 XP per level - return Math.floor(experience / 100) + 1; - } - - learnRecipe(userId, recipeId) { - const recipe = this.getRecipe(recipeId); - if (!recipe) { - throw new Error('Recipe not found'); - } - - const playerData = this.getPlayerData(userId); - playerData.knownRecipes.add(recipeId); - - return { - success: true, - recipeId, - recipeName: recipe.name - }; - } - - getPlayerKnownRecipes(userId) { - const playerData = this.getPlayerData(userId); - return Array.from(playerData.knownRecipes).map(recipeId => this.getRecipe(recipeId)); - } - - getCraftingStats(userId) { - const playerData = this.getPlayerData(userId); - return { - skill: playerData.skill, - experience: playerData.experience, - totalCrafted: playerData.totalCrafted, - knownRecipesCount: playerData.knownRecipes.size, - recentCrafting: playerData.craftingHistory.slice(-10) - }; - } - - discoverRecipe(userId, recipeId) { - const recipe = this.getRecipe(recipeId); - if (!recipe) { - throw new Error('Recipe not found'); - } - - const playerData = this.getPlayerData(userId); - - if (playerData.knownRecipes.has(recipeId)) { - return { success: false, reason: 'Recipe already known' }; - } - - // Chance to discover based on skill level - const discoveryChance = Math.min(0.1 + (playerData.skill * 0.05), 0.5); - const discovered = Math.random() < discoveryChance; - - if (discovered) { - playerData.knownRecipes.add(recipeId); - return { - success: true, - recipeId, - recipeName: recipe.name, - discovered: true - }; - } - - return { success: false, discovered: false }; + + getPlayerCrafting(userId) { return this.initializePlayerData(userId); } + + recordCraft(userId, recipeId) { + const pd = this.initializePlayerData(userId); + pd.totalCrafted++; + pd.knownRecipes.add(recipeId); } } - module.exports = CraftingSystem; diff --git a/GameServer/systems/DungeonSystem.js b/GameServer/systems/DungeonSystem.js index b8c0df4..61fccc4 100644 --- a/GameServer/systems/DungeonSystem.js +++ b/GameServer/systems/DungeonSystem.js @@ -1,1585 +1,171 @@ /** - * Galaxy Strike Online - Server Dungeon System - * Manages dungeon instances, encounters, and rewards + * DungeonSystem — wraps ContentLoader for dungeon/enemy defs + manages instances. */ - class DungeonSystem { - constructor() { - this.dungeons = new Map(); - this.instances = new Map(); // instanceId -> dungeon instance + constructor(contentLoader) { + this.loader = contentLoader; + this.io = null; + this.instances = new Map(); // instanceId -> instance this.playerInstances = new Map(); // userId -> instanceId - - // Initialize room templates + this.roomTypes = { - entrance: { name: 'Entrance', enemies: 0, rewards: false }, - corridor: { name: 'Corridor', enemies: 1, rewards: false }, - chamber: { name: 'Chamber', enemies: 2, rewards: true }, - treasure: { name: 'Treasure Room', enemies: 0, rewards: true }, - boss: { name: 'Boss Room', enemies: 1, rewards: true, isBoss: true }, - exit: { name: 'Exit', enemies: 0, rewards: false } + entrance: { name: 'Entrance', enemies: 0, rewards: false }, + corridor: { name: 'Corridor', enemies: 1, rewards: false }, + chamber: { name: 'Chamber', enemies: 2, rewards: true }, + treasure: { name: 'Treasure Room',enemies: 0, rewards: true }, + boss: { name: 'Boss Room', enemies: 1, rewards: true, isBoss: true }, + exit: { name: 'Exit', enemies: 0, rewards: false } }; - - // Initialize enemy templates - this.enemyTemplates = { - // Original enemies - training_drone: { - name: 'Training Drone', - health: 10, - attack: 5, - defense: 2, - experience: 5, - credits: 3 - }, - practice_target: { - name: 'Practice Target', - health: 5, - attack: 0, - defense: 0, - experience: 2, - credits: 1 - }, - alien_guardian: { - name: 'Alien Guardian', - health: 50, - attack: 8, - defense: 5, - experience: 25, - credits: 15 - }, - ancient_drone: { - name: 'Ancient Drone', - health: 30, - attack: 12, - defense: 2, - experience: 20, - credits: 10 - }, - crystal_golem: { - name: 'Crystal Golem', - health: 80, - attack: 6, - defense: 10, - experience: 35, - credits: 25 - }, - space_pirate: { - name: 'Space Pirate', - health: 25, - attack: 10, - defense: 3, - experience: 15, - credits: 12 - }, - pirate_captain: { - name: 'Pirate Captain', - health: 40, - attack: 15, - defense: 6, - experience: 30, - credits: 20 - }, - defense_turret: { - name: 'Defense Turret', - health: 20, - attack: 18, - defense: 8, - experience: 18, - credits: 8 - }, - security_drone: { - name: 'Security Drone', - health: 35, - attack: 14, - defense: 4, - experience: 22, - credits: 15 - }, - corrupted_ai: { - name: 'Corrupted AI', - health: 60, - attack: 20, - defense: 2, - experience: 40, - credits: 30 - }, - virus_program: { - name: 'Virus Program', - health: 15, - attack: 25, - defense: 1, - experience: 20, - credits: 12 - }, - mining_drone: { - name: 'Mining Drone', - health: 20, - attack: 8, - defense: 3, - experience: 12, - credits: 8 - }, - rock_creature: { - name: 'Rock Creature', - health: 45, - attack: 6, - defense: 12, - experience: 25, - credits: 15 - }, - explosive_asteroid: { - name: 'Explosive Asteroid', - health: 10, - attack: 30, - defense: 0, - experience: 15, - credits: 5 - }, - energy_being: { - name: 'Energy Being', - health: 55, - attack: 22, - defense: 3, - experience: 45, - credits: 35 - }, - phase_shifter: { - name: 'Phase Shifter', - health: 30, - attack: 28, - defense: 1, - experience: 35, - credits: 25 - }, - quantum_entity: { - name: 'Quantum Entity', - health: 70, - attack: 35, - defense: 5, - experience: 60, - credits: 50 - }, - - // NEW ENEMIES - Space Theme - maintenance_drone: { - name: 'Maintenance Drone', - health: 28, - attack: 11, - defense: 4, - experience: 18, - credits: 12 - }, - security_android: { - name: 'Security Android', - health: 42, - attack: 16, - defense: 7, - experience: 28, - credits: 20 - }, - station_ai: { - name: 'Station AI', - health: 65, - attack: 24, - defense: 3, - experience: 45, - credits: 32 - }, - ice_elemental: { - name: 'Ice Elemental', - health: 38, - attack: 18, - defense: 8, - experience: 32, - credits: 24 - }, - frost_wyrm: { - name: 'Frost Wyrm', - health: 72, - attack: 26, - defense: 6, - experience: 52, - credits: 38 - }, - gravity_wraith: { - name: 'Gravity Wraith', - health: 85, - attack: 32, - defense: 4, - experience: 68, - credits: 48 - }, - void_stalker: { - name: 'Void Stalker', - health: 78, - attack: 38, - defense: 5, - experience: 72, - credits: 52 - }, - singularity_spawn: { - name: 'Singularity Spawn', - health: 95, - attack: 42, - defense: 8, - experience: 85, - credits: 65 - }, - plasma_elemental: { - name: 'Plasma Elemental', - health: 58, - attack: 28, - defense: 4, - experience: 48, - credits: 35 - }, - solar_guardian: { - name: 'Solar Guardian', - health: 82, - attack: 34, - defense: 7, - experience: 65, - credits: 48 - }, - fusion_core: { - name: 'Fusion Core', - health: 68, - attack: 30, - defense: 12, - experience: 58, - credits: 42 - }, - scrap_golem: { - name: 'Scrap Golem', - health: 35, - attack: 14, - defense: 9, - experience: 22, - credits: 16 - }, - hull_breacher: { - name: 'Hull Breacher', - health: 32, - attack: 20, - defense: 3, - experience: 26, - credits: 18 - }, - salage_drone: { - name: 'Salvage Drone', - health: 22, - attack: 12, - defense: 5, - experience: 16, - credits: 11 - }, - - // NEW ENEMIES - Planet Theme - plant_beast: { - name: 'Plant Beast', - health: 48, - attack: 15, - defense: 8, - experience: 35, - credits: 26 - }, - tribal_warrior: { - name: 'Tribal Warrior', - health: 38, - attack: 18, - defense: 6, - experience: 28, - credits: 20 - }, - jungle_spirit: { - name: 'Jungle Spirit', - health: 55, - attack: 22, - defense: 4, - experience: 42, - credits: 30 - }, - sand_worm: { - name: 'Sand Worm', - health: 75, - attack: 28, - defense: 9, - experience: 58, - credits: 42 - }, - mummy_guardian: { - name: 'Mummy Guardian', - health: 62, - attack: 24, - defense: 7, - experience: 48, - credits: 35 - }, - heat_elemental: { - name: 'Heat Elemental', - health: 52, - attack: 26, - defense: 3, - experience: 45, - credits: 32 - }, - lava_elemental: { - name: 'Lava Elemental', - health: 68, - attack: 30, - defense: 5, - experience: 55, - credits: 40 - }, - fire_demon: { - name: 'Fire Demon', - health: 72, - attack: 32, - defense: 6, - experience: 62, - credits: 45 - }, - magma_beast: { - name: 'Magma Beast', - health: 85, - attack: 28, - defense: 12, - experience: 68, - credits: 50 - }, - cryo_mutant: { - name: 'Cryo Mutant', - health: 45, - attack: 20, - defense: 7, - experience: 38, - credits: 28 - }, - frost_android: { - name: 'Frost Android', - health: 52, - attack: 22, - defense: 8, - experience: 42, - credits: 30 - }, - ice_wraith: { - name: 'Ice Wraith', - health: 58, - attack: 25, - defense: 4, - experience: 48, - credits: 35 - }, - swamp_beast: { - name: 'Swamp Beast', - health: 35, - attack: 16, - defense: 9, - experience: 24, - credits: 18 - }, - toxic_spitter: { - name: 'Toxic Spitter', - health: 28, - attack: 19, - defense: 3, - experience: 22, - credits: 16 - }, - mud_golem: { - name: 'Mud Golem', - health: 42, - attack: 12, - defense: 11, - experience: 26, - credits: 19 - }, - - // NEW ENEMIES - Technology Theme - glitch_wraith: { - name: 'Glitch Wraith', - health: 48, - attack: 26, - defense: 2, - experience: 45, - credits: 32 - }, - firewall_guardian: { - name: 'Firewall Guardian', - health: 65, - attack: 22, - defense: 8, - experience: 52, - credits: 38 - }, - data_vampire: { - name: 'Data Vampire', - health: 38, - attack: 30, - defense: 3, - experience: 35, - credits: 26 - }, - assembly_drone: { - name: 'Assembly Drone', - health: 32, - attack: 15, - defense: 6, - experience: 24, - credits: 17 - }, - welder_bot: { - name: 'Welder Bot', - health: 28, - attack: 20, - defense: 4, - experience: 22, - credits: 15 - }, - factory_overseer: { - name: 'Factory Overseer', - health: 58, - attack: 24, - defense: 7, - experience: 46, - credits: 33 - }, - quantum_phantom: { - name: 'Quantum Phantom', - health: 78, - attack: 35, - defense: 4, - experience: 68, - credits: 50 - }, - particle_accelerator: { - name: 'Particle Accelerator', - health: 92, - attack: 40, - defense: 6, - experience: 85, - credits: 62 - }, - reality_bender: { - name: 'Reality Bender', - health: 88, - attack: 45, - defense: 3, - experience: 92, - credits: 68 - }, - sentinel_program: { - name: 'Sentinel Program', - health: 42, - attack: 21, - defense: 8, - experience: 32, - credits: 24 - }, - data_corruptor: { - name: 'Data Corruptor', - health: 35, - attack: 25, - defense: 3, - experience: 28, - credits: 20 - }, - system_guardian: { - name: 'System Guardian', - health: 55, - attack: 23, - defense: 9, - experience: 42, - credits: 30 - }, - - // NEW ENEMIES - Biome/Elemental Theme - shard_elemental: { - name: 'Shard Elemental', - health: 45, - attack: 19, - defense: 10, - experience: 38, - credits: 28 - }, - resonance_beast: { - name: 'Resonance Beast', - health: 52, - attack: 22, - defense: 6, - experience: 42, - credits: 30 - }, - mutant_horror: { - name: 'Mutant Horror', - health: 68, - attack: 28, - defense: 5, - experience: 58, - credits: 42 - }, - toxic_slime: { - name: 'Toxic Slime', - health: 42, - attack: 18, - defense: 8, - experience: 32, - credits: 24 - }, - radiation_beast: { - name: 'Radiation Beast', - health: 75, - attack: 30, - defense: 4, - experience: 65, - credits: 48 - }, - shadow_demon: { - name: 'Shadow Demon', - health: 72, - attack: 34, - defense: 3, - experience: 68, - credits: 50 - }, - nightmare_stalker: { - name: 'Nightmare Stalker', - health: 85, - attack: 38, - defense: 5, - experience: 78, - credits: 58 - }, - void_walker: { - name: 'Void Walker', - health: 92, - attack: 42, - defense: 7, - experience: 88, - credits: 65 - }, - temporal_paradox: { - name: 'Temporal Paradox', - health: 78, - attack: 40, - defense: 4, - experience: 75, - credits: 55 - }, - future_soldier: { - name: 'Future Soldier', - health: 65, - attack: 32, - defense: 9, - experience: 58, - credits: 42 - }, - past_guardian: { - name: 'Past Guardian', - health: 70, - attack: 28, - defense: 12, - experience: 62, - credits: 45 - }, - - // NEW ENEMIES - Military/War Theme - enemy_soldier: { - name: 'Enemy Soldier', - health: 45, - attack: 20, - defense: 7, - experience: 35, - credits: 26 - }, - combat_drone: { - name: 'Combat Drone', - health: 38, - attack: 22, - defense: 5, - experience: 30, - credits: 22 - }, - field_commander: { - name: 'Field Commander', - health: 62, - attack: 28, - defense: 9, - experience: 52, - credits: 38 - }, - turret_system: { - name: 'Turret System', - health: 48, - attack: 26, - defense: 10, - experience: 38, - credits: 28 - }, - combat_android: { - name: 'Combat Android', - health: 55, - attack: 24, - defense: 8, - experience: 45, - credits: 33 - }, - base_commander: { - name: 'Base Commander', - health: 72, - attack: 30, - defense: 11, - experience: 62, - credits: 45 - }, - weapon_drone: { - name: 'Weapon Drone', - health: 42, - attack: 28, - defense: 4, - experience: 38, - credits: 28 - }, - test_subject: { - name: 'Test Subject', - health: 58, - attack: 25, - defense: 6, - experience: 48, - credits: 35 - }, - chief_scientist: { - name: 'Chief Scientist', - health: 35, - attack: 32, - defense: 3, - experience: 42, - credits: 30 - }, - - // NEW ENEMIES - Special/Unique Theme - nightmare_creature: { - name: 'Nightmare Creature', - health: 62, - attack: 28, - defense: 5, - experience: 55, - credits: 40 - }, - dream_guardian: { - name: 'Dream Guardian', - health: 68, - attack: 30, - defense: 8, - experience: 58, - credits: 42 - }, - subconscious_demon: { - name: 'Subconscious Demon', - health: 75, - attack: 34, - defense: 4, - experience: 68, - credits: 50 - }, - memory_fragment: { - name: 'Memory Fragment', - health: 48, - attack: 26, - defense: 6, - experience: 45, - credits: 33 - }, - forgetfulness_demon: { - name: 'Forgetfulness Demon', - health: 55, - attack: 30, - defense: 3, - experience: 48, - credits: 35 - }, - nostalgia_spirit: { - name: 'Nostalgia Spirit', - health: 52, - attack: 24, - defense: 9, - experience: 42, - credits: 30 - }, - rift_demon: { - name: 'Rift Demon', - health: 88, - attack: 44, - defense: 5, - experience: 92, - credits: 68 - }, - dimensional_hunter: { - name: 'Dimensional Hunter', - health: 95, - attack: 48, - defense: 8, - experience: 105, - credits: 78 - }, - reality_tear: { - name: 'Reality Tear', - health: 102, - attack: 52, - defense: 3, - experience: 115, - credits: 85 - } - }; - - this.initializeDungeons(); } - - initializeDungeons() { - // Tutorial Dungeon - this.addDungeon('tutorial', { - name: 'Tutorial Dungeon', - description: 'Learn the basics of dungeon exploration in this guided tutorial', - difficulty: 'tutorial', - enemyTypes: ['training_drone', 'practice_target'], - rewardMultiplier: 0.5, - oneTimeOnly: true, - healthType: 'player', // Ground mission - energyCost: 0, - minLevel: 1, - maxLevel: 5, - maxPlayers: 4, - estimatedTime: 15 - }); - - // Space Theme Dungeons - this.addDungeon('alien_ruins', { - name: 'Alien Ruins', - description: 'Ancient alien structures filled with mysterious technology', - difficulty: 'medium', - enemyTypes: ['alien_guardian', 'ancient_drone', 'crystal_golem'], - rewardMultiplier: 1.2, - healthType: 'player', // Ground mission - energyCost: 20, - minLevel: 5, - maxLevel: 15, - maxPlayers: 4, - estimatedTime: 25 - }); - - this.addDungeon('pirate_lair', { - name: 'Pirate Lair', - description: 'Dangerous pirate hideouts with valuable loot', - difficulty: 'easy', - enemyTypes: ['space_pirate', 'pirate_captain', 'defense_turret'], - rewardMultiplier: 1.0, - healthType: 'ship', // Space mission - energyCost: 15, - minLevel: 3, - maxLevel: 10, - maxPlayers: 4, - estimatedTime: 20 - }); - - this.addDungeon('corrupted_vault', { - name: 'Corrupted AI Vault', - description: ' malfunctioning AI facilities with corrupted security', - difficulty: 'hard', - enemyTypes: ['security_drone', 'corrupted_ai', 'virus_program'], - rewardMultiplier: 1.5, - healthType: 'ship', // Space mission - energyCost: 25, - minLevel: 10, - maxLevel: 20, - maxPlayers: 4, - estimatedTime: 35 - }); - - this.addDungeon('asteroid_mine', { - name: 'Asteroid Mine', - description: 'Abandoned mining facilities in asteroid fields', - difficulty: 'easy', - enemyTypes: ['mining_drone', 'rock_creature', 'explosive_asteroid'], - rewardMultiplier: 0.8, - healthType: 'ship', // Space mission - energyCost: 10, - minLevel: 2, - maxLevel: 8, - maxPlayers: 4, - estimatedTime: 18 - }); - - this.addDungeon('nebula_anomaly', { - name: 'Nebula Anomaly', - description: 'Strange energy anomalies in deep space', - difficulty: 'extreme', - enemyTypes: ['energy_being', 'phase_shifter', 'quantum_entity'], - rewardMultiplier: 2.0, - healthType: 'ship', // Space mission - energyCost: 30, - minLevel: 15, - maxLevel: 25, - maxPlayers: 4, - estimatedTime: 40 - }); - - this.addDungeon('space_station', { - name: 'Abandoned Space Station', - description: 'A derelict space station floating in the void', - difficulty: 'medium', - enemyTypes: ['maintenance_drone', 'security_android', 'station_ai'], - rewardMultiplier: 1.3, - healthType: 'player', - energyCost: 22, - minLevel: 6, - maxLevel: 16, - maxPlayers: 4, - estimatedTime: 28 - }); - - this.addDungeon('comet_core', { - name: 'Comet Core', - description: 'The frozen heart of a passing comet', - difficulty: 'hard', - enemyTypes: ['ice_elemental', 'frost_wyrm', 'crystal_guardian'], - rewardMultiplier: 1.6, - healthType: 'ship', - energyCost: 28, - minLevel: 12, - maxLevel: 22, - maxPlayers: 4, - estimatedTime: 32 - }); - - this.addDungeon('black_hole_perimeter', { - name: 'Black Hole Perimeter', - description: 'Dangerous space near a black hole event horizon', - difficulty: 'extreme', - enemyTypes: ['gravity_wraith', 'void_stalker', 'singularity_spawn'], - rewardMultiplier: 2.5, - healthType: 'ship', - energyCost: 35, - minLevel: 18, - maxLevel: 30, - maxPlayers: 4, - estimatedTime: 45 - }); - - this.addDungeon('star_forge', { - name: 'Star Forge', - description: 'Ancient facility that harnesses stellar energy', - difficulty: 'hard', - enemyTypes: ['plasma_elemental', 'solar_guardian', 'fusion_core'], - rewardMultiplier: 1.8, - healthType: 'ship', - energyCost: 30, - minLevel: 14, - maxLevel: 24, - maxPlayers: 4, - estimatedTime: 35 - }); - - this.addDungeon('debris_field', { - name: 'Ship Debris Field', - description: 'Graveyard of destroyed spacecraft', - difficulty: 'easy', - enemyTypes: ['scrap_golem', 'hull_breacher', 'salage_drone'], - rewardMultiplier: 0.9, - healthType: 'ship', - energyCost: 12, - minLevel: 3, - maxLevel: 9, - maxPlayers: 4, - estimatedTime: 20 - }); - - // Planet Theme Dungeons - this.addDungeon('jungle_temple', { - name: 'Jungle Temple', - description: 'Overgrown temple hidden in dense alien jungle', - difficulty: 'medium', - enemyTypes: ['plant_beast', 'tribal_warrior', 'jungle_spirit'], - rewardMultiplier: 1.4, - healthType: 'player', - energyCost: 25, - minLevel: 7, - maxLevel: 17, - maxPlayers: 4, - estimatedTime: 30 - }); - - this.addDungeon('desert_pyramid', { - name: 'Desert Pyramid', - description: 'Ancient pyramid buried under endless sand dunes', - difficulty: 'hard', - enemyTypes: ['sand_worm', 'mummy_guardian', 'heat_elemental'], - rewardMultiplier: 1.7, - healthType: 'player', - energyCost: 28, - minLevel: 13, - maxLevel: 23, - maxPlayers: 4, - estimatedTime: 33 - }); - - this.addDungeon('volcanic_caverns', { - name: 'Volcanic Caverns', - description: 'Molten caverns deep within an active volcano', - difficulty: 'hard', - enemyTypes: ['lava_elemental', 'fire_demon', 'magma_beast'], - rewardMultiplier: 1.6, - healthType: 'player', - energyCost: 26, - minLevel: 12, - maxLevel: 22, - maxPlayers: 4, - estimatedTime: 32 - }); - - this.addDungeon('arctic_research', { - name: 'Arctic Research Base', - description: 'Frozen research facility with failed experiments', - difficulty: 'medium', - enemyTypes: ['cryo_mutant', 'frost_android', 'ice_wraith'], - rewardMultiplier: 1.5, - healthType: 'player', - energyCost: 24, - minLevel: 8, - maxLevel: 18, - maxPlayers: 4, - estimatedTime: 29 - }); - - this.addDungeon('swamp_lair', { - name: 'Swamp Lair', - description: 'Murky swamp inhabited by strange creatures', - difficulty: 'easy', - enemyTypes: ['swamp_beast', 'toxic_spitter', 'mud_golem'], - rewardMultiplier: 1.1, - healthType: 'player', - energyCost: 18, - minLevel: 4, - maxLevel: 12, - maxPlayers: 4, - estimatedTime: 22 - }); - - // Technology Theme Dungeons - this.addDungeon('cyber_realm', { - name: 'Cyber Realm', - description: 'Virtual reality space corrupted by malware', - difficulty: 'hard', - enemyTypes: ['glitch_wraith', 'firewall_guardian', 'data_vampire'], - rewardMultiplier: 1.9, - healthType: 'player', - energyCost: 32, - minLevel: 16, - maxLevel: 26, - maxPlayers: 4, - estimatedTime: 38 - }); - - this.addDungeon('robot_factory', { - name: 'Robot Factory', - description: 'Automated factory producing hostile machines', - difficulty: 'medium', - enemyTypes: ['assembly_drone', 'welder_bot', 'factory_overseer'], - rewardMultiplier: 1.4, - healthType: 'player', - energyCost: 23, - minLevel: 9, - maxLevel: 19, - maxPlayers: 4, - estimatedTime: 27 - }); - - this.addDungeon('quantum_lab', { - name: 'Quantum Laboratory', - description: 'Research facility experimenting with quantum physics', - difficulty: 'extreme', - enemyTypes: ['quantum_phantom', 'particle_accelerator', 'reality_bender'], - rewardMultiplier: 2.3, - healthType: 'player', - energyCost: 38, - minLevel: 20, - maxLevel: 30, - maxPlayers: 4, - estimatedTime: 42 - }); - - this.addDungeon('server_farm', { - name: 'Server Farm', - description: 'Massive data center with rogue security programs', - difficulty: 'medium', - enemyTypes: ['sentinel_program', 'data_corruptor', 'system_guardian'], - rewardMultiplier: 1.3, - healthType: 'player', - energyCost: 21, - minLevel: 7, - maxLevel: 17, - maxPlayers: 4, - estimatedTime: 25 - }); - - console.log(`[DUNGEON SYSTEM] Initialized ${this.dungeons.size} dungeons`); - } - - setIO(io) { - this.io = io; - console.log('[DUNGEON SYSTEM] Socket.IO instance set for quest events'); - } - - generateDungeonEncounters(dungeon) { - console.log('[DUNGEON SYSTEM] Generating encounters for dungeon:', dungeon.id); - - const encounters = []; - const encounterCount = Math.max(3, Math.floor(dungeon.estimatedTime / 5)); // 1 encounter per ~5 minutes - - console.log('[DUNGEON SYSTEM] Encounter count:', encounterCount, 'for difficulty:', dungeon.difficulty); - - // Always start with entrance - encounters.push({ - type: 'entrance', - name: 'Dungeon Entrance', - description: 'The entrance to the dungeon', - enemies: [], - rewards: false - }); - - // Generate random encounters based on dungeon difficulty - for (let i = 1; i < encounterCount - 1; i++) { - const encounterTypes = ['corridor', 'chamber']; - if (i === Math.floor(encounterCount / 2)) { - encounterTypes.push('treasure'); // Mid-dungeon treasure room - } - - const type = encounterTypes[Math.floor(Math.random() * encounterTypes.length)]; - const template = this.roomTypes[type]; - - encounters.push({ - type, - name: template.name, - description: `A ${template.name.toLowerCase()} in the dungeon`, - enemies: this.generateEnemies(dungeon.enemyTypes, template.enemies), - rewards: template.rewards - }); - } - - // Always end with boss room (except tutorial) - if (dungeon.difficulty !== 'tutorial') { - encounters.push({ - type: 'boss', - name: 'Boss Chamber', - description: 'The final chamber with a powerful enemy', - enemies: this.generateEnemies(dungeon.enemyTypes, 1, true), - rewards: true, - isBoss: true - }); - } else { - // Tutorial ends with simple chamber (only practice targets for auto-completion) - encounters.push({ - type: 'chamber', - name: 'Training Room', - description: 'The final training room', - enemies: this.generateEnemies(['practice_target'], 1), // Only practice targets - rewards: true - }); - } - - console.log('[DUNGEON SYSTEM] Generated encounters:', encounters.map((e, i) => ({ - index: i, - type: e.type, - name: e.name, - enemyCount: e.enemies?.length || 0 - }))); - - return encounters; - } - - generateEnemies(enemyTypes, count, isBoss = false) { - console.log('[DUNGEON SYSTEM] Generating enemies:', { enemyTypes, count, isBoss }); - console.log('[DUNGEON SYSTEM] Available enemy templates:', Object.keys(this.enemyTemplates)); - - const enemies = []; - - for (let i = 0; i < count; i++) { - const enemyTypeId = enemyTypes[Math.floor(Math.random() * enemyTypes.length)]; - const template = this.enemyTemplates[enemyTypeId]; - - console.log(`[DUNGEON SYSTEM] Generating enemy ${i}: type=${enemyTypeId}, template=`, template); - - if (!template) { - console.warn(`[DUNGEON SYSTEM] Enemy template not found for type: ${enemyTypeId}`); - continue; - } - - // Scale enemy for boss fights - const enemy = { ...template }; - if (isBoss) { - enemy.health *= 3; - enemy.attack *= 2; - enemy.defense *= 1.5; - enemy.experience *= 5; - enemy.credits *= 3; - enemy.name = `Elite ${enemy.name}`; - } - - enemies.push(enemy); - } - - console.log('[DUNGEON SYSTEM] Generated enemies:', enemies); - return enemies; - } - - addDungeon(id, dungeon) { - // Generate encounters for this dungeon - const encounters = this.generateDungeonEncounters(dungeon); - - this.dungeons.set(id, { - id, - ...dungeon, - encounters, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString() - }); - } - - getDungeon(id) { - return this.dungeons.get(id); - } - - getAllDungeons() { - return Array.from(this.dungeons.values()); - } - - getDungeonsGroupedByDifficulty() { - const dungeonsByDifficulty = { - tutorial: [], - easy: [], - medium: [], - hard: [], - extreme: [] - }; - - this.dungeons.forEach(dungeon => { - if (dungeonsByDifficulty[dungeon.difficulty]) { - dungeonsByDifficulty[dungeon.difficulty].push(dungeon); - } - }); - - return dungeonsByDifficulty; - } - - getRoomTypes() { - return this.roomTypes; - } - - getEnemyTemplates() { - return this.enemyTemplates; - } - - getEnemyTemplate(enemyId) { - return this.enemyTemplates[enemyId]; - } - - getDungeonsByDifficulty(difficulty) { - return Array.from(this.dungeons.values()).filter(dungeon => dungeon.difficulty === difficulty); - } - - createInstance(dungeonId, creatorId, playerIds = []) { - console.log('[DUNGEON SYSTEM] Creating instance:', { dungeonId, creatorId, playerIds }); - - const dungeon = this.getDungeon(dungeonId); - if (!dungeon) { - throw new Error('Dungeon not found'); - } - - console.log('[DUNGEON SYSTEM] Dungeon found:', { - id: dungeon.id, - encountersCount: dungeon.encounters?.length || 0, - encounters: dungeon.encounters?.map((e, i) => ({ index: i, type: e.type, name: e.name })) - }); - - // Validate requirements - if (playerIds.length > dungeon.maxPlayers) { - throw new Error('Too many players for this dungeon'); - } - - const instanceId = `instance_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; - + + setIO(io) { this.io = io; } + + // ── Content queries (delegated to ContentLoader) ────────────────── + getAllDungeons() { return this.loader.getAllDungeons(); } + getDungeon(id) { return this.loader.getDungeon(id); } + getDungeonsByDifficulty(d) { return this.loader.getDungeonsByDifficulty(d); } + getDungeonsGroupedByDifficulty() { return this.loader.getDungeonsGroupedByDifficulty(); } + getAvailableDungeons(lvl) { return this.loader.getAvailableDungeons(lvl); } + getEnemyTemplates() { return this.loader.getAllEnemies(); } + getEnemy(id) { return this.loader.getEnemy(id); } + + // ── Instance management ─────────────────────────────────────────── + createInstance(dungeonId, userId, _partyIds = []) { + const def = this.loader.getDungeon(dungeonId); + if (!def) return null; + + const [minR, maxR] = def.roomCount || [4, 6]; + const roomCount = Math.floor(Math.random() * (maxR - minR + 1)) + minR; + const instanceId = `${userId}_${dungeonId}_${Date.now()}`; + const instance = { - id: instanceId, + instanceId, dungeonId, - creatorId, - players: new Set([creatorId, ...playerIds]), - currentEncounter: 0, - status: 'active', - startedAt: new Date().toISOString(), - completedAt: null, - progress: {}, - rewards: null, - deaths: new Map() // userId -> death count + userId, + startTime: Date.now(), + currentRoom: 0, + totalRooms: roomCount, + rooms: this._generateRooms(def, roomCount), + completed: false, + failed: false }; - - // Initialize progress tracking - dungeon.encounters.forEach((encounter, index) => { - instance.progress[index] = { - completed: false, - attempts: 0, - deaths: 0 - }; - }); - + this.instances.set(instanceId, instance); - - // Track player instances - for (const playerId of instance.players) { - this.playerInstances.set(playerId, instanceId); - } - - return instance; - } - - getInstance(instanceId) { - return this.instances.get(instanceId); - } - - getPlayerInstance(userId) { - const instanceId = this.playerInstances.get(userId); - return instanceId ? this.getInstance(instanceId) : null; - } - - joinInstance(instanceId, userId) { - const instance = this.getInstance(instanceId); - if (!instance) { - throw new Error('Instance not found'); - } - - if (instance.status !== 'active') { - throw new Error('Instance is not active'); - } - - const dungeon = this.getDungeon(instance.dungeonId); - if (instance.players.size >= dungeon.maxPlayers) { - throw new Error('Instance is full'); - } - - // Remove from existing instance if any - const existingInstanceId = this.playerInstances.get(userId); - if (existingInstanceId) { - this.leaveInstance(userId); - } - - // Add to new instance - instance.players.add(userId); this.playerInstances.set(userId, instanceId); - return instance; } - - leaveInstance(userId) { - const instanceId = this.playerInstances.get(userId); - if (!instanceId) { - return null; - } - - const instance = this.getInstance(instanceId); - if (instance) { - instance.players.delete(userId); - - // If instance is empty, clean it up - if (instance.players.size === 0) { - this.instances.delete(instanceId); - } - } - - this.playerInstances.delete(userId); - return instance; + + getPlayerInstance(userId) { + const id = this.playerInstances.get(userId); + return id ? this.instances.get(id) || null : null; } - + + getPlayerCompletedDungeons(_userId) { + // Could be persisted to DB; return empty array for now + return []; + } + startEncounter(instanceId, userId) { - const instance = this.getInstance(instanceId); - if (!instance) { - throw new Error('Instance not found'); - } - - if (!instance.players.has(userId)) { - throw new Error('Player not in instance'); - } - - const dungeon = this.getDungeon(instance.dungeonId); - const encounter = dungeon.encounters[instance.currentEncounter]; - - if (!encounter) { - throw new Error('No more encounters'); - } - - return { - encounter, - encounterIndex: instance.currentEncounter, - instance - }; + const instance = this.instances.get(instanceId); + if (!instance) return { success: false, error: 'Instance not found' }; + const room = instance.rooms[instance.currentRoom]; + if (!room) return { success: false, error: 'No more rooms' }; + return { success: true, room, currentRoom: instance.currentRoom, totalRooms: instance.totalRooms }; } - - completeEncounter(instanceId, userId, result) { - const instance = this.getInstance(instanceId); - if (!instance) { - throw new Error('Instance not found'); - } - - const dungeon = this.getDungeon(instance.dungeonId); - const encounter = dungeon.encounters[instance.currentEncounter]; - - if (!encounter) { - throw new Error('No encounter to complete'); - } - - const progress = instance.progress[instance.currentEncounter]; - progress.completed = true; - progress.attempts++; - - if (result.deaths) { - progress.deaths += result.deaths; - instance.deaths.set(userId, (instance.deaths.get(userId) || 0) + result.deaths); - } - - // Move to next encounter - instance.currentEncounter++; - - // Check if dungeon is complete - if (instance.currentEncounter >= dungeon.encounters.length) { - return this.completeDungeon(instanceId); - } - - return { - success: true, - nextEncounter: instance.currentEncounter < dungeon.encounters.length ? dungeon.encounters[instance.currentEncounter] : null, - encounterIndex: instance.currentEncounter, - instance - }; + + completeEncounter(instanceId, userId, result = {}) { + const instance = this.instances.get(instanceId); + if (!instance) return { success: false, error: 'Instance not found' }; + + const room = instance.rooms[instance.currentRoom]; + if (room) room.cleared = true; + instance.currentRoom++; + + const isComplete = instance.currentRoom >= instance.totalRooms; + const rewards = isComplete ? this._generateRewards(instance.dungeonId) : []; + if (isComplete) instance.completed = true; + + return { success: true, room, rewards, completed: isComplete }; } - + + moveToNextRoom(instanceId, userId) { + const instance = this.instances.get(instanceId); + if (!instance) return { success: false, error: 'Instance not found' }; + const room = instance.rooms[instance.currentRoom]; + return { success: true, room: room || null, currentRoom: instance.currentRoom, totalRooms: instance.totalRooms }; + } + completeDungeon(instanceId) { - const instance = this.getInstance(instanceId); - if (!instance) { - throw new Error('Instance not found'); - } - - const dungeon = this.getDungeon(instance.dungeonId); - - // Calculate rewards - const rewards = this.calculateRewards(dungeon, instance); - - // Mark as completed - instance.status = 'completed'; - instance.completedAt = new Date().toISOString(); - instance.rewards = rewards; - - // Remove players from instance tracking - for (const playerId of instance.players) { - this.playerInstances.delete(playerId); - - // Check for quest completion - this.checkQuestCompletion(playerId, instance.dungeonId); - } - - return { - success: true, - dungeon, - rewards, - instance, - encounterIndex: -1, // Dungeon complete - isComplete: true - }; + const instance = this.instances.get(instanceId); + if (!instance) return { success: false, error: 'Instance not found' }; + + const rewards = this._generateRewards(instance.dungeonId); + const def = this.loader.getDungeon(instance.dungeonId); + + this.instances.delete(instanceId); + this.playerInstances.delete(instance.userId); + + return { success: true, rewards, dungeonName: def?.name, timeMs: Date.now() - instance.startTime }; } - - // Check if dungeon completion should trigger quests - checkQuestCompletion(userId, dungeonId) { - // Tutorial dungeon completion should trigger main story progression - if (dungeonId === 'tutorial') { - console.log('[DUNGEON SYSTEM] Tutorial dungeon completed, triggering quest progression'); - - // Set player stat for tutorial completion - if (this.io) { - this.io.emit('player_stat_update', { - userId: userId, - stat: 'tutorialDungeonCompleted', - value: true - }); - - // Emit quest completion event for quest system - this.io.emit('quest_completed', { - userId: userId, - questId: 'main_story_first_dungeon', - questType: 'dungeon', - dungeonId: dungeonId, - rewards: { - experience: 50, - credits: 25 - } - }); + + // ── Internals ───────────────────────────────────────────────────── + _generateRooms(def, count) { + const rooms = []; + const enemyPool = def.enemyPool || []; + + for (let i = 0; i < count; i++) { + const isFirst = i === 0; + const isLast = i === count - 1; + const isBoss = i === count - 2 && count > 2; + + let type = 'corridor', enemies = []; + + if (isFirst) type = 'entrance'; + else if (isLast) type = 'exit'; + else if (isBoss) { + type = 'boss'; + const bossId = enemyPool[enemyPool.length - 1] || enemyPool[0]; + const tmpl = this.loader.getEnemy(bossId); + if (tmpl) enemies.push({ ...tmpl, health: Math.round(tmpl.health * 2), attack: Math.round(tmpl.attack * 1.5) }); + } else { + const roll = Math.random(); + if (roll < 0.2) type = 'treasure'; + else if (roll < 0.5) type = 'chamber'; + else type = 'corridor'; + + const ec = this.roomTypes[type]?.enemies || 0; + for (let e = 0; e < ec; e++) { + const eid = enemyPool[Math.floor(Math.random() * enemyPool.length)]; + const tmpl = this.loader.getEnemy(eid); + if (tmpl) enemies.push({ ...tmpl }); + } + } + + rooms.push({ index: i, type, enemies, cleared: false, rewards: [] }); + } + return rooms; + } + + _generateRewards(dungeonId) { + const def = this.loader.getDungeon(dungeonId); + if (!def?.lootTable) return []; + + const rewards = []; + const totalWeight = def.lootTable.reduce((s, e) => s + (e.weight || 0), 0); + const picks = Math.floor(Math.random() * 3) + 1; + + for (let i = 0; i < picks; i++) { + let roll = Math.random() * totalWeight; + for (const entry of def.lootTable) { + roll -= entry.weight; + if (roll <= 0) { + const qty = Math.floor(Math.random() * (entry.qtyMax - entry.qtyMin + 1)) + entry.qtyMin; + rewards.push({ itemId: entry.itemId, quantity: qty }); + break; + } } } - } - - getPlayerCompletedDungeons(userId) { - const instances = Array.from(this.instances.values()).filter(instance => - instance.players.has(userId) && instance.status === 'completed' - ); - - return instances.map(instance => instance.dungeonId); - } - - calculateRewards(dungeon, instance) { - const rewards = { - experience: 0, - credits: 0, - items: [] - }; - - // Check if dungeon has rewards defined - if (!dungeon.rewards) { - console.warn('[DUNGEON SYSTEM] No rewards defined for dungeon:', dungeon.id); - return rewards; - } - - // Calculate base rewards - const expRange = dungeon.rewards.experience; - const creditRange = dungeon.rewards.credits; - - if (!expRange || !creditRange) { - console.warn('[DUNGEON SYSTEM] Incomplete rewards defined for dungeon:', dungeon.id); - return rewards; - } - - rewards.experience = Math.floor(Math.random() * (expRange.max - expRange.min + 1)) + expRange.min; - rewards.credits = Math.floor(Math.random() * (creditRange.max - creditRange.min + 1)) + creditRange.min; - - // Add death penalty - const totalDeaths = Array.from(instance.deaths.values()).reduce((sum, deaths) => sum + deaths, 0); - const deathPenalty = Math.floor(totalDeaths * 0.1); // 10% penalty per death - rewards.experience = Math.max(0, rewards.experience - deathPenalty); - rewards.credits = Math.max(0, rewards.credits - (deathPenalty * 2)); - - // Add items (chance based on performance) - const itemChance = Math.max(0.1, 0.5 - (totalDeaths * 0.1)); // Lower chance with more deaths - if (Math.random() < itemChance) { - const itemPool = dungeon.rewards.items; - if (itemPool && itemPool.length > 0) { - rewards.items.push(itemPool[Math.floor(Math.random() * itemPool.length)]); - } - } - return rewards; } - - getAvailableDungeons(playerLevel, playerCount = 1) { - return Array.from(this.dungeons.values()).filter(dungeon => - dungeon.minLevel <= playerLevel && - dungeon.maxLevel >= playerLevel && - dungeon.maxPlayers >= playerCount - ); - } - - moveToNextRoom(instanceId, userId) { - console.log('[DUNGEON SYSTEM] moveToNextRoom called:', { instanceId, userId }); - - const instance = this.getInstance(instanceId); - if (!instance) { - throw new Error('Instance not found'); - } - - if (!instance.players.has(userId)) { - throw new Error('Player not in instance'); - } - - const dungeon = this.getDungeon(instance.dungeonId); - - console.log('[DUNGEON SYSTEM] Current state:', { - currentEncounter: instance.currentEncounter, - totalEncounters: dungeon.encounters.length, - dungeonId: dungeon.id - }); - - // Move to next encounter - instance.currentEncounter++; - - console.log('[DUNGEON SYSTEM] After increment:', { - newEncounter: instance.currentEncounter, - isComplete: instance.currentEncounter >= dungeon.encounters.length - }); - - // Check if dungeon is complete - if (instance.currentEncounter >= dungeon.encounters.length) { - return this.completeDungeon(instanceId); - } - - // Get next encounter - const encounter = dungeon.encounters[instance.currentEncounter]; - - console.log('[DUNGEON SYSTEM] Next encounter:', { - encounterIndex: instance.currentEncounter, - encounterType: encounter.type, - encounterName: encounter.name, - enemies: encounter.enemies - }); - - return { - success: true, - encounter, - encounterIndex: instance.currentEncounter, - instance, - isComplete: false - }; - } - - getDungeonStatistics(userId) { - const instances = Array.from(this.instances.values()).filter(instance => - instance.players.has(userId) - ); - - const completed = instances.filter(instance => instance.status === 'completed'); - const active = instances.filter(instance => instance.status === 'active'); - - return { - totalDungeons: instances.length, - completedDungeons: completed.length, - activeDungeons: active.length, - totalDeaths: Array.from(this.playerInstances.values()).length, - recentCompletions: completed.slice(-5).map(instance => ({ - dungeonId: instance.dungeonId, - completedAt: instance.completedAt, - rewards: instance.rewards - })) - }; - } - - cleanupExpiredInstances() { - const now = Date.now(); - const expiredInstances = []; - - for (const [instanceId, instance] of this.instances) { - const age = now - new Date(instance.startedAt).getTime(); - const maxAge = 2 * 60 * 60 * 1000; // 2 hours - - if (age > maxAge && instance.status === 'active') { - expiredInstances.push(instanceId); - } - } - - // Clean up expired instances - for (const instanceId of expiredInstances) { - const instance = this.instances.get(instanceId); - if (instance) { - for (const playerId of instance.players) { - this.playerInstances.delete(playerId); - } - this.instances.delete(instanceId); - } - } - - return expiredInstances.length; - } } - module.exports = DungeonSystem; diff --git a/GameServer/systems/FleetSystem.js b/GameServer/systems/FleetSystem.js new file mode 100644 index 0000000..6061c96 --- /dev/null +++ b/GameServer/systems/FleetSystem.js @@ -0,0 +1,53 @@ +/** + * Galaxy Strike Online - Fleet System + * Manages player fleets, ship roster, and galaxy movement + */ +class FleetSystem { + constructor(contentLoader) { + this.contentLoader = contentLoader; + } + + /** Return all ship templates from ContentLoader */ + getAllShipTemplates() { + const items = this.contentLoader.getAllItems ? this.contentLoader.getAllItems() : []; + return items.filter(i => i.type === 'ship'); + } + + /** Build fleet summary from playerData */ + getFleetData(playerData) { + const ships = playerData.inventory?.filter(i => i.type === 'ship') || []; + const activeShipId = playerData.stats?.activeShipId || null; + return { + ships, + activeShipId: activeShipId || (ships[0]?.id ?? null), + maxFleetSize: 5 + Math.floor((playerData.stats?.level || 1) / 10), + }; + } + + /** Set active ship — returns updated stats block */ + setActiveShip(playerData, shipId) { + const ship = (playerData.inventory || []).find(i => i.id === shipId && i.type === 'ship'); + if (!ship) throw new Error('Ship not found in inventory'); + playerData.stats = playerData.stats || {}; + playerData.stats.activeShipId = shipId; + // Apply ship stats to player combat stats + playerData.stats.attack = (playerData.stats.baseAttack || 10) + (ship.stats?.attack || 0); + playerData.stats.defense = (playerData.stats.baseDefense || 5) + (ship.stats?.defense || 0); + playerData.stats.speed = (playerData.stats.baseSpeed || 10) + (ship.stats?.speed || 0); + return playerData.stats; + } + + /** Repair a ship (full restore of hull) */ + repairShip(playerData, shipId, creditsAvailable) { + const ship = (playerData.inventory || []).find(i => i.id === shipId && i.type === 'ship'); + if (!ship) throw new Error('Ship not found'); + const missingHull = (ship.stats?.maxHull || ship.stats?.hull || 100) - (ship.stats?.currentHull || ship.stats?.hull || 100); + const repairCost = Math.ceil(missingHull * 2); + if (creditsAvailable < repairCost) throw new Error(`Need ${repairCost} credits to repair`); + ship.stats = ship.stats || {}; + ship.stats.currentHull = ship.stats.maxHull || ship.stats.hull || 100; + return { repairCost, ship }; + } +} + +module.exports = FleetSystem; diff --git a/GameServer/systems/GalaxySystem.js b/GameServer/systems/GalaxySystem.js new file mode 100644 index 0000000..361b8c7 --- /dev/null +++ b/GameServer/systems/GalaxySystem.js @@ -0,0 +1,112 @@ +/** + * Galaxy Strike Online - Galaxy System + * Procedurally generates and manages sectors + */ +class GalaxySystem { + constructor() { + this.GRID_W = 30; + this.GRID_H = 20; + this.TYPES = ['empty','empty','empty','asteroid','asteroid','trade_hub','npc_territory','ruins','void_rift']; + this._sectors = null; + } + + /** Lazy-generate the galaxy grid once */ + getSectors() { + if (!this._sectors) this._sectors = this._generate(); + return this._sectors; + } + + _generate() { + const sectors = {}; + for (let y = 0; y < this.GRID_H; y++) { + for (let x = 0; x < this.GRID_W; x++) { + const id = `${x}_${y}`; + const distFromCenter = Math.hypot(x - 15, y - 10) / 18; + // Richer regions further from core + const typeRoll = Math.random(); + let type = 'empty'; + if (typeRoll > 0.70) type = 'asteroid'; + if (typeRoll > 0.88) type = 'npc_territory'; + if (typeRoll > 0.94) type = 'trade_hub'; + if (typeRoll > 0.97 && distFromCenter > 0.4) type = 'ruins'; + if (typeRoll > 0.99 && distFromCenter > 0.6) type = 'void_rift'; + + const richness = Math.random(); + sectors[id] = { + id, x, y, type, + name: this._sectorName(x, y), + threat: Math.min(10, Math.floor(distFromCenter * 10 + Math.random() * 3)), + richness: type === 'asteroid' ? (0.3 + richness * 0.7) : 0, + owner: null, + explored: false, + }; + } + } + // Always make start sector safe + sectors['15_10'] = { id:'15_10', x:15, y:10, type:'trade_hub', name:'New Haven', threat:0, richness:0, owner:null, explored:true }; + return sectors; + } + + _sectorName(x, y) { + const prefixes = ['Alpha','Beta','Gamma','Delta','Sigma','Tau','Zeta','Omega','Nova','Vega','Lyra','Cygni']; + const suffixes = ['Prime','Station','Reach','Deep','Expanse','Crossing','Rift','Gate','Void','Fields']; + const seed = (x * 31 + y * 17) % (prefixes.length * suffixes.length); + return prefixes[seed % prefixes.length] + ' ' + suffixes[Math.floor(seed / prefixes.length) % suffixes.length]; + } + + /** Returns the sector or null */ + getSector(id) { + return this.getSectors()[id] || null; + } + + /** Mark a sector explored by a player */ + exploreSector(sectorId, playerData) { + const sector = this.getSector(sectorId); + if (!sector) throw new Error('Sector not found'); + const explored = playerData.exploredSectors || []; + if (!explored.includes(sectorId)) { + explored.push(sectorId); + playerData.exploredSectors = explored; + } + return sector; + } + + /** Return list of sectors visible to player (explored + adjacent) */ + getVisibleSectors(playerData) { + const explored = new Set(playerData.exploredSectors || ['15_10']); + const visible = new Set(explored); + // Reveal adjacent sectors to all explored + for (const id of explored) { + const [x, y] = id.split('_').map(Number); + for (const [dx, dy] of [[-1,0],[1,0],[0,-1],[0,1]]) { + const nid = `${x+dx}_${y+dy}`; + if (this.getSector(nid)) visible.add(nid); + } + } + const all = this.getSectors(); + return Array.from(visible).map(id => { + const s = { ...all[id] }; + s.explored = explored.has(id); + if (!s.explored) { + // Fog: hide details of unexplored but adjacent sectors + s.name = '???'; + s.richness = 0; + s.owner = null; + } + return s; + }); + } + + /** Player claims/sets base in a sector */ + claimSector(sectorId, playerData) { + const sector = this.getSector(sectorId); + if (!sector) throw new Error('Sector not found'); + if (sector.type === 'void_rift') throw new Error('Cannot claim Void Rift sectors'); + sector.owner = playerData.userId; + this.exploreSector(sectorId, playerData); + playerData.homeSector = sectorId; + return sector; + } +} + +module.exports = GalaxySystem; diff --git a/GameServer/systems/ItemSystem.js b/GameServer/systems/ItemSystem.js index 22d411e..c52880f 100644 --- a/GameServer/systems/ItemSystem.js +++ b/GameServer/systems/ItemSystem.js @@ -1,1124 +1,209 @@ /** - * Galaxy Strike Online - Item System - * Centralized item management for shop, inventory, dungeons, and all game systems + * Galaxy Strike Online — Server Item System + * All item/shop data comes from ContentLoader (data/gso/items/). + * No hardcoded item data lives here. */ class ItemSystem { - constructor() { - // Server configuration - will be set dynamically + constructor(contentLoader = null) { this.serverUrl = process.env.SERVER_URL || 'http://localhost:3002'; - - // 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', - texturePath: 'images/ships/starter_cruiser_common.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', - texturePath: 'images/ships/starter_cruiser_uncommon.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', - texturePath: 'images/ships/starter_cruiser_rare.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', - texturePath: 'images/ships/interceptor_common.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', - texturePath: 'images/ships/interceptor_uncommon.png', - stats: { attack: 15, speed: 22, defense: 10, hull: 95 }, - categories: ['shop', 'dungeon_reward'], - requirements: { level: 7 }, - stackable: false - }, - { - id: 'heavy_cruiser_rare', - name: 'Heavy Cruiser', - type: 'ship', - rarity: 'rare', - price: 35000, - currency: 'credits', - description: 'Powerful heavy cruiser with superior firepower and armor', - texturePath: 'images/ships/heavy_cruiser_rare.png', - stats: { attack: 28, speed: 8, defense: 25, hull: 200 }, - categories: ['shop', 'dungeon_reward'], - requirements: { level: 12 }, - stackable: false + this._loader = contentLoader; + + if (contentLoader) { + console.log(`[ITEM SYSTEM] ${contentLoader.items.size} items available from ContentLoader`); + } else { + console.warn('[ITEM SYSTEM] No ContentLoader provided — item catalog is empty'); + } + } + + setServerUrl(url) { this.serverUrl = url; } + + // ── Catalog access ───────────────────────────────────────────────────── + + getAllItems() { return this._loader ? this._loader.getAllItems() : []; } + getItem(id) { return this._loader ? this._loader.getItem(id) : null; } + getItemsByType(type) { return this._loader ? this._loader.getItemsByType(type) : []; } + + /** + * Returns all items that include 'shop' in their categories array. + * Grouped by item type: { ship: [...], weapon: [...], ... } + */ + getShopItemsByCategory() { + const out = {}; + for (const item of this.getAllItems()) { + if (!Array.isArray(item.categories) || !item.categories.includes('shop')) continue; + const t = item.type || 'misc'; + if (!out[t]) out[t] = []; + out[t].push(this._normaliseForClient(item)); + } + return out; + } + + /** + * Flat array of all shop items. + * Called as getRandomShopItems() by legacy server.js code — returns the same thing, + * just grouped (matching the expected { category: [items] } shape). + */ + getRandomShopItems() { + return this.getShopItemsByCategory(); + } + + /** + * All shop items for a single type/category. + * Called as getRandomItemsByCategory(category) by legacy server.js code. + */ + getRandomItemsByCategory(category) { + return this.getAllItems() + .filter(i => + Array.isArray(i.categories) && i.categories.includes('shop') && + i.type === category + ) + .map(i => this._normaliseForClient(i)); + } + + // ── Shop helpers ─────────────────────────────────────────────────────── + + /** + * Find an item that is available in the shop. + * Returns null if the item doesn't exist or isn't in the shop. + */ + findShopItem(itemId) { + const item = this.getItem(itemId); + if (!item) return null; + if (!Array.isArray(item.categories) || !item.categories.includes('shop')) return null; + return item; + } + + /** + * Validate a purchase against player wallet. + * Returns { valid: true } or { valid: false, error: '...' } + */ + validatePurchase(item, playerStats, quantity = 1) { + const currency = item.currency || 'credits'; + const totalCost = (item.price || 0) * quantity; + + if (currency === 'credits' && (playerStats.credits || 0) < totalCost) { + return { valid: false, error: 'Not enough credits' }; + } + if (currency === 'gems' && (playerStats.gems || 0) < totalCost) { + return { valid: false, error: 'Not enough gems' }; + } + return { valid: true, totalCost, currency }; + } + + /** + * Apply a purchase to playerData (mutates directly, like the original server did). + * Returns the purchase summary for the response payload. + */ + applyPurchase(item, playerData, quantity = 1) { + const currency = item.currency || 'credits'; + const totalCost = (item.price || 0) * quantity; + + // Deduct currency + if (currency === 'credits') playerData.stats.credits = (playerData.stats.credits || 0) - totalCost; + if (currency === 'gems') playerData.stats.gems = (playerData.stats.gems || 0) - totalCost; + + // Grant item + switch (item.type) { + case 'decoration': + if (!playerData.starbase) playerData.starbase = { wallpaper: null, ownedWallpapers: [], unlockedRooms: [] }; + if (item.subtype === 'wallpaper') { + if (!playerData.starbase.ownedWallpapers) playerData.starbase.ownedWallpapers = []; + if (!playerData.starbase.ownedWallpapers.includes(item.id)) { + playerData.starbase.ownedWallpapers.push(item.id); + } + } else if (item.subtype === 'room_unlock') { + if (!playerData.starbase.unlockedRooms) playerData.starbase.unlockedRooms = []; + if (item.roomId && !playerData.starbase.unlockedRooms.includes(item.roomId)) { + playerData.starbase.unlockedRooms.push(item.roomId); + } } - ], - - // Materials - materials: [ - { - id: 'steel_plating', - name: 'Steel Plating', - type: 'material', - rarity: 'common', - price: 100, - currency: 'credits', - description: 'Basic armor material for ship upgrades', - texture: 'http://localhost:3002/images/items/materials/steel_plating.png', - 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', - texture: 'http://localhost:3002/images/items/materials/energy_crystal.png', - 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', - texture: 'http://localhost:3002/images/items/materials/quantum_core.png', - 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', - texture: 'http://localhost:3002/images/items/materials/dark_matter_fragment.png', - categories: ['shop', 'dungeon_loot', 'crafting'], - requirements: { level: 15 }, - stackable: true, - maxStack: 10, - effects: { attack: 20, defense: 20 } - }, - { - id: 'titanium_alloy', - name: 'Titanium Alloy', - type: 'material', - rarity: 'uncommon', - price: 300, - currency: 'credits', - description: 'Lightweight yet durable metal for ship construction', - texture: 'http://localhost:3002/images/items/materials/titanium_alloy.png', - categories: ['shop', 'dungeon_loot', 'crafting'], - requirements: { level: 8 }, - stackable: true, - maxStack: 75, - effects: { defense: 8, speed: 3 } - }, - { - id: 'plasma_coil', - name: 'Plasma Coil', - type: 'material', - rarity: 'rare', - price: 750, - currency: 'credits', - description: 'High-energy component for weapon systems', - texture: 'http://localhost:3002/images/items/materials/plasma_coil.png', - categories: ['shop', 'dungeon_loot', 'crafting'], - requirements: { level: 12 }, - stackable: true, - maxStack: 30, - effects: { attack: 12, energy: 8 } - }, - { - id: 'nanite_gel', - name: 'Nanite Gel', - type: 'material', - rarity: 'epic', - price: 1200, - currency: 'gems', - description: 'Self-repairing nanomachines in gel form', - texture: 'http://localhost:3002/images/items/materials/nanite_gel.png', - categories: ['shop', 'dungeon_loot', 'crafting'], - requirements: { level: 18 }, - stackable: true, - maxStack: 15, - effects: { hull_repair: 25, defense: 5 } - }, - { - id: 'cryo_crystal', - name: 'Cryo Crystal', - type: 'material', - rarity: 'rare', - price: 600, - currency: 'credits', - description: 'Frozen crystal that enhances cooling systems', - texture: 'http://localhost:3002/images/items/materials/cryo_crystal.png', - categories: ['shop', 'dungeon_loot', 'crafting'], - requirements: { level: 10 }, - stackable: true, - maxStack: 40, - effects: { energy: 15, speed: 5 } - }, - { - id: 'void_shard', - name: 'Void Shard', - type: 'material', - rarity: 'legendary', - price: 2500, - currency: 'gems', - description: 'Fragment of void space with reality-bending properties', - texture: 'http://localhost:3002/images/items/materials/void_shard.png', - categories: ['shop', 'dungeon_loot', 'crafting'], - requirements: { level: 20 }, - stackable: true, - maxStack: 8, - effects: { attack: 15, defense: 15, speed: 10 } + break; + + case 'ship': + if (!playerData.ownedShips) playerData.ownedShips = []; + if (!playerData.ownedShips.includes(item.id)) playerData.ownedShips.push(item.id); + break; + + case 'cosmetic': + if (!playerData.ownedCosmetics) playerData.ownedCosmetics = []; + if (!playerData.ownedCosmetics.includes(item.id)) playerData.ownedCosmetics.push(item.id); + break; + + case 'weapon': + case 'armour': + case 'consumable': + case 'material': + case 'hullPlating': + default: + if (!playerData.inventory) playerData.inventory = { items: [] }; + if (!playerData.inventory.items) playerData.inventory.items = []; + // Stack consumables/materials; unique otherwise + const existing = playerData.inventory.items.find(i => i.id === item.id && item.stackable); + if (existing) { + existing.quantity = (existing.quantity || 1) + quantity; + } else { + playerData.inventory.items.push({ + id: item.id, + name: item.name, + type: item.type, + quantity: quantity, + acquired: new Date().toISOString() + }); } - ], - - // Consumables - consumables: [ - { - id: 'repair_kit', - name: 'Repair Kit', - type: 'consumable', - rarity: 'common', - price: 50, - currency: 'credits', - description: 'Instantly repairs ship damage', - texture: 'http://localhost:3002/images/items/consumables/repair_kit.png', - 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', - texture: 'http://localhost:3002/images/items/consumables/energy_boost.png', - 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', - texture: 'http://localhost:3002/images/items/consumables/shield_recharge.png', - 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', - texture: 'http://localhost:3002/images/items/consumables/ammo_pack.png', - 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 - }, - { - id: 'speed_boost', - name: 'Speed Boost', - type: 'consumable', - rarity: 'uncommon', - price: 200, - currency: 'credits', - description: 'Temporary speed increase for quick escapes', - texture: 'http://localhost:3002/images/items/consumables/speed_boost.png', - categories: ['shop', 'dungeon_loot'], - requirements: { level: 6 }, - stackable: true, - maxStack: 8, - effects: { speed_multiplier: 1.3, duration: 30000 }, // 30 seconds - consumable: true, - cooldown: 90000 // 1.5 minutes - }, - { - id: 'nanite_repair', - name: 'Nanite Repair', - type: 'consumable', - rarity: 'epic', - price: 500, - currency: 'credits', - description: 'Advanced nanites that continuously repair hull damage', - texture: 'http://localhost:3002/images/items/consumables/nanite_repair.png', - categories: ['shop', 'dungeon_loot'], - requirements: { level: 12 }, - stackable: true, - maxStack: 3, - effects: { hull_repair_rate: 10, duration: 60000 }, // 10 HP/sec for 1 minute - consumable: true, - cooldown: 180000 // 3 minutes - }, - { - id: 'stealth_field', - name: 'Stealth Field Generator', - type: 'consumable', - rarity: 'rare', - price: 400, - currency: 'credits', - description: 'Temporary invisibility to enemy sensors', - texture: 'http://localhost:3002/images/items/consumables/stealth_field.png', - categories: ['shop', 'dungeon_loot'], - requirements: { level: 10 }, - stackable: true, - maxStack: 4, - effects: { stealth: true, duration: 20000 }, // 20 seconds - consumable: true, - cooldown: 150000 // 2.5 minutes - }, - { - id: 'emergency_warp', - name: 'Emergency Warp Core', - type: 'consumable', - rarity: 'legendary', - price: 1000, - currency: 'gems', - description: 'Instant emergency teleport to safety', - texture: 'http://localhost:3002/images/items/consumables/emergency_warp.png', - categories: ['shop', 'dungeon_loot'], - requirements: { level: 15 }, - stackable: true, - maxStack: 2, - effects: { emergency_escape: true }, - consumable: true, - cooldown: 300000 // 5 minutes - }, - { - id: 'combat_stim', - name: 'Combat Stimulant', - type: 'consumable', - rarity: 'uncommon', - price: 250, - currency: 'credits', - description: 'Enhances combat abilities for short duration', - texture: 'http://localhost:3002/images/items/consumables/combat_stim.png', - categories: ['shop', 'dungeon_loot'], - requirements: { level: 7 }, - stackable: true, - maxStack: 6, - effects: { attack: 10, speed: 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', - texture: 'http://localhost:3002/images/items/cosmetics/cool_paint_job.png', - 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', - texture: 'http://localhost:3002/images/items/cosmetics/neon_lights.png', - 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', - texture: 'http://localhost:3002/images/items/cosmetics/golden_trim.png', - 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', - texture: 'http://localhost:3002/images/items/cosmetics/custom_decal.png', - categories: ['shop'], - requirements: { level: 10 }, - stackable: false, - cosmetic: true, - slot: 'engine' - }, - { - id: 'skull_decal', - name: 'Skull Decal', - type: 'cosmetic', - rarity: 'uncommon', - price: 1500, - currency: 'credits', - description: 'Intimidating skull decal for your ship', - texture: 'http://localhost:3002/images/items/cosmetics/skull_decal.png', - categories: ['shop'], - requirements: { level: 3 }, - stackable: false, - cosmetic: true, - slot: 'decal' - }, - { - id: 'neon_lights', - name: 'Neon Underglow', - type: 'cosmetic', - rarity: 'rare', - price: 3000, - currency: 'credits', - description: 'Colorful neon lights under your ship', - texture: 'http://localhost:3002/images/items/cosmetics/neon_lights.png', - categories: ['shop'], - requirements: { level: 8 }, - stackable: false, - cosmetic: true, - slot: 'lights' - }, - { - id: 'chrome_finish', - name: 'Chrome Finish', - type: 'cosmetic', - rarity: 'epic', - price: 5000, - currency: 'credits', - description: 'Reflective chrome coating for premium look', - texture: 'http://localhost:3002/images/items/cosmetics/chrome_finish.png', - categories: ['shop'], - requirements: { level: 12 }, - stackable: false, - cosmetic: true, - slot: 'finish' - }, - { - id: 'holographic_display', - name: 'Holographic Display', - type: 'cosmetic', - rarity: 'legendary', - price: 8000, - currency: 'gems', - description: 'Advanced holographic projection system', - texture: 'http://localhost:3002/images/items/cosmetics/holographic_display.png', - categories: ['shop'], - requirements: { level: 15 }, - stackable: false, - cosmetic: true, - slot: 'display' - }, - { - id: 'carbon_fiber', - name: 'Carbon Fiber Body', - type: 'cosmetic', - rarity: 'uncommon', - price: 2000, - currency: 'credits', - description: 'Lightweight carbon fiber ship body', - texture: 'http://localhost:3002/images/items/cosmetics/carbon_fiber.png', - categories: ['shop'], - requirements: { level: 6 }, - stackable: false, - cosmetic: true, - slot: 'body' - } - ], - - // Weapons - weapons: [ - { - id: 'laser_cannon_common', - name: 'Laser Cannon', - type: 'weapon', - rarity: 'common', - price: 1000, - currency: 'credits', - description: 'Basic laser weapon for beginners', - texture: 'http://localhost:3002/images/weapons/laser_pistol_common.png', - 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', - texture: 'http://localhost:3002/images/weapons/plasma_rifle_uncommon.png', - 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', - texture: 'http://localhost:3002/images/weapons/plasma_rifle_rare.png', - categories: ['shop', 'dungeon_loot'], - requirements: { level: 10 }, - stackable: false, - stats: { attack: 30, range: 150, fire_rate: 1 } - }, - { - id: 'ion_cannon_uncommon', - name: 'Ion Cannon', - type: 'weapon', - rarity: 'uncommon', - price: 3500, - currency: 'credits', - description: 'Ion-based weapon effective against shields', - texture: 'http://localhost:3002/images/weapons/ion_cannon_uncommon.png', - categories: ['shop', 'dungeon_loot'], - requirements: { level: 6 }, - stackable: false, - stats: { attack: 22, range: 110, fire_rate: 1.8, shield_damage: 1.5 } - }, - { - id: 'missile_launcher_rare', - name: 'Missile Launcher', - type: 'weapon', - rarity: 'rare', - price: 8000, - currency: 'credits', - description: 'Homing missile system for heavy damage', - texture: 'http://localhost:3002/images/weapons/missile_launcher_rare.png', - categories: ['shop', 'dungeon_loot'], - requirements: { level: 12 }, - stackable: false, - stats: { attack: 45, range: 200, fire_rate: 0.8, explosive: true } - }, - { - id: 'plasma_thrower_epic', - name: 'Plasma Thrower', - type: 'weapon', - rarity: 'epic', - price: 12000, - currency: 'credits', - description: 'Continuous plasma stream weapon', - texture: 'http://localhost:3002/images/weapons/plasma_thrower_epic.png', - categories: ['shop', 'dungeon_loot'], - requirements: { level: 15 }, - stackable: false, - stats: { attack: 35, range: 80, fire_rate: 5, continuous: true } - }, - { - id: 'railgun_legendary', - name: 'Railgun', - type: 'weapon', - rarity: 'legendary', - price: 20000, - currency: 'gems', - description: 'Electromagnetic railgun with piercing shots', - texture: 'http://localhost:3002/images/weapons/railgun_legendary.png', - categories: ['shop', 'dungeon_loot'], - requirements: { level: 18 }, - stackable: false, - stats: { attack: 60, range: 300, fire_rate: 0.5, piercing: true } - }, - { - id: 'pulse_cannon_common', - name: 'Pulse Cannon', - type: 'weapon', - rarity: 'common', - price: 1500, - currency: 'credits', - description: 'Rapid-fire pulse weapon', - texture: 'http://localhost:3002/images/weapons/pulse_cannon_common.png', - categories: ['shop', 'dungeon_loot'], - requirements: { level: 2 }, - stackable: false, - stats: { attack: 8, range: 90, fire_rate: 4 } - } - ], - - // Armors - armors: [ - { - id: 'basic_shield_common', - name: 'Basic Shield Generator', - type: 'armor', - rarity: 'common', - price: 800, - currency: 'credits', - description: 'Basic shield protection for beginners', - texture: 'http://localhost:3002/images/armors/light_armor_common.png', - 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', - texture: 'http://localhost:3002/images/armors/light_armor_uncommon.png', - 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', - texture: 'http://localhost:3002/images/armors/light_armor_rare.png', - categories: ['shop', 'dungeon_loot'], - requirements: { level: 10 }, - stackable: false, - stats: { defense: 25, shield_capacity: 200, recharge_rate: 12 } - }, - { - id: 'plasma_barrier_uncommon', - name: 'Plasma Barrier', - type: 'armor', - rarity: 'uncommon', - price: 3000, - currency: 'credits', - description: 'Plasma-based barrier system', - texture: 'http://localhost:3002/images/armors/plasma_barrier_uncommon.png', - categories: ['shop', 'dungeon_loot'], - requirements: { level: 7 }, - stackable: false, - stats: { defense: 18, shield_capacity: 120, recharge_rate: 10, energy_resistance: 0.8 } - }, - { - id: 'reactive_armor_epic', - name: 'Reactive Armor', - type: 'armor', - rarity: 'epic', - price: 9000, - currency: 'credits', - description: 'Adaptive armor that responds to damage types', - texture: 'http://localhost:3002/images/armors/reactive_armor_epic.png', - categories: ['shop', 'dungeon_loot'], - requirements: { level: 14 }, - stackable: false, - stats: { defense: 35, shield_capacity: 250, recharge_rate: 15, adaptive: true } - }, - { - id: 'kinetic_dampener_rare', - name: 'Kinetic Dampener', - type: 'armor', - rarity: 'rare', - price: 6000, - currency: 'credits', - description: 'Specialized armor against kinetic weapons', - texture: 'http://localhost:3002/images/armors/kinetic_dampener_rare.png', - categories: ['shop', 'dungeon_loot'], - requirements: { level: 11 }, - stackable: false, - stats: { defense: 22, shield_capacity: 180, recharge_rate: 11, kinetic_resistance: 0.7 } - }, - { - id: 'phase_shifter_legendary', - name: 'Phase Shifter', - type: 'armor', - rarity: 'legendary', - price: 15000, - currency: 'gems', - description: 'Experimental phase-shifting armor system', - texture: 'http://localhost:3002/images/armors/phase_shifter_legendary.png', - categories: ['shop', 'dungeon_loot'], - requirements: { level: 18 }, - stackable: false, - stats: { defense: 40, shield_capacity: 300, recharge_rate: 20, phase_shift: 0.2 } - }, - { - id: 'carbon_plate_common', - name: 'Carbon Plate Armor', - type: 'armor', - rarity: 'common', - price: 1200, - currency: 'credits', - description: 'Lightweight carbon composite armor', - texture: 'http://localhost:3002/images/armors/carbon_plate_common.png', - categories: ['shop', 'dungeon_loot'], - requirements: { level: 3 }, - stackable: false, - stats: { defense: 12, shield_capacity: 70, recharge_rate: 6, weight_reduction: 0.1 } - } - ], - - // 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' - } - ] + break; + } + + return { + totalCost, + currency, + newBalance: currency === 'credits' ? playerData.stats.credits : playerData.stats.gems }; - - // 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 + + // ── Packet builder helpers ───────────────────────────────────────────── + + /** Builds the full shopItemsReceived payload */ + buildShopResponse() { + return { + success: true, + shopItems: this.getShopItemsByCategory(), + timestamp: new Date().toISOString() }; - - 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]; } - + + /** Builds the itemDetailsReceived payload */ + buildItemDetailResponse(itemId) { + const item = this.getItem(itemId); + if (!item) return { success: false, error: `Item '${itemId}' not found` }; + return { success: true, item: this._normaliseForClient(item) }; + } + + // ── Internal normalisation ───────────────────────────────────────────── + /** - * Get item statistics + * Normalise an item object into the flat shape the client expects. + * Handles both bare-item format (what ContentLoader returns after unwrapping templates) + * and the raw wrapped format { templates: { : {...} } }. */ - getItemStats() { - const stats = { - totalItems: this.itemLookup.size, - byCategory: {}, - byRarity: {}, - byType: {} + _normaliseForClient(item) { + return { + id: item.id, + name: item.name || item.id, + type: item.type || 'misc', + rarity: item.rarity || 'common', + price: item.price || 0, + currency: item.currency || 'credits', + description: item.description || '', + texture: item.texture || '', + texturePath: item.texture || '', + stats: item.stats || {}, + categories: item.categories || [], + requirements:item.requirements || {}, + stackable: item.stackable ?? false, + maxStack: item.maxStack || 1, + meta: item.meta || {} }; - - 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 }; - } - - /** - * Set server URL dynamically - */ - setServerUrl(url) { - this.serverUrl = url; - console.log(`[ITEM SYSTEM] Server URL set to: ${this.serverUrl}`); - } - - /** - * Process item to add full texture URL - */ - processItem(item) { - const processedItem = { ...item }; - - // Add full texture URL if texturePath exists - if (item.texturePath) { - processedItem.texture = `${this.serverUrl}/${item.texturePath}`; - } - - // Fallback to old texture field if exists - else if (item.texture) { - processedItem.texture = item.texture; - } - - // Default placeholder - else { - processedItem.texture = `${this.serverUrl}/images/ui/placeholder.png`; - } - - return processedItem; - } - - /** - * Get items with processed URLs - */ - getItemsByType(type) { - const items = this.itemCatalog[type] || []; - return items.map(item => this.processItem(item)); - } - - /** - * Get all items with processed URLs - */ - getAllItems() { - const allItems = {}; - - Object.keys(this.itemCatalog).forEach(type => { - allItems[type] = this.getItemsByType(type); - }); - - return allItems; - } - - /** - * Get random shop items with processed URLs - 6 items per category - */ - getRandomShopItems(count = 6) { - const shopItems = {}; - - // Get 6 random items from each category - Object.keys(this.itemCatalog).forEach(category => { - const categoryItems = this.itemCatalog[category] || []; - const selectedItems = []; - - if (categoryItems.length > 0) { - // Randomly select items from this category - const shuffled = [...categoryItems].sort(() => Math.random() - 0.5); - - for (let i = 0; i < Math.min(count, shuffled.length); i++) { - selectedItems.push(this.processItem(shuffled[i])); - } - } - - shopItems[category] = selectedItems; - }); - - return shopItems; - } - - /** - * Get random items for a specific category - */ - getRandomItemsByCategory(category, count = 6) { - const categoryItems = this.itemCatalog[category] || []; - const selectedItems = []; - - if (categoryItems.length > 0) { - const shuffled = [...categoryItems].sort(() => Math.random() - 0.5); - - for (let i = 0; i < Math.min(count, shuffled.length); i++) { - selectedItems.push(this.processItem(shuffled[i])); - } - } - - return selectedItems; } } diff --git a/GameServer/systems/QuestSystem.js b/GameServer/systems/QuestSystem.js index f115046..ac6e76e 100644 --- a/GameServer/systems/QuestSystem.js +++ b/GameServer/systems/QuestSystem.js @@ -1,823 +1,61 @@ /** - * Galaxy Strike Online - Server Quest System - * Manages quests, progress tracking, and rewards + * QuestSystem — thin wrapper around ContentLoader for quest definitions. */ - class QuestSystem { - constructor() { - this.quests = new Map(); - this.playerQuests = new Map(); // userId -> quest data - this.initializeQuests(); + constructor(contentLoader) { + this.loader = contentLoader; + this.playerQuests = new Map(); } - - initializeQuests() { - // Tutorial quests - this.addQuest('tutorial_first_battle', { - name: 'First Battle', - description: 'Complete your first battle to learn the basics', - type: 'tutorial', - difficulty: 'easy', - requirements: { - battlesWon: 1 - }, - rewards: { - experience: 100, - credits: 500, - items: ['health_kit', 'energy_pack'] - }, - prerequisites: [], - repeatable: false, - 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', - description: 'Win 10 battles to prove your combat skills', - type: 'combat', - difficulty: 'medium', - requirements: { - battlesWon: 10, - enemiesDefeated: 50 - }, - rewards: { - experience: 500, - credits: 2000, - items: ['basic_armor'] - }, - prerequisites: ['tutorial_first_battle'], - repeatable: false, - timeLimit: null - }); - - // Crafting quests - this.addQuest('novice_crafter', { - name: 'Novice Crafter', - description: 'Craft 5 items to learn the basics of crafting', - type: 'crafting', - difficulty: 'easy', - requirements: { - itemsCrafted: 5 - }, - rewards: { - experience: 200, - credits: 1000, - craftingExperience: 100 - }, - prerequisites: [], - repeatable: false, - timeLimit: null - }); - - // Exploration quests - this.addQuest('explorer_spirit', { - name: 'Explorer Spirit', - description: 'Discover 5 different locations in the galaxy', - type: 'exploration', - difficulty: 'medium', - requirements: { - locationsDiscovered: 5 - }, - rewards: { - experience: 300, - credits: 1500, - items: ['navigation_device'] - }, - prerequisites: ['tutorial_first_battle'], - repeatable: false, - timeLimit: null - }); - - // Daily quests - this.addQuest('daily_bounty', { - name: 'Daily Bounty', - description: 'Complete 3 battles today', - type: 'daily', - difficulty: 'easy', - requirements: { - battlesWon: 3 - }, - rewards: { - experience: 150, - credits: 750 - }, - prerequisites: [], - repeatable: true, - timeLimit: 86400000 // 24 hours - }); - - // Weekly quests - this.addQuest('weekly_champion', { - name: 'Weekly Champion', - description: 'Win 20 battles this week', - type: 'weekly', - difficulty: 'hard', - requirements: { - battlesWon: 20 - }, - rewards: { - experience: 1000, - credits: 5000, - items: ['rare_weapon_part'] - }, - prerequisites: [], - repeatable: true, - timeLimit: 604800000 // 7 days - }); - } - - addQuest(id, quest) { - this.quests.set(id, { - id, - ...quest, - createdAt: new Date().toISOString() - }); - } - - getQuest(id) { - return this.quests.get(id); - } - - getAllQuests() { - return Array.from(this.quests.values()); - } - - getQuestsByType(type) { - return Array.from(this.quests.values()).filter(quest => quest.type === type); - } - - getQuestsByDifficulty(difficulty) { - return Array.from(this.quests.values()).filter(quest => quest.difficulty === difficulty); - } - + + getAllQuests() { return this.loader.getAllQuests(); } + getQuest(id) { return this.loader.getQuest(id); } + getQuestsByCategory(cat) { return this.loader.getQuestsByCategory(cat); } + getQuestCategories() { return this.loader.getQuestCategories(); } + initializePlayerData(userId) { - if (!this.playerQuests.has(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); + if (this.playerQuests.has(userId)) return this.playerQuests.get(userId); + const data = { active: {}, completed: [], failed: [] }; + this.playerQuests.set(userId, data); + return data; } - - 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); - } - - getAvailableQuests(userId) { - const playerData = this.getPlayerData(userId); - const availableQuests = []; - - for (const [questId, quest] of this.quests) { - // Skip if already active or completed (for non-repeatable quests) - if (playerData.activeQuests.has(questId)) continue; - if (playerData.completedQuests.has(questId) && !quest.repeatable) continue; - - // Check prerequisites - const prerequisitesMet = quest.prerequisites.every(prereqId => - playerData.completedQuests.has(prereqId) - ); - - if (prerequisitesMet) { - availableQuests.push({ - ...quest, - status: playerData.activeQuests.has(questId) ? 'active' : 'available' - }); - } - } - - return availableQuests; - } - + + getPlayerData(userId) { return this.initializePlayerData(userId); } + getPlayerQuests(userId) { return this.initializePlayerData(userId); } + startQuest(userId, questId) { - const quest = this.getQuest(questId); - if (!quest) { - throw new Error('Quest not found'); + const quest = this.loader.getQuest(questId); + if (!quest) return { success: false, error: 'Quest not found' }; + const pd = this.initializePlayerData(userId); + if (pd.active[questId]) return { success: false, error: 'Already active' }; + const objectives = {}; + for (const obj of (quest.objectives || [])) { + objectives[obj.id] = { progress: 0, complete: false }; } - - const playerData = this.getPlayerData(userId); - - // Check if quest is already active - if (playerData.activeQuests.has(questId)) { - throw new Error('Quest already active'); - } - - // Check if quest is completed and not repeatable - if (playerData.completedQuests.has(questId) && !quest.repeatable) { - throw new Error('Quest already completed'); - } - - // Check prerequisites - const prerequisitesMet = quest.prerequisites.every(prereqId => - playerData.completedQuests.has(prereqId) - ); - - if (!prerequisitesMet) { - throw new Error('Prerequisites not met'); - } - - // Start the quest - const questProgress = { - questId, - startedAt: new Date().toISOString(), - progress: {}, - completed: false, - expiredAt: quest.timeLimit ? new Date(Date.now() + quest.timeLimit).toISOString() : null - }; - - // Initialize progress tracking - for (const requirement of Object.keys(quest.requirements)) { - questProgress.progress[requirement] = 0; - } - - playerData.activeQuests.set(questId, questProgress); - - return { - success: true, - quest, - progress: questProgress - }; + pd.active[questId] = { startedAt: Date.now(), objectives }; + return { success: true, quest }; } - - updateQuestProgress(userId, questId, progressUpdates) { - const playerData = this.getPlayerData(userId); - const activeQuest = playerData.activeQuests.get(questId); - - if (!activeQuest) { - throw new Error('Quest not active'); - } - - // Check if quest is expired - if (activeQuest.expiredAt && new Date() > new Date(activeQuest.expiredAt)) { - playerData.activeQuests.delete(questId); - throw new Error('Quest expired'); - } - - const quest = this.getQuest(questId); - let updated = false; - - // Update progress - for (const [requirement, amount] of Object.entries(progressUpdates)) { - if (quest.requirements[requirement] !== undefined) { - activeQuest.progress[requirement] = Math.min( - activeQuest.progress[requirement] + amount, - quest.requirements[requirement] - ); - updated = true; - } - } - - // Check if quest is completed - const isCompleted = Object.entries(quest.requirements).every( - ([requirement, requiredAmount]) => - activeQuest.progress[requirement] >= requiredAmount - ); - - if (isCompleted && !activeQuest.completed) { - activeQuest.completed = true; - activeQuest.completedAt = new Date().toISOString(); - - return { - success: true, - questCompleted: true, - quest, - progress: activeQuest.progress - }; - } - - return { - success: true, - questCompleted: false, - quest, - progress: activeQuest.progress, - updated - }; + + completeQuest(userId, questId, rewards) { + const pd = this.initializePlayerData(userId); + delete pd.active[questId]; + if (!pd.completed.includes(questId)) pd.completed.push(questId); + return { success: true, questId, rewards }; } - - completeQuest(userId, questId) { - const playerData = this.getPlayerData(userId); - const activeQuest = playerData.activeQuests.get(questId); - - if (!activeQuest) { - throw new Error('Quest not active'); - } - - if (!activeQuest.completed) { - throw new Error('Quest requirements not met'); - } - - const quest = this.getQuest(questId); - - // Move to completed quests - playerData.completedQuests.set(questId, { - ...activeQuest, - quest, - completedAt: new Date().toISOString() - }); - - // Remove from active quests - playerData.activeQuests.delete(questId); - - // Update statistics - playerData.totalQuestsCompleted += 1; - - if (quest.type === 'daily') { - playerData.dailyQuestsCompleted += 1; - } else if (quest.type === 'weekly') { - playerData.weeklyQuestsCompleted += 1; - } - - // Add to history - playerData.questHistory.push({ - questId, - questName: quest.name, - completedAt: new Date().toISOString(), - rewards: quest.rewards - }); - - // Keep only last 100 quest records - if (playerData.questHistory.length > 100) { - playerData.questHistory = playerData.questHistory.slice(-100); - } - - return { - success: true, - quest, - rewards: quest.rewards, - statistics: { - totalCompleted: playerData.totalQuestsCompleted, - dailyCompleted: playerData.dailyQuestsCompleted, - weeklyCompleted: playerData.weeklyQuestsCompleted - } - }; - } - - getPlayerActiveQuests(userId) { - const playerData = this.getPlayerData(userId); - const activeQuests = []; - - for (const [questId, progress] of playerData.activeQuests) { - const quest = this.getQuest(questId); - activeQuests.push({ - ...quest, - progress: progress.progress, - startedAt: progress.startedAt, - expiredAt: progress.expiredAt - }); - } - - return activeQuests; - } - - getPlayerCompletedQuests(userId) { - const playerData = this.getPlayerData(userId); - const completedQuests = []; - - for (const [questId, completion] of playerData.completedQuests) { - completedQuests.push({ - ...completion.quest, - completedAt: completion.completedAt, - progress: completion.progress - }); - } - - return completedQuests; - } - - getQuestStatistics(userId) { - const playerData = this.getPlayerData(userId); - const availableQuests = this.getAvailableQuests(userId); - - return { - activeQuests: playerData.activeQuests.size, - completedQuests: playerData.completedQuests.size, - availableQuests: availableQuests.length, - totalCompleted: playerData.totalQuestsCompleted, - dailyCompleted: playerData.dailyQuestsCompleted, - weeklyCompleted: playerData.weeklyQuestsCompleted, - recentHistory: playerData.questHistory.slice(-10) - }; - } - - resetDailyQuests(userId) { - const playerData = this.getPlayerData(userId); - - // Remove completed daily quests - for (const [questId, quest] of this.quests) { - if (quest.type === 'daily') { - playerData.completedQuests.delete(questId); - playerData.activeQuests.delete(questId); - } - } - - playerData.dailyQuestsCompleted = 0; - - return { success: true, message: 'Daily quests reset' }; - } - - getPlayerQuests(userId) { - console.log('[QUEST SYSTEM] Getting quests for user:', userId); - - // Get or create player data - let playerData = this.playerQuests.get(userId); - if (!playerData) { - playerData = { - activeQuests: new Map(), - completedQuests: new Map(), - failedQuests: new Map() - }; - this.playerQuests.set(userId, playerData); - } - - // Prepare quest data for client - const questData = { - mainQuests: [], - dailyQuests: [], - weeklyQuests: [], - activeQuests: [], - completedQuests: [], - failedQuests: [] - }; - - // Add main quests - for (const [questId, quest] of this.quests) { - if (quest.type === 'main') { - const playerQuest = playerData.activeQuests.get(questId) || playerData.completedQuests.get(questId) || playerData.failedQuests.get(questId); - const status = playerQuest ? playerQuest.status : 'available'; - - questData.mainQuests.push({ - ...quest, - status: status, - objectives: quest.objectives.map(obj => ({ - ...obj, - current: playerQuest && playerQuest.objectives && playerQuest.objectives[obj.id] ? playerQuest.objectives[obj.id].current || 0 : 0 - })) - }); - } - } - - // Add daily quests - for (const [questId, quest] of this.quests) { - if (quest.type === 'daily') { - const playerQuest = playerData.activeQuests.get(questId) || playerData.completedQuests.get(questId) || playerData.failedQuests.get(questId); - const status = playerQuest ? playerQuest.status : 'available'; - - questData.dailyQuests.push({ - ...quest, - status: status, - objectives: quest.objectives.map(obj => ({ - ...obj, - current: playerQuest && playerQuest.objectives && playerQuest.objectives[obj.id] ? playerQuest.objectives[obj.id].current || 0 : 0 - })) - }); - } - } - - // Add weekly quests - for (const [questId, quest] of this.quests) { - if (quest.type === 'weekly') { - const playerQuest = playerData.activeQuests.get(questId) || playerData.completedQuests.get(questId) || playerData.failedQuests.get(questId); - const status = playerQuest ? playerQuest.status : 'available'; - - questData.weeklyQuests.push({ - ...quest, - status: status, - objectives: quest.objectives.map(obj => ({ - ...obj, - current: playerQuest && playerQuest.objectives && playerQuest.objectives[obj.id] ? playerQuest.objectives[obj.id].current || 0 : 0 - })) - }); - } - } - - // Add active quests (for compatibility) - questData.activeQuests = Array.from(playerData.activeQuests.values()); - questData.completedQuests = Array.from(playerData.completedQuests.values()); - questData.failedQuests = Array.from(playerData.failedQuests.values()); - - console.log('[QUEST SYSTEM] Returning quest data:', { - mainQuests: questData.mainQuests.length, - dailyQuests: questData.dailyQuests.length, - weeklyQuests: questData.weeklyQuests.length, - activeQuests: questData.activeQuests.length, - completedQuests: questData.completedQuests.length - }); - - return questData; - } - - completeQuest(userId, questId, rewards = null) { - console.log('[QUEST SYSTEM] Completing quest:', questId, 'for user:', userId); - - const playerData = this.playerQuests.get(userId); - if (!playerData) { - console.log('[QUEST SYSTEM] Player data not found for user:', userId); - return { success: false, error: 'Player not found' }; - } - - const quest = this.quests.get(questId); - if (!quest) { - console.log('[QUEST SYSTEM] Quest not found:', questId); - return { success: false, error: 'Quest not found' }; - } - - // Move quest from active to completed - const activeQuest = playerData.activeQuests.get(questId); - if (activeQuest) { - // Mark all objectives as completed - const completedQuest = { - ...activeQuest, - status: 'completed', - completedAt: Date.now(), - objectives: quest.objectives.map(obj => ({ - ...obj, - current: obj.target - })) - }; - - playerData.activeQuests.delete(questId); - playerData.completedQuests.set(questId, completedQuest); - - console.log('[QUEST SYSTEM] Quest completed successfully:', questId); - return { success: true, quest: completedQuest }; - } else { - // Quest might not be active, try to complete it anyway - const completedQuest = { - ...quest, - status: 'completed', - completedAt: Date.now(), - objectives: quest.objectives.map(obj => ({ - ...obj, - current: obj.target - })) - }; - - playerData.completedQuests.set(questId, completedQuest); - console.log('[QUEST SYSTEM] Quest force-completed:', questId); - return { success: true, quest: completedQuest }; - } - } - - resetWeeklyQuests(userId) { - const playerData = this.getPlayerData(userId); - - // Remove completed weekly quests - for (const [questId, quest] of this.quests) { - if (quest.type === 'weekly') { - playerData.completedQuests.delete(questId); - playerData.activeQuests.delete(questId); - } - } - - playerData.weeklyQuestsCompleted = 0; - - return { success: true, message: 'Weekly quests reset' }; + + updateObjective(userId, questId, objectiveId, amount = 1) { + const pd = this.initializePlayerData(userId); + const state = pd.active[questId]; + if (!state) return null; + const quest = this.loader.getQuest(questId); + const objDef = quest?.objectives?.find(o => o.id === objectiveId); + if (!objDef) return null; + const obj = state.objectives[objectiveId] || { progress: 0, complete: false }; + obj.progress = Math.min(obj.progress + amount, objDef.target); + obj.complete = obj.progress >= objDef.target; + state.objectives[objectiveId] = obj; + const allDone = quest.objectives.every(o => state.objectives[o.id]?.complete); + if (allDone) return this.completeQuest(userId, questId, quest.rewards); + return { questId, objectiveId, progress: obj.progress, completed: false }; } } - module.exports = QuestSystem; diff --git a/GameServer/systems/ResearchSystem.js b/GameServer/systems/ResearchSystem.js new file mode 100644 index 0000000..30b6704 --- /dev/null +++ b/GameServer/systems/ResearchSystem.js @@ -0,0 +1,110 @@ +/** + * Galaxy Strike Online - Research System + * Manages the research tree and player tech progress + */ +const RESEARCH_TREE = { + // ── Weapons ───────────────────────────────────────────────────────────── + basic_lasers: { id:'basic_lasers', name:'Basic Lasers', branch:'weapons', tier:1, cost:{credits:500, gems:0}, time:60, effects:{weaponDamage:10}, requires:[] }, + pulse_lasers: { id:'pulse_lasers', name:'Pulse Lasers', branch:'weapons', tier:2, cost:{credits:1200, gems:0}, time:180, effects:{weaponDamage:25}, requires:['basic_lasers'] }, + advanced_lasers: { id:'advanced_lasers', name:'Advanced Lasers', branch:'weapons', tier:3, cost:{credits:3000, gems:5}, time:600, effects:{weaponDamage:50}, requires:['pulse_lasers'] }, + railgun_tech: { id:'railgun_tech', name:'Railgun Tech', branch:'weapons', tier:2, cost:{credits:2000, gems:0}, time:240, effects:{armorPierce:20}, requires:['basic_lasers'] }, + smart_warheads: { id:'smart_warheads', name:'Smart Warheads', branch:'weapons', tier:4, cost:{credits:9000, gems:10},time:1200, effects:{missileBonus:30}, requires:['railgun_tech'] }, + emp_technology: { id:'emp_technology', name:'EMP Technology', branch:'weapons', tier:3, cost:{credits:4000, gems:5}, time:720, effects:{empChance:15}, requires:['pulse_lasers'] }, + // ── Engineering ───────────────────────────────────────────────────────── + structural_analysis:{ id:'structural_analysis', name:'Structural Analysis', branch:'engineering', tier:1, cost:{credits:400, gems:0}, time:60, effects:{hullBonus:10}, requires:[] }, + nano_composites: { id:'nano_composites', name:'Nano-Composites', branch:'engineering',tier:2, cost:{credits:1000, gems:0}, time:180, effects:{hullBonus:25}, requires:['structural_analysis'] }, + reactive_armor: { id:'reactive_armor', name:'Reactive Armor', branch:'engineering',tier:3, cost:{credits:3500, gems:5}, time:600, effects:{armorBonus:40}, requires:['nano_composites'] }, + improved_engines: { id:'improved_engines', name:'Improved Engines', branch:'engineering',tier:1, cost:{credits:300, gems:0}, time:60, effects:{speedBonus:10}, requires:[] }, + warp_drive: { id:'warp_drive', name:'Warp Drive', branch:'engineering',tier:5, cost:{credits:20000,gems:20},time:3600, effects:{travelSpeed:60}, requires:['improved_engines'] }, + shield_matrix: { id:'shield_matrix', name:'Shield Matrix', branch:'engineering',tier:2, cost:{credits:1200, gems:0}, time:180, effects:{shieldRegen:50}, requires:[] }, + // ── Economy ───────────────────────────────────────────────────────────── + enhanced_mining: { id:'enhanced_mining', name:'Enhanced Mining', branch:'economy', tier:1, cost:{credits:200, gems:0}, time:60, effects:{miningBonus:15}, requires:[] }, + deep_core_drills: { id:'deep_core_drills', name:'Deep-Core Drills', branch:'economy', tier:3, cost:{credits:2500, gems:5}, time:600, effects:{miningBonus:35}, requires:['enhanced_mining'] }, + trade_protocols: { id:'trade_protocols', name:'Trade Protocols', branch:'economy', tier:2, cost:{credits:600, gems:0}, time:120, effects:{tradeFeeReduction:10}, requires:[] }, + construction_eff: { id:'construction_eff', name:'Construction Efficiency', branch:'economy', tier:1, cost:{credits:350, gems:0}, time:60, effects:{buildTimeReduction:10}, requires:[] }, + mass_production: { id:'mass_production', name:'Mass Production', branch:'economy', tier:4, cost:{credits:10000,gems:10},time:1800, effects:{buildTimeReduction:35}, requires:['construction_eff'] }, + // ── Exploration ───────────────────────────────────────────────────────── + long_range_sensors:{ id:'long_range_sensors',name:'Long Range Sensors',branch:'exploration',tier:1, cost:{credits:300, gems:0}, time:60, effects:{sensorRange:2}, requires:[] }, + stealth_systems: { id:'stealth_systems', name:'Stealth Systems', branch:'exploration',tier:3, cost:{credits:5000, gems:10},time:900, effects:{stealthRating:30}, requires:['long_range_sensors'] }, + wormhole_nav: { id:'wormhole_nav', name:'Wormhole Navigation',branch:'exploration',tier:4, cost:{credits:8000, gems:15},time:1200, effects:{warpCooldown:50}, requires:['long_range_sensors'] }, + // ── Defense ───────────────────────────────────────────────────────────── + point_defense: { id:'point_defense', name:'Point Defense', branch:'defense', tier:1, cost:{credits:400, gems:0}, time:60, effects:{missileDefense:20}, requires:[] }, + heavy_armor_plating:{ id:'heavy_armor_plating', name:'Heavy Armor Plating', branch:'defense', tier:4, cost:{credits:12000,gems:15},time:1800, effects:{armorBonus:80}, requires:['reactive_armor'] }, + auto_repair: { id:'auto_repair', name:'Auto-Repair Drones', branch:'defense', tier:3, cost:{credits:4000, gems:5}, time:720, effects:{hullRegen:2}, requires:['shield_matrix'] }, +}; + +class ResearchSystem { + constructor() { + this.tree = RESEARCH_TREE; + } + + getTree() { return Object.values(this.tree); } + + /** Return what a player can currently research */ + getAvailableResearch(playerData) { + const completed = new Set(playerData.research?.completed || []); + const inProgress = playerData.research?.inProgress || null; + return Object.values(this.tree).map(tech => ({ + ...tech, + status: completed.has(tech.id) ? 'completed' + : inProgress?.techId === tech.id ? 'in_progress' + : tech.requires.every(r => completed.has(r)) ? 'available' + : 'locked', + progressPercent: inProgress?.techId === tech.id + ? Math.floor(((Date.now() - inProgress.startedAt) / (tech.time * 1000)) * 100) + : 0, + })); + } + + /** Start researching a technology */ + startResearch(playerData, techId) { + const tech = this.tree[techId]; + if (!tech) throw new Error('Unknown technology'); + + const completed = new Set(playerData.research?.completed || []); + if (completed.has(techId)) throw new Error('Already researched'); + if (playerData.research?.inProgress) throw new Error('Research already in progress'); + if (!tech.requires.every(r => completed.has(r))) throw new Error('Prerequisites not met'); + + const stats = playerData.stats || {}; + if ((stats.credits || 0) < tech.cost.credits) throw new Error(`Need ${tech.cost.credits} credits`); + if ((stats.gems || 0) < tech.cost.gems) throw new Error(`Need ${tech.cost.gems} gems`); + + stats.credits = (stats.credits || 0) - tech.cost.credits; + stats.gems = (stats.gems || 0) - tech.cost.gems; + playerData.stats = stats; + playerData.research = playerData.research || { completed: [], inProgress: null, effects: {} }; + playerData.research.inProgress = { techId, startedAt: Date.now(), completesAt: Date.now() + tech.time * 1000 }; + return { tech, completesAt: playerData.research.inProgress.completesAt }; + } + + /** Check and resolve completed research — call on tick or on request */ + checkCompletion(playerData) { + const ip = playerData.research?.inProgress; + if (!ip || Date.now() < ip.completesAt) return null; + + const tech = this.tree[ip.techId]; + playerData.research.completed = playerData.research.completed || []; + playerData.research.completed.push(ip.techId); + playerData.research.inProgress = null; + // Apply effects + playerData.research.effects = playerData.research.effects || {}; + for (const [k, v] of Object.entries(tech.effects || {})) { + playerData.research.effects[k] = (playerData.research.effects[k] || 0) + v; + } + return tech; + } + + /** Cancel in-progress research — refund 75% */ + cancelResearch(playerData) { + const ip = playerData.research?.inProgress; + if (!ip) throw new Error('No research in progress'); + const tech = this.tree[ip.techId]; + playerData.stats.credits += Math.floor(tech.cost.credits * 0.75); + playerData.stats.gems += Math.floor(tech.cost.gems * 0.75); + playerData.research.inProgress = null; + return tech; + } +} + +module.exports = { ResearchSystem, RESEARCH_TREE }; diff --git a/GameServer/systems/SkillSystem.js b/GameServer/systems/SkillSystem.js index dc25620..983cced 100644 --- a/GameServer/systems/SkillSystem.js +++ b/GameServer/systems/SkillSystem.js @@ -1,397 +1,29 @@ /** - * Galaxy Strike Online - Server Skill System - * Manages skill progression, experience, and abilities + * SkillSystem — thin wrapper around ContentLoader for skill definitions. + * Player state is managed in-memory here. */ - class SkillSystem { - constructor() { - this.skills = new Map(); - this.playerSkills = new Map(); // userId -> skill data - this.initializeSkills(); + constructor(contentLoader) { + this.loader = contentLoader; + this.playerSkills = new Map(); // userId -> player data } - - initializeSkills() { - // Combat skills - this.addSkill('weapons_mastery', { - name: 'Weapons Mastery', - description: 'Increases damage and accuracy with all weapons', - category: 'combat', - maxLevel: 100, - experiencePerLevel: 1000, - bonuses: { - damage: 2, // +2% per level - accuracy: 1, // +1% per level - criticalChance: 0.5 // +0.5% per level - } - }); - - this.addSkill('defense_training', { - name: 'Defense Training', - description: 'Improves damage reduction and resistance', - category: 'combat', - maxLevel: 100, - experiencePerLevel: 1000, - bonuses: { - damageReduction: 1.5, // +1.5% per level - resistance: 1, // +1% per level - health: 5 // +5 HP per level - } - }); - - this.addSkill('speed_reflexes', { - name: 'Speed & Reflexes', - description: 'Enhances movement speed and reaction time', - category: 'combat', - maxLevel: 100, - experiencePerLevel: 1000, - bonuses: { - speed: 2, // +2% per level - dodgeChance: 0.8, // +0.8% per level - initiative: 1 // +1 per level - } - }); - - // Crafting skills - this.addSkill('weapons_crafting', { - name: 'Weapons Crafting', - description: 'Ability to craft and improve weapons', - category: 'crafting', - maxLevel: 100, - experiencePerLevel: 800, - bonuses: { - craftingSpeed: 2, // +2% per level - craftingSuccess: 1, // +1% per level - rareChance: 0.5 // +0.5% per level - } - }); - - this.addSkill('armor_crafting', { - name: 'Armor Crafting', - description: 'Ability to craft and improve armor', - category: 'crafting', - maxLevel: 100, - experiencePerLevel: 800, - bonuses: { - craftingSpeed: 2, // +2% per level - durabilityBonus: 3, // +3% per level - rareChance: 0.5 // +0.5% per level - } - }); - - // Social skills - this.addSkill('leadership', { - name: 'Leadership', - description: 'Improves team performance and guild bonuses', - category: 'social', - maxLevel: 100, - experiencePerLevel: 1200, - bonuses: { - teamDamage: 1, // +1% per level - guildBonus: 2, // +2% per level - maxPartySize: 0.1 // +0.1 per level (rounded) - } - }); - - this.addSkill('negotiation', { - name: 'Negotiation', - description: 'Better prices from shops and traders', - category: 'social', - maxLevel: 100, - experiencePerLevel: 1000, - bonuses: { - shopDiscount: 0.5, // -0.5% per level - tradeBonus: 1, // +1% per level - reputationGain: 2 // +2% per level - } - }); - - // Exploration skills - this.addSkill('navigation', { - name: 'Navigation', - description: 'Faster travel and better map awareness', - category: 'exploration', - maxLevel: 100, - experiencePerLevel: 900, - bonuses: { - travelSpeed: 3, // +3% per level - discoveryRange: 2, // +2% per level - mapAccuracy: 1 // +1% per level - } - }); - - this.addSkill('scavenging', { - name: 'Scavenging', - description: 'Find more materials and rare items', - category: 'exploration', - maxLevel: 100, - experiencePerLevel: 900, - bonuses: { - materialFind: 2, // +2% per level - rareFind: 1, // +1% per level - salvageBonus: 3 // +3% per level - } - }); - } - - addSkill(id, skill) { - this.skills.set(id, { - id, - ...skill, - createdAt: new Date().toISOString() - }); - } - - getSkill(id) { - return this.skills.get(id); - } - - getAllSkills() { - return Array.from(this.skills.values()); - } - - getSkillsByCategory(category) { - return Array.from(this.skills.values()).filter(skill => skill.category === category); - } - - initializePlayerData(userId) { - if (!this.playerSkills.has(userId)) { - this.playerSkills.set(userId, { - skillPoints: 0, - totalExperience: 0, - skills: new Map(), - unlockedSkills: new Set(['weapons_mastery', 'defense_training']), // Start with basic skills - skillHistory: [] - }); - - // Initialize unlocked skills at level 1 - const playerData = this.playerSkills.get(userId); - for (const skillId of playerData.unlockedSkills) { - playerData.skills.set(skillId, { - level: 1, - experience: 0, - totalExperience: 0, - unlockedAt: new Date().toISOString() - }); - } - } - return this.playerSkills.get(userId); - } - - getPlayerData(userId) { - return this.playerSkills.get(userId) || this.initializePlayerData(userId); - } - - addExperience(userId, skillId, amount) { - const skill = this.getSkill(skillId); - if (!skill) { - throw new Error('Skill not found'); - } - - const playerData = this.getPlayerData(userId); - - // Check if skill is unlocked - if (!playerData.unlockedSkills.has(skillId)) { - throw new Error('Skill not unlocked'); - } - - const playerSkill = playerData.skills.get(skillId); - if (!playerSkill) { - throw new Error('Skill data not found'); - } - - // Add experience - playerSkill.experience += amount; - playerSkill.totalExperience += amount; - playerData.totalExperience += amount; - - let levelsGained = 0; - let oldLevel = playerSkill.level; - - // Check for level up - while (playerSkill.experience >= skill.experiencePerLevel && playerSkill.level < skill.maxLevel) { - playerSkill.experience -= skill.experiencePerLevel; - playerSkill.level += 1; - levelsGained++; - } - - // Record in history - playerData.skillHistory.push({ - skillId, - experienceGained: amount, - levelsGained, - newLevel: playerSkill.level, - timestamp: new Date().toISOString() - }); - - // Keep only last 100 skill records - if (playerData.skillHistory.length > 100) { - playerData.skillHistory = playerData.skillHistory.slice(-100); - } - - return { - success: true, - experienceGained: amount, - levelsGained, - newLevel: playerSkill.level, - oldLevel, - experienceToNext: playerSkill.level < skill.maxLevel ? skill.experiencePerLevel - playerSkill.experience : 0 - }; - } - - unlockSkill(userId, skillId) { - const skill = this.getSkill(skillId); - if (!skill) { - throw new Error('Skill not found'); - } - - const playerData = this.getPlayerData(userId); - - if (playerData.unlockedSkills.has(skillId)) { - throw new Error('Skill already unlocked'); - } - - // Add to unlocked skills - playerData.unlockedSkills.add(skillId); - - // Initialize at level 1 - playerData.skills.set(skillId, { - level: 1, - experience: 0, - totalExperience: 0, - unlockedAt: new Date().toISOString() - }); - - return { - success: true, - skillId, - skillName: skill.name, - category: skill.category - }; - } - - getPlayerSkills(userId) { - const playerData = this.getPlayerData(userId); - const skills = []; - - for (const skillId of playerData.unlockedSkills) { - const skill = this.getSkill(skillId); - const playerSkill = playerData.skills.get(skillId); - - skills.push({ - ...skill, - level: playerSkill.level, - experience: playerSkill.experience, - totalExperience: playerSkill.totalExperience, - experienceToNext: playerSkill.level < skill.maxLevel ? skill.experiencePerLevel - playerSkill.experience : 0, - isMaxLevel: playerSkill.level >= skill.maxLevel, - unlockedAt: playerSkill.unlockedAt - }); - } - - return skills; - } - - getSkillBonuses(userId) { - const playerData = this.getPlayerData(userId); - const totalBonuses = { - damage: 0, - accuracy: 0, - criticalChance: 0, - damageReduction: 0, - resistance: 0, - health: 0, - speed: 0, - dodgeChance: 0, - initiative: 0, - craftingSpeed: 0, - craftingSuccess: 0, - rareChance: 0, - durabilityBonus: 0, - teamDamage: 0, - guildBonus: 0, - maxPartySize: 0, - shopDiscount: 0, - tradeBonus: 0, - reputationGain: 0, - travelSpeed: 0, - discoveryRange: 0, - mapAccuracy: 0, - materialFind: 0, - rareFind: 0, - salvageBonus: 0 - }; - - for (const skillId of playerData.unlockedSkills) { - const skill = this.getSkill(skillId); - const playerSkill = playerData.skills.get(skillId); - - if (skill && playerSkill) { - for (const [bonus, value] of Object.entries(skill.bonuses)) { - totalBonuses[bonus] += value * playerSkill.level; - } - } - } - - return totalBonuses; - } - - awardSkillPoints(userId, amount) { - const playerData = this.getPlayerData(userId); - playerData.skillPoints += amount; - - return { - success: true, - pointsAwarded: amount, - totalPoints: playerData.skillPoints - }; - } - - allocateSkillPoint(userId, skillId) { - const playerData = this.getPlayerData(userId); - - if (playerData.skillPoints <= 0) { - throw new Error('No skill points available'); - } - - const skill = this.getSkill(skillId); - if (!skill) { - throw new Error('Skill not found'); - } - - const playerSkill = playerData.skills.get(skillId); - if (!playerSkill) { - throw new Error('Skill not unlocked'); - } - - if (playerSkill.level >= skill.maxLevel) { - throw new Error('Skill already at max level'); - } - - // Allocate point - playerSkill.level += 1; - playerData.skillPoints -= 1; - - return { - success: true, - skillId, - newLevel: playerSkill.level, - remainingPoints: playerData.skillPoints - }; - } - - getSkillStatistics(userId) { - const playerData = this.getPlayerData(userId); - const skills = this.getPlayerSkills(userId); - - return { - skillPoints: playerData.skillPoints, - totalExperience: playerData.totalExperience, - unlockedSkills: playerData.unlockedSkills.size, - maxLevelSkills: skills.filter(skill => skill.isMaxLevel).length, - averageLevel: skills.length > 0 ? Math.floor(skills.reduce((sum, skill) => sum + skill.level, 0) / skills.length) : 0, - recentHistory: playerData.skillHistory.slice(-10) - }; - } -} + getAllSkills() { return this.loader.getAllSkills(); } + getSkill(id) { return this.loader.getSkill(id); } + getSkillsByCategory(cat) { return this.loader.getSkillsByCategory(cat); } + + initializePlayerData(userId) { + if (this.playerSkills.has(userId)) return this.playerSkills.get(userId); + const defaultUnlocked = this.loader.getAllSkills() + .filter(s => s.defaultUnlocked) + .map(s => s.id); + const skills = {}; + for (const id of defaultUnlocked) skills[id] = { level: 1, experience: 0 }; + const data = { skillPoints: 0, skills, unlockedSkills: new Set(defaultUnlocked) }; + this.playerSkills.set(userId, data); + return data; + } + + getPlayerSkills(userId) { return this.initializePlayerData(userId); } +} module.exports = SkillSystem;