use anyhow::{anyhow, bail, Result}; use atomic_refcell::AtomicRefCell; use protocol::*; use std::sync::Arc; use crate::game::{manager::UniqueIDManager, util, PlayerOperationResult}; use qwer::{ pdkhashmap, phashmap, phashset, PropertyDoubleKeyHashMap, PropertyHashMap, PropertyHashSet, }; pub struct DungeonManager { uid_mgr: Arc>, player: Arc>, scene_properties: AtomicRefCell>, } impl DungeonManager { pub fn new( uid_mgr: Arc>, player: Arc>, ) -> Self { Self { uid_mgr, player, scene_properties: AtomicRefCell::new(pdkhashmap![]), } } pub fn enter_main_city(&self) -> Result> { let (player_uid, scene_position, scene_rotation) = { let player = self.player.borrow(); let pos_in_main_city = player.pos_in_main_city.as_ref().unwrap(); ( player.uid.unwrap(), *pos_in_main_city.position.as_ref().unwrap(), *pos_in_main_city.rotation.as_ref().unwrap(), ) }; let mut player = self.player.borrow_mut(); let default_scene_uid = player .dungeon_collection .as_ref() .unwrap() .default_scene_uid .unwrap(); player.scene_uid.replace(default_scene_uid); let dungeon_collection = player.dungeon_collection.as_mut().unwrap(); let scene_info = dungeon_collection .scenes .as_mut() .unwrap() .get_mut(&default_scene_uid) .ok_or_else(|| anyhow!("Scene with uid {default_scene_uid} doesn't exist"))?; let dungeon_uid = scene_info.get_dungeon_uid(); let dungeon_info = dungeon_collection .dungeons .as_mut() .unwrap() .get_mut(&scene_info.get_dungeon_uid()) .ok_or_else(|| anyhow!("Dungeon with uid {dungeon_uid} doesn't exist"))?; scene_info.set_entered_times(scene_info.get_entered_times() + 1); dungeon_info.entered_times += 1; let ptc_enter_scene = PtcEnterSceneArg { player_uid, scene_uid: default_scene_uid, section_id: scene_info.get_section_id(), open_ui: UIType::Default, condition_config_ids: Vec::new(), transform: Transform { position: scene_position, rotation: scene_rotation, }, timestamp: util::cur_timestamp_ms(), camera_x: 0, camera_y: 6000, entered_times: scene_info.get_entered_times(), ext: match scene_info { SceneInfo::Hall { .. } => SceneTableExt::Hall { event_graphs_info: EventGraphsInfo { default_event_graph_id: 0, event_graphs_info: phashmap![], }, }, _ => bail!("Unexpected main city scene type"), }, }; Ok(PlayerOperationResult::with_changes( ptc_enter_scene, PlayerInfo { dungeon_collection: Some(DungeonCollection { dungeons: Some(PropertyHashMap::Modify { to_add: vec![(dungeon_info.uid, dungeon_info.clone())], to_remove: Vec::new(), }), scenes: Some(PropertyHashMap::Modify { to_add: vec![(scene_info.get_uid(), scene_info.clone())], to_remove: Vec::new(), }), ..Default::default() }), scene_uid: Some(scene_info.get_uid()), ..Default::default() }, )) } pub fn enter_scene_section( &self, scene_uid: u64, section_id: i32, ) -> PlayerOperationResult { let mut player = self.player.borrow_mut(); let scene_info = player .dungeon_collection .as_mut() .unwrap() .scenes .as_mut() .unwrap() .get_mut(&scene_uid) .unwrap(); scene_info.set_section_id(section_id); PlayerOperationResult::with_changes( PtcEnterSectionArg { section_id }, PlayerInfo { dungeon_collection: Some(DungeonCollection { scenes: Some(PropertyHashMap::Modify { to_add: vec![(scene_uid, scene_info.clone())], to_remove: Vec::new(), }), ..Default::default() }), ..Default::default() }, ) } pub fn enter_scene(&self, scene_uid: u64) -> Result> { let (player_uid, prev_scene_uid) = { let player = self.player.borrow(); ( *player.uid.as_ref().unwrap(), *player.scene_uid.as_ref().unwrap(), ) }; let mut player = self.player.borrow_mut(); player.scene_uid.replace(scene_uid); player.prev_scene_uid.replace(prev_scene_uid); let dungeon_collection = player.dungeon_collection.as_mut().unwrap(); let scene_info = dungeon_collection .scenes .as_mut() .unwrap() .get_mut(&scene_uid) .ok_or_else(|| anyhow!("Scene with uid {scene_uid} doesn't exist"))?; let dungeon_uid = scene_info.get_dungeon_uid(); let dungeon_info = dungeon_collection .dungeons .as_mut() .unwrap() .get_mut(&scene_info.get_dungeon_uid()) .ok_or_else(|| anyhow!("Dungeon with uid {dungeon_uid} doesn't exist"))?; scene_info.set_entered_times(scene_info.get_entered_times() + 1); dungeon_info.entered_times += 1; if let SceneInfo::Hollow { sections_info, .. } = scene_info { let section = sections_info.get_mut(&1).unwrap(); section.entered_times += 1; } let ptc_enter_scene = PtcEnterSceneArg { player_uid, scene_uid, section_id: scene_info.get_section_id(), open_ui: UIType::Default, condition_config_ids: Vec::new(), transform: Transform::default(), timestamp: util::cur_timestamp_ms(), camera_x: 0, camera_y: 6000, entered_times: scene_info.get_entered_times(), ext: match scene_info { SceneInfo::Hall { .. } => SceneTableExt::Hall { event_graphs_info: EventGraphsInfo { default_event_graph_id: 0, event_graphs_info: phashmap![], }, }, SceneInfo::Fresh { .. } => SceneTableExt::Fresh { event_graphs_info: EventGraphsInfo { default_event_graph_id: 0, event_graphs_info: phashmap![], }, }, SceneInfo::Hollow { .. } => SceneTableExt::Hollow { event_graphs_info: EventGraphsInfo { default_event_graph_id: 0, event_graphs_info: phashmap![], }, grid_random_seed: 0, alter_section_id: 0, }, SceneInfo::Fight { .. } => SceneTableExt::Fight { event_graphs_info: EventGraphsInfo { default_event_graph_id: 0, event_graphs_info: phashmap![], }, }, }, }; Ok(PlayerOperationResult::with_changes( ptc_enter_scene, PlayerInfo { dungeon_collection: Some(DungeonCollection { dungeons: Some(PropertyHashMap::Modify { to_add: vec![(dungeon_info.uid, dungeon_info.clone())], to_remove: Vec::new(), }), scenes: Some(PropertyHashMap::Modify { to_add: vec![(scene_info.get_uid(), scene_info.clone())], to_remove: Vec::new(), }), ..Default::default() }), scene_uid: Some(scene_uid), prev_scene_uid: Some(prev_scene_uid), ..Default::default() }, )) } pub fn hollow_finished(&self) -> PlayerOperationResult { let cur_scene_uid = self.get_cur_scene_uid(); let mut player = self.player.borrow_mut(); let hollow_scene = player .dungeon_collection .as_mut() .unwrap() .scenes .as_mut() .unwrap() .get_mut(&cur_scene_uid) .unwrap(); if let SceneInfo::Hollow { hollow_system_ui_state, .. } = hollow_scene { hollow_system_ui_state.insert( HollowSystemType::HollowResultPage, HollowSystemUIState::Normal, ); hollow_system_ui_state.insert(HollowSystemType::Menu, HollowSystemUIState::Close); } PlayerOperationResult::with_changes( cur_scene_uid, PlayerInfo { dungeon_collection: Some(DungeonCollection { scenes: Some(PropertyHashMap::Modify { to_add: vec![(cur_scene_uid, hollow_scene.clone())], to_remove: Vec::new(), }), ..Default::default() }), ..Default::default() }, ) } pub fn get_default_scene_uid(&self) -> u64 { self.player .borrow() .dungeon_collection .as_ref() .unwrap() .default_scene_uid .unwrap() } #[allow(dead_code)] pub fn get_default_scene_uid_for_dungeon(&self, dungeon_uid: u64) -> u64 { self.player .borrow() .dungeon_collection .as_ref() .unwrap() .dungeons .as_ref() .unwrap() .get(&dungeon_uid) .unwrap() .default_scene_uid } pub fn get_cur_scene_uid(&self) -> u64 { self.player.borrow().scene_uid.unwrap() } fn add_default_hollow_properties(&self, scene_uid: u64) { let mut props = self.scene_properties.borrow_mut(); for (sub_key, value) in &[ (1001, 0), (1002, 100), (1003, 0), (1004, 0), (1019, 10), (1020, 1), (1005, 0), (1006, 0), (1007, 0), (1008, 0), (1009, 0), (1010, 0), (1011, 0), (1012, 0), (1013, 1), (1014, 1), (1015, 0), (1016, 0), (1017, 4), (1018, 10000), (1021, 1), (1025, 1), (1035, 10000), (1041, 10000), (1042, 10000), (1043, 1), (1044, 1), ] { props.insert(scene_uid, *sub_key, *value); } } pub fn leave_battle(&self) -> Result> { let back_scene_uid = self.get_back_scene_uid(); { let mut player = self.player.borrow_mut(); let hollow_scene = player .dungeon_collection .as_mut() .unwrap() .scenes .as_mut() .unwrap() .get_mut(&back_scene_uid) .unwrap(); if let SceneInfo::Hollow { battle_scene_uid, .. } = hollow_scene { *battle_scene_uid = 0; } } self.enter_scene(back_scene_uid) } fn get_back_scene_uid(&self) -> u64 { let player = self.player.borrow(); let fight_scene_uid = player.scene_uid.as_ref().unwrap(); let fight_scene = player .dungeon_collection .as_ref() .unwrap() .scenes .as_ref() .unwrap() .get(fight_scene_uid) .unwrap(); fight_scene.get_back_scene_uid() } pub fn enter_battle(&self, scene_uid: u64) -> PlayerOperationResult { let hollow_scene_uid = *self.player.borrow().scene_uid.as_ref().unwrap(); let hollow_scene = self.set_cur_hollow_battle(scene_uid, hollow_scene_uid); let ptc_enter_scene = self.enter_scene(scene_uid).unwrap().unwrap().clone(); let player = self.player.borrow(); let dungeon_collection = player.dungeon_collection.as_ref().unwrap(); let fight_scene = dungeon_collection .scenes .as_ref() .unwrap() .get(&scene_uid) .unwrap(); PlayerOperationResult::with_changes( ptc_enter_scene, PlayerInfo { dungeon_collection: Some(DungeonCollection { scenes: Some(PropertyHashMap::Modify { to_add: vec![ (hollow_scene_uid, hollow_scene), (scene_uid, fight_scene.clone()), ], to_remove: Vec::new(), }), ..Default::default() }), scene_uid: Some(scene_uid), prev_scene_uid: Some(hollow_scene_uid), ..Default::default() }, ) } fn set_cur_hollow_battle(&self, scene_uid: u64, hollow_scene_uid: u64) -> SceneInfo { let mut player = self.player.borrow_mut(); let hollow_scene = player .dungeon_collection .as_mut() .unwrap() .scenes .as_mut() .unwrap() .get_mut(&hollow_scene_uid) .unwrap(); let SceneInfo::Hollow { on_battle_success, battle_scene_uid, .. } = hollow_scene else { panic!("Unexpected scene type") }; *battle_scene_uid = scene_uid; *on_battle_success = String::from("OnEnd"); hollow_scene.clone() } pub fn create_fight(&self, id: i32, hollow_scene_uid: u64) -> PlayerOperationResult { let mut player = self.player.borrow_mut(); let dungeon_collection = player.dungeon_collection.as_mut().unwrap(); let scenes = dungeon_collection.scenes.as_mut().unwrap(); let hollow_scene = scenes.get_mut(&hollow_scene_uid).unwrap(); let fight_scene_uid = self.uid_mgr.borrow().next(); let fight_scene = SceneInfo::Fight { uid: fight_scene_uid, id, dungeon_uid: hollow_scene.get_dungeon_uid(), end_timestamp: 0, back_scene_uid: hollow_scene_uid, entered_times: 1, section_id: 1, open_ui: UIType::Default, to_be_destroyed: false, camera_x: 0xFFFFFFFF, camera_y: 0xFFFFFFFF, perform_show_progress: phashmap![], end_hollow: false, random_seed: 2281337, }; scenes.insert(fight_scene_uid, fight_scene.clone()); PlayerOperationResult::with_changes( fight_scene_uid, PlayerInfo { dungeon_collection: Some(DungeonCollection { scenes: Some(PropertyHashMap::Modify { to_add: vec![(fight_scene_uid, fight_scene)], to_remove: Vec::new(), }), ..Default::default() }), ..Default::default() }, ) } #[allow(dead_code)] pub fn is_in_tutorial(&self) -> bool { let cur_scene_uid = self.get_cur_scene_uid(); let player = self.player.borrow(); let cur_scene = player .dungeon_collection .as_ref() .unwrap() .scenes .as_ref() .unwrap() .get(&cur_scene_uid) .unwrap(); matches!(cur_scene, SceneInfo::Fresh { .. }) } pub fn create_hollow( &self, id: i32, world_quest_id: i32, avatar_uids: &[u64], ) -> PlayerOperationResult<(u64, u64)> { let back_scene_uid = self.get_default_scene_uid(); let mut dungeon = self.create_base_dungeon(id, back_scene_uid, world_quest_id); dungeon.hollow_event_version = 526; let scene_uid = self.uid_mgr.borrow().next(); dungeon.default_scene_uid = scene_uid; dungeon.scene_properties_uid = scene_uid; self.add_default_hollow_properties(scene_uid); for (index, avatar_uid) in avatar_uids.iter().enumerate() { dungeon.avatar_map.insert( index.try_into().unwrap(), AvatarUnitInfo { uid: *avatar_uid, properties_uid: self.uid_mgr.borrow().next(), is_banned: false, modified_property: pdkhashmap![], hp_add_hollow: 0, hp_lost_hollow: 0, layer_property_change: phashmap![], }, ); } let scene = SceneInfo::Hollow { uid: scene_uid, id, dungeon_uid: dungeon.uid, end_timestamp: 0, back_scene_uid, entered_times: 1, section_id: 1, open_ui: UIType::Default, to_be_destroyed: false, camera_x: 0xFFFFFFFF, camera_y: 0xFFFFFFFF, event_variables: phashmap![], buddy: BuddyUnitInfo { uid: 0, properties: 0, }, stress_punish_ability_random_pool: vec![String::from( "Stress_Punish_RandomDebuff_Normal", )], finished: false, event_weight_factor: phashmap![], shop_modification: HollowShopModification { ability_modified_num: pdkhashmap![], action_modified_num: phashmap![], overwrite_price: phashmap![], }, last_challenge_stat: phashmap![], cur_challenge: phashset![], hollow_system_switch: phashmap![], sections_info: phashmap![( 1, PlayerHollowSectionInfo { prev_grid_index: 0, cur_grid_index: 22, entered_times: 0, global_event: 0, perform_event_graph: 3405096459205834, pos_before_move: 0, } )], executing_event: true, event_id: 1000, hollow_event_graph_uid: 22, on_battle_success: String::new(), on_battle_failure: String::new(), battle_finished: false, battle_success: false, battle_scene_uid: 0, scene_global_events: phashmap![], prepare_section: PrepareSection { section_id: 0, initial_pos: 0, show_other: false, battle_end_goto_next_hollow: false, }, abilities_info: AbilitiesInfo { abilities: phashmap![], sequence_no: 0, }, blackout: false, hollow_system_ui_state: phashmap![], }; { let mut player = self.player.borrow_mut(); player .scene_properties .replace(self.scene_properties.borrow().clone()); let dungeon_collection = player.dungeon_collection.as_mut().unwrap(); dungeon_collection .dungeons .as_mut() .unwrap() .insert(dungeon.uid, dungeon.clone()); dungeon_collection .scenes .as_mut() .unwrap() .insert(scene_uid, scene.clone()); } let mut player = self.player.borrow_mut(); let items = player.items.as_mut().unwrap(); let mut updated_items = Vec::new(); for avatar_uid in avatar_uids { let item = items.get_mut(avatar_uid).unwrap(); let ItemInfo::Avatar { robot_id, .. } = item else { continue; }; *robot_id = 101000101; updated_items.push((*avatar_uid, item.clone())); } let mut prop_changes = Vec::new(); for (key, sub_key, value) in &*self.scene_properties.borrow_mut() { prop_changes.push((*key, *sub_key, *value)); } PlayerOperationResult::with_changes( (dungeon.uid, scene_uid), PlayerInfo { items: Some(PropertyHashMap::Modify { to_add: updated_items, to_remove: vec![], }), dungeon_collection: Some(DungeonCollection { dungeons: Some(PropertyHashMap::Modify { to_add: vec![(dungeon.uid, dungeon)], to_remove: Vec::new(), }), scenes: Some(PropertyHashMap::Modify { to_add: vec![(scene_uid, scene)], to_remove: Vec::new(), }), ..Default::default() }), scene_properties: Some(PropertyDoubleKeyHashMap::Modify { to_add: prop_changes, to_remove: Vec::new(), }), ..Default::default() }, ) } pub fn create_hall(&self, id: i32) -> PlayerOperationResult { let mut dungeon = self.create_base_dungeon(id, 0, 0); let dungeon_uid = dungeon.uid; let scene_uid = self.uid_mgr.borrow().next(); let hall_scene_info = SceneInfo::Hall { uid: scene_uid, id, dungeon_uid, end_timestamp: 0, back_scene_uid: 0, entered_times: 1, section_id: 1, open_ui: UIType::Default, to_be_destroyed: true, camera_x: 0xFFFFFFFF, camera_y: 0xFFFFFFFF, }; dungeon.default_scene_uid = scene_uid; let mut player = self.player.borrow_mut(); let dungeon_collection = player.dungeon_collection.as_mut().unwrap(); dungeon_collection .dungeons .as_mut() .unwrap() .insert(dungeon_uid, dungeon.clone()); dungeon_collection .scenes .as_mut() .unwrap() .insert(scene_uid, hall_scene_info.clone()); dungeon_collection.default_scene_uid.replace(scene_uid); PlayerOperationResult::with_changes( scene_uid, PlayerInfo { dungeon_collection: Some(DungeonCollection { dungeons: Some(PropertyHashMap::Modify { to_add: vec![(dungeon_uid, dungeon)], to_remove: vec![], }), scenes: Some(PropertyHashMap::Modify { to_add: vec![(scene_uid, hall_scene_info)], to_remove: vec![], }), default_scene_uid: Some(scene_uid), ..Default::default() }), ..Default::default() }, ) } pub fn create_fresh(&self) -> PlayerOperationResult { let mut dungeon = self.create_base_dungeon(2, 0, 0); let dungeon_uid = dungeon.uid; let scene_uid = self.uid_mgr.borrow().next(); let fresh_scene_info = SceneInfo::Fresh { uid: scene_uid, id: 2, dungeon_uid, end_timestamp: 0, back_scene_uid: 0, entered_times: 1, section_id: 1, open_ui: UIType::Default, to_be_destroyed: true, camera_x: 0xFFFFFFFF, camera_y: 0xFFFFFFFF, }; dungeon.default_scene_uid = scene_uid; let mut player = self.player.borrow_mut(); let dungeon_collection = player.dungeon_collection.as_mut().unwrap(); dungeon_collection .dungeons .as_mut() .unwrap() .insert(dungeon_uid, dungeon.clone()); dungeon_collection .scenes .as_mut() .unwrap() .insert(scene_uid, fresh_scene_info.clone()); PlayerOperationResult::with_changes( scene_uid, PlayerInfo { dungeon_collection: Some(DungeonCollection { dungeons: Some(PropertyHashMap::Modify { to_add: vec![(dungeon_uid, dungeon)], to_remove: vec![], }), scenes: Some(PropertyHashMap::Modify { to_add: vec![(scene_uid, fresh_scene_info)], to_remove: vec![], }), ..Default::default() }), ..Default::default() }, ) } fn create_base_dungeon( &self, id: i32, back_scene_uid: u64, world_quest_id: i32, ) -> DungeonInfo { let player = self.player.borrow(); let uid = self.uid_mgr.borrow().next(); DungeonInfo { uid, id, default_scene_uid: 0, start_timestamp: util::cur_timestamp_ms(), to_be_destroyed: false, back_scene_uid, quest_collection_uid: uid, avatars: phashmap![], buddy: BuddyUnitInfo { uid: 0, properties: 0, }, world_quest_id, scene_properties_uid: 0, drop_poll_chg_infos: phashmap![], is_in_dungeon: false, initiative_item: 0, initiative_item_used_times: 0, avatar_map: phashmap![], battle_report: Vec::new(), dungeon_group_uid: player.uid.unwrap(), entered_times: 0, is_preset_avatar: false, hollow_event_version: 0, } } }