elseware/src/ship/room.rs

253 lines
7.0 KiB
Rust
Raw Normal View History

2020-06-05 22:10:53 -06:00
use std::collections::HashMap;
2020-01-02 20:33:37 -08:00
use std::convert::{From, Into, TryFrom, TryInto};
2020-04-21 07:20:25 -06:00
use rand::Rng;
use crate::ship::map::Maps;
2020-04-06 23:42:38 -07:00
use crate::ship::drops::DropTable;
use crate::entity::character::SectionID;
2020-06-05 22:10:53 -06:00
use crate::ship::monster::{load_monster_stats_table, MonsterType, MonsterStats};
use crate::ship::map::area::MapAreaLookup;
use crate::ship::map::enemy::RareMonsterAppearTable;
2020-01-02 20:33:37 -08:00
#[derive(Debug)]
pub enum RoomCreationError {
InvalidMode,
InvalidEpisode(u8),
InvalidDifficulty(u8),
CouldNotLoadMonsterStats(RoomMode),
2020-01-02 20:33:37 -08:00
}
2020-03-14 10:44:27 -07:00
#[derive(Debug, Copy, Clone, derive_more::Display)]
2020-01-02 20:33:37 -08:00
pub enum Episode {
2020-03-14 10:44:27 -07:00
#[display(fmt="ep1")]
2020-01-02 20:33:37 -08:00
One,
2020-03-14 10:44:27 -07:00
#[display(fmt="ep2")]
2020-01-02 20:33:37 -08:00
Two,
2020-03-14 10:44:27 -07:00
#[display(fmt="ep4")]
2020-01-02 20:33:37 -08:00
Four,
}
impl TryFrom<u8> for Episode {
type Error = RoomCreationError;
fn try_from(value: u8) -> Result<Episode, RoomCreationError> {
match value {
2020-02-20 22:57:50 -08:00
1 => Ok(Episode::One),
2 => Ok(Episode::Two),
3 => Ok(Episode::Four),
2020-01-02 20:33:37 -08:00
_ => Err(RoomCreationError::InvalidEpisode(value))
}
}
}
2021-06-18 17:38:36 -06:00
impl From<Episode> for u8 {
fn from(other: Episode) -> u8 {
match other {
2020-01-02 20:33:37 -08:00
Episode::One => 1,
Episode::Two => 2,
Episode::Four => 3,
}
}
}
2020-05-24 15:59:48 -06:00
impl Episode {
pub fn from_quest(value: u8) -> Result<Episode, RoomCreationError> {
match value {
0 => Ok(Episode::One),
1 => Ok(Episode::Two),
2 => Ok(Episode::Four),
_ => Err(RoomCreationError::InvalidEpisode(value))
}
}
}
2020-09-27 18:16:27 -06:00
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, derive_more::Display)]
2020-01-02 20:33:37 -08:00
pub enum Difficulty {
Normal,
Hard,
VeryHard,
Ultimate,
}
impl TryFrom<u8> for Difficulty {
type Error = RoomCreationError;
fn try_from(value: u8) -> Result<Difficulty, RoomCreationError> {
match value {
0 => Ok(Difficulty::Normal),
1 => Ok(Difficulty::Hard),
2 => Ok(Difficulty::VeryHard),
3 => Ok(Difficulty::Ultimate),
_ => Err(RoomCreationError::InvalidDifficulty(value))
}
}
}
2021-06-18 17:38:36 -06:00
impl From<Difficulty> for u8 {
fn from(other: Difficulty) -> u8 {
match other {
2020-01-02 20:33:37 -08:00
Difficulty::Normal => 0,
Difficulty::Hard => 1,
Difficulty::VeryHard => 2,
Difficulty::Ultimate => 3,
}
}
}
2020-03-18 01:36:46 -03:00
#[derive(Debug, Copy, Clone)]
2020-01-02 20:33:37 -08:00
pub enum RoomMode {
Single {
episode: Episode,
difficulty: Difficulty,
},
Multi {
episode: Episode,
difficulty: Difficulty,
},
Challenge {
episode: Episode,
},
Battle {
episode: Episode,
difficulty: Difficulty,
}
}
2020-02-20 23:02:25 -08:00
impl RoomMode {
2020-03-17 08:02:17 -03:00
pub fn difficulty(&self) -> Difficulty {
2020-02-20 23:02:25 -08:00
match self {
RoomMode::Single {difficulty, ..} => *difficulty,
RoomMode::Multi {difficulty, ..} => *difficulty,
RoomMode::Battle {difficulty, ..} => *difficulty,
RoomMode::Challenge {..} => Difficulty::Normal,
}
}
2020-03-17 08:02:17 -03:00
pub fn episode(&self) -> Episode {
2020-02-20 23:02:25 -08:00
match self {
RoomMode::Single {episode, ..} => *episode,
RoomMode::Multi {episode, ..} => *episode,
RoomMode::Battle {episode, ..} => *episode,
RoomMode::Challenge {episode, ..} => *episode,
}
}
pub fn battle(&self) -> u8 {
match self {
RoomMode::Battle {..} => 1,
_ => 0,
}
}
pub fn challenge(&self) -> u8 {
match self {
RoomMode::Challenge {..} => 1,
_ => 0,
}
}
pub fn single_player(&self) -> u8 {
match self {
RoomMode::Single {..} => 1,
_ => 0,
}
}
2020-02-20 23:02:25 -08:00
}
2020-01-02 20:33:37 -08:00
pub struct RoomState {
2020-03-17 08:02:17 -03:00
pub mode: RoomMode,
2020-01-18 23:49:15 -08:00
pub name: String,
2020-03-17 08:02:17 -03:00
pub password: [u16; 16],
pub maps: Maps,
pub drop_table: Box<DropTable<rand_chacha::ChaCha20Rng>>,
2020-04-21 07:20:25 -06:00
pub section_id: SectionID,
pub random_seed: u32,
pub bursting: bool,
2020-06-05 22:10:53 -06:00
pub monster_stats: Box<HashMap<MonsterType, MonsterStats>>,
pub map_areas: MapAreaLookup,
pub rare_monster_table: Box<RareMonsterAppearTable>,
2020-01-02 20:33:37 -08:00
// items on ground
// enemy info
}
impl RoomState {
pub fn get_flags_for_room_list(&self) -> u8 {
2020-03-18 01:36:46 -03:00
let mut flags = 0u8;
match self.mode {
2020-03-18 01:36:46 -03:00
RoomMode::Single {..} => {flags += 0x04}
RoomMode::Battle {..} => {flags += 0x10},
RoomMode::Challenge {..} => {flags += 0x20},
_ => {flags += 0x40},
};
if self.password[0] > 0 {
2020-03-18 01:36:46 -03:00
flags += 0x02;
}
flags
}
pub fn get_episode_for_room_list(&self) -> u8 {
let episode: u8 = self.mode.episode().into();
2020-03-18 01:36:46 -03:00
match self.mode {
RoomMode::Single {..} => episode + 0x10,
_ => episode + 0x40,
2020-03-18 01:36:46 -03:00
}
}
pub fn get_difficulty_for_room_list(&self) -> u8 {
let difficulty: u8 = self.mode.difficulty().into();
difficulty + 0x22
2020-03-18 01:36:46 -03:00
}
2020-04-06 23:42:38 -07:00
pub fn from_create_room(create_room: &libpso::packet::ship::CreateRoom, section_id: SectionID) -> Result<RoomState, RoomCreationError> {
2020-01-02 20:33:37 -08:00
if [create_room.battle, create_room.challenge, create_room.single_player].iter().sum::<u8>() > 1 {
return Err(RoomCreationError::InvalidMode)
}
let room_mode = if create_room.battle == 1 {
RoomMode::Battle {
episode: create_room.episode.try_into()?,
difficulty: create_room.difficulty.try_into()?,
}
}
else if create_room.challenge == 1 {
RoomMode::Challenge {
episode: create_room.episode.try_into()?,
}
}
else if create_room.single_player == 1 {
RoomMode::Single {
episode: create_room.episode.try_into()?,
difficulty: create_room.difficulty.try_into()?,
}
}
else { // normal multimode
RoomMode::Multi {
episode: create_room.episode.try_into()?,
difficulty: create_room.difficulty.try_into()?,
}
};
let rare_monster_table = RareMonsterAppearTable::new(room_mode.episode());
2020-01-02 20:33:37 -08:00
Ok(RoomState {
2021-06-18 17:38:36 -06:00
monster_stats: Box::new(load_monster_stats_table(&room_mode).map_err(|_| RoomCreationError::CouldNotLoadMonsterStats(room_mode))?),
2020-01-02 20:33:37 -08:00
mode: room_mode,
random_seed: rand::thread_rng().gen(),
rare_monster_table: Box::new(rare_monster_table.clone()),
name: String::from_utf16_lossy(&create_room.name).trim_matches(char::from(0)).into(),
2020-01-02 20:33:37 -08:00
password: create_room.password,
maps: Maps::new(room_mode, &rare_monster_table), // TODO: rare_monster_table here feels janky. is there some way to call the the RoomState.rare_monster_table we already created?
2021-06-18 20:38:29 -06:00
section_id,
drop_table: Box::new(DropTable::new(room_mode.episode(), room_mode.difficulty(), section_id)),
bursting: false,
map_areas: MapAreaLookup::new(&room_mode.episode()),
2020-01-02 20:33:37 -08:00
})
}
}