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 } ;
2021-12-28 14:16:05 -07:00
use thiserror ::Error ;
2020-04-21 07:20:25 -06:00
use rand ::Rng ;
2020-02-20 23:03:51 -08:00
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 } ;
2020-08-04 20:43:02 -06:00
use crate ::ship ::map ::area ::MapAreaLookup ;
2021-07-18 18:40:10 +00:00
use crate ::ship ::map ::enemy ::RareMonsterAppearTable ;
2022-01-27 01:07:19 +00:00
use crate ::ship ::quests ;
use std ::path ::PathBuf ;
2020-02-20 23:03:51 -08:00
2021-05-23 18:18:36 +00:00
#[ derive(Debug, Error) ]
#[ error( " " ) ]
2020-01-02 20:33:37 -08:00
pub enum RoomCreationError {
InvalidMode ,
InvalidEpisode ( u8 ) ,
InvalidDifficulty ( u8 ) ,
2020-10-30 22:58:10 -06:00
CouldNotLoadMonsterStats ( RoomMode ) ,
2022-01-27 01:07:19 +00:00
CouldNotLoadQuests ,
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 ,
}
}
}
2022-01-27 01:07:19 +00:00
#[ derive(Debug, Copy, Clone, derive_more::Display) ]
2020-01-02 20:33:37 -08:00
pub enum RoomMode {
2022-01-27 01:07:19 +00:00
#[ display(fmt= " single " ) ]
2020-01-02 20:33:37 -08:00
Single {
episode : Episode ,
difficulty : Difficulty ,
} ,
2022-01-27 01:07:19 +00:00
#[ display(fmt= " multi " ) ]
2020-01-02 20:33:37 -08:00
Multi {
episode : Episode ,
difficulty : Difficulty ,
} ,
2022-01-27 01:07:19 +00:00
#[ display(fmt= " challenge " ) ]
2020-01-02 20:33:37 -08:00
Challenge {
episode : Episode ,
} ,
2022-01-27 01:07:19 +00:00
#[ display(fmt= " battle " ) ]
2020-01-02 20:33:37 -08:00
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 ,
}
}
2020-04-24 20:38:56 -06:00
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 ] ,
2020-02-20 23:03:51 -08:00
pub maps : Maps ,
2020-04-12 14:27:06 -07:00
pub drop_table : Box < DropTable < rand_chacha ::ChaCha20Rng > > ,
2020-04-21 07:20:25 -06:00
pub section_id : SectionID ,
pub random_seed : u32 ,
2020-04-21 08:51:06 -06:00
pub bursting : bool ,
2020-06-05 22:10:53 -06:00
pub monster_stats : Box < HashMap < MonsterType , MonsterStats > > ,
2020-08-04 20:43:02 -06:00
pub map_areas : MapAreaLookup ,
2021-07-18 18:40:10 +00:00
pub rare_monster_table : Box < RareMonsterAppearTable > ,
2022-01-27 01:07:19 +00:00
pub quests : quests ::QuestList ,
2020-01-02 20:33:37 -08:00
// items on ground
// enemy info
}
impl RoomState {
2020-03-18 21:46:13 -03:00
pub fn get_flags_for_room_list ( & self ) -> u8 {
2020-03-18 01:36:46 -03:00
let mut flags = 0 u8 ;
2020-03-18 21:46:13 -03:00
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 } ,
} ;
2020-03-18 21:46:13 -03:00
if self . password [ 0 ] > 0 {
2020-03-18 01:36:46 -03:00
flags + = 0x02 ;
}
flags
}
2020-03-18 21:46:13 -03:00
pub fn get_episode_for_room_list ( & self ) -> u8 {
let episode : u8 = self . mode . episode ( ) . into ( ) ;
2020-03-18 01:36:46 -03:00
2020-03-18 21:46:13 -03:00
match self . mode {
RoomMode ::Single { .. } = > episode + 0x10 ,
_ = > episode + 0x40 ,
2020-03-18 01:36:46 -03:00
}
2020-03-18 21:46:13 -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 ( ) ? ,
}
} ;
2020-02-20 23:03:51 -08:00
2021-07-23 01:45:02 +00:00
let rare_monster_table = RareMonsterAppearTable ::new ( room_mode . episode ( ) ) ;
2022-01-27 01:07:19 +00:00
let mut qpath = PathBuf ::from ( " data/quests/bb " ) ;
qpath . push ( room_mode . episode ( ) . to_string ( ) ) ;
qpath . push ( room_mode . to_string ( ) ) ;
qpath . push ( " quests.toml " ) ;
let room_quests = match quests ::load_quests ( & mut qpath ) {
Ok ( qlist ) = > qlist ,
Err ( _ ) = > return Err ( RoomCreationError ::CouldNotLoadQuests ) ,
} ;
2021-07-18 18:40:10 +00:00
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 ,
2021-07-23 01:45:02 +00:00
random_seed : rand ::thread_rng ( ) . gen ( ) ,
2021-07-18 18:40:10 +00:00
rare_monster_table : Box ::new ( rare_monster_table . clone ( ) ) ,
2020-01-25 18:02:17 -08:00
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 ,
2021-07-23 01:45:02 +00:00
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 ,
2020-04-12 14:27:06 -07:00
drop_table : Box ::new ( DropTable ::new ( room_mode . episode ( ) , room_mode . difficulty ( ) , section_id ) ) ,
2020-04-21 08:51:06 -06:00
bursting : false ,
2020-08-04 20:43:02 -06:00
map_areas : MapAreaLookup ::new ( & room_mode . episode ( ) ) ,
2022-01-27 01:07:19 +00:00
quests : room_quests ,
2020-01-02 20:33:37 -08:00
} )
}
}