use sevenx_engine::*;
use sevenx_engine::world::World;
#[derive(PartialEq)]
enum GameMode {
Playing,
Paused,
GameOver,
}
#[derive(Clone, Copy, PartialEq)]
enum EnemyType {
Basic, Fast, Tank, }
struct FPSGame {
renderer3d: Renderer3D,
graphics: GraphicsSettings,
performance: PerformanceMonitor,
walls: Vec<Mesh3D>,
floor: Mesh3D,
enemies: Vec<Enemy>,
powerups: Vec<PowerUp>,
player_pos: Vec3,
player_yaw: f32,
player_pitch: f32,
weapon_recoil: f32,
particles: ParticleSystem3D,
muzzle_flash_timer: f32,
mode: GameMode,
health: i32,
ammo: i32,
score: i32,
wave: i32,
time: f32,
wave_timer: f32,
show_fps: bool,
}
struct Enemy {
mesh: Mesh3D,
enemy_type: EnemyType,
position: Vec3,
health: i32,
active: bool,
attack_timer: f32,
}
struct PowerUp {
mesh: Mesh3D,
position: Vec3,
active: bool,
rotation: f32,
}
impl GameState for FPSGame {
fn new() -> Self {
println!("╔════════════════════════════════════════════════════════╗");
println!("║ 🎮 FPS 3D GAME - SevenX Engine v0.2.7 🎮 ║");
println!("╚════════════════════════════════════════════════════════╝");
println!();
let graphics = GraphicsSettings::load("fps_graphics.json")
.unwrap_or_else(|_| GraphicsSettings::medium());
println!("📊 Qualidade: {}", graphics.quality.name());
println!();
let mut renderer3d = Renderer3D::new(800, 600);
renderer3d.wireframe = false;
renderer3d.backface_culling = true;
let mut walls = Vec::new();
let wall_texture = Texture3D::checkerboard(16, [120, 80, 60, 255], [100, 70, 50, 255]);
let wall_material = Material3D::new([255, 255, 255, 255])
.with_texture(wall_texture)
.with_roughness(0.8);
let wall_positions = vec![
(Vec3::new(-10.0, 0.0, -10.0), Vec3::new(20.0, 3.0, 1.0)),
(Vec3::new(-10.0, 0.0, 10.0), Vec3::new(20.0, 3.0, 1.0)),
(Vec3::new(-10.0, 0.0, -10.0), Vec3::new(1.0, 3.0, 20.0)),
(Vec3::new(10.0, 0.0, -10.0), Vec3::new(1.0, 3.0, 20.0)),
];
for (pos, scale) in wall_positions {
let mut wall = Mesh3D::cube(1.0);
wall.position = pos;
wall.scale = scale;
wall.set_material(wall_material.clone());
walls.push(wall);
}
let mut floor = Mesh3D::grid(20.0, 20.0, 20, 20);
floor.position = Vec3::new(0.0, -1.5, 0.0);
let floor_texture = Texture3D::checkerboard(8, [60, 80, 60, 255], [50, 70, 50, 255]);
let floor_material = Material3D::new([255, 255, 255, 255])
.with_texture(floor_texture);
floor.set_material(floor_material);
let particle_limit = graphics.get_particle_limit();
let particles = ParticleSystem3D::new(particle_limit);
println!("🎯 CONTROLES:");
println!(" WASD/Setas - Mover");
println!(" Shift - Correr (v0.2.7!)");
println!(" Mouse - Olhar");
println!(" Scroll - Zoom (v0.2.7!)");
println!(" SPACE - Atirar");
println!(" R - Recarregar");
println!(" ESC - Pausar");
println!(" F1 - Toggle FPS (v0.2.7!)");
println!(" F2 - Qualidade (v0.2.7!)");
println!();
Self {
renderer3d,
graphics,
performance: PerformanceMonitor::new(),
walls,
floor,
enemies: Vec::new(),
powerups: Vec::new(),
player_pos: Vec3::new(0.0, 0.0, 0.0),
player_yaw: 0.0,
player_pitch: 0.0,
weapon_recoil: 0.0,
particles,
muzzle_flash_timer: 0.0,
mode: GameMode::Playing,
health: 100,
ammo: 30,
score: 0,
wave: 1,
time: 0.0,
wave_timer: 0.0,
show_fps: true,
}
}
fn update(&mut self, dt: f32, input: &sevenx_engine::input::InputHandler, _world: &mut World) {
self.time += dt;
let fps = 1.0 / dt;
self.performance.update(fps, dt);
self.graphics.dynamic_adjust(fps);
if input.is_key_just_pressed(KeyCode::Escape) {
self.mode = if self.mode == GameMode::Playing {
GameMode::Paused
} else {
GameMode::Playing
};
}
if input.is_key_just_pressed(KeyCode::F1) {
self.show_fps = !self.show_fps;
}
if input.is_key_just_pressed(KeyCode::F2) {
let current = self.graphics.quality.to_index();
let new_quality = GraphicsQuality::from_index((current + 1) % 5);
self.graphics.apply_preset(new_quality);
println!("📊 Qualidade: {}", self.graphics.quality.name());
}
if self.mode != GameMode::Playing {
return;
}
if self.health <= 0 {
self.mode = GameMode::GameOver;
return;
}
self.wave_timer += dt;
let active_enemies = self.enemies.iter().filter(|e| e.active).count();
if active_enemies == 0 && self.wave_timer > 3.0 {
self.wave += 1;
self.wave_timer = 0.0;
self.spawn_wave();
}
if self.enemies.is_empty() && self.wave == 1 {
self.spawn_wave();
}
let (move_x, move_z) = input.get_movement_vector();
let speed = if input.is_shift_pressed() { 10.0 } else { 5.0 };
if move_x != 0.0 || move_z != 0.0 {
let forward_x = self.player_yaw.sin();
let forward_z = self.player_yaw.cos();
let right_x = (self.player_yaw + std::f32::consts::FRAC_PI_2).sin();
let right_z = (self.player_yaw + std::f32::consts::FRAC_PI_2).cos();
let new_x = self.player_pos.x + (forward_x * move_z + right_x * move_x) * speed * dt;
let new_z = self.player_pos.z + (forward_z * move_z + right_z * move_x) * speed * dt;
if !self.check_collision(new_x, new_z) {
self.player_pos.x = new_x;
self.player_pos.z = new_z;
}
}
let mouse_delta = input.get_mouse_delta();
self.player_yaw += (mouse_delta.0 as f32) * 0.002;
self.player_pitch -= (mouse_delta.1 as f32) * 0.002;
self.player_pitch = self.player_pitch.clamp(-1.2, 1.2);
if input.is_key_just_pressed(KeyCode::KeyR) {
self.ammo = 30;
println!("🔄 Recarregado!");
}
if input.is_key_just_pressed(KeyCode::Space) && self.ammo > 0 {
self.shoot();
}
self.muzzle_flash_timer -= dt;
self.weapon_recoil *= 0.9;
self.update_camera();
self.update_enemies(dt);
for powerup in &mut self.powerups {
if powerup.active {
powerup.rotation += dt * 2.0;
powerup.mesh.rotation.y = powerup.rotation;
let dx = powerup.position.x - self.player_pos.x;
let dy = powerup.position.y - self.player_pos.y;
let dz = powerup.position.z - self.player_pos.z;
let dist = (dx * dx + dy * dy + dz * dz).sqrt();
if dist < 1.0 {
powerup.active = false;
self.health = (self.health + 50).min(100);
self.particles.sparkles(powerup.position);
println!("💚 +50 Vida!");
}
}
}
self.particles.update(dt);
}
fn draw(&mut self, _world: &World, frame: &mut [u8]) {
for y in 0..600 {
let sky_color = 20 + (y as f32 / 600.0 * 60.0) as u8;
for x in 0..800 {
let idx = ((y * 800 + x) * 4) as usize;
if idx + 3 < frame.len() {
frame[idx] = sky_color / 2;
frame[idx + 1] = sky_color / 2;
frame[idx + 2] = sky_color;
frame[idx + 3] = 255;
}
}
}
self.renderer3d.render_mesh(&self.floor, frame, 800, 600);
for wall in &self.walls {
self.renderer3d.render_mesh(wall, frame, 800, 600);
}
for powerup in &self.powerups {
if powerup.active {
self.renderer3d.render_mesh(&powerup.mesh, frame, 800, 600);
}
}
for enemy in &self.enemies {
if enemy.active {
self.renderer3d.render_mesh(&enemy.mesh, frame, 800, 600);
}
}
self.renderer3d.render_particles(&self.particles, frame, 800, 600);
match self.mode {
GameMode::Playing => {
self.draw_weapon(frame);
self.draw_hud(frame);
self.draw_crosshair(frame);
}
GameMode::Paused => {
self.draw_pause_menu(frame);
}
GameMode::GameOver => {
self.draw_game_over(frame);
}
}
}
}
impl FPSGame {
fn spawn_wave(&mut self) {
println!("🌊 Onda {} começou!", self.wave);
let enemy_count = 3 + self.wave;
let spawn_positions = vec![
Vec3::new(8.0, 0.0, 8.0),
Vec3::new(-8.0, 0.0, 8.0),
Vec3::new(8.0, 0.0, -8.0),
Vec3::new(-8.0, 0.0, -8.0),
];
for i in 0..enemy_count.min(spawn_positions.len() as i32) {
let enemy_type = if i % 3 == 0 {
EnemyType::Fast
} else if i % 3 == 1 {
EnemyType::Tank
} else {
EnemyType::Basic
};
self.spawn_enemy(enemy_type, spawn_positions[i as usize]);
}
if self.wave % 2 == 0 {
self.spawn_powerup(Vec3::new(0.0, 0.0, 5.0));
}
}
fn spawn_enemy(&mut self, enemy_type: EnemyType, position: Vec3) {
let (mesh, health, color) = match enemy_type {
EnemyType::Basic => {
let mut m = Mesh3D::cube(1.0);
m.scale = Vec3::new(0.8, 1.5, 0.8);
(m, 100, [200, 50, 50, 255])
}
EnemyType::Fast => {
let m = Mesh3D::cone(0.6, 1.5, 8);
(m, 50, [255, 255, 50, 255])
}
EnemyType::Tank => {
let m = Mesh3D::sphere(0.8, 8);
(m, 200, [50, 200, 50, 255])
}
};
let mut enemy_mesh = mesh;
enemy_mesh.position = position;
enemy_mesh.set_color(color);
self.enemies.push(Enemy {
mesh: enemy_mesh,
enemy_type,
position,
health,
active: true,
attack_timer: 0.0,
});
}
fn spawn_powerup(&mut self, position: Vec3) {
let mut mesh = Mesh3D::sphere(0.3, 8);
mesh.position = position;
mesh.set_color([255, 50, 50, 255]);
self.powerups.push(PowerUp {
mesh,
position,
active: true,
rotation: 0.0,
});
}
fn update_camera(&mut self) {
self.renderer3d.camera.position = Vec3::new(
self.player_pos.x,
self.player_pos.y + 1.0,
self.player_pos.z,
);
let target = Vec3::new(
self.player_pos.x + self.player_yaw.sin(),
self.player_pos.y + 1.0 + self.player_pitch,
self.player_pos.z + self.player_yaw.cos(),
);
self.renderer3d.camera.look_at(target);
}
fn check_collision(&self, x: f32, z: f32) -> bool {
x < -9.0 || x > 9.0 || z < -9.0 || z > 9.0
}
fn shoot(&mut self) {
self.ammo -= 1;
self.weapon_recoil = 0.1;
self.muzzle_flash_timer = 0.05;
let ray_dir = Vec3::new(
self.player_yaw.sin(),
self.player_pitch,
self.player_yaw.cos(),
);
for enemy in &mut self.enemies {
if !enemy.active { continue; }
let dx = enemy.position.x - self.player_pos.x;
let dy = enemy.position.y - self.player_pos.y;
let dz = enemy.position.z - self.player_pos.z;
let distance = (dx * dx + dy * dy + dz * dz).sqrt();
if distance < 15.0 {
let to_enemy = Vec3::new(dx, dy, dz);
let to_enemy_norm = to_enemy.normalize();
let dot = ray_dir.normalize().dot(&to_enemy_norm);
if dot > 0.95 {
enemy.health -= 50;
if enemy.health <= 0 {
enemy.active = false;
let points = match enemy.enemy_type {
EnemyType::Basic => 100,
EnemyType::Fast => 150,
EnemyType::Tank => 200,
};
self.score += points;
self.particles.explosion(enemy.position);
println!("💀 +{} pontos!", points);
} else {
self.particles.sparkles(enemy.position);
}
}
}
}
let muzzle_pos = Vec3::new(
self.player_pos.x + self.player_yaw.sin() * 0.5,
self.player_pos.y + 1.0,
self.player_pos.z + self.player_yaw.cos() * 0.5,
);
self.particles.sparkles(muzzle_pos);
}
fn update_enemies(&mut self, dt: f32) {
for enemy in &mut self.enemies {
if !enemy.active { continue; }
enemy.attack_timer += dt;
let dx = self.player_pos.x - enemy.position.x;
let dy = self.player_pos.y - enemy.position.y;
let dz = self.player_pos.z - enemy.position.z;
let distance = (dx * dx + dy * dy + dz * dz).sqrt();
if distance > 2.0 && distance < 15.0 {
let move_amount = 2.0 * dt;
enemy.position.x += (dx / distance) * move_amount;
enemy.position.z += (dz / distance) * move_amount;
enemy.mesh.position = enemy.position;
}
if distance < 2.0 && enemy.attack_timer > 1.0 {
self.health -= 10;
enemy.attack_timer = 0.0;
println!("💔 Vida: {}", self.health);
}
let angle = (self.player_pos.x - enemy.position.x)
.atan2(self.player_pos.z - enemy.position.z);
enemy.mesh.rotation.y = angle;
}
}
fn draw_weapon(&self, frame: &mut [u8]) {
let weapon_x = 400;
let weapon_y = 500 - (self.weapon_recoil * 200.0) as i32;
for y in weapon_y..weapon_y + 80 {
for x in weapon_x..weapon_x + 40 {
if x >= 0 && x < 800 && y >= 0 && y < 600 {
let idx = ((y * 800 + x) * 4) as usize;
if idx + 3 < frame.len() {
frame[idx] = 80;
frame[idx + 1] = 80;
frame[idx + 2] = 80;
}
}
}
}
if self.muzzle_flash_timer > 0.0 {
let flash_size = 20;
for y in weapon_y - flash_size..weapon_y {
for x in weapon_x..weapon_x + 40 {
if x >= 0 && x < 800 && y >= 0 && y < 600 {
let idx = ((y * 800 + x) * 4) as usize;
if idx + 3 < frame.len() {
frame[idx] = 255;
frame[idx + 1] = 200;
frame[idx + 2] = 0;
}
}
}
}
}
}
fn draw_crosshair(&self, frame: &mut [u8]) {
let cx = 400;
let cy = 300;
let size = 10;
for i in -size..=size {
let idx_h = ((cy * 800 + (cx + i)) * 4) as usize;
if idx_h + 3 < frame.len() {
frame[idx_h] = 255;
frame[idx_h + 1] = 255;
frame[idx_h + 2] = 255;
}
let idx_v = (((cy + i) * 800 + cx) * 4) as usize;
if idx_v + 3 < frame.len() {
frame[idx_v] = 255;
frame[idx_v + 1] = 255;
frame[idx_v + 2] = 255;
}
}
}
fn draw_hud(&self, frame: &mut [u8]) {
for y in 0..60 {
for x in 0..800 {
let idx = ((y * 800 + x) * 4) as usize;
if idx + 3 < frame.len() {
frame[idx] = (frame[idx] as f32 * 0.3) as u8;
frame[idx + 1] = (frame[idx + 1] as f32 * 0.3) as u8;
frame[idx + 2] = (frame[idx + 2] as f32 * 0.3) as u8;
}
}
}
let health_width = (self.health as f32 / 100.0 * 200.0) as i32;
for y in 10..30 {
for x in 10..10 + health_width.max(0) {
let idx = ((y * 800 + x) * 4) as usize;
if idx + 3 < frame.len() {
frame[idx] = 200;
frame[idx + 1] = 50;
frame[idx + 2] = 50;
}
}
}
for y in 10..30 {
for x in 220..220 + (self.ammo * 5).min(150) {
let idx = ((y * 800 + x) * 4) as usize;
if idx + 3 < frame.len() {
frame[idx] = 200;
frame[idx + 1] = 200;
frame[idx + 2] = 50;
}
}
}
if self.show_fps {
let _fps = self.performance.average_fps();
}
}
fn draw_pause_menu(&self, frame: &mut [u8]) {
for y in 200..400 {
for x in 200..600 {
let idx = ((y * 800 + x) * 4) as usize;
if idx + 3 < frame.len() {
frame[idx] = (frame[idx] as f32 * 0.5) as u8;
frame[idx + 1] = (frame[idx + 1] as f32 * 0.5) as u8;
frame[idx + 2] = (frame[idx + 2] as f32 * 0.5) as u8;
}
}
}
}
fn draw_game_over(&self, frame: &mut [u8]) {
for y in 200..400 {
for x in 200..600 {
let idx = ((y * 800 + x) * 4) as usize;
if idx + 3 < frame.len() {
frame[idx] = (frame[idx] as f32 * 0.2) as u8;
frame[idx + 1] = (frame[idx + 1] as f32 * 0.2) as u8;
frame[idx + 2] = (frame[idx + 2] as f32 * 0.2) as u8;
}
}
}
}
}
fn main() {
let config = EngineConfig::default()
.with_title("SevenX Engine v0.2.7 - FPS 3D Game")
.with_size(800, 600);
let engine = Engine::with_config(config);
engine.run::<FPSGame>();
}