mod hud; mod playerlogic; use raylib::prelude::*; use crate::{entities::enemy::{base::EnemyBase, whirlpool::Whirlpool}, gamecore::{self, GameCore, GameState}, lib::wrappers::audio::player::AudioPlayer}; use super::screen::Screen; use crate::entities::fish::FishEntity; pub struct InGameScreen { shader_time_var_location: i32, swim_playing: bool, } impl InGameScreen { pub unsafe fn new(game_core: &GameCore) -> Self { Self { shader_time_var_location: raylib::ffi::GetShaderLocation( *game_core.resources.pixel_shader, rstr!("time").as_ptr(), ), swim_playing: false, } } fn render_world( &mut self, context_2d: &mut RaylibMode2D, game_core: &mut GameCore, dt: f64, audio_system: &mut AudioPlayer, ) { // Build source bounds let source_bounds = Rectangle { x: 0.0, y: 0.0, width: game_core.resources.cave_mid_layer.width as f32, height: game_core.resources.cave_mid_layer.height as f32, }; let world_bounds = Rectangle { x: 0.0, y: 0.0, width: game_core.resources.cave_mid_layer.width as f32, height: game_core.resources.cave_mid_layer.height as f32, }; // Clear the background // context_2d.draw_rectangle_gradient_v( // world_bounds.x as i32, // world_bounds.y as i32, // world_bounds.width as i32, // world_bounds.height as i32, // WATER, // WATER_DARK, // ); context_2d.draw_texture_pro( &game_core.resources.background_back, Rectangle { x: 0.0, y: 0.0, width: game_core.resources.background_back.width as f32, height: game_core.resources.background_back.height as f32, }, Rectangle::new( 0.0, 0.0, (game_core.resources.background_back.width * 2) as f32, (game_core.resources.background_back.height * 2) as f32, ), Vector2 { x: 0.0, y: 0.0 }, 0.0, Color::WHITE, ); context_2d.draw_texture_pro( &game_core.resources.background_front, Rectangle { x: 0.0, y: 0.0, width: game_core.resources.background_front.width as f32, height: game_core.resources.background_front.height as f32, }, Rectangle::new( 0.0, 0.0, (game_core.resources.background_front.width * 2) as f32, (game_core.resources.background_front.height * 2) as f32, ), Vector2 { x: 0.0, y: 0.0 }, 0.0, Color::WHITE, ); // Render fish let fish_clone = game_core.world.fish.clone(); for fish in game_core.world.fish.iter_mut() { if fish.update_position(&mut game_core.player, dt, &fish_clone) { audio_system.play_sound(&game_core.resources.fish_pickup); } fish.render(context_2d, &mut game_core.resources); } context_2d.draw_texture_pro( &game_core.resources.tut1, Rectangle { x: 0.0, y: 0.0, width: 44.0, height: 41.0, }, Rectangle { x: 110.0, y: 100.0, width: 44.0, height: 41.0, }, Vector2 { x: 0.0, y: 0.0, }, 0.0, Color::WHITE, ); context_2d.draw_texture_pro( &game_core.resources.tut2, Rectangle { x: 0.0, y: 0.0, width: 44.0, height: 41.0, }, Rectangle { x: 160.0, y: 110.0, width: 44.0, height: 41.0, }, Vector2 { x: 0.0, y: 0.0, }, 0.0, Color::WHITE, ); // Render the world texture context_2d.draw_texture_rec( &game_core.resources.cave_mid_layer, source_bounds, Vector2 { x: world_bounds.x, y: world_bounds.y, }, Color::WHITE, ); } fn render_colliders( &mut self, context_2d: &mut RaylibMode2D, game_core: &mut GameCore, ) { // Render every collider for collider in game_core.world.colliders.iter() { context_2d.draw_rectangle_lines_ex(collider, 1, Color::RED); } } fn render_darkness(&mut self, draw_handle: &mut RaylibDrawHandle, game_core: &mut GameCore) { // Calculate the min view radius based on the current flashlight let mut min_radius = 0.0; if game_core.player.inventory.flashlight.is_some() { min_radius = game_core .player .inventory .flashlight .as_ref() .unwrap() .radius; } // Get the window center let win_height = draw_handle.get_screen_height(); let win_width = draw_handle.get_screen_width(); // Calculate the occusion radius based on depth let radius = (1.0 - (game_core.player.calculate_depth_percent(&game_core.world) * 1.3).clamp(0.0, 1.0)) .max(min_radius); // Determine width and height scales // This is clamped to make the rendering logic below easier by removing the need to overdraw let width_scale = (5.0 * radius).max(0.5); let height_scale = (5.0 * radius).max(0.5); // Get the base sizes of everything let texture_width = game_core.resources.darkness_overlay.width as f32; let texture_height = game_core.resources.darkness_overlay.height as f32; let texture_width_scaled = texture_width * width_scale; let texture_height_scaled = texture_height * height_scale; // Render the overlay draw_handle.draw_texture_pro( &game_core.resources.darkness_overlay, Rectangle { x: 0.0, y: 0.0, width: texture_width, height: texture_height, }, Rectangle { x: (win_width as f32 - texture_width_scaled) / 2.0, y: (win_height as f32 - texture_height_scaled) / 2.0, width: texture_width_scaled, height: texture_height_scaled, }, Vector2 { x: 0.0, y: 0.0 }, 0.0, Color::WHITE, ); } } impl Screen for InGameScreen { fn render( &mut self, draw_handle: &mut RaylibDrawHandle, _thread: &RaylibThread, _audio_system: &mut AudioPlayer, game_core: &mut GameCore, ) -> Option { // Calculate DT let dt = draw_handle.get_time() - game_core.last_frame_time; // Handle the pause menu being opened if draw_handle.is_key_pressed(KeyboardKey::KEY_ESCAPE) { return Some(GameState::PauseMenu); } // music if !_audio_system.is_sound_playing(&game_core.resources.song_swim) { _audio_system.play_sound(&game_core.resources.song_swim); } // Window dimensions let win_height = draw_handle.get_screen_height(); let win_width = draw_handle.get_screen_width(); let window_center = Vector2 { x: (win_width as f32 / 2.0), y: (win_height as f32 / 2.0), }; // Update player movement playerlogic::update_player_movement(draw_handle, game_core, window_center, _audio_system); if draw_handle.get_time() % 10.0 <= 0.1 && !_audio_system.is_sound_playing(&game_core.resources.breath){ _audio_system.set_sound_volume(&game_core.resources.breath, 0.5); _audio_system.play_sound(&game_core.resources.breath); } // Open a 2D context { unsafe { raylib::ffi::BeginTextureMode(*game_core.resources.shader_texture); } { let mut context_2d = draw_handle.begin_mode2D(game_core.master_camera); // Clear frame context_2d.clear_background(Color::BLACK); // Render the world self.render_world(&mut context_2d, game_core, dt, _audio_system); if game_core.show_simple_debug_info { self.render_colliders(&mut context_2d, game_core); } // Render entities for jellyfish in game_core.world.jellyfish.iter_mut() { jellyfish.handle_logic(&mut game_core.player, dt); jellyfish.render( &mut context_2d, &mut game_core.player, &mut game_core.resources, dt, ); } for octopus in game_core.world.octopus.iter_mut() { if octopus.handle_logic(&mut game_core.player, dt) == 1 { _audio_system.play_sound(&game_core.resources.succ); } octopus.render( &mut context_2d, &mut game_core.player, &mut game_core.resources, dt, ); } // Iterates over whirlpools and runs render and logic funcs for whirlpool_mob in game_core.world.whirlpool.iter_mut(){ whirlpool_mob.handle_logic(&mut game_core.player, dt); whirlpool_mob.render(&mut context_2d, &mut game_core.player, &mut game_core.resources, dt); // Spawns 10 fish on spawn if whirlpool_mob.should_remove(){ for _ in 0..10{ game_core.world.fish.push(FishEntity::new(whirlpool_mob.position)); } } } // Iterates over pufferfish for pufferfish in game_core.world.pufferfish.iter_mut(){ pufferfish.handle_logic(&mut game_core.player, dt); pufferfish.render(&mut context_2d, &mut game_core.player, &mut game_core.resources, dt); } // Removes whirlpools set for removal game_core.world.whirlpool.retain(|x| !x.should_remove()); // Render transponder game_core.resources.transponder.draw( &mut context_2d, game_core.world.end_position, 0.0, ); // Render Player game_core .player .render(&mut context_2d, &mut game_core.resources, dt); } unsafe { raylib::ffi::EndTextureMode(); } } // Update the shader's internal time game_core .resources .pixel_shader .set_shader_value(self.shader_time_var_location, draw_handle.get_time() as f32); // Render the 2D context via the ripple shader { let mut shader_context = draw_handle.begin_shader_mode(&game_core.resources.pixel_shader); // Blit the texture shader_context.draw_texture_pro( &game_core.resources.shader_texture, Rectangle { x: 0.0, y: 0.0, width: game_core.resources.shader_texture.width() as f32, height: (game_core.resources.shader_texture.height() as f32) * -1.0, }, Rectangle { x: -10.0, y: -10.0, width: win_width as f32 + 20.0, height: win_height as f32 + 20.0, }, Vector2::zero(), 0.0, Color::WHITE, ); } // Render the darkness layer self.render_darkness(draw_handle, game_core); // Render the hud hud::render_hud(draw_handle, game_core, window_center); // Handle player out of breath if game_core.player.breath_percent == 0.0 { return Some(GameState::GameEnd); } if game_core .world .end_position .distance_to(game_core.player.position) <= 40.0 { return Some(GameState::WinGame); } return None; } }