use tracing::{debug, warn}; use trigger_logic::quest::EQuestType; use trigger_protocol::{AvatarSync, CafeSync, ItemSync, PlayerSyncScNotify, util::ProtocolUnit}; use trigger_sv::gm_command::GMCommand; use crate::AppState; use super::{NapPlayer, player::AvatarPropertyChanges}; pub struct CommandContext<'player> { pub player: &'player mut NapPlayer, pub state: &'static AppState, notifies: Vec, } impl<'player> CommandContext<'player> { pub fn new(player: &'player mut NapPlayer, state: &'static AppState) -> Self { Self { player, state, notifies: Vec::new(), } } pub fn add_notify>(&mut self, notify: Notify) { self.notifies.push(notify.into()); } pub fn remove_notifies(&mut self) -> Vec { std::mem::take(&mut self.notifies) } } pub async fn execute_command(context: &mut CommandContext<'_>, command: &GMCommand) { debug!( "executing {command:?} for player with uid {}", context.player.player_uid() ); match command { GMCommand::AddAvatar { id } => gm_add_avatar(context, *id).await, GMCommand::SetAvatarLevel { id, level } => { gm_modify_avatar_properties(context, *id, Some(*level), None, None).await } GMCommand::SetAvatarRank { id, rank } => { gm_modify_avatar_properties(context, *id, None, Some(*rank), None).await } GMCommand::SetAvatarTalent { id, talent } => { gm_modify_avatar_properties(context, *id, None, None, Some(*talent)).await } GMCommand::AddAllWeapon => gm_add_all_weapon(context).await, GMCommand::AddAllEquip => gm_add_all_equip(context).await, GMCommand::AddEquip { equip_id, level, star, property_params, } => gm_add_equip(context, *equip_id, *level, *star, property_params).await, GMCommand::AddQuest { quest_type, quest_id, } => gm_add_quest(context, *quest_type, *quest_id).await, GMCommand::FinishQuest { quest_type, quest_id, } => { gm_finish_quest(context, *quest_type, *quest_id).await; } GMCommand::AddItemByType { .. } => { warn!("AddItemByType is not implemented yet"); } GMCommand::UnlockAllHollow => gm_unlock_all_hollow(context).await, GMCommand::UnlockAllHollowBuff => gm_unlock_all_hollow_buff(context).await, GMCommand::UnlockAllCafeItem => gm_unlock_all_cafe_item(context).await, } } async fn gm_unlock_all_hollow(context: &mut CommandContext<'_>) { context .player .yorozuya_model .unlock_hollow( &context .state .filecfg .hollow_config_template_tb() .data() .unwrap() .iter() .map(|tmpl| tmpl.id()) .collect::>(), ) .await; context.add_notify(PlayerSyncScNotify { // TODO: HollowData sync! ..Default::default() }); } async fn gm_unlock_all_cafe_item(context: &mut CommandContext<'_>) { context .player .cafe_model .unlock_cafe_item( &context .state .filecfg .cafe_config_template_tb() .data() .unwrap() .iter() .map(|tmpl| tmpl.id()) .collect::>(), ) .await; context.add_notify(PlayerSyncScNotify { cafe_sync: Some(CafeSync { cafe_data: Some(context.player.cafe_model.get_protocol_cafe_data()), }), ..Default::default() }); } async fn gm_unlock_all_hollow_buff(context: &mut CommandContext<'_>) { context .player .ramen_model .unlock_ramen( &context .state .filecfg .hollow_buff_template_tb() .data() .unwrap() .iter() .map(|tmpl| tmpl.id()) .collect::>(), ) .await; context.add_notify(PlayerSyncScNotify { ramen_sync: Some(context.player.ramen_model.get_protocol_ramen_sync(false)), ..Default::default() }); } async fn gm_add_quest(context: &mut CommandContext<'_>, quest_type: i32, quest_id: i32) { if let Ok(ty) = EQuestType::try_from(quest_type) { match ty { EQuestType::ArchiveFile => { context .player .main_story_model .add_archive_files(&[quest_id]) .await; } EQuestType::Hollow => { context .player .yorozuya_model .add_hollow_quest(&[quest_id]) .await; } EQuestType::MainCity => {} _ => { warn!("quest type {quest_type:?} is not implemented yet"); return; } }; context.player.quest_model.add_quest(ty, quest_id).await; } else { warn!("gm_add_quest: invalid quest type: {quest_type}"); } } async fn gm_finish_quest(context: &mut CommandContext<'_>, quest_type: i32, quest_id: i32) { if let Ok(quest_type) = EQuestType::try_from(quest_type) { let quest_id_list = match quest_type { EQuestType::ArchiveFile => { let quest_id_list = if quest_id != 0 { vec![quest_id] } else { context .state .filecfg .archive_file_quest_template_tb() .data() .unwrap() .iter() .map(|tmpl| tmpl.id()) .collect::>() }; context .player .main_story_model .add_archive_files(&quest_id_list) .await; quest_id_list } EQuestType::Hollow => { let quest_id_list = if quest_id != 0 { vec![quest_id] } else { context .state .filecfg .hollow_quest_template_tb() .data() .unwrap() .iter() .map(|tmpl| tmpl.id()) .collect::>() }; context .player .yorozuya_model .add_hollow_quest(&quest_id_list) .await; quest_id_list } _ => { warn!("quest type {quest_type:?} is not implemented yet"); return; } }; context .player .quest_model .finish_quests(quest_type, &quest_id_list) .await; } else { warn!("gm_add_quest: invalid quest type: {quest_type}"); } } async fn gm_add_equip( context: &mut CommandContext<'_>, equip_id: i32, level: i16, star: i16, params: &[i32], ) { let equip_uid = context .player .equip_model .add_custom_equip(equip_id, level, star, params) .await; context.add_notify(PlayerSyncScNotify { item_sync: Some(ItemSync { equip_list: context .player .equip_model .get_protocol_equip_list(&[equip_uid]), ..Default::default() }), ..Default::default() }); } async fn gm_add_all_equip(context: &mut CommandContext<'_>) { let added_equip_uid_list = context .player .equip_model .add_equip( &context .state .filecfg .equipment_template_tb() .data() .unwrap() .iter() .map(|tmpl| tmpl.item_id()) .collect::>(), ) .await; context.add_notify(PlayerSyncScNotify { item_sync: Some(ItemSync { equip_list: context .player .equip_model .get_protocol_equip_list(&added_equip_uid_list), ..Default::default() }), ..Default::default() }); } async fn gm_add_all_weapon(context: &mut CommandContext<'_>) { let added_weapon_uid_list = context .player .equip_model .add_weapon( &context .state .filecfg .weapon_template_tb() .data() .unwrap() .iter() .map(|tmpl| tmpl.item_id()) .collect::>(), ) .await; context.add_notify(PlayerSyncScNotify { item_sync: Some(ItemSync { weapon_list: context .player .equip_model .get_protocol_weapon_list(&added_weapon_uid_list), ..Default::default() }), ..Default::default() }); } async fn gm_modify_avatar_properties( context: &mut CommandContext<'_>, id: i32, level: Option, rank: Option, talent_num: Option, ) { if let Some(updated_avatars) = context .player .role_model .change_avatar_properties(AvatarPropertyChanges { avatar_id: id as u32, level, rank, talent_num, }) .await { context.add_notify(PlayerSyncScNotify { avatar_sync: Some(AvatarSync { avatar_list: context .player .role_model .get_protocol_avatar_list(&updated_avatars), }), ..Default::default() }); } } async fn gm_add_avatar(context: &mut CommandContext<'_>, id: i32) { if id == 0 { let added_avatars = context .player .role_model .unlock_avatars( &context .state .filecfg .avatar_base_template_tb() .data() .unwrap() .iter() .map(|tmpl| tmpl.id()) .filter(|id| { !context .state .gm_blacklist .black_item_list .iter() .any(|item| item.id == *id) }) .collect::>(), ) .await; context.add_notify(PlayerSyncScNotify { avatar_sync: Some(AvatarSync { avatar_list: context .player .role_model .get_protocol_avatar_list(&added_avatars), }), ..Default::default() }); } else { context.player.role_model.unlock_avatars(&[id]).await; context.add_notify(PlayerSyncScNotify { avatar_sync: Some(AvatarSync { avatar_list: context .player .role_model .get_protocol_avatar_list(&[id as u32]), }), ..Default::default() }); } }