Compare commits

...

2 commits

Author SHA1 Message Date
05034b1774 Fix formation (#15)
Reviewed-on: WutheringSlaves/wicked-waifus-rs#15
2025-05-24 20:27:04 +00:00
4d6b79aa29 2.4.1 (#9)
Review this, includes:

- Unlocks for weapons
    - All weapons unlock
    - Max level weapons
    - Max resonance weapons
- Fixed unlocks for roles, now max level, breakthrought and resonance come from bindata
- Unlocks for all skin types(flight, wing, role, skin)
- Weapon inventory implemented
- Change role skin implemented
- Change weapon skin implmented
- Change flight skin implemented
- Change wing skin implemented
- Change weapon implemented(basic, no attributes, no switch)
- Created SequenceGenerator to emulate sql sequences with reusable ids

Known bugs:

- In prod only one projection can be equipped simulataneously, we dont care :xdskull:
- Change skin will plot with invalid Rotation so a change of character is required, investigate this

Reviewed-on: WutheringSlaves/wicked-waifus-rs#9
Reviewed-by: RabbyDevs <rabbydevs@gmail.com>
2025-05-23 01:33:05 +00:00
28 changed files with 1363 additions and 191 deletions

127
Cargo.lock generated
View file

@ -276,9 +276,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.22"
version = "1.2.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1"
checksum = "16595d3be041c03b09d08d0858631facccee9221e579704070e6e9e4915d3bc7"
dependencies = [
"jobserver",
"libc",
@ -902,9 +902,9 @@ dependencies = [
[[package]]
name = "hyper-util"
version = "0.1.11"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2"
checksum = "cf9f1e950e0d9d1d3c47184416723cf29c0d1f93bd8cccf37e4beb6b44f31710"
dependencies = [
"bytes",
"futures-util",
@ -965,9 +965,9 @@ checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
[[package]]
name = "icu_properties"
version = "2.0.0"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2549ca8c7241c82f59c80ba2a6f415d931c5b58d24fb8412caa1a1f02c49139a"
checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
dependencies = [
"displaydoc",
"icu_collections",
@ -981,9 +981,9 @@ dependencies = [
[[package]]
name = "icu_properties_data"
version = "2.0.0"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8197e866e47b68f8f7d95249e172903bec06004b18b2937f1095d40a0c57de04"
checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
[[package]]
name = "icu_provider"
@ -1118,6 +1118,26 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "liblzma"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66352d7a8ac12d4877b6e6ea5a9b7650ee094257dc40889955bea5bc5b08c1d0"
dependencies = [
"liblzma-sys",
]
[[package]]
name = "liblzma-sys"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5839bad90c3cc2e0b8c4ed8296b80e86040240f81d46b9c0e9bc8dd51ddd3af1"
dependencies = [
"cc",
"libc",
"pkg-config",
]
[[package]]
name = "libm"
version = "0.2.15"
@ -1207,27 +1227,6 @@ version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "lzma-rs"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e"
dependencies = [
"byteorder",
"crc",
]
[[package]]
name = "lzma-sys"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27"
dependencies = [
"cc",
"libc",
"pkg-config",
]
[[package]]
name = "matchers"
version = "0.1.0"
@ -1295,13 +1294,13 @@ dependencies = [
[[package]]
name = "mio"
version = "1.0.3"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
dependencies = [
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@ -1872,9 +1871,9 @@ dependencies = [
[[package]]
name = "rustversion"
version = "1.0.20"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
[[package]]
name = "ryu"
@ -2073,9 +2072,9 @@ dependencies = [
[[package]]
name = "sqlx"
version = "0.8.5"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3c3a85280daca669cfd3bcb68a337882a8bc57ec882f72c5d13a430613a738e"
checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc"
dependencies = [
"sqlx-core",
"sqlx-macros",
@ -2086,9 +2085,9 @@ dependencies = [
[[package]]
name = "sqlx-core"
version = "0.8.5"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f743f2a3cea30a58cd479013f75550e879009e3a02f616f18ca699335aa248c3"
checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6"
dependencies = [
"base64",
"bytes",
@ -2122,9 +2121,9 @@ dependencies = [
[[package]]
name = "sqlx-macros"
version = "0.8.5"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4200e0fde19834956d4252347c12a083bdcb237d7a1a1446bffd8768417dce"
checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d"
dependencies = [
"proc-macro2",
"quote",
@ -2135,9 +2134,9 @@ dependencies = [
[[package]]
name = "sqlx-macros-core"
version = "0.8.5"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "882ceaa29cade31beca7129b6beeb05737f44f82dbe2a9806ecea5a7093d00b7"
checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b"
dependencies = [
"dotenvy",
"either",
@ -2154,16 +2153,15 @@ dependencies = [
"sqlx-postgres",
"sqlx-sqlite",
"syn",
"tempfile",
"tokio",
"url",
]
[[package]]
name = "sqlx-mysql"
version = "0.8.5"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0afdd3aa7a629683c2d750c2df343025545087081ab5942593a5288855b1b7a7"
checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526"
dependencies = [
"atoi",
"base64",
@ -2203,9 +2201,9 @@ dependencies = [
[[package]]
name = "sqlx-postgres"
version = "0.8.5"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0bedbe1bbb5e2615ef347a5e9d8cd7680fb63e77d9dafc0f29be15e53f1ebe6"
checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46"
dependencies = [
"atoi",
"base64",
@ -2240,9 +2238,9 @@ dependencies = [
[[package]]
name = "sqlx-sqlite"
version = "0.8.5"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c26083e9a520e8eb87a06b12347679b142dc2ea29e6e409f805644a7a979a5bc"
checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea"
dependencies = [
"atoi",
"flume",
@ -2422,9 +2420,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.45.0"
version = "1.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165"
checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
dependencies = [
"backtrace",
"bytes",
@ -2739,11 +2737,13 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "uuid"
version = "1.16.0"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9"
checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d"
dependencies = [
"getrandom 0.3.3",
"js-sys",
"wasm-bindgen",
]
[[package]]
@ -2793,6 +2793,7 @@ checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
]
@ -3026,7 +3027,7 @@ dependencies = [
[[package]]
name = "wicked-waifus-protocol"
version = "0.1.0"
source = "git+https://git.xeondev.com/wickedwaifus/wicked-waifus-proto#6f2733c09b78a6e874f379254c9b71fbef23c3a0"
source = "git+https://git.xeondev.com/wickedwaifus/wicked-waifus-proto#316e3ee06f5ee0405e4cab567e67f4315c0f2cfb"
dependencies = [
"byteorder",
"crc32fast",
@ -3044,7 +3045,7 @@ dependencies = [
[[package]]
name = "wicked-waifus-protocol-derive"
version = "0.1.0"
source = "git+https://git.xeondev.com/wickedwaifus/wicked-waifus-proto#6f2733c09b78a6e874f379254c9b71fbef23c3a0"
source = "git+https://git.xeondev.com/wickedwaifus/wicked-waifus-proto#316e3ee06f5ee0405e4cab567e67f4315c0f2cfb"
dependencies = [
"proc-macro2",
"quote",
@ -3280,15 +3281,6 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
[[package]]
name = "xz2"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2"
dependencies = [
"lzma-sys",
]
[[package]]
name = "yoke"
version = "0.8.0"
@ -3436,9 +3428,9 @@ dependencies = [
[[package]]
name = "zip"
version = "3.0.0"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12598812502ed0105f607f941c386f43d441e00148fce9dec3ca5ffb0bde9308"
checksum = "153a6fff49d264c4babdcfa6b4d534747f520e56e8f0f384f3b808c4b64cc1fd"
dependencies = [
"aes",
"arbitrary",
@ -3450,12 +3442,11 @@ dependencies = [
"getrandom 0.3.3",
"hmac",
"indexmap",
"lzma-rs",
"liblzma",
"memchr",
"pbkdf2",
"sha1",
"time",
"xz2",
"zeroize",
"zopfli",
"zstd",

View file

@ -55,7 +55,7 @@ wicked-waifus-network = { path = "wicked-waifus-network" }
wicked-waifus-protocol-internal = { path = "wicked-waifus-protocol-internal" }
wicked-waifus-protokey = { path = "wicked-waifus-protokey" }
wicked-waifus-protocol = { git = "https://git.xeondev.com/wickedwaifus/wicked-waifus-proto" }
wicked-waifus-protocol = { git = "https://git.xeondev.com/wickedwaifus/wicked-waifus-proto", features = ["debug"] }
wicked-waifus-protocol-derive = { git = "https://git.xeondev.com/wickedwaifus/wicked-waifus-proto" }
[profile.release]

View file

@ -0,0 +1,55 @@
use serde::Deserialize;
#[derive(Deserialize)]
#[cfg_attr(feature = "strict_json_fields", serde(deny_unknown_fields))]
#[serde(rename_all = "PascalCase")]
pub struct FlySkinConfigData {
pub id: i32,
pub skin_type: i32,
#[cfg(feature = "strict_json_fields")]
pub model_id: i32,
#[cfg(feature = "strict_json_fields")]
pub stand_anim: String,
pub quality_id: i32,
pub skin_grade: i32,
#[cfg(feature = "strict_json_fields")]
pub name: String,
#[cfg(feature = "strict_json_fields")]
pub type_description: String,
#[cfg(feature = "strict_json_fields")]
pub attributes_description: String,
#[cfg(feature = "strict_json_fields")]
pub bg_description: String,
#[cfg(feature = "strict_json_fields")]
pub icon: String,
#[cfg(feature = "strict_json_fields")]
pub icon_middle: String,
#[cfg(feature = "strict_json_fields")]
pub icon_small: String,
#[cfg(feature = "strict_json_fields")]
pub mesh: String,
#[cfg(feature = "strict_json_fields")]
pub obtained_show_description: String,
#[cfg(feature = "strict_json_fields")]
pub show_in_bag: bool,
#[cfg(feature = "strict_json_fields")]
pub item_access: Vec<i32>,
#[cfg(feature = "strict_json_fields")]
pub sort_index: i32,
#[cfg(feature = "strict_json_fields")]
pub red_dot_disable_rule: i32,
#[cfg(feature = "strict_json_fields")]
pub preview_texture_in_buy_view: String,
#[cfg(feature = "strict_json_fields")]
pub preview_texture_in_pay_shop: String,
#[cfg(feature = "strict_json_fields")]
pub preview_texture_in_pop: String,
#[cfg(feature = "strict_json_fields")]
pub skin_obtain_color1: String,
#[cfg(feature = "strict_json_fields")]
pub skin_obtain_color2: String,
#[cfg(feature = "strict_json_fields")]
pub skin_obtain_image: String,
#[cfg(feature = "strict_json_fields")]
pub is_special_view_after_obtain: bool,
}

View file

@ -125,6 +125,7 @@ json_data! {
FavorLevel;
FavorStory;
FavorWord;
FlySkinConfig;
ForgeFormula;
FunctionCondition;
Gacha;
@ -145,7 +146,6 @@ json_data! {
ResonanceAmplification;
ResonantChain;
RoleBreach;
RoleExpItem;
RoleInfo;
RoleLevelConsume;
RolePropertyGrowth;
@ -159,6 +159,7 @@ json_data! {
WeaponLevel;
WeaponPropertyGrowth;
WeaponReson;
WeaponSkin;
}
json_hash_table_data! {
@ -167,6 +168,7 @@ json_hash_table_data! {
BlueprintConfig, blueprint_type, String;
DragonPool, id, i32;
DropPackage, id, i32;
RoleExpItem, id, i32;
TemplateConfig, blueprint_type, String;
}
@ -184,7 +186,10 @@ pub mod level_entity_config_data {
}
pub fn get(map_id: i32, entity_id: i64) -> Option<&'static Data> {
TABLE.get().unwrap().get(&create_key_internal(map_id, entity_id))
TABLE
.get()
.unwrap()
.get(&create_key_internal(map_id, entity_id))
}
#[inline(always)]
@ -207,10 +212,9 @@ fn load_json_entity_level_config_data(base_path: &str) -> Result<(), LoadDataErr
serde_json::from_reader::<BufReader<File>, Vec<level_entity_config_data::Data>>(reader)?
.into_iter()
.map(|element| (level_entity_config_data::create_key(&element), element))
.collect::<std::collections::HashMap<_, _>>()
.collect::<std::collections::HashMap<_, _>>(),
);
tracing::info!("Loading data finished: {path}");
Ok(())
}
}

View file

@ -0,0 +1,54 @@
use serde::Deserialize;
#[derive(Deserialize)]
#[cfg_attr(feature = "strict_json_fields", serde(deny_unknown_fields))]
#[serde(rename_all = "PascalCase")]
pub struct WeaponSkinData {
pub id: i32,
pub weapon_skin_type: i32,
#[cfg(feature = "strict_json_fields")]
pub name: String,
pub hide_in_skin_view: bool,
pub quality_id: i32,
#[cfg(feature = "strict_json_fields")]
pub model_id: i32,
#[cfg(feature = "strict_json_fields")]
pub transform_id: i32,
#[cfg(feature = "strict_json_fields")]
pub models: Vec<i32>,
#[cfg(feature = "strict_json_fields")]
pub type_description: String,
#[cfg(feature = "strict_json_fields")]
pub attributes_description: String,
#[cfg(feature = "strict_json_fields")]
pub bg_description: String,
#[cfg(feature = "strict_json_fields")]
pub card_icon_path: String,
#[cfg(feature = "strict_json_fields")]
pub icon: String,
#[cfg(feature = "strict_json_fields")]
pub icon_middle: String,
#[cfg(feature = "strict_json_fields")]
pub icon_small: String,
#[cfg(feature = "strict_json_fields")]
#[serde(rename = "MaxCapcity")] // kuro!
pub max_capacity: i32,
#[cfg(feature = "strict_json_fields")]
pub item_access: Vec<i32>,
#[cfg(feature = "strict_json_fields")]
pub obtained_show: i32,
#[cfg(feature = "strict_json_fields")]
pub obtained_show_description: String,
#[cfg(feature = "strict_json_fields")]
pub num_limit: i32,
#[cfg(feature = "strict_json_fields")]
pub show_in_bag: bool,
#[cfg(feature = "strict_json_fields")]
pub sort_index: i32,
#[cfg(feature = "strict_json_fields")]
pub hidden_time: i32,
#[cfg(feature = "strict_json_fields")]
pub destructible: bool,
#[cfg(feature = "strict_json_fields")]
pub red_dot_disable_rule: i32,
}

View file

@ -19,7 +19,7 @@ load_textmaps = true
quadrant_size = 1000000
[asset_config]
asset_url = "https://git.xeondev.com/wickedwaifus/wicked-waifus-data/releases/download/pioneer_2.4.1/bundle.zip"
asset_url = "https://git.xeondev.com/wickedwaifus/wicked-waifus-data/releases/download/pioneer_2.4.4/bundle.zip"
buffer_size = 268435456
[default_unlocks]
@ -28,8 +28,15 @@ unlock_all_roles_max_level = false
unlock_all_roles_all_sequences = false
unlock_all_mc_elements = true
unlock_all_weapons = false
unlock_all_weapons_max_level = false
unlock_all_weapons_all_reson = false
unlock_all_adventures = false
unlock_all_functions = true
unlock_all_guides = false
unlock_all_tutorials = false
unlock_all_teleporter = false
unlock_all_role_skins = false
# TODO: Set this to the same value as unlock_all_role_skins, without it, it fails(maybe jinshi weapon skin problem??)
unlock_all_weapon_skins = false
unlock_all_fly_skins = false
unlock_all_wing_skins = false

View file

@ -37,12 +37,18 @@ pub struct DefaultUnlocks {
pub unlock_all_roles_max_level: bool,
pub unlock_all_roles_all_sequences: bool,
pub unlock_all_mc_elements: bool,
pub unlock_all_weapons: bool, // TODO:
pub unlock_all_weapons: bool,
pub unlock_all_weapons_max_level: bool,
pub unlock_all_weapons_all_reson: bool,
pub unlock_all_adventures: bool,
pub unlock_all_functions: bool,
pub unlock_all_guides: bool,
pub unlock_all_tutorials: bool,
pub unlock_all_teleporter: bool,
pub unlock_all_role_skins: bool,
pub unlock_all_weapon_skins: bool,
pub unlock_all_fly_skins: bool,
pub unlock_all_wing_skins: bool,
}
impl TomlConfig for ServiceConfig {

View file

@ -19,6 +19,7 @@ mod summoner;
mod tag;
mod visibility;
mod vision_skill;
mod weapon_skin;
pub use attribute::Attribute;
pub use autonomous::Autonomous;
@ -41,3 +42,4 @@ pub use summoner::Summoner;
pub use tag::Tag;
pub use visibility::Visibility;
pub use vision_skill::VisionSkill;
pub use weapon_skin::WeaponSkin;

View file

@ -0,0 +1,17 @@
use wicked_waifus_protocol::entity_component_pb::ComponentPb;
use wicked_waifus_protocol::{EntityComponentPb, WeaponSkinComponentPb};
use crate::logic::ecs::component::Component;
pub struct WeaponSkin {
pub skin_id: i32,
}
impl Component for WeaponSkin {
fn set_pb_data(&self, pb: &mut wicked_waifus_protocol::EntityPb) {
pb.component_pbs.push(EntityComponentPb {
component_pb: Some(ComponentPb::WeaponSkinComponentPb(WeaponSkinComponentPb {
weapon_skin_id: self.skin_id,
})),
})
}
}

View file

@ -44,6 +44,7 @@ impl_component_container! {
Summoner;
SoarWingSkin;
ParaglidingSkin;
WeaponSkin;
}
pub trait Component {

View file

@ -113,7 +113,13 @@ impl GachaPool {
match player.role_list.get(&item_id) {
None => {
if required_role_ids.contains(&item_id) {
player.role_list.insert(item_id, Role::new(item_id));
let role = Role::new(item_id);
let role_id = role.role_id;
let weapon_id = role.equip_weapon;
player.role_list.insert(item_id, role);
// TODO notifies player update
player.inventory.add_weapon(role_id, 0, 1, 0, 0, 0, weapon_id).unwrap();
// TODO notifies weapon update
}
}
Some(role) => {

View file

@ -33,7 +33,6 @@ dummy_handler! {
ExchangeReward;
Liveness;
PhotoMemory;
WeaponSkin;
VisionEquipGroupInfo;
UpdatePlayStationBlockAccount;
AdventureManual;

View file

@ -12,12 +12,11 @@ pub fn on_normal_item_request(
}
pub fn on_weapon_item_request(
_player: &mut Player,
player: &mut Player,
_: WeaponItemRequest,
_response: &mut WeaponItemResponse,
response: &mut WeaponItemResponse,
) {
// TODO: Implement this
tracing::warn!("Unhandled WeaponItemRequest");
response.weapon_item_list = player.inventory.to_weapon_item_list();
}
pub fn on_phantom_item_request(

View file

@ -19,6 +19,7 @@ use wicked_waifus_protocol::message::Message;
pub use skill::*;
pub use teleport::*;
pub use tutorial::*;
pub use weapon::*;
mod advice;
mod animal;
@ -40,6 +41,7 @@ mod scene;
mod skill;
mod teleport;
mod tutorial;
mod weapon;
macro_rules! handle_request {
($($name:ident $(, $inner_package:ident)?;)*) => {
@ -200,13 +202,19 @@ handle_request! {
RoleFavorList;
FormationAttr;
UpdateFormation;
UnlockRoleSkinList;
RoleSkinChange;
FlySkinWear;
FlySkinWearAllRole;
RoleLevelUpView;
PbUpLevelRole;
RoleBreakThroughView;
// Scene (TODO: Review this on_..., port some from go)
SceneTrace;
SceneLoadingFinish;
UpdateSceneDate;
AccessPathTimeServerConfig;
UnlockRoleSkinList;
PlayerHeadData;
// Shop (TODO: Review this on_..., port some from go)
@ -237,6 +245,12 @@ handle_request! {
TutorialReceive;
TutorialUnlock;
// Weapon
WeaponSkin;
EquipWeaponSkin;
SendEquipSkin;
EquipTakeOn;
// TODO: Implement all this properly, workaround for game enter
EntityPatrolStop;
InitRange;
@ -264,7 +278,6 @@ handle_request! {
Liveness;
WebSign;
PhotoMemory;
WeaponSkin;
VisionEquipGroupInfo;
UpdatePlayStationBlockAccount;
AdventureManual;

View file

@ -1,14 +1,11 @@
use std::collections::HashSet;
use wicked_waifus_protocol::{
ClientCurrentRoleReportRequest, ClientCurrentRoleReportResponse, ERemoveEntityType, ErrorCode,
FormationAttrRequest, FormationAttrResponse, PlayerMotionRequest, PlayerMotionResponse,
RoleFavorListRequest, RoleFavorListResponse, RoleShowListUpdateRequest,
RoleShowListUpdateResponse, UpdateFormationRequest, UpdateFormationResponse,
};
use crate::logic::player::Player;
use crate::logic::components::{ParaglidingSkin, RoleSkin, SoarWingSkin, WeaponSkin};
use crate::logic::ecs::component::ComponentContainer;
use crate::logic::player::{ItemUsage, Player};
use crate::logic::role::{Role, RoleFormation};
use crate::modify_component;
use wicked_waifus_protocol::{ArrayIntInt, ClientCurrentRoleReportRequest, ClientCurrentRoleReportResponse, ERemoveEntityType, EntityAddNotify, EntityEquipSkinChangeNotify, EntityFlySkinChangeData, EntityPb, EntityRemoveInfo, EntityRemoveNotify, EquipFlySkinData, ErrorCode, FlySkinConfigData, FlySkinWearAllRoleRequest, FlySkinWearAllRoleResponse, FlySkinWearRequest, FlySkinWearResponse, FormationAttrRequest, FormationAttrResponse, PbUpLevelRoleRequest, PbUpLevelRoleResponse, PlayerMotionRequest, PlayerMotionResponse, RoleBreakThroughViewRequest, RoleBreakThroughViewResponse, RoleFavorListRequest, RoleFavorListResponse, RoleFlyEquipChangeNotify, RoleLevelUpViewRequest, RoleLevelUpViewResponse, RoleShowListUpdateRequest, RoleShowListUpdateResponse, RoleSkinChangeRequest, RoleSkinChangeResponse, SoarWingOrParaglidingSkinChangeNotify, UnlockRoleSkinListRequest, UnlockRoleSkinListResponse, UpdateFormationRequest, UpdateFormationResponse, WeaponSkinComponentPb};
pub fn on_role_show_list_update_request(
player: &mut Player,
@ -103,7 +100,7 @@ pub fn on_update_formation_request(
if !added_roles.is_empty() {
// add new roles
player.notify(player.build_player_entity_add_notify(added_roles));
player.notify(player.build_player_entity_add_notify(added_roles, world));
}
// send update group formation notify
@ -152,3 +149,421 @@ pub fn on_player_motion_request(
Some(_) => response.error_id = ErrorCode::Success.into(),
}
}
pub fn on_unlock_role_skin_list_request(
player: &Player,
_request: UnlockRoleSkinListRequest,
response: &mut UnlockRoleSkinListResponse,
) {
response.role_skin_list = player.unlocked_skins.role_skins.iter().cloned().collect();
}
pub fn on_role_skin_change_request(
player: &mut Player,
request: RoleSkinChangeRequest,
response: &mut RoleSkinChangeResponse,
) {
// TODO: Should we verify role id first against bindata?
let role = player.role_list.get_mut(&request.role_id);
let Some(role) = role else {
response.error_code = ErrorCode::NotValidRole.into();
return;
};
// Verify Id exist in bindata
let Some(skin_data) =
wicked_waifus_data::role_skin_data::iter().find(|data| data.id == request.skin_id)
else {
response.error_code = ErrorCode::ErrRoleSkinConfig.into();
return;
};
// Verify Skin is unlocked
if !player.unlocked_skins.role_skins.contains(&skin_data.id) {
response.error_code = ErrorCode::ErrRoleSkinLocked.into();
return;
}
// Check that role has this skin
if skin_data.role_id != role.role_id {
response.error_code = ErrorCode::ErrRoleSkinNotMatch.into();
return;
}
role.skin_id = request.skin_id;
if request.is_wear_weapon_skin {
if skin_data.suit_weapon_skin_id == 0 {
response.error_code = ErrorCode::ErrRoleSkinWeaponNotSuit.into();
return;
}
role.weapon_skin_id = skin_data.suit_weapon_skin_id;
}
{
let world_ref = player.world.borrow();
let world = world_ref.get_world_entity();
let entity_id = world.get_entity_id(request.role_id);
modify_component!(
world.get_entity_components(entity_id as i32),
RoleSkin,
|skin_component: &mut RoleSkin| {
skin_component.skin_id = role.skin_id;
}
);
if request.is_wear_weapon_skin {
// Check for suit_weapon_skin_id == 0 has already been done
modify_component!(
world.get_entity_components(entity_id as i32),
WeaponSkin,
|skin_component: &mut WeaponSkin| {
skin_component.skin_id = skin_data.suit_weapon_skin_id;
}
);
// Since the whole entity is recreated this shouldn't be needed but meh, whatever
player.notify(EntityEquipSkinChangeNotify {
entity_id,
weapon_skin_component_pb: Some(WeaponSkinComponentPb {
weapon_skin_id:skin_data.suit_weapon_skin_id,
}),
});
}
player.notify(EntityRemoveNotify {
remove_infos: vec![EntityRemoveInfo {
entity_id,
r#type: 0,
}],
is_remove: false,
});
let mut pb = EntityPb {
id: entity_id,
..Default::default()
};
world
.get_entity_components(entity_id as i32)
.into_iter()
.for_each(|comp| comp.set_pb_data(&mut pb));
player.notify(EntityAddNotify {
entity_pbs: vec![pb],
remove_tag_ids: false,
});
}
player.notify(player.build_update_formation_notify());
response.error_code = ErrorCode::Success.into();
}
pub fn on_fly_skin_wear_request(
player: &mut Player,
request: FlySkinWearRequest,
response: &mut FlySkinWearResponse,
) {
let role = player.role_list.get_mut(&request.role_id);
let Some(role) = role else {
response.error_code = ErrorCode::NotValidRole.into();
return;
};
// Verify Id exist in bindata
let skin =
wicked_waifus_data::fly_skin_config_data::iter().find(|&skin| skin.id == request.skin_id);
let Some(skin) = skin else {
response.error_code = ErrorCode::NoFlySkinItem.into();
return;
};
match skin.skin_type {
0 => {
// Verify Skin is unlocked
if !player.unlocked_skins.fly_skins.contains(&skin.id) {
response.error_code = ErrorCode::ErrRoleSkinLocked.into();
return;
}
role.fly_skin_id = request.skin_id
}
1 => {
if !player.unlocked_skins.wing_skins.contains(&skin.id) {
response.error_code = ErrorCode::ErrRoleSkinLocked.into();
return;
}
role.wing_skin_id = request.skin_id
}
_ => {
response.error_code = ErrorCode::FlySkinTypeErr.into();
return;
}
}
{
let world_ref = player.world.borrow();
let world = world_ref.get_world_entity();
let entity_id = world.get_entity_id(request.role_id);
match skin.skin_type {
0 => {
modify_component!(
world.get_entity_components(entity_id as i32),
SoarWingSkin,
|skin_component: &mut SoarWingSkin| {
skin_component.skin_id = role.skin_id;
}
);
}
1 => {
modify_component!(
world.get_entity_components(entity_id as i32),
ParaglidingSkin,
|skin_component: &mut ParaglidingSkin| {
skin_component.skin_id = role.skin_id;
}
);
}
_ => unreachable!("Already tested above"),
}
player.notify(SoarWingOrParaglidingSkinChangeNotify {
fly_skin_data: vec![EntityFlySkinChangeData {
entity_id,
fly_skin_config_data: vec![FlySkinConfigData {
skin_id: request.skin_id,
fly_skin_id: skin.skin_type,
}],
}],
});
}
player.notify(RoleFlyEquipChangeNotify {
fly_skin_data: vec![EquipFlySkinData {
role_id: request.role_id,
skin_id: request.skin_id,
}],
});
response.error_code = ErrorCode::Success.into();
}
pub fn on_fly_skin_wear_all_role_request(
player: &mut Player,
request: FlySkinWearAllRoleRequest,
response: &mut FlySkinWearAllRoleResponse,
) {
let skin =
wicked_waifus_data::fly_skin_config_data::iter().find(|&skin| skin.id == request.skin_id);
let Some(skin) = skin else {
response.error_code = ErrorCode::NoFlySkinItem.into();
return;
};
match skin.skin_type {
0 => {
// Verify Skin is unlocked
if !player.unlocked_skins.fly_skins.contains(&skin.id) {
response.error_code = ErrorCode::ErrRoleSkinLocked.into();
return;
}
for role in player.role_list.values_mut() {
role.fly_skin_id = request.skin_id;
}
}
1 => {
if !player.unlocked_skins.wing_skins.contains(&skin.id) {
response.error_code = ErrorCode::ErrRoleSkinLocked.into();
return;
}
for role in player.role_list.values_mut() {
role.wing_skin_id = request.skin_id;
}
}
_ => {
response.error_code = ErrorCode::FlySkinTypeErr.into();
return;
}
}
player.notify(RoleFlyEquipChangeNotify {
fly_skin_data: player
.role_list
.values()
.map(|r| EquipFlySkinData {
role_id: r.role_id,
skin_id: request.skin_id,
})
.collect::<Vec<_>>(),
});
{
let world_ref = player.world.borrow();
let world = world_ref.get_world_entity();
let data = player
.role_list
.values()
.filter_map(|role| {
let entity_id = world.get_entity_id(role.role_id);
if entity_id == -1 {
None
} else {
match skin.skin_type {
0 => {
modify_component!(
world.get_entity_components(entity_id as i32),
SoarWingSkin,
|skin_component: &mut SoarWingSkin| {
skin_component.skin_id = role.skin_id;
}
);
}
1 => {
modify_component!(
world.get_entity_components(entity_id as i32),
ParaglidingSkin,
|skin_component: &mut ParaglidingSkin| {
skin_component.skin_id = role.skin_id;
}
);
}
_ => unreachable!("Already tested above"),
}
Some(EntityFlySkinChangeData {
entity_id,
fly_skin_config_data: vec![FlySkinConfigData {
skin_id: request.skin_id,
fly_skin_id: skin.skin_type,
}],
})
}
})
.collect::<Vec<_>>();
player.notify(SoarWingOrParaglidingSkinChangeNotify {
fly_skin_data: data,
});
}
response.error_code = ErrorCode::Success.into();
}
pub fn on_role_level_up_view_request(
player: &mut Player,
request: RoleLevelUpViewRequest,
response: &mut RoleLevelUpViewResponse,
) {
let role = player.role_list.get(&request.role_id);
let Some(role) = role else {
response.error_code = ErrorCode::NotValidRole.into();
return;
};
response.level = role.level;
// TODO: shall we get from data? seems client can do it by himself
response.level_exp_info = vec![ArrayIntInt { key: 1, value: 200 }];
response.exp = role.exp;
// it seems add_exp, final_prop, cost_list amd overflow_list are handled by client, so skip
let items = wicked_waifus_data::role_exp_item_data::iter()
.map(|(&id, _)| id)
.collect::<Vec<_>>();
response.item_list = player.inventory.to_array_int_int_filtered(&items);
response.error_code = ErrorCode::Success.into();
}
pub fn on_pb_up_level_role_request(
player: &mut Player,
request: PbUpLevelRoleRequest,
response: &mut PbUpLevelRoleResponse,
) {
response.role_id = request.role_id;
let role = player.role_list.get(&request.role_id);
let Some(role) = role else {
response.error_code = ErrorCode::NotValidRole.into();
return;
};
// TODO: no shell_credit??? :turtle_skull:
let items = player.inventory.consume_items(
&request
.item_list
.iter()
.map(|item| ItemUsage {
id: item.key,
quantity: -item.value,
})
.collect::<Vec<_>>(),
);
let Ok(_items) = items else {
response.error_code = ErrorCode::ErrConsumeNotEnough.into();
return;
};
let mut total_exp = request
.item_list
.iter()
.filter_map(|item| {
wicked_waifus_data::role_exp_item_data::get(&item.key)
.map(|exp| exp.basic_exp * item.value)
})
.sum();
let role_level_consume_id = wicked_waifus_data::role_info_data::iter()
.find(|role| role.id == request.role_id)
.map(|role| role.level_consume_id)
.unwrap_or(10001);
let mut levels_consume = wicked_waifus_data::role_level_consume_data::iter()
.filter(|consume| {
consume.consume_group_id == role_level_consume_id && consume.level > role.level
}) // TODO: add upper bound too(till breakthrough)
.collect::<Vec<_>>();
levels_consume.sort_by_key(|item| item.level);
let mut level = role.level;
for level_consume in levels_consume {
if level_consume.exp_count > total_exp {
break;
}
total_exp -= level_consume.exp_count;
level = level_consume.level;
}
response.level = level;
response.exp = total_exp;
// TODO is item_map the overflowing items or all items used? also should we sent more notifies?
response.error_code = ErrorCode::Success.into();
}
// on_role_break_through_view_request
pub fn on_role_break_through_view_request(
player: &mut Player,
request: RoleBreakThroughViewRequest,
response: &mut RoleBreakThroughViewResponse,
) {
let role = player.role_list.get(&request.role_id);
let Some(role) = role else {
response.error_code = ErrorCode::NotValidRole.into();
return;
};
// TODO:
// if !condition {
// response.error_code = ErrorCode::ErrRoleConditionNotFind.into();
// response.is_condition_finish = false;
// }
let role_breach_id = wicked_waifus_data::role_info_data::iter()
.find(|role| role.id == request.role_id)
.map(|role| role.breach_id)
.unwrap_or(request.role_id);
let condition = wicked_waifus_data::role_breach_data::iter()
.find(|role_breach_data| {
role_breach_data.breach_group_id == role_breach_id
&& role_breach_data.breach_level == role.breakthrough
})
.unwrap(); // TODO: handling
response.error_code = ErrorCode::Success.into();
response.level_limit = condition.max_level;
response.cost_list = condition
.breach_consume
.iter()
.map(|(&id, &count)| ArrayIntInt {
key: id,
value: count,
})
.collect::<Vec<_>>();
// TODO: un_lock_skill_id, reward_list, final_prop
response.is_condition_finish = true; // is this last??
}

View file

@ -1,4 +1,9 @@
use wicked_waifus_protocol::{ErrorCode, SceneLoadingFinishRequest, SceneLoadingFinishResponse, SceneTraceRequest, SceneTraceResponse, UpdateSceneDateRequest, UpdateSceneDateResponse, AccessPathTimeServerConfigRequest, AccessPathTimeServerConfigResponse, PlayerHeadDataRequest, PlayerHeadDataResponse, UnlockRoleSkinListRequest, UnlockRoleSkinListResponse, JsPatchNotify};
use wicked_waifus_protocol::{
AccessPathTimeServerConfigRequest, AccessPathTimeServerConfigResponse, ErrorCode,
JsPatchNotify, PlayerHeadDataRequest, PlayerHeadDataResponse, SceneLoadingFinishRequest,
SceneLoadingFinishResponse, SceneTraceRequest, SceneTraceResponse, UpdateSceneDateRequest,
UpdateSceneDateResponse,
};
const WATER_MASK: &str = include_str!("../../../scripts/watermask-disable.js");
const UID_FIX: &str = include_str!("../../../scripts/uidfix.js");
@ -65,14 +70,3 @@ pub fn on_player_head_data_request(
// TODO: port this from golang
response.pi = vec![];
}
pub fn on_unlock_role_skin_list_request(
_player: &Player,
_request: UnlockRoleSkinListRequest,
response: &mut UnlockRoleSkinListResponse,
) {
// TODO: port this from golang
response.role_skin_list = wicked_waifus_data::role_skin_data::iter()
.map(|data| data.id)
.collect::<Vec<_>>();
}

View file

@ -0,0 +1,175 @@
use crate::logic::components::{Equip, WeaponSkin};
use crate::logic::ecs::component::ComponentContainer;
use crate::logic::player::Player;
use crate::modify_component;
use wicked_waifus_protocol::{EntityEquipChangeNotify, EntityEquipSkinChangeNotify, EquipComponentPb, EquipTakeOnNotify, EquipTakeOnRequest, EquipTakeOnResponse, EquipWeaponSkinRequest, EquipWeaponSkinResponse, ErrorCode, LoadEquipData, SendEquipSkinRequest, SendEquipSkinResponse, WeaponSkinComponentPb, WeaponSkinDeleteNotify, WeaponSkinRequest, WeaponSkinResponse};
pub fn on_weapon_skin_request(
player: &Player,
_request: WeaponSkinRequest,
response: &mut WeaponSkinResponse,
) {
response.equip_list = player
.role_list
.values()
.filter(|role| role.weapon_skin_id != 0)
.map(|role| LoadEquipData {
role_id: role.role_id,
skin_id: role.weapon_skin_id,
})
.collect();
response.error_code = ErrorCode::Success.into();
}
pub fn on_equip_weapon_skin_request(
player: &mut Player,
request: EquipWeaponSkinRequest,
response: &mut EquipWeaponSkinResponse,
) {
let Some(equip_data) = request.data else {
return;
};
let role = player.role_list.get_mut(&equip_data.role_id);
let Some(role) = role else {
response.error_code = ErrorCode::NotValidRole.into();
return;
};
// Verify Id exist in bindata
let Some(skin_data) =
wicked_waifus_data::weapon_skin_data::iter().find(|data| data.id == equip_data.skin_id)
else {
response.error_code = ErrorCode::WeaponSkinDataErr.into();
return;
};
// Verify Skin is unlocked
if !player.unlocked_skins.weapon_skins.contains(&skin_data.id) {
response.error_code = ErrorCode::WeaponSkinUnLockErr.into();
return;
}
role.weapon_skin_id = equip_data.skin_id;
{
let world_ref = player.world.borrow();
let world = world_ref.get_world_entity();
let entity_id = world.get_entity_id(equip_data.role_id);
modify_component!(
world.get_entity_components(entity_id as i32),
WeaponSkin,
|skin_component: &mut WeaponSkin| {
skin_component.skin_id = equip_data.skin_id;
}
);
player.notify(EntityEquipSkinChangeNotify {
entity_id,
weapon_skin_component_pb: Some(WeaponSkinComponentPb {
weapon_skin_id: equip_data.skin_id,
}),
});
}
// Is the all list needed or only the new one??
response.data_list = player
.role_list
.values()
.filter(|role| role.weapon_skin_id != 0)
.map(|role| LoadEquipData {
role_id: role.role_id,
skin_id: role.weapon_skin_id,
})
.collect();
response.error_code = ErrorCode::Success.into();
}
pub fn on_send_equip_skin_request(
player: &mut Player,
request: SendEquipSkinRequest,
response: &mut SendEquipSkinResponse,
) {
let role = player.role_list.get_mut(&request.role_id);
let Some(role) = role else {
response.error_code = ErrorCode::NotValidRole.into();
return;
};
let old_skin_id = role.weapon_skin_id;
role.weapon_skin_id = 0;
{
let world_ref = player.world.borrow();
let world = world_ref.get_world_entity();
let entity_id = world.get_entity_id(request.role_id);
modify_component!(
world.get_entity_components(entity_id as i32),
WeaponSkin,
|skin_component: &mut WeaponSkin| {
skin_component.skin_id = 0;
}
);
player.notify(EntityEquipSkinChangeNotify {
entity_id,
weapon_skin_component_pb: Some(WeaponSkinComponentPb {
weapon_skin_id: 0,
}),
});
player.notify(WeaponSkinDeleteNotify {
role_id: request.role_id,
skin_id: old_skin_id,
})
}
response.error_code = ErrorCode::Success.into();
}
pub fn on_equip_take_on_request(
player: &mut Player,
request: EquipTakeOnRequest,
response: &mut EquipTakeOnResponse,
) {
let Some(equip_data) = request.data else {
return;
};
// TODO: Add sanity checks(add from another role, a.k.a.: switch from roles)
player.notify(EquipTakeOnNotify { data_list: vec![equip_data] });
let role = player.role_list.get_mut(&equip_data.role_id);
let Some(role) = role else {
response.error_code = ErrorCode::NotValidRole.into();
return;
};
let Some((id, breach)) = player.inventory.get_weapon_equip_info(equip_data.equip_inc_id) else {
response.error_code = ErrorCode::ErrItemNotFound.into();
return;
};
role.equip_weapon = id;
// TODO: Change attributes based on weapon (PbRolePropsNotify + buffs + CombatNotifyAttributeChangedNotify)
{
// TODO: remove from old one if in scene in case of weapon switch
let world_ref = player.world.borrow();
let world = world_ref.get_world_entity();
let entity_id = world.get_entity_id(equip_data.role_id);
modify_component!(
world.get_entity_components(entity_id as i32),
Equip,
|equip_component: &mut Equip| {
equip_component.weapon_id = id;
equip_component.weapon_breach_level = breach;
}
);
player.notify(EntityEquipChangeNotify {
entity_id,
equip_component: Some(EquipComponentPb {
weapon_id: id,
weapon_breach_level: breach,
}),
})
}
// TODO: Should we return all of them??
response.data_list = vec![equip_data];
response.error_code = ErrorCode::Success.into();
}

View file

@ -11,16 +11,16 @@ use wicked_waifus_protocol::{
AdventreTask, AdventureManualData, AdventureUpdateNotify, AdviceSettingNotify, BuffItemNotify,
ControlInfoNotify, EEntityType, ERemoveEntityType, EnergyInfo, EnergyUpdateNotify,
EntityAddNotify, EntityConfigType, EntityPb, EntityRemoveInfo, EntityRemoveNotify, EntityState,
FavorItem, FightFormationNotifyInfo, FightRoleInfo, FightRoleInfos, FormationRoleInfo,
GroupFormation, HostTeleportUnlockNotify, InstDataNotify, ItemPkgOpenNotify,
LevelPlayInfoNotify, LivingStatus, MailInfosNotify, MapUnlockFieldNotify,
MonthCardDailyRewardNotify, MoonChasingTargetGetCountNotify,
MoonChasingTrackMoonHandbookRewardNotify, NormalItemUpdateNotify, PassiveSkillNotify,
PbGetRoleListNotify, PlayerAttr, PlayerAttrKey, PlayerAttrNotify, PlayerAttrType,
PlayerFightFormations, PlayerVarNotify, ProtocolUnit, PushContextIdNotify,
PushDataCompleteNotify, RoguelikeCurrencyNotify, RoleChangeUnlockNotify, RoleFavor,
RoleFavorListNotify, RoleMotion, RoleMotionListNotify, SettingNotify, TeleportUpdateNotify,
UpdateFormationNotify, UpdateGroupFormationNotify,
FavorItem, FightFormationNotifyInfo, FightRoleInfo, FightRoleInfos, FlyEquipAddNotify,
FlySkinEquipData, GroupFormation, HostTeleportUnlockNotify, InstDataNotify, ItemPkgOpenNotify,
LevelPlayInfoNotify, LivingStatus, MailInfosNotify, MonthCardDailyRewardNotify,
MoonChasingTargetGetCountNotify, MoonChasingTrackMoonHandbookRewardNotify,
NormalItemUpdateNotify, PassiveSkillNotify, PbGetRoleListNotify, PlayerAttr, PlayerAttrKey,
PlayerAttrNotify, PlayerAttrType, PlayerFightFormations, PlayerVarNotify, ProtocolUnit,
PushContextIdNotify, PushDataCompleteNotify, RoguelikeCurrencyNotify, RoleChangeUnlockNotify,
RoleFavor, RoleFavorListNotify, RoleFlyEquipNotify, RoleMotion, RoleMotionListNotify,
SettingNotify, TeleportUpdateNotify, UnlockSkinDataNotify, UpdateFormationNotify,
UpdateGroupFormationNotify,
};
use wicked_waifus_protocol_internal::{PlayerBasicData, PlayerRoleData, PlayerSaveData};
@ -46,16 +46,17 @@ use crate::logic::player::player_mc_element::PlayerMcElement;
use crate::logic::player::player_month_card::PlayerMonthCard;
use crate::logic::player::player_teleports::{PlayerTeleport, PlayerTeleports};
use crate::logic::player::player_tutorials::{PlayerTutorial, PlayerTutorials};
use crate::logic::player::Element::Spectro;
use crate::logic::{
components::{
Attribute, EntityConfig, Equip, FightBuff, Movement, OwnerPlayer, PlayerOwnedEntityMarker,
Position, Visibility, VisionSkill, SoarWingSkin
Attribute, EntityConfig, Equip, FightBuff, Movement, OwnerPlayer, ParaglidingSkin,
PlayerOwnedEntityMarker, Position, SoarWingSkin, Visibility, VisionSkill, WeaponSkin,
},
ecs::component::ComponentContainer,
};
use crate::session::Session;
use crate::{config, create_player_entity_pb, query_components};
use crate::logic::player::Element::Spectro;
use crate::{config, create_player_entity_pb};
use crate::logic::player::player_unlocked_skins::PlayerUnlockedSkins;
mod basic_info;
mod explore_tools;
@ -72,6 +73,7 @@ mod player_mc_element;
mod player_month_card;
mod player_teleports;
mod player_tutorials;
mod player_unlocked_skins;
pub struct Player {
session: Option<Arc<Session>>,
@ -93,6 +95,7 @@ pub struct Player {
pub map_trace: PlayerMapTrace,
pub month_card: PlayerMonthCard,
pub mc_element: PlayerMcElement,
pub unlocked_skins: PlayerUnlockedSkins,
// Runtime
pub world: Rc<RefCell<World>>,
pub last_save_time: u64,
@ -123,6 +126,10 @@ impl Player {
self.notify(self.explore_tools.build_roulette_update_notify());
self.notify(self.build_role_favor_list_notify());
self.notify(self.func.build_func_open_notify());
self.notify(self.build_weapon_skin_notify());
self.notify(self.build_fly_equip_notify());
self.notify(self.build_role_fly_equip_notify());
self.notify(InstDataNotify {
enter_infos: vec![], // TODO: No effect in normal world, to implement for dungeon::logic()
});
@ -211,6 +218,11 @@ impl Player {
self.role_list.insert(role_id, Role::new(role_id));
});
}
for role in self.role_list.values() {
self.inventory
.add_weapon(role.equip_weapon, 0, 1, 0, 0, 0, role.role_id)
.unwrap();
}
self.formation_list.insert(1, RoleFormation::default());
self.cur_formation_id = 1;
@ -305,8 +317,8 @@ impl Player {
RoleFavorListNotify {
favor_list: self
.role_list
.iter()
.map(|(_, role)| RoleFavor {
.values()
.map(|role| RoleFavor {
role_id: role.role_id,
level: role.favor_level,
exp: role.favor_exp,
@ -338,12 +350,73 @@ impl Player {
}
}
pub fn build_weapon_skin_notify(&self) -> UnlockSkinDataNotify {
UnlockSkinDataNotify {
phantom_skin_list: self.unlocked_skins.weapon_skins.iter().cloned().collect(),
is_login: true,
}
}
pub fn build_fly_equip_notify(&self) -> FlyEquipAddNotify {
FlyEquipAddNotify {
unlock_fly_skin_ids: self
.unlocked_skins
.fly_skins
.iter()
.chain(&self.unlocked_skins.wing_skins)
.cloned()
.collect(),
}
}
pub fn build_role_fly_equip_notify(&self) -> RoleFlyEquipNotify {
let merged: Vec<_> = self
.unlocked_skins
.fly_skins
.iter()
.chain(&self.unlocked_skins.wing_skins)
.cloned()
.collect();
let mut equipped_skins: HashMap<i32, Vec<i32>> = HashMap::new();
for role in self.role_list.values() {
if role.fly_skin_id != 0 {
equipped_skins
.entry(role.fly_skin_id)
.or_default()
.push(role.role_id);
}
if role.wing_skin_id != 0 {
equipped_skins
.entry(role.wing_skin_id)
.or_default()
.push(role.role_id);
}
}
RoleFlyEquipNotify {
fly_skin_equip_data: merged
.iter()
.map(|&skin| match equipped_skins.get(&skin) {
Some(role_list) => FlySkinEquipData {
role_ids: role_list.to_vec(),
skin_id: skin,
},
None => FlySkinEquipData {
role_ids: vec![],
skin_id: skin,
},
})
.collect(),
}
}
pub fn build_motion_list_notify(&self) -> RoleMotionListNotify {
RoleMotionListNotify {
motion_list: self
.role_list
.iter()
.map(|(_, role)| {
.values()
.map(|role| {
RoleMotion {
role_id: role.role_id,
motion_ids: motion_data::iter()
@ -362,14 +435,15 @@ impl Player {
}
}
pub fn build_player_entity_add_notify(&self, role_list: Vec<Role>) -> EntityAddNotify {
pub fn build_player_entity_add_notify(&self, role_list: Vec<Role>, world: &mut WorldEntity) -> EntityAddNotify {
create_player_entity_pb!(
role_list,
self.basic_info.cur_map_id,
self,
self.basic_info.id,
self.location.position.clone(),
self.explore_tools
self.explore_tools,
world
)
}
@ -404,15 +478,10 @@ impl Player {
fight_role_infos: cur_formation
.role_ids
.iter()
.map(|&role_id| {
let entity_id = world.get_entity_id(role_id);
let role_skin =
query_components!(world, entity_id, RoleSkin).0.unwrap();
FightRoleInfo {
role_id,
entity_id: world.get_entity_id(role_id),
on_stage_without_control: false,
}
.map(|&role_id| FightRoleInfo {
role_id,
entity_id: world.get_entity_id(role_id),
on_stage_without_control: false,
})
.collect(),
cur_role: cur_formation.cur_role,
@ -449,14 +518,7 @@ impl Player {
tracing::warn!("Role {} not found in use role list", role_id);
return Default::default();
}
let role = *role_map.get(&role_id).unwrap();
FormationRoleInfo {
role_id: role.role_id,
max_hp: 0,
cur_hp: 0,
level: role.level,
role_skin_id: role.skin_id,
}
role_map.get(role_id).unwrap().to_formation_protobuf()
})
.collect(),
is_current: formation.is_current,
@ -514,7 +576,7 @@ impl Player {
}],
});
self.notify(NormalItemUpdateNotify {
normal_item_list: self.inventory.to_normal_item_list_filtered(vec![3]),
normal_item_list: self.inventory.to_normal_item_list_filtered(&[3]),
no_tips: false,
});
self.notify(MonthCardDailyRewardNotify {
@ -596,6 +658,10 @@ impl Player {
.mc_element
.map(PlayerMcElement::load_from_save)
.unwrap_or_default(),
unlocked_skins: save_data
.unlocked_skins
.map(PlayerUnlockedSkins::load_from_save)
.unwrap_or_default(),
world: Rc::new(RefCell::new(World::new())),
last_save_time: time_util::unix_timestamp(),
quadrant_id: 0,
@ -631,6 +697,7 @@ impl Player {
map_trace: Some(self.map_trace.build_save_data()),
month_card: Some(self.month_card.build_save_data()),
mc_element: Some(self.mc_element.build_save_data()),
unlocked_skins: Some(self.unlocked_skins.build_save_data()),
}
}
@ -639,12 +706,11 @@ impl Player {
}
pub fn build_role_list_notify(&self) -> PbGetRoleListNotify {
// TODO: There is a bug we are investigating with several resonators, this is a workaround
PbGetRoleListNotify {
role_list: self
.role_list
.iter()
.map(|(_, role)| role.to_protobuf())
.values()
.map(|role| role.to_protobuf())
.collect(),
}
}

View file

@ -1,11 +1,15 @@
use std::collections::HashMap;
use std::sync::atomic::AtomicI32;
use wicked_waifus_protocol::{ArrayIntInt, NormalItem, WeaponItem};
use wicked_waifus_protocol::NormalItem;
use wicked_waifus_protocol_internal::PlayerInventoryData;
use crate::config;
use crate::logic::utils::seq_utils::{SequenceGenerator, Sequencer};
use wicked_waifus_protocol_internal::{PlayerInventoryData, PlayerInventoryWeaponData};
pub struct PlayerInventory {
items: HashMap<i32, i32>,
weapons_seq: SequenceGenerator<i32, AtomicI32>,
weapons: HashMap<i32, PlayerInventoryWeaponData>,
}
pub struct ItemUsage {
@ -33,13 +37,16 @@ impl PlayerInventory {
pub fn load_from_save(data: PlayerInventoryData) -> Self {
Self {
weapons_seq: SequenceGenerator::from_data(&data.weapons),
items: data.items.clone(),
weapons: data.weapons.clone(),
}
}
pub fn build_save_data(&self) -> PlayerInventoryData {
PlayerInventoryData {
items: self.items.clone(),
weapons: self.weapons.clone(),
}
}
@ -52,10 +59,19 @@ impl PlayerInventory {
}
pub fn consume_item(&mut self, id: i32, quantity: i32) -> Result<i32, InventoryError> {
Ok(*self.consume_items(&[ItemUsage { id, quantity: -quantity }])?.get(&id).unwrap())
Ok(*self
.consume_items(&[ItemUsage {
id,
quantity: -quantity,
}])?
.get(&id)
.unwrap())
}
pub fn consume_items(&mut self, usages: &[ItemUsage]) -> Result<HashMap<i32, i32>, InventoryError> {
pub fn consume_items(
&mut self,
usages: &[ItemUsage],
) -> Result<HashMap<i32, i32>, InventoryError> {
if !self.has_enough_items(usages) {
return Err(InventoryError::ItemsNotEnough());
}
@ -68,6 +84,11 @@ impl PlayerInventory {
self.items.get(&Self::UNION_EXP_ID).copied().unwrap_or(0)
}
#[inline(always)]
pub fn add_shell_credits(&mut self, count: i32) -> i32 {
self.add_internal(Self::SHELL_CREDIT_ID, count)
}
#[inline(always)]
pub fn get_shell_credits(&self) -> i32 {
self.items.get(&Self::SHELL_CREDIT_ID).copied().unwrap_or(0)
@ -97,35 +118,113 @@ impl PlayerInventory {
// TODO: Check if this is item or not
#[inline(always)]
pub fn get_waveplate_crystal(&self) -> i32 {
self.items.get(&Self::WAVEPLATE_CRYSTAL_ID).copied().unwrap_or(0)
self.items
.get(&Self::WAVEPLATE_CRYSTAL_ID)
.copied()
.unwrap_or(0)
}
pub fn to_normal_item_list(&self) -> Vec<NormalItem> {
self.items.iter()
self.items
.iter()
.filter(|(&id, _)| Self::WAVEPLATE_ID != id && Self::WAVEPLATE_CRYSTAL_ID != id)
// TODO: Implement expiration
.map(|(&id, &count)| NormalItem { id, count, expire_time: 0 })
.map(|(&id, &count)| NormalItem {
id,
count,
expire_time: 0,
})
.collect::<Vec<_>>()
}
pub fn to_normal_item_list_filtered(&self, ids: Vec<i32>) -> Vec<NormalItem> {
self.items.iter()
pub fn to_normal_item_list_filtered(&self, ids: &[i32]) -> Vec<NormalItem> {
self.items
.iter()
.filter(|(&id, _)| ids.contains(&id))
// TODO: Implement expiration
.map(|(&id, &count)| NormalItem { id, count, expire_time: 0 })
.map(|(&id, &count)| NormalItem {
id,
count,
expire_time: 0,
})
.collect::<Vec<_>>()
}
pub fn to_array_int_int_filtered(&self, ids: &[i32]) -> Vec<ArrayIntInt> {
ids.iter()
.map(|id| ArrayIntInt {
key: *id,
value: self.items.get(id).copied().unwrap_or(0),
})
.collect::<Vec<_>>()
}
pub fn add_weapon(
&mut self,
id: i32,
func: i32,
level: i32,
exp: i32,
breach: i32,
reson: i32,
role: i32,
) -> Result<i32, InventoryError> {
let inc_id = self.weapons_seq.take_id();
self.weapons.insert(
inc_id,
PlayerInventoryWeaponData {
id,
func_value: func,
level,
exp,
breach,
reson_level: reson,
role_id: role,
},
);
Ok(inc_id)
}
pub fn remove_weapon(&mut self, id: i32) {
self.weapons.remove(&id);
self.weapons_seq.give_id(id);
}
pub fn to_weapon_item_list(&self) -> Vec<WeaponItem> {
self.weapons
.iter()
.map(|(&inc_id, data)| WeaponItem {
id: data.id,
incr_id: inc_id,
func_value: data.func_value,
weapon_level: data.level,
weapon_exp: data.exp,
weapon_breach: data.breach,
weapon_reson_level: data.reson_level,
role_id: data.role_id,
})
.collect()
}
pub fn get_weapon_equip_info(&self, inc_id: i32) -> Option<(i32, i32)> {
self.weapons
.get(&inc_id)
.map(|weapon_data| (weapon_data.id, weapon_data.breach))
}
#[inline(always)]
fn add_internal(&mut self, id: i32, quantity: i32) -> i32 {
*self.items.entry(id)
*self
.items
.entry(id)
.and_modify(|count| *count += quantity)
.or_insert(quantity)
}
#[inline(always)]
fn add_many_internal(&mut self, usages: &[ItemUsage]) -> HashMap<i32, i32> {
usages.iter()
usages
.iter()
.filter(|usage| usage.quantity != 0)
.map(|delta| (delta.id, self.add_internal(delta.id, delta.quantity)))
.collect::<HashMap<_, _>>()
@ -138,17 +237,80 @@ impl PlayerInventory {
#[inline(always)]
fn has_enough_items(&self, items_delta: &[ItemUsage]) -> bool {
items_delta.iter()
.fold(true, |is_enough, delta| {
is_enough && self.has_enough_item(delta.id, -delta.quantity)
})
items_delta
.iter()
.all(|delta| self.has_enough_item(delta.id, -delta.quantity))
}
}
impl Default for PlayerInventory {
fn default() -> Self {
let mut weapons_seq = SequenceGenerator::new();
let default_unlocks = &config::get_config().default_unlocks;
let weapons: HashMap<i32, PlayerInventoryWeaponData> =
match default_unlocks.unlock_all_weapons {
true => wicked_waifus_data::weapon_conf_data::iter()
.map(|data| {
let (level, breach) = if default_unlocks.unlock_all_weapons_max_level {
(
wicked_waifus_data::weapon_level_data::iter()
.filter(|level_data| level_data.level_id == data.level_id)
.map(|level_data| level_data.level)
.max()
.unwrap_or(1),
wicked_waifus_data::weapon_breach_data::iter()
.filter(|level_data| level_data.breach_id == data.breach_id)
.map(|level_data| level_data.level)
.max()
.unwrap_or(0),
)
} else {
(
wicked_waifus_data::weapon_level_data::iter()
.filter(|level_data| level_data.level_id == data.level_id)
.map(|level_data| level_data.level)
.min()
.unwrap_or(1),
wicked_waifus_data::weapon_breach_data::iter()
.filter(|level_data| level_data.breach_id == data.breach_id)
.map(|level_data| level_data.level)
.min()
.unwrap_or(0),
)
};
let reson_level = if default_unlocks.unlock_all_weapons_all_reson {
wicked_waifus_data::weapon_reson_data::iter()
.filter(|level_data| level_data.reson_id == data.reson_id)
.map(|level_data| level_data.level)
.max()
.unwrap_or(0)
} else {
wicked_waifus_data::weapon_reson_data::iter()
.filter(|level_data| level_data.reson_id == data.reson_id)
.map(|level_data| level_data.level)
.min()
.unwrap_or(0)
};
(
weapons_seq.take_id(),
PlayerInventoryWeaponData {
id: data.item_id,
func_value: 0,
level,
exp: 0,
breach,
reson_level,
role_id: 0,
},
)
})
.collect::<HashMap<_, _>>(),
false => Default::default(),
};
Self {
items: HashMap::new(),
weapons_seq,
weapons,
}
}
}
}

View file

@ -0,0 +1,72 @@
use crate::config;
use std::collections::HashSet;
use wicked_waifus_protocol_internal::PlayerUnlockedSkinsData;
pub struct PlayerUnlockedSkins {
pub role_skins: HashSet<i32>,
pub weapon_skins: HashSet<i32>,
pub fly_skins: HashSet<i32>,
pub wing_skins: HashSet<i32>,
}
impl PlayerUnlockedSkins {
pub fn load_from_save(data: PlayerUnlockedSkinsData) -> Self {
Self {
role_skins: data.role_skins.iter().cloned().collect(),
weapon_skins: data.weapon_skins.iter().cloned().collect(),
fly_skins: data.fly_skins.iter().cloned().collect(),
wing_skins: data.wing_skins.iter().cloned().collect(),
}
}
pub fn build_save_data(&self) -> PlayerUnlockedSkinsData {
PlayerUnlockedSkinsData {
role_skins: self.role_skins.iter().cloned().collect(),
weapon_skins: self.weapon_skins.iter().cloned().collect(),
fly_skins: self.fly_skins.iter().cloned().collect(),
wing_skins: self.wing_skins.iter().cloned().collect(),
}
}
}
impl Default for PlayerUnlockedSkins {
fn default() -> Self {
let unlocks = &config::get_config().default_unlocks;
Self {
role_skins: if unlocks.unlock_all_role_skins {
wicked_waifus_data::role_skin_data::iter()
.map(|skin| skin.id)
.collect()
} else {
HashSet::new()
},
weapon_skins: if unlocks.unlock_all_weapon_skins {
wicked_waifus_data::weapon_skin_data::iter()
.map(|skin| skin.id)
.collect()
} else {
HashSet::new()
},
fly_skins: if unlocks.unlock_all_fly_skins {
wicked_waifus_data::fly_skin_config_data::iter()
.filter(|skin| skin.skin_type == 0)
.map(|skin| skin.id)
.collect()
} else {
HashSet::new()
},
wing_skins: if unlocks.unlock_all_wing_skins {
wicked_waifus_data::fly_skin_config_data::iter()
.filter(|skin| skin.skin_type == 1)
.map(|skin| skin.id)
.collect()
} else {
HashSet::new()
},
}
}
}

View file

@ -29,7 +29,7 @@ impl RoleFormation {
RoleFormationData {
formation_id: self.id,
cur_role: self.cur_role,
role_id_list: self.role_ids.iter().map(|&role_id| role_id).collect(),
role_id_list: self.role_ids.iter().copied().collect(),
is_current: self.is_current,
}
}

View file

@ -1,6 +1,6 @@
use std::collections::HashMap;
use wicked_waifus_protocol::{ArrayIntInt, RoleInfo};
use wicked_waifus_protocol::{ArrayIntInt, FormationRoleInfo, RoleInfo};
use crate::config;
use crate::logic::utils::growth_utils::get_role_props_by_level;
@ -50,6 +50,9 @@ pub struct Role {
pub element_energy: i32,
pub favor_level: i32,
pub favor_exp: i32,
pub wing_skin_id: i32,
pub fly_skin_id: i32,
pub weapon_skin_id: i32,
}
impl Role {
@ -119,21 +122,44 @@ impl Role {
let default_unlocks = &config::get_config().default_unlocks;
let (level, breakthrough) = if default_unlocks.unlock_all_roles_max_level {
(data.max_level, 6)
(
data.max_level,
wicked_waifus_data::role_breach_data::iter()
.filter(|level_data| level_data.breach_group_id == data.breach_id)
.map(|level_data| level_data.breach_level)
.max()
.unwrap_or(0),
)
} else {
(1, 0)
(
1,
wicked_waifus_data::role_breach_data::iter()
.filter(|level_data| level_data.breach_group_id == data.breach_id)
.map(|level_data| level_data.breach_level)
.min()
.unwrap_or(0),
)
};
let resonant_chain_group_index = if default_unlocks.unlock_all_roles_all_sequences {
6
wicked_waifus_data::resonant_chain_data::iter()
.filter(|level_data| level_data.group_id == data.resonant_chain_group_id)
.map(|level_data| level_data.group_index)
.max()
.unwrap_or(0)
} else {
0
wicked_waifus_data::resonant_chain_data::iter()
.filter(|level_data| level_data.group_id == data.resonant_chain_group_id)
.map(|level_data| level_data.group_index)
.min()
.unwrap_or(0)
};
// TODO: add weapon and echo stats
let base_stats = &get_role_props_by_level(role_id, level, breakthrough);
Self {
role_id,
name: String::with_capacity(0),
level,
exp: 0, // TODO: Compute based on level??
exp: 0,
breakthrough,
skill_map: HashMap::new(), // TODO!
star: 0,
@ -151,12 +177,16 @@ impl Role {
element_energy: base_stats.element_energy,
favor_level: 0,
favor_exp: 0,
wing_skin_id: 0,
fly_skin_id: 0,
weapon_skin_id: 0,
}
}
pub fn get_base_properties(&self) -> BasePropertyData {
// Overwrite dynamic attributes with stores values
let mut base_stats = get_role_props_by_level(self.role_id, self.level, self.breakthrough);
// TODO: add weapon and echo stats
// TODO: Integrity check, value has to be between 0 and max
base_stats.life = self.hp;
base_stats.energy = self.energy;
@ -169,6 +199,7 @@ impl Role {
}
pub fn to_protobuf(&self) -> RoleInfo {
// TODO: add weapon and echo stats
let base_prop: HashMap<i32, i32> = load_key_value(&self.get_base_properties());
RoleInfo {
role_id: self.role_id,
@ -192,6 +223,17 @@ impl Role {
}
}
pub fn to_formation_protobuf(&self) -> FormationRoleInfo {
let base_stats = get_role_props_by_level(self.role_id, self.level, self.breakthrough);
FormationRoleInfo {
role_id: self.role_id,
max_hp: base_stats.life_max,
cur_hp: base_stats.life,
level: self.level,
role_skin_id: self.skin_id,
}
}
pub fn load_from_save(data: RoleData) -> (i32, Self) {
(
data.role_id,
@ -217,6 +259,9 @@ impl Role {
element_energy: data.stats.unwrap().element_energy,
favor_level: data.favor_level,
favor_exp: data.favor_exp,
wing_skin_id: data.wing_skin_id,
fly_skin_id: data.fly_skin_id,
weapon_skin_id: data.weapon_skin_id,
},
)
}
@ -250,6 +295,9 @@ impl Role {
}),
favor_level: self.favor_level,
favor_exp: self.favor_exp,
wing_skin_id: self.wing_skin_id,
fly_skin_id: self.fly_skin_id,
weapon_skin_id: self.weapon_skin_id,
..Default::default()
}
}

View file

@ -229,7 +229,7 @@ fn collect_action(player: &mut Player,
.collect::<Vec<_>>();
let updated_items = player.inventory.add_items(&usages);
let normal_item_list = player.inventory.to_normal_item_list_filtered(
updated_items.keys().cloned().collect::<Vec<i32>>()
&updated_items.keys().cloned().collect::<Vec<i32>>()
);
player.notify(NormalItemUpdateNotify { normal_item_list, no_tips: false });
// UpdateHandBookActiveStateMapNotify

View file

@ -1,8 +1,9 @@
pub mod action_utils;
pub mod condition_utils;
pub mod entity_serializer;
pub mod load_role_info;
pub mod world_util;
pub mod quadrant_util;
pub mod growth_utils;
pub mod load_role_info;
pub mod quadrant_util;
pub mod seq_utils;
pub mod tag_utils;
pub mod world_util;

View file

@ -3,7 +3,7 @@ use std::sync::OnceLock;
use wicked_waifus_data::LevelEntityConfigData;
struct StaticConfig {
pub(crate) struct StaticConfig {
edge_size: f32,
edge_check: f32,
}

View file

@ -0,0 +1,55 @@
use std::collections::{HashMap, VecDeque};
pub trait Sequencer<T> {
fn new() -> Self;
fn from_data<V>(data: &HashMap<T, V>) -> Self;
fn take_id(&mut self) -> T;
fn give_id(&mut self, id: T);
}
pub struct SequenceGenerator<T, A> {
recycled_ids: VecDeque<T>,
next_id: A,
}
macro_rules! sequence_trait_impl {
($t:ty, $a:ty) => (
impl Sequencer<$t> for SequenceGenerator<$t, $a> {
fn new() -> Self {
Self {
recycled_ids: Default::default(),
next_id: <$a>::new(1),
}
}
fn from_data<V>(data: &HashMap<$t, V>) -> Self {
let max_id = data.keys().max().copied().unwrap_or(1);
let next_id = <$a>::new(max_id);
let mut recycled_ids = VecDeque::new();
for i in 1..max_id {
if !data.contains_key(&i) {
recycled_ids.push_back(i);
}
}
Self {
recycled_ids,
next_id,
}
}
fn take_id(&mut self) -> $t {
self.recycled_ids
.pop_front()
.unwrap_or_else(|| self.next_id.fetch_add(1, std::sync::atomic::Ordering::Relaxed))
}
fn give_id(&mut self, id: $t) {
self.recycled_ids.push_back(id);
}
}
)
}
sequence_trait_impl!(i32, std::sync::atomic::AtomicI32);
sequence_trait_impl!(i64, std::sync::atomic::AtomicI64);

View file

@ -9,7 +9,7 @@ use wicked_waifus_data::{
blueprint_config_data, template_config_data, EntityLogic, EntityType, LevelEntityConfigData,
};
use crate::logic::components::{Autonomous, Fsm, Interact, MonsterAi, SoarWingSkin, StateTag, Tag};
use crate::logic::components::{Autonomous, Fsm, Interact, MonsterAi, ParaglidingSkin, SoarWingSkin, StateTag, Tag, WeaponSkin};
use crate::logic::ecs::entity::EntityBuilder;
use crate::logic::ecs::world::World;
use crate::logic::math::Transform;
@ -27,29 +27,26 @@ use crate::query_with;
#[macro_export]
macro_rules! create_player_entity_pb {
($role_list:expr, $cur_map_id:expr, $player:expr, $player_id:expr, $position:expr, $explore_tools:expr) => {{
let mut world_ref = $player.world.borrow_mut();
let world = world_ref.get_mut_world_entity();
($role_list:expr, $cur_map_id:expr, $player:expr, $player_id:expr, $position:expr, $explore_tools:expr, $world:expr) => {{
let current_formation = $player.formation_list.get(&$player.cur_formation_id).unwrap();
let cur_role_id = current_formation.cur_role;
let mut pbs = Vec::new();
for role in $role_list {
let entity = world.create_entity(
let entity = $world.create_entity(
role.role_id,
EEntityType::Player.into(),
$cur_map_id,
);
// Once per character buffs are implemented, add a mut on role_buffs
let fight_buff_infos = world.generate_role_permanent_buffs(entity.entity_id as i64);
let fight_buff_infos = $world.generate_role_permanent_buffs(entity.entity_id as i64);
let buf_manager = FightBuff {
fight_buff_infos,
list_buff_effect_cd: vec![],
};
let entity = world.create_builder(entity)
let entity = $world.create_builder(entity)
.with(ComponentContainer::PlayerOwnedEntityMarker(PlayerOwnedEntityMarker {
entity_type: EEntityType::Player,
}))
@ -87,7 +84,13 @@ macro_rules! create_player_entity_pb {
skin_id: role.skin_id,
}))
.with(ComponentContainer::SoarWingSkin(SoarWingSkin {
skin_id: 84000001,
skin_id: role.fly_skin_id,
}))
.with(ComponentContainer::ParaglidingSkin(ParaglidingSkin {
skin_id: role.wing_skin_id,
}))
.with(ComponentContainer::WeaponSkin(WeaponSkin {
skin_id: role.weapon_skin_id,
}))
.with(ComponentContainer::FightBuff(buf_manager))
.build();
@ -97,7 +100,7 @@ macro_rules! create_player_entity_pb {
..Default::default()
};
world
$world
.get_entity_components(entity.entity_id)
.into_iter()
.for_each(|comp| comp.set_pb_data(&mut pb));
@ -181,7 +184,13 @@ pub fn add_player_entities(player: &Player) {
skin_id: role.skin_id,
}))
.with(ComponentContainer::SoarWingSkin(SoarWingSkin {
skin_id: 84000001,
skin_id: role.fly_skin_id,
}))
.with(ComponentContainer::ParaglidingSkin(ParaglidingSkin {
skin_id: role.wing_skin_id,
}))
.with(ComponentContainer::WeaponSkin(WeaponSkin {
skin_id: role.weapon_skin_id, // TODO: Is this kept on weapon change
}))
.with(ComponentContainer::FightBuff(buf_manager))
.build();
@ -243,11 +252,10 @@ fn build_player_info_list(world: &World) -> Vec<ScenePlayerInformation> {
world.get_world_entity(),
PlayerOwnedEntityMarker,
OwnerPlayer,
EntityConfig,
RoleSkin
EntityConfig
)
.into_iter()
.filter(|(_, _, owner, _, _)| owner.0 == sp.player_id);
.filter(|(_, _, owner, _)| owner.0 == sp.player_id);
ScenePlayerInformation {
cur_role: cur_role_id,
@ -264,7 +272,7 @@ fn build_player_info_list(world: &World) -> Vec<ScenePlayerInformation> {
cur_role: cur_role_id,
// is_retain: true,
fight_role_infos: active_characters
.map(|(id, _, _, conf, role_skin)| FightRoleInfo {
.map(|(id, _, _, conf)| FightRoleInfo {
entity_id: id.into(),
role_id: conf.config_id,
on_stage_without_control: false,

View file

@ -60,6 +60,9 @@ message RoleData {
RoleStats stats = 17;
int32 favor_level = 18;
int32 favor_exp = 19;
int32 wing_skin_id = 20;
int32 fly_skin_id = 21;
int32 weapon_skin_id = 22;
}
message RoleFormationData {
@ -135,8 +138,19 @@ message PlayerAdventureStatusData {
repeated PlayerAdventureGlobalStatusData status = 1;
}
message PlayerInventoryWeaponData {
int32 id = 1;
int32 func_value = 2;
int32 level = 3;
int32 exp = 4;
int32 breach = 5;
int32 reson_level = 6;
int32 role_id = 7;
}
message PlayerInventoryData {
map<int32, int32> items = 1;
map<int32, PlayerInventoryWeaponData> weapons = 2;
}
message PlayerTeleportData {
@ -182,6 +196,13 @@ message PlayerMcElementData {
PlayerMcElementType current_element = 2;
}
message PlayerUnlockedSkinsData {
repeated int32 role_skins = 1;
repeated int32 weapon_skins = 2;
repeated int32 fly_skins = 3;
repeated int32 wing_skins = 4;
}
message PlayerSaveData {
PlayerBasicData basic_data = 1;
PlayerRoleData role_data = 2;
@ -198,4 +219,5 @@ message PlayerSaveData {
PlayerMapTraceData map_trace = 13;
PlayerMonthCardData month_card = 14;
PlayerMcElementData mc_element = 15;
PlayerUnlockedSkinsData unlocked_skins = 16;
}