You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

252 lines
7.0 KiB

4 years ago
3 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
  1. use std::collections::HashMap;
  2. use std::convert::{From, Into, TryFrom, TryInto};
  3. use rand::Rng;
  4. use crate::ship::map::Maps;
  5. use crate::ship::drops::DropTable;
  6. use crate::entity::character::SectionID;
  7. use crate::ship::monster::{load_monster_stats_table, MonsterType, MonsterStats};
  8. use crate::ship::map::area::MapAreaLookup;
  9. use crate::ship::map::enemy::RareMonsterAppearTable;
  10. #[derive(Debug)]
  11. pub enum RoomCreationError {
  12. InvalidMode,
  13. InvalidEpisode(u8),
  14. InvalidDifficulty(u8),
  15. CouldNotLoadMonsterStats(RoomMode),
  16. }
  17. #[derive(Debug, Copy, Clone, derive_more::Display)]
  18. pub enum Episode {
  19. #[display(fmt="ep1")]
  20. One,
  21. #[display(fmt="ep2")]
  22. Two,
  23. #[display(fmt="ep4")]
  24. Four,
  25. }
  26. impl TryFrom<u8> for Episode {
  27. type Error = RoomCreationError;
  28. fn try_from(value: u8) -> Result<Episode, RoomCreationError> {
  29. match value {
  30. 1 => Ok(Episode::One),
  31. 2 => Ok(Episode::Two),
  32. 3 => Ok(Episode::Four),
  33. _ => Err(RoomCreationError::InvalidEpisode(value))
  34. }
  35. }
  36. }
  37. impl From<Episode> for u8 {
  38. fn from(other: Episode) -> u8 {
  39. match other {
  40. Episode::One => 1,
  41. Episode::Two => 2,
  42. Episode::Four => 3,
  43. }
  44. }
  45. }
  46. impl Episode {
  47. pub fn from_quest(value: u8) -> Result<Episode, RoomCreationError> {
  48. match value {
  49. 0 => Ok(Episode::One),
  50. 1 => Ok(Episode::Two),
  51. 2 => Ok(Episode::Four),
  52. _ => Err(RoomCreationError::InvalidEpisode(value))
  53. }
  54. }
  55. }
  56. #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, derive_more::Display)]
  57. pub enum Difficulty {
  58. Normal,
  59. Hard,
  60. VeryHard,
  61. Ultimate,
  62. }
  63. impl TryFrom<u8> for Difficulty {
  64. type Error = RoomCreationError;
  65. fn try_from(value: u8) -> Result<Difficulty, RoomCreationError> {
  66. match value {
  67. 0 => Ok(Difficulty::Normal),
  68. 1 => Ok(Difficulty::Hard),
  69. 2 => Ok(Difficulty::VeryHard),
  70. 3 => Ok(Difficulty::Ultimate),
  71. _ => Err(RoomCreationError::InvalidDifficulty(value))
  72. }
  73. }
  74. }
  75. impl From<Difficulty> for u8 {
  76. fn from(other: Difficulty) -> u8 {
  77. match other {
  78. Difficulty::Normal => 0,
  79. Difficulty::Hard => 1,
  80. Difficulty::VeryHard => 2,
  81. Difficulty::Ultimate => 3,
  82. }
  83. }
  84. }
  85. #[derive(Debug, Copy, Clone)]
  86. pub enum RoomMode {
  87. Single {
  88. episode: Episode,
  89. difficulty: Difficulty,
  90. },
  91. Multi {
  92. episode: Episode,
  93. difficulty: Difficulty,
  94. },
  95. Challenge {
  96. episode: Episode,
  97. },
  98. Battle {
  99. episode: Episode,
  100. difficulty: Difficulty,
  101. }
  102. }
  103. impl RoomMode {
  104. pub fn difficulty(&self) -> Difficulty {
  105. match self {
  106. RoomMode::Single {difficulty, ..} => *difficulty,
  107. RoomMode::Multi {difficulty, ..} => *difficulty,
  108. RoomMode::Battle {difficulty, ..} => *difficulty,
  109. RoomMode::Challenge {..} => Difficulty::Normal,
  110. }
  111. }
  112. pub fn episode(&self) -> Episode {
  113. match self {
  114. RoomMode::Single {episode, ..} => *episode,
  115. RoomMode::Multi {episode, ..} => *episode,
  116. RoomMode::Battle {episode, ..} => *episode,
  117. RoomMode::Challenge {episode, ..} => *episode,
  118. }
  119. }
  120. pub fn battle(&self) -> u8 {
  121. match self {
  122. RoomMode::Battle {..} => 1,
  123. _ => 0,
  124. }
  125. }
  126. pub fn challenge(&self) -> u8 {
  127. match self {
  128. RoomMode::Challenge {..} => 1,
  129. _ => 0,
  130. }
  131. }
  132. pub fn single_player(&self) -> u8 {
  133. match self {
  134. RoomMode::Single {..} => 1,
  135. _ => 0,
  136. }
  137. }
  138. }
  139. pub struct RoomState {
  140. pub mode: RoomMode,
  141. pub name: String,
  142. pub password: [u16; 16],
  143. pub maps: Maps,
  144. pub drop_table: Box<DropTable<rand_chacha::ChaCha20Rng>>,
  145. pub section_id: SectionID,
  146. pub random_seed: u32,
  147. pub bursting: bool,
  148. pub monster_stats: Box<HashMap<MonsterType, MonsterStats>>,
  149. pub map_areas: MapAreaLookup,
  150. pub rare_monster_table: Box<RareMonsterAppearTable>,
  151. // items on ground
  152. // enemy info
  153. }
  154. impl RoomState {
  155. pub fn get_flags_for_room_list(&self) -> u8 {
  156. let mut flags = 0u8;
  157. match self.mode {
  158. RoomMode::Single {..} => {flags += 0x04}
  159. RoomMode::Battle {..} => {flags += 0x10},
  160. RoomMode::Challenge {..} => {flags += 0x20},
  161. _ => {flags += 0x40},
  162. };
  163. if self.password[0] > 0 {
  164. flags += 0x02;
  165. }
  166. flags
  167. }
  168. pub fn get_episode_for_room_list(&self) -> u8 {
  169. let episode: u8 = self.mode.episode().into();
  170. match self.mode {
  171. RoomMode::Single {..} => episode + 0x10,
  172. _ => episode + 0x40,
  173. }
  174. }
  175. pub fn get_difficulty_for_room_list(&self) -> u8 {
  176. let difficulty: u8 = self.mode.difficulty().into();
  177. difficulty + 0x22
  178. }
  179. pub fn from_create_room(create_room: &libpso::packet::ship::CreateRoom, section_id: SectionID) -> Result<RoomState, RoomCreationError> {
  180. if [create_room.battle, create_room.challenge, create_room.single_player].iter().sum::<u8>() > 1 {
  181. return Err(RoomCreationError::InvalidMode)
  182. }
  183. let room_mode = if create_room.battle == 1 {
  184. RoomMode::Battle {
  185. episode: create_room.episode.try_into()?,
  186. difficulty: create_room.difficulty.try_into()?,
  187. }
  188. }
  189. else if create_room.challenge == 1 {
  190. RoomMode::Challenge {
  191. episode: create_room.episode.try_into()?,
  192. }
  193. }
  194. else if create_room.single_player == 1 {
  195. RoomMode::Single {
  196. episode: create_room.episode.try_into()?,
  197. difficulty: create_room.difficulty.try_into()?,
  198. }
  199. }
  200. else { // normal multimode
  201. RoomMode::Multi {
  202. episode: create_room.episode.try_into()?,
  203. difficulty: create_room.difficulty.try_into()?,
  204. }
  205. };
  206. let rare_monster_table = RareMonsterAppearTable::new(room_mode.episode());
  207. Ok(RoomState {
  208. monster_stats: Box::new(load_monster_stats_table(&room_mode).map_err(|_| RoomCreationError::CouldNotLoadMonsterStats(room_mode))?),
  209. mode: room_mode,
  210. random_seed: rand::thread_rng().gen(),
  211. rare_monster_table: Box::new(rare_monster_table.clone()),
  212. name: String::from_utf16_lossy(&create_room.name).trim_matches(char::from(0)).into(),
  213. password: create_room.password,
  214. 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?
  215. section_id,
  216. drop_table: Box::new(DropTable::new(room_mode.episode(), room_mode.difficulty(), section_id)),
  217. bursting: false,
  218. map_areas: MapAreaLookup::new(&room_mode.episode()),
  219. })
  220. }
  221. }