Added FirstLoginConfig

This commit is contained in:
traffic95 2025-05-24 00:12:49 +02:00
parent aec5a50d3e
commit ad0d79acc1
7 changed files with 157 additions and 45 deletions

View file

@ -0,0 +1,23 @@
interknot_level = 60
control_avatar_id = 2011
control_guise_avatar_id = 1371
day_of_week = 5
start_main_quest = false
default_section_id = 1
[avatar]
unlock_all = true
unlock_id_list = [ 1011, 1081 ]
level = 60
rank = 6
unlocked_talent_num = 0
talent_switch = [ false, false, false, false, false, false ]
passive_skill_level = 7
skill_level_map = [ 12, 12, 12, 12, 7, 7, 12 ]
[weapon]
unlock_all = true
unlock_id_list = [ 13101 ]
level = 60
star = 5
refine_level = 1

View file

@ -43,3 +43,38 @@ pub struct GachaMaterialConfig {
pub id: u32,
pub count: u32,
}
#[derive(Deserialize)]
pub struct FirstLoginConfig {
pub interknot_level: u32,
pub control_avatar_id: u32,
pub control_guise_avatar_id: u32,
pub day_of_week: u32,
pub start_main_quest: bool,
pub default_section_id: u32,
pub avatar: FirstLoginAvatarConfig,
pub weapon: FirstLoginWeaponConfig,
}
#[derive(Deserialize)]
pub struct FirstLoginAvatarConfig {
pub unlock_all: bool,
#[serde(default)]
pub unlock_id_list: Vec<u32>,
pub level: u32,
pub rank: u32,
pub unlocked_talent_num: u32,
pub talent_switch: Vec<bool>,
pub passive_skill_level: u32,
pub skill_level_map: Vec<u32>,
}
#[derive(Deserialize)]
pub struct FirstLoginWeaponConfig {
pub unlock_all: bool,
#[serde(default)]
pub unlock_id_list: Vec<u32>,
pub level: u32,
pub star: u32,
pub refine_level: u32,
}

View file

