decoupled camera from map, wands of digging, wand use tracking
This commit is contained in:
parent
7aa440612e
commit
2ecfd25d95
9 changed files with 430 additions and 92 deletions
279
src/camera.rs
Normal file
279
src/camera.rs
Normal file
|
|
@ -0,0 +1,279 @@
|
||||||
|
use super::{Door, Hidden, Map, Mind, Position, Renderable, TileType};
|
||||||
|
use rltk::{Point, Rltk, RGB};
|
||||||
|
use specs::prelude::*;
|
||||||
|
use std::ops::{Add, Mul};
|
||||||
|
|
||||||
|
const SHOW_BOUNDARIES: bool = true;
|
||||||
|
|
||||||
|
pub fn get_screen_bounds(ecs: &World, ctx: &mut Rltk) -> (i32, i32, i32, i32) {
|
||||||
|
let player_pos = ecs.fetch::<Point>();
|
||||||
|
let (x_chars, y_chars) = ctx.get_char_size();
|
||||||
|
|
||||||
|
let centre_x = (x_chars / 2) as i32;
|
||||||
|
let centre_y = (y_chars / 2) as i32;
|
||||||
|
|
||||||
|
let min_x = player_pos.x - centre_x;
|
||||||
|
let min_y = player_pos.y - centre_y;
|
||||||
|
let max_x = min_x + x_chars as i32;
|
||||||
|
let max_y = min_y + y_chars as i32;
|
||||||
|
|
||||||
|
(min_x, max_x, min_y, max_y)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_camera(ecs: &World, ctx: &mut Rltk) {
|
||||||
|
let map = ecs.fetch::<Map>();
|
||||||
|
let (min_x, max_x, min_y, max_y) = get_screen_bounds(ecs, ctx);
|
||||||
|
|
||||||
|
// Might need to -1 here?
|
||||||
|
let map_width = map.width;
|
||||||
|
let map_height = map.height;
|
||||||
|
|
||||||
|
// Render map
|
||||||
|
let mut y = 0;
|
||||||
|
for t_y in min_y..max_y {
|
||||||
|
let mut x = 0;
|
||||||
|
for t_x in min_x..max_x {
|
||||||
|
if t_x >= 0 && t_x < map.width && t_y >= 0 && t_y < map_height {
|
||||||
|
let idx = map.xy_idx(t_x, t_y);
|
||||||
|
if map.revealed_tiles[idx] {
|
||||||
|
let (glyph, fg, bg) = get_tile_glyph(idx, &*map);
|
||||||
|
ctx.set(x, y, fg, bg, glyph);
|
||||||
|
}
|
||||||
|
} else if SHOW_BOUNDARIES {
|
||||||
|
ctx.set(x, y, RGB::named(rltk::DARKSLATEGRAY), RGB::named(rltk::BLACK), rltk::to_cp437('#'));
|
||||||
|
}
|
||||||
|
x += 1;
|
||||||
|
}
|
||||||
|
y += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render entities
|
||||||
|
{
|
||||||
|
let positions = ecs.read_storage::<Position>();
|
||||||
|
let renderables = ecs.read_storage::<Renderable>();
|
||||||
|
let minds = ecs.read_storage::<Mind>();
|
||||||
|
let hidden = ecs.read_storage::<Hidden>();
|
||||||
|
let doors = ecs.write_storage::<Door>();
|
||||||
|
let map = ecs.fetch::<Map>();
|
||||||
|
let entities = ecs.entities();
|
||||||
|
|
||||||
|
let mut data = (&positions, &renderables, &entities, !&hidden).join().collect::<Vec<_>>();
|
||||||
|
data.sort_by(|&a, &b| b.1.render_order.cmp(&a.1.render_order));
|
||||||
|
for (pos, render, ent, _hidden) in data.iter() {
|
||||||
|
let idx = map.xy_idx(pos.x, pos.y);
|
||||||
|
let entity_offset_x = pos.x - min_x;
|
||||||
|
let entity_offset_y = pos.y - min_y;
|
||||||
|
if entity_offset_x > 0 && entity_offset_x < map_width && entity_offset_y > 0 && entity_offset_y < map_height
|
||||||
|
{
|
||||||
|
let offsets = RGB::from_u8(map.red_offset[idx], map.green_offset[idx], map.blue_offset[idx]);
|
||||||
|
let mut draw = false;
|
||||||
|
let mut fg = render.fg;
|
||||||
|
let mut bg = render.bg.add(RGB::from_u8(26, 45, 45)).add(offsets);
|
||||||
|
// Get bloodstain colours
|
||||||
|
if map.bloodstains.contains(&idx) {
|
||||||
|
bg = bg.add(RGB::from_f32(0.6, 0., 0.));
|
||||||
|
}
|
||||||
|
// Draw entities on visible tiles
|
||||||
|
if map.visible_tiles[idx] {
|
||||||
|
draw = true;
|
||||||
|
}
|
||||||
|
// Draw entities with minds within telepath range
|
||||||
|
if map.telepath_tiles[idx] {
|
||||||
|
let has_mind = minds.get(*ent);
|
||||||
|
if let Some(_) = has_mind {
|
||||||
|
draw = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Draw all doors
|
||||||
|
let is_door = doors.get(*ent);
|
||||||
|
if let Some(_) = is_door {
|
||||||
|
if map.revealed_tiles[idx] {
|
||||||
|
if !map.visible_tiles[idx] {
|
||||||
|
fg = fg.mul(0.6);
|
||||||
|
bg = bg.mul(0.6);
|
||||||
|
}
|
||||||
|
draw = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if draw {
|
||||||
|
ctx.set(entity_offset_x, entity_offset_y, fg, bg, render.glyph);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_tile_glyph(idx: usize, map: &Map) -> (rltk::FontCharType, RGB, RGB) {
|
||||||
|
let offsets = RGB::from_u8(map.red_offset[idx], map.green_offset[idx], map.blue_offset[idx]);
|
||||||
|
let glyph;
|
||||||
|
let mut fg = offsets.mul(2.0);
|
||||||
|
let mut bg = offsets.add(RGB::from_u8(26, 45, 45));
|
||||||
|
|
||||||
|
match map.tiles[idx] {
|
||||||
|
TileType::Floor => {
|
||||||
|
glyph = rltk::to_cp437('.');
|
||||||
|
fg = fg.add(RGB::from_f32(0.1, 0.8, 0.5));
|
||||||
|
}
|
||||||
|
TileType::Wall => {
|
||||||
|
let x = idx as i32 % map.width;
|
||||||
|
let y = idx as i32 / map.width;
|
||||||
|
glyph = wall_glyph(&*map, x, y);
|
||||||
|
fg = fg.add(RGB::from_f32(0.6, 0.5, 0.25));
|
||||||
|
}
|
||||||
|
TileType::DownStair => {
|
||||||
|
glyph = rltk::to_cp437('>');
|
||||||
|
fg = RGB::from_f32(0., 1., 1.);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if map.bloodstains.contains(&idx) {
|
||||||
|
bg = bg.add(RGB::from_f32(0.6, 0., 0.));
|
||||||
|
}
|
||||||
|
if !map.visible_tiles[idx] {
|
||||||
|
fg = fg.mul(0.6);
|
||||||
|
bg = bg.mul(0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (glyph, fg, bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_revealed_and_wall(map: &Map, x: i32, y: i32) -> bool {
|
||||||
|
let idx = map.xy_idx(x, y);
|
||||||
|
map.tiles[idx] == TileType::Wall && map.revealed_tiles[idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wall_glyph(map: &Map, x: i32, y: i32) -> rltk::FontCharType {
|
||||||
|
if x < 1 || x > map.width - 2 || y < 1 || y > map.height - 2 as i32 {
|
||||||
|
return 35;
|
||||||
|
}
|
||||||
|
let mut mask: u8 = 0;
|
||||||
|
let diagonals_matter: Vec<u8> = vec![7, 11, 13, 14, 15];
|
||||||
|
|
||||||
|
if is_revealed_and_wall(map, x, y - 1) {
|
||||||
|
// N
|
||||||
|
mask += 1;
|
||||||
|
}
|
||||||
|
if is_revealed_and_wall(map, x, y + 1) {
|
||||||
|
// S
|
||||||
|
mask += 2;
|
||||||
|
}
|
||||||
|
if is_revealed_and_wall(map, x - 1, y) {
|
||||||
|
// W
|
||||||
|
mask += 4;
|
||||||
|
}
|
||||||
|
if is_revealed_and_wall(map, x + 1, y) {
|
||||||
|
// E
|
||||||
|
mask += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if diagonals_matter.contains(&mask) {
|
||||||
|
if is_revealed_and_wall(map, x + 1, y - 1) {
|
||||||
|
// Top right
|
||||||
|
mask += 16;
|
||||||
|
}
|
||||||
|
if is_revealed_and_wall(map, x - 1, y - 1) {
|
||||||
|
// Top left
|
||||||
|
mask += 32;
|
||||||
|
}
|
||||||
|
if is_revealed_and_wall(map, x + 1, y + 1) {
|
||||||
|
// Bottom right
|
||||||
|
mask += 64;
|
||||||
|
}
|
||||||
|
if is_revealed_and_wall(map, x - 1, y + 1) {
|
||||||
|
// Bottom left
|
||||||
|
mask += 128;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match mask {
|
||||||
|
0 => 254, // ■ (254) square pillar; but maybe ○ (9) looks better
|
||||||
|
1 => 186, // Wall only to the north
|
||||||
|
2 => 186, // Wall only to the south
|
||||||
|
3 => 186, // Wall to the north and south
|
||||||
|
4 => 205, // Wall only to the west
|
||||||
|
5 => 188, // Wall to the north and west
|
||||||
|
6 => 187, // Wall to the south and west
|
||||||
|
7 => 185, // Wall to the north, south and west
|
||||||
|
8 => 205, // Wall only to the east
|
||||||
|
9 => 200, // Wall to the north and east
|
||||||
|
10 => 201, // Wall to the south and east
|
||||||
|
11 => 204, // Wall to the north, south and east
|
||||||
|
12 => 205, // Wall to the east and west
|
||||||
|
13 => 202, // Wall to the east, west, and north
|
||||||
|
14 => 203, // Wall to the east, west, and south
|
||||||
|
15 => 206, // ╬ Wall on all sides
|
||||||
|
29 => 202,
|
||||||
|
31 => 206,
|
||||||
|
45 => 202,
|
||||||
|
46 => 203,
|
||||||
|
47 => 206,
|
||||||
|
55 => 185,
|
||||||
|
59 => 204,
|
||||||
|
63 => 203,
|
||||||
|
87 => 185,
|
||||||
|
126 => 203,
|
||||||
|
143 => 206,
|
||||||
|
77 => 202,
|
||||||
|
171 => 204,
|
||||||
|
187 => 204,
|
||||||
|
215 => 185,
|
||||||
|
190 => 203,
|
||||||
|
237 => 202,
|
||||||
|
30 => 203,
|
||||||
|
110 => 203,
|
||||||
|
111 => 206,
|
||||||
|
119 => 185,
|
||||||
|
142 => 203,
|
||||||
|
158 => 203,
|
||||||
|
235 => 204,
|
||||||
|
93 => 202,
|
||||||
|
109 => 202,
|
||||||
|
94 => 203,
|
||||||
|
174 => 203,
|
||||||
|
159 => 206,
|
||||||
|
221 => 202,
|
||||||
|
157 => 202,
|
||||||
|
79 => 206,
|
||||||
|
95 => 185,
|
||||||
|
23 => 185, // NSW and NSE + 1 diagonal
|
||||||
|
39 => 185,
|
||||||
|
71 => 185,
|
||||||
|
103 => 185,
|
||||||
|
135 => 185,
|
||||||
|
151 => 185,
|
||||||
|
199 => 185,
|
||||||
|
78 => 203,
|
||||||
|
27 => 204,
|
||||||
|
43 => 204,
|
||||||
|
75 => 204,
|
||||||
|
107 => 204,
|
||||||
|
139 => 204,
|
||||||
|
155 => 204,
|
||||||
|
173 => 202,
|
||||||
|
141 => 202,
|
||||||
|
205 => 202,
|
||||||
|
175 => 204,
|
||||||
|
203 => 204,
|
||||||
|
61 => 205, // NEW cases
|
||||||
|
125 => 205, // NEW cases
|
||||||
|
189 => 205, // NEW cases
|
||||||
|
206 => 205,
|
||||||
|
207 => 202,
|
||||||
|
222 => 205,
|
||||||
|
238 => 205,
|
||||||
|
253 => 205,
|
||||||
|
254 => 205,
|
||||||
|
167 => 186, // NSW, NW, SW
|
||||||
|
91 => 186, // NSE, NE, SE
|
||||||
|
183 => 186, // NSW, NW, SW, NE
|
||||||
|
123 => 186, // NSE, NE, SE, NW
|
||||||
|
231 => 186, // NSW, NW, SW, SE
|
||||||
|
219 => 186, // NSE, NE, SE, SW
|
||||||
|
247 => 186,
|
||||||
|
251 => 186,
|
||||||
|
127 => 187, // Everything except NE
|
||||||
|
191 => 201, // Everything except NW
|
||||||
|
223 => 188, // Everything except SE
|
||||||
|
239 => 200, // Everything except SW
|
||||||
|
_ => 35, // We missed one?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -235,6 +235,9 @@ pub struct Wand {
|
||||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct Destructible {}
|
pub struct Destructible {}
|
||||||
|
|
||||||
|
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct Digger {}
|
||||||
|
|
||||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct Hidden {}
|
pub struct Hidden {}
|
||||||
|
|
||||||
|
|
|
||||||
47
src/gui.rs
47
src/gui.rs
|
|
@ -1,6 +1,6 @@
|
||||||
use super::{
|
use super::{
|
||||||
gamelog, rex_assets::RexAssets, CombatStats, Equipped, Hidden, HungerClock, HungerState, InBackpack, Map, Name,
|
camera, gamelog, rex_assets::RexAssets, CombatStats, Equipped, Hidden, HungerClock, HungerState, InBackpack, Map,
|
||||||
Player, Point, Position, RunState, State, Viewshed,
|
Name, Player, Point, Position, RunState, State, Viewshed,
|
||||||
};
|
};
|
||||||
use rltk::{Rltk, VirtualKeyCode, RGB};
|
use rltk::{Rltk, VirtualKeyCode, RGB};
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
|
|
@ -83,19 +83,31 @@ pub fn get_input_direction(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_tooltips(ecs: &World, ctx: &mut Rltk) {
|
fn draw_tooltips(ecs: &World, ctx: &mut Rltk) {
|
||||||
|
let (min_x, _max_x, min_y, _max_y) = camera::get_screen_bounds(ecs, ctx);
|
||||||
let map = ecs.fetch::<Map>();
|
let map = ecs.fetch::<Map>();
|
||||||
let names = ecs.read_storage::<Name>();
|
let names = ecs.read_storage::<Name>();
|
||||||
let positions = ecs.read_storage::<Position>();
|
let positions = ecs.read_storage::<Position>();
|
||||||
let hidden = ecs.read_storage::<Hidden>();
|
let hidden = ecs.read_storage::<Hidden>();
|
||||||
|
|
||||||
let mouse_pos = ctx.mouse_pos();
|
let mouse_pos = ctx.mouse_pos();
|
||||||
if mouse_pos.0 >= map.width || mouse_pos.1 >= map.height {
|
let mut mouse_pos_adjusted = mouse_pos;
|
||||||
|
mouse_pos_adjusted.0 += min_x;
|
||||||
|
mouse_pos_adjusted.1 += min_y;
|
||||||
|
if mouse_pos_adjusted.0 >= map.width
|
||||||
|
|| mouse_pos_adjusted.1 >= map.height
|
||||||
|
|| mouse_pos_adjusted.1 < 0 // Might need to be 1, and -1 from map height/width.
|
||||||
|
|| mouse_pos_adjusted.0 < 0
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if !(map.visible_tiles[map.xy_idx(mouse_pos_adjusted.0, mouse_pos_adjusted.1)]
|
||||||
|
|| map.telepath_tiles[map.xy_idx(mouse_pos_adjusted.0, mouse_pos_adjusted.1)])
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mut tooltip: Vec<String> = Vec::new();
|
let mut tooltip: Vec<String> = Vec::new();
|
||||||
for (name, position, _hidden) in (&names, &positions, !&hidden).join() {
|
for (name, position, _hidden) in (&names, &positions, !&hidden).join() {
|
||||||
let idx = map.xy_idx(position.x, position.y);
|
if position.x == mouse_pos_adjusted.0 && position.y == mouse_pos_adjusted.1 {
|
||||||
if position.x == mouse_pos.0 && position.y == mouse_pos.1 && map.visible_tiles[idx] {
|
|
||||||
tooltip.push(name.name.to_string());
|
tooltip.push(name.name.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -367,11 +379,12 @@ pub fn remove_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Opti
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ranged_target(gs: &mut State, ctx: &mut Rltk, range: i32, aoe: i32) -> (ItemMenuResult, Option<Point>) {
|
pub fn ranged_target(gs: &mut State, ctx: &mut Rltk, range: i32, aoe: i32) -> (ItemMenuResult, Option<Point>) {
|
||||||
|
let (min_x, max_x, min_y, max_y) = camera::get_screen_bounds(&gs.ecs, ctx);
|
||||||
let player_entity = gs.ecs.fetch::<Entity>();
|
let player_entity = gs.ecs.fetch::<Entity>();
|
||||||
let player_pos = gs.ecs.fetch::<Point>();
|
let player_pos = gs.ecs.fetch::<Point>();
|
||||||
let viewsheds = gs.ecs.read_storage::<Viewshed>();
|
let viewsheds = gs.ecs.read_storage::<Viewshed>();
|
||||||
|
|
||||||
ctx.print_color(5, 0, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "select target");
|
ctx.print_color(1, 1, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "Targeting which tile? [mouse input]");
|
||||||
|
|
||||||
// Highlight available cells
|
// Highlight available cells
|
||||||
let mut available_cells = Vec::new();
|
let mut available_cells = Vec::new();
|
||||||
|
|
@ -381,8 +394,12 @@ pub fn ranged_target(gs: &mut State, ctx: &mut Rltk, range: i32, aoe: i32) -> (I
|
||||||
for idx in visible.visible_tiles.iter() {
|
for idx in visible.visible_tiles.iter() {
|
||||||
let distance = rltk::DistanceAlg::Pythagoras.distance2d(*player_pos, *idx);
|
let distance = rltk::DistanceAlg::Pythagoras.distance2d(*player_pos, *idx);
|
||||||
if distance <= range as f32 {
|
if distance <= range as f32 {
|
||||||
ctx.set_bg(idx.x, idx.y, RGB::named(rltk::BLUE));
|
let screen_x = idx.x - min_x;
|
||||||
available_cells.push(idx);
|
let screen_y = idx.y - min_y;
|
||||||
|
if screen_x > 0 && screen_x < (max_x - min_x) && screen_y > 0 && screen_y < (max_y - min_y) {
|
||||||
|
ctx.set_bg(screen_x, screen_y, RGB::named(rltk::BLUE));
|
||||||
|
available_cells.push(idx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -391,24 +408,30 @@ pub fn ranged_target(gs: &mut State, ctx: &mut Rltk, range: i32, aoe: i32) -> (I
|
||||||
|
|
||||||
// Draw mouse cursor
|
// Draw mouse cursor
|
||||||
let mouse_pos = ctx.mouse_pos();
|
let mouse_pos = ctx.mouse_pos();
|
||||||
|
let mut mouse_pos_adjusted = mouse_pos;
|
||||||
|
mouse_pos_adjusted.0 += min_x;
|
||||||
|
mouse_pos_adjusted.1 += min_y;
|
||||||
let map = gs.ecs.fetch::<Map>();
|
let map = gs.ecs.fetch::<Map>();
|
||||||
let mut valid_target = false;
|
let mut valid_target = false;
|
||||||
for idx in available_cells.iter() {
|
for idx in available_cells.iter() {
|
||||||
if idx.x == mouse_pos.0 && idx.y == mouse_pos.1 {
|
if idx.x == mouse_pos_adjusted.0 && idx.y == mouse_pos_adjusted.1 {
|
||||||
valid_target = true;
|
valid_target = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if valid_target {
|
if valid_target {
|
||||||
if aoe > 0 {
|
if aoe > 0 {
|
||||||
let mut blast_tiles = rltk::field_of_view(Point::new(mouse_pos.0, mouse_pos.1), aoe, &*map);
|
// We adjust for camera position when getting FOV, but then we need to adjust back
|
||||||
|
// when iterating through the tiles themselves, by taking away min_x/min_y.
|
||||||
|
let mut blast_tiles =
|
||||||
|
rltk::field_of_view(Point::new(mouse_pos_adjusted.0, mouse_pos_adjusted.1), aoe, &*map);
|
||||||
blast_tiles.retain(|p| p.x > 0 && p.x < map.width - 1 && p.y > 0 && p.y < map.height - 1);
|
blast_tiles.retain(|p| p.x > 0 && p.x < map.width - 1 && p.y > 0 && p.y < map.height - 1);
|
||||||
for tile in blast_tiles.iter() {
|
for tile in blast_tiles.iter() {
|
||||||
ctx.set_bg(tile.x, tile.y, RGB::named(rltk::DARKCYAN));
|
ctx.set_bg(tile.x - min_x, tile.y - min_y, RGB::named(rltk::DARKCYAN));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.set_bg(mouse_pos.0, mouse_pos.1, RGB::named(rltk::CYAN));
|
ctx.set_bg(mouse_pos.0, mouse_pos.1, RGB::named(rltk::CYAN));
|
||||||
if ctx.left_click {
|
if ctx.left_click {
|
||||||
return (ItemMenuResult::Selected, Some(Point::new(mouse_pos.0, mouse_pos.1)));
|
return (ItemMenuResult::Selected, Some(Point::new(mouse_pos_adjusted.0, mouse_pos_adjusted.1)));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ctx.set_bg(mouse_pos.0, mouse_pos.1, RGB::named(rltk::RED));
|
ctx.set_bg(mouse_pos.0, mouse_pos.1, RGB::named(rltk::RED));
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use super::{
|
use super::{
|
||||||
gamelog, CombatStats, Confusion, Consumable, Cursed, Destructible, Equippable, Equipped, HungerClock, HungerState,
|
gamelog, CombatStats, Confusion, Consumable, Cursed, Destructible, Digger, Equippable, Equipped, HungerClock,
|
||||||
InBackpack, InflictsDamage, MagicMapper, Map, Name, ParticleBuilder, Point, Position, ProvidesHealing,
|
HungerState, InBackpack, InflictsDamage, MagicMapper, Map, Name, ParticleBuilder, Point, Position, ProvidesHealing,
|
||||||
ProvidesNutrition, RandomNumberGenerator, RunState, SufferDamage, Wand, WantsToDropItem, WantsToPickupItem,
|
ProvidesNutrition, RandomNumberGenerator, RunState, SufferDamage, TileType, Viewshed, Wand, WantsToDropItem,
|
||||||
WantsToRemoveItem, WantsToUseItem, AOE, DEFAULT_PARTICLE_LIFETIME, LONG_PARTICLE_LIFETIME,
|
WantsToPickupItem, WantsToRemoveItem, WantsToUseItem, AOE, DEFAULT_PARTICLE_LIFETIME, LONG_PARTICLE_LIFETIME,
|
||||||
};
|
};
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
|
|
||||||
|
|
@ -38,16 +38,21 @@ impl<'a> System<'a> for ItemCollectionSystem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Grouping together components because of type complexity issues - SystemData was too large.
|
||||||
|
// This is a temporary solution that'll be fixed once inventory use is refactored into separate
|
||||||
|
// systems.
|
||||||
|
type EquipComponents<'a> = (ReadStorage<'a, Equippable>, WriteStorage<'a, Equipped>);
|
||||||
|
|
||||||
pub struct ItemUseSystem {}
|
pub struct ItemUseSystem {}
|
||||||
impl<'a> System<'a> for ItemUseSystem {
|
impl<'a> System<'a> for ItemUseSystem {
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
type SystemData = (
|
type SystemData = (
|
||||||
ReadExpect<'a, Entity>,
|
ReadExpect<'a, Entity>,
|
||||||
ReadExpect<'a, Map>,
|
WriteExpect<'a, Map>,
|
||||||
WriteExpect<'a, RandomNumberGenerator>,
|
WriteExpect<'a, RandomNumberGenerator>,
|
||||||
Entities<'a>,
|
Entities<'a>,
|
||||||
WriteStorage<'a, WantsToUseItem>,
|
WriteStorage<'a, WantsToUseItem>,
|
||||||
ReadStorage<'a, Name>,
|
WriteStorage<'a, Name>,
|
||||||
WriteStorage<'a, Consumable>,
|
WriteStorage<'a, Consumable>,
|
||||||
WriteStorage<'a, Wand>,
|
WriteStorage<'a, Wand>,
|
||||||
ReadStorage<'a, Destructible>,
|
ReadStorage<'a, Destructible>,
|
||||||
|
|
@ -61,22 +66,23 @@ impl<'a> System<'a> for ItemUseSystem {
|
||||||
ReadStorage<'a, Position>,
|
ReadStorage<'a, Position>,
|
||||||
ReadStorage<'a, InflictsDamage>,
|
ReadStorage<'a, InflictsDamage>,
|
||||||
ReadStorage<'a, AOE>,
|
ReadStorage<'a, AOE>,
|
||||||
|
ReadStorage<'a, Digger>,
|
||||||
WriteStorage<'a, Confusion>,
|
WriteStorage<'a, Confusion>,
|
||||||
ReadStorage<'a, MagicMapper>,
|
ReadStorage<'a, MagicMapper>,
|
||||||
WriteExpect<'a, RunState>,
|
WriteExpect<'a, RunState>,
|
||||||
ReadStorage<'a, Equippable>,
|
EquipComponents<'a>,
|
||||||
WriteStorage<'a, Equipped>,
|
|
||||||
WriteStorage<'a, InBackpack>,
|
WriteStorage<'a, InBackpack>,
|
||||||
|
WriteStorage<'a, Viewshed>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn run(&mut self, data: Self::SystemData) {
|
fn run(&mut self, data: Self::SystemData) {
|
||||||
let (
|
let (
|
||||||
player_entity,
|
player_entity,
|
||||||
map,
|
mut map,
|
||||||
mut rng,
|
mut rng,
|
||||||
entities,
|
entities,
|
||||||
mut wants_to_use,
|
mut wants_to_use,
|
||||||
names,
|
mut names,
|
||||||
mut consumables,
|
mut consumables,
|
||||||
mut wands,
|
mut wands,
|
||||||
destructibles,
|
destructibles,
|
||||||
|
|
@ -90,23 +96,24 @@ impl<'a> System<'a> for ItemUseSystem {
|
||||||
positions,
|
positions,
|
||||||
inflicts_damage,
|
inflicts_damage,
|
||||||
aoe,
|
aoe,
|
||||||
|
digger,
|
||||||
mut confused,
|
mut confused,
|
||||||
magic_mapper,
|
magic_mapper,
|
||||||
mut runstate,
|
mut runstate,
|
||||||
equippable,
|
(equippable, mut equipped),
|
||||||
mut equipped,
|
|
||||||
mut backpack,
|
mut backpack,
|
||||||
|
mut viewsheds,
|
||||||
) = data;
|
) = data;
|
||||||
|
|
||||||
for (entity, wants_to_use) in (&entities, &wants_to_use).join() {
|
for (entity, wants_to_use) in (&entities, &wants_to_use).join() {
|
||||||
let mut verb = "use";
|
let mut verb = "use";
|
||||||
let mut used_item = true;
|
let mut used_item = true;
|
||||||
let mut aoe_item = false;
|
let mut aoe_item = false;
|
||||||
let item_being_used = names.get(wants_to_use.item).unwrap();
|
|
||||||
|
|
||||||
let is_cursed = cursed_items.get(wants_to_use.item);
|
let is_cursed = cursed_items.get(wants_to_use.item);
|
||||||
let wand = wands.get_mut(wants_to_use.item);
|
let wand = wands.get_mut(wants_to_use.item);
|
||||||
if let Some(wand) = wand {
|
if let Some(wand) = wand {
|
||||||
|
let name = names.get_mut(wants_to_use.item).unwrap();
|
||||||
// If want has no uses, roll 1d121. On a 121, wrest the wand, then delete it.
|
// If want has no uses, roll 1d121. On a 121, wrest the wand, then delete it.
|
||||||
if wand.uses == 0 {
|
if wand.uses == 0 {
|
||||||
if rng.roll_dice(1, 121) != 121 {
|
if rng.roll_dice(1, 121) != 121 {
|
||||||
|
|
@ -120,9 +127,14 @@ impl<'a> System<'a> for ItemUseSystem {
|
||||||
consumables.insert(wants_to_use.item, Consumable {}).expect("Could not insert consumable");
|
consumables.insert(wants_to_use.item, Consumable {}).expect("Could not insert consumable");
|
||||||
}
|
}
|
||||||
verb = "zap";
|
verb = "zap";
|
||||||
|
// TODO: Change this to track uses better, after adding in identification.
|
||||||
|
name.name.push_str("*");
|
||||||
|
name.plural.push_str("*");
|
||||||
wand.uses -= 1;
|
wand.uses -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let item_being_used = names.get(wants_to_use.item).unwrap();
|
||||||
|
|
||||||
let is_edible = provides_nutrition.get(wants_to_use.item);
|
let is_edible = provides_nutrition.get(wants_to_use.item);
|
||||||
if let Some(_) = is_edible {
|
if let Some(_) = is_edible {
|
||||||
verb = "eat";
|
verb = "eat";
|
||||||
|
|
@ -140,9 +152,14 @@ impl<'a> System<'a> for ItemUseSystem {
|
||||||
|
|
||||||
// TARGETING
|
// TARGETING
|
||||||
let mut targets: Vec<Entity> = Vec::new();
|
let mut targets: Vec<Entity> = Vec::new();
|
||||||
|
let mut target_idxs: Vec<usize> = Vec::new();
|
||||||
match wants_to_use.target {
|
match wants_to_use.target {
|
||||||
None => {
|
None => {
|
||||||
targets.push(*player_entity);
|
targets.push(*player_entity);
|
||||||
|
let pos = positions.get(*player_entity);
|
||||||
|
if let Some(pos) = pos {
|
||||||
|
target_idxs.push(map.xy_idx(pos.x, pos.y));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Some(mut target) => {
|
Some(mut target) => {
|
||||||
let area_effect = aoe.get(wants_to_use.item);
|
let area_effect = aoe.get(wants_to_use.item);
|
||||||
|
|
@ -150,6 +167,7 @@ impl<'a> System<'a> for ItemUseSystem {
|
||||||
None => {
|
None => {
|
||||||
// Single target in a tile
|
// Single target in a tile
|
||||||
let idx = map.xy_idx(target.x, target.y);
|
let idx = map.xy_idx(target.x, target.y);
|
||||||
|
target_idxs.push(idx);
|
||||||
for mob in map.tile_content[idx].iter() {
|
for mob in map.tile_content[idx].iter() {
|
||||||
targets.push(*mob);
|
targets.push(*mob);
|
||||||
}
|
}
|
||||||
|
|
@ -177,6 +195,7 @@ impl<'a> System<'a> for ItemUseSystem {
|
||||||
blast_tiles.retain(|p| p.x > 0 && p.x < map.width - 1 && p.y > 0 && p.y < map.height - 1);
|
blast_tiles.retain(|p| p.x > 0 && p.x < map.width - 1 && p.y > 0 && p.y < map.height - 1);
|
||||||
for tile_idx in blast_tiles.iter() {
|
for tile_idx in blast_tiles.iter() {
|
||||||
let idx = map.xy_idx(tile_idx.x, tile_idx.y);
|
let idx = map.xy_idx(tile_idx.x, tile_idx.y);
|
||||||
|
target_idxs.push(idx);
|
||||||
for mob in map.tile_content[idx].iter() {
|
for mob in map.tile_content[idx].iter() {
|
||||||
targets.push(*mob);
|
targets.push(*mob);
|
||||||
}
|
}
|
||||||
|
|
@ -250,13 +269,7 @@ impl<'a> System<'a> for ItemUseSystem {
|
||||||
if let Some(stats) = stats {
|
if let Some(stats) = stats {
|
||||||
stats.hp = i32::min(stats.max_hp, stats.hp + heal.amount);
|
stats.hp = i32::min(stats.max_hp, stats.hp + heal.amount);
|
||||||
if entity == *player_entity {
|
if entity == *player_entity {
|
||||||
gamelog::Logger::new()
|
gamelog::Logger::new().append("Quaffing, you recover some vigour.").log();
|
||||||
.append("Quaffing, you heal")
|
|
||||||
.colour(rltk::GREEN)
|
|
||||||
.append(heal.amount)
|
|
||||||
.colour(rltk::WHITE)
|
|
||||||
.append("hit points.")
|
|
||||||
.log();
|
|
||||||
}
|
}
|
||||||
let pos = positions.get(entity);
|
let pos = positions.get(entity);
|
||||||
if let Some(pos) = pos {
|
if let Some(pos) = pos {
|
||||||
|
|
@ -371,6 +384,27 @@ impl<'a> System<'a> for ItemUseSystem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let is_digger = digger.get(wants_to_use.item);
|
||||||
|
match is_digger {
|
||||||
|
None => {}
|
||||||
|
Some(_) => {
|
||||||
|
used_item = true;
|
||||||
|
for idx in target_idxs {
|
||||||
|
if map.tiles[idx] == TileType::Wall {
|
||||||
|
map.tiles[idx] = TileType::Floor;
|
||||||
|
}
|
||||||
|
for viewshed in (&mut viewsheds).join() {
|
||||||
|
if viewshed
|
||||||
|
.visible_tiles
|
||||||
|
.contains(&Point::new(idx % map.width as usize, idx / map.width as usize))
|
||||||
|
{
|
||||||
|
viewshed.dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ITEM DELETION AFTER USE
|
// ITEM DELETION AFTER USE
|
||||||
if used_item {
|
if used_item {
|
||||||
let consumable = consumables.get(wants_to_use.item);
|
let consumable = consumables.get(wants_to_use.item);
|
||||||
|
|
|
||||||
50
src/main.rs
50
src/main.rs
|
|
@ -4,6 +4,7 @@ use specs::saveload::{SimpleMarker, SimpleMarkerAllocator};
|
||||||
use std::ops::{Add, Mul};
|
use std::ops::{Add, Mul};
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
|
|
||||||
|
pub mod camera;
|
||||||
mod components;
|
mod components;
|
||||||
pub use components::*;
|
pub use components::*;
|
||||||
mod map;
|
mod map;
|
||||||
|
|
@ -251,52 +252,8 @@ impl GameState for State {
|
||||||
RunState::MainMenu { .. } => {}
|
RunState::MainMenu { .. } => {}
|
||||||
_ => {
|
_ => {
|
||||||
// Draw map and ui
|
// Draw map and ui
|
||||||
draw_map(&self.ecs.fetch::<Map>(), ctx);
|
camera::render_camera(&self.ecs, ctx);
|
||||||
{
|
gui::draw_ui(&self.ecs, ctx);
|
||||||
let positions = self.ecs.read_storage::<Position>();
|
|
||||||
let renderables = self.ecs.read_storage::<Renderable>();
|
|
||||||
let minds = self.ecs.read_storage::<Mind>();
|
|
||||||
let hidden = self.ecs.read_storage::<Hidden>();
|
|
||||||
let doors = self.ecs.write_storage::<Door>();
|
|
||||||
let map = self.ecs.fetch::<Map>();
|
|
||||||
let entities = self.ecs.entities();
|
|
||||||
|
|
||||||
let mut data = (&positions, &renderables, &entities, !&hidden).join().collect::<Vec<_>>();
|
|
||||||
data.sort_by(|&a, &b| b.1.render_order.cmp(&a.1.render_order));
|
|
||||||
for (pos, render, ent, _hidden) in data.iter() {
|
|
||||||
let idx = map.xy_idx(pos.x, pos.y);
|
|
||||||
let offsets = RGB::from_u8(map.red_offset[idx], map.green_offset[idx], map.blue_offset[idx]);
|
|
||||||
let mut fg = render.fg;
|
|
||||||
let mut bg = render.bg.add(RGB::from_u8(26, 45, 45)).add(offsets);
|
|
||||||
// Get bloodstain colours
|
|
||||||
if map.bloodstains.contains(&idx) {
|
|
||||||
bg = bg.add(RGB::from_f32(0.6, 0., 0.));
|
|
||||||
}
|
|
||||||
// Draw entities on visible tiles
|
|
||||||
if map.visible_tiles[idx] {
|
|
||||||
ctx.set(pos.x, pos.y, fg, bg, render.glyph);
|
|
||||||
}
|
|
||||||
// Draw entities with minds within telepath range
|
|
||||||
if map.telepath_tiles[idx] {
|
|
||||||
let has_mind = minds.get(*ent);
|
|
||||||
if let Some(_) = has_mind {
|
|
||||||
ctx.set(pos.x, pos.y, render.fg, RGB::named(rltk::BLACK), render.glyph);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Draw all doors
|
|
||||||
let is_door = doors.get(*ent);
|
|
||||||
if let Some(_) = is_door {
|
|
||||||
if map.revealed_tiles[idx] {
|
|
||||||
if !map.visible_tiles[idx] {
|
|
||||||
fg = fg.mul(0.6);
|
|
||||||
bg = bg.mul(0.6);
|
|
||||||
}
|
|
||||||
ctx.set(pos.x, pos.y, fg, bg, render.glyph);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gui::draw_ui(&self.ecs, ctx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -566,6 +523,7 @@ fn main() -> rltk::BError {
|
||||||
gs.ecs.register::<InflictsDamage>();
|
gs.ecs.register::<InflictsDamage>();
|
||||||
gs.ecs.register::<Ranged>();
|
gs.ecs.register::<Ranged>();
|
||||||
gs.ecs.register::<AOE>();
|
gs.ecs.register::<AOE>();
|
||||||
|
gs.ecs.register::<Digger>();
|
||||||
gs.ecs.register::<Confusion>();
|
gs.ecs.register::<Confusion>();
|
||||||
gs.ecs.register::<MagicMapper>();
|
gs.ecs.register::<MagicMapper>();
|
||||||
gs.ecs.register::<InBackpack>();
|
gs.ecs.register::<InBackpack>();
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,11 @@ pub enum TileType {
|
||||||
DownStair,
|
DownStair,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const MAPWIDTH: usize = 80;
|
// FIXME: If the map size gets too small, entities stop being rendered starting from the right.
|
||||||
pub const MAPHEIGHT: usize = 43;
|
// i.e. on a map size of 40*40, only entities to the left of the player are rendered.
|
||||||
|
// on a map size of 42*42, the player can see entities up to 2 tiles to their right.
|
||||||
|
pub const MAPWIDTH: usize = 64;
|
||||||
|
pub const MAPHEIGHT: usize = 64;
|
||||||
pub const MAPCOUNT: usize = MAPHEIGHT * MAPWIDTH;
|
pub const MAPCOUNT: usize = MAPHEIGHT * MAPWIDTH;
|
||||||
|
|
||||||
#[derive(Default, Serialize, Deserialize, Clone)]
|
#[derive(Default, Serialize, Deserialize, Clone)]
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ pub fn try_door(i: i32, j: i32, ecs: &mut World) -> RunState {
|
||||||
let mut blocks_movement = ecs.write_storage::<BlocksTile>();
|
let mut blocks_movement = ecs.write_storage::<BlocksTile>();
|
||||||
let mut renderables = ecs.write_storage::<Renderable>();
|
let mut renderables = ecs.write_storage::<Renderable>();
|
||||||
let names = ecs.read_storage::<Name>();
|
let names = ecs.read_storage::<Name>();
|
||||||
|
let mut rng = ecs.write_resource::<RandomNumberGenerator>();
|
||||||
|
|
||||||
let mut result = RunState::AwaitingInput;
|
let mut result = RunState::AwaitingInput;
|
||||||
let mut door_pos: Option<Point> = None;
|
let mut door_pos: Option<Point> = None;
|
||||||
|
|
@ -45,6 +46,10 @@ pub fn try_door(i: i32, j: i32, ecs: &mut World) -> RunState {
|
||||||
if let Some(name) = names.get(*potential_target) {
|
if let Some(name) = names.get(*potential_target) {
|
||||||
gamelog::Logger::new().append("The").item_name(&name.name).append("is blocked.").log();
|
gamelog::Logger::new().append("The").item_name(&name.name).append("is blocked.").log();
|
||||||
}
|
}
|
||||||
|
} else if rng.roll_dice(1, 6) == 1 {
|
||||||
|
if let Some(name) = names.get(*potential_target) {
|
||||||
|
gamelog::Logger::new().append("The").item_name(&name.name).append("resists!").log();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
door.open = false;
|
door.open = false;
|
||||||
blocks_visibility
|
blocks_visibility
|
||||||
|
|
@ -59,8 +64,8 @@ pub fn try_door(i: i32, j: i32, ecs: &mut World) -> RunState {
|
||||||
}
|
}
|
||||||
render_data.glyph = rltk::to_cp437('+'); // Nethack open door, maybe just use '/' instead.
|
render_data.glyph = rltk::to_cp437('+'); // Nethack open door, maybe just use '/' instead.
|
||||||
door_pos = Some(Point::new(pos.x + delta_x, pos.y + delta_y));
|
door_pos = Some(Point::new(pos.x + delta_x, pos.y + delta_y));
|
||||||
result = RunState::PlayerTurn;
|
|
||||||
}
|
}
|
||||||
|
result = RunState::PlayerTurn;
|
||||||
} else {
|
} else {
|
||||||
gamelog::Logger::new().append("It's already closed.").log();
|
gamelog::Logger::new().append("It's already closed.").log();
|
||||||
}
|
}
|
||||||
|
|
@ -94,6 +99,7 @@ pub fn open(i: i32, j: i32, ecs: &mut World) -> RunState {
|
||||||
let mut blocks_movement = ecs.write_storage::<BlocksTile>();
|
let mut blocks_movement = ecs.write_storage::<BlocksTile>();
|
||||||
let mut renderables = ecs.write_storage::<Renderable>();
|
let mut renderables = ecs.write_storage::<Renderable>();
|
||||||
let names = ecs.read_storage::<Name>();
|
let names = ecs.read_storage::<Name>();
|
||||||
|
let mut rng = ecs.write_resource::<RandomNumberGenerator>();
|
||||||
|
|
||||||
let mut result = RunState::AwaitingInput;
|
let mut result = RunState::AwaitingInput;
|
||||||
let mut door_pos: Option<Point> = None;
|
let mut door_pos: Option<Point> = None;
|
||||||
|
|
@ -116,15 +122,21 @@ pub fn open(i: i32, j: i32, ecs: &mut World) -> RunState {
|
||||||
let door = doors.get_mut(*potential_target);
|
let door = doors.get_mut(*potential_target);
|
||||||
if let Some(door) = door {
|
if let Some(door) = door {
|
||||||
if door.open == false {
|
if door.open == false {
|
||||||
door.open = true;
|
if rng.roll_dice(1, 6) == 1 {
|
||||||
blocks_visibility.remove(*potential_target);
|
if let Some(name) = names.get(*potential_target) {
|
||||||
blocks_movement.remove(*potential_target);
|
gamelog::Logger::new().append("The").item_name(&name.name).append("resists!").log();
|
||||||
let render_data = renderables.get_mut(*potential_target).unwrap();
|
}
|
||||||
if let Some(name) = names.get(*potential_target) {
|
} else {
|
||||||
gamelog::Logger::new().append("You open the").item_name_n(&name.name).period().log();
|
door.open = true;
|
||||||
|
blocks_visibility.remove(*potential_target);
|
||||||
|
blocks_movement.remove(*potential_target);
|
||||||
|
let render_data = renderables.get_mut(*potential_target).unwrap();
|
||||||
|
if let Some(name) = names.get(*potential_target) {
|
||||||
|
gamelog::Logger::new().append("You open the").item_name_n(&name.name).period().log();
|
||||||
|
}
|
||||||
|
render_data.glyph = rltk::to_cp437('▓'); // Nethack open door, maybe just use '/' instead.
|
||||||
|
door_pos = Some(Point::new(pos.x + delta_x, pos.y + delta_y));
|
||||||
}
|
}
|
||||||
render_data.glyph = rltk::to_cp437('▓'); // Nethack open door, maybe just use '/' instead.
|
|
||||||
door_pos = Some(Point::new(pos.x + delta_x, pos.y + delta_y));
|
|
||||||
result = RunState::PlayerTurn;
|
result = RunState::PlayerTurn;
|
||||||
} else {
|
} else {
|
||||||
gamelog::Logger::new().append("It's already open.").log();
|
gamelog::Logger::new().append("It's already open.").log();
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ pub fn save_game(ecs: &mut World) {
|
||||||
Cursed,
|
Cursed,
|
||||||
DefenceBonus,
|
DefenceBonus,
|
||||||
Destructible,
|
Destructible,
|
||||||
|
Digger,
|
||||||
Door,
|
Door,
|
||||||
EntityMoved,
|
EntityMoved,
|
||||||
EntryTrigger,
|
EntryTrigger,
|
||||||
|
|
@ -152,6 +153,7 @@ pub fn load_game(ecs: &mut World) {
|
||||||
Cursed,
|
Cursed,
|
||||||
DefenceBonus,
|
DefenceBonus,
|
||||||
Destructible,
|
Destructible,
|
||||||
|
Digger,
|
||||||
Door,
|
Door,
|
||||||
EntityMoved,
|
EntityMoved,
|
||||||
EntryTrigger,
|
EntryTrigger,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use super::{
|
use super::{
|
||||||
random_table::RandomTable, Attribute, Attributes, BlocksTile, BlocksVisibility, CombatStats, Confusion, Consumable,
|
random_table::RandomTable, Attribute, Attributes, BlocksTile, BlocksVisibility, CombatStats, Confusion, Consumable,
|
||||||
Cursed, DefenceBonus, Destructible, Door, EntryTrigger, EquipmentSlot, Equippable, Hidden, HungerClock,
|
Cursed, DefenceBonus, Destructible, Digger, Door, EntryTrigger, EquipmentSlot, Equippable, Hidden, HungerClock,
|
||||||
HungerState, InflictsDamage, Item, MagicMapper, Map, MeleePowerBonus, Mind, Monster, Name, Player, Position,
|
HungerState, InflictsDamage, Item, MagicMapper, Map, MeleePowerBonus, Mind, Monster, Name, Player, Position,
|
||||||
ProvidesHealing, ProvidesNutrition, Ranged, Rect, Renderable, SerializeMe, SingleActivation, TileType, Viewshed,
|
ProvidesHealing, ProvidesNutrition, Ranged, Rect, Renderable, SerializeMe, SingleActivation, TileType, Viewshed,
|
||||||
Wand, AOE, MAPWIDTH,
|
Wand, AOE, MAPWIDTH,
|
||||||
|
|
@ -168,6 +168,7 @@ pub fn spawn_entity(ecs: &mut World, spawn: &(&usize, &String)) {
|
||||||
"magic missile wand" => magic_missile_wand(ecs, x, y),
|
"magic missile wand" => magic_missile_wand(ecs, x, y),
|
||||||
"fireball wand" => fireball_wand(ecs, x, y),
|
"fireball wand" => fireball_wand(ecs, x, y),
|
||||||
"confusion wand" => confusion_wand(ecs, x, y),
|
"confusion wand" => confusion_wand(ecs, x, y),
|
||||||
|
"digging wand" => digging_wand(ecs, x, y),
|
||||||
// Food
|
// Food
|
||||||
"rations" => rations(ecs, x, y),
|
"rations" => rations(ecs, x, y),
|
||||||
"apple" => apple(ecs, x, y),
|
"apple" => apple(ecs, x, y),
|
||||||
|
|
@ -180,7 +181,7 @@ pub fn spawn_entity(ecs: &mut World, spawn: &(&usize, &String)) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 20 mobs : 6 items : 2 food : 1 trap
|
// 12 mobs : 6 items : 2 food : 1 trap
|
||||||
fn category_table() -> RandomTable {
|
fn category_table() -> RandomTable {
|
||||||
return RandomTable::new().add("mob", 12).add("item", 6).add("food", 2).add("trap", 1);
|
return RandomTable::new().add("mob", 12).add("item", 6).add("food", 2).add("trap", 1);
|
||||||
}
|
}
|
||||||
|
|
@ -230,7 +231,11 @@ pub fn scroll_table(_map_depth: i32) -> RandomTable {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wand_table(_map_depth: i32) -> RandomTable {
|
pub fn wand_table(_map_depth: i32) -> RandomTable {
|
||||||
return RandomTable::new().add("magic missile wand", 1).add("fireball wand", 1).add("confusion wand", 1);
|
return RandomTable::new()
|
||||||
|
.add("magic missile wand", 1)
|
||||||
|
.add("fireball wand", 1)
|
||||||
|
.add("confusion wand", 1)
|
||||||
|
.add("digging wand", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn food_table(_map_depth: i32) -> RandomTable {
|
pub fn food_table(_map_depth: i32) -> RandomTable {
|
||||||
|
|
@ -600,6 +605,25 @@ fn confusion_wand(ecs: &mut World, x: i32, y: i32) {
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn digging_wand(ecs: &mut World, x: i32, y: i32) {
|
||||||
|
ecs.create_entity()
|
||||||
|
.with(Position { x, y })
|
||||||
|
.with(Renderable {
|
||||||
|
glyph: rltk::to_cp437('/'),
|
||||||
|
fg: RGB::named(rltk::PURPLE),
|
||||||
|
bg: RGB::named(rltk::BLACK),
|
||||||
|
render_order: 2,
|
||||||
|
})
|
||||||
|
.with(Name { name: "wand of digging".to_string(), plural: "wands of digging".to_string() })
|
||||||
|
.with(Item {})
|
||||||
|
.with(Wand { uses: 3, max_uses: 3 })
|
||||||
|
.with(Destructible {})
|
||||||
|
.with(Ranged { range: 10 })
|
||||||
|
.with(Digger {})
|
||||||
|
.marked::<SimpleMarker<SerializeMe>>()
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
// TRAPS
|
// TRAPS
|
||||||
fn bear_trap(ecs: &mut World, x: i32, y: i32) {
|
fn bear_trap(ecs: &mut World, x: i32, y: i32) {
|
||||||
ecs.create_entity()
|
ecs.create_entity()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue