use crate::ui::grid::Grid;
use crate::plants::PlantType;
use crate::core::resources::Resources;
use crate::entities::sun::Sun;
use crate::zombies::Zombie;
use crate::entities::pea::Pea;
use crate::ui::shop::Shop;
use crate::ui::shovel::Shovel;
use crate::plants::Plant;
use crate::mechanics::collision::CollisionManager;
use crate::mechanics::entity_manager::EntityManager;
use crate::ui::input_handler::InputHandler;
use crate::core::renderer::Renderer;
use ggez::event::EventHandler;
use ggez::input::mouse::MouseButton;
use ggez::{Context, GameResult};
use std::time::Instant;
pub struct GameState {
resources: Resources,
grid: Grid,
plants: Vec<Plant>,
zombies: Vec<Zombie>,
suns: Vec<Sun>,
peas: Vec<Pea>,
sun_count: i32,
selected_plant: Option<PlantType>,
game_over: bool,
victory: bool,
show_final_wave: bool,
final_wave_message_time: Option<Instant>,
shop: Shop,
shovel: Shovel,
entity_manager: EntityManager,
game_state: crate::core::states::GameState,
pause_button_rect: (f32, f32, f32, f32),
pause_start_time: Option<std::time::Instant>,
is_initial_pause: bool,
}
impl GameState {
pub fn new(ctx: &mut Context) -> GameResult<GameState> {
let resources = Resources::new(ctx)?;
let grid = Grid::new();
let shop = Shop::new();
let shovel = Shovel::new();
let entity_manager = EntityManager::new();
Ok(GameState {
resources,
grid,
plants: Vec::new(),
zombies: Vec::new(),
suns: Vec::new(),
peas: Vec::new(),
sun_count: 50,
selected_plant: None,
game_over: false,
victory: false,
show_final_wave: false,
final_wave_message_time: None,
shop,
shovel,
entity_manager,
game_state: crate::core::states::GameState::Paused,
pause_button_rect: (950.0, 10.0, 80.0, 40.0), pause_start_time: None,
is_initial_pause: true,
})
}
}
impl EventHandler for GameState {
fn mouse_motion_event(&mut self, _ctx: &mut Context, x: f32, y: f32, _dx: f32, _dy: f32) {
if self.shovel.is_dragging {
self.shovel.update_position(x, y);
}
}
fn mouse_button_up_event(&mut self, _ctx: &mut Context, button: MouseButton, x: f32, y: f32) {
if self.game_state != crate::core::states::GameState::InGame || self.game_over {
return;
}
if button == MouseButton::Left {
if self.shovel.is_dragging {
if self.shovel.dig(x, y, &mut self.grid, &mut self.plants) {
self.shovel.reset();
} else {
self.shovel.reset();
}
}
}
}
fn update(&mut self, ctx: &mut Context) -> GameResult {
const DESIRED_FPS: u32 = 60;
const FIXED_UPDATE_DT_MS: u64 = (1000.0_f32 / DESIRED_FPS as f32) as u64;
while ggez::timer::check_update_time(ctx, DESIRED_FPS) {
if self.game_over || self.game_state == crate::core::states::GameState::Paused {
continue;
}
for sun in &mut self.suns {
sun.update(FIXED_UPDATE_DT_MS);
}
let mut new_suns = Vec::new();
for plant in &mut self.plants {
plant.update(FIXED_UPDATE_DT_MS, &mut new_suns, &mut self.peas, &self.zombies); }
self.suns.append(&mut new_suns);
for zombie in &mut self.zombies {
zombie.update(FIXED_UPDATE_DT_MS);
}
for pea in &mut self.peas {
pea.update(FIXED_UPDATE_DT_MS);
}
CollisionManager::handle_pea_zombie_collision(&mut self.peas, &mut self.zombies);
CollisionManager::handle_zombie_plant_interaction(&mut self.zombies, &mut self.plants, ctx);
for zombie in &self.zombies {
if zombie.x <= 0.0 {
self.game_over = true;
break;
}
}
if self.game_over { continue; }
if self.entity_manager.check_natural_sun_spawn(FIXED_UPDATE_DT_MS) {
let new_sun = self.entity_manager.spawn_natural_sun();
self.suns.push(new_sun);
}
let zombie_spawns = self.entity_manager.update(FIXED_UPDATE_DT_MS, &self.zombies);
for spawn_info in zombie_spawns {
let zombie = self.entity_manager.spawn_zombie(spawn_info.zombie_type, spawn_info.row);
self.zombies.push(zombie);
}
if self.entity_manager.level_controller.is_final_wave_announced() {
self.show_final_wave = true;
self.final_wave_message_time = Some(Instant::now());
}
if let Some(time) = self.final_wave_message_time {
if time.elapsed().as_secs() > 5 {
self.show_final_wave = false;
}
}
if self.entity_manager.level_controller.is_level_completed(&self.zombies) {
self.victory = true;
}
if self.game_state != crate::core::states::GameState::Paused {
self.shop.update(self.sun_count);
}
}
Ok(())
}
fn draw(&mut self, ctx: &mut Context) -> GameResult {
Renderer::draw_game(
ctx,
&self.resources,
&self.grid,
&self.plants,
&self.peas,
&self.zombies,
&self.suns,
&self.shop,
self.sun_count,
self.game_over,
self.victory,
self.show_final_wave,
self.game_state,
self.pause_button_rect,
&self.shovel,
self.is_initial_pause
)
}
fn mouse_button_down_event(&mut self, _ctx: &mut Context, button: MouseButton, x: f32, y: f32) {
let (btn_x, btn_y, btn_w, btn_h) = self.pause_button_rect;
if button == MouseButton::Left
&& x >= btn_x && x <= btn_x + btn_w
&& y >= btn_y && y <= btn_y + btn_h {
match self.game_state {
crate::core::states::GameState::Paused => {
self.game_state = crate::core::states::GameState::InGame;
self.is_initial_pause = false;
if let Some(pause_time) = self.pause_start_time {
let pause_duration = pause_time.elapsed();
for card in &mut self.shop.cards {
if let Some(last_used) = card.last_used {
card.last_used = Some(std::time::Instant::now()
.checked_sub(last_used.elapsed().checked_sub(pause_duration)
.unwrap_or_else(|| std::time::Duration::from_secs(0)))
.unwrap_or_else(std::time::Instant::now));
let elapsed = card.last_used.unwrap().elapsed();
if elapsed < card.cooldown {
card.cooldown_display_progress = elapsed.as_millis() as f32 / card.cooldown.as_millis() as f32;
} else {
card.cooldown_display_progress = 1.0;
}
}
}
self.pause_start_time = None;
}
},
_ => {
self.game_state = crate::core::states::GameState::Paused;
self.pause_start_time = Some(std::time::Instant::now());
}
}
return;
}
if self.game_state == crate::core::states::GameState::Paused {
return;
}
InputHandler::handle_mouse_down(
button,
x,
y,
&mut self.shop,
&mut self.suns,
&mut self.grid,
&mut self.plants,
&mut self.selected_plant,
&mut self.sun_count,
self.game_over,
&mut self.shovel
);
}
}