M src/blocks.rs => src/blocks.rs +1 -0
@@ 2,6 2,7 @@ use macroquad::prelude::*;
use crate::textures::AssetStore;
use crate::types::Direction;
+#[derive(Eq, Hash, Clone, PartialEq)]
pub enum Block {
Dirt(Direction),
GrassCenter(Direction)
M src/main.rs => src/main.rs +2 -21
@@ 10,34 10,15 @@ use types::{
GameEvent
};
use textures::AssetStore;
-use screens::welcome::WelcomeScreen;
-use screens::build::BuildScreen;
+use screens::Screen;
-enum Screen {
- Welcome(WelcomeScreen),
- Build(BuildScreen)
-}
-impl GameComponent for Screen {
- fn draw(&self, assets: &AssetStore) {
- match self {
- Screen::Welcome(w) => w.draw(&assets),
- Screen::Build(b) => b.draw(&assets)
- }
- }
- fn ev_loop(&mut self) -> GameEvent {
- match self {
- Screen::Welcome(w) => w.ev_loop(),
- Screen::Build(b) => b.ev_loop()
- }
- }
-}
#[macroquad::main("Little Town")]
async fn main() {
let assets = AssetStore::init().await;
- let mut screen = Screen::Welcome(WelcomeScreen::new());
+ let mut screen = Screen::default();
loop {
clear_background(Color::from_rgba(215, 189, 165, 255));
D src/screens/# => src/screens/# +0 -238
@@ 1,238 0,0 @@
-use macroquad::prelude::*;
-use std::collections::HashMap;
-use crate::textures::AssetStore;
-use crate::types::{
- GameComponent,
- GameEvent,
- Pos3
-};
-use crate::blocks::Block;
-
-struct Camera {
- center: Vec2,
- scale: f32
-}
-impl Camera {
- fn new() -> Self {
- Self {
- center: Vec2::new(0.0, 0.0),
- scale: 1.0
- }
- }
-}
-
-/// image width
-const TEXTURE_WIDTH: f32 = 256.0;
-/// actual width inside ob cube
-const TEXTURE_INNER_WIDTH: f32 = 223.0; // 216
-/// height of image
-const TEXTURE_HEIGHT: f32 = 352.0;
-/// empty space above texture
-const TEXTURE_Y_WHITESPACE: f32 = 130.0;
-/// actual height of cube
-const TEXTURE_INNER_HEIGHT: f32 = 108.0; // 110
-/// height of top surface
-const TEXTURE_DEPTH: f32 = 100.0;
-
-pub struct BuildScreen {
- grid: HashMap<Pos3, Block>,
- cam: Camera,
- mouse_down: bool
-}
-impl BuildScreen {
- pub fn new() -> Self {
- let mut this = Self {
- grid: HashMap::new(),
- cam: Camera::new(),
- mouse_down: false
- };
-
- this.grid.insert(Pos3::new(0, 0, 0), Block::Dirt(crate::types::Direction::North));
- this.grid.insert(Pos3::new(1, 0, 0), Block::Dirt(crate::types::Direction::North));
- this.grid.insert(Pos3::new(2, 0, 0), Block::Dirt(crate::types::Direction::North));
- this.grid.insert(Pos3::new(2, 1, 0), Block::Dirt(crate::types::Direction::North));
- this.grid.insert(Pos3::new(2, 1, 1), Block::Dirt(crate::types::Direction::North));
-
- this
- }
-}
-
-fn get_screen_coords(pos: &Pos3, scale: f32, center: Vec2) -> (f32, f32) {
- let w_i = TEXTURE_INNER_WIDTH * scale;
- let width = TEXTURE_WIDTH * scale;
- let height = TEXTURE_HEIGHT * scale;
- let h_i = TEXTURE_INNER_HEIGHT * scale;
-
- let dx = pos.y - pos.x;
- let dy = pos.y + pos.x;
-
- let x = screen_width() / 2.0
- + dx as f32 * w_i / 2.0
- + center.x
- - width / 2.0;
- let y = screen_height() / 2.0
- + dy as f32 * h_i / 2.0
- - pos.z as f32 * h_i
- + center.y
- - height / 2.0;
-
- return (x,y);
-}
-
-/// A block's face
-/// /'\
-/// / A \
-/// |\ /|
-/// | \./ |
-/// \ B|C |
-/// \.|./
-/// A - Top
-/// B- Left
-/// C -Right
-enum Face {
- Top,
- Left,
- Right
-}
-impl Face {
- /// gets the face (if any) that was clicked
- /// coordinates have to be normalized,
- /// the top left should be (0,0)
- fn from_xy(x: f32, y: f32) -> Option<Self> {
-
- // test B face
- {
- let ox = (TEXTURE_WIDTH / 2.0 - x).abs();
- let oy = (TEXTURE_Y_WHITESPACE + TEXTURE_DEPTH / 2.0 - y).abs();
-
- let y_max = TEXTURE_DEPTH / 2.0;
- let x_max = TEXTURE_WIDTH / 2.0;
-
- let grow = (0.0 - y_max)/(x_max - 0.0);
- let cy = grow * ox + y_max;
-
- if cy <= oy {
- return Some(Face::Top);
- }
-
- }
-
- None
- }
-}
-
-impl GameComponent for BuildScreen {
- fn draw(&self, assets: &AssetStore) {
- // order: x (inc) -> y (inc) -> z (inc)
- let mut render_order: Vec<(&Pos3, &Block)> = self.grid.iter().collect();
- render_order.sort_by_key(|(pos, _)| pos.x + pos.y*2 + pos.z*3 );
-
- for (pos, block) in render_order.iter() {
- let texture = block.get_texture(&assets);
- let width = TEXTURE_WIDTH * self.cam.scale;
- let height = TEXTURE_HEIGHT * self.cam.scale;
-
- let (x,y) = get_screen_coords(pos, self.cam.scale, self.cam.center);
-
- if x >= -width && x <= screen_width()
- && y >= -width && y <= screen_height() {
- // render block
- draw_texture_ex(
- texture,
- x,
- y,
- WHITE,
- DrawTextureParams {
- dest_size: Some(Vec2::new(width, height)),
- source: None,
- rotation: 0.0,
- flip_x: false,
- flip_y: false,
- pivot: Some(Vec2::new(width / 2.0, height / 2.0))
- });
- }
- }
- }
- fn ev_loop(&mut self) -> GameEvent {
- // mouse input
- if is_mouse_button_down(MouseButton::Left) {
- // currently holding button down
- self.mouse_down = true;
- } else if self.mouse_down {
- // button was released
- self.mouse_down = false;
- // determine which block / side was clicked
- let (mx, my) = mouse_position();
-
- // virtual render cycle
- let render_order: Vec<(&Pos3, &Block)> = self.grid.iter().collect();
- // no need to sort the first time
- //render_order.sort_by_key(|(pos, _)| pos.x + pos.y*2 + pos.z*3 );
-
- // list of positions in render que for given pixel
- // (mx, my)
- let mut in_path:Vec<&Pos3> = Vec::new();
-
- for (pos, _) in render_order.iter() {
- let (x,y) = get_screen_coords(pos, self.cam.scale, self.cam.center);
-
- if mx >= x
- && mx <= x + TEXTURE_WIDTH
- && my >= y + TEXTURE_Y_WHITESPACE
- && my <= y + TEXTURE_HEIGHT {
-
- // check if mouse is above transparent area
- // and skip block if necessary
- if Face::from_xy(mx-x, my-y).is_none() {
- println!("skipping");
- continue;
- }
-
- // block in mouse path
- in_path.push(pos);
- }
- }
- in_path.sort_by_key(|pos| pos.x + pos.y*2 + pos.z*3 );
- if let Some(pos) = in_path.last() {
- // position of clicked block
- // because it is the last block in de render queue
- // for a given pixel
- let pos = Pos3::new(pos.x, pos.y, pos.z);
- let (x,y) = get_screen_coords(&pos, self.cam.scale, self.cam.center);
- let face = Face::from_xy(mx-x, my-y);
- println!("{:?}", face);
-
- // TODO: determine side
- self.grid.insert(pos, Block::GrassCenter(crate::types::Direction::West));
- }
- }
-
- // zoom with Ctrl-MouseWheel
- if is_key_down(KeyCode::LeftControl) {
- let mut scale = self.cam.scale;
- let (_, wy) = mouse_wheel();
-
- scale += wy as f32 * 1.0/10.0;
-
- if scale > 0.05 && scale < 6.0 {
- self.cam.scale = scale;
- }
- }
-
- // keyboard control
- if is_key_down(KeyCode::Down) {
- self.cam.center.y += 1.0 * (1.0/self.cam.scale);
- }
- if is_key_down(KeyCode::Up) {
- self.cam.center.y -= 1.0 * (1.0/self.cam.scale);
- }
- if is_key_down(KeyCode::Left) {
- self.cam.center.x -= 1.0 * (1.0/self.cam.scale);
- }
- if is_key_down(KeyCode::Right) {
- self.cam.center.x += 1.0 * (1.0/self.cam.scale);
- }
-
- GameEvent::None
- }
-}
M src/screens/build.rs => src/screens/build.rs +29 -3
@@ 7,12 7,17 @@ use crate::types::{
Pos3
};
use crate::blocks::Block;
+use super::inventory::Inventory;
+/// the game camera
struct Camera {
+ /// the centered pixel
center: Vec2,
+ /// scales the texture
scale: f32
}
impl Camera {
+ /// initialize new camera
fn new() -> Self {
Self {
center: Vec2::new(0.0, 0.0),
@@ 37,14 42,18 @@ const TEXTURE_DEPTH: f32 = 100.0;
pub struct BuildScreen {
grid: HashMap<Pos3, Block>,
cam: Camera,
- mouse_down: bool
+ mouse_down: bool,
+ show_inv: bool,
+ inv: Inventory
}
impl BuildScreen {
pub fn new() -> Self {
let mut this = Self {
grid: HashMap::new(),
cam: Camera::new(),
- mouse_down: false
+ mouse_down: false,
+ show_inv: false,
+ inv: Inventory::new()
};
this.grid.insert(Pos3::new(0, 0, 0), Block::Dirt(crate::types::Direction::North));
@@ 145,6 154,10 @@ impl Face {
impl GameComponent for BuildScreen {
fn draw(&self, assets: &AssetStore) {
+ if self.show_inv {
+ // TODO draw inventory
+ return;
+ }
// order: x (inc) -> y (inc) -> z (inc)
let mut render_order: Vec<(&Pos3, &Block)> = self.grid.iter().collect();
render_order.sort_by_key(|(pos, _)| pos.x + pos.y*2 + pos.z*3 );
@@ 234,7 247,10 @@ impl GameComponent for BuildScreen {
}
}
- self.grid.insert(pos, Block::GrassCenter(crate::types::Direction::West));
+ if let Some(block) = self.inv.place() {
+ self.grid.insert(pos, block);
+ }
+
}
}
@@ 264,6 280,16 @@ impl GameComponent for BuildScreen {
self.cam.center.x += 1.0 * (1.0/self.cam.scale);
}
+ if is_key_down(KeyCode::I) {
+ self.show_inv = true;
+ match self.inv.ev_loop() {
+ GameEvent::Quit => {
+ self.show_inv = false;
+ }
+ _ => ()
+ }
+ }
+
GameEvent::None
}
}
A src/screens/inventory.rs => src/screens/inventory.rs +55 -0
@@ 0,0 1,55 @@
+use macroquad::prelude::*;
+use crate::types::{
+ GameComponent,
+ GameEvent
+};
+use crate::textures::AssetStore;
+use crate::blocks::Block;
+use std::collections::HashMap;
+
+pub struct Inventory {
+ /// when set to true, the player has an infinite amount of items
+ infinite_items: bool,
+ /// items in inventory
+ /// with their appropriate amount
+ /// if amount is zero, the item should be removed
+ /// if infinite_items is true, the amount wont decrease
+ contents: HashMap<Block, usize>,
+ /// which block is currently selected
+ pub selected: Option<Block>
+}
+impl Inventory {
+ pub fn new() -> Self {
+ Self {
+ infinite_items: true,
+ contents: HashMap::new(),
+ selected: None
+ }
+ }
+ pub fn place(&mut self) -> Option<Block> {
+ if let Some(block) = &self.selected {
+
+ if !self.infinite_items {
+ let cv: &usize = self.contents.get(&block).unwrap_or(&0);
+
+ if cv > &0 {
+ *self.contents.get_mut(&block).unwrap_or(&mut 0) -= 1;
+ } else {
+ self.contents.remove(&block);
+ return None;
+ }
+ }
+
+ return Some(block.clone());
+ }
+
+ return None;
+ }
+}
+impl GameComponent for Inventory {
+ fn draw(&self, assets: &AssetStore) {
+ }
+ fn ev_loop(&mut self) -> GameEvent {
+ GameEvent::None
+ }
+}
M src/screens/mod.rs => src/screens/mod.rs +36 -2
@@ 1,2 1,36 @@
-pub mod welcome;
-pub mod build;
+mod welcome;
+mod build;
+mod inventory;
+
+use welcome::WelcomeScreen;
+use build::BuildScreen;
+
+use crate::types::{
+ GameComponent,
+ GameEvent
+};
+use crate::textures::AssetStore;
+
+pub enum Screen {
+ Welcome(WelcomeScreen),
+ Build(BuildScreen),
+}
+impl GameComponent for Screen {
+ fn draw(&self, assets: &AssetStore) {
+ match self {
+ Screen::Welcome(w) => w.draw(&assets),
+ Screen::Build(b) => b.draw(&assets),
+ }
+ }
+ fn ev_loop(&mut self) -> GameEvent {
+ match self {
+ Screen::Welcome(w) => w.ev_loop(),
+ Screen::Build(b) => b.ev_loop(),
+ }
+ }
+}
+impl Default for Screen {
+ fn default() -> Self {
+ Self::Welcome(WelcomeScreen::new())
+ }
+}
M src/types.rs => src/types.rs +1 -1
@@ 25,7 25,7 @@ impl Pos3 {
}
/// Directions
-#[derive(PartialEq)]
+#[derive(Eq, Hash, Clone, PartialEq)]
pub enum Direction {
/// Left
/// (left edge of screen)