~gloatingfiddle/Atonement

bac81008289499f9f8b7567ea953ee049a700899 — David Hagerty 1 year, 3 months ago 1186890 master
Fix input system and rendering bug
M Cargo.toml => Cargo.toml +1 -1
@@ 1,7 1,7 @@
[package]
name = "atonement-game"
version = "0.1.0"
authors = ["david"]
authors = ["David Hagerty <david@dathagerty.com>"]
edition = "2018"
readme = "README.md"
license = "GPLv3"

M src/components/input.rs => src/components/input.rs +0 -26
@@ 16,35 16,9 @@
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

use std::collections::HashMap;

use rltk::VirtualKeyCode;
use specs::prelude::*;
use specs_derive::Component;

use crate::components::position::{Direction, WantsToMove};

#[derive(PartialEq, Copy, Clone)]
pub enum Command {
    Move(WantsToMove)
}

#[derive(Default)]
pub struct Keymap(pub HashMap<VirtualKeyCode, Command>);

impl Keymap {
    pub fn load(&mut self) {
        self.0.insert(VirtualKeyCode::H, Command::Move(WantsToMove{direction: Direction::Left}));
        self.0.insert(VirtualKeyCode::J, Command::Move(WantsToMove{direction: Direction::Down}));
        self.0.insert(VirtualKeyCode::K, Command::Move(WantsToMove{direction: Direction::Up}));
        self.0.insert(VirtualKeyCode::L, Command::Move(WantsToMove{direction: Direction::Right}));
        self.0.insert(VirtualKeyCode::U, Command::Move(WantsToMove{direction: Direction::UpLeft}));
        self.0.insert(VirtualKeyCode::I, Command::Move(WantsToMove{direction: Direction::UpRight}));
        self.0.insert(VirtualKeyCode::N, Command::Move(WantsToMove{direction: Direction::DownLeft}));
        self.0.insert(VirtualKeyCode::M, Command::Move(WantsToMove{direction: Direction::DownRight}));
    }
}

#[derive(Component, Default)]
#[storage(NullStorage)]
pub struct Controllable {}

M src/components/position.rs => src/components/position.rs +1 -1
@@ 40,7 40,7 @@ pub struct Position {
    pub y: i32,
}

#[derive(Component, PartialEq, Copy, Clone)]
#[derive(Component, PartialEq, Copy, Clone, Debug)]
pub struct WantsToMove {
    pub direction: Direction
}

M src/components/renderable.rs => src/components/renderable.rs +1 -0
@@ 22,6 22,7 @@ use specs_derive::Component;

