commit efd0b529d056a6831e41f01dce9fb3f37672e511 Author: Paul-Christian Volkmer Date: Wed Feb 16 14:37:54 2022 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..5dba0ea --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,188 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + +[[package]] +name = "getrandom" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e509672465a0504304aa87f9f176f2b2b716ed8fb105ebe5c02dc6dce96a94" + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "sdl2" +version = "0.35.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7959277b623f1fb9e04aea73686c3ca52f01b2145f8ea16f4ff30d8b7623b1a" +dependencies = [ + "bitflags", + "lazy_static", + "libc", + "sdl2-sys", +] + +[[package]] +name = "sdl2-sys" +version = "0.35.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3586be2cf6c0a8099a79a12b4084357aa9b3e0b0d7980e3b67aaf7a9d55f9f0" +dependencies = [ + "cfg-if", + "libc", + "version-compare", +] + +[[package]] +name = "time" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "version-compare" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe88247b92c1df6b6de80ddc290f3976dbdf2f5f5d3fd049a9fb598c6dd5ca73" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winelounge" +version = "0.1.0" +dependencies = [ + "chrono", + "rand", + "sdl2", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..67ff80e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "winelounge" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +chrono = "0.4" +rand = "0.8" + +[dependencies.sdl2] +version = "0.35" +features = ["image", "ttf"] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f4aa621 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Paul-Christian Volkmer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.adoc b/README.adoc new file mode 100644 index 0000000..e7ed154 --- /dev/null +++ b/README.adoc @@ -0,0 +1,15 @@ += Wine Lounge + +This simple retro game is my first attempt at creating a small game. + +Since it was written within one day, the code is more of a quick hack than proper software. +But this game may evolve ... ;) + +== Gameplay + +Use the arrow keys to move the lady to hidden boxes, collect empty glasses and fill them with delicious wine. +Bring the wine to her wine lounge and increase your score. But beware of the stumbling blocks. + +image::assets/image.png[] + +That's all. diff --git a/assets/font.ttf b/assets/font.ttf new file mode 100644 index 0000000..ab11d31 Binary files /dev/null and b/assets/font.ttf differ diff --git a/assets/image.png b/assets/image.png new file mode 100644 index 0000000..1849e8f Binary files /dev/null and b/assets/image.png differ diff --git a/assets/sprite.png b/assets/sprite.png new file mode 100644 index 0000000..15145e7 Binary files /dev/null and b/assets/sprite.png differ diff --git a/assets/sprite.svg b/assets/sprite.svg new file mode 100644 index 0000000..249b914 --- /dev/null +++ b/assets/sprite.svg @@ -0,0 +1,2160 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ? + + + + + + + + + + + + + + + + + + + + + + + + + + Lounge + + + + + + + + + + + + + + diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..613c9c8 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,129 @@ +mod player; +mod world; + +use std::time::Duration; + +use crate::player::player::Player; +use crate::world::world::{BoxAreaContent, BoxAreaPosition, World}; +use sdl2::event::Event; +use sdl2::image::LoadTexture; +use sdl2::keyboard::Keycode; +use sdl2::pixels::Color; +use sdl2::rect::{Point, Rect}; +use sdl2::render::{Texture, WindowCanvas}; +use sdl2::ttf::Font; + +const GLASS_SPACE: u8 = 5; + +fn main() { + let sdl_context = sdl2::init().unwrap(); + let video_subsystem = sdl_context.video().unwrap(); + + let window = video_subsystem + .window("Wine Lounge", 800, 600) + .position_centered() + .build() + .unwrap(); + + let mut canvas = window.into_canvas().build().unwrap(); + + canvas.set_draw_color(Color::RGB(44, 48, 63)); + canvas.clear(); + canvas.present(); + + let texture_creator = canvas.texture_creator(); + let texture = texture_creator.load_texture("./assets/sprite.png").unwrap(); + + let font = sdl2::ttf::init().unwrap(); + let font = font.load_font("./assets/font.ttf", 20).unwrap(); + + let mut event_pump = sdl_context.event_pump().unwrap(); + + let mut world = World::init(); + + 'running: loop { + for event in event_pump.poll_iter() { + match event { + Event::Quit { .. } + | Event::KeyDown { + keycode: Some(Keycode::Escape), + .. + } => { + break 'running; + } + Event::KeyDown { + keycode: Some(Keycode::Up) | Some(Keycode::W), + .. + } => { + world.move_up(); + } + Event::KeyDown { + keycode: Some(Keycode::Down) | Some(Keycode::S), + .. + } => { + world.move_down(); + } + Event::KeyDown { + keycode: Some(Keycode::Left) | Some(Keycode::A), + .. + } => { + world.move_left(); + } + Event::KeyDown { + keycode: Some(Keycode::Right) | Some(Keycode::D), + .. + } => { + world.move_right(); + } + Event::KeyUp { .. } => { + world.stop_player(); + } + _ => {} + } + } + + if world.collides_with_lounge() && world.player.can_drink_glass() { + world.player.drink_glass() + } + + let colliding_box_area = match world.collides_with_box_area() { + Some(BoxAreaPosition::RightTop) => Option::Some(&mut world.right_top_box_area), + Some(BoxAreaPosition::RightBottom) => Option::Some(&mut world.right_bottom_box_area), + Some(BoxAreaPosition::LeftBottom) => Option::Some(&mut world.left_bottom_box_area), + Some(BoxAreaPosition::LeftTop) => Option::Some(&mut world.left_top_box_area), + None => Option::None, + }; + + match colliding_box_area { + Some(ba) => { + let content = match &ba.content { + BoxAreaContent::HiddenBox => BoxAreaContent::random(), + BoxAreaContent::EmptyGlass => BoxAreaContent::EmptyGlass, + BoxAreaContent::FilledBottle => BoxAreaContent::FilledBottle, + _ => BoxAreaContent::Nothing, + }; + + if content == BoxAreaContent::EmptyGlass && world.player.can_pick_glass() { + ba.update_content(BoxAreaContent::Nothing); + world.player.pick_glass(); + } else if content == BoxAreaContent::EmptyGlass && !world.player.can_pick_glass() { + ba.update_content(BoxAreaContent::EmptyGlass); + } else if content == BoxAreaContent::FilledBottle && world.player.can_fill_glass() { + ba.update_content(BoxAreaContent::EmptyBottle); + world.player.fill_glass(); + } else if content == BoxAreaContent::FilledBottle && !world.player.can_fill_glass() + { + ba.update_content(BoxAreaContent::FilledBottle); + } + } + None => {} + } + + if chrono::Utc::now().timestamp_millis() % 1000 > 950 { + world.update_box_areas(); + } + world.render(&mut canvas, &texture, &font); + + ::std::thread::sleep(Duration::from_millis(25)); + } +} diff --git a/src/player.rs b/src/player.rs new file mode 100644 index 0000000..0b35c44 --- /dev/null +++ b/src/player.rs @@ -0,0 +1,128 @@ +pub mod player { + use crate::GLASS_SPACE; + use sdl2::rect::{Point, Rect}; + + pub struct Player { + pub position: Point, + direction: PlayerDirection, + footstep: u8, + pub empty_glasses: u8, + pub filled_glasses: u8, + pub points: u32, + } + + enum PlayerDirection { + UP, + DOWN, + LEFT, + RIGHT, + } + + impl Player { + pub fn init() -> Player { + return Player { + position: Point::new(380, 250), + direction: PlayerDirection::DOWN, + footstep: 0, + empty_glasses: 0, + filled_glasses: 0, + points: 0, + }; + } + + pub fn can_pick_glass(&self) -> bool { + self.empty_glasses + self.filled_glasses < GLASS_SPACE + } + + pub fn pick_glass(&mut self) { + self.empty_glasses = self.empty_glasses + 1; + self.points = self.points + 2 + } + + pub fn can_fill_glass(&self) -> bool { + self.empty_glasses > 0 + } + + pub fn fill_glass(&mut self) { + self.empty_glasses = self.empty_glasses - 1; + self.filled_glasses = self.filled_glasses + 1; + self.points = self.points + 3 + } + + pub fn can_drink_glass(&self) -> bool { + self.filled_glasses > 0 + } + + pub fn drink_glass(&mut self) { + self.filled_glasses = self.filled_glasses - 1; + self.points = self.points + 5 + } + + pub fn center(&self) -> Point { + Point::new(self.position.x() + 19, self.position.y() + 56) + } + + pub fn bounding_rect(&self) -> Rect { + Rect::new(self.position.x(), self.position.y(), 40, 115) + } + + pub fn sprite(&self) -> Rect { + let x = match self.footstep { + 1 => 60, + 2 => 115, + _ => 5, + }; + + match self.direction { + PlayerDirection::DOWN => Rect::new(x, 5, 40, 115), + PlayerDirection::LEFT => Rect::new(x, 255, 40, 115), + PlayerDirection::RIGHT => Rect::new(x, 130, 40, 115), + PlayerDirection::UP => Rect::new(x, 380, 40, 115), + } + } + + pub fn face_up(&mut self) { + self.direction = PlayerDirection::UP; + } + + pub fn face_down(&mut self) { + self.direction = PlayerDirection::DOWN; + } + + pub fn face_left(&mut self) { + self.direction = PlayerDirection::LEFT; + } + + pub fn face_right(&mut self) { + self.direction = PlayerDirection::RIGHT; + } + + pub fn move_up(&mut self) { + self.face_up(); + self.footstep = &self.footstep % 2 + 1; + self.position.y = self.position.y - 15 + } + + pub fn move_down(&mut self) { + self.face_down(); + self.footstep = &self.footstep % 2 + 1; + self.position.y = self.position.y + 15 + } + + pub fn move_left(&mut self) { + self.face_left(); + self.footstep = &self.footstep % 2 + 1; + self.position.x = self.position.x - 15 + } + + pub fn move_right(&mut self) { + self.face_right(); + self.footstep = &self.footstep % 2 + 1; + self.position.x = self.position.x + 15 + } + + pub fn stop(&mut self) { + self.footstep = 0; + } + } +} diff --git a/src/world.rs b/src/world.rs new file mode 100644 index 0000000..63ebd77 --- /dev/null +++ b/src/world.rs @@ -0,0 +1,391 @@ +pub mod world { + use crate::{Player, GLASS_SPACE}; + use sdl2::pixels::Color; + use sdl2::rect::{Point, Rect}; + use sdl2::render::{Texture, WindowCanvas}; + use sdl2::ttf::Font; + + pub struct World { + pub player: Player, + pub right_top_box_area: BoxArea, + pub right_bottom_box_area: BoxArea, + pub left_bottom_box_area: BoxArea, + pub left_top_box_area: BoxArea, + stops: Vec, + } + + impl World { + pub fn init() -> World { + World { + player: Player::init(), + right_top_box_area: BoxArea::new( + BoxAreaPosition::RightTop, + BoxAreaContent::EmptyGlass, + ), + right_bottom_box_area: BoxArea::new( + BoxAreaPosition::RightBottom, + BoxAreaContent::HiddenBox, + ), + left_bottom_box_area: BoxArea::new( + BoxAreaPosition::LeftBottom, + BoxAreaContent::Nothing, + ), + left_top_box_area: BoxArea::new(BoxAreaPosition::LeftTop, BoxAreaContent::Nothing), + stops: vec![ + Point::new(380, 60), + Point::new(590, 450), + Point::new(720, 300), + Point::new(20, 410), + Point::new(190, 560), + ], + } + } + + pub fn collides_with_box_area(&mut self) -> Option { + if self.right_top_box_area.collides_with(&self.player) { + return Some(BoxAreaPosition::RightTop); + } else if self.right_bottom_box_area.collides_with(&self.player) { + return Some(BoxAreaPosition::RightBottom); + } else if self.left_bottom_box_area.collides_with(&self.player) { + return Some(BoxAreaPosition::LeftBottom); + } else if self.left_top_box_area.collides_with(&self.player) { + return Some(BoxAreaPosition::LeftTop); + } + + None + } + + pub fn collides_with_lounge(&mut self) -> bool { + let lounge_rect = Rect::new(325, 260, 150, 95); + lounge_rect.contains_point(self.player.center()) + } + + fn collides_with_stop(&mut self) -> bool { + for s in &self.stops { + let x = s.x() + 12; + let y = s.y() + 12; + if self.player.bounding_rect().contains_point(Point::new(x, y)) { + return true; + } + } + return false; + } + + pub fn move_up(&mut self) { + if self.player.position.y > 50 { + self.player.move_up(); + if self.collides_with_stop() { + self.player.move_down(); + self.player.face_up(); + } + } + } + + pub fn move_down(&mut self) { + if self.player.position.y < 600 - 110 { + self.player.move_down(); + if self.collides_with_stop() { + self.player.move_up(); + self.player.face_down(); + } + } + } + + pub fn move_left(&mut self) { + if self.player.position.x > 0 { + self.player.move_left(); + if self.collides_with_stop() { + self.player.move_right(); + self.player.face_left(); + } + } + } + + pub fn move_right(&mut self) { + if self.player.position.x < 800 - 40 { + self.player.move_right(); + if self.collides_with_stop() { + self.player.move_left(); + self.player.face_right(); + } + } + } + + pub fn stop_player(&mut self) { + self.player.stop() + } + + pub fn update_box_areas(&mut self) { + World::update_box_area(&mut self.right_top_box_area); + World::update_box_area(&mut self.right_bottom_box_area); + World::update_box_area(&mut self.left_bottom_box_area); + World::update_box_area(&mut self.left_top_box_area); + } + + fn update_box_area(box_area: &mut BoxArea) { + let now = chrono::Utc::now().timestamp(); + let r: i64 = (rand::random::() % 10) + 3; + + if box_area.content == BoxAreaContent::Nothing && box_area.last_update + 10 < now { + box_area.content = BoxAreaContent::HiddenBox; + box_area.last_update = now; + } else if box_area.content != BoxAreaContent::Nothing + && box_area.last_update + 30 < now - r + { + box_area.content = BoxAreaContent::Nothing; + box_area.last_update = now; + } + } + + pub fn render(&self, canvas: &mut WindowCanvas, texture: &Texture, font: &Font) { + canvas.clear(); + + canvas.set_draw_color(Color::RGB(160, 90, 44)); + canvas.fill_rect(Rect::new(0, 0, 800, 45)); + + canvas.set_draw_color(Color::RGB(206, 182, 115)); + + // Points/Glasses + (1..=GLASS_SPACE).for_each(|i| { + canvas.set_draw_color(Color::RGB(128, 51, 0)); + canvas.fill_rect(Rect::new(5, 37, GLASS_SPACE as u32 * 25 + 5, 4)); + + if self.player.filled_glasses + self.player.empty_glasses >= i { + canvas.copy( + texture, + Rect::new(35, 510, 20, 25), + Rect::new((i as i32) * 25 - 15, 10, 20, 25), + ); + } + if self.player.filled_glasses >= i { + canvas.copy( + texture, + Rect::new(5, 510, 20, 25), + Rect::new((i as i32) * 25 - 15, 10, 20, 25), + ); + } + }); + + // Lounge + canvas.copy( + texture, + Rect::new(5, 700, 150, 95), + Rect::new(325, 260, 150, 95), + ); + + // Box Areas + self.right_top_box_area.render(canvas, texture); + self.right_bottom_box_area.render(canvas, texture); + self.left_bottom_box_area.render(canvas, texture); + self.left_top_box_area.render(canvas, texture); + + // Decoration + canvas.copy( + texture, + Rect::new(130, 550, 25, 25), + Rect::new(235, 130, 25, 25), + ); + canvas.copy( + texture, + Rect::new(130, 550, 25, 25), + Rect::new(120, 210, 25, 25), + ); + canvas.copy( + texture, + Rect::new(130, 550, 25, 25), + Rect::new(535, 150, 25, 25), + ); + canvas.copy( + texture, + Rect::new(130, 550, 25, 25), + Rect::new(435, 370, 25, 25), + ); + canvas.copy( + texture, + Rect::new(130, 550, 25, 25), + Rect::new(235, 470, 25, 25), + ); + canvas.copy( + texture, + Rect::new(130, 550, 25, 25), + Rect::new(555, 510, 25, 25), + ); + + // Stops + for s in &self.stops { + canvas.copy( + texture, + Rect::new(130, 510, 25, 25), + Rect::new(s.x(), s.y(), 25, 25), + ); + } + + // Player + canvas.copy( + texture, + self.player.sprite(), + Rect::new(self.player.position.x(), self.player.position.y(), 40, 115), + ); + + // Points + let x = font + .render(format!("Score: {:#04}", self.player.points).as_str()) + .blended(Color::RGBA(246, 222, 155, 255)) + .unwrap(); + let t2 = canvas.texture_creator(); + let t2 = t2.create_texture_from_surface(&x).unwrap(); + + canvas.copy( + &t2, + x.rect(), + Some(Rect::new(790 - x.width() as i32, 8, x.width(), x.height())), + ); + canvas.set_draw_color(Color::RGB(206, 182, 115)); + + canvas.present(); + } + } + + #[derive(Debug)] + pub struct BoxArea { + position: BoxAreaPosition, + pub content: BoxAreaContent, + last_update: i64, + } + + impl BoxArea { + fn new(position: BoxAreaPosition, content: BoxAreaContent) -> BoxArea { + return BoxArea { + position, + content, + last_update: chrono::Utc::now().timestamp(), + }; + } + + pub fn update_content(&mut self, content: BoxAreaContent) { + self.content = content; + self.last_update = chrono::Utc::now().timestamp(); + } + + fn bounding_rect(&self) -> Rect { + let x_offset = match self.position { + BoxAreaPosition::RightTop => 685, + BoxAreaPosition::RightBottom => 685, + BoxAreaPosition::LeftBottom => 5, + BoxAreaPosition::LeftTop => 5, + }; + let y_offset = match self.position { + BoxAreaPosition::RightTop => 50, + BoxAreaPosition::RightBottom => 480, + BoxAreaPosition::LeftBottom => 480, + BoxAreaPosition::LeftTop => 50, + }; + + Rect::new(x_offset, y_offset, 110, 110) + } + + fn enter_rect(&self) -> Rect { + match self.position { + BoxAreaPosition::RightTop => { + Rect::new(self.bounding_rect().x(), self.bounding_rect().y(), 25, 110) + } + BoxAreaPosition::RightBottom => { + Rect::new(self.bounding_rect().x(), self.bounding_rect().y(), 25, 110) + } + BoxAreaPosition::LeftBottom => Rect::new( + self.bounding_rect().x() + 85, + self.bounding_rect().y(), + 25, + 110, + ), + BoxAreaPosition::LeftTop => Rect::new( + self.bounding_rect().x() + 85, + self.bounding_rect().y(), + 25, + 110, + ), + } + } + + fn collides_with(&self, player: &Player) -> bool { + self.bounding_rect().contains_point(player.center()) + } + + fn render(&self, canvas: &mut WindowCanvas, texture: &Texture) { + let x_offset = self.bounding_rect().x(); + let y_offset = self.bounding_rect().y(); + + // Border + canvas.copy( + texture, + Rect::new(70, 510, 50, 25), + Rect::new(x_offset + 30, y_offset, 50, 25), + ); + canvas.copy( + texture, + Rect::new(70, 510, 50, 25), + Rect::new(x_offset + 30, y_offset + 85, 50, 25), + ); + let dst = match self.position { + BoxAreaPosition::RightTop => Rect::new(x_offset + 85, y_offset + 30, 25, 50), + BoxAreaPosition::RightBottom => Rect::new(x_offset + 85, y_offset + 30, 25, 50), + BoxAreaPosition::LeftBottom => Rect::new(x_offset, y_offset + 30, 25, 50), + BoxAreaPosition::LeftTop => Rect::new(x_offset, y_offset + 30, 25, 50), + }; + canvas.copy(texture, Rect::new(70, 550, 25, 50), dst); + + // Box + let box_src = match self.content { + BoxAreaContent::Nothing => Rect::new(70, 620, 50, 50), + BoxAreaContent::HiddenBox => Rect::new(5, 620, 50, 50), + BoxAreaContent::EmptyGlass => Rect::new(35, 510, 20, 25), + BoxAreaContent::FilledBottle => Rect::new(5, 550, 20, 50), + BoxAreaContent::EmptyBottle => Rect::new(35, 550, 20, 50), + }; + let (box_width, box_height) = match self.content { + BoxAreaContent::Nothing => (50, 50), + BoxAreaContent::HiddenBox => (50, 50), + BoxAreaContent::EmptyGlass => (20, 25), + BoxAreaContent::FilledBottle => (20, 50), + BoxAreaContent::EmptyBottle => (20, 50), + }; + canvas.copy( + texture, + box_src, + Rect::new( + x_offset + 30 + (50 - box_width) / 2, + y_offset + 30 + (50 - box_height) / 2, + box_width as u32, + box_height as u32, + ), + ); + } + } + + #[derive(Debug)] + pub enum BoxAreaPosition { + RightTop, + RightBottom, + LeftBottom, + LeftTop, + } + + #[derive(Debug, PartialEq, Eq)] + pub enum BoxAreaContent { + Nothing, + HiddenBox, + EmptyGlass, + FilledBottle, + EmptyBottle, + } + + impl BoxAreaContent { + pub fn random() -> BoxAreaContent { + match rand::random::() % 5 { + 1 | 4 => BoxAreaContent::EmptyGlass, + 2 | 3 => BoxAreaContent::FilledBottle, + _ => BoxAreaContent::Nothing, + } + } + } +}