@ -10,11 +10,10 @@ use vivian_logic::{
use vivian_proto::server_only::{AvatarData, AvatarItemInfo};
use crate::{
logic::{
config::FirstLoginAvatarConfig, logic::{
property::{Property, PropertyHashMap},
sync::{LoginDataSyncComponent, PlayerSyncComponent, SyncType},
},
resources::NapResources,
}, resources::NapResources
};
use super::{Model, Saveable};
@ -28,16 +27,24 @@ pub struct AvatarModel {
impl AvatarModel {
pub fn on_first_login(&mut self, res: &NapResources) {
const STARTING_AVATARS: &[u32] = &[1011, 1081];
let cfg = &res.gameplay.first_login.avatar;
STARTING_AVATARS
.iter()
.filter_map(|id| {
res.templates
.avatar_base_template_tb()
.find(|tmpl| tmpl.id() == *id)
})
.for_each(|tmpl| self.unlock_avatar(&tmpl, None));
if cfg.unlock_all {
res
.templates
.avatar_base_template_tb()
.for_each(|tmpl| self.unlock_avatar(&tmpl, None, Some(cfg)));
} else {
cfg
.unlock_id_list
.iter()
.filter_map(|id| {
res.templates
.avatar_base_template_tb()
.find(|tmpl| tmpl.id() == *id)
})
.for_each(|tmpl| self.unlock_avatar(&tmpl, None, Some(cfg)));
}
}
pub fn send_add_avatar_notify(&self, listener: &mut dyn NotifyListener) {
@ -96,6 +103,7 @@ impl AvatarModel {
&mut self,
base_template: &AvatarBaseTemplate,
perform_type: Option<vivian_proto::add_avatar_sc_notify::PerformType>,
first_login_cfg: Option<&FirstLoginAvatarConfig>,
) {
const AVATAR_BLACKLIST: &[u32] = &[];
@ -105,20 +113,28 @@ impl AvatarModel {
&& !self.avatar_map.contains_key(&avatar_id)
&& !AVATAR_BLACKLIST.contains(&avatar_id)
{
let mut skill_level_map: HashMap<EAvatarSkillType, u32> = (0..EAvatarSkillType::EnumCount.into())
.map(|ty| EAvatarSkillType::try_from(ty).unwrap())
.zip([0].into_iter().cycle())
.collect();
if let Some(cfg) = first_login_cfg {
skill_level_map = (0..EAvatarSkillType::EnumCount.into())
.map(|ty| EAvatarSkillType::try_from(ty).unwrap())
.zip(cfg.skill_level_map.clone())
.collect();
}
self.avatar_map.insert(
avatar_id,
AvatarItem {
id: avatar_id,
level: 1,
level: first_login_cfg.map_or(1, |cfg| cfg.level),
exp: 0,
rank: 1,
unlocked_talent_num: 0,
talent_switch: [false; 6],
passive_skill_level: 0,
skill_level_map: (0..EAvatarSkillType::EnumCount.into())
.map(|ty| EAvatarSkillType::try_from(ty).unwrap())
.zip([0].into_iter().cycle())
.collect(),
rank: first_login_cfg.map_or(1, |cfg| cfg.rank),
unlocked_talent_num: first_login_cfg.map_or(0, |cfg| cfg.unlocked_talent_num),
talent_switch: first_login_cfg.map_or([false; 6], |cfg| cfg.talent_switch.as_slice().try_into().unwrap()),
passive_skill_level: first_login_cfg.map_or(0, |cfg| cfg.passive_skill_level),
skill_level_map,
weapon_uid: 0,
dressed_equip_map: HashMap::new(),
first_get_time: time_util::unix_timestamp_seconds(),

View file

@ -37,21 +37,47 @@ impl ItemModel {
.avatar_skin_base_template_tb()
.for_each(|tmpl| self.item_count_map.insert(tmpl.id(), 1));
// Unlock all weapons as well
res.templates.weapon_template_tb().for_each(|tmpl| {
let uid = self.next_uid();
self.weapon_map.insert(
uid,
WeaponItem {
id: tmpl.item_id(),
level: 60,
exp: 0,
star: tmpl.star_limit() + 1,
refine_level: tmpl.refine_limit(),
lock: false,
},
);
});
let weapon_cfg = &res.gameplay.first_login.weapon;
if weapon_cfg.unlock_all {
res.templates.weapon_template_tb().for_each(|tmpl| {
let uid = self.next_uid();
self.weapon_map.insert(
uid,
WeaponItem {
id: tmpl.item_id(),
level: weapon_cfg.level,
exp: 0,
star: weapon_cfg.star,
refine_level: weapon_cfg.refine_level,
lock: false,
},
);
});
} else {
weapon_cfg
.unlock_id_list
.iter()
.filter_map(|id| {
res.templates
.weapon_template_tb()
.find(|tmpl| tmpl.item_id() == *id)
})
.for_each(|tmpl| {
let uid = self.next_uid();
self.weapon_map.insert(
uid,
WeaponItem {
id: tmpl.item_id(),
level: weapon_cfg.level,
exp: 0,
star: weapon_cfg.star,
refine_level: weapon_cfg.refine_level,
lock: false,
},
);
});
}
}
pub fn add_weapon(&mut self, template: &WeaponTemplate) -> u32 {

View file

@ -140,21 +140,28 @@ impl Player {
// and it won't be needed to set avatar ids in basic module
// (player will select them in beginner procedure)
let cfg = &self.resources.gameplay.first_login;
self.avatar_model.on_first_login(self.resources);
self.basic_model.level.set(1);
self.basic_model.avatar_id.set(2011);
self.basic_model.control_avatar_id.set(2011);
self.basic_model.control_guise_avatar_id.set(2011);
self.basic_model.level.set(cfg.interknot_level);
self.basic_model.avatar_id.set(cfg.control_avatar_id);
self.basic_model.control_avatar_id.set(cfg.control_avatar_id);
self.basic_model.control_guise_avatar_id.set(cfg.control_guise_avatar_id);
self.main_city_model.day_of_week.set(5); // Friday
self.main_city_model.day_of_week.set(cfg.day_of_week);
self.item_model.on_first_login(self.resources);
self.misc_model.on_first_login(self.resources);
self.gacha_model.on_first_login();
let mut main_city_quest_id = 10020001;
if !cfg.start_main_quest {
main_city_quest_id = 10020028;
}
self.quest_model
.add_main_city_quest(10020001, self.resources);
.add_main_city_quest(main_city_quest_id, self.resources);
// Initialize hall scene with WorkShop section
let scene_uid = self.scene_model.next_scene_uid();
@ -163,7 +170,7 @@ impl Player {
scene::SceneSnapshot {
scene_id: 1,
ext: scene::SceneSnapshotExt::Hall(scene::HallSceneSnapshot {
cur_section_id: 2,
cur_section_id: cfg.default_section_id,
sections: HashMap::new(),
main_city_objects_state: HashMap::new(),
}),
@ -234,7 +241,7 @@ impl Player {
AddItemSource::Mail => Some(PerformType::PerformAnimation),
};
self.avatar_model.unlock_avatar(&template, perform_type);
self.avatar_model.unlock_avatar(&template, perform_type, None);
}
None
}

View file

@ -47,6 +47,10 @@ async fn main() -> Result<(), StartupError> {
concatcp!(CONFIG_DIR, "gacha_schedule.toml"),
include_str!("../gacha_schedule.default.toml"),
),
first_login: common::config_util::load_or_create(
concatcp!(CONFIG_DIR, "first_login.toml"),
include_str!("../first_login.default.toml"),
),
};
let resources = NapResources::load(&config.resources, gameplay_cfg)?;

View file

@ -5,7 +5,7 @@ use config::{
LoadConditionsError, TemplateCollection, TemplateCollectionError,
};
use crate::config::{GachaScheduleConfig, ResourceConfig};
use crate::config::{FirstLoginConfig, GachaScheduleConfig, ResourceConfig};
#[derive(thiserror::Error, Debug)]
pub enum LoadResourcesError {
@ -40,6 +40,7 @@ pub struct NapResources {
pub struct ServerGameplayConfig {
pub gacha_schedule: GachaScheduleConfig,
pub first_login: FirstLoginConfig,
}
impl NapResources {