use ggez::{Context, GameResult};
use ggez::graphics::{self, DrawParam, Rect}; use crate::core::resources::Resources;
use crate::ui::grid::{GRID_START_Y, GRID_CELL_HEIGHT, GRID_CELL_WIDTH, GRID_START_X};
pub mod normal_zombie;
pub mod zombie_trait;
pub mod zombie_factory;
pub mod conehead_zombie;
pub use zombie_factory::{ZombieType, ZombieFactory};
pub struct Zombie {
zombie_type: ZombieType,
pub row: usize,
pub x: f32,
health: i32,
max_health: i32,
speed: f32,
animation_frame: usize,
animation_timer: u64,
attacking: bool,
pub is_dying: bool,
pub death_animation_complete: bool,
attack_damage: i32,
attack_interval: u64,
attack_timer: u64,
attack_target: Option<usize>,
head_falling: bool,
head_animation_frame: usize,
head_animation_timer: u64,
head_x: f32,
head_y: f32,
zombie_impl: Box<dyn zombie_trait::ZombieTrait>,
}
impl Zombie {
pub fn new(zombie_type: ZombieType, row: usize) -> Self {
let zombie_impl = ZombieFactory::create_zombie(zombie_type);
let health = zombie_impl.get_initial_health();
let speed = zombie_impl.get_speed();
let attack_damage = zombie_impl.get_attack_damage();
let attack_interval = zombie_impl.get_attack_interval();
Zombie {
zombie_type,
row,
x: 950.0, health,
max_health: health, speed,
animation_frame: 0,
animation_timer: 0,
attacking: false,
is_dying: false,
death_animation_complete: false,
attack_damage,
attack_interval,
attack_timer: 0,
attack_target: None,
head_falling: false,
head_animation_frame: 0,
head_animation_timer: 0,
head_x: 0.0,
head_y: 0.0,
zombie_impl,
}
}
pub fn update(&mut self, dt: u64) {
if self.is_dying {
self.animation_timer += dt;
if self.animation_timer > 200 { if self.animation_frame < 9 { self.animation_frame += 1;
if self.animation_frame == 2 && !self.head_falling {
self.head_falling = true;
self.head_x = self.x + 40.0; let y = GRID_START_Y + (self.row as f32) * GRID_CELL_HEIGHT - GRID_CELL_HEIGHT/4.0;
self.head_y = y + 20.0; }
} else {
self.death_animation_complete = true;
}
self.animation_timer = 0;
}
if self.head_falling {
self.head_animation_timer += dt;
if self.head_animation_timer > 150 { if self.head_animation_frame < 11 { self.head_animation_frame += 1;
}
self.head_animation_timer = 0;
}
}
return; }
self.animation_timer += dt;
if self.animation_timer > 200 {
let frame_count = if self.attacking {
self.zombie_impl.get_attack_frame_count()
} else {
self.zombie_impl.get_walk_frame_count()
};
self.animation_frame = (self.animation_frame + 1) % frame_count;
self.animation_timer = 0;
}
if !self.attacking {
self.x -= self.speed * dt as f32;
}
self.zombie_impl.update_special(dt);
}
pub fn draw(&self, ctx: &mut Context, resources: &Resources) -> GameResult {
let y = GRID_START_Y + (self.row as f32) * GRID_CELL_HEIGHT - GRID_CELL_HEIGHT/4.0;
let image = if self.is_dying {
self.zombie_impl.get_die_image(resources, self.animation_frame)
} else if self.attacking {
self.zombie_impl.get_attack_image(resources, self.animation_frame)
} else {
self.zombie_impl.get_walk_image(resources, self.animation_frame)
};
let mut draw_params = self.zombie_impl.get_draw_params();
draw_params = draw_params.dest([self.x, y]);
graphics::draw(ctx, image, draw_params)?;
if self.head_falling && !self.death_animation_complete {
let frame_count = resources.zombie_head_images.len();
if frame_count > 0 && self.head_animation_frame < frame_count {
let head_image = &resources.zombie_head_images[self.head_animation_frame];
graphics::draw(
ctx,
head_image,
DrawParam::default()
.dest([self.head_x, self.head_y])
.scale([0.7, 0.7]), )?;
}
}
Ok(())
}
pub fn get_rect(&self) -> Rect {
let y = GRID_START_Y + (self.row as f32) * GRID_CELL_HEIGHT - GRID_CELL_HEIGHT/4.0;
let width = 20.0;
let height = 100.0;
Rect::new(self.x + 40.0, y + 20.0, width, height)
}
pub fn take_damage(&mut self, damage: i32) -> bool {
let damage_handled = self.zombie_impl.handle_damage(damage);
if damage_handled {
if let Some(new_health) = self.zombie_impl.transform_health() {
self.health = new_health;
println!("僵尸转变形态,健康值重置为: {}", self.health);
}
} else {
self.health -= damage;
println!("僵尸受到{}点伤害,剩余生命值: {}", damage, self.health);
}
if self.health <= 0 {
self.is_dying = true;
self.animation_frame = 0;
self.animation_timer = 0;
return true;
}
false
}
pub fn attack_plant(&mut self, plant_health: &mut i32, dt: u64) {
self.attack_timer += dt;
if self.attack_timer >= self.attack_interval {
*plant_health -= self.attack_damage;
println!("僵尸啃咬了植物,造成{}点伤害,植物剩余生命值: {}", self.attack_damage, *plant_health);
self.attack_timer = 0;
}
}
pub fn has_plant_in_front(&self, plant_grid_x: usize, plant_grid_y: usize) -> bool {
if self.row != plant_grid_y {
return false;
}
let plant_left_edge = GRID_START_X + (plant_grid_x as f32) * GRID_CELL_WIDTH - GRID_CELL_WIDTH;
let zombie_right_edge = self.x + 20.0;
zombie_right_edge >= plant_left_edge && zombie_right_edge <= plant_left_edge + GRID_CELL_WIDTH
}
pub fn set_attacking(&mut self, is_attacking: bool, target_index: Option<usize>) {
if self.attacking != is_attacking {
self.attacking = is_attacking;
self.attack_target = target_index;
if is_attacking {
self.animation_frame = 0;
self.animation_timer = 0;
self.attack_timer = 0;
}
}
}
pub fn get_zombie_type(&self) -> ZombieType {
self.zombie_type
}
}