diff --git a/assets/ewpratten/prp_cupIcon/prp_cupIcon.xcf b/assets/ewpratten/prp_cupIcon/prp_cupIcon.xcf new file mode 100644 index 00000000..e8225560 Binary files /dev/null and b/assets/ewpratten/prp_cupIcon/prp_cupIcon.xcf differ diff --git a/game/dist/assets/prp/prp_cupIcon/prp_cupIcon.png b/game/dist/assets/prp/prp_cupIcon/prp_cupIcon.png new file mode 100644 index 00000000..fe5a3911 Binary files /dev/null and b/game/dist/assets/prp/prp_cupIcon/prp_cupIcon.png differ diff --git a/game/dist/map_gameMap.end.json b/game/dist/map_gameMap.end.json new file mode 100644 index 00000000..77e1c780 --- /dev/null +++ b/game/dist/map_gameMap.end.json @@ -0,0 +1,4 @@ +[ + 0, + 0 +] \ No newline at end of file diff --git a/game/game_logic/src/rendering/utilities/map_render.rs b/game/game_logic/src/rendering/utilities/map_render.rs index 6452595e..93708e60 100644 --- a/game/game_logic/src/rendering/utilities/map_render.rs +++ b/game/game_logic/src/rendering/utilities/map_render.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, path::PathBuf, sync::Arc}; use crate::{ - asset_manager::{load_texture_from_internal_data, InternalData}, + asset_manager::{load_json_structure, load_texture_from_internal_data, InternalData}, model::{world_object::WorldSpaceObjectCollider, world_object_package::WorldObjectPackage}, }; use nalgebra as na; @@ -79,6 +79,8 @@ pub struct MapRenderer { map: Map, tile_textures: HashMap, world_objects: WorldObjectPackage, + world_end: na::Vector2, + cup_icon: Texture2D, } impl MapRenderer { @@ -86,6 +88,7 @@ impl MapRenderer { pub fn new( tmx_path: &str, objects_path: &str, + end_path: &str, raylib: &mut RaylibHandle, raylib_thread: &RaylibThread, ) -> Result { @@ -99,6 +102,14 @@ impl MapRenderer { let mut loader = Loader::with_cache(ProgramDataTileCache::new()); let map = loader.load_tmx_map_from(data.as_slice(), tmx_path)?; + // Load the cup icon + let cup_icon = load_texture_from_internal_data( + raylib, + raylib_thread, + "assets/prp/prp_cupIcon/prp_cupIcon.png", + ) + .unwrap(); + // Iterate over all images in the map let mut tile_textures = HashMap::new(); for tileset in map.tilesets() { @@ -123,11 +134,14 @@ impl MapRenderer { // Load the world objects let world_objects = WorldObjectPackage::load(raylib, raylib_thread, objects_path).unwrap(); + let world_end = load_json_structure(end_path).unwrap(); Ok(Self { map, tile_textures, world_objects, + world_end, + cup_icon, }) } @@ -264,10 +278,7 @@ impl MapRenderer { Vector2::new(screen_width as f32, screen_height as f32), camera, ); - let player_position = na::Vector2::new( - player_position.x, - player_position.y * -1.0, - ); + let player_position = na::Vector2::new(player_position.x, player_position.y * -1.0); // Handle each layer from the bottom up for layer in self.map.layers() { @@ -324,7 +335,8 @@ impl MapRenderer { // Check if there is an object at this tile for obj_ref in &self.world_objects.object_references { if obj_ref.get_tile_space_position().x == sampler_x as f32 - && obj_ref.get_tile_space_position().y == sampler_y as f32 + && obj_ref.get_tile_space_position().y + == sampler_y as f32 { // Get access to the actual object definition let object_key = obj_ref.into_key(); @@ -344,7 +356,8 @@ impl MapRenderer { .unwrap(); tex.render_automatic( draw_handle, - obj_ref.get_world_space_position() - (tex.size() / 2.0), + obj_ref.get_world_space_position() + - (tex.size() / 2.0), None, Some(tex.size() / 2.0), Some(obj_ref.rotation_degrees), @@ -356,7 +369,8 @@ impl MapRenderer { .bottom_static_textures .get_mut(&object_key) .unwrap(); - let p: Vector2 = obj_ref.get_world_space_position().into(); + let p: Vector2 = + obj_ref.get_world_space_position().into(); let r1 = Rectangle { x: 0.0, y: 0.0, @@ -390,8 +404,10 @@ impl MapRenderer { if let Some(footprint_radius) = obj_def.visualization_radius { - let player_dist_to_object = - (obj_ref.get_world_space_position() - player_position).norm(); + let player_dist_to_object = (obj_ref + .get_world_space_position() + - player_position) + .norm(); // debug!( // "Player dist to object: {}", // player_dist_to_object @@ -409,7 +425,8 @@ impl MapRenderer { .unwrap(); tex.render_automatic( draw_handle, - obj_ref.get_world_space_position() - (tex.size() / 2.0), + obj_ref.get_world_space_position() + - (tex.size() / 2.0), None, Some(tex.size() / 2.0), Some(obj_ref.rotation_degrees), @@ -421,7 +438,8 @@ impl MapRenderer { .top_static_textures .get_mut(&object_key) .unwrap(); - let p: Vector2 = obj_ref.get_world_space_position().into(); + let p: Vector2 = + obj_ref.get_world_space_position().into(); let r1 = Rectangle { x: 0.0, y: 0.0, @@ -478,73 +496,103 @@ impl MapRenderer { self.world_objects.world_space_colliders.clone() } - // /// Used to modify the player's velocity based on the effects of the world - // pub fn effect_velocity_with_collisions( - // &self, - // player_position: na::Vector2, - // player_velocity: na::Vector2, - // ) -> na::Vector2 { - // // If the player is not moving, we don't need to do anything - // if player_velocity.norm() == 0.0 { - // return player_velocity; - // } + pub fn is_point_inside_win_zone(&self, position: na::Vector2) -> bool { + // Convert the position to a tile position + let tile_x = (position.x / 128.0).floor() as i32; + let tile_y = ((position.y * -1.0) / 128.0).floor() as i32; + // debug!("Tile x: {}, y: {}", tile_x, tile_y); - // // Get the velocity unit vector - // let player_velocity_unit_vector = player_velocity.normalize(); + // Check if the tile is inside the win zone + tile_x == self.world_end.x && tile_y == self.world_end.y + } - // // Find the position 1 pixel infront of the player - // let player_position_1_pixel_infront = player_position + player_velocity_unit_vector; - // let next_player_position = player_position + player_velocity; + pub fn get_screenspace_vector_to_win_zone( + &self, + draw: &mut RaylibDrawHandle, + screen_center: na::Vector2, + camera: &Camera2D, + ) -> na::Vector2 { + let win_zone_position_world_space = Vector2::new( + self.world_end.x as f32 * 128.0, + (self.world_end.y as f32 * -1.0) * 128.0, + ); + let win_zone_position_screenspace = + draw.get_world_to_screen2D(win_zone_position_world_space, camera); + na::Vector2::new( + win_zone_position_screenspace.x - screen_center.x, + win_zone_position_screenspace.y - screen_center.y, + ) + } - // // Check if this is in the collision zone of any objects - // for obj_ref in &self.world_objects.object_references { - // // Filter out anything more than 1000 pixels away - // if (obj_ref.position - player_position).norm() > 1000.0 { - // continue; - // } + pub fn render_hud_endgoal_arrow( + &self, + draw: &mut RaylibDrawHandle, + player_position: na::Vector2, + camera: &Camera2D, + ) { + // Get the center of the screen + let screen_center = na::Vector2::new( + draw.get_screen_width() as f32 / 2.0, + draw.get_screen_height() as f32 / 2.0, + ); - // // Get the object definition - // let object_key = obj_ref.into_key(); - // let obj_def = self - // .world_objects - // .object_definitions - // .get(&object_key) - // .unwrap(); + // Get the vector to the win zone + let vector_to_win_zone = + self.get_screenspace_vector_to_win_zone(draw, screen_center, camera); + let unit_vector_to_win_zone = vector_to_win_zone.normalize(); - // // Check if the player is about to be in a collision zone - // for collider in &obj_def.physics_colliders { - // // Handle a radius collider vs a size collider - // if let Some(radius) = collider.radius { - // // Check if we are about to collide with the circle - // if (next_player_position - obj_ref.position).norm() < radius { - // // Get the angle from the player to the center of the collider - // let angle_to_center = - // (obj_ref.position - player_position).angle(&na::Vector2::new(0.0, 0.0)); - // if angle_to_center.abs() <= std::f32::consts::FRAC_PI_2 { - // // Apply the inverse of the velocity to the player - // return na::Vector2::zeros(); - // } - // } - // } else if let Some(size) = collider.size { - // // TODO: make this work for regular and rotated objects - // } - // } - // } + // Get the screenspace position of the win zone + let win_zone_position_screenspace = draw.get_world_to_screen2D( + Vector2::new( + self.world_end.x as f32 * 128.0, + (self.world_end.y as f32 * -1.0) * 128.0, + ), + camera, + ); - // // Check if the player is about to leave the map - // let mut player_velocity = player_velocity; - // if next_player_position.x < 0.0 { - // player_velocity.x = 0.0; - // } else if next_player_position.x > self.map.width as f32 * 128.0 { - // player_velocity.x = 0.0; - // } - // if next_player_position.y > 0.0 { - // player_velocity.y = 0.0; - // } else if next_player_position.y < self.map.height as f32 * -128.0 { - // player_velocity.y = 0.0; - // } + // If the win zone is inside the camera's view, don't render the arrow + if !(win_zone_position_screenspace.x < 0.0 + || win_zone_position_screenspace.x > draw.get_screen_width() as f32 + || win_zone_position_screenspace.y < 0.0 + || win_zone_position_screenspace.y > draw.get_screen_height() as f32) + { + return; + } - // // If we got here, the player is not in a collision zone - // player_velocity - // } + // // Draw the line + // draw.draw_line( + // screen_center.x as i32, + // screen_center.y as i32, + // (win_zone_position_screenspace.x + unit_vector_to_win_zone.x * 32.0) as i32, + // (win_zone_position_screenspace.y + unit_vector_to_win_zone.y * 32.0) as i32, + // Color::RED, + // ); + + // // Define the screen rect + // let screen_rect = Rectangle { + // x: 0.0, + // y: 0.0, + // width: draw.get_screen_width() as f32, + // height: draw.get_screen_height() as f32, + // }; + + // Find the intersection of the line and the screen edge + let intersect = unit_vector_to_win_zone * (draw.get_screen_height() as f32 / 2.0); + + // Render the cup icon + draw.draw_texture( + &self.cup_icon, + (screen_center.x + intersect.x) as i32 - self.cup_icon.width as i32 / 2, + (screen_center.y + intersect.y) as i32 - self.cup_icon.height as i32 / 2, + Color::WHITE, + ); + + // Render a circle where the line meets the screen edge + draw.draw_circle_lines( + (screen_center.x + intersect.x) as i32, + (screen_center.y + intersect.y) as i32, + 16.0, + Color::BLACK, + ); + } } diff --git a/game/game_logic/src/scenes/main_menu.rs b/game/game_logic/src/scenes/main_menu.rs index 58d39cf9..661352df 100644 --- a/game/game_logic/src/scenes/main_menu.rs +++ b/game/game_logic/src/scenes/main_menu.rs @@ -107,11 +107,16 @@ impl MainMenu { // TODO: Render stuff //Label Colors - let label_colors = Color::BLACK; - let label_shadow_colors = Color::GRAY; + let label_colors = Color::new(123, 201, 244, 255); + let label_shadow_colors = Color::new(82, 135, 195, 255); //Initial Option placeholder words in the main menu + draw.draw_text(&constants.game_name, 103, 93, 60, label_shadow_colors); + draw.draw_text(&constants.game_name, 97, 87, 60, label_shadow_colors); draw.draw_text(&constants.game_name, 100, 90, 60, label_colors); + + + //Options draw.draw_text("Start Game", 100, 190, 34, label_colors); draw.draw_text("Credits", 100, 410, 34, label_colors); draw.draw_text("Leaderboard", 100, 470, 34, label_colors); @@ -146,7 +151,7 @@ impl MainMenu { } //Volume Controller - //Color Pallete Variables + //Colors Pallete Variables let tile_color = Color::new(158, 93, 65, 255); let outer_ring_color = Color::new(255, 191, 113, 255); let inner_ring_color = Color::new(244, 203, 184, 255); @@ -395,9 +400,10 @@ impl MainMenu { constants: &ProjectConstants, audio_subsystem: &mut RaylibAudio, ) -> MenuStateSignal { + //Colors - let label_colors = Color::BLACK; - let label_shadow_colors = Color::GRAY; + let label_colors = Color::new(123, 201, 244, 255); + let label_shadow_colors = Color::new(82, 135, 195, 255); let credits_colours = Color::new(82, 135, 195, 255); let mut draw = raylib.begin_drawing(rl_thread); @@ -414,6 +420,8 @@ impl MainMenu { let window_height = draw.get_screen_height(); let window_width = draw.get_screen_width(); + draw.draw_text("Credits", (window_width / 2) - 97, 27, 55, label_shadow_colors); + draw.draw_text("Credits", (window_width / 2) - 103, 33, 55, label_shadow_colors); draw.draw_text("Credits", (window_width / 2) - 100, 30, 55, label_colors); draw.draw_text( @@ -511,8 +519,8 @@ impl MainMenu { audio_subsystem: &mut RaylibAudio, ) -> MenuStateSignal { //Colors - let label_colors = Color::BLACK; - let label_shadow_colors = Color::GRAY; + let label_colors = Color::new(123, 201, 244, 255); + let label_shadow_colors = Color::new(82, 135, 195, 255); let mut draw = raylib.begin_drawing(rl_thread); draw.clear_background(Color::WHITE); @@ -529,6 +537,20 @@ impl MainMenu { draw.draw_text(&mouse_y.to_string(), 70, 5, 20, Color::BLACK); let window_width = draw.get_screen_width(); + draw.draw_text( + "Leaderboard", + (window_width / 2) - 173, + 27, + 55, + label_shadow_colors, + ); + draw.draw_text( + "Leaderboard", + (window_width / 2) - 179, + 33, + 55, + label_shadow_colors, + ); draw.draw_text( "Leaderboard", (window_width / 2) - 176, diff --git a/game/game_logic/src/scenes/pause_menu.rs b/game/game_logic/src/scenes/pause_menu.rs index c5069e8e..54573b2f 100644 --- a/game/game_logic/src/scenes/pause_menu.rs +++ b/game/game_logic/src/scenes/pause_menu.rs @@ -54,8 +54,8 @@ impl PauseMenu { draw.clear_background(Color::WHITE); //Color Pallette - let label_colors = Color::BLACK; - let label_shadow_colors = Color::GRAY; + let label_colors = Color::new(123, 201, 244, 255); + let label_shadow_colors = Color::new(82, 135, 195, 255); //Obtain mouse position let mouse_x = draw.get_mouse_x(); @@ -78,6 +78,8 @@ impl PauseMenu { } // Title + draw.draw_text("Paused", 97, 87, 60, label_shadow_colors); + draw.draw_text("Paused", 103, 93, 60, label_shadow_colors); draw.draw_text("Paused", 100, 90, 60, label_colors); //Return to Main Menu button variables diff --git a/game/game_logic/src/scenes/player_interaction.rs b/game/game_logic/src/scenes/player_interaction.rs index 385e1182..9ff4ab04 100644 --- a/game/game_logic/src/scenes/player_interaction.rs +++ b/game/game_logic/src/scenes/player_interaction.rs @@ -30,6 +30,7 @@ pub struct PlayableScene { world_colliders: Vec, show_debug_info: bool, play_start_time: DateTime, + player_start_position: na::Vector2, } impl PlayableScene { @@ -42,22 +43,27 @@ impl PlayableScene { let map_renderer = MapRenderer::new( "map_gameMap.tmx", "map_gameMap.objects.json", + "map_gameMap.end.json", raylib_handle, thread, ) .unwrap(); let world_colliders = map_renderer.get_world_colliders(); + // Define the player start position + let player_start_position = na::Vector2::new( + 10.0 * constants.tile_size as f32, + -10.0 * constants.tile_size as f32, + ); + // Load the game music let game_soundtrack = load_music_from_internal_data(thread, "assets/audio/gameSoundtrack.mp3").unwrap(); Self { has_updated_discord_rpc: false, - player: Player::new(na::Vector2::new( - 10.0 * constants.tile_size as f32, - -10.0 * constants.tile_size as f32, - )), + player_start_position, + player: Player::new(player_start_position), world_map: map_renderer, camera: raylib::camera::Camera2D { target: raylib::math::Vector2 { x: 0.0, y: 0.0 }, @@ -98,6 +104,8 @@ impl PlayableScene { .unwrap(); self.has_updated_discord_rpc = true; self.play_start_time = Utc::now(); + self.player.position = self.player_start_position; + self.player.velocity = na::Vector2::new(0.0, 0.0); } // Ensure the game soundtrack is playing @@ -119,9 +127,6 @@ impl PlayableScene { self.draw_ui(&mut draw, constants); // NOTE: If you want to trigger a cutscene, do it here by using one of: - // return MenuStateSignal::DoFinishedCutscene { - // playtime: Utc::now().signed_duration_since(self.play_start_time), - // }; // return MenuStateSignal::DoMeltedDeathCutscene { // playtime: Utc::now().signed_duration_since(self.play_start_time), // }; @@ -129,6 +134,16 @@ impl PlayableScene { // playtime: Utc::now().signed_duration_since(self.play_start_time), // }; + // Handle winning + if self + .world_map + .is_point_inside_win_zone(self.player.position) + { + return MenuStateSignal::DoFinishedCutscene { + playtime: Utc::now().signed_duration_since(self.play_start_time), + }; + } + // A little hack to make pausing work if draw.is_key_pressed(KeyboardKey::KEY_ESCAPE) { return MenuStateSignal::DoPauseMenu; @@ -197,14 +212,18 @@ impl PlayableScene { ); } - draw.draw_rectangle(draw.get_screen_width() / 2 - 225, 0, 450, 40, Color::WHITE); - draw.draw_text( - "Unregistered HyperCam 2", - draw.get_screen_width() / 2 - 215, - 0, - 32, - Color::BLACK, - ); + // Draw the hint arrow + self.world_map + .render_hud_endgoal_arrow(draw, self.player.position, &self.camera); + + // draw.draw_rectangle(draw.get_screen_width() / 2 - 225, 0, 450, 40, Color::WHITE); + // draw.draw_text( + // "Unregistered HyperCam 2", + // draw.get_screen_width() / 2 - 215, + // 0, + // 32, + // Color::BLACK, + // ); } // Physics diff --git a/game/game_logic/src/scenes/test_fox.rs b/game/game_logic/src/scenes/test_fox.rs index a4d19043..99d68e94 100644 --- a/game/game_logic/src/scenes/test_fox.rs +++ b/game/game_logic/src/scenes/test_fox.rs @@ -27,6 +27,7 @@ impl TestFoxScene { let map_renderer = MapRenderer::new( "map_gameMap.tmx", "map_gameMap.objects.json", + "map_gameMap.end.json", raylib_handle, thread, )