Allow player to switch between multiple inhabitable entities
This commit is contained in:
parent
ac3c399598
commit
fcd2b643af
|
@ -1 +1,2 @@
|
|||
/target
|
||||
savegame.json
|
||||
|
|
|
@ -247,17 +247,6 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chapter-41-camera"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"rltk",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"specs",
|
||||
"specs-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloudabi"
|
||||
version = "0.0.3"
|
||||
|
@ -806,6 +795,17 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "machine-roguelike"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"rltk",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"specs",
|
||||
"specs-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "malloc_buf"
|
||||
version = "0.0.6"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "chapter-41-camera"
|
||||
name = "machine-roguelike"
|
||||
version = "0.1.0"
|
||||
authors = ["Herbert Wolverson <herberticus@gmail.com>"]
|
||||
authors = ["Vivian Lim <vivlim@pm.me>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
|
|
@ -22,6 +22,9 @@ pub struct Renderable {
|
|||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Player {}
|
||||
|
||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Inhabitable {}
|
||||
|
||||
#[derive(Component, ConvertSaveload, Clone)]
|
||||
pub struct Viewshed {
|
||||
pub visible_tiles : Vec<rltk::Point>,
|
||||
|
|
|
@ -402,9 +402,9 @@ impl State {
|
|||
fn main() -> rltk::BError {
|
||||
use rltk::RltkBuilder;
|
||||
let mut context = RltkBuilder::simple80x50()
|
||||
.with_title("Roguelike Tutorial")
|
||||
.with_title("My Life as a Distributed Machine Consciousness")
|
||||
.build()?;
|
||||
context.with_post_scanlines(true);
|
||||
//context.with_post_scanlines(true);
|
||||
let mut gs = State {
|
||||
ecs: World::new(),
|
||||
mapgen_next_state : Some(RunState::MainMenu{ menu_selection: gui::MainMenuSelection::NewGame }),
|
||||
|
@ -450,6 +450,7 @@ fn main() -> rltk::BError {
|
|||
gs.ecs.register::<SingleActivation>();
|
||||
gs.ecs.register::<BlocksVisibility>();
|
||||
gs.ecs.register::<Door>();
|
||||
gs.ecs.register::<Inhabitable>();
|
||||
|
||||
gs.ecs.insert(SimpleMarkerAllocator::<SerializeMe>::new());
|
||||
|
||||
|
@ -459,7 +460,7 @@ fn main() -> rltk::BError {
|
|||
let player_entity = spawner::player(&mut gs.ecs, 0, 0);
|
||||
gs.ecs.insert(player_entity);
|
||||
gs.ecs.insert(RunState::MapGeneration{} );
|
||||
gs.ecs.insert(gamelog::GameLog{ entries : vec!["Welcome to Rusty Roguelike".to_string()] });
|
||||
gs.ecs.insert(gamelog::GameLog{ entries : vec!["Welcome to My Life as a Distributed Machine Consciousness".to_string()] });
|
||||
gs.ecs.insert(particle_system::ParticleBuilder::new());
|
||||
gs.ecs.insert(rex_assets::RexAssets::new());
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use rltk::{VirtualKeyCode, Rltk, Point};
|
||||
use rltk::{Point, Rltk, VirtualKeyCode};
|
||||
use specs::prelude::*;
|
||||
use std::cmp::{max, min};
|
||||
use crate::Inhabitable;
|
||||
|
||||
use super::{Position, Player, Viewshed, State, Map, RunState, CombatStats, WantsToMelee, Item,
|
||||
gamelog::GameLog, WantsToPickupItem, TileType, Monster, HungerClock, HungerState,
|
||||
EntityMoved, Door, BlocksTile, BlocksVisibility, Renderable};
|
||||
|
@ -90,6 +92,50 @@ fn get_item(ecs: &mut World) {
|
|||
}
|
||||
}
|
||||
|
||||
fn inhabit_another_body(ecs: &mut World) {
|
||||
let mut player_entity = ecs.write_resource::<Entity>();
|
||||
let entities = ecs.entities();
|
||||
let inhabitables = ecs.read_storage::<Inhabitable>();
|
||||
let mut players = ecs.write_storage::<Player>();
|
||||
|
||||
// pick next one
|
||||
let mut next_player: Option<Entity> = None;
|
||||
|
||||
for (inhabitable_entity, _inhabitable, _notplayer) in (&entities, &inhabitables, !&players).join(){
|
||||
match next_player {
|
||||
None => { // if not set, just take the first one.
|
||||
next_player = Some(inhabitable_entity);
|
||||
},
|
||||
Some(_) => {
|
||||
if inhabitable_entity.id() > player_entity.id(){
|
||||
next_player = Some(inhabitable_entity);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match next_player {
|
||||
None => (),
|
||||
Some(e) => {
|
||||
// remove Player component from old entity and add it to the new one
|
||||
players.remove(*player_entity);
|
||||
players.insert(e, Player {}).unwrap();
|
||||
|
||||
// change Player resource to the new one
|
||||
//let mut player_resource = ecs.write_resource::<Entity>();
|
||||
*player_entity = e;
|
||||
|
||||
// update player position resource to the new one
|
||||
let positions = ecs.read_storage::<Position>();
|
||||
let new_pos = positions.get(e).unwrap();
|
||||
let mut ppos = ecs.write_resource::<Point>();
|
||||
ppos.x = new_pos.x;
|
||||
ppos.y = new_pos.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn skip_turn(ecs: &mut World) -> RunState {
|
||||
let player_entity = ecs.fetch::<Entity>();
|
||||
let viewshed_components = ecs.read_storage::<Viewshed>();
|
||||
|
@ -174,6 +220,9 @@ pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState {
|
|||
}
|
||||
}
|
||||
|
||||
// Inhabit another body
|
||||
VirtualKeyCode::Tab => inhabit_another_body(&mut gs.ecs),
|
||||
|
||||
// Picking up items
|
||||
VirtualKeyCode::G => get_item(&mut gs.ecs),
|
||||
VirtualKeyCode::I => return RunState::ShowInventory,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use rltk::{ RGB, RandomNumberGenerator };
|
||||
use specs::prelude::*;
|
||||
use super::{CombatStats, Player, Renderable, Name, Position, Viewshed, Monster, BlocksTile, Rect, Item,
|
||||
use super::{CombatStats, Player, Renderable, Name, Position, Viewshed, Monster, BlocksTile, Inhabitable, Rect, Item,
|
||||
Consumable, Ranged, ProvidesHealing, InflictsDamage, AreaOfEffect, Confusion, SerializeMe,
|
||||
random_table::RandomTable, EquipmentSlot, Equippable, MeleePowerBonus, DefenseBonus, HungerClock,
|
||||
HungerState, ProvidesFood, MagicMapper, Hidden, EntryTrigger, SingleActivation, Map, TileType,
|
||||
|
@ -10,30 +10,35 @@ use std::collections::HashMap;
|
|||
|
||||
/// Spawns the player and returns his/her entity object.
|
||||
pub fn player(ecs : &mut World, player_x : i32, player_y : i32) -> Entity {
|
||||
base_walker(ecs, player_x, player_y)
|
||||
.with(Player{})
|
||||
.build()
|
||||
}
|
||||
|
||||
fn base_walker(ecs: &mut World, x: i32, y: i32) -> EntityBuilder {
|
||||
ecs
|
||||
.create_entity()
|
||||
.with(Position { x: player_x, y: player_y })
|
||||
.with(Position { x: x, y: y })
|
||||
.with(Renderable {
|
||||
glyph: rltk::to_cp437('@'),
|
||||
fg: RGB::named(rltk::YELLOW),
|
||||
bg: RGB::named(rltk::BLACK),
|
||||
render_order: 0
|
||||
})
|
||||
.with(Player{})
|
||||
.with(Inhabitable{})
|
||||
.with(Viewshed{ visible_tiles : Vec::new(), range: 8, dirty: true })
|
||||
.with(Name{name: "Player".to_string() })
|
||||
.with(Name{name: "Walker".to_string() })
|
||||
.with(CombatStats{ max_hp: 30, hp: 30, defense: 2, power: 5 })
|
||||
.with(HungerClock{ state: HungerState::WellFed, duration: 20 })
|
||||
.marked::<SimpleMarker<SerializeMe>>()
|
||||
.build()
|
||||
}
|
||||
|
||||
const MAX_MONSTERS : i32 = 4;
|
||||
|
||||
fn room_table(map_depth: i32) -> RandomTable {
|
||||
RandomTable::new()
|
||||
.add("Goblin", 10)
|
||||
.add("Orc", 1 + map_depth)
|
||||
.add("Walker", 10)
|
||||
.add("Battery", 1 + map_depth)
|
||||
.add("Health Potion", 7)
|
||||
.add("Fireball Scroll", 2 + map_depth)
|
||||
.add("Confusion Scroll", 2 + map_depth)
|
||||
|
@ -99,24 +104,34 @@ pub fn spawn_entity(ecs: &mut World, spawn : &(&usize, &String)) {
|
|||
std::mem::drop(map);
|
||||
|
||||
match spawn.1.as_ref() {
|
||||
"Goblin" => goblin(ecs, x, y),
|
||||
"Orc" => orc(ecs, x, y),
|
||||
"Health Potion" => health_potion(ecs, x, y),
|
||||
"Fireball Scroll" => fireball_scroll(ecs, x, y),
|
||||
"Confusion Scroll" => confusion_scroll(ecs, x, y),
|
||||
"Magic Missile Scroll" => magic_missile_scroll(ecs, x, y),
|
||||
"Dagger" => dagger(ecs, x, y),
|
||||
"Shield" => shield(ecs, x, y),
|
||||
"Longsword" => longsword(ecs, x, y),
|
||||
"Tower Shield" => tower_shield(ecs, x, y),
|
||||
"Walker" => walker(ecs, x, y, 32),
|
||||
"Battery" => battery(ecs, x, y, 32),
|
||||
"Rations" => rations(ecs, x, y),
|
||||
"Magic Mapping Scroll" => magic_mapping_scroll(ecs, x, y),
|
||||
"Bear Trap" => bear_trap(ecs, x, y),
|
||||
"Door" => door(ecs, x, y),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn walker(ecs: &mut World, x: i32, y: i32, starting_energy: i32) {
|
||||
base_walker(ecs, x, y)
|
||||
.build();
|
||||
}
|
||||
|
||||
fn battery(ecs: &mut World, x: i32, y: i32, starting_energy: i32) {
|
||||
ecs.create_entity()
|
||||
.with(Position{ x, y })
|
||||
.with(Renderable{
|
||||
glyph: rltk::to_cp437('B'),
|
||||
fg: RGB::named(rltk::MAGENTA),
|
||||
bg: RGB::named(rltk::BLACK),
|
||||
render_order: 2
|
||||
})
|
||||
.with(Name{ name : "Battery".to_string() })
|
||||
.with(BlocksTile{})
|
||||
.marked::<SimpleMarker<SerializeMe>>()
|
||||
.build();
|
||||
}
|
||||
|
||||
fn orc(ecs: &mut World, x: i32, y: i32) { monster(ecs, x, y, rltk::to_cp437('o'), "Orc"); }
|
||||
fn goblin(ecs: &mut World, x: i32, y: i32) { monster(ecs, x, y, rltk::to_cp437('g'), "Goblin"); }
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use specs::prelude::*;
|
||||
use super::{Viewshed, Position, Map, Player, Hidden, BlocksVisibility, gamelog::GameLog, Name};
|
||||
use super::{Viewshed, Position, Map, Player, Inhabitable, Hidden, BlocksVisibility, gamelog::GameLog, Name};
|
||||
use rltk::{field_of_view, Point};
|
||||
|
||||
pub struct VisibilitySystem {}
|
||||
|
@ -11,6 +11,7 @@ impl<'a> System<'a> for VisibilitySystem {
|
|||
WriteStorage<'a, Viewshed>,
|
||||
ReadStorage<'a, Position>,
|
||||
ReadStorage<'a, Player>,
|
||||
ReadStorage<'a, Inhabitable>,
|
||||
WriteStorage<'a, Hidden>,
|
||||
WriteExpect<'a, rltk::RandomNumberGenerator>,
|
||||
WriteExpect<'a, GameLog>,
|
||||
|
@ -18,7 +19,7 @@ impl<'a> System<'a> for VisibilitySystem {
|
|||
ReadStorage<'a, BlocksVisibility>);
|
||||
|
||||
fn run(&mut self, data : Self::SystemData) {
|
||||
let (mut map, entities, mut viewshed, pos, player,
|
||||
let (mut map, entities, mut viewshed, pos, player, inhabitable,
|
||||
mut hidden, mut rng, mut log, names, blocks_visibility) = data;
|
||||
|
||||
map.view_blocked.clear();
|
||||
|
@ -27,33 +28,45 @@ impl<'a> System<'a> for VisibilitySystem {
|
|||
map.view_blocked.insert(idx);
|
||||
}
|
||||
|
||||
let mut player_visible_tiles_changed = false;
|
||||
for (ent,viewshed,pos) in (&entities, &mut viewshed, &pos).join() {
|
||||
if viewshed.dirty {
|
||||
viewshed.dirty = false;
|
||||
viewshed.visible_tiles = field_of_view(Point::new(pos.x, pos.y), viewshed.range, &*map);
|
||||
viewshed.visible_tiles.retain(|p| p.x >= 0 && p.x < map.width && p.y >= 0 && p.y < map.height );
|
||||
|
||||
// If this is the player, reveal what they can see
|
||||
let _p : Option<&Player> = player.get(ent);
|
||||
if let Some(_p) = _p {
|
||||
for t in map.visible_tiles.iter_mut() { *t = false };
|
||||
for vis in viewshed.visible_tiles.iter() {
|
||||
if vis.x > 0 && vis.x < map.width-1 && vis.y > 0 && vis.y < map.height-1 {
|
||||
let idx = map.xy_idx(vis.x, vis.y);
|
||||
map.revealed_tiles[idx] = true;
|
||||
map.visible_tiles[idx] = true;
|
||||
// If this is an inhabitable viewshed that was dirtied, we need to recompute which tiles are visible to the player.
|
||||
if !player_visible_tiles_changed {
|
||||
let _p : Option<&Inhabitable> = inhabitable.get(ent);
|
||||
if let Some(_p) = _p {
|
||||
player_visible_tiles_changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Chance to reveal hidden things
|
||||
for e in map.tile_content[idx].iter() {
|
||||
let maybe_hidden = hidden.get(*e);
|
||||
if let Some(_maybe_hidden) = maybe_hidden {
|
||||
if rng.roll_dice(1,24)==1 {
|
||||
let name = names.get(*e);
|
||||
if let Some(name) = name {
|
||||
log.entries.push(format!("You spotted a {}.", &name.name));
|
||||
}
|
||||
hidden.remove(*e);
|
||||
if player_visible_tiles_changed {
|
||||
// Clear all map visible tiles
|
||||
for t in map.visible_tiles.iter_mut() { *t = false };
|
||||
|
||||
for (ent,viewshed,pos,_inhabitable) in (&entities, &mut viewshed, &pos, &inhabitable).join() {
|
||||
|
||||
for vis in viewshed.visible_tiles.iter() {
|
||||
if vis.x > 0 && vis.x < map.width-1 && vis.y > 0 && vis.y < map.height-1 {
|
||||
let idx = map.xy_idx(vis.x, vis.y);
|
||||
map.revealed_tiles[idx] = true;
|
||||
map.visible_tiles[idx] = true;
|
||||
|
||||
// Chance to reveal hidden things
|
||||
for e in map.tile_content[idx].iter() {
|
||||
let maybe_hidden = hidden.get(*e);
|
||||
if let Some(_maybe_hidden) = maybe_hidden {
|
||||
if rng.roll_dice(1,24)==1 {
|
||||
let name = names.get(*e);
|
||||
if let Some(name) = name {
|
||||
log.entries.push(format!("You spotted a {}.", &name.name));
|
||||
}
|
||||
hidden.remove(*e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue