trigger-rs/crates/game-server/src/logic/player/role.rs
2025-02-21 14:03:43 +03:00

444 lines
15 KiB
Rust

use std::collections::{HashMap, HashSet};
use trigger_database::entity::avatar;
use trigger_database::prelude::*;
use trigger_protocol::{Avatar, AvatarSkillLevel, DressedEquip};
use trigger_sv::time_util;
use crate::logic::{avatar_util, bit_util::BitSetExt};
use super::NapContext;
pub struct RoleModel {
context: NapContext,
avatar_map: HashMap<u32, avatar::Model>,
}
pub struct AvatarPropertyChanges {
pub avatar_id: u32,
pub level: Option<i16>,
pub rank: Option<i16>,
pub talent_num: Option<i16>,
}
impl RoleModel {
pub async fn init(context: NapContext) -> Self {
let avatar_map = Self::load_or_create_avatar_map(&context).await;
Self {
context,
avatar_map,
}
}
pub async fn weapon_dress(&mut self, avatar_id: u32, weapon_uid: i32) -> Option<[u32; 2]> {
if !self.avatar_map.contains_key(&avatar_id) {
return None;
}
let mut updated_avatars = [0xFFFFFFFF_u32; 2];
let prev_avatar = if let Some(prev_avatar) = (weapon_uid != 0)
.then(|| {
self.avatar_map
.values_mut()
.find(|avatar| avatar.cur_weapon_uid == weapon_uid)
})
.flatten()
{
prev_avatar.cur_weapon_uid = 0;
prev_avatar.avatar_id as u32
} else {
0
};
let avatar = self.avatar_map.remove(&avatar_id).unwrap();
let prev_weapon = avatar.cur_weapon_uid;
updated_avatars[0] = avatar.avatar_id as u32;
self.avatar_map.insert(
avatar.avatar_id as u32,
avatar::ActiveModel {
cur_weapon_uid: Set(weapon_uid),
..avatar.into()
}
.update(self.context.database)
.await
.expect("avatar::update failed"),
);
if prev_avatar != 0 && prev_weapon != 0 {
if let Some(avatar) = self.avatar_map.remove(&prev_avatar) {
updated_avatars[1] = avatar.avatar_id as u32;
self.avatar_map.insert(
avatar.avatar_id as u32,
avatar::ActiveModel {
cur_weapon_uid: Set(prev_weapon),
..avatar.into()
}
.update(self.context.database)
.await
.expect("avatar::update failed"),
);
}
}
Some(updated_avatars)
}
pub async fn dress_equipment(
&mut self,
avatar_id: u32,
params: &[(u32, u32)],
) -> Option<Vec<u32>> {
if !self.avatar_map.contains_key(&avatar_id) {
return None;
}
let mut updated_avatars = HashSet::new();
for &(equip_uid, _) in params.iter() {
if let Some(avatar_id) = (equip_uid != 0)
.then(|| {
self.avatar_map.values_mut().find(|avatar| {
avatar
.equip_slot_list
.iter()
.any(|slot| ((slot & 0xFFFFFFFF) as u32) == equip_uid)
})
})
.flatten()
.map(|avatar| avatar.avatar_id)
{
if let Some(mut avatar) = self.avatar_map.remove(&(avatar_id as u32)) {
updated_avatars.insert(avatar_id as u32);
avatar
.equip_slot_list
.retain(|slot| ((slot & 0xFFFFFFFF) as u32) != equip_uid);
self.avatar_map.insert(
avatar_id as u32,
avatar::ActiveModel {
equip_slot_list: Set(avatar.equip_slot_list.clone()),
..avatar.into()
}
.update(self.context.database)
.await
.expect("avatar::update failed"),
);
}
}
}
let mut avatar = self.avatar_map.remove(&avatar_id).unwrap();
updated_avatars.insert(avatar.avatar_id as u32);
for &(equip_uid, dress_index) in params.iter() {
let slot = ((dress_index as i64) << 32) | (equip_uid as i64);
if let Some(old_slot) = avatar
.equip_slot_list
.iter_mut()
.find(|slot| ((**slot >> 32) as u32) == dress_index)
{
*old_slot = slot;
} else {
avatar.equip_slot_list.push(slot);
}
}
self.avatar_map.insert(
avatar.avatar_id as u32,
avatar::ActiveModel {
equip_slot_list: Set(avatar.equip_slot_list.clone()),
..avatar.into()
}
.update(self.context.database)
.await
.expect("avatar::update failed"),
);
Some(updated_avatars.into_iter().collect())
}
pub async fn undress_equipment(&mut self, avatar_id: u32, undress_indexes: &[u32]) -> bool {
let Some(mut avatar) = self.avatar_map.remove(&avatar_id) else {
return false;
};
avatar
.equip_slot_list
.retain(|slot| !undress_indexes.contains(&((*slot >> 32) as u32)));
self.avatar_map.insert(
avatar_id,
avatar::ActiveModel {
equip_slot_list: Set(avatar.equip_slot_list.clone()),
..avatar.into()
}
.update(self.context.database)
.await
.expect("avatar::update failed"),
);
true
}
pub async fn talent_switch(&mut self, avatar_id: u32, talent_switch: Vec<bool>) -> bool {
if avatar_util::is_valid_talent_switch(&talent_switch) {
let required_talent_num = talent_switch
.iter()
.enumerate()
.filter(|(_, b)| **b)
.max_by_key(|(i, _)| *i)
.map(|(i, _)| (i + 1) as i16)
.unwrap_or(0);
if self
.avatar_map
.get(&avatar_id)
.map(|avatar| avatar.unlocked_talent_num >= required_talent_num)
.unwrap_or(false)
{
if let Some(avatar) = self.avatar_map.remove(&avatar_id) {
self.avatar_map.insert(
avatar.avatar_id as u32,
avatar::ActiveModel {
talent_switch: Set(BitSetExt::from_vec(talent_switch)),
..avatar.into()
}
.update(self.context.database)
.await
.expect("avatar::update failed"),
);
return true;
}
}
}
false
}
pub async fn change_avatar_properties(
&mut self,
changes: AvatarPropertyChanges,
) -> Option<Vec<u32>> {
if changes.avatar_id == 0 {
// applies to all
let (updated_models, updated_avatar_ids): (Vec<avatar::ActiveModel>, Vec<u32>) = self
.avatar_map
.iter_mut()
.map(|(id, avatar)| {
let mut model = avatar::ActiveModel::from(avatar.clone());
changes.level.inspect(|&level| {
avatar.level = level;
model.level = Set(level);
});
changes.rank.inspect(|&rank| {
avatar.rank = rank;
model.rank = Set(rank);
});
changes.talent_num.inspect(|&num| {
avatar.unlocked_talent_num = num;
model.unlocked_talent_num = Set(num)
});
(model, *id)
})
.unzip();
self.context
.database
.transaction::<_, (), DbErr>(|txn| {
Box::pin(async move {
for model in updated_models {
model.update(txn).await?;
}
Ok(())
})
})
.await
.expect("change_avatar_properties: update transaction failed");
Some(updated_avatar_ids)
} else {
if let Some(avatar) = self.avatar_map.remove(&changes.avatar_id) {
let model = avatar::ActiveModel {
level: changes.level.map(|level| Set(level)).unwrap_or_default(),
rank: changes.rank.map(|rank| Set(rank)).unwrap_or_default(),
unlocked_talent_num: changes.talent_num.map(|num| Set(num)).unwrap_or_default(),
..avatar.into()
};
self.avatar_map.insert(
changes.avatar_id,
model
.update(self.context.database)
.await
.expect("avatar::update failed"),
);
Some(vec![changes.avatar_id])
} else {
None
}
}
}
pub fn is_avatar_unlocked(&self, avatar_id: u32) -> bool {
self.avatar_map.contains_key(&avatar_id)
}
pub async fn unlock_avatars(&mut self, avatar_id_list: &[i32]) -> Vec<u32> {
let models = avatar_id_list
.iter()
.filter(|id| !self.is_avatar_unlocked(**id as u32))
.map(|id| {
self.context
.filecfg
.avatar_base_template_tb
.data()
.unwrap()
.iter()
.find(|tmpl| tmpl.id() == *id)
})
.flatten()
.map(|tmpl| avatar::Model {
owner_player_uid: self.context.player_uid as i32,
avatar_id: tmpl.id(),
level: 60,
exp: 0,
rank: 6,
passive_skill_level: 6,
skill_type_level: (0..=6).map(|_| 12).collect(),
unlocked_talent_num: 6,
talent_switch: 0b111000,
cur_weapon_uid: 0,
taken_rank_up_reward_list: Vec::with_capacity(0),
first_get_time: time_util::cur_timestamp_seconds(),
show_weapon_type: 0,
avatar_skin_id: 0,
equip_slot_list: Vec::with_capacity(0),
})
.collect::<Vec<_>>();
avatar::Entity::insert_many(
models
.iter()
.map(|model| model.clone().into())
.collect::<Vec<avatar::ActiveModel>>(),
)
.exec(self.context.database)
.await
.expect("avatar::insert_many failed");
let mut added_avatars = Vec::with_capacity(models.len());
for model in models {
added_avatars.push(model.avatar_id as u32);
self.avatar_map.insert(model.avatar_id as u32, model);
}
added_avatars
}
pub fn get_protocol_avatar_list(&self, filter: &[u32]) -> Vec<Avatar> {
self.avatar_map
.values()
.filter(|avatar| filter.is_empty() || filter.contains(&(avatar.avatar_id as u32)))
.map(|avatar| Avatar {
id: avatar.avatar_id as u32,
level: avatar.level as u32,
exp: avatar.exp as u32,
rank: avatar.rank as u32,
passive_skill_level: avatar.passive_skill_level as u32,
unlocked_talent_num: avatar.unlocked_talent_num as u32,
cur_weapon_uid: avatar.cur_weapon_uid as u32,
first_get_time: avatar.first_get_time,
show_weapon_type: avatar.show_weapon_type as i32,
avatar_skin_id: avatar.avatar_skin_id as u32,
talent_switch_list: avatar.talent_switch.to_vec(6),
taken_rank_up_reward_list: avatar
.taken_rank_up_reward_list
.iter()
.map(|&i| i as u32)
.collect(),
dressed_equip_list: avatar
.equip_slot_list
.iter()
.map(|&slot| DressedEquip {
index: (slot >> 32) as u32,
equip_uid: (slot & 0xFFFFFFFF) as u32,
})
.collect(),
skill_type_level: avatar
.skill_type_level
.iter()
.enumerate()
.map(|(st, level)| AvatarSkillLevel {
skill_type: st as u32,
level: *level as u32,
})
.collect(),
})
.collect()
}
async fn load_or_create_avatar_map(context: &NapContext) -> HashMap<u32, avatar::Model> {
let player_uid = context.player_uid as i32;
match avatar::Entity::find()
.filter(avatar::Column::OwnerPlayerUid.eq(player_uid))
.all(context.database)
.await
.expect("avatar::find(all) failed")
{
list if list.is_empty() => {
let initial_map = Self::create_initial_avatar_map(context);
avatar::Entity::insert_many(
initial_map
.values()
.cloned()
.map(avatar::ActiveModel::from)
.collect::<Vec<_>>(),
)
.exec(context.database)
.await
.expect("avatar::insert_many(initial_list) failed");
initial_map
}
list => list
.into_iter()
.map(|avatar| (avatar.avatar_id as u32, avatar))
.collect(),
}
}
fn create_initial_avatar_map(context: &NapContext) -> HashMap<u32, avatar::Model> {
HashMap::from([(
1011,
avatar::Model {
owner_player_uid: context.player_uid as i32,
avatar_id: 1011,
level: 60,
exp: 0,
rank: 6,
passive_skill_level: 6,
skill_type_level: (0..=6).map(|_| 12).collect(),
unlocked_talent_num: 6,
talent_switch: 0b111000,
cur_weapon_uid: 0,
taken_rank_up_reward_list: Vec::with_capacity(0),
first_get_time: time_util::cur_timestamp_seconds(),
show_weapon_type: 0,
avatar_skin_id: 0,
equip_slot_list: Vec::with_capacity(0),
},
)])
}
}