From 616035373d4b4fc0edcbbc463f2ac462f9c68beb Mon Sep 17 00:00:00 2001 From: Nobody Date: Thu, 17 Feb 2022 23:03:49 +0500 Subject: [PATCH] Implement almost proper monster attribute scaling --- src/dbmanager/database_manager.rs | 27 +- src/jsonmanager/entity_curve.rs | 16 + src/jsonmanager/gadget_prop.rs | 15 + src/jsonmanager/gather.rs | 26 ++ src/jsonmanager/json_manager.rs | 31 +- src/jsonmanager/mod.rs | 7 + src/jsonmanager/monster.rs | 92 ++++ src/jsonmanager/world_level.rs | 8 + src/luamanager/lua_manager.rs | 115 +---- src/luamanager/mod.rs | 3 +- src/server/game_server.rs | 2 +- src/subsystems/entity_subsystem/entities.rs | 400 ++++++++++++++++++ .../entity_subsystem.rs | 17 +- src/subsystems/entity_subsystem/mod.rs | 5 + src/subsystems/mod.rs | 2 +- src/utils/mod.rs | 2 + src/utils/remapper.rs | 27 ++ 17 files changed, 667 insertions(+), 128 deletions(-) create mode 100644 src/jsonmanager/entity_curve.rs create mode 100644 src/jsonmanager/gadget_prop.rs create mode 100644 src/jsonmanager/gather.rs create mode 100644 src/jsonmanager/monster.rs create mode 100644 src/jsonmanager/world_level.rs create mode 100644 src/subsystems/entity_subsystem/entities.rs rename src/subsystems/{ => entity_subsystem}/entity_subsystem.rs (93%) create mode 100644 src/subsystems/entity_subsystem/mod.rs diff --git a/src/dbmanager/database_manager.rs b/src/dbmanager/database_manager.rs index bfd570d..6b6d35f 100644 --- a/src/dbmanager/database_manager.rs +++ b/src/dbmanager/database_manager.rs @@ -1,6 +1,8 @@ // Database Manager use std::collections::HashMap; +use crate::collection; + use sea_orm::{entity::*, error::*, query::*, DbConn, FromQueryResult, Database}; use sea_orm::entity::prelude::*; use crate::JsonManager; @@ -59,19 +61,6 @@ use super::reliquary_prop::Entity as ReliquaryPropEntity; pub use super::furniture_info::Model as FurnitureInfo; use super::furniture_info::Entity as FurnitureInfoEntity; -macro_rules! collection { - // map-like - ($($k:expr => $v:expr),* $(,)?) => {{ - use std::iter::{Iterator, IntoIterator}; - Iterator::collect(IntoIterator::into_iter([$(($k, $v),)*])) - }}; - // set-like - ($($v:expr),* $(,)?) => {{ - use std::iter::{Iterator, IntoIterator}; - Iterator::collect(IntoIterator::into_iter([$($v,)*])) - }}; -} - trait Block { fn wait(self) -> ::Output where Self: Sized, Self: futures::Future @@ -84,6 +73,7 @@ impl Block for F where F: futures::Future {} +#[derive(Debug)] pub struct DatabaseManager { db: DbConn, } @@ -146,6 +136,17 @@ impl DatabaseManager { return Some(props); } + + pub fn get_player_prop(&self, uid: u32, prop_id: u32) -> Option { + match PlayerPropEntity::find().filter( + Condition::all() + .add(super::player_prop::Column::Uid.eq(uid)) + .add(super::player_prop::Column::PropId.eq(prop_id)) + ).one(&self.db).wait() { + Ok(prop) => Some(prop?.prop_value), // Returns None if prop is none + Err(_) => panic!("DB ERROR!"), + } + } /* pub fn _get_avatar_props(&self, guid: u64) -> Option> { let map = collection! { diff --git a/src/jsonmanager/entity_curve.rs b/src/jsonmanager/entity_curve.rs new file mode 100644 index 0000000..7979b01 --- /dev/null +++ b/src/jsonmanager/entity_curve.rs @@ -0,0 +1,16 @@ +use serde::{Serialize, Deserialize}; + +#[derive(Deserialize, Clone)] +#[serde(rename_all="PascalCase")] +pub struct CurveInfo { + pub r#type: proto::GrowCurveType, + pub arith: proto::ArithType, + pub value: Option, +} + +#[derive(Deserialize, Clone)] +#[serde(rename_all="PascalCase")] +pub struct EntityCurve { + pub level: u32, + pub curve_infos: Vec +} \ No newline at end of file diff --git a/src/jsonmanager/gadget_prop.rs b/src/jsonmanager/gadget_prop.rs new file mode 100644 index 0000000..16093e4 --- /dev/null +++ b/src/jsonmanager/gadget_prop.rs @@ -0,0 +1,15 @@ +use serde::{Serialize, Deserialize}; + +#[derive(Deserialize, Clone)] +#[serde(rename_all="PascalCase")] +pub struct GadgetProp { + pub id: u32, + pub hp: f32, + pub hp_curve: proto::GrowCurveType, + #[serde(default)] + pub attack: f32, + pub attack_curve: proto::GrowCurveType, + #[serde(default)] + pub defense: f32, + pub defense_curve: proto::GrowCurveType, +} \ No newline at end of file diff --git a/src/jsonmanager/gather.rs b/src/jsonmanager/gather.rs new file mode 100644 index 0000000..9d9a343 --- /dev/null +++ b/src/jsonmanager/gather.rs @@ -0,0 +1,26 @@ +use serde::{Serialize, Deserialize}; + +#[derive(Deserialize, Clone)] +#[serde(rename_all="PascalCase")] +pub struct BlockLimit { + pub block_id: u32, + pub count: u32, +} + +#[derive(Deserialize, Clone)] +#[serde(rename_all="PascalCase")] +pub struct Gather { + pub id: u32, + pub area_id: Option, + pub point_type: u32, // TODO: probs an enum? + pub gadget_id: u32, + pub item_id: u32, + pub extra_item_id_vec: Vec, + pub cd: u32, + pub priority: u32, + pub refresh_id: Option, + pub block_limits: Vec, + #[serde(default)] + pub init_disable_interact: bool, + pub save_type: Option, // TODO: this is an enum! +} \ No newline at end of file diff --git a/src/jsonmanager/json_manager.rs b/src/jsonmanager/json_manager.rs index 211a1fc..517b7c5 100644 --- a/src/jsonmanager/json_manager.rs +++ b/src/jsonmanager/json_manager.rs @@ -4,8 +4,13 @@ use std::collections::HashMap; use serde::Deserialize; use serde::de::DeserializeOwned; +use crate::jsonmanager::gather::Gather; use super::avatar_skill_depot::AvatarSkillDepot; +use super::entity_curve::EntityCurve; +use super::monster::Monster; +use super::world_level::WorldLevel; +use super::gadget_prop::GadgetProp; struct JsonReader { base_path: String, @@ -14,6 +19,18 @@ struct JsonReader { pub struct JsonManager { reader: JsonReader, pub avatar_skill_depot: HashMap, + pub monster_curves: HashMap, + pub monsters: HashMap, + pub world_levels: HashMap, + pub gadget_props: HashMap, + pub gadget_curves: HashMap, + pub gathers: HashMap, +} + +impl std::fmt::Debug for JsonManager { // TODO: fucking hack! +fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "JsonManager is not debuggable!") +} } impl JsonManager { @@ -21,10 +38,22 @@ impl JsonManager { let reader = JsonReader::new(directory); let asd: Vec = reader.read_json_list("AvatarSkillDepot"); + let mc: Vec = reader.read_json_list("MonsterCurve"); + let monsters: Vec = reader.read_json_list("Monster"); + let world_levels: Vec = reader.read_json_list("WorldLevel"); + let gadget_props: Vec = reader.read_json_list("GadgetProp"); + let gc: Vec = reader.read_json_list("GadgetCurve"); + let gathers: Vec = reader.read_json_list("Gather"); return JsonManager { reader: reader, avatar_skill_depot: asd.into_iter().map(|a| (a.id, a)).collect(), + monster_curves: mc.into_iter().map(|m| (m.level, m)).collect(), + monsters: monsters.into_iter().map(|m| (m.id, m)).collect(), + world_levels: world_levels.into_iter().map(|wl| (wl.level, wl)).collect(), + gadget_props: gadget_props.into_iter().map(|gp| (gp.id, gp)).collect(), + gadget_curves: gc.into_iter().map(|g| (g.level, g)).collect(), + gathers: gathers.into_iter().map(|g| (g.gadget_id, g)).collect(), // TODO: we index it by gadget_id and not by it's id! }; } } @@ -43,7 +72,7 @@ impl JsonReader { let json_file_path = Path::new(&path); let json_file_str = read_to_string(json_file_path).unwrap_or_else(|_| panic!("File {} not found", path)); - let data: Vec = serde_json::from_str(&json_file_str).expect("Error while reading json"); + let data: Vec = serde_json::from_str(&json_file_str).expect(&format!("Error while reading json {}", name)); return data; } } diff --git a/src/jsonmanager/mod.rs b/src/jsonmanager/mod.rs index 4266719..cc1b50c 100644 --- a/src/jsonmanager/mod.rs +++ b/src/jsonmanager/mod.rs @@ -3,3 +3,10 @@ mod json_manager; pub use self::json_manager::JsonManager; mod avatar_skill_depot; +mod entity_curve; +mod monster; +mod world_level; +mod gadget_prop; +mod gather; + +pub use entity_curve::{CurveInfo,EntityCurve}; \ No newline at end of file diff --git a/src/jsonmanager/monster.rs b/src/jsonmanager/monster.rs new file mode 100644 index 0000000..ac09981 --- /dev/null +++ b/src/jsonmanager/monster.rs @@ -0,0 +1,92 @@ +use serde::{Serialize, Deserialize}; + +// TODO: those two structs have fields that are usually missing all together, so it makes sense to omit +// an entire record from the list in the generator. +// For the sake of being compatible with Dimbreath's data I've chosen this way for now - wrapping real data with Option<> + +#[derive(Deserialize, Clone)] +#[serde(rename_all="PascalCase")] +pub struct HpDrop { + pub drop_id: u32, + pub hp_percent: u32, +} + +#[derive(Deserialize, Clone)] +#[serde(rename_all="PascalCase")] +pub struct HpDropWrap { + #[serde(flatten)] + pub data: Option, +} + +#[derive(Deserialize, Clone)] +#[serde(rename_all="PascalCase")] +pub struct PropGrowCurve { + #[serde(default = "PropGrowCurve::default_type")] + pub r#type: proto::FightPropType, + #[serde(default = "PropGrowCurve::default_curve")] + pub grow_curve: proto::GrowCurveType, +} +// TODO: fucking hack! +impl PropGrowCurve { + fn default_type() -> proto::FightPropType { proto::FightPropType::FightPropNone } + fn default_curve() -> proto::GrowCurveType { proto::GrowCurveType::GrowCurveNone } +} + +/* +#[derive(Deserialize, Clone)] +#[serde(rename_all="PascalCase")] +pub struct PropGrowCurveWrap { + #[serde(flatten)] + pub data: Option, +}*/ + +#[derive(Deserialize, Clone)] +#[serde(rename_all="PascalCase")] +pub struct Monster { + pub id: u32, + #[serde(rename = "CampID")] + pub camp_id: u32, + + pub monster_name: String, + pub r#type: String, // TODO: this is an enum! + pub server_script: String, + pub affix: Vec, + pub ai: String, + #[serde(default)] + pub is_ai_hash_check: bool, + pub equips: Vec, + pub hp_drops: Vec, + pub kill_drop_id: Option, + pub exclude_weathers: String, + + #[serde(rename = "FeatureTagGroupID")] + pub feature_tag_group_id: u32, + #[serde(rename = "MpPropID")] + pub mp_prop_id: u32, + + pub hp_base: f32, + #[serde(default)] // Missing for Slimes that attack during the tutorial fight + pub attack_base: f32, + #[serde(default)] // Missing for Dvalin in the aerial fight + pub defense_base: f32, + + #[serde(default)] // Missing for Dvalin in the aerial fight + pub fire_sub_hurt: f32, + #[serde(default)] // Missing for Dvalin in the aerial fight + pub grass_sub_hurt: f32, + #[serde(default)] // Missing for Dvalin in the aerial fight + pub water_sub_hurt: f32, + #[serde(default)] // Missing for Dvalin in the aerial fight + pub elec_sub_hurt: f32, + #[serde(default)] // Missing for Dvalin in the aerial fight + pub wind_sub_hurt: f32, + #[serde(default)] // Missing for Dvalin in the aerial fight + pub ice_sub_hurt: f32, + #[serde(default)] // Missing for Dvalin in the aerial fight + pub rock_sub_hurt: f32, + #[serde(default)] // Missing for Dvalin in the aerial fight + pub physical_sub_hurt: f32, + + //pub prop_grow_curves: Vec, + pub prop_grow_curves: Vec, +} \ No newline at end of file diff --git a/src/jsonmanager/world_level.rs b/src/jsonmanager/world_level.rs new file mode 100644 index 0000000..a13e448 --- /dev/null +++ b/src/jsonmanager/world_level.rs @@ -0,0 +1,8 @@ +use serde::{Serialize, Deserialize}; + +#[derive(Deserialize, Clone)] +#[serde(rename_all="PascalCase")] +pub struct WorldLevel { + pub level: u32, + pub monster_level: u32, +} \ No newline at end of file diff --git a/src/luamanager/lua_manager.rs b/src/luamanager/lua_manager.rs index f026845..d4767ad 100644 --- a/src/luamanager/lua_manager.rs +++ b/src/luamanager/lua_manager.rs @@ -2,11 +2,10 @@ use std::collections::HashMap; use std::result::Result; use std::sync::Arc; -use lua_serde::from_file; - -#[macro_use] -use packet_processor::*; use crate::utils::IdManager; +use crate::subsystems::entity_subsystem::{Entity, EntityTrait}; + +use lua_serde::from_file; use super::scene_config; @@ -43,22 +42,6 @@ pub struct InternalGroupData { // No extra data here } -#[derive(Debug,Clone)] -pub enum EntityType { - Monster(Monster), - Npc(Npc), - Gadget(Gadget), - None, -} - -#[derive(Debug,Clone)] -pub struct Entity { - pub entity_id: u32, - pub group_id: u32, - pub block_id: u32, - pub entity: EntityType, -} - /// Implementation of utility functions impl InternalSceneData { pub fn get_block_by_pos(&self, pos: &Vector) -> Result<&InternalBlockData, String> { @@ -80,89 +63,6 @@ impl InternalSceneData { } } -impl Entity { - pub fn pos(&self) -> Vector { - match &self.entity { - EntityType::Monster(m) => m.pos.clone(), - EntityType::Npc(n) => n.pos.clone(), - EntityType::Gadget(g) => g.pos.clone(), - _ => panic!("Unsupported entity type!"), - } - } - - pub fn rot(&self) -> Vector { - match &self.entity { - EntityType::Monster(m) => m.rot.clone(), - EntityType::Npc(n) => n.rot.clone(), - EntityType::Gadget(g) => g.rot.clone(), - _ => panic!("Unsupported entity type!"), - } - } - - pub fn speed(&self) -> Vector { - // TODO! - Vector { x: 0.0, y: 0.0, z: 0.0 } - } - - pub fn etype(&self) -> i32 { - (match &self.entity { - EntityType::Monster(_) => proto::ProtEntityType::ProtEntityMonster, - EntityType::Npc(n) => proto::ProtEntityType::ProtEntityNpc, - EntityType::Gadget(g) => proto::ProtEntityType::ProtEntityGadget, - _ => panic!("Unsupported entity type!"), - }) as i32 - } - - pub fn convert(&self) -> proto::SceneEntityInfo { - let mut sei = build!(SceneEntityInfo { - entity_id: self.entity_id, - entity_type: self.etype(), - motion_info: Some(build!(MotionInfo { - pos: Some((&self.pos()).into()), - rot: Some((&self.rot()).into()), - speed: Some((&self.speed()).into()), - })), - animator_para_list: vec![], - entity_client_data: Some(build!(EntityClientData {})), - entity_authority_info: Some(build!(EntityAuthorityInfo { - renderer_changed_info: Some(build!(EntityRendererChangedInfo{})), - ai_info: Some(build!(SceneEntityAiInfo { - is_ai_open: true, // TODO! - born_pos: Some((&self.pos()).into()), - })), - born_pos: Some((&self.pos()).into()), - })), - }); - - sei.entity = Some(match &self.entity { - EntityType::Monster(m) => proto::scene_entity_info::Entity::Monster(build!(SceneMonsterInfo { - monster_id: m.monster_id, - group_id: self.group_id, - config_id: m.config_id, - authority_peer_id: 1, // TODO: hardcoded value! - born_type: proto::MonsterBornType::MonsterBornDefault as i32, // TODO: hardcoded value! - block_id: self.block_id, - // TODO: level scaling, weapons! - })), - EntityType::Npc(n) => proto::scene_entity_info::Entity::Npc(build!(SceneNpcInfo { - npc_id: n.npc_id, - block_id: self.block_id, - })), - EntityType::Gadget(g) => proto::scene_entity_info::Entity::Gadget(build!(SceneGadgetInfo { - gadget_id: g.gadget_id, - group_id: self.group_id, - config_id: g.config_id, - authority_peer_id: 1, // TODO: hardcoded value! - is_enable_interact: true, - // TODO: collectibles! - })), - _ => panic!("Unsupported entity type!"), - }); - - sei - } -} - #[derive(Debug)] pub struct LuaManager { scenes_data: HashMap, @@ -254,7 +154,8 @@ impl LuaManager { entity_id: entity_id, group_id: *group_id, block_id: block_id, - entity: EntityType::Npc(npc.clone()), // TODO: very fucking inefficient! + health: 0, + entity: Arc::new(npc.clone()), // TODO: very fucking inefficient! })); } @@ -266,7 +167,8 @@ impl LuaManager { entity_id: entity_id, group_id: *group_id, block_id: block_id, - entity: EntityType::Monster(monster.clone()), // TODO: very fucking inefficient! + health: 0, + entity: Arc::new(monster.clone()), // TODO: very fucking inefficient! })); } @@ -278,7 +180,8 @@ impl LuaManager { entity_id: entity_id, group_id: *group_id, block_id: block_id, - entity: EntityType::Gadget(gadget.clone()), // TODO: very fucking inefficient! + health: 0, + entity: Arc::new(gadget.clone()), // TODO: very fucking inefficient! })); } } diff --git a/src/luamanager/mod.rs b/src/luamanager/mod.rs index 074175c..52b0ec5 100644 --- a/src/luamanager/mod.rs +++ b/src/luamanager/mod.rs @@ -2,5 +2,4 @@ mod lua_manager; mod scene_config; pub use self::lua_manager::LuaManager; -pub use self::scene_config::Vector; -pub use self::lua_manager::Entity; \ No newline at end of file +pub use self::scene_config::{Vector, Monster, Gadget, Npc}; \ No newline at end of file diff --git a/src/server/game_server.rs b/src/server/game_server.rs index a964e23..3bd8326 100644 --- a/src/server/game_server.rs +++ b/src/server/game_server.rs @@ -29,7 +29,7 @@ impl GameServer { let jm = Arc::new(JsonManager::new("./data/json")); let lm = LoginManager::new(db.clone(), jm.clone(), packets_to_send_tx.clone()); let lum = Arc::new(LuaManager::new("./data/lua")); - let em = EntitySubsystem::new(lum.clone(), packets_to_send_tx.clone()); + let em = EntitySubsystem::new(lum.clone(), jm.clone(), db.clone(), packets_to_send_tx.clone()); let gs = GameServer { packets_to_process_rx: packets_to_process_rx, diff --git a/src/subsystems/entity_subsystem/entities.rs b/src/subsystems/entity_subsystem/entities.rs new file mode 100644 index 0000000..110b4f7 --- /dev/null +++ b/src/subsystems/entity_subsystem/entities.rs @@ -0,0 +1,400 @@ +use actix_web::web::Json; +use std::collections::HashMap; +use std::sync::Arc; + +use rand::{self, Rng}; + +#[macro_use] +use packet_processor::*; +use crate::{DatabaseManager, JsonManager}; +use crate::jsonmanager::{CurveInfo, EntityCurve}; + +use crate::collection; + +use crate::luamanager::{Monster, Npc, Gadget, Vector}; +use crate::utils::Remapper; + +pub trait EntityTrait { + fn id(&self) -> String; + fn pos(&self) -> Vector; + fn rot(&self) -> Vector; + fn speed(&self) -> Vector; + fn etype(&self) -> proto::ProtEntityType; + fn info(&self, block_id: u32, group_id: u32, jm: &Arc) -> proto::scene_entity_info::Entity; + fn props(&self, world_level: u32, jm: &Arc, db: &Arc) -> HashMap; + fn fight_props(&self, world_level: u32, jm: &Arc, db: &Arc) -> HashMap; + fn get_scaled_level(&self, world_level: u32, jm: &Arc) -> u32; + + fn curve_info_for_level<'a>(&self, list: &'a HashMap, level: u32) -> HashMap { + list[&level].curve_infos.iter() + .map(|c| (c.r#type.clone() as u32, c)) + .collect() + } + + fn get_scaled_props(&self, world_level: u32, jm: &Arc, list: &HashMap, + scaling_helper: &HashMap, + grow_curves: &HashMap) -> HashMap { + let level = self.get_scaled_level(world_level, jm); + + let curve_info = EntityTrait::curve_info_for_level(self, list, level); + + let mut props = HashMap::new(); + + for (k, v) in scaling_helper.iter() { + let gct = match grow_curves.get(&v.0) { + Some(gct) => gct.clone() as u32, + None => { + println!("No curve {:?} for entity {}!", v.0, self.id()); + continue; + }, + }; + + let curve = match curve_info.get(&gct) { + Some(curve) => curve, + None => panic!("Unknown curve {:?} for level {}", gct, level), + }; + + let scaled_value = match curve.arith { + proto::ArithType::ArithMulti => { + curve.value.unwrap() * v.1 + }, + proto::ArithType::ArithAdd => { + println!("Don't know how to use ArithAdd!"); + v.1 + } + _ => { + panic!("Unknown arith type {:?} for curve {:?} (level {})", curve.arith, curve.r#type.clone() as u32, level); + } + }; + + props.insert(*k, scaled_value); + props.insert(v.0, scaled_value); + } + + return props; + } +} + +impl std::fmt::Debug for EntityTrait+Sync+Send { // TODO: fucking hack! + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{{ \npos: {:?},\n rot: {:?},\n speed: {:?}, \netype: {:?},\n}}", + self.pos(), self.rot(), self.speed(), self.etype() + ) + } +} + +#[derive(Debug,Clone)] +pub struct Entity { + pub entity_id: u32, + pub group_id: u32, + pub block_id: u32, + pub health: u32, + pub entity: Arc, +} + +impl EntityTrait for Monster { + fn id(&self) -> String { + format!("Monster {}", self.monster_id) + } + fn pos(&self) -> Vector { + self.pos.clone() + } + fn rot(&self) -> Vector { + self.rot.clone() + } + fn speed(&self) -> Vector { // TODO! + Vector {x:0.0, y:0.0, z:0.0} + } + fn etype(&self) -> proto::ProtEntityType { + proto::ProtEntityType::ProtEntityMonster + } + fn info(&self, block_id: u32, group_id: u32, jm: &Arc) -> proto::scene_entity_info::Entity { + let monster_info = &jm.monsters.get(&self.monster_id); + + let affixes = match monster_info { + Some(mi) => mi.affix.clone(), + None => { + println!("No monster info found for monster {}! No affix.", self.monster_id); + vec![] + }, + }; + + proto::scene_entity_info::Entity::Monster(build!(SceneMonsterInfo { + monster_id: self.monster_id, + group_id: group_id, + config_id: self.config_id, + authority_peer_id: 1, // TODO: hardcoded value! + born_type: proto::MonsterBornType::MonsterBornDefault as i32, // TODO: hardcoded value! + block_id: block_id, + affix_list: affixes, + // TODO: special_name! + })) + } + fn props(&self, world_level: u32, jm: &Arc, db: &Arc) -> HashMap { + let level = self.get_scaled_level(world_level, jm) as i64; + + collection!{ + proto::PropType::PropLevel as u32 => level, + } + } + fn fight_props(&self, world_level: u32, jm: &Arc, db: &Arc) -> HashMap { + let mut props = HashMap::new(); + + let monster_info = &jm.monsters.get(&self.monster_id); + + match monster_info { + Some(mi) => { + // Non-scaled props + + let non_scaled_props: HashMap = collection!{ + proto::FightPropType::FightPropPhysicalSubHurt => mi.physical_sub_hurt, + proto::FightPropType::FightPropFireSubHurt => mi.fire_sub_hurt, + proto::FightPropType::FightPropElecSubHurt => mi.elec_sub_hurt, + proto::FightPropType::FightPropWaterSubHurt => mi.water_sub_hurt, + proto::FightPropType::FightPropGrassSubHurt => mi.grass_sub_hurt, + proto::FightPropType::FightPropWindSubHurt => mi.wind_sub_hurt, + proto::FightPropType::FightPropRockSubHurt => mi.rock_sub_hurt, + proto::FightPropType::FightPropIceSubHurt => mi.ice_sub_hurt, + }; + + props.extend(non_scaled_props); + + // Scaled props + + // Transform monster's dict into usable format + let grow_curves: HashMap = mi.prop_grow_curves.iter() + //.filter_map(|g| g.data.as_ref()) + .map(|g| (g.r#type, g.grow_curve.clone())) + .collect(); + + let scaling_helper: HashMap = collection!{ + proto::FightPropType::FightPropCurAttack => ( + proto::FightPropType::FightPropBaseAttack, + mi.attack_base, + ), + proto::FightPropType::FightPropCurDefense => ( + proto::FightPropType::FightPropBaseDefense, + mi.defense_base, + ), + proto::FightPropType::FightPropMaxHp => ( + proto::FightPropType::FightPropBaseHp, + mi.hp_base, + ), + }; + + props.extend( + self.get_scaled_props(world_level, jm, &jm.monster_curves, &scaling_helper, &grow_curves) + ); + + // TODO: hack! Properly calculate HP! + match props.get(&proto::FightPropType::FightPropMaxHp) { + Some(value) => { + props.insert(proto::FightPropType::FightPropCurHp, value * 0.7); + }, + None => { + println!("Monster {} has no HP!", self.monster_id); + } + } + }, + None=> { + println!("No monster info found for monster {}! No fight props.", self.monster_id); + }, + }; + + return props; + } + + fn get_scaled_level(&self, world_level: u32, jm: &Arc) -> u32 { + let base_level = jm.world_levels[&1].monster_level; + + let max_level = jm.world_levels[&world_level].monster_level; + + let level = max_level - base_level + self.level; + + return level; + } +} + +impl EntityTrait for Npc { + fn id(&self) -> String { + format!("Npc {}", self.npc_id) + } + fn pos(&self) -> Vector { + self.pos.clone() + } + fn rot(&self) -> Vector { + self.rot.clone() + } + fn speed(&self) -> Vector { // TODO! + Vector {x:0.0, y:0.0, z:0.0} + } + fn etype(&self) -> proto::ProtEntityType { + proto::ProtEntityType::ProtEntityNpc + } + fn info(&self, block_id: u32, group_id: u32, jm: &Arc,) -> proto::scene_entity_info::Entity { + proto::scene_entity_info::Entity::Npc(build!(SceneNpcInfo { + npc_id: self.npc_id, + block_id: block_id, + })) + } + fn props(&self, world_level: u32, jm: &Arc, db: &Arc) -> HashMap { + HashMap::new() // TODO + } + fn fight_props(&self, world_level: u32, jm: &Arc, db: &Arc) -> HashMap { + HashMap::new() // TODO + } + fn get_scaled_level(&self, world_level: u32, jm: &Arc) -> u32 { + /*let base_level = jm.world_levels[&1].monster_level; + + let max_level = jm.world_levels[&world_level].monster_level; + + let level = max_level - base_level + self.level; + + return level;*/ + return 1; // TODO! + } +} + +impl EntityTrait for Gadget { + fn id(&self) -> String { + format!("Gadget {}", self.gadget_id) + } + fn pos(&self) -> Vector { + self.pos.clone() + } + fn rot(&self) -> Vector { + self.rot.clone() + } + fn speed(&self) -> Vector { // TODO! + Vector {x:0.0, y:0.0, z:0.0} + } + fn etype(&self) -> proto::ProtEntityType { + proto::ProtEntityType::ProtEntityGadget + } + fn info(&self, block_id: u32, group_id: u32, jm: &Arc,) -> proto::scene_entity_info::Entity { + proto::scene_entity_info::Entity::Gadget(build!(SceneGadgetInfo { + gadget_id: self.gadget_id, + group_id: group_id, + config_id: self.config_id, + authority_peer_id: 1, // TODO: hardcoded value! + is_enable_interact: true, + content: self.get_content(jm), + })) + } + fn props(&self, world_level: u32, jm: &Arc, db: &Arc) -> HashMap { + HashMap::new() // TODO + } + fn fight_props(&self, world_level: u32, jm: &Arc, db: &Arc) -> HashMap { + let mut props = HashMap::new(); + + let gadget_props = jm.gadget_props.get(&self.gadget_id); + + match gadget_props { + Some(gp) => { + // Scaled props + + // Transform monster's dict into usable format + let grow_curves: HashMap = collection!{ + proto::FightPropType::FightPropBaseHp => gp.hp_curve.clone(), + proto::FightPropType::FightPropBaseAttack => gp.attack_curve.clone(), + proto::FightPropType::FightPropBaseDefense => gp.defense_curve.clone(), + }; + + let scaling_helper: HashMap = collection!{ + proto::FightPropType::FightPropCurAttack => ( + proto::FightPropType::FightPropBaseAttack, + gp.attack, + ), + proto::FightPropType::FightPropCurDefense => ( + proto::FightPropType::FightPropBaseDefense, + gp.defense, + ), + proto::FightPropType::FightPropMaxHp => ( + proto::FightPropType::FightPropBaseHp, + gp.hp, + ), + }; + + props.extend( + self.get_scaled_props(world_level, jm, &jm.gadget_curves, &scaling_helper, &grow_curves) + ); + + // TODO: hack! Properly calculate HP! + match props.get(&proto::FightPropType::FightPropMaxHp) { + Some(value) => { + props.insert(proto::FightPropType::FightPropCurHp, value * 0.7); + }, + None => { + println!("Gadget {} has no HP!", self.gadget_id); + } + } + }, + None=> { + println!("No gadget info found for gadget {}! No fight props.", self.gadget_id); + }, + }; + + return props; + } + fn get_scaled_level(&self, world_level: u32, jm: &Arc) -> u32 { + /*let base_level = jm.world_levels[&1].monster_level; + + let max_level = jm.world_levels[&world_level].monster_level; + + let level = max_level - base_level + self.level; + + return level;*/ + return 1; // TODO! + } +} + +impl Entity { + pub fn pos(&self) -> Vector { + self.entity.pos() + } + + pub fn convert(&self, world_level: u32, jm: &Arc, db: &Arc) -> proto::SceneEntityInfo { + let mut sei = build!(SceneEntityInfo { + entity_id: self.entity_id, + entity_type: self.entity.etype() as i32, + motion_info: Some(build!(MotionInfo { + pos: Some((&self.entity.pos()).into()), + rot: Some((&self.entity.rot()).into()), + speed: Some((&self.entity.speed()).into()), + })), + prop_list: Remapper::remap2(&self.entity.props(world_level, jm, db)), + fight_prop_list: Remapper::remap4(&self.entity.fight_props(world_level, jm, db)), + animator_para_list: vec![], + entity_client_data: Some(build!(EntityClientData {})), + entity_authority_info: Some(build!(EntityAuthorityInfo { + renderer_changed_info: Some(build!(EntityRendererChangedInfo{})), + ai_info: Some(build!(SceneEntityAiInfo { + is_ai_open: true, // TODO! + born_pos: Some((&self.entity.pos()).into()), + })), + born_pos: Some((&self.entity.pos()).into()), + })), + }); + + sei.entity = Some(self.entity.info(self.block_id, self.group_id, &jm)); + + sei + } +} + +impl Gadget { + fn get_content(&self, jm: &Arc) -> Option { + match jm.gathers.get(&self.gadget_id) { // TODO: worktop and other options are missing! + Some(gather) => { + println!("GATHERABLE {} FOUND FOR GADGET {}!", gather.item_id, self.gadget_id); + Some(proto::scene_gadget_info::Content::GatherGadget(build!(GatherGadgetInfo { + item_id: gather.item_id, + }))) + }, + None => { + println!("NO CONTENT FOUND FOR GADGET {}!", self.gadget_id); + None + }, + } + } +} \ No newline at end of file diff --git a/src/subsystems/entity_subsystem.rs b/src/subsystems/entity_subsystem/entity_subsystem.rs similarity index 93% rename from src/subsystems/entity_subsystem.rs rename to src/subsystems/entity_subsystem/entity_subsystem.rs index 1576fff..1f129b5 100644 --- a/src/subsystems/entity_subsystem.rs +++ b/src/subsystems/entity_subsystem/entity_subsystem.rs @@ -14,11 +14,11 @@ use packet_processor_macro::*; #[macro_use] use packet_processor::*; use serde_json::de::Read; -use crate::LuaManager; +use crate::{DatabaseManager, JsonManager, LuaManager}; use crate::utils::{IdManager, TimeManager}; use crate::luamanager::Vector; -use crate::luamanager::Entity; +use super::entities::Entity; #[derive(Debug, Clone)] struct Player { @@ -28,6 +28,8 @@ struct Player { current_block: u32, entities: HashMap>, lua_manager: Arc, + json_manager: Arc, + db_manager: Arc, packets_to_send_tx: Sender, } @@ -102,9 +104,10 @@ impl Player { sent_ms: TimeManager::timestamp(), client_sequence_id: 0, }); + let world_level = self.db_manager.get_player_prop(self.player_id, proto::PropType::PropPlayerWorldLevel as u32).unwrap() as u32; // TODO: hardcoded value! build_and_send!(self, player_id, metadata, SceneEntityAppearNotify { - entity_list: spawn_list.iter().map(|e| e.convert()).collect(), + entity_list: spawn_list.iter().map(|e| e.convert(world_level, &self.json_manager, &self.db_manager)).collect(), appear_type: proto::VisionType::VisionBorn as i32, }); @@ -125,10 +128,12 @@ pub struct EntitySubsystem { players: Arc>>, players_moved: Sender, lua_manager: Arc, + json_manager: Arc, + db_manager: Arc, } impl EntitySubsystem { - pub fn new(lua_manager: Arc, packets_to_send_tx: Sender) -> EntitySubsystem { + pub fn new(lua_manager: Arc, json_manager: Arc, db_manager: Arc, packets_to_send_tx: Sender) -> EntitySubsystem { let (tx, rx): (Sender, Receiver) = mpsc::channel(); let mut es = EntitySubsystem { @@ -137,6 +142,8 @@ impl EntitySubsystem { players_moved: tx, players: Arc::new(Mutex::new(HashMap::new())), lua_manager: lua_manager, + json_manager: json_manager, + db_manager: db_manager, }; es.register(); @@ -244,6 +251,8 @@ impl EntitySubsystem { current_scene: 3, entities: HashMap::new(), lua_manager: self.lua_manager.clone(), + json_manager: self.json_manager.clone(), + db_manager: self.db_manager.clone(), packets_to_send_tx: self.packets_to_send_tx.clone(), }; diff --git a/src/subsystems/entity_subsystem/mod.rs b/src/subsystems/entity_subsystem/mod.rs new file mode 100644 index 0000000..ef9fafa --- /dev/null +++ b/src/subsystems/entity_subsystem/mod.rs @@ -0,0 +1,5 @@ +mod entity_subsystem; +mod entities; + +pub use self::entity_subsystem::EntitySubsystem; +pub use self::entities::{Entity, EntityTrait}; \ No newline at end of file diff --git a/src/subsystems/mod.rs b/src/subsystems/mod.rs index b3301f2..9e49537 100644 --- a/src/subsystems/mod.rs +++ b/src/subsystems/mod.rs @@ -1,3 +1,3 @@ -mod entity_subsystem; +pub mod entity_subsystem; pub use self::entity_subsystem::EntitySubsystem; \ No newline at end of file diff --git a/src/utils/mod.rs b/src/utils/mod.rs index dd2bec0..ad7572d 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -3,6 +3,8 @@ mod data_packet; mod id_manager; mod time_manager; mod avatar_builder; + +#[macro_use] mod remapper; pub use self::handshake_packet::HandshakePacket; diff --git a/src/utils/remapper.rs b/src/utils/remapper.rs index 9c2734a..8045fa6 100644 --- a/src/utils/remapper.rs +++ b/src/utils/remapper.rs @@ -1,5 +1,19 @@ use std::collections::HashMap; +#[macro_export] +macro_rules! collection { + // map-like + ($($k:expr => $v:expr),* $(,)?) => {{ + use std::iter::{Iterator, IntoIterator}; + Iterator::collect(IntoIterator::into_iter([$(($k, $v),)*])) + }}; + // set-like + ($($v:expr),* $(,)?) => {{ + use std::iter::{Iterator, IntoIterator}; + Iterator::collect(IntoIterator::into_iter([$($v,)*])) + }}; +} + pub struct Remapper {} impl Remapper { @@ -46,4 +60,17 @@ impl Remapper { return ret; } + + pub fn remap4(map: &HashMap) -> Vec { + let mut ret = vec![]; + + for (key, value) in map { + let mut pair = proto::FightPropPair::default(); + pair.prop_type = *key as u32; + pair.prop_value = *value; + ret.push(pair); + } + + return ret; + } } \ No newline at end of file