use crate::rules::PiratesRules;
use crate::rules::*;
use rand::Rng;
use std::fs::{self, File};
use std::time::SystemTime;
use std::{env, io::Read};
use weasel::creature::CreatureId;
use weasel::team::TeamId;
use weasel::{
ActivateAbility, AlterStatistics, Battle, BattleController, BattleState, Character,
CreateCreature, CreateTeam, EndBattle, EndTurn, EntityId, EventKind, EventQueue, EventReceiver,
EventTrigger, EventWrapper, FlatVersionedEvent, RemoveCreature, ResetEntropy, Server,
StartTurn,
};
const PLAYER_TEAM: &str = "player";
const ENEMY_TEAM: &str = "enemy";
static PLAYER_SHIP: CreatureId<PiratesRules> = 0;
static ENEMY_SHIP: CreatureId<PiratesRules> = 1;
pub struct Game {
server: Server<PiratesRules>,
}
impl Game {
pub fn new() -> Self {
let battle = Battle::builder(PiratesRules::new())
.event_callback(Box::new(commentary))
.build();
let mut server = Server::builder(battle).build();
let time = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap();
ResetEntropy::trigger(&mut server)
.seed(time.as_secs())
.fire()
.unwrap();
CreateTeam::trigger(&mut server, PLAYER_TEAM.to_string())
.objectives_seed(ENEMY_TEAM.to_string())
.fire()
.unwrap();
CreateCreature::trigger(&mut server, PLAYER_SHIP, PLAYER_TEAM.to_string(), ())
.fire()
.unwrap();
CreateTeam::trigger(&mut server, ENEMY_TEAM.to_string())
.objectives_seed(PLAYER_TEAM.to_string())
.fire()
.unwrap();
CreateCreature::trigger(&mut server, ENEMY_SHIP, ENEMY_TEAM.to_string(), ())
.fire()
.unwrap();
Self { server }
}
pub fn fire_cannonball(&mut self) {
StartTurn::trigger(&mut self.server, EntityId::Creature(PLAYER_SHIP))
.fire()
.unwrap();
ActivateAbility::trigger(
&mut self.server,
EntityId::Creature(PLAYER_SHIP),
ABILITY_CANNONBALL.to_string(),
)
.activation(EntityId::Creature(ENEMY_SHIP))
.fire()
.unwrap();
EndTurn::trigger(&mut self.server).fire().unwrap();
}
pub fn fire_grapeshot(&mut self) {
StartTurn::trigger(&mut self.server, EntityId::Creature(PLAYER_SHIP))
.fire()
.unwrap();
ActivateAbility::trigger(
&mut self.server,
EntityId::Creature(PLAYER_SHIP),
ABILITY_GRAPESHOT.to_string(),
)
.activation(EntityId::Creature(ENEMY_SHIP))
.fire()
.unwrap();
EndTurn::trigger(&mut self.server).fire().unwrap();
}
pub fn enemy_turn(&mut self) {
StartTurn::trigger(&mut self.server, EntityId::Creature(ENEMY_SHIP))
.fire()
.unwrap();
let mut rng = rand::thread_rng();
let rng_number = rng.gen_range(0, 2);
let ability = if rng_number == 0 {
ABILITY_CANNONBALL
} else {
ABILITY_GRAPESHOT
};
ActivateAbility::trigger(
&mut self.server,
EntityId::Creature(ENEMY_SHIP),
ability.to_string(),
)
.activation(EntityId::Creature(PLAYER_SHIP))
.fire()
.unwrap();
EndTurn::trigger(&mut self.server).fire().unwrap();
}
pub fn save(&mut self) {
let events: Vec<FlatVersionedEvent<_>> = self
.server
.battle()
.versioned_events(std::ops::Range {
start: 0,
end: self.server.battle().history().len() as usize,
})
.map(|e| e.into())
.collect();
let json = serde_json::to_string(&events).unwrap();
let mut path = env::temp_dir();
path.push("savegame");
fs::write(path, json).unwrap();
println!("game saved!");
}
pub fn load(&mut self) {
let mut json = String::new();
let mut path = env::temp_dir();
path.push("savegame");
let file = File::open(path);
match file {
Ok(mut file) => {
file.read_to_string(&mut json).unwrap();
let events: Vec<FlatVersionedEvent<_>> = serde_json::from_str(&json).unwrap();
let battle = Battle::builder(PiratesRules::new()).build();
self.server = Server::builder(battle).build();
for event in events {
self.server.receive(event.into()).unwrap();
}
self.server.set_event_callback(Some(Box::new(commentary)));
println!("savegame loaded!");
}
Err(_) => println!("no savegame found!"),
}
}
fn ship_stats(&self, id: CreatureId<PiratesRules>) -> (i16, i16) {
let creature = self.server.battle().entities().creature(&id);
match creature {
Some(creature) => (
creature.statistic(&STAT_HULL).unwrap().value(),
creature.statistic(&STAT_CREW).unwrap().value(),
),
None => (0, 0),
}
}
pub fn player_stats(&self) -> (i16, i16) {
self.ship_stats(PLAYER_SHIP)
}
pub fn enemy_stats(&self) -> (i16, i16) {
self.ship_stats(ENEMY_SHIP)
}
pub fn check_winner(&mut self) -> bool {
let winner: Vec<_> = self.server.battle().entities().victorious_id().collect();
if !winner.is_empty() {
println!("{} won!", pretty_team_id(&winner[0]));
EndBattle::trigger(&mut self.server).fire().unwrap();
true
} else {
false
}
}
}
fn commentary(
event: &EventWrapper<PiratesRules>,
_: &BattleState<PiratesRules>,
_: &mut Option<EventQueue<PiratesRules>>,
) {
match event.kind() {
EventKind::AlterStatistics => {
let event: &AlterStatistics<PiratesRules> =
match event.as_any().downcast_ref::<AlterStatistics<_>>() {
Some(e) => e,
None => panic!("incorrect cast!"),
};
let (hull_damage, crew_damage) = event.alteration();
if *hull_damage != 0 {
println!(
"{} took {} hull damage!",
pretty_creature_id(&event.id().creature().unwrap()),
hull_damage
);
}
if *crew_damage != 0 {
println!(
"{} took {} crew damage!",
pretty_creature_id(&event.id().creature().unwrap()),
crew_damage
);
}
}
EventKind::RemoveCreature => {
let event: &RemoveCreature<PiratesRules> =
match event.as_any().downcast_ref::<RemoveCreature<_>>() {
Some(e) => e,
None => panic!("incorrect cast!"),
};
println!("{} destroyed!", pretty_creature_id(event.id()));
}
_ => {} }
}
fn pretty_creature_id(id: &CreatureId<PiratesRules>) -> &'static str {
if *id == PLAYER_SHIP {
"Player ship"
} else {
"Enemy ship"
}
}
fn pretty_team_id(id: &TeamId<PiratesRules>) -> &'static str {
if id == PLAYER_TEAM {
"Player"
} else {
"Enemy"
}
}