Files
smart-rgb/crates/borders-core/src/game/lifecycle.rs
2025-10-10 20:00:55 -05:00

165 lines
7.4 KiB
Rust

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<bool>,
pub tile_expansion_times: Vec<u8>,
pub tile_expansion_costs: Vec<u8>,
pub client_player_id: usize,
pub intent_rx: Receiver<crate::networking::Intent>,
}
/// 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, &params.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::<LocalTurnServerHandle>() {
server_handle.stop();
world.remove_resource::<LocalTurnServerHandle>();
}
// Remove all game-specific resources
world.remove_resource::<GameInstance>();
world.remove_resource::<LocalPlayerContext>();
world.remove_resource::<TurnReceiver>();
world.remove_resource::<SpawnManager>();
world.remove_resource::<SpawnTimeout>();
world.remove_resource::<crate::networking::GameView>();
world.remove_resource::<crate::TerrainData>();
world.remove_resource::<crate::networking::TurnGenerator>();
// Note: SpawnPhase is a permanent resource (init_resource), not removed on quit
info!("Game resources cleaned up successfully");
}