mirror of
https://github.com/Xevion/smart-rgb.git
synced 2025-12-05 23:16:23 -06:00
165 lines
7.4 KiB
Rust
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, ¶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::<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");
|
|
}
|