#[derive(Component)]
pub struct Renderable {
    pub character: char,
    pub glyph: u16,
    pub foreground_color: RGB,
    pub background_color: RGB

M src/entities/mod.rs => src/entities/mod.rs +3 -0
@@ 28,6 28,7 @@ pub fn new_player(ecs: &mut World, x: i32, y: i32) {
    ecs.create_entity()
        .with(Position { x, y })
        .with(Renderable {
            character: '@',
            glyph: rltk::to_cp437('@'),
            foreground_color: RGB::named(rltk::WHITE),
            background_color: RGB::named(rltk::BLACK),


@@ 46,6 47,7 @@ pub fn new_friendly(ecs: &mut World, x: i32, y: i32) {
    ecs.create_entity()
        .with(Position { x, y })
        .with(Renderable {
            character: 'B',
            glyph: rltk::to_cp437('B'),
            foreground_color: RGB::named(rltk::AZURE3),
            background_color: RGB::named(rltk::BLACK),


@@ 61,6 63,7 @@ pub fn new_enemy(ecs: &mut World, x: i32, y: i32) {
    ecs.create_entity()
        .with(Position { x, y })
        .with(Renderable {
            character: 'e',
            glyph: rltk::to_cp437('e'),
            foreground_color: RGB::named(rltk::RED),
            background_color: RGB::named(rltk::BLACK),

A src/input.rs => src/input.rs +58 -0
@@ 0,0 1,58 @@
use rltk::{Rltk, VirtualKeyCode};
use crate::{State, RunState};
use crate::components::position::{WantsToMove, Direction};
use std::collections::HashMap;
use specs::{WorldExt, Entity, Join};
use crate::components::input::Controllable;

#[derive(PartialEq, Copy, Clone, Debug)]
pub enum Command {
    Move(WantsToMove)
}

#[derive(Default)]
pub struct Keymap(pub HashMap<VirtualKeyCode, Command>);

impl Keymap {
    pub fn load(&mut self) {
        self.0.insert(VirtualKeyCode::H, Command::Move(WantsToMove{direction: Direction::Left}));
        self.0.insert(VirtualKeyCode::J, Command::Move(WantsToMove{direction: Direction::Down}));
        self.0.insert(VirtualKeyCode::K, Command::Move(WantsToMove{direction: Direction::Up}));
        self.0.insert(VirtualKeyCode::L, Command::Move(WantsToMove{direction: Direction::Right}));
        self.0.insert(VirtualKeyCode::U, Command::Move(WantsToMove{direction: Direction::UpLeft}));
        self.0.insert(VirtualKeyCode::I, Command::Move(WantsToMove{direction: Direction::UpRight}));
        self.0.insert(VirtualKeyCode::N, Command::Move(WantsToMove{direction: Direction::DownLeft}));
        self.0.insert(VirtualKeyCode::M, Command::Move(WantsToMove{direction: Direction::DownRight}));
    }
}

pub fn get_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
    let km = gs.ecs.fetch::<Keymap>();
    let controllables = gs.ecs.read_storage::<Controllable>();
    let entities = gs.ecs.entities();
    let mut wants_to_move = gs.ecs.write_storage::<WantsToMove>();
    let mut commanded = false;
    for (entity, _controllable) in (&entities, &controllables).join() {
       match ctx.key {
           None => {}
           Some(key) => {
               match km.0.get(&key) {
                   None => {}
                   Some(command) => {
                       println!("Got command: {:#?}", command);
                       match command {
                           Command::Move(movement) => {
                               commanded = true;
                               wants_to_move.insert(entity, *movement).expect("could not insert command")
                           }
                       };
                   }
               }
           }
       }
    }
    if commanded {
        return RunState::ProcessTurns;
    }
    return RunState::AwaitingInput;
}
\ No newline at end of file

M src/main.rs => src/main.rs +34 -41
@@ 19,23 19,25 @@
use std::borrow::BorrowMut;
use std::collections::HashMap;

use rltk::prelude::*;
use rltk::{BError, GameState, Rltk};
use rltk::prelude::*;
use specs::join::Join;
use specs::prelude::*;

use crate::components::ai::HasAttitude;
use crate::components::input::{Controllable, Keymap};
use crate::components::input::{Controllable};
use crate::components::position::{Direction, Moveable, Position, WantsToMove};
use crate::components::renderable::{Renderable, ViewShed};
use crate::entities::*;
use crate::map::*;
use crate::systems::visibility::VisibilitySystem;
use crate::components::ai::HasAttitude;
use crate::input::{Keymap, get_input};

mod components;
mod entities;
mod map;
mod systems;
mod map;
mod input;

const MAP_HEIGHT: i32 = 100;
const MAP_WIDTH: i32 = 160;


@@ 44,10 46,10 @@ const MAP_WIDTH: i32 = 160;
pub enum RunState {
    PreRun,
    AwaitingInput,
    PlayerTurn,
    ProcessTurns,
}

struct State {
pub struct State {
    ecs: World,
}



@@ 63,19 65,14 @@ impl GameState for State {

        match newrunstate {
            RunState::PreRun => {
                println!("pre-run");
                self.run_systems();
                newrunstate = RunState::AwaitingInput;
            }
            RunState::AwaitingInput => {
                println!("awaiting input");
                let mut controller = Controllable {};
                controller.run_now(&self.ecs);
                self.ecs.maintain();
                newrunstate = RunState::PlayerTurn;
                newrunstate = get_input(self, ctx)
            }
            RunState::PlayerTurn => {
                println!("player turn");
            RunState::ProcessTurns => {
                println!("RunState::ProcessTurns");
                self.run_systems();
                newrunstate = RunState::AwaitingInput;
            }


@@ 92,23 89,17 @@ impl GameState for State {
        let positions = self.ecs.read_storage::<Position>();
        let renderables = self.ecs.read_storage::<Renderable>();
        for (position, renderable) in (&positions, &renderables).join() {
            ctx.set(
                position.x,
                position.y,
                renderable.foreground_color,
                renderable.background_color,
                renderable.glyph,
            );
            if map.visible_tiles[map.xy_idx(position.x, position.y)] {
                ctx.set(position.x, position.y, renderable.foreground_color, renderable.background_color, renderable.glyph);
            }
        }
    }
}

impl State {
    fn run_systems(&mut self) {
        let mut mover = WantsToMove {
            direction: Direction::None,
        };
        let mut visibility = VisibilitySystem {};
        let mut mover = WantsToMove{ direction: Direction::None};
        let mut visibility = VisibilitySystem{};
        mover.run_now(&self.ecs);
        visibility.run_now(&self.ecs);
        self.ecs.maintain();


@@ 120,15 111,17 @@ fn main() -> BError {
        .unwrap()
        .with_title("Atonement")
        .build()?;
    let mut gameState = State { ecs: World::new() };

    gameState.ecs.register::<Position>();
    gameState.ecs.register::<Renderable>();
    gameState.ecs.register::<Controllable>();
    gameState.ecs.register::<WantsToMove>();
    gameState.ecs.register::<Moveable>();
    gameState.ecs.register::<ViewShed>();
    gameState.ecs.register::<HasAttitude>();
    let mut gs = State {
        ecs: World::new(),
    };

    gs.ecs.register::<Position>();
    gs.ecs.register::<Renderable>();
    gs.ecs.register::<Controllable>();
    gs.ecs.register::<WantsToMove>();
    gs.ecs.register::<Moveable>();
    gs.ecs.register::<ViewShed>();
    gs.ecs.register::<HasAttitude>();

    let mut keymap = Keymap(HashMap::new());
    keymap.load();


@@ 136,16 129,16 @@ fn main() -> BError {
    let map = Map::new_dungeon(MAP_WIDTH, MAP_HEIGHT);
    let (px, py) = map.rooms[0].center();

    new_player(gameState.ecs.borrow_mut(), px, py);
    new_friendly(gameState.ecs.borrow_mut(), px + 3, py + 3);
    new_player(gs.ecs.borrow_mut(), px, py);
    new_friendly(gs.ecs.borrow_mut(), px+3, py+3);

    for room in map.rooms[1..].iter() {
        new_enemy(gameState.ecs.borrow_mut(), room.center().0, room.center().1)
        new_enemy(gs.ecs.borrow_mut(), room.center().0, room.center().1)
    }

    gameState.ecs.insert(keymap);
    gameState.ecs.insert(map);
    gameState.ecs.insert(RunState::PreRun);
    gs.ecs.insert(keymap);
    gs.ecs.insert(map);
    gs.ecs.insert(RunState::PreRun);

    rltk::main_loop(context, gameState)
    rltk::main_loop(context, gs)
}

D src/systems/input.rs => src/systems/input.rs +0 -45
@@ 1,45 0,0 @@
/*
    atonement, a roguelike game set in the Wild West
    Copyright (C) 2020  David Hagerty (gloatingfiddle)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

use rltk::INPUT;
use specs::{Entities, Read, ReadStorage, System, WriteStorage};
use specs::join::Join;

use crate::components::input::{Controllable, Keymap, Command};
use crate::components::position::WantsToMove;

impl<'a> System<'a> for Controllable {
    type SystemData = (Entities<'a>,
                       ReadStorage<'a, Controllable>,
                       WriteStorage<'a, WantsToMove>,
                       Read<'a, Keymap>,
    );

    fn run(&mut self, (entities, controllables, mut wants_to_move, keymap) : Self::SystemData) {
        let input = INPUT.lock();
        for (entity, _controllable) in (&entities, &controllables).join() {
            keymap.0.iter().for_each(|(key, command)| {
                if input.is_key_pressed(*key) {
                    match *command {
                        Command::Move(movement) => wants_to_move.insert(entity, movement).expect("add failed!"),
                    };
                }
            });
        }
    }
}
\ No newline at end of file

M src/systems/mod.rs => src/systems/mod.rs +0 -1
@@ 17,5 17,4 @@
*/

pub mod movement;
pub mod input;
pub mod visibility;

M src/systems/visibility.rs => src/systems/visibility.rs +1 -0
@@ 53,6 53,7 @@ impl<'a> System<'a> for VisibilitySystem {
                        *t = false
                    }
                    for v in vs.visible_tiles.iter() {
                        println!("updating visible tiles");
                        let idx = map.xy_idx(v.x, v.y);
                        map.revealed_tiles[idx] = true;
                        map.visible_tiles[idx] = true;