Implement almost proper monster attribute scaling

This commit is contained in:
Nobody 2022-02-17 23:03:49 +05:00
parent 3053cb987b
commit 616035373d
17 changed files with 667 additions and 128 deletions

View File

@ -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) -> <Self as futures::Future>::Output
where Self: Sized, Self: futures::Future
@ -84,6 +73,7 @@ impl<F,T> Block for F
where F: futures::Future<Output = T>
{}
#[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<i64> {
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<HashMap<u32, i64>> {
let map = collection! {

View File

@ -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<f32>,
}
#[derive(Deserialize, Clone)]
#[serde(rename_all="PascalCase")]
pub struct EntityCurve {
pub level: u32,
pub curve_infos: Vec<CurveInfo>
}

View File

@ -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,
}

26
src/jsonmanager/gather.rs Normal file
View File

@ -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<u32>,
pub point_type: u32, // TODO: probs an enum?
pub gadget_id: u32,
pub item_id: u32,
pub extra_item_id_vec: Vec<u32>,
pub cd: u32,
pub priority: u32,
pub refresh_id: Option<u32>,
pub block_limits: Vec<BlockLimit>,
#[serde(default)]
pub init_disable_interact: bool,
pub save_type: Option<String>, // TODO: this is an enum!
}

View File

@ -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<u32,AvatarSkillDepot>,
pub monster_curves: HashMap<u32,EntityCurve>,
pub monsters: HashMap<u32, Monster>,
pub world_levels: HashMap<u32, WorldLevel>,
pub gadget_props: HashMap<u32, GadgetProp>,
pub gadget_curves: HashMap<u32,EntityCurve>,
pub gathers: HashMap<u32, Gather>,
}
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<AvatarSkillDepot> = reader.read_json_list("AvatarSkillDepot");
let mc: Vec<EntityCurve> = reader.read_json_list("MonsterCurve");
let monsters: Vec<Monster> = reader.read_json_list("Monster");
let world_levels: Vec<WorldLevel> = reader.read_json_list("WorldLevel");
let gadget_props: Vec<GadgetProp> = reader.read_json_list("GadgetProp");
let gc: Vec<EntityCurve> = reader.read_json_list("GadgetCurve");
let gathers: Vec<Gather> = 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<T> = serde_json::from_str(&json_file_str).expect("Error while reading json");
let data: Vec<T> = serde_json::from_str(&json_file_str).expect(&format!("Error while reading json {}", name));
return data;
}
}

View File

@ -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};

View File

@ -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<HpDrop>,
}
#[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<PropGrowCurve>,
}*/
#[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<u32>,
pub ai: String,
#[serde(default)]
pub is_ai_hash_check: bool,
pub equips: Vec<u32>,
pub hp_drops: Vec<HpDropWrap>,
pub kill_drop_id: Option<u32>,
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<PropGrowCurveWrap>,
pub prop_grow_curves: Vec<PropGrowCurve>,
}

View File

@ -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,
}

View File

@ -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<u32,InternalSceneData>,
@ -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!
}));
}
}

View File

@ -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;
pub use self::scene_config::{Vector, Monster, Gadget, Npc};

View File

@ -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,

View File

@ -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<JsonManager>) -> proto::scene_entity_info::Entity;
fn props(&self, world_level: u32, jm: &Arc<JsonManager>, db: &Arc<DatabaseManager>) -> HashMap<u32, i64>;
fn fight_props(&self, world_level: u32, jm: &Arc<JsonManager>, db: &Arc<DatabaseManager>) -> HashMap<proto::FightPropType, f32>;
fn get_scaled_level(&self, world_level: u32, jm: &Arc<JsonManager>) -> u32;
fn curve_info_for_level<'a>(&self, list: &'a HashMap<u32, EntityCurve>, level: u32) -> HashMap<u32, &'a CurveInfo> {
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<JsonManager>, list: &HashMap<u32, EntityCurve>,
scaling_helper: &HashMap<proto::FightPropType, (proto::FightPropType, f32)>,
grow_curves: &HashMap<proto::FightPropType, proto::GrowCurveType>) -> HashMap<proto::FightPropType,f32> {
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<EntityTrait + Sync + Send>,
}
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<JsonManager>) -> 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<JsonManager>, db: &Arc<DatabaseManager>) -> HashMap<u32, i64> {
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<JsonManager>, db: &Arc<DatabaseManager>) -> HashMap<proto::FightPropType,f32> {
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<proto::FightPropType,f32> = 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<proto::FightPropType, proto::GrowCurveType> = 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<proto::FightPropType, (proto::FightPropType, f32)> = 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<JsonManager>) -> 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<JsonManager>,) -> 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<JsonManager>, db: &Arc<DatabaseManager>) -> HashMap<u32, i64> {
HashMap::new() // TODO
}
fn fight_props(&self, world_level: u32, jm: &Arc<JsonManager>, db: &Arc<DatabaseManager>) -> HashMap<proto::FightPropType,f32> {
HashMap::new() // TODO
}
fn get_scaled_level(&self, world_level: u32, jm: &Arc<JsonManager>) -> 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<JsonManager>,) -> 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<JsonManager>, db: &Arc<DatabaseManager>) -> HashMap<u32, i64> {
HashMap::new() // TODO
}
fn fight_props(&self, world_level: u32, jm: &Arc<JsonManager>, db: &Arc<DatabaseManager>) -> HashMap<proto::FightPropType,f32> {
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<proto::FightPropType, proto::GrowCurveType> = 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<proto::FightPropType, (proto::FightPropType, f32)> = 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<JsonManager>) -> 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<JsonManager>, db: &Arc<DatabaseManager>) -> 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<JsonManager>) -> Option<proto::scene_gadget_info::Content> {
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
},
}
}
}

View File

@ -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<u32, Arc<Entity>>,
lua_manager: Arc<LuaManager>,
json_manager: Arc<JsonManager>,
db_manager: Arc<DatabaseManager>,
packets_to_send_tx: Sender<IpcMessage>,
}
@ -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<Mutex<HashMap<u32, Player>>>,
players_moved: Sender<u32>,
lua_manager: Arc<LuaManager>,
json_manager: Arc<JsonManager>,
db_manager: Arc<DatabaseManager>,
}
impl EntitySubsystem {
pub fn new(lua_manager: Arc<LuaManager>, packets_to_send_tx: Sender<IpcMessage>) -> EntitySubsystem {
pub fn new(lua_manager: Arc<LuaManager>, json_manager: Arc<JsonManager>, db_manager: Arc<DatabaseManager>, packets_to_send_tx: Sender<IpcMessage>) -> EntitySubsystem {
let (tx, rx): (Sender<u32>, Receiver<u32>) = 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(),
};

View File

@ -0,0 +1,5 @@
mod entity_subsystem;
mod entities;
pub use self::entity_subsystem::EntitySubsystem;
pub use self::entities::{Entity, EntityTrait};

View File

@ -1,3 +1,3 @@
mod entity_subsystem;
pub mod entity_subsystem;
pub use self::entity_subsystem::EntitySubsystem;

View File

@ -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;

View File

@ -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<proto::FightPropType, f32>) -> Vec<proto::FightPropPair> {
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;
}
}