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.

272 lines
7.4 KiB

2 years ago
12 months ago
2 years ago
12 months ago
2 years ago
12 months ago
12 months ago
  1. use std::collections::HashMap;
  2. use std::convert::{From, Into};
  3. use async_std::sync::{Arc, RwLock, RwLockReadGuard};
  4. use futures::future::BoxFuture;
  5. use futures::stream::{FuturesOrdered, Stream};
  6. use thiserror::Error;
  7. use rand::Rng;
  8. use maps::maps::Maps;
  9. use drops::DropTable;
  10. use entity::character::SectionID;
  11. use entity::room::{RoomEntityId, RoomEntityMode};
  12. use maps::monster::{load_monster_stats_table, MonsterType, MonsterStats};
  13. use maps::area::MapAreaLookup;
  14. use quests;
  15. use maps::Holiday;
  16. use location::{MAX_ROOMS, RoomId};
  17. use maps::room::{Episode, Difficulty, RoomMode};
  18. #[derive(Error, Debug)]
  19. pub enum RoomError {
  20. #[error("invalid room id {0}")]
  21. Invalid(u32),
  22. }
  23. #[derive(Clone)]
  24. pub struct Rooms([Arc<RwLock<Option<RoomState>>>; MAX_ROOMS]);
  25. impl Default for Rooms {
  26. fn default() -> Rooms {
  27. Rooms(core::array::from_fn(|_| Arc::new(RwLock::new(None))))
  28. }
  29. }
  30. impl Rooms {
  31. pub async fn add(&self, room_id: RoomId, room: RoomState) -> Result<(), anyhow::Error> {
  32. *self.0
  33. .get(room_id.0)
  34. .ok_or(RoomError::Invalid(room_id.0 as u32))?
  35. .write()
  36. .await = Some(room);
  37. Ok(())
  38. }
  39. pub async fn remove(&self, room_id: RoomId) {
  40. if let Some(room) = self.0.get(room_id.0) {
  41. *room
  42. .write()
  43. .await = None;
  44. }
  45. }
  46. pub async fn exists(&self, room_id: RoomId) -> bool {
  47. match self.0.get(room_id.0) {
  48. Some(room) => {
  49. room
  50. .read()
  51. .await
  52. .is_some()
  53. },
  54. None => false,
  55. }
  56. }
  57. pub async fn with<'a, T, F>(&'a self, room_id: RoomId, func: F) -> Result<T, anyhow::Error>
  58. where
  59. T: Send,
  60. F: for<'b> FnOnce(&'b RoomState) -> BoxFuture<'b, T> + Send + 'a
  61. {
  62. let room = self.0
  63. .get(room_id.0)
  64. .ok_or(RoomError::Invalid(room_id.0 as u32))?
  65. .read()
  66. .await;
  67. if let Some(room) = room.as_ref() {
  68. Ok(func(room).await)
  69. }
  70. else {
  71. Err(RoomError::Invalid(room_id.0 as u32).into())
  72. }
  73. }
  74. pub async fn with_mut<'a, T, F>(&'a self, room_id: RoomId, func: F) -> Result<T, anyhow::Error>
  75. where
  76. T: Send,
  77. F: for<'b> FnOnce(&'b mut RoomState) -> BoxFuture<'b, T> + Send + 'a
  78. {
  79. let mut room = self.0
  80. .get(room_id.0)
  81. .ok_or(RoomError::Invalid(room_id.0 as u32))?
  82. .write()
  83. .await;
  84. if let Some(room) = room.as_mut() {
  85. Ok(func(room).await)
  86. }
  87. else {
  88. Err(RoomError::Invalid(room_id.0 as u32).into())
  89. }
  90. }
  91. pub async fn get(&self, room_id: RoomId) -> RwLockReadGuard<Option<RoomState>> {
  92. self.0
  93. .get(room_id.0)
  94. .unwrap()
  95. .read()
  96. .await
  97. }
  98. pub fn stream(&self) -> impl Stream<Item = RwLockReadGuard<Option<RoomState>>> {
  99. self.0
  100. .iter()
  101. .map(|room| async move {
  102. room
  103. .read()
  104. .await
  105. })
  106. .collect::<FuturesOrdered<_>>()
  107. }
  108. }
  109. #[derive(Debug, Error)]
  110. #[error("")]
  111. pub enum RoomCreationError {
  112. InvalidMode,
  113. InvalidEpisode(u8),
  114. InvalidDifficulty(u8),
  115. CouldNotLoadMonsterStats(RoomMode),
  116. CouldNotLoadQuests,
  117. }
  118. pub enum QuestCategoryType {
  119. Standard,
  120. Government,
  121. }
  122. impl From<usize> for QuestCategoryType {
  123. fn from(f: usize) -> QuestCategoryType {
  124. match f {
  125. 0 => QuestCategoryType::Standard,
  126. _ => QuestCategoryType::Government,
  127. }
  128. }
  129. }
  130. impl From<u32> for QuestCategoryType {
  131. fn from(f: u32) -> QuestCategoryType {
  132. match f {
  133. 0 => QuestCategoryType::Standard,
  134. _ => QuestCategoryType::Government,
  135. }
  136. }
  137. }
  138. impl QuestCategoryType {
  139. pub fn value(&self) -> usize {
  140. match self {
  141. QuestCategoryType::Standard => 0,
  142. QuestCategoryType::Government => 1,
  143. }
  144. }
  145. }
  146. pub struct RoomState {
  147. pub room_id: RoomEntityId,
  148. pub mode: RoomMode,
  149. pub name: String,
  150. pub password: [u16; 16],
  151. pub maps: Maps,
  152. pub drop_table: Box<DropTable>,
  153. pub section_id: SectionID,
  154. pub random_seed: u32,
  155. pub bursting: bool,
  156. pub monster_stats: Box<HashMap<MonsterType, MonsterStats>>,
  157. pub map_areas: MapAreaLookup,
  158. pub quest_group: QuestCategoryType,
  159. pub standard_quests: quests::QuestList,
  160. pub government_quests: quests::QuestList,
  161. // enemy info
  162. }
  163. impl RoomState {
  164. pub fn get_flags_for_room_list(&self) -> u8 {
  165. let mut flags = 0u8;
  166. match self.mode {
  167. RoomMode::Single {..} => {flags += 0x04}
  168. RoomMode::Battle {..} => {flags += 0x10},
  169. RoomMode::Challenge {..} => {flags += 0x20},
  170. _ => {flags += 0x40},
  171. };
  172. if self.password[0] > 0 {
  173. flags += 0x02;
  174. }
  175. flags
  176. }
  177. pub fn get_episode_for_room_list(&self) -> u8 {
  178. let episode: u8 = self.mode.episode().into();
  179. match self.mode {
  180. RoomMode::Single {..} => episode + 0x10,
  181. _ => episode + 0x40,
  182. }
  183. }
  184. pub fn get_difficulty_for_room_list(&self) -> u8 {
  185. let difficulty: u8 = self.mode.difficulty().into();
  186. difficulty + 0x22
  187. }
  188. pub fn quests(&self) -> &quests::QuestList {
  189. match self.quest_group {
  190. QuestCategoryType::Standard => &self.standard_quests,
  191. QuestCategoryType::Government => &self.government_quests,
  192. }
  193. }
  194. #[allow(clippy::too_many_arguments, clippy::type_complexity)]
  195. pub fn new (room_id: RoomEntityId,
  196. mode: RoomEntityMode,
  197. episode: Episode,
  198. difficulty: Difficulty,
  199. section_id: SectionID,
  200. name: String,
  201. password: [u16; 16],
  202. event: Holiday,
  203. map_builder: Arc<Box<dyn Fn(RoomMode, Holiday) -> Maps + Send + Sync>>,
  204. drop_table_builder: Arc<Box<dyn Fn(Episode, Difficulty, SectionID) -> DropTable + Send + Sync>>,
  205. ) -> Result<RoomState, anyhow::Error> {
  206. let mode = match mode {
  207. RoomEntityMode::Single => RoomMode::Single {
  208. episode,
  209. difficulty,
  210. },
  211. RoomEntityMode::Multi => RoomMode::Multi {
  212. episode,
  213. difficulty,
  214. },
  215. RoomEntityMode::Challenge => RoomMode::Challenge {
  216. episode,
  217. },
  218. RoomEntityMode::Battle => RoomMode::Battle {
  219. episode,
  220. difficulty,
  221. },
  222. };
  223. Ok(RoomState {
  224. room_id,
  225. monster_stats: Box::new(load_monster_stats_table(&mode).map_err(|_| RoomCreationError::CouldNotLoadMonsterStats(mode))?),
  226. mode,
  227. random_seed: rand::thread_rng().gen(),
  228. name,
  229. password,
  230. maps: map_builder(mode, event),
  231. section_id,
  232. drop_table: Box::new(drop_table_builder(episode, difficulty, section_id)),
  233. bursting: false,
  234. map_areas: MapAreaLookup::new(&episode),
  235. quest_group: QuestCategoryType::Standard,
  236. standard_quests: quests::load_standard_quests(mode)?,
  237. government_quests: quests::load_government_quests(mode)?,
  238. })
  239. }
  240. }