Going berserk!
This commit is contained in:
parent
d81ace4555
commit
e85828fd33
179
src/main.rs
179
src/main.rs
@ -36,6 +36,8 @@ const MAX_ROOMS: i32 = 30;
|
|||||||
const FOV_ALGO: FovAlgorithm = FovAlgorithm::Basic;
|
const FOV_ALGO: FovAlgorithm = FovAlgorithm::Basic;
|
||||||
const FOV_LIGHT_WALLS: bool = true;
|
const FOV_LIGHT_WALLS: bool = true;
|
||||||
const TORCH_RADIUS: i32 = 10;
|
const TORCH_RADIUS: i32 = 10;
|
||||||
|
const MAX_ROOM_MONSTERS: i32 = 3;
|
||||||
|
const PLAYER: usize = 0;
|
||||||
|
|
||||||
struct Tcod {
|
struct Tcod {
|
||||||
root: Root,
|
root: Root,
|
||||||
@ -49,18 +51,21 @@ struct Object {
|
|||||||
y: i32,
|
y: i32,
|
||||||
char: char,
|
char: char,
|
||||||
color: Color,
|
color: Color,
|
||||||
|
name: String,
|
||||||
|
blocks: bool,
|
||||||
|
alive: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Object {
|
impl Object {
|
||||||
pub fn new(x: i32, y: i32, char: char, color: Color) -> Self {
|
pub fn new(x: i32, y: i32, char: char, name: &str, color: Color, blocks: bool) -> Self {
|
||||||
Object { x, y, char, color }
|
Object {
|
||||||
}
|
x: x,
|
||||||
|
y: y,
|
||||||
// Move by given amount, if the destination is not blocked
|
char: char,
|
||||||
pub fn move_by(&mut self, dx: i32, dy: i32, game: &Game) {
|
color: color,
|
||||||
if !game.map[(self.x + dx) as usize][(self.y + dy) as usize].blocked {
|
name: name.into(),
|
||||||
self.x += dx;
|
blocks: blocks,
|
||||||
self.y += dy;
|
alive: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,6 +74,15 @@ impl Object {
|
|||||||
con.set_default_foreground(self.color);
|
con.set_default_foreground(self.color);
|
||||||
con.put_char(self.x, self.y, self.char, BackgroundFlag::None);
|
con.put_char(self.x, self.y, self.char, BackgroundFlag::None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pos(&self) -> (i32, i32) {
|
||||||
|
(self.x, self.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_pos(&mut self, x: i32, y: i32) {
|
||||||
|
self.x = x;
|
||||||
|
self.y = y;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
@ -132,34 +146,60 @@ impl Rect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_keys(tcod: &mut Tcod, game: &Game, player: &mut Object) -> bool {
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
enum PlayerAction {
|
||||||
|
TookTurn,
|
||||||
|
DidntTakeTurn,
|
||||||
|
Exit,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_keys(tcod: &mut Tcod, game: &Game, objects: &mut Vec<Object>) -> PlayerAction {
|
||||||
use tcod::input::Key;
|
use tcod::input::Key;
|
||||||
use tcod::input::KeyCode::*;
|
use tcod::input::KeyCode::*;
|
||||||
|
use PlayerAction::*;
|
||||||
|
|
||||||
let key = tcod.root.wait_for_keypress(true);
|
let key = tcod.root.wait_for_keypress(true);
|
||||||
match key {
|
let player_alive = objects[PLAYER].alive;
|
||||||
|
|
||||||
|
match (key, key.text(), player_alive) {
|
||||||
|
(
|
||||||
Key {
|
Key {
|
||||||
code: Enter,
|
code: Enter,
|
||||||
alt: true,
|
alt: true,
|
||||||
..
|
..
|
||||||
} => {
|
},
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
) => {
|
||||||
// Alt+Enter: toggle fullscreen
|
// Alt+Enter: toggle fullscreen
|
||||||
let fullscreen = tcod.root.is_fullscreen();
|
let fullscreen = tcod.root.is_fullscreen();
|
||||||
tcod.root.set_fullscreen(!fullscreen);
|
tcod.root.set_fullscreen(!fullscreen);
|
||||||
|
DidntTakeTurn
|
||||||
}
|
}
|
||||||
Key { code: Escape, .. } => return true, // exit game
|
(Key { code: Escape, .. }, _, _) => Exit, // exit game
|
||||||
|
|
||||||
Key { code: Up, .. } => player.move_by(0, -1, game),
|
(Key { code: Up, .. }, _, true) => {
|
||||||
Key { code: Down, .. } => player.move_by(0, 1, game),
|
player_move_or_attack(0, -1, game, objects);
|
||||||
Key { code: Left, .. } => player.move_by(-1, 0, game),
|
TookTurn
|
||||||
Key { code: Right, .. } => player.move_by(1, 0, game),
|
|
||||||
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
false
|
(Key { code: Down, .. }, _, true) => {
|
||||||
|
player_move_or_attack(0, 1, game, objects);
|
||||||
|
TookTurn
|
||||||
|
}
|
||||||
|
(Key { code: Left, .. }, _, true) => {
|
||||||
|
player_move_or_attack(-1, 0, game, objects);
|
||||||
|
TookTurn
|
||||||
|
}
|
||||||
|
(Key { code: Right, .. }, _, true) => {
|
||||||
|
player_move_or_attack(1, 0, game, objects);
|
||||||
|
TookTurn
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_map(player: &mut Object) -> Map {
|
_ => DidntTakeTurn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_map(objects: &mut Vec<Object>) -> Map {
|
||||||
// Fill the map with unblocked tiles
|
// Fill the map with unblocked tiles
|
||||||
let mut map = vec![vec![Tile::wall(); MAP_HEIGHT as usize]; MAP_WIDTH as usize];
|
let mut map = vec![vec![Tile::wall(); MAP_HEIGHT as usize]; MAP_WIDTH as usize];
|
||||||
|
|
||||||
@ -178,11 +218,11 @@ fn make_map(player: &mut Object) -> Map {
|
|||||||
|
|
||||||
if !failed {
|
if !failed {
|
||||||
create_room(new_room, &mut map);
|
create_room(new_room, &mut map);
|
||||||
|
place_objects(new_room, &map, objects);
|
||||||
let (new_x, new_y) = new_room.center();
|
let (new_x, new_y) = new_room.center();
|
||||||
|
|
||||||
if rooms.is_empty() {
|
if rooms.is_empty() {
|
||||||
player.x = new_x;
|
objects[PLAYER].set_pos(new_x, new_y);
|
||||||
player.y = new_y;
|
|
||||||
} else {
|
} else {
|
||||||
let (prev_x, prev_y) = rooms[rooms.len() - 1].center();
|
let (prev_x, prev_y) = rooms[rooms.len() - 1].center();
|
||||||
|
|
||||||
@ -225,9 +265,71 @@ fn create_v_tunnel(y1: i32, y2: i32, x: i32, map: &mut Map) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn place_objects(room: Rect, map: &Map, objects: &mut Vec<Object>) {
|
||||||
|
let num_monsters = rand::thread_rng().gen_range(0, MAX_ROOM_MONSTERS + 1);
|
||||||
|
|
||||||
|
for _ in 0..num_monsters {
|
||||||
|
let x = rand::thread_rng().gen_range(room.x1 + 1, room.x2);
|
||||||
|
let y = rand::thread_rng().gen_range(room.y1 + 1, room.y2);
|
||||||
|
|
||||||
|
if !is_blocked(x, y, map, objects) {
|
||||||
|
let mut monster = if rand::random::<f32>() < 0.8 {
|
||||||
|
// 80% chance of spawning an orc
|
||||||
|
Object::new(x, y, 'o', "orc", DESATURATED_GREEN, true)
|
||||||
|
} else {
|
||||||
|
// 20% chance of spawning a troll
|
||||||
|
Object::new(x, y, 'T', "troll", DARKER_GREEN, true)
|
||||||
|
};
|
||||||
|
|
||||||
|
monster.alive = true;
|
||||||
|
objects.push(monster);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_blocked(x: i32, y: i32, map: &Map, objects: &[Object]) -> bool {
|
||||||
|
if map[x as usize][y as usize].blocked {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
objects
|
||||||
|
.iter()
|
||||||
|
.any(|object| object.blocks && object.pos() == (x, y))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move by given amount, if the destination is not blocked
|
||||||
|
fn move_by(id: usize, dx: i32, dy: i32, map: &Map, objects: &mut [Object]) {
|
||||||
|
let (x, y) = objects[id].pos();
|
||||||
|
if !is_blocked(x + dx, y + dy, map, objects) {
|
||||||
|
objects[id].set_pos(x + dx, y + dy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn player_move_or_attack(dx: i32, dy: i32, game: &Game, objects: &mut [Object]) {
|
||||||
|
// The coordinates the player is moving to / attacking
|
||||||
|
let x = objects[PLAYER].x + dx;
|
||||||
|
let y = objects[PLAYER].y + dy;
|
||||||
|
|
||||||
|
// Try to find an attackable object there
|
||||||
|
let target_id = objects.iter().position(|object| object.pos() == (x, y));
|
||||||
|
|
||||||
|
// Attack target if found, move otherwise
|
||||||
|
match target_id {
|
||||||
|
Some(target_id) => {
|
||||||
|
println!(
|
||||||
|
"The {} laughs at your puny efforts to attack him!",
|
||||||
|
objects[target_id].name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
move_by(PLAYER, dx, dy, &game.map, objects);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render_all(tcod: &mut Tcod, game: &mut Game, objects: &[Object], fov_recompute: bool) {
|
fn render_all(tcod: &mut Tcod, game: &mut Game, objects: &[Object], fov_recompute: bool) {
|
||||||
if fov_recompute {
|
if fov_recompute {
|
||||||
let player = &objects[0];
|
let player = &objects[PLAYER];
|
||||||
tcod
|
tcod
|
||||||
.fov
|
.fov
|
||||||
.compute_fov(player.x, player.y, TORCH_RADIUS, FOV_LIGHT_WALLS, FOV_ALGO);
|
.compute_fov(player.x, player.y, TORCH_RADIUS, FOV_LIGHT_WALLS, FOV_ALGO);
|
||||||
@ -294,17 +396,15 @@ fn main() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Create player object
|
// Create player object
|
||||||
let player = Object::new(0, 0, '@', WHITE);
|
let mut player = Object::new(0, 0, '@', "player", WHITE, true);
|
||||||
|
player.alive = true;
|
||||||
// // Create an NPC
|
|
||||||
let npc = Object::new(SCREEN_WIDTH / 2 - 5, SCREEN_HEIGHT / 2, '@', YELLOW);
|
|
||||||
|
|
||||||
// List of objects in the world
|
// List of objects in the world
|
||||||
let mut objects = [player, npc];
|
let mut objects = vec![player];
|
||||||
|
|
||||||
let mut game = Game {
|
let mut game = Game {
|
||||||
// Generate map
|
// Generate map
|
||||||
map: make_map(&mut objects[0]),
|
map: make_map(&mut objects),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Populate FOV map, according to generated map
|
// Populate FOV map, according to generated map
|
||||||
@ -325,15 +425,24 @@ fn main() {
|
|||||||
while !tcod.root.window_closed() {
|
while !tcod.root.window_closed() {
|
||||||
tcod.con.clear();
|
tcod.con.clear();
|
||||||
|
|
||||||
let fov_recompute = previous_player_position != (objects[0].x, objects[0].y);
|
let fov_recompute = previous_player_position != (objects[PLAYER].pos());
|
||||||
render_all(&mut tcod, &mut game, &objects, fov_recompute);
|
render_all(&mut tcod, &mut game, &objects, fov_recompute);
|
||||||
tcod.root.flush();
|
tcod.root.flush();
|
||||||
|
|
||||||
let player = &mut objects[0];
|
previous_player_position = objects[PLAYER].pos();
|
||||||
previous_player_position = (player.x, player.y);
|
let player_action = handle_keys(&mut tcod, &game, &mut objects);
|
||||||
let exit = handle_keys(&mut tcod, &game, player);
|
if player_action == PlayerAction::Exit {
|
||||||
if exit {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Let monsters take their turn
|
||||||
|
if objects[PLAYER].alive && player_action != PlayerAction::DidntTakeTurn {
|
||||||
|
for object in &objects {
|
||||||
|
// ONly if object is not player
|
||||||
|
if (object as *const _) != (&objects[PLAYER] as *const _) {
|
||||||
|
println!("The {} growls!", object.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user