mirror of
https://github.com/pcvolkmer/winelounge.git
synced 2025-04-19 10:26:50 +00:00
Initial commit
This commit is contained in:
commit
efd0b529d0
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
188
Cargo.lock
generated
Normal file
188
Cargo.lock
generated
Normal file
@ -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",
|
||||||
|
]
|
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
@ -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"]
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -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.
|
15
README.adoc
Normal file
15
README.adoc
Normal file
@ -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.
|
BIN
assets/font.ttf
Normal file
BIN
assets/font.ttf
Normal file
Binary file not shown.
BIN
assets/image.png
Normal file
BIN
assets/image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
assets/sprite.png
Normal file
BIN
assets/sprite.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.9 KiB |
2160
assets/sprite.svg
Normal file
2160
assets/sprite.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 72 KiB |
129
src/main.rs
Normal file
129
src/main.rs
Normal file
@ -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));
|
||||||
|
}
|
||||||
|
}
|
128
src/player.rs
Normal file
128
src/player.rs
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
391
src/world.rs
Normal file
391
src/world.rs
Normal file
@ -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<Point>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<BoxAreaPosition> {
|
||||||
|
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::<i64>() % 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::<i32>() % 5 {
|
||||||
|
1 | 4 => BoxAreaContent::EmptyGlass,
|
||||||
|
2 | 3 => BoxAreaContent::FilledBottle,
|
||||||
|
_ => BoxAreaContent::Nothing,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user