~comcloudway/little_town

a92446d02464e5694afd08fa036b91590f0c1c02 — Jakob Meier 1 year, 8 months ago 8b872c4
Added inventory rendering
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)