M src/blocks.rs => src/blocks.rs +25 -8
@@ 1,17 1,34 @@
-use macroquad::prelude::*;
-use crate::textures::AssetStore;
-use crate::types::Direction;
+use crate::textures::{
+ AssetStore,
+ DirectionalTexture
+};
#[derive(Eq, Hash, Clone, PartialEq)]
pub enum Block {
- Dirt(Direction),
- GrassCenter(Direction)
+ Dirt,
+ GrassCenter
}
impl Block {
- pub fn get_texture(&self, assets: &AssetStore) -> Texture2D {
+ /// returns the texture for the block
+ /// with the direction the block is facing
+ pub fn get_texture<'a>(&self, assets: &'a AssetStore) -> &'a DirectionalTexture {
match self {
- Block::Dirt(d) => assets.blocks.dirt.get_dir(d),
- Block::GrassCenter(d) => assets.blocks.grass_center.get_dir(d)
+ Block::Dirt => &assets.blocks.dirt,
+ Block::GrassCenter => &assets.blocks.grass_center
+ }
+ }
+ /// returns a list of all blocks
+ pub fn all() -> Vec<Block> {
+ vec![
+ Block::Dirt,
+ Block::GrassCenter
+ ]
+ }
+
+ pub fn get_name(&self) -> &str {
+ match self {
+ Block::Dirt => "Dirt",
+ Block::GrassCenter => "Grass",
}
}
}
A src/draw.rs => src/draw.rs +41 -0
@@ 0,0 1,41 @@
+use macroquad::prelude::*;
+use crate::textures::AssetStore;
+
+/// draws centered text
+/// with the given font and font size
+pub fn draw_centered_text(text: &str, x: f32, y: f32, font: Option<Font>, font_size: u16, color: Color) {
+ let mut title_params = TextParams {
+ font_size,
+ color,
+ ..Default::default()
+ };
+ if let Some(font) = font {
+ title_params.font = font;
+ }
+ let title_center = get_text_center(
+ text,
+ font,
+ font_size,
+ 1.0,
+ 0.0);
+ draw_text_ex(
+ text,
+ x - title_center.x,
+ y - title_center.y,
+ title_params);
+}
+
+/// draws a wide button with text
+pub fn draw_wide_button(text: &str, pressed: bool, x: f32, y: f32, assets: &AssetStore) {
+ draw_texture(
+ if pressed {
+ assets.ui.long_button.1
+ } else { assets.ui.long_button.0 },
+ x - 190.0 / 2.0,
+ y,
+ WHITE);
+
+ draw_centered_text(text,
+ x, y + 49.0 / 2.0 - 2.0,
+ Some(assets.font), 60, LIGHTGRAY);
+}
M src/main.rs => src/main.rs +4 -0
@@ 1,9 1,13 @@
+#![feature(int_roundings)]
+#![feature(let_chains)]
+
use macroquad::prelude::*;
mod screens;
mod textures;
mod types;
mod blocks;
+mod draw;
use types::{
GameComponent,
M src/screens/build.rs => src/screens/build.rs +28 -22
@@ 4,7 4,8 @@ use crate::textures::AssetStore;
use crate::types::{
GameComponent,
GameEvent,
- Pos3
+ Pos3,
+ Direction
};
use crate::blocks::Block;
use super::inventory::Inventory;
@@ 27,20 28,20 @@ impl Camera {
}
/// image width
-const TEXTURE_WIDTH: f32 = 256.0;
+pub 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;
+pub const TEXTURE_HEIGHT: f32 = 352.0;
/// empty space above texture
-const TEXTURE_Y_WHITESPACE: f32 = 130.0;
+pub 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>,
+ grid: HashMap<Pos3, (Block, Direction)>,
cam: Camera,
mouse_down: bool,
show_inv: bool,
@@ 56,7 57,7 @@ impl BuildScreen {
inv: Inventory::new()
};
- this.grid.insert(Pos3::new(0, 0, 0), Block::Dirt(crate::types::Direction::North));
+ this.grid.insert(Pos3::new(0, 0, 0), (Block::Dirt, Direction::North));
this
}
@@ 155,15 156,16 @@ impl Face {
impl GameComponent for BuildScreen {
fn draw(&self, assets: &AssetStore) {
if self.show_inv {
- // TODO draw inventory
+ // draw inventory
+ self.inv.draw(&assets);
return;
}
// order: x (inc) -> y (inc) -> z (inc)
- let mut render_order: Vec<(&Pos3, &Block)> = self.grid.iter().collect();
+ let mut render_order: Vec<(&Pos3, &(Block, Direction))> = 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);
+ for (pos, (block, dir)) in render_order.iter() {
+ let texture = block.get_texture(&assets).get_dir(dir);
let width = TEXTURE_WIDTH * self.cam.scale;
let height = TEXTURE_HEIGHT * self.cam.scale;
@@ 189,6 191,20 @@ impl GameComponent for BuildScreen {
}
}
fn ev_loop(&mut self) -> GameEvent {
+
+ if is_key_down(KeyCode::I) {
+ self.show_inv = true;
+ }
+ if self.show_inv {
+ match self.inv.ev_loop() {
+ GameEvent::Quit => {
+ self.show_inv = false;
+ }
+ _ => return GameEvent::None
+ }
+ }
+
+
// mouse input
if is_mouse_button_down(MouseButton::Left) {
// currently holding button down
@@ 200,7 216,7 @@ impl GameComponent for BuildScreen {
let (mx, my) = mouse_position();
// virtual render cycle
- let render_order: Vec<(&Pos3, &Block)> = self.grid.iter().collect();
+ let render_order: Vec<(&Pos3, &(Block, Direction))> = self.grid.iter().collect();
// list of positions in render que for given pixel
// (mx, my)
@@ 248,7 264,7 @@ impl GameComponent for BuildScreen {
}
if let Some(block) = self.inv.place() {
- self.grid.insert(pos, block);
+ self.grid.insert(pos, (block, self.inv.direction.clone()));
}
}
@@ 280,16 296,6 @@ 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
}
}
M src/screens/inventory.rs => src/screens/inventory.rs +152 -5
@@ 1,10 1,17 @@
use macroquad::prelude::*;
use crate::types::{
GameComponent,
- GameEvent
+ GameEvent,
+ Direction
+};
+use super::build::{
+ TEXTURE_WIDTH,
+ TEXTURE_HEIGHT,
+ TEXTURE_Y_WHITESPACE
};
use crate::textures::AssetStore;
use crate::blocks::Block;
+use crate::draw::draw_centered_text;
use std::collections::HashMap;
pub struct Inventory {
@@ 16,14 23,33 @@ pub struct Inventory {
/// if infinite_items is true, the amount wont decrease
contents: HashMap<Block, usize>,
/// which block is currently selected
- pub selected: Option<Block>
+ pub selected: Option<Block>,
+ /// in which direction the block should be facing
+ pub direction: Direction,
+ /// current page
+ /// set to zero when closing inventory
+ /// the page count depends on the amount of items
+ /// and on how many slots to draw per page
+ /// see SLOTS_PER_PAGE for more
+ page: usize
}
impl Inventory {
pub fn new() -> Self {
+ let mut cont = HashMap::new();
+ let infinite_items = true;
+
+ if infinite_items {
+ for block in Block::all() {
+ cont.insert(block, 1);
+ }
+ }
+
Self {
- infinite_items: true,
- contents: HashMap::new(),
- selected: None
+ infinite_items,
+ contents: cont,
+ selected: Some(Block::Dirt),
+ direction: Direction::North,
+ page: 0
}
}
pub fn place(&mut self) -> Option<Block> {
@@ 46,10 72,131 @@ impl Inventory {
return None;
}
}
+
+/// how many slots to draw per inventory page
+const SLOTS_PER_PAGE: usize = 9;
+
+
impl GameComponent for Inventory {
fn draw(&self, assets: &AssetStore) {
+ let mid_x = screen_width()/2.0;
+ let mid_y = screen_height()/2.0;
+
+ let smallest = if screen_width() < screen_height() {
+ screen_width()
+ } else {
+ screen_height()
+ };
+ // use 70% of screen width for slots
+ let width = smallest * 0.7;
+ let hslot_count = ((SLOTS_PER_PAGE as f32).sqrt()) as usize;
+ let slot_dim = width / hslot_count as f32;
+ // only use 90% of slot width for the actual item
+ // ensures enough space around item
+ let slot_render_dim = slot_dim * 0.9;
+
+ let mut list: Vec<(&Block, &usize)> = self.contents.iter().collect();
+ list.sort_by(|a,b| a.0.get_name().cmp(b.0.get_name()));
+
+ let ox = mid_x - (hslot_count as f32 /2.0) * slot_dim;
+ let oy = mid_y - (hslot_count as f32 /2.0) * slot_dim;
+ for x in 0..hslot_count {
+ for y in 0..hslot_count {
+ let tx = ox + slot_dim * x as f32 + (slot_dim - slot_render_dim) / 2.0;
+ let ty = oy + slot_dim * y as f32 + (slot_dim - slot_render_dim) / 2.0;
+
+ let mut bg = assets.ui.panel_brown.0;
+ if let Some(block) = list.get(self.page * SLOTS_PER_PAGE + x + y*hslot_count)
+ && let Some(sb) = &self.selected && sb == block.0 {
+ // selected block
+ // draw blue background
+ bg = assets.ui.panel_blue.0;
+ }
+ // draw background
+ draw_texture_ex(
+ bg,
+ tx,
+ ty,
+ WHITE,
+ DrawTextureParams {
+ dest_size: Some(Vec2::new(slot_render_dim, slot_render_dim)),
+ ..Default::default()
+ }
+ );
+
+ // draw brown foreground
+ draw_texture_ex(
+ assets.ui.panel_brown.1,
+ tx + 4.0 + 2.0/2.0,
+ ty + 4.0 + 2.0/2.0,
+ WHITE,
+ DrawTextureParams {
+ dest_size: Some(Vec2::new(slot_render_dim - 4.0 * 2.0 - 4.0, slot_render_dim - 4.0 * 2.0 - 4.0)),
+ ..Default::default()
+ }
+ );
+
+ if let Some((block, amount)) = list.get(self.page * SLOTS_PER_PAGE + x + y * hslot_count) {
+ // draw block
+ draw_texture_ex(
+ block.get_texture(&assets).get_dir(&self.direction),
+ tx + slot_render_dim / 6.0,
+ ty + slot_dim / 2.0 - TEXTURE_HEIGHT / 2.0 + TEXTURE_Y_WHITESPACE / 2.0,
+ WHITE,
+ DrawTextureParams {
+ dest_size: Some(Vec2::new(
+ slot_render_dim * 2.0/3.0,
+ slot_render_dim * 2.0/3.0 * TEXTURE_HEIGHT / TEXTURE_WIDTH)),
+ ..Default::default()
+ }
+ );
+
+ // draw name
+ draw_centered_text(
+ block.get_name(),
+ tx + slot_render_dim / 2.0,
+ ty + 4.0 * 7.0,
+ Some(assets.font),
+ 40,
+ WHITE
+ );
+
+ // draw amount
+ let text = if self.infinite_items { "inf.".to_string() } else { amount.to_string() };
+ draw_centered_text(
+ &text,
+ tx + slot_render_dim / 6.0 + slot_render_dim * 2.0/3.0,
+ ty + slot_dim / 2.0
+ - TEXTURE_HEIGHT / 2.0 + TEXTURE_Y_WHITESPACE / 2.0
+ + slot_render_dim * 2.0/3.0 * TEXTURE_HEIGHT / TEXTURE_WIDTH
+ - 4.0,
+ Some(assets.font),
+ 30,
+ WHITE
+ );
+ }
+ }
+ }
+
+ let page_count = self.contents.len().div_ceil(SLOTS_PER_PAGE);
+
+ draw_centered_text(
+ &format!("{}/{}", self.page+1, page_count),
+ mid_x,
+ mid_y + hslot_count as f32 /2.0 * slot_dim + slot_dim/3.0,
+ Some(assets.font),
+ 60,
+ WHITE
+ );
}
fn ev_loop(&mut self) -> GameEvent {
+
+ // close inventory
+ if is_key_down(KeyCode::Q) {
+ self.page = 0;
+ return GameEvent::Quit;
+ }
+
GameEvent::None
}
}
M src/screens/welcome.rs => src/screens/welcome.rs +12 -35
@@ 4,47 4,24 @@ use crate::types::{
GameEvent
};
use crate::textures::AssetStore;
+use crate::draw::{
+ draw_wide_button,
+ draw_centered_text
+};
-fn draw_centered_text(text: &str, x: f32, y: f32, font: Option<Font>, font_size: u16, color: Color) {
- let mut title_params = TextParams {
- font_size,
- color,
- ..Default::default()
- };
- if let Some(font) = font {
- title_params.font = font;
- }
- let title_center = get_text_center(
- text,
- font,
- font_size,
- 1.0,
- 0.0);
- draw_text_ex(
- text,
- x - title_center.x,
- y - title_center.y,
- title_params);
-}
-fn draw_wide_button(text: &str, pressed: bool, x: f32, y: f32, assets: &AssetStore) {
- draw_texture(
- if pressed {
- assets.long_button.1
- } else { assets.long_button.0 },
- x - 190.0 / 2.0,
- y,
- WHITE);
-
- draw_centered_text(text,
- x, y + 49.0 / 2.0 - 2.0,
- Some(assets.font), 60, LIGHTGRAY);
-}
-
+/// The welcome screen
pub struct WelcomeScreen {
+ /// true when mouse is above the build button
+ /// and the left button is held
+ /// used for press-on-release-mechanism
button_build_down: bool,
+ /// true when mouse is above the exit button
+ /// and the left button is held
+ /// used for press-on-release-mechanism
button_exit_down: bool
}
impl WelcomeScreen {
+ /// create a new WelcomeScreen instance
pub fn new() -> Self {
Self {
button_build_down: false,
M src/textures.rs => src/textures.rs +44 -11
@@ 27,9 27,17 @@ fn in_font_folder(path: &str) -> String {
/// Collection of textures required by game
pub struct AssetStore {
pub font: Font,
- pub long_button: (Texture2D, Texture2D),
+ pub ui: UIAssetCollection,
pub blocks: BlockAssetCollection
}
+pub struct UIAssetCollection {
+ pub long_button: (Texture2D, Texture2D),
+ pub panel_blue: (Texture2D, Texture2D),
+ pub panel_brown: (Texture2D, Texture2D),
+ pub arrow_left: Texture2D,
+ pub arrow_right: Texture2D,
+ pub close_button: Texture2D
+}
pub struct DirectionalTexture {
pub north: Texture2D,
pub south: Texture2D,
@@ 41,16 49,16 @@ impl DirectionalTexture {
Self {
north: load_texture(
&in_base_folder(&format!("{}_N.png", name)))
- .await.expect("Unable to load texture N"),
+ .await.expect("Unable to load texture N"),
south: load_texture(
&in_base_folder(&format!("{}_S.png", name)))
- .await.expect("Unable to load texture S"),
+ .await.expect("Unable to load texture S"),
west: load_texture(
&in_base_folder(&format!("{}_W.png", name)))
- .await.expect("Unable to load texture W"),
+ .await.expect("Unable to load texture W"),
east: load_texture(
&in_base_folder(&format!("{}_E.png", name)))
- .await.expect("Unable to load texture E"),
+ .await.expect("Unable to load texture E"),
}
}
pub fn get_dir(&self, dir: &Direction) -> Texture2D {
@@ 73,13 81,38 @@ impl AssetStore {
Self {
font: load_ttf_font(&in_font_folder("Fonts/Kenney Pixel.ttf"))
.await.expect("Unable to load font"),
- long_button: (
- load_texture(
- &in_ui_folder("PNG/buttonLong_brown.png"))
+ ui: UIAssetCollection {
+ long_button: (
+ load_texture(
+ &in_ui_folder("PNG/buttonLong_brown.png"))
+ .await.expect("Unable to load texture"),
+ load_texture(
+ &in_ui_folder("PNG/buttonLong_brown_pressed.png"))
+ .await.expect("Unable to load texture")),
+ panel_blue: (
+ load_texture(
+ &in_ui_folder("PNG/panel_blue.png"))
+ .await.expect("Unable to load texture"),
+ load_texture(
+ &in_ui_folder("PNG/panelInset_blue.png"))
+ .await.expect("Unable to load texture")),
+ panel_brown: (
+ load_texture(
+ &in_ui_folder("PNG/panel_brown.png"))
+ .await.expect("Unable to load texture"),
+ load_texture(
+ &in_ui_folder("PNG/panelInset_brown.png"))
+ .await.expect("Unable to load texture")),
+ arrow_left: load_texture(
+ &in_ui_folder("PNG/arrowBrown_left.png"))
+ .await.expect("Unable to load texture"),
+ arrow_right: load_texture(
+ &in_ui_folder("PNG/arrowBrown_right.png"))
.await.expect("Unable to load texture"),
- load_texture(
- &in_ui_folder("PNG/buttonLong_brown_pressed.png"))
- .await.expect("Unable to load texture")),
+ close_button: load_texture(
+ &in_ui_folder("PNG/iconCross_brown.png"))
+ .await.expect("Unable to load texture")
+ },
blocks: BlockAssetCollection {
dirt: DirectionalTexture::from_auto_png("Tiles/dirt_center").await,
grass_center: DirectionalTexture::from_auto_png("Tiles/grass_center").await
M src/types.rs => src/types.rs +1 -1
@@ 25,7 25,7 @@ impl Pos3 {
}
/// Directions
-#[derive(Eq, Hash, Clone, PartialEq)]
+#[derive(Clone)]
pub enum Direction {
/// Left
/// (left edge of screen)