Add messages, with global lookup map

Add support for messages, along with a global lookup map and random
choice of messages.
This commit is contained in:
Griffin Smith 2019-07-07 12:41:15 -04:00
parent 78a52142d1
commit c643ee1dfc
10 changed files with 443 additions and 52 deletions

106
Cargo.lock generated
View file

@ -131,6 +131,15 @@ name = "byteorder"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "c2-chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cc"
version = "1.0.37"
@ -296,6 +305,15 @@ name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "getrandom"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "humantime"
version = "1.2.0"
@ -326,6 +344,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "lazy_static"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libc"
@ -384,6 +405,11 @@ dependencies = [
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "maplit"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "memchr"
version = "2.2.0"
@ -472,6 +498,11 @@ dependencies = [
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ppv-lite86"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "prettytable-rs"
version = "0.8.0"
@ -553,6 +584,19 @@ dependencies = [
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"getrandom 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_chacha 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_pcg 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_chacha"
version = "0.1.1"
@ -562,6 +606,16 @@ dependencies = [
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_chacha"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.3.1"
@ -575,6 +629,14 @@ name = "rand_core"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rand_core"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"getrandom 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_hc"
version = "0.1.0"
@ -583,6 +645,14 @@ dependencies = [
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_isaac"
version = "0.1.1"
@ -623,6 +693,15 @@ dependencies = [
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_pcg"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_xorshift"
version = "0.1.1"
@ -805,6 +884,11 @@ dependencies = [
"yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "spin"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "strsim"
version = "0.8.0"
@ -909,6 +993,14 @@ dependencies = [
"serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "toml"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "traitobject"
version = "0.1.0"
@ -997,12 +1089,15 @@ dependencies = [
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log4rs 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)",
"maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"prettytable-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"proptest 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
"proptest-derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)",
"termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1037,6 +1132,7 @@ dependencies = [
"checksum bstr 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc0572e02f76cb335f309b19e0a0d585b4f62788f7d26de2a13a836a637385f"
"checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39"
"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb"
"checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101"
"checksum cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "39f75544d7bbaf57560d2168f28fd649ff9c76153874db88bdbdfd839b1a7e7d"
"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878"
@ -1057,6 +1153,7 @@ dependencies = [
"checksum flate2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f87e68aa82b2de08a6e037f1385455759df6e445a8df5e005b4297191dbf18aa"
"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
"checksum getrandom 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e65cce4e5084b14874c4e7097f38cab54f47ee554f9194673456ea379dcc4c55"
"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114"
"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358"
"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
@ -1068,6 +1165,7 @@ dependencies = [
"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
"checksum log-mdc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7"
"checksum log4rs 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "100052474df98158c0738a7d3f4249c99978490178b5f9f68cd835ac57adbd1b"
"checksum maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08cbb6b4fef96b6d77bfc40ec491b1690c779e77b05cd9f07f787ed376fd4c43"
"checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39"
"checksum miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9e3ae51cea1576ceba0dde3d484d30e6e5b86dee0b2d412fe3a16a15c98202"
"checksum miniz_oxide 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c468f2369f07d651a5d0bb2c9079f8488a66d5466efe42d0c5c6466edcb7f71e"
@ -1079,6 +1177,7 @@ dependencies = [
"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32"
"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
"checksum ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518"
"checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b"
"checksum prettytable-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0fd04b170004fa2daccf418a7f8253aaf033c27760b5f225889024cf66d7ac2e"
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
"checksum proptest 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2afed8cbdc8a64b58a5c021757a782351ec1afee85be374872721c84d5da5d80"
@ -1086,14 +1185,19 @@ dependencies = [
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
"checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db"
"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
"checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c"
"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
"checksum rand_chacha 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e193067942ef6f485a349a113329140d0ab9e2168ce92274499bb0e9a4190d9d"
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0"
"checksum rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "615e683324e75af5d43d8f7a39ffe3ee4a9dc42c5c701167a71dc59c3a493aca"
"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
"checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
"checksum rand_pcg 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e196346cbbc5c70c77e7b4926147ee8e383a38ee4d15d58a08098b169e492b6"
"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
"checksum redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252"
@ -1117,6 +1221,7 @@ dependencies = [
"checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d"
"checksum serde_test 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "110b3dbdf8607ec493c22d5d947753282f3bae73c0f56d322af1e8c78e4c23d5"
"checksum serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)" = "38b08a9a90e5260fe01c6480ec7c811606df6d3a660415808c3c3fa8ed95b582"
"checksum spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44363f6f51401c34e7be73db0db371c04705d35efbe9f7d6082e03a921a32c55"
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
"checksum syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)" = "641e117d55514d6d918490e47102f7e08d096fdde360247e4a10f7a91a8478d3"
"checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f"
@ -1128,6 +1233,7 @@ dependencies = [
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f"
"checksum toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b8c96d7873fa7ef8bdeb3a9cda3ac48389b4154f32b9803b4bc26220b677b039"
"checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079"
"checksum typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6"
"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"

View file

@ -5,17 +5,20 @@ authors = ["Griffin Smith <root@gws.fyi>"]
edition = "2018"
[dependencies]
clap = {version = "^2.33.0", features = ["yaml"]}
config = "*"
itertools = "*"
lazy_static = "*"
log = "*"
log4rs = "*"
maplit = "^1.0.1"
prettytable-rs = "^0.8"
proptest = "0.9.3"
proptest-derive = "*"
rand = {version = "^0.7.0", features = ["small_rng"]}
serde = "^1.0.8"
serde_derive = "^1.0.8"
termion = "*"
clap = {version = "^2.33.0", features = ["yaml"]}
prettytable-rs = "^0.8"
toml = "^0.5.1"
[dev-dependencies]

View file

@ -1,10 +1,12 @@
use crate::display::utils::clone_times;
use crate::display::utils::times;
use crate::types::BoundingBox;
use crate::types::Dimensions;
use itertools::Itertools;
use proptest::prelude::Arbitrary;
use proptest::strategy;
use proptest_derive::Arbitrary;
use std::io::{self, Write};
// Box Drawing
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
@ -164,6 +166,21 @@ pub fn make_box(style: BoxStyle, dims: Dimensions) -> String {
}
}
/// Draw the box described by the given BoundingBox's position and dimensions to
/// the given output, with the given style
pub fn draw_box<W: Write>(
out: &mut W,
bbox: BoundingBox,
style: BoxStyle,
) -> io::Result<()> {
write!(
out,
"{}{}",
bbox.position.cursor_goto(),
make_box(style, bbox.dimensions)
)
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -1,5 +1,8 @@
use super::BoxStyle;
use super::Draw;
use super::{make_box, BoxStyle};
use crate::display::draw_box::draw_box;
use crate::display::utils::clone_times;
use crate::display::utils::times;
use crate::types::{BoundingBox, Position, Positioned};
use std::fmt::{self, Debug};
use std::io::{self, Write};
@ -10,15 +13,39 @@ pub struct Viewport<W> {
/// Generally the size of the terminal, and positioned at 0, 0
pub outer: BoundingBox,
/// The box describing the game part of the viewport.
pub game: BoundingBox,
/// The box describing the inner part of the viewport
///
/// Its position is relative to `outer.inner()`, and its size should generally not
/// be smaller than outer
/// Its position is relative to `outer.inner()`, and its size should
/// generally not be smaller than outer
pub inner: BoundingBox,
/// The actual screen that the viewport writes to
pub out: W,
}
impl<W> Viewport<W> {
pub fn new(outer: BoundingBox, inner: BoundingBox, out: W) -> Self {
Viewport {
outer,
inner,
out,
game: outer.move_tr_corner(Position { x: 0, y: 1 }),
}
}
/// Returns true if the (inner-relative) position of the given entity is
/// visible within this viewport
pub fn visible<E: Positioned>(&self, ent: &E) -> bool {
self.on_screen(ent.position()).within(self.game.inner())
}
/// Convert the given inner-relative position to one on the actual screen
fn on_screen(&self, pos: Position) -> Position {
pos + self.inner.position + self.game.inner().position
}
}
impl<W> Debug for Viewport<W> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@ -30,44 +57,52 @@ impl<W> Debug for Viewport<W> {
}
}
impl<W> Viewport<W> {
/// Returns true if the (inner-relative) position of the given entity is
/// visible within this viewport
fn visible<E: Positioned>(&self, ent: &E) -> bool {
self.on_screen(ent.position()).within(self.outer.inner())
}
/// Convert the given inner-relative position to one on the actual screen
fn on_screen(&self, pos: Position) -> Position {
pos + self.inner.position + self.outer.inner().position
}
}
impl<W: Write> Viewport<W> {
/// Draw the given entity to the viewport at its position, if visible
pub fn draw<T: Draw>(&mut self, entity: &T) -> io::Result<()> {
if !self.visible(entity) {
return Ok(());
}
write!(
self,
"{}",
(entity.position()
+ self.inner.position
+ self.outer.inner().position)
.cursor_goto()
)?;
self.cursor_goto(entity.position())?;
entity.do_draw(self)
}
/// Clear whatever is drawn at the given inner-relative position, if visible
/// Move the cursor to the given inner-relative position
pub fn cursor_goto(&mut self, pos: Position) -> io::Result<()> {
write!(self, "{}", self.on_screen(pos).cursor_goto())
}
/// Clear whatever single character is drawn at the given inner-relative
/// position, if visible
pub fn clear(&mut self, pos: Position) -> io::Result<()> {
write!(self, "{} ", self.on_screen(pos).cursor_goto(),)
}
/// Initialize this viewport by drawing its outer box to the screen
pub fn init(&mut self) -> io::Result<()> {
write!(self, "{}", make_box(BoxStyle::Thin, self.outer.dimensions))
draw_box(self, self.game, BoxStyle::Thin)
}
/// Write a message to the message area on the screen
///
/// Will overwrite any message already present, and if the given message is
/// longer than the screen will truncate. This means callers should handle
/// message buffering and ellipsisization
pub fn write_message(&mut self, msg: &str) -> io::Result<()> {
write!(
self,
"{}{}{}",
self.outer.position.cursor_goto(),
if msg.len() <= self.outer.dimensions.w as usize {
msg
} else {
&msg[0..self.outer.dimensions.w as usize]
},
clone_times::<_, String>(
" ".to_string(),
self.outer.dimensions.w - msg.len() as u16
),
)
}
}
@ -99,24 +134,24 @@ mod tests {
#[test]
fn test_visible() {
assert!(Viewport {
outer: BoundingBox::at_origin(Dimensions { w: 10, h: 10 }),
inner: BoundingBox {
assert!(Viewport::new(
BoundingBox::at_origin(Dimensions { w: 10, h: 10 }),
BoundingBox {
position: Position { x: -10, y: -10 },
dimensions: Dimensions { w: 15, h: 15 },
},
out: (),
}
()
)
.visible(&Position { x: 13, y: 13 }));
assert!(!Viewport {
outer: BoundingBox::at_origin(Dimensions { w: 10, h: 10 }),
inner: BoundingBox {
assert!(!Viewport::new(
BoundingBox::at_origin(Dimensions { w: 10, h: 10 }),
BoundingBox {
position: Position { x: -10, y: -10 },
dimensions: Dimensions { w: 15, h: 15 },
},
out: (),
}
(),
)
.visible(&Position { x: 1, y: 1 }));
}

View file

@ -1,17 +1,21 @@
use crate::display::{self, Viewport};
use crate::entities::Character;
use crate::messages::message;
use crate::settings::Settings;
use crate::types::command::Command;
use crate::types::Positioned;
use crate::types::{BoundingBox, Dimensions, Position};
use rand::rngs::SmallRng;
use rand::SeedableRng;
use std::io::{self, StdinLock, StdoutLock, Write};
use termion::input::Keys;
use termion::input::TermRead;
use termion::raw::RawTerminal;
use crate::display::{self, Viewport};
use crate::entities::Character;
use crate::types::command::Command;
type Stdout<'a> = RawTerminal<StdoutLock<'a>>;
type Rng = SmallRng;
/// The full state of a running Game
pub struct Game<'a> {
settings: Settings,
@ -23,6 +27,12 @@ pub struct Game<'a> {
/// The player character
character: Character,
/// The messages that have been said to the user, in forward time order
messages: Vec<String>,
/// A global random number generator for the game
rng: Rng,
}
impl<'a> Game<'a> {
@ -33,18 +43,21 @@ impl<'a> Game<'a> {
w: u16,
h: u16,
) -> Game<'a> {
let rng = match settings.seed {
Some(seed) => SmallRng::seed_from_u64(seed),
None => SmallRng::from_entropy(),
};
Game {
settings: settings,
viewport: Viewport {
outer: BoundingBox::at_origin(Dimensions { w, h }),
inner: BoundingBox::at_origin(Dimensions {
w: w - 2,
h: h - 2,
}),
out: stdout,
},
settings,
rng,
viewport: Viewport::new(
BoundingBox::at_origin(Dimensions { w, h }),
BoundingBox::at_origin(Dimensions { w: w - 2, h: h - 2 }),
stdout,
),
keys: stdin.keys(),
character: Character::new(),
messages: Vec::new(),
}
}
@ -53,15 +66,29 @@ impl<'a> Game<'a> {
!pos.within(self.viewport.inner)
}
/// Draw all the game entities to the screen
fn draw_entities(&mut self) -> io::Result<()> {
self.viewport.draw(&self.character)
}
/// Get a message from the global map based on the rng in this game
fn message(&mut self, name: &str) -> &'static str {
message(name, &mut self.rng)
}
/// Say a message to the user
fn say(&mut self, message_name: &str) -> io::Result<()> {
let message = self.message(message_name);
self.messages.push(message.to_string());
self.viewport.write_message(message)
}
/// Run the game
pub fn run(mut self) -> io::Result<()> {
info!("Running game");
self.viewport.init()?;
self.draw_entities()?;
self.say("global.welcome")?;
self.flush()?;
loop {
let mut old_position = None;
@ -86,7 +113,7 @@ impl<'a> Game<'a> {
self.viewport.clear(old_pos)?;
self.viewport.draw(&self.character)?;
}
None => ()
None => (),
}
self.flush()?;
debug!("{:?}", self.character);

View file

@ -3,18 +3,26 @@ extern crate termion;
extern crate log;
extern crate config;
extern crate log4rs;
extern crate serde;
extern crate toml;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate clap;
#[macro_use]
extern crate prettytable;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate maplit;
mod display;
mod game;
#[macro_use]
mod types;
mod entities;
mod messages;
mod settings;
use clap::App;

186
src/messages.rs Normal file
View file

@ -0,0 +1,186 @@
use rand::seq::SliceRandom;
use rand::Rng;
use serde::de::MapAccess;
use serde::de::SeqAccess;
use serde::de::Visitor;
use std::collections::HashMap;
use std::fmt;
use std::marker::PhantomData;
#[derive(Deserialize, Debug, PartialEq, Eq)]
#[serde(untagged)]
enum Message<'a> {
Single(&'a str),
Choice(Vec<&'a str>),
}
impl<'a> Message<'a> {
fn resolve<R: Rng + ?Sized>(&self, rng: &mut R) -> Option<&'a str> {
use Message::*;
match self {
Single(msg) => Some(*msg),
Choice(msgs) => msgs.choose(rng).map(|msg| *msg),
}
}
}
#[derive(Debug, PartialEq, Eq)]
enum NestedMap<'a> {
Direct(Message<'a>),
Nested(HashMap<&'a str, NestedMap<'a>>),
}
impl<'a> NestedMap<'a> {
fn lookup(&'a self, path: &str) -> Option<&'a Message<'a>> {
use NestedMap::*;
let leaf =
path.split(".")
.fold(Some(self), |current, key| match current {
Some(Nested(m)) => m.get(key),
_ => None,
});
match leaf {
Some(Direct(msg)) => Some(msg),
_ => None,
}
}
}
struct NestedMapVisitor<'a> {
marker: PhantomData<fn() -> NestedMap<'a>>,
}
impl<'a> NestedMapVisitor<'a> {
fn new() -> Self {
NestedMapVisitor {
marker: PhantomData,
}
}
}
impl<'de> Visitor<'de> for NestedMapVisitor<'de> {
type Value = NestedMap<'de>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str(
"A message, a list of messages, or a nested map of messages",
)
}
fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E> {
Ok(NestedMap::Direct(Message::Single(v)))
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut choices = Vec::with_capacity(seq.size_hint().unwrap_or(0));
while let Some(choice) = seq.next_element()? {
choices.push(choice);
}
Ok(NestedMap::Direct(Message::Choice(choices)))
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut nested = HashMap::with_capacity(map.size_hint().unwrap_or(0));
while let Some((k, v)) = map.next_entry()? {
nested.insert(k, v);
}
Ok(NestedMap::Nested(nested))
}
}
impl<'de> serde::Deserialize<'de> for NestedMap<'de> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_any(NestedMapVisitor::new())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_deserialize_nested_map() {
let src = r#"
[global]
hello = "Hello World!"
[foo.bar]
single = "Single"
choice = ["Say this", "Or this"]
"#;
let result = toml::from_str(src);
assert_eq!(
result,
Ok(NestedMap::Nested(hashmap! {
"global" => NestedMap::Nested(hashmap!{
"hello" => NestedMap::Direct(Message::Single("Hello World!")),
}),
"foo" => NestedMap::Nested(hashmap!{
"bar" => NestedMap::Nested(hashmap!{
"single" => NestedMap::Direct(Message::Single("Single")),
"choice" => NestedMap::Direct(Message::Choice(
vec!["Say this", "Or this"]
))
})
})
}))
)
}
#[test]
fn test_lookup() {
let map: NestedMap<'static> = toml::from_str(
r#"
[global]
hello = "Hello World!"
[foo.bar]
single = "Single"
choice = ["Say this", "Or this"]
"#,
)
.unwrap();
assert_eq!(
map.lookup("global.hello"),
Some(&Message::Single("Hello World!"))
);
assert_eq!(
map.lookup("foo.bar.single"),
Some(&Message::Single("Single"))
);
assert_eq!(
map.lookup("foo.bar.choice"),
Some(&Message::Choice(vec!["Say this", "Or this"]))
);
}
}
static MESSAGES_RAW: &'static str = include_str!("messages.toml");
lazy_static! {
static ref MESSAGES: NestedMap<'static> =
toml::from_str(MESSAGES_RAW).unwrap();
}
/// Look up a game message based on the given (dot-separated) name, with the
/// given random generator used to select from choice-based messages
pub fn message<R: Rng + ?Sized>(name: &str, rng: &mut R) -> &'static str {
use Message::*;
MESSAGES
.lookup(name)
.and_then(|msg| msg.resolve(rng))
.unwrap_or_else(|| {
error!("Message not found: {}", name);
"Message not found"
})
}

2
src/messages.toml Normal file
View file

@ -0,0 +1,2 @@
[global]
welcome = "Welcome to Xanthous! It's dangerous out there, why not stay inside?"

View file

@ -48,6 +48,7 @@ impl Logging {
#[derive(Debug, Deserialize)]
pub struct Settings {
pub seed: Option<u64>,
pub logging: Logging,
}

View file

@ -66,6 +66,12 @@ impl BoundingBox {
pub fn inner(self) -> BoundingBox {
self + UNIT_POSITION - UNIT_DIMENSIONS - UNIT_DIMENSIONS
}
/// Moves the top right corner of the bounding box by the offset specified
/// by the given position, keeping the lower right corner in place
pub fn move_tr_corner(self, offset: Position) -> BoundingBox {
self + offset - Dimensions { w: offset.x as u16, h: offset.y as u16 }
}
}
impl ops::Add<Position> for BoundingBox {