use bevy_ecs::prelude::*; use rand::rngs::StdRng; use rand::{Rng, SeedableRng}; use tracing::{debug, info}; use crate::constants::BOT_COUNT; use crate::game::{AttackActionHandler, BorderManager, BotManager, GameInstance, HSLColor, LocalPlayerContext, Player, PlayerManager, SpawnManager, SpawnPhase, SpawnTimeout, TerritoryManager}; use crate::networking::{GameView, LocalTurnServerHandle, PlayerView, TurnReceiver}; use flume::Receiver; /// Parameters needed to initialize a new game pub struct GameInitParams { pub map_width: u32, pub map_height: u32, pub conquerable_tiles: Vec, pub tile_expansion_times: Vec, pub tile_expansion_costs: Vec, pub client_player_id: usize, pub intent_rx: Receiver, } /// Initialize all game resources when starting a new game /// This should be called by the StartGame command handler pub fn initialize_game_resources(commands: &mut Commands, params: GameInitParams) { info!("Initializing game resources (map: {}x{}, player: {})", params.map_width, params.map_height, params.client_player_id); // Initialize territory manager let mut territory_manager = TerritoryManager::new(params.map_width, params.map_height); territory_manager.reset(params.map_width, params.map_height, ¶ms.conquerable_tiles); debug!("Territory manager initialized with {} tiles", params.conquerable_tiles.len()); // Initialize border manager let mut border_manager = BorderManager::new(); border_manager.reset(params.map_width, params.map_height, 1 + BOT_COUNT); // Initialize attack handler let mut attack_handler = AttackActionHandler::new(); attack_handler.init(1 + BOT_COUNT, params.tile_expansion_times, params.tile_expansion_costs, params.map_width, params.map_height); // Initialize bot manager (1 human player + BOT_COUNT bots) let bot_manager = BotManager::new(BOT_COUNT, 1); debug!("BotManager initialized with {} bots", BOT_COUNT); // Use a fixed seed for deterministic bot behavior and color generation // In multiplayer, this should come from the server let rng_seed = 0xDEADBEEF; // Create RNG for deterministic color generation let mut rng = StdRng::seed_from_u64(rng_seed); // Create players: 1 human + BOT_COUNT bots // Player IDs start at 0 (human), then 1, 2, 3... for bots let mut players = Vec::new(); // Generate random hue offset for color spread let hue_offset = rng.random_range(0.0..360.0); // All players (including human) get deterministically generated colors for i in 0..=BOT_COUNT { let is_human = i == 0; let id = i as f32; // Use golden angle distribution with random offset for visually distinct colors let hue = (id * 137.5 + hue_offset) % 360.0; let saturation = rng.random_range(0.75..=0.95); let lightness = rng.random_range(0.35..=0.65); let color = HSLColor::new(hue, saturation, lightness); if is_human { players.push(Player::new(i, "Player".to_string(), color)); } else { players.push(Player::new(i, format!("Bot {}", i), color)); } } // Initialize player manager // Human player is always ID 0 let mut player_manager = PlayerManager::new(); let human_player_id = 0; player_manager.init(players, human_player_id); debug!("Player manager initialized with {} players (human: {}, bots: {})", 1 + BOT_COUNT, human_player_id, BOT_COUNT); // Create game instance (bots won't be spawned until player chooses spawn) let game_instance = GameInstance::new(player_manager, territory_manager, attack_handler, border_manager, bot_manager, rng_seed); // Calculate initial bot spawn positions (first pass) // These will be shown to the player, but not applied to game state yet let initial_bot_spawns = game_instance.bot_manager.calculate_initial_spawns(&game_instance.territory_manager, rng_seed); debug!("Calculated {} initial bot spawn positions", initial_bot_spawns.len()); // Create SpawnManager to track spawn positions during spawn phase let spawn_manager = SpawnManager::new(initial_bot_spawns.clone(), rng_seed, params.map_width, params.map_height); commands.insert_resource(spawn_manager); // Initialize GameView with initial game state // Calculate total land tiles once for caching (performance optimization) use std::sync::Arc; let total_land_tiles = game_instance.territory_manager.as_slice().iter().filter(|ownership| !ownership.is_water()).count() as u32; let game_view = GameView { width: params.map_width, height: params.map_height, territories: Arc::from(game_instance.territory_manager.to_u16_vec().as_slice()), turn_number: 0, total_land_tiles, changed_tiles: Vec::new(), // Empty on initialization players: game_instance.player_manager.get_players().iter().map(|p| PlayerView { id: p.id as u16, color: p.color.to_rgba(), name: p.name.clone(), tile_count: p.get_territory_size() as u32, troops: p.get_troops() as u32, is_alive: p.is_alive() }).collect(), }; commands.insert_resource(game_instance); commands.insert_resource(game_view); debug!("GameInstance and GameView resources created"); // Initialize local player context commands.insert_resource(LocalPlayerContext::new(0)); // Human player is ID 0 debug!("LocalPlayerContext created for player 0 (human)"); // Initialize spawn timeout (5 seconds for local mode) commands.insert_resource(SpawnTimeout::new(5.0)); debug!("SpawnTimeout initialized (5.0 seconds)"); // Initialize turn generation resources let (turn_tx, turn_rx) = flume::unbounded(); let server_handle = LocalTurnServerHandle { paused: std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true)), running: std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true)) }; commands.insert_resource(server_handle); commands.insert_resource(TurnReceiver { turn_rx }); commands.insert_resource(crate::networking::TurnGenerator { turn_number: 0, accumulated_time: 0.0, turn_tx, spawn_config: std::collections::HashMap::new(), spawn_timeout_accumulated: None, game_started: false }); debug!("Turn generator initialized (paused until player spawn)"); // Activate spawn phase (SpawnPhasePlugin will emit initial SpawnPhaseUpdate) commands.insert_resource(SpawnPhase { active: true }); debug!("Spawn phase activated"); info!("Game resources initialized successfully - ready to start"); } /// Clean up all game resources when quitting a game /// This should be called by the QuitGame command handler pub fn cleanup_game_resources(world: &mut World) { info!("Cleaning up game resources..."); // Stop local turn server if running if let Some(server_handle) = world.get_resource::() { server_handle.stop(); world.remove_resource::(); } // Remove all game-specific resources world.remove_resource::(); world.remove_resource::(); world.remove_resource::(); world.remove_resource::(); world.remove_resource::(); world.remove_resource::(); world.remove_resource::(); world.remove_resource::(); // Note: SpawnPhase is a permanent resource (init_resource), not removed on quit info!("Game resources cleaned up successfully"); }