Skip to content

Commit

Permalink
added line-of-sight falloff and some more features (maybe)
Browse files Browse the repository at this point in the history
  • Loading branch information
Addison Schuhardt committed Sep 5, 2017
1 parent 36825bc commit b6b6f13
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 66 deletions.
7 changes: 2 additions & 5 deletions src/game/actor.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::collections::VecDeque;
use mopa;
use uuid::Uuid;
use super::{Drawable, Message, Movable, Positioned};
use super::{Drawable, Message, Map, Movable, Positioned};
use super::actors::*;

/// Dictates which set of behavior patterns the actor will exhibit
Expand Down Expand Up @@ -58,14 +58,11 @@ impl ActorInfo {
////
///Examples of actors include enemies, NPCs, creatures, etc.
pub trait Actor: mopa::Any + Drawable + Movable + Positioned {
/// Initializes the actor and returns its new ID
fn init(&mut self, position: [i32; 2], behavior: BehaviorStyle) -> Result<Uuid, String>;

/// Called when the object is created, after it is initialized
fn on_create(&mut self);

/// Called on each update tick
fn on_update(&mut self, actors: &[ActorInfo]);
fn on_update(&mut self, map: &Map, actors: &[ActorInfo]);

/// Called when interacted with by another Actor
fn on_interact(&mut self, actors: &[ActorInfo]);
Expand Down
6 changes: 1 addition & 5 deletions src/game/actors/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,18 +163,14 @@ impl Drawable for Player {
}

impl Actor for Player {
fn init(&mut self, _: [i32; 2], _: BehaviorStyle) -> Result<Uuid, String> {
Ok(self.id)
}

fn on_create(&mut self) {
self.messages.push_back(Message {
contents: String::from("Welcome!"),
message_type: MessageType::Background,
});
}

fn on_update(&mut self, _: &[ActorInfo]) {}
fn on_update(&mut self, _: &Map, _: &[ActorInfo]) {}

fn on_interact(&mut self, _: &[ActorInfo]) {}

Expand Down
8 changes: 2 additions & 6 deletions src/game/actors/soldier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use bresenham::Bresenham;
use game::actor::{Actor, ActorStatus, ActorType, ActorInfo, BehaviorStyle};
use game;
use game::message::MessageType;
use game::{Message, Movable, MovementDirection, Drawable, Positioned, SpriteInfo};
use game::{Message, Movable, Map, MovementDirection, Drawable, Positioned, SpriteInfo};

/// The default number of spaces that the player moves at once.
pub const MOVEMENT_AMOUNT: i32 = 1;
Expand Down Expand Up @@ -64,18 +64,14 @@ impl Drawable for Soldier {
}

impl Actor for Soldier {
fn init(&mut self, _: [i32; 2], _: BehaviorStyle) -> Result<Uuid, String> {
Ok(self.id)
}

fn on_create(&mut self) {
self.messages.push_back(Message {
contents: format!("Soldier was created! ID: {}", self.id),
message_type: MessageType::Danger,
});
}

fn on_update(&mut self, actors: &[ActorInfo]) {
fn on_update(&mut self, _: &Map, actors: &[ActorInfo]) {
if let Some(player) = actors.iter().find(|a| a.actor_type == ActorType::Player) {
let self_pos = (self.position[0], self.position[1]);
let player_pos = (player.position[0], player.position[1]);
Expand Down
16 changes: 11 additions & 5 deletions src/game/entity.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
/// Stores information pertaining to a single entity in the game's
/// current state.
////
/// Examples of entities include interactive objects, doors, ladders, etc.
use mopa;
use super::{Movable, Drawable, Map, Positioned};
use super::actor::ActorInfo;

pub struct Entity {}
/// Things that implement Entity are those which exist in the world but are not
/// "alive".
////
/// Examples include static props and areas of effect.
pub trait Entity: mopa::Any + Movable + Positioned + Drawable {
fn on_create(&mut self, map: &Map, actors: &[ActorInfo]);
}
mopafy!(Entity);
70 changes: 52 additions & 18 deletions src/game/game_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ use uuid::Uuid;
use piston::input::GenericEvent;
use bresenham::Bresenham;
use status::ControllerStatus;
use super::{Drawable, Movable, Positioned, GameState, MapBuilder, SpriteInfo};
use super::{Drawable, Positioned, Map, GameState, MapBuilder, SpriteInfo};
use super::actor;
use super::actor::{Actor, ActorStatus, ActorInfo};
use super::actors::player::Player;
use super::message::Message;

const SPRITE_KEY_VOID: &'static str = "void";
const MAX_VISIBLE_DISTANCE: u32 = 8;
const VISIBILITY_FALLOFF: u32 = 5;

/// Stores and updates the game's current state.
pub struct GameController {
Expand All @@ -25,6 +27,12 @@ enum Action {
Spawn(Box<Actor>),
}

enum Visibility {
Full,
Half,
Invisible,
}

impl GameController {
/// Creates and returns an instance of the GameController struct with
/// a default state.
Expand All @@ -47,7 +55,7 @@ impl GameController {
}

/// Returns the sprite key for the tile at the specified position
pub fn tile_sprite_at(&self, position: [i32; 2]) -> Result<SpriteInfo, String> {
pub fn tile_sprite_at(&self, position: [i32; 2]) -> Result<Vec<SpriteInfo>, String> {
if let Some(tile) = self.state.map.get_at(position) {
Ok(self.get_sprite_at_distance(
tile.current_position(),
Expand Down Expand Up @@ -106,9 +114,11 @@ impl GameController {
pub fn actor_sprites(&self) -> Vec<(SpriteInfo, [i32; 2])> {
let mut sprite_positions = Vec::<(SpriteInfo, [i32; 2])>::new();
for actor in self.state.actors.values() {
let sprite =
let sprites =
self.get_sprite_at_distance(actor.current_position(), actor.sprite_components());
sprite_positions.push((sprite, actor.current_position()));
for sprite in sprites {
sprite_positions.push((sprite, actor.current_position()));
}
}
sprite_positions
}
Expand Down Expand Up @@ -168,7 +178,7 @@ impl GameController {
// update actors
for actor in &mut self.state.actors.values_mut() {
// update
actor.on_update(&actor_info);
actor.on_update(&self.state.map, &actor_info);

// retrieve messages
if let Some(messages) = actor.messages() {
Expand Down Expand Up @@ -201,37 +211,61 @@ impl GameController {
}
}

fn get_sprite_at_distance(&self, position: [i32; 2], sprite: SpriteInfo) -> SpriteInfo {
if !self.within_player_view(position) {
return SpriteInfo {
key: SPRITE_KEY_VOID,
color: [0.0; 4],
};
fn get_sprite_at_distance(&self, position: [i32; 2], sprite: SpriteInfo) -> Vec<SpriteInfo> {
match self.within_player_view(position) {
Visibility::Full => vec!(sprite),
Visibility::Half => vec!(
sprite,
SpriteInfo { key: SPRITE_KEY_VOID, color: [0.0, 0.0, 0.0, 0.5] },
),
Visibility::Invisible => vec!(
SpriteInfo { key: SPRITE_KEY_VOID, color: [0.0; 4] },
),
}
sprite
}

fn within_player_view(&self, position: [i32; 2]) -> bool {
self.ray_visible(self.player_position, position)
fn within_player_view(&self, position: [i32; 2]) -> Visibility {
let (unobscured, distance) =
GameController::ray_visible(&self.state.map, self.player_position, position);

if !unobscured || distance > MAX_VISIBLE_DISTANCE{
Visibility::Invisible
} else if distance > MAX_VISIBLE_DISTANCE - VISIBILITY_FALLOFF &&
distance <= MAX_VISIBLE_DISTANCE {
Visibility::Half
} else {
Visibility::Full
}
}

fn ray_visible(&self, origin: [i32; 2], target: [i32; 2]) -> bool {
/// Returns a tuple indicating both whether the view from the origin to the
/// target is uninterrupted, and also the distance between the two.
fn ray_visible(map: &Map, origin: [i32; 2], target: [i32; 2]) -> (bool, u32) {
use super::tile::TileType;
let origin_point = (origin[0], origin[1]);
let target_point = (target[0], target[1]);

if origin == target {
return (true, 0);
}

// first iteration will be on starting position, and will increment this to 0.
let mut distance = -1i32;

let mut ray = Bresenham::new(origin_point, target_point);

while let Some(coords) = ray.next() {
if let Some(ray_node) = self.state.map.get_at([coords.0, coords.1]) {
distance += 1;
if let Some(ray_node) = map.get_at([coords.0, coords.1]) {
match ray_node.tile_type {
// visibility means not being blocked by a wall
TileType::Wall(_, _) => {
return false;
return (false, distance as u32);
}
_ => {}
}
}
}
true
(true, distance as u32)
}
}
82 changes: 57 additions & 25 deletions src/game/game_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,32 +35,21 @@ impl GameView {
.unwrap_or_else(|| panic!("Could not get the viewport!"))
.rect;

self.draw_tiles(screen_rect, controller, c, g);
self.draw_actors(screen_rect, controller, c, g);
self.draw_messages(screen_rect, controller, c, g);
}

fn draw_actors(
&mut self,
screen_rect: [i32; 4],
controller: &GameController,
c: &Context,
g: &mut GlGraphics,
) {
let tile_w = screen_rect[2] as f64 / MAP_WIDTH as f64;
let tile_h = screen_rect[3] as f64 / MAP_HEIGHT as f64;

// draw tiles
for x in 0..MAP_WIDTH {
for y in 0..MAP_HEIGHT {
match controller.tile_sprite_at([x as i32, y as i32]) {
Ok(sprite) => {
self.tm.draw_at(
[
x as f64 * tile_w,
y as f64 * tile_h,
tile_w + 1.0,
tile_h + 1.0,
],
sprite.key,
sprite.color,
c.transform,
g,
);
}
Err(why) => error!("{:?}", why),
};
}
}

// draw actors (this includes the player)
for (sprite, position) in controller.actor_sprites() {
self.tm.draw_at(
Expand All @@ -76,12 +65,55 @@ impl GameView {
g,
);
}
}

fn draw_tiles(
&mut self,
screen_rect: [i32; 4],
controller: &GameController,
c: &Context,
g: &mut GlGraphics,
) {
let tile_w = screen_rect[2] as f64 / MAP_WIDTH as f64;
let tile_h = screen_rect[3] as f64 / MAP_HEIGHT as f64;

// draw tiles
for x in 0..MAP_WIDTH {
for y in 0..MAP_HEIGHT {
match controller.tile_sprite_at([x as i32, y as i32]) {
Ok(sprites) => {
for sprite in sprites {
self.tm.draw_at(
[
x as f64 * tile_w,
y as f64 * tile_h,
tile_w + 1.0,
tile_h + 1.0,
],
sprite.key,
sprite.color,
c.transform,
g,
);
}
}
Err(why) => error!("{:?}", why),
};
}
}
}

// draw message
fn draw_messages(
&mut self,
screen_rect: [i32; 4],
controller: &GameController,
c: &Context,
g: &mut GlGraphics,
) {
if controller.should_show_messages() {
// background
let screen_w = screen_rect[2] as f64;
let screen_h = screen_rect[3] as f64;
let tile_w = screen_rect[2] as f64 / MAP_WIDTH as f64;

let line_height = self.text_renderer.line_height(FontSize::Size24) as f64;

Expand Down
4 changes: 2 additions & 2 deletions src/game/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ pub struct GameState {
pub actors: HashMap<Uuid, Box<Actor>>,

/// The Entities (interactive objects, terrain, etc.) currently in the map.
pub entities: HashMap<Uuid, Entity>,
pub entities: HashMap<Uuid, Box<Entity>>,

/// The items currently present in the map.
pub items: HashMap<Uuid, Item>,
Expand Down Expand Up @@ -196,7 +196,7 @@ impl GameState {
player_id: Uuid::new_v4(),
map: map_builder.create(),
actors: HashMap::<Uuid, Box<Actor>>::new(),
entities: HashMap::<Uuid, Entity>::new(),
entities: HashMap::<Uuid, Box<Entity>>::new(),
items: HashMap::<Uuid, Item>::new(),
messages: VecDeque::<Message>::new(),
show_messages: true,
Expand Down

0 comments on commit b6b6f13

Please sign in to comment.