From 15e91a5634be985c2d83dbb373b122f70d683132 Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Sun, 4 Oct 2020 12:14:46 +0900 Subject: [PATCH] Impl moving from room to room Added doors and interactables --- resources/door.png | Bin 0 -> 3200 bytes src/door.rs | 78 ++++++++++++++ src/interactable.rs | 73 +++++++++++++ src/main.rs | 2 + src/scenes/mainscene.rs | 231 ++++++++++++++++++++++++++++++++++++---- 5 files changed, 365 insertions(+), 19 deletions(-) create mode 100644 resources/door.png create mode 100644 src/door.rs create mode 100644 src/interactable.rs diff --git a/resources/door.png b/resources/door.png new file mode 100644 index 0000000000000000000000000000000000000000..6e04c7c56d934b2a2e7846fcb3284b59766eb53c GIT binary patch literal 3200 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 Self { + Door { + is_open, + x, + y, + id, + enter_radius: DEFAULT_RADIUS, + } + } + + pub fn draw(&self, ctx: &mut Context, door_image: &Image) -> GameResult<()> { + if self.is_open { + graphics::draw( + ctx, + door_image, + DrawParam::new() + .src(Rect::new(0f32, 0.8f32, 1f32, 0.2f32)) + .dest([self.x, self.y]), + )?; + } else { + graphics::draw(ctx, door_image, DrawParam::new().dest([self.x, self.y]))?; + } + Ok(()) + } + + pub fn get_open(&self) -> bool { + self.is_open + } + + pub fn set_open(&mut self, is_open: bool) { + self.is_open = is_open; + } + + pub fn toggle_open(&mut self) -> bool { + self.is_open = !self.is_open; + self.is_open + } + + pub fn get_id(&self) -> usize { + self.id + } + + pub fn is_within_range(&self, x: f32, y: f32) -> bool { + let a = (self.x + 96f32 / 2f32) - x; + let b = (self.y + 80f32) - y; + (a * a + b * b).sqrt() <= self.enter_radius + } + + pub fn get_x(&self) -> f32 { + self.x + } + + pub fn get_y(&self) -> f32 { + self.y + } + + pub fn get_center_x(&self) -> f32 { + self.x + 96f32 / 2f32 + } + + pub fn get_center_y(&self) -> f32 { + self.y + 80f32 + } +} diff --git a/src/interactable.rs b/src/interactable.rs new file mode 100644 index 0000000..a7c78fe --- /dev/null +++ b/src/interactable.rs @@ -0,0 +1,73 @@ +use ggez::graphics::{self, Color, DrawMode, DrawParam, Mesh, Rect}; +use ggez::{Context, GameResult}; + +const DEFAULT_RADIUS: f32 = 70f32; + +#[derive(Copy, Clone, PartialEq)] +pub enum InteractableType { + Door(usize), +} + +pub struct Interactable { + itype: InteractableType, + x: f32, + y: f32, + radius: f32, +} + +impl Interactable { + pub fn new(itype: InteractableType, x: f32, y: f32) -> Self { + Self { + itype, + x, + y, + radius: DEFAULT_RADIUS, + } + } + + pub fn is_within_range(&self, x: f32, y: f32) -> bool { + let a = self.x - x; + let b = self.y - y; + (a * a + b * b).sqrt() <= self.radius + } + + pub fn get_type(&self) -> InteractableType { + self.itype + } + + pub fn get_radius(&mut self) -> f32 { + self.radius + } + + pub fn set_radius(&mut self, radius: f32) { + self.radius = radius; + } + + pub fn get_x(&self) -> f32 { + self.x + } + + pub fn get_y(&self) -> f32 { + self.y + } + + pub fn draw(&self, ctx: &mut Context) -> GameResult<()> { + match self.itype { + InteractableType::Door(_) => { + let panel_mesh = Mesh::new_rectangle( + ctx, + DrawMode::fill(), + Rect::new(0f32, 0f32, 14f32, 16f32), + Color::from_rgb(0x16, 0x9c, 0xd8), + )?; + graphics::draw( + ctx, + &panel_mesh, + DrawParam::new().dest([self.x - 7f32, self.y - 8f32]), + )?; + } + } + + Ok(()) + } +} diff --git a/src/main.rs b/src/main.rs index 1d51933..795c423 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,6 @@ +mod door; mod game; +mod interactable; mod player; mod scenes; diff --git a/src/scenes/mainscene.rs b/src/scenes/mainscene.rs index 532f49f..79cf0c7 100644 --- a/src/scenes/mainscene.rs +++ b/src/scenes/mainscene.rs @@ -10,6 +10,8 @@ use ggez::timer::delta; use ggez::{Context, GameResult}; use super::Scene; +use crate::door::Door; +use crate::interactable::{Interactable, InteractableType}; use crate::player::Player; const DARKNESS_PAN_RATE: f32 = 40f32; @@ -30,6 +32,7 @@ enum State { enum Room { StasisPod, + LeftOfPod, } enum WalkingState { @@ -57,6 +60,11 @@ pub struct MainScene { index: usize, room: Room, walking_state: WalkingState, + door_image: Image, + interactables: Vec, + interact_text: Text, + doors: Vec, + door_text: Text, } impl MainScene { @@ -64,8 +72,12 @@ impl MainScene { let mut music = Source::new(ctx, "/music00.ogg").unwrap(); music.set_repeat(true); // music.play().unwrap(); - let mut current_text = Text::new("".to_owned()); + let mut current_text = Text::new(""); current_text.set_font(font, Scale::uniform(26f32)); + let mut interact_text = Text::new("[E] or Left Click to Interact"); + interact_text.set_font(font, Scale::uniform(20f32)); + let mut door_text = Text::new("[W] or Right Click to enter door"); + door_text.set_font(font, Scale::uniform(20f32)); Self { font, player, @@ -85,12 +97,105 @@ impl MainScene { index: 0usize, room: Room::StasisPod, walking_state: WalkingState::Standing, + door_image: Image::new(ctx, "/door.png").unwrap(), + interactables: Vec::new(), + interact_text, + doors: Vec::new(), + door_text, } } pub fn new_boxed(ctx: &mut Context, font: Font, player: Rc>) -> Box { Box::new(Self::new(ctx, font, player)) } + + fn init_room(&mut self) { + match self.room { + Room::StasisPod => { + self.current_text = Text::new("A and D or Left and Right or Left Click to move"); + self.current_text.set_font(self.font, Scale::uniform(26f32)); + self.darkness_yoffset = -300f32; + self.interactables.clear(); + self.doors.clear(); + } + Room::LeftOfPod => { + self.current_text = Text::new(""); + self.current_text.set_font(self.font, Scale::uniform(26f32)); + self.interactables.clear(); + self.interactables.push(Interactable::new( + InteractableType::Door(0), + 430f32, + 450f32, + )); + self.darkness_yoffset = -300f32; + self.doors.clear(); + self.doors + .push(Door::new(false, 300f32, 600f32 - 160f32 - 50f32, 0)); + } + } + } + + fn draw_room_arrows(&mut self, ctx: &mut Context) -> GameResult<()> { + let mut draw_left = false; + let mut draw_right = false; + match self.room { + Room::StasisPod => { + draw_left = true; + } + Room::LeftOfPod => { + draw_right = true; + } + } + + if draw_left { + let mesh = Mesh::from_triangles( + ctx, + &[[32f32, 0f32], [32f32, 32f32], [0f32, 16f32]], + graphics::WHITE, + )?; + graphics::draw(ctx, &mesh, DrawParam::new().dest([32f32, 530f32]))?; + } + if draw_right { + let mesh = Mesh::from_triangles( + ctx, + &[[0f32, 0f32], [32f32, 16f32], [0f32, 32f32]], + graphics::WHITE, + )?; + graphics::draw(ctx, &mesh, DrawParam::new().dest([800f32 - 64f32, 530f32]))?; + } + + Ok(()) + } + + fn check_exit_left(&mut self) { + match self.room { + Room::StasisPod => { + self.room = Room::LeftOfPod; + self.player.borrow_mut().x = 800f32 - 70f32; + self.init_room(); + } + Room::LeftOfPod => (), + } + } + + fn check_exit_right(&mut self) { + match self.room { + Room::StasisPod => (), + Room::LeftOfPod => { + self.room = Room::StasisPod; + self.player.borrow_mut().x = 70f32; + self.init_room(); + } + } + } + + fn use_interactable(&mut self, itype: InteractableType) { + match itype { + InteractableType::Door(id) => { + self.doors[id].toggle_open(); + } + } + } } impl EventHandler for MainScene { @@ -147,37 +252,43 @@ impl EventHandler for MainScene { } else if self.timer <= 0f32 { self.state = State::Investigate; self.music.play()?; + self.init_room(); } } State::Investigate => { - let mut player = self.player.borrow_mut(); match self.walking_state { WalkingState::Standing => { - player.set_walking(false); + self.player.borrow_mut().set_walking(false); } WalkingState::Left => { - player.x -= dt * PLAYER_MOVEMENT_SPEED; - if player.x <= 0f32 { - player.x = 0f32; + self.player.borrow_mut().x -= dt * PLAYER_MOVEMENT_SPEED; + if self.player.borrow().x <= 0f32 { + self.player.borrow_mut().x = 0f32; self.walking_state = WalkingState::Standing; - player.set_walking(false); + self.player.borrow_mut().set_walking(false); + self.check_exit_left(); } else { - player.set_walking(true); - player.set_xflip(true); + self.player.borrow_mut().set_walking(true); + self.player.borrow_mut().set_xflip(true); } } WalkingState::Right => { - player.x += dt * PLAYER_MOVEMENT_SPEED; - if player.x + 64f32 >= 800f32 { - player.x = 800f32 - 64f32; + self.player.borrow_mut().x += dt * PLAYER_MOVEMENT_SPEED; + if self.player.borrow().x + 64f32 >= 800f32 { + self.player.borrow_mut().x = 800f32 - 64f32; self.walking_state = WalkingState::Standing; - player.set_walking(false); + self.player.borrow_mut().set_walking(false); + self.check_exit_right(); } else { - player.set_walking(true); - player.set_xflip(false); + self.player.borrow_mut().set_walking(true); + self.player.borrow_mut().set_xflip(false); } } } + match self.room { + Room::StasisPod => (), + Room::LeftOfPod => (), + } } } self.player.borrow_mut().update(ctx)?; @@ -229,6 +340,13 @@ impl EventHandler for MainScene { DrawParam::new().dest([600f32, 170f32]).rotation(0.7f32), )?; } + Room::LeftOfPod => {} + } + for door in &self.doors { + door.draw(ctx, &self.door_image)?; + } + for interactable in &self.interactables { + interactable.draw(ctx)?; } self.player.borrow_mut().draw(ctx)?; } @@ -250,7 +368,56 @@ impl EventHandler for MainScene { )?; } State::GetOutOfPod => (), - State::Investigate => (), + State::Investigate => { + match self.room { + Room::StasisPod => { + graphics::draw( + ctx, + &self.current_text, + DrawParam::new() + .dest([100f32, 100f32]) + .color(graphics::WHITE), + )?; + } + Room::LeftOfPod => { + for interactable in &self.interactables { + if interactable.is_within_range( + self.player.borrow().x + 32f32, + self.player.borrow().y + 64f32, + ) { + let text_offset = (self.interact_text.width(ctx) / 2) as f32; + graphics::draw( + ctx, + &self.interact_text, + DrawParam::new().dest([ + interactable.get_x() - text_offset, + interactable.get_y() - 50f32, + ]), + )?; + } + } + for door in &self.doors { + if door.is_within_range( + self.player.borrow().x + 32f32, + self.player.borrow().y + 64f32, + ) && door.get_open() + { + let text_offset = (self.door_text.width(ctx) / 2) as f32; + graphics::draw( + ctx, + &self.door_text, + DrawParam::new().dest([ + door.get_center_x() - text_offset, + door.get_y() - 15f32, + ]), + )?; + } + } + } + } + + self.draw_room_arrows(ctx)?; + } } Ok(()) @@ -270,10 +437,22 @@ impl EventHandler for MainScene { State::GetOutOfPod => (), State::Investigate => { if button == MouseButton::Left { - let player = self.player.borrow(); - if player.x > x { + let mut itype: Option = None; + for interactable in &self.interactables { + if interactable.is_within_range( + self.player.borrow().x + 32f32, + self.player.borrow().y + 64f32, + ) && interactable.is_within_range(x, y) + { + itype = Some(interactable.get_type()); + break; + } + } + if let Some(it) = itype { + self.use_interactable(it); + } else if self.player.borrow().x > x { self.walking_state = WalkingState::Left; - } else if player.x + 64f32 < x { + } else if self.player.borrow().x + 64f32 < x { self.walking_state = WalkingState::Right; } } @@ -322,6 +501,20 @@ impl EventHandler for MainScene { if self.player.borrow().x + 64f32 < 800f32 { self.walking_state = WalkingState::Right; } + } else if keycode == KeyCode::E { + let mut itype: Option = None; + for interactable in &self.interactables { + if interactable.is_within_range( + self.player.borrow().x + 32f32, + self.player.borrow().y + 64f32, + ) { + itype = Some(interactable.get_type()); + break; + } + } + if let Some(it) = itype { + self.use_interactable(it); + } } } } -- 2.49.0