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.

321 lines
9.1 KiB

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