act/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
//!
//! Act is a simple engine for making simple, text-based adventure games.
//!
//! It's on [crates.io](https://crates.io/crates/act) and on [GitHub](https://github.com/ichy-wayland/act)!
//!
//! # Examples
//!
//! ```rust
//! extern crate act;
//!
//! use act::load_game;
//!
//! fn main() {
//! // Create a string containing our Act game
//! let game_string = r#"
//! {
//! "rooms": [
//! {
//! "name": "start",
//! "scene": "Im a starting room! Welcome to this example game.",
//! "actions": [
//! {
//! "variant": "Move",
//! "fields": [
//! "Move to another room","example",""
//! ]
//! }
//! ]
//! },
//! {
//! "name": "example",
//! "scene": "You enter an example room, with a big, triangular key in it. Theres also a door with a keyhole in triangular shape.",
//! "actions": [
//! {
//! "variant": "PickUp",
//! "fields": [
//! "Pick the key up","TriangleKey",""
//! ]
//! },
//! {
//! "variant": "Move",
//! "fields": [
//! "Try to open the door","locked","TriangleKey"
//! ]
//! }
//! ]
//! },
//! {
//! "name": "locked",
//! "scene": "You picked an item up and used it to open the door! This is the final room. Congratz!",
//! "actions": [
//! {
//! "variant": "Move",
//! "fields": [
//! "Move to another room","example",""
//! ]
//! }
//! ]
//! }
//! ]
//! }
//! "#;
//! // Load the game into a proper Game struct
//! let mut game = load_game(game_string).unwrap();
//! // Start the game
//! game.play();
//! // Profit!
//! }
//! ```
//!
extern crate rustc_serialize;
extern crate ansi_escapes;
use std::io;
use std::collections::HashMap;
use std::{thread, time};
use rustc_serialize::json;
macro_rules! try_opt_res {
($o:expr) => {
match $o {
Ok(x) => x,
Err(_) => return None
}
}
}
///
///An Action is composed of three strings.
///The first one is the text that will be shown to the user.
///The second is what the action will do. For example, in a PickUp action, it would be the item that would be given to the user.
///The third one is the requirement. This will check if the user has the item specified, and only if true will proceed.
///# Examples
///```
///use act::Action;
///
///fn main() {
/// let move_to_locked = Action::Move("Unlock the door ","locked_room","LockedRoomKey");
///}
///```
///
#[derive(Clone,Debug)]
pub enum Action {
PickUp(String,String,String),
Move(String,String,String)
}
impl Action {
pub fn text(&self) -> String {
match &self {
&&Action::PickUp(ref s,_,_) => s.clone(),
&&Action::Move(ref s,_,_) => s.clone()
}
}
}
#[derive(Clone)]
pub struct Room {
pub scene: String,
pub actions: Vec<Action>
}
pub struct Character {
inventory: HashMap<String,String>,
room: String
}
pub struct Game {
rooms: HashMap<String,Room>,
character: Character
}
impl Game {
pub fn action(&mut self, a: Action) {
match a {
Action::PickUp(_,i,r) => {
if self.character.inventory.contains_key(&r) || &r == "" {
self.character.inventory.insert(i.clone(),i);
}
},
Action::Move(_,r,i) => {
if self.character.inventory.contains_key(&i) || &i == "" {
self.character.room = r;
self.clear();
} else {
println!("For opening this room, you need to have a {}",i);
}
}
}
}
pub fn render_room(&mut self,r: Room) {
for c in r.scene.lines() {
println!("{}",c);
}
println!("");
for (i,a) in r.actions.iter().enumerate() {
println!("{}. {}\n",i,a.text());
}
println!("");
}
pub fn clear(&self) {
print!("{}",ansi_escapes::ClearScreen);
}
pub fn play(&mut self) {
println!("Made with \n");
print!("
_/ _// _//! _//!//!
_/ // _// _// _//
_/ _// _// _//
_// _// _// _//
_//!//! _// _// _//
_// _// _// _// _//
_// _// _//!/ _//
\n");
println!("Make your own game at github.com/ichy-wayland/act");
thread::sleep(time::Duration::from_millis(4000));
self.clear();
'outer: loop {
let rooms = self.rooms.clone();
let r = rooms.get(&self.character.room).unwrap();
self.render_room(r.clone());
let mut s = String::new();
io::stdin().read_line(&mut s).unwrap();
match s.chars().nth(0).unwrap().to_digit(10) {
Some(u) => {
let a = match r.actions.iter().nth(u as usize) {
Some(x) => x,
None => { continue 'outer; }
};
self.action(a.clone());
// self.character.on_action(a);
},
None => { continue 'outer }
};
}
}
}
pub fn load_game(s: &str) -> Result<Game,json::DecoderError> {
use raw::*;
let rg: RawGame = try!(json::decode(&s));
Ok(rg.process())
}
mod raw {
use std::collections::HashMap;
use super::*;
#[derive(Clone,RustcDecodable)]
pub struct RawGame {
rooms: Vec<RawRoom>
}
impl RawGame {
pub fn process(&self) -> Game {
use std::io;
let mut r_hash: HashMap<String,Room> = HashMap::new();
for r in self.rooms.clone() {
r_hash.insert(r.name.clone(),Room {
scene: r.scene.clone(),
actions: r.process_actions()
});
}
let character = Character {
inventory: HashMap::new(),
room: "start".into()
};
Game {
// out: io::stdout(),
rooms: r_hash,
character: character
}
}
}
#[derive(Clone,RustcDecodable)]
pub struct RawAction {
pub variant: String,
fields: Vec<String>
}
impl RawAction {
pub fn process(&self) -> Action {
match &*self.variant {
"PickUp" => {
Action::PickUp(self.fields[0].clone(),self.fields[1].clone(),self.fields[2].clone())
},
"Move" => { Action::Move(self.fields[0].clone(),self.fields[1].clone(),self.fields[2].clone()) },
_ => { panic!("Could not parse Action!") }
}
}
}
#[derive(Clone,RustcDecodable)]
pub struct RawRoom {
pub name: String,
pub scene: String,
pub actions: Vec<RawAction>
}
impl RawRoom {
pub fn process_actions(&self) -> Vec<Action> {
let mut actions: Vec<Action> = Vec::new();
for a in self.actions.clone() {
actions.push(a.process())
}
actions
}
}
}