[PATCH] feat: implement formation and switch roles functionality
- Add support for multi-character formations and role switching - Refactor entity management system to handle multiple entities per map - Update attribute component to use a map-based approach for properties - Implement formation-related handlers and notifications - Add world entity management per map instance - Update player save/load logic to handle formations - Add visibility controls for active character display
This commit is contained in:
parent
85fbc933ba
commit
c6b66eacb4
23 changed files with 1298 additions and 543 deletions
|
@ -1,49 +1,31 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use shorekeeper_data::BasePropertyData;
|
use shorekeeper_data::BasePropertyData;
|
||||||
use shorekeeper_protocol::{
|
use shorekeeper_protocol::{
|
||||||
entity_component_pb::ComponentPb, AttrData, AttributeComponentPb, EAttributeType,
|
entity_component_pb::ComponentPb, AttrData, AttributeComponentPb, EAttributeType,
|
||||||
EntityComponentPb, LivingStatus,
|
EntityComponentPb, LivingStatus,
|
||||||
};
|
};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::logic::ecs::component::Component;
|
use crate::logic::ecs::component::Component;
|
||||||
|
use crate::logic::utils::load_role_info::attribute_from_data;
|
||||||
|
|
||||||
pub struct Attribute {
|
pub struct Attribute {
|
||||||
pub attr_map: HashMap<EAttributeType, (i32, i32)>,
|
pub attr_map: HashMap<EAttributeType, (i32, i32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_from_data {
|
|
||||||
($($name:ident),*) => {
|
|
||||||
pub fn from_data(base_property: &BasePropertyData) -> Self {
|
|
||||||
Self {
|
|
||||||
attr_map: HashMap::from([$(
|
|
||||||
::paste::paste!((EAttributeType::[<$name:camel>], (base_property.$name, 0))),
|
|
||||||
)*]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Component for Attribute {
|
impl Component for Attribute {
|
||||||
fn set_pb_data(&self, pb: &mut shorekeeper_protocol::EntityPb) {
|
fn set_pb_data(&self, pb: &mut shorekeeper_protocol::EntityPb) {
|
||||||
pb.living_status = (if self.is_alive() { LivingStatus::Alive } else { LivingStatus::Dead })
|
pb.living_status = (if self.is_alive() {
|
||||||
.into();
|
LivingStatus::Alive
|
||||||
|
} else {
|
||||||
|
LivingStatus::Dead
|
||||||
|
})
|
||||||
|
.into();
|
||||||
|
|
||||||
pb.component_pbs.push(EntityComponentPb {
|
pb.component_pbs.push(EntityComponentPb {
|
||||||
component_pb: Some(ComponentPb::AttributeComponent(AttributeComponentPb {
|
component_pb: Some(ComponentPb::AttributeComponent(
|
||||||
attr_data: self
|
self.build_entity_attribute(),
|
||||||
.attr_map
|
)),
|
||||||
.iter()
|
});
|
||||||
.map(|(ty, (base, incr))| AttrData {
|
|
||||||
attribute_type: (*ty).into(),
|
|
||||||
base_value: *base,
|
|
||||||
increment: *incr,
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
hardness_mode_id: 0,
|
|
||||||
rage_mode_id: 0,
|
|
||||||
})),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,142 +39,27 @@ impl Attribute {
|
||||||
> 0
|
> 0
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_from_data!(
|
#[inline(always)]
|
||||||
lv,
|
pub fn from_data(base_property: &BasePropertyData) -> Self {
|
||||||
life_max,
|
Self {
|
||||||
life,
|
attr_map: attribute_from_data(base_property),
|
||||||
sheild,
|
}
|
||||||
sheild_damage_change,
|
}
|
||||||
sheild_damage_reduce,
|
|
||||||
atk,
|
#[inline(always)]
|
||||||
crit,
|
pub fn build_entity_attribute(&self) -> AttributeComponentPb {
|
||||||
crit_damage,
|
AttributeComponentPb {
|
||||||
def,
|
attr_data: self
|
||||||
energy_efficiency,
|
.attr_map
|
||||||
cd_reduse,
|
.iter()
|
||||||
element_efficiency,
|
.map(|(ty, (base, incr))| AttrData {
|
||||||
damage_change_normal_skill,
|
attribute_type: (*ty).into(),
|
||||||
damage_change,
|
base_value: *base,
|
||||||
damage_reduce,
|
increment: *incr,
|
||||||
damage_change_auto,
|
})
|
||||||
damage_change_cast,
|
.collect(),
|
||||||
damage_change_ultra,
|
hardness_mode_id: 0,
|
||||||
damage_change_qte,
|
rage_mode_id: 0,
|
||||||
damage_change_phys,
|
}
|
||||||
damage_change_element1,
|
}
|
||||||
damage_change_element2,
|
|
||||||
damage_change_element3,
|
|
||||||
damage_change_element4,
|
|
||||||
damage_change_element5,
|
|
||||||
damage_change_element6,
|
|
||||||
damage_resistance_phys,
|
|
||||||
damage_resistance_element1,
|
|
||||||
damage_resistance_element2,
|
|
||||||
damage_resistance_element3,
|
|
||||||
damage_resistance_element4,
|
|
||||||
damage_resistance_element5,
|
|
||||||
damage_resistance_element6,
|
|
||||||
heal_change,
|
|
||||||
healed_change,
|
|
||||||
damage_reduce_phys,
|
|
||||||
damage_reduce_element1,
|
|
||||||
damage_reduce_element2,
|
|
||||||
damage_reduce_element3,
|
|
||||||
damage_reduce_element4,
|
|
||||||
damage_reduce_element5,
|
|
||||||
damage_reduce_element6,
|
|
||||||
reaction_change1,
|
|
||||||
reaction_change2,
|
|
||||||
reaction_change3,
|
|
||||||
reaction_change4,
|
|
||||||
reaction_change5,
|
|
||||||
reaction_change6,
|
|
||||||
reaction_change7,
|
|
||||||
reaction_change8,
|
|
||||||
reaction_change9,
|
|
||||||
reaction_change10,
|
|
||||||
reaction_change11,
|
|
||||||
reaction_change12,
|
|
||||||
reaction_change13,
|
|
||||||
reaction_change14,
|
|
||||||
reaction_change15,
|
|
||||||
energy_max,
|
|
||||||
energy,
|
|
||||||
special_energy_1_max,
|
|
||||||
special_energy_1,
|
|
||||||
special_energy_2_max,
|
|
||||||
special_energy_2,
|
|
||||||
special_energy_3_max,
|
|
||||||
special_energy_3,
|
|
||||||
special_energy_4_max,
|
|
||||||
special_energy_4,
|
|
||||||
strength_max,
|
|
||||||
strength,
|
|
||||||
strength_recover,
|
|
||||||
strength_punish_time,
|
|
||||||
strength_run,
|
|
||||||
strength_swim,
|
|
||||||
strength_fast_swim,
|
|
||||||
hardness_max,
|
|
||||||
hardness,
|
|
||||||
hardness_recover,
|
|
||||||
hardness_punish_time,
|
|
||||||
hardness_change,
|
|
||||||
hardness_reduce,
|
|
||||||
rage_max,
|
|
||||||
rage,
|
|
||||||
rage_recover,
|
|
||||||
rage_punish_time,
|
|
||||||
rage_change,
|
|
||||||
rage_reduce,
|
|
||||||
tough_max,
|
|
||||||
tough,
|
|
||||||
tough_recover,
|
|
||||||
tough_change,
|
|
||||||
tough_reduce,
|
|
||||||
tough_recover_delay_time,
|
|
||||||
element_power1,
|
|
||||||
element_power2,
|
|
||||||
element_power3,
|
|
||||||
element_power4,
|
|
||||||
element_power5,
|
|
||||||
element_power6,
|
|
||||||
special_damage_change,
|
|
||||||
strength_fast_climb_cost,
|
|
||||||
element_property_type,
|
|
||||||
weak_time,
|
|
||||||
ignore_def_rate,
|
|
||||||
ignore_damage_resistance_phys,
|
|
||||||
ignore_damage_resistance_element1,
|
|
||||||
ignore_damage_resistance_element2,
|
|
||||||
ignore_damage_resistance_element3,
|
|
||||||
ignore_damage_resistance_element4,
|
|
||||||
ignore_damage_resistance_element5,
|
|
||||||
ignore_damage_resistance_element6,
|
|
||||||
skill_tough_ratio,
|
|
||||||
strength_climb_jump,
|
|
||||||
strength_gliding,
|
|
||||||
mass,
|
|
||||||
braking_friction_factor,
|
|
||||||
gravity_scale,
|
|
||||||
speed_ratio,
|
|
||||||
damage_change_phantom,
|
|
||||||
auto_attack_speed,
|
|
||||||
cast_attack_speed,
|
|
||||||
status_build_up_1_max,
|
|
||||||
status_build_up_1,
|
|
||||||
status_build_up_2_max,
|
|
||||||
status_build_up_2,
|
|
||||||
status_build_up_3_max,
|
|
||||||
status_build_up_3,
|
|
||||||
status_build_up_4_max,
|
|
||||||
status_build_up_4,
|
|
||||||
status_build_up_5_max,
|
|
||||||
status_build_up_5,
|
|
||||||
paralysis_time_max,
|
|
||||||
paralysis_time,
|
|
||||||
paralysis_time_recover,
|
|
||||||
element_energy_max,
|
|
||||||
element_energy
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +1,118 @@
|
||||||
use std::{cell::RefCell, collections::HashSet};
|
|
||||||
|
|
||||||
use super::component::ComponentContainer;
|
use super::component::ComponentContainer;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::{HashMap, VecDeque};
|
||||||
|
use std::sync::atomic::{AtomicI32, Ordering};
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||||
pub struct Entity(i64);
|
pub struct Entity {
|
||||||
|
pub entity_id: i32,
|
||||||
|
pub entity_type: i32,
|
||||||
|
pub map_id: i32,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct EntityBuilder<'comp>(Entity, &'comp mut Vec<RefCell<ComponentContainer>>);
|
pub struct EntityBuilder<'comp>(Entity, &'comp mut Vec<RefCell<ComponentContainer>>);
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct EntityManager {
|
pub struct EntityManager {
|
||||||
entity_id_counter: i64,
|
active_entity_set: HashMap<i32, Vec<Entity>>,
|
||||||
active_entity_set: HashSet<Entity>,
|
next_id: AtomicI32,
|
||||||
|
recycled_ids: HashMap<i32, VecDeque<i32>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EntityManager {
|
impl EntityManager {
|
||||||
pub fn create(&mut self) -> Entity {
|
pub fn create(&mut self, config_id: i32, entity_type: i32, map_id: i32) -> Entity {
|
||||||
self.entity_id_counter += 1;
|
let entity_id = self
|
||||||
let entity = Entity(self.entity_id_counter);
|
.recycled_ids
|
||||||
|
.get_mut(&config_id)
|
||||||
|
.and_then(|ids| ids.pop_front())
|
||||||
|
.unwrap_or_else(|| self.next_id.fetch_add(1, Ordering::Relaxed));
|
||||||
|
|
||||||
|
let entity = Entity {
|
||||||
|
entity_id,
|
||||||
|
entity_type,
|
||||||
|
map_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.active_entity_set
|
||||||
|
.entry(config_id)
|
||||||
|
.or_default()
|
||||||
|
.push(entity);
|
||||||
|
|
||||||
self.active_entity_set.insert(entity);
|
|
||||||
entity
|
entity
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, id: i64) -> Option<Entity> {
|
pub fn get_entity_id(&self, config_id: i32) -> i32 {
|
||||||
self.active_entity_set.get(&Entity(id)).copied()
|
self.active_entity_set
|
||||||
|
.get(&config_id)
|
||||||
|
.and_then(|entities| entities.first())
|
||||||
|
.map(|entity| entity.entity_id)
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
tracing::error!("Entity Configuration ID {} not found.", config_id);
|
||||||
|
-1
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(dead_code)]
|
pub fn get_config_id(&self, entity_id: i32) -> i32 {
|
||||||
pub fn remove(&mut self, entity: Entity) -> bool {
|
self.active_entity_set
|
||||||
self.active_entity_set.remove(&entity)
|
.iter()
|
||||||
|
.find_map(|(config_id, entities)| {
|
||||||
|
entities
|
||||||
|
.iter()
|
||||||
|
.any(|e| e.entity_id == entity_id)
|
||||||
|
.then_some(*config_id)
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
tracing::error!("Entity ID {} not found.", entity_id);
|
||||||
|
-1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, config_id: i32) -> Entity {
|
||||||
|
self.active_entity_set
|
||||||
|
.get(&config_id)
|
||||||
|
.and_then(|entities| entities.first())
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
tracing::error!("Entity Configuration ID {} not found.", config_id);
|
||||||
|
Entity::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_all_entity_id(&self) -> Vec<i32> {
|
||||||
|
self.active_entity_set
|
||||||
|
.iter()
|
||||||
|
.flat_map(|(_, entities)| entities.iter().map(|e| e.entity_id))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn active_entity_empty(&self) -> bool {
|
||||||
|
self.active_entity_set.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_entity_ids_by_map(&self, map_id: i32) -> Vec<i32> {
|
||||||
|
self.active_entity_set
|
||||||
|
.iter()
|
||||||
|
.flat_map(|(_, entities)| {
|
||||||
|
entities
|
||||||
|
.iter()
|
||||||
|
.filter(|e| e.map_id == map_id)
|
||||||
|
.map(|e| e.entity_id)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn remove(&mut self, entity_id: i32) -> bool {
|
||||||
|
for (config_id, entities) in self.active_entity_set.iter_mut() {
|
||||||
|
if let Some(index) = entities.iter().position(|e| e.entity_id == entity_id) {
|
||||||
|
let entity = entities.remove(index);
|
||||||
|
self.recycled_ids
|
||||||
|
.entry(*config_id)
|
||||||
|
.or_default()
|
||||||
|
.push_back(entity.entity_id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,6 +136,26 @@ impl<'comp> EntityBuilder<'comp> {
|
||||||
|
|
||||||
impl From<Entity> for i64 {
|
impl From<Entity> for i64 {
|
||||||
fn from(value: Entity) -> Self {
|
fn from(value: Entity) -> Self {
|
||||||
value.0
|
value.entity_id as i64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for EntityManager {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
active_entity_set: HashMap::new(),
|
||||||
|
next_id: AtomicI32::new(1),
|
||||||
|
recycled_ids: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Entity {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
entity_id: -1,
|
||||||
|
entity_type: -1,
|
||||||
|
map_id: 8,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,28 +2,56 @@ pub mod component;
|
||||||
pub mod entity;
|
pub mod entity;
|
||||||
pub mod world;
|
pub mod world;
|
||||||
|
|
||||||
// Query specified components from all entities
|
#[macro_export]
|
||||||
|
macro_rules! find_component {
|
||||||
|
($comps:expr, $comp:ident) => {
|
||||||
|
$comps.iter().find_map(|comp| {
|
||||||
|
let r = comp.try_borrow_mut().ok()?;
|
||||||
|
if matches!(&*r, ComponentContainer::$comp(_)) {
|
||||||
|
Some(::std::cell::RefMut::map(r, |r| {
|
||||||
|
let ComponentContainer::$comp(comp_inner) = r else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
comp_inner
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query specified components from all entities (and)
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! query_with {
|
macro_rules! query_with {
|
||||||
($world:expr, $($comp:ident),*) => {
|
($world_entitys:expr, $($comp:ident),*) => {
|
||||||
$world.components().iter().filter(|(_, comps)| {
|
$world_entitys.components().iter().filter(|(_, comps)| {
|
||||||
$(comps.iter().any(|comp| matches!(&*comp.borrow(), ComponentContainer::$comp(_))) && )* true
|
$(comps.iter().any(|comp| matches!(&*comp.borrow(), ComponentContainer::$comp(_))) && )
|
||||||
|
* true
|
||||||
})
|
})
|
||||||
.map(|(e, comps)| {
|
.map(|(e, comps)| {
|
||||||
(*e,
|
(*e,
|
||||||
$(
|
$(
|
||||||
comps.iter().find_map(|comp| {
|
$crate::find_component!(comps, $comp).unwrap(),
|
||||||
let r = comp.try_borrow_mut().ok()?;
|
)*
|
||||||
if matches!(&*r, ComponentContainer::$comp(_)) {
|
)
|
||||||
Some(::std::cell::RefMut::map(r, |r| {
|
})
|
||||||
let ComponentContainer::$comp(comp_inner) = r else { unreachable!() };
|
.collect::<Vec<_>>()
|
||||||
comp_inner
|
};
|
||||||
}))
|
}
|
||||||
}
|
|
||||||
else {
|
// Query specified components from all entities (or)
|
||||||
None
|
#[macro_export]
|
||||||
}
|
macro_rules! query_hn_with {
|
||||||
}).unwrap(),
|
($world_entitys:expr, $($comp:ident),*) => {
|
||||||
|
$world_entitys.components().iter().filter(|(_, comps)| {
|
||||||
|
$(comps.iter().any(|comp| matches!(&*comp.borrow(), ComponentContainer::$comp(_))) || )
|
||||||
|
* false
|
||||||
|
})
|
||||||
|
.map(|(e, comps)| {
|
||||||
|
(*e,
|
||||||
|
$(
|
||||||
|
$crate::find_component!(comps, $comp),
|
||||||
)*
|
)*
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -41,24 +69,24 @@ macro_rules! ident_as_none {
|
||||||
// Query components of specified entity
|
// Query components of specified entity
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! query_components {
|
macro_rules! query_components {
|
||||||
($world:expr, $entity_id:expr, $($comp:ident),*) => {
|
($world_entitys:expr, $entity_id:expr, $($comp:ident),*) => {
|
||||||
$world.components().iter().find(|(id, _)| $entity_id == i64::from(**id))
|
$world_entitys.components().iter().find(|(id, _)| $entity_id == i64::from(**id))
|
||||||
.map(|(_, comps)| {
|
.map(|(_, comps)| {
|
||||||
($(
|
($(
|
||||||
comps.iter().find_map(|comp| {
|
$crate::find_component!(comps, $comp),
|
||||||
let r = comp.try_borrow_mut().ok()?;
|
|
||||||
if matches!(&*r, ComponentContainer::$comp(_)) {
|
|
||||||
Some(::std::cell::RefMut::map(r, |r| {
|
|
||||||
let ComponentContainer::$comp(comp_inner) = r else { unreachable!() };
|
|
||||||
comp_inner
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
)*)
|
)*)
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| ($( $crate::ident_as_none!($comp), )*))
|
.unwrap_or_else(|| ($( $crate::ident_as_none!($comp), )*))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! modify_component {
|
||||||
|
($comps:expr, $comp:ident, $modifier:expr) => {
|
||||||
|
$comps.iter_mut().for_each(|comp| {
|
||||||
|
if let ComponentContainer::$comp(ref mut inner_comp) = &mut **comp {
|
||||||
|
$modifier(inner_comp);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -1,48 +1,30 @@
|
||||||
|
use super::component::ComponentContainer;
|
||||||
|
use super::entity::{Entity, EntityBuilder, EntityManager};
|
||||||
|
use crate::logic::player::InWorldPlayer;
|
||||||
use std::cell::{RefCell, RefMut};
|
use std::cell::{RefCell, RefMut};
|
||||||
use std::collections::hash_map::{Keys, Values};
|
use std::collections::hash_map::{Keys, Values};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::logic::player::InWorldPlayer;
|
pub struct WorldEntity {
|
||||||
|
components: HashMap<i32, Vec<RefCell<ComponentContainer>>>,
|
||||||
use super::component::ComponentContainer;
|
entity_manager: EntityManager,
|
||||||
use super::entity::{Entity, EntityBuilder, EntityManager};
|
}
|
||||||
|
|
||||||
pub struct World {
|
pub struct World {
|
||||||
components: HashMap<Entity, Vec<RefCell<ComponentContainer>>>,
|
pub player_cur_map_id: i32,
|
||||||
entity_manager: EntityManager,
|
pub world_entitys: HashMap<i32, WorldEntity>, // i32 -> map_id
|
||||||
in_world_players: HashMap<i32, InWorldPlayer>, // joined players metadata
|
pub in_world_players: HashMap<i32, InWorldPlayer>, // joined players metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
impl World {
|
impl World {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
components: HashMap::new(),
|
player_cur_map_id: 8,
|
||||||
entity_manager: EntityManager::default(),
|
world_entitys: HashMap::new(),
|
||||||
in_world_players: HashMap::new(),
|
in_world_players: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_entity(&mut self) -> EntityBuilder {
|
|
||||||
let entity = self.entity_manager.create();
|
|
||||||
EntityBuilder::builder(entity, self.components.entry(entity).or_default())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_in_world(&self, entity_id: i64) -> bool {
|
|
||||||
self.entity_manager.get(entity_id).is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn components(&self) -> &HashMap<Entity, Vec<RefCell<ComponentContainer>>> {
|
|
||||||
&self.components
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_entity_components(&self, entity: Entity) -> Vec<RefMut<ComponentContainer>> {
|
|
||||||
let Some(components) = self.components.get(&entity) else {
|
|
||||||
return Vec::with_capacity(0);
|
|
||||||
};
|
|
||||||
|
|
||||||
components.iter().map(|rc| rc.borrow_mut()).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn player_ids(&self) -> Keys<'_, i32, InWorldPlayer> {
|
pub fn player_ids(&self) -> Keys<'_, i32, InWorldPlayer> {
|
||||||
self.in_world_players.keys()
|
self.in_world_players.keys()
|
||||||
}
|
}
|
||||||
|
@ -55,4 +37,84 @@ impl World {
|
||||||
self.in_world_players
|
self.in_world_players
|
||||||
.insert(in_world_player.player_id, in_world_player);
|
.insert(in_world_player.player_id, in_world_player);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_mut_world_entity(&mut self) -> &mut WorldEntity {
|
||||||
|
self.world_entitys
|
||||||
|
.get_mut(&self.player_cur_map_id)
|
||||||
|
.unwrap_or_else(|| panic!("Failed to get cur map data: {}", self.player_cur_map_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_world_entity(&self) -> &WorldEntity {
|
||||||
|
self.world_entitys
|
||||||
|
.get(&self.player_cur_map_id)
|
||||||
|
.unwrap_or_else(|| panic!("Failed to get cur map data: {}", self.player_cur_map_id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WorldEntity {
|
||||||
|
pub fn create_entity(
|
||||||
|
&mut self,
|
||||||
|
config_id: i32,
|
||||||
|
entity_type: i32,
|
||||||
|
map_id: i32,
|
||||||
|
) -> EntityBuilder {
|
||||||
|
let entity = self.entity_manager.create(config_id, entity_type, map_id);
|
||||||
|
EntityBuilder::builder(
|
||||||
|
entity,
|
||||||
|
self.components
|
||||||
|
.entry(entity.entity_id)
|
||||||
|
.or_insert(Vec::new()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_in_all_world_map(&self, entity_id: i32) -> bool {
|
||||||
|
self.entity_manager.get_all_entity_id().contains(&entity_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_in_world_map(&self, entity_id: i32, map_id: i32) -> bool {
|
||||||
|
self.entity_manager
|
||||||
|
.get_entity_ids_by_map(map_id)
|
||||||
|
.contains(&entity_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_entity_id(&self, config_id: i32) -> i64 {
|
||||||
|
self.entity_manager.get_entity_id(config_id) as i64
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_config_id(&self, entity_id: i32) -> i32 {
|
||||||
|
self.entity_manager.get_config_id(entity_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_entity(&self, config_id: i32) -> Entity {
|
||||||
|
self.entity_manager.get(config_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn components(&self) -> &HashMap<i32, Vec<RefCell<ComponentContainer>>> {
|
||||||
|
&self.components
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_entity_components(&self, entity_id: i32) -> Vec<RefMut<ComponentContainer>> {
|
||||||
|
if let Some(components) = self.components.get(&entity_id) {
|
||||||
|
components.iter().map(|rc| rc.borrow_mut()).collect()
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_entity(&mut self, entity_id: i32) -> bool {
|
||||||
|
self.components.remove(&entity_id).is_some() && self.entity_manager.remove(entity_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn active_entity_empty(&self) -> bool {
|
||||||
|
self.entity_manager.active_entity_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for WorldEntity {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
components: HashMap::new(),
|
||||||
|
entity_manager: EntityManager::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,73 @@
|
||||||
use shorekeeper_protocol::combat_message::{CombatSendPackRequest, CombatSendPackResponse};
|
|
||||||
use shorekeeper_protocol::ErrorCode;
|
|
||||||
|
|
||||||
use crate::logic::player::Player;
|
use crate::logic::player::Player;
|
||||||
|
use shorekeeper_protocol::combat_message::{
|
||||||
|
combat_receive_data, combat_request_data, combat_response_data, combat_send_data,
|
||||||
|
CombatReceiveData, CombatRequestData, CombatResponseData, CombatSendPackRequest,
|
||||||
|
CombatSendPackResponse,
|
||||||
|
};
|
||||||
|
use shorekeeper_protocol::{ErrorCode, SwitchRoleRequest, SwitchRoleResponse};
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn create_combat_response(
|
||||||
|
combat_request: &CombatRequestData,
|
||||||
|
message: combat_response_data::Message,
|
||||||
|
) -> CombatReceiveData {
|
||||||
|
CombatReceiveData {
|
||||||
|
message: Some(combat_receive_data::Message::CombatResponseData(
|
||||||
|
CombatResponseData {
|
||||||
|
combat_common: combat_request.combat_common,
|
||||||
|
request_id: combat_request.request_id,
|
||||||
|
message: Some(message),
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn on_combat_message_combat_send_pack_request(
|
pub fn on_combat_message_combat_send_pack_request(
|
||||||
_player: &Player,
|
player: &mut Player,
|
||||||
request: CombatSendPackRequest,
|
request: CombatSendPackRequest,
|
||||||
response: &mut CombatSendPackResponse,
|
response: &mut CombatSendPackResponse,
|
||||||
) {
|
) {
|
||||||
tracing::debug!("CombatSendPackRequest: for {:?}", request);
|
for data in request.data.iter() {
|
||||||
// TODO: Implement this
|
if let Some(combat_send_data::Message::Request(ref request_data)) = data.message {
|
||||||
|
if let Some(ref request_message) = request_data.message {
|
||||||
|
match request_message {
|
||||||
|
combat_request_data::Message::SwitchRoleRequest(ref request) => {
|
||||||
|
handle_switch_role_request(player, request_data, request, response);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response.error_code = ErrorCode::Success.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_switch_role_request(
|
||||||
|
player: &mut Player,
|
||||||
|
combat_request: &CombatRequestData,
|
||||||
|
request: &SwitchRoleRequest,
|
||||||
|
response: &mut CombatSendPackResponse,
|
||||||
|
) {
|
||||||
|
// Find current formation and update current role
|
||||||
|
if let Some(formation) = player.formation_list.values_mut().find(|f| f.is_current) {
|
||||||
|
formation.cur_role = request.role_id;
|
||||||
|
|
||||||
|
let receive_pack = response
|
||||||
|
.receive_pack_notify
|
||||||
|
.get_or_insert_with(Default::default);
|
||||||
|
|
||||||
|
receive_pack.data.push(create_combat_response(
|
||||||
|
combat_request,
|
||||||
|
combat_response_data::Message::SwitchRoleResponse(SwitchRoleResponse {
|
||||||
|
error_code: ErrorCode::Success.into(),
|
||||||
|
role_id: request.role_id,
|
||||||
|
}),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
tracing::error!("Role with id {} not found", request.role_id);
|
||||||
|
response.error_code = ErrorCode::ErrSwitchRoleEntityNotExist.into();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
response.error_code = ErrorCode::Success.into();
|
response.error_code = ErrorCode::Success.into();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
use shorekeeper_protocol::{EntityActiveRequest, EntityActiveResponse, EntityLoadCompleteRequest,
|
|
||||||
EntityLoadCompleteResponse, EntityOnLandedRequest,
|
|
||||||
EntityOnLandedResponse, EntityPositionRequest, EntityPositionResponse,
|
|
||||||
ErrorCode, MovePackagePush,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{logic::ecs::component::ComponentContainer, logic::player::Player, query_components};
|
use crate::{logic::ecs::component::ComponentContainer, logic::player::Player, query_components};
|
||||||
|
use shorekeeper_protocol::entity_component_pb::ComponentPb;
|
||||||
|
use shorekeeper_protocol::{
|
||||||
|
EntityActiveRequest, EntityActiveResponse, EntityComponentPb, EntityLoadCompleteRequest,
|
||||||
|
EntityLoadCompleteResponse, EntityOnLandedRequest, EntityOnLandedResponse,
|
||||||
|
EntityPositionRequest, EntityPositionResponse, ErrorCode, MovePackagePush,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn on_entity_active_request(
|
pub fn on_entity_active_request(
|
||||||
player: &Player,
|
player: &Player,
|
||||||
request: EntityActiveRequest,
|
request: EntityActiveRequest,
|
||||||
response: &mut EntityActiveResponse,
|
response: &mut EntityActiveResponse,
|
||||||
) {
|
) {
|
||||||
let world = player.world.borrow();
|
let world_ref = player.world.borrow();
|
||||||
|
let world = world_ref.get_world_entity();
|
||||||
|
|
||||||
if !world.is_in_world(request.entity_id) {
|
if !world.is_in_all_world_map(request.entity_id as i32) {
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
"EntityActiveRequest: entity with id {} doesn't exist, player_id: {}",
|
"EntityActiveRequest: entity with id {} doesn't exist, player_id: {}",
|
||||||
request.entity_id,
|
request.entity_id,
|
||||||
|
@ -22,14 +23,28 @@ pub fn on_entity_active_request(
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(position) = query_components!(world, request.entity_id, Position).0 {
|
if let (Some(position), Some(attribute)) =
|
||||||
// TODO: proper entity "activation" logic
|
query_components!(world, request.entity_id, Position, Attribute)
|
||||||
|
{
|
||||||
|
response.is_visible = true;
|
||||||
response.pos = Some(position.0.get_position_protobuf());
|
response.pos = Some(position.0.get_position_protobuf());
|
||||||
response.rot = Some(position.0.get_rotation_protobuf());
|
response.rot = Some(position.0.get_rotation_protobuf());
|
||||||
}
|
|
||||||
|
|
||||||
response.component_pbs = Vec::new(); // not implemented
|
response.component_pbs.push(EntityComponentPb {
|
||||||
response.error_code = ErrorCode::Success.into();
|
component_pb: Some(ComponentPb::AttributeComponent(
|
||||||
|
attribute.build_entity_attribute(),
|
||||||
|
)),
|
||||||
|
});
|
||||||
|
|
||||||
|
response.error_code = ErrorCode::Success.into();
|
||||||
|
} else {
|
||||||
|
tracing::error!(
|
||||||
|
"EntityActiveRequest: entity with id {} not found",
|
||||||
|
request.entity_id
|
||||||
|
);
|
||||||
|
response.error_code = ErrorCode::ErrEntityNotFound.into(); // TODO: replace with appropriate error code
|
||||||
|
return;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_entity_on_landed_request(
|
pub fn on_entity_on_landed_request(
|
||||||
|
@ -38,33 +53,43 @@ pub fn on_entity_on_landed_request(
|
||||||
_: &mut EntityOnLandedResponse,
|
_: &mut EntityOnLandedResponse,
|
||||||
) {
|
) {
|
||||||
// TODO: More implementation?
|
// TODO: More implementation?
|
||||||
tracing::debug!("EntityOnLandedRequest: entity with id {} landed", request.entity_id);
|
tracing::debug!(
|
||||||
|
"EntityOnLandedRequest: entity with id {} landed",
|
||||||
|
request.entity_id
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_entity_position_request(_: &Player,
|
pub fn on_entity_position_request(
|
||||||
request: EntityPositionRequest,
|
_: &Player,
|
||||||
_: &mut EntityPositionResponse,
|
request: EntityPositionRequest,
|
||||||
|
_: &mut EntityPositionResponse,
|
||||||
) {
|
) {
|
||||||
// TODO: Implement this
|
// TODO: Implement this
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
"EntityPositionRequest: config with id {} for map {} position requested",
|
"EntityPositionRequest: config with id {} for map {} position requested",
|
||||||
request.config_id, request.map_id
|
request.config_id,
|
||||||
|
request.map_id
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_entity_load_complete_request(_: &Player,
|
pub fn on_entity_load_complete_request(
|
||||||
request: EntityLoadCompleteRequest,
|
_: &Player,
|
||||||
_: &mut EntityLoadCompleteResponse,
|
request: EntityLoadCompleteRequest,
|
||||||
|
_: &mut EntityLoadCompleteResponse,
|
||||||
) {
|
) {
|
||||||
// TODO: Implement this
|
// TODO: Implement this
|
||||||
tracing::debug!("EntityLoadCompleteRequest: for ids {:?}", request.entity_ids);
|
tracing::debug!(
|
||||||
|
"EntityLoadCompleteRequest: for ids {:?}",
|
||||||
|
request.entity_ids
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_move_package_push(player: &mut Player, push: MovePackagePush) {
|
pub fn on_move_package_push(player: &mut Player, push: MovePackagePush) {
|
||||||
let world = player.world.borrow();
|
let world_ref = player.world.borrow();
|
||||||
|
let world = world_ref.get_world_entity();
|
||||||
|
|
||||||
for moving_entity in push.moving_entities {
|
for moving_entity in push.moving_entities {
|
||||||
if !world.is_in_world(moving_entity.entity_id) {
|
if !world.is_in_all_world_map(moving_entity.entity_id as i32) {
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
"MovePackage: entity with id {} doesn't exist",
|
"MovePackage: entity with id {} doesn't exist",
|
||||||
moving_entity.entity_id
|
moving_entity.entity_id
|
||||||
|
|
|
@ -3,8 +3,8 @@ use shorekeeper_protocol::{GuideInfoRequest, GuideInfoResponse};
|
||||||
use crate::logic::player::Player;
|
use crate::logic::player::Player;
|
||||||
|
|
||||||
pub fn on_guide_info_request(
|
pub fn on_guide_info_request(
|
||||||
player: &Player,
|
_player: &Player,
|
||||||
request: GuideInfoRequest,
|
_request: GuideInfoRequest,
|
||||||
response: &mut GuideInfoResponse,
|
response: &mut GuideInfoResponse,
|
||||||
) {
|
) {
|
||||||
// TODO: Implement this
|
// TODO: Implement this
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
use shorekeeper_protocol::{ErrorCode, Hih, InputSettingRequest, InputSettingResponse, InputSettingUpdateRequest, InputSettingUpdateResponse, LanguageSettingUpdateRequest, LanguageSettingUpdateResponse, ServerPlayStationPlayOnlyStateRequest, ServerPlayStationPlayOnlyStateResponse, VersionInfoPush};
|
use shorekeeper_protocol::{
|
||||||
|
ErrorCode, Hih, InputSettingRequest, InputSettingResponse, InputSettingUpdateRequest,
|
||||||
|
InputSettingUpdateResponse, LanguageSettingUpdateRequest, LanguageSettingUpdateResponse,
|
||||||
|
ServerPlayStationPlayOnlyStateRequest, ServerPlayStationPlayOnlyStateResponse, VersionInfoPush,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::logic::player::Player;
|
use crate::logic::player::Player;
|
||||||
|
|
||||||
|
@ -34,7 +38,7 @@ pub fn on_server_play_station_play_only_state_request(
|
||||||
response.play_station_play_only_state = false;
|
response.play_station_play_only_state = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_version_info_push(player: &Player, push: VersionInfoPush) {
|
pub fn on_version_info_push(_player: &Player, push: VersionInfoPush) {
|
||||||
// TODO: Shall we do safety check and ensure we have compatible versions?
|
// TODO: Shall we do safety check and ensure we have compatible versions?
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
"Client versions: launcher: {}, app: {}, resources: {}",
|
"Client versions: launcher: {}, app: {}, resources: {}",
|
||||||
|
|
|
@ -3,6 +3,7 @@ mod entity;
|
||||||
mod guide;
|
mod guide;
|
||||||
mod mail;
|
mod mail;
|
||||||
mod misc;
|
mod misc;
|
||||||
|
mod role;
|
||||||
mod scene;
|
mod scene;
|
||||||
mod skill;
|
mod skill;
|
||||||
|
|
||||||
|
@ -11,6 +12,7 @@ pub use entity::*;
|
||||||
pub use guide::*;
|
pub use guide::*;
|
||||||
pub use mail::*;
|
pub use mail::*;
|
||||||
pub use misc::*;
|
pub use misc::*;
|
||||||
|
pub use role::*;
|
||||||
pub use scene::*;
|
pub use scene::*;
|
||||||
pub use skill::*;
|
pub use skill::*;
|
||||||
|
|
||||||
|
@ -89,6 +91,13 @@ handle_request! {
|
||||||
// Combat
|
// Combat
|
||||||
CombatSendPack, combat_message;
|
CombatSendPack, combat_message;
|
||||||
|
|
||||||
|
// Role
|
||||||
|
RoleShowListUpdate;
|
||||||
|
ClientCurrentRoleReport;
|
||||||
|
RoleFavorList;
|
||||||
|
FormationAttr;
|
||||||
|
UpdateFormation;
|
||||||
|
|
||||||
// Entity
|
// Entity
|
||||||
EntityActive;
|
EntityActive;
|
||||||
EntityOnLanded;
|
EntityOnLanded;
|
||||||
|
|
141
game-server/src/logic/handler/role.rs
Normal file
141
game-server/src/logic/handler/role.rs
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
use crate::logic::player::Player;
|
||||||
|
use crate::logic::role::{Role, RoleFormation};
|
||||||
|
use shorekeeper_protocol::{
|
||||||
|
ClientCurrentRoleReportRequest, ClientCurrentRoleReportResponse, ERemoveEntityType, ErrorCode,
|
||||||
|
FormationAttrRequest, FormationAttrResponse, RoleFavorListRequest, RoleFavorListResponse,
|
||||||
|
RoleShowListUpdateRequest, RoleShowListUpdateResponse, UpdateFormationRequest,
|
||||||
|
UpdateFormationResponse,
|
||||||
|
};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
pub fn on_role_show_list_update_request(
|
||||||
|
player: &mut Player,
|
||||||
|
request: RoleShowListUpdateRequest,
|
||||||
|
response: &mut RoleShowListUpdateResponse,
|
||||||
|
) {
|
||||||
|
let role_ids: HashSet<i32> = player.role_list.keys().cloned().collect();
|
||||||
|
let all_exist = request.role_list.iter().all(|id| role_ids.contains(id));
|
||||||
|
|
||||||
|
if all_exist {
|
||||||
|
player.basic_info.role_show_list = request.role_list;
|
||||||
|
response.error_code = ErrorCode::Success.into();
|
||||||
|
} else {
|
||||||
|
response.error_code = ErrorCode::InvalidRequest.into(); // TODO: replace with appropriate error code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_client_current_role_report_request(
|
||||||
|
_player: &Player,
|
||||||
|
request: ClientCurrentRoleReportRequest,
|
||||||
|
response: &mut ClientCurrentRoleReportResponse,
|
||||||
|
) {
|
||||||
|
response.current_entity_id = request.current_entity_id;
|
||||||
|
response.player_id = request.player_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_role_favor_list_request(
|
||||||
|
_player: &Player,
|
||||||
|
_request: RoleFavorListRequest,
|
||||||
|
response: &mut RoleFavorListResponse,
|
||||||
|
) {
|
||||||
|
response.favor_list = vec![]; // TODO: add favor
|
||||||
|
response.error_code = ErrorCode::Success.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_formation_attr_request(
|
||||||
|
_player: &Player,
|
||||||
|
_request: FormationAttrRequest,
|
||||||
|
response: &mut FormationAttrResponse,
|
||||||
|
) {
|
||||||
|
response.error_code = ErrorCode::Success.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_update_formation_request(
|
||||||
|
player: &mut Player,
|
||||||
|
request: UpdateFormationRequest,
|
||||||
|
response: &mut UpdateFormationResponse,
|
||||||
|
) {
|
||||||
|
let mut world_ref = player.world.borrow_mut();
|
||||||
|
let world = world_ref.get_mut_world_entity();
|
||||||
|
|
||||||
|
for formation in request.formations {
|
||||||
|
let formation_id = formation.formation_id;
|
||||||
|
let cur_role = formation.cur_role;
|
||||||
|
let is_current = formation.is_current;
|
||||||
|
|
||||||
|
if is_current {
|
||||||
|
// update player current formation id
|
||||||
|
player.cur_formation_id = formation_id;
|
||||||
|
|
||||||
|
// search old formation id and set real_formation_id, set is_current to false
|
||||||
|
let mut real_formation_id = formation_id;
|
||||||
|
if let Some(rf) = player
|
||||||
|
.formation_list
|
||||||
|
.values_mut()
|
||||||
|
.find(|rf| rf.is_current && rf.id != formation_id)
|
||||||
|
{
|
||||||
|
real_formation_id = rf.id;
|
||||||
|
rf.is_current = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(old_formation) = player.formation_list.get(&real_formation_id) {
|
||||||
|
let removed_entities: Vec<i64> = old_formation
|
||||||
|
.role_ids
|
||||||
|
.iter()
|
||||||
|
.map(|&role_id| world.get_entity_id(role_id))
|
||||||
|
.collect();
|
||||||
|
removed_entities.iter().for_each(|&entity_id| {
|
||||||
|
world.remove_entity(entity_id as i32);
|
||||||
|
});
|
||||||
|
player.notify(player.build_player_entity_remove_notify(
|
||||||
|
removed_entities,
|
||||||
|
ERemoveEntityType::RemoveTypeNormal,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let added_roles: Vec<Role> = formation
|
||||||
|
.role_ids
|
||||||
|
.iter()
|
||||||
|
.map(|&role_id| Role::new(role_id))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if !added_roles.is_empty() {
|
||||||
|
// add new roles
|
||||||
|
player.notify(player.build_player_entity_add_notify(added_roles, world));
|
||||||
|
}
|
||||||
|
|
||||||
|
// send update group formation notify
|
||||||
|
player.notify(player.build_update_group_formation_notify(
|
||||||
|
RoleFormation {
|
||||||
|
id: formation_id,
|
||||||
|
cur_role,
|
||||||
|
role_ids: formation.role_ids.clone(),
|
||||||
|
is_current,
|
||||||
|
},
|
||||||
|
world,
|
||||||
|
));
|
||||||
|
|
||||||
|
response.formation = Some(formation.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// update all formation and check formation_list
|
||||||
|
player
|
||||||
|
.formation_list
|
||||||
|
.entry(formation_id)
|
||||||
|
.and_modify(|r| {
|
||||||
|
r.cur_role = formation.cur_role;
|
||||||
|
r.role_ids = formation.role_ids.clone();
|
||||||
|
r.is_current = is_current;
|
||||||
|
})
|
||||||
|
.or_insert(RoleFormation {
|
||||||
|
id: formation_id,
|
||||||
|
cur_role: formation.cur_role,
|
||||||
|
role_ids: formation.role_ids,
|
||||||
|
is_current,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
player.notify(player.build_update_formation_notify());
|
||||||
|
|
||||||
|
response.error_code = ErrorCode::Success.into();
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
use shorekeeper_protocol::{ErrorCode, SceneLoadingFinishRequest, SceneLoadingFinishResponse,
|
use shorekeeper_protocol::{
|
||||||
SceneTraceRequest, SceneTraceResponse, UpdateSceneDateRequest,
|
ErrorCode, SceneLoadingFinishRequest, SceneLoadingFinishResponse, SceneTraceRequest,
|
||||||
UpdateSceneDateResponse,
|
SceneTraceResponse, UpdateSceneDateRequest, UpdateSceneDateResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::logic::player::Player;
|
use crate::logic::player::Player;
|
||||||
|
@ -15,7 +15,7 @@ pub fn on_scene_trace_request(
|
||||||
|
|
||||||
pub fn on_scene_loading_finish_request(
|
pub fn on_scene_loading_finish_request(
|
||||||
_player: &Player,
|
_player: &Player,
|
||||||
request: SceneLoadingFinishRequest,
|
_request: SceneLoadingFinishRequest,
|
||||||
response: &mut SceneLoadingFinishResponse,
|
response: &mut SceneLoadingFinishResponse,
|
||||||
) {
|
) {
|
||||||
// TODO: Implement this if needed
|
// TODO: Implement this if needed
|
||||||
|
|
|
@ -13,8 +13,11 @@ pub fn on_vision_explore_skill_set_request(
|
||||||
) {
|
) {
|
||||||
player.explore_tools.active_explore_skill = request.skill_id;
|
player.explore_tools.active_explore_skill = request.skill_id;
|
||||||
|
|
||||||
let world = player.world.borrow();
|
for (entity, owner, mut vision_skill) in query_with!(
|
||||||
for (entity, owner, mut vision_skill) in query_with!(world, OwnerPlayer, VisionSkill) {
|
player.world.borrow().get_world_entity(),
|
||||||
|
OwnerPlayer,
|
||||||
|
VisionSkill
|
||||||
|
) {
|
||||||
if owner.0 == player.basic_info.id {
|
if owner.0 == player.basic_info.id {
|
||||||
vision_skill.skill_id = request.skill_id;
|
vision_skill.skill_id = request.skill_id;
|
||||||
player.notify(VisionSkillChangeNotify {
|
player.notify(VisionSkillChangeNotify {
|
||||||
|
|
|
@ -10,6 +10,8 @@ pub struct PlayerBasicInfo {
|
||||||
pub exp: i32,
|
pub exp: i32,
|
||||||
pub head_photo: i32,
|
pub head_photo: i32,
|
||||||
pub head_frame: i32,
|
pub head_frame: i32,
|
||||||
|
pub cur_map_id: i32,
|
||||||
|
pub role_show_list: Vec<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlayerBasicInfo {
|
impl PlayerBasicInfo {
|
||||||
|
@ -37,6 +39,8 @@ impl PlayerBasicInfo {
|
||||||
exp: data.exp,
|
exp: data.exp,
|
||||||
head_photo: data.head_photo,
|
head_photo: data.head_photo,
|
||||||
head_frame: data.head_frame,
|
head_frame: data.head_frame,
|
||||||
|
cur_map_id: data.cur_map_id,
|
||||||
|
role_show_list: data.role_show_list,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,6 +53,8 @@ impl PlayerBasicInfo {
|
||||||
exp: self.exp,
|
exp: self.exp,
|
||||||
head_photo: self.head_photo,
|
head_photo: self.head_photo,
|
||||||
head_frame: self.head_frame,
|
head_frame: self.head_frame,
|
||||||
|
cur_map_id: self.cur_map_id,
|
||||||
|
role_show_list: self.role_show_list.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,23 @@
|
||||||
use std::{cell::RefCell, collections::HashSet, rc::Rc, sync::Arc};
|
|
||||||
|
|
||||||
use basic_info::PlayerBasicInfo;
|
|
||||||
use common::time_util;
|
use common::time_util;
|
||||||
use explore_tools::ExploreTools;
|
|
||||||
use location::PlayerLocation;
|
|
||||||
use player_func::PlayerFunc;
|
|
||||||
use shorekeeper_protocol::{
|
use shorekeeper_protocol::{
|
||||||
message::Message, ItemPkgOpenNotify, PbGetRoleListNotify, PlayerBasicData, PlayerRoleData,
|
EEntityType, ERemoveEntityType, EntityAddNotify, EntityConfigType, EntityPb, EntityRemoveInfo,
|
||||||
PlayerSaveData, ProtocolUnit,
|
EntityRemoveNotify, FightFormationNotifyInfo, FightRoleInfo, FightRoleInfos, FormationRoleInfo,
|
||||||
|
GroupFormation, ItemPkgOpenNotify, LivingStatus, PbGetRoleListNotify, PlayerBasicData,
|
||||||
|
PlayerFightFormations, PlayerRoleData, PlayerSaveData, ProtocolUnit, UpdateFormationNotify,
|
||||||
|
UpdateGroupFormationNotify,
|
||||||
};
|
};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::logic::{
|
||||||
|
components::{
|
||||||
|
Attribute, EntityConfig, Equip, Movement, OwnerPlayer, PlayerEntityMarker, Position,
|
||||||
|
Visibility, VisionSkill,
|
||||||
|
},
|
||||||
|
ecs::component::ComponentContainer,
|
||||||
|
};
|
||||||
use crate::session::Session;
|
use crate::session::Session;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
@ -23,14 +31,24 @@ mod in_world_player;
|
||||||
mod location;
|
mod location;
|
||||||
mod player_func;
|
mod player_func;
|
||||||
|
|
||||||
|
use crate::create_player_entity_pb;
|
||||||
|
use crate::logic::ecs::world::WorldEntity;
|
||||||
|
use crate::logic::player::basic_info::PlayerBasicInfo;
|
||||||
|
use crate::logic::player::explore_tools::ExploreTools;
|
||||||
|
use crate::logic::player::location::PlayerLocation;
|
||||||
|
use crate::logic::player::player_func::PlayerFunc;
|
||||||
pub use in_world_player::InWorldPlayer;
|
pub use in_world_player::InWorldPlayer;
|
||||||
|
use shorekeeper_data::base_property_data;
|
||||||
|
use shorekeeper_data::role_info_data;
|
||||||
|
use shorekeeper_protocol::message::Message;
|
||||||
|
|
||||||
pub struct Player {
|
pub struct Player {
|
||||||
session: Option<Arc<Session>>,
|
session: Option<Arc<Session>>,
|
||||||
// Persistent
|
// Persistent
|
||||||
pub basic_info: PlayerBasicInfo,
|
pub basic_info: PlayerBasicInfo,
|
||||||
pub role_list: Vec<Role>,
|
pub role_list: HashMap<i32, Role>,
|
||||||
pub formation_list: Vec<RoleFormation>,
|
pub formation_list: HashMap<i32, RoleFormation>,
|
||||||
|
pub cur_formation_id: i32,
|
||||||
pub location: PlayerLocation,
|
pub location: PlayerLocation,
|
||||||
pub func: PlayerFunc,
|
pub func: PlayerFunc,
|
||||||
pub explore_tools: ExploreTools,
|
pub explore_tools: ExploreTools,
|
||||||
|
@ -41,28 +59,10 @@ pub struct Player {
|
||||||
|
|
||||||
impl Player {
|
impl Player {
|
||||||
pub fn init(&mut self) {
|
pub fn init(&mut self) {
|
||||||
if self.role_list.is_empty() {
|
if self.role_list.is_empty() || self.formation_list.is_empty() {
|
||||||
self.on_first_enter();
|
self.init_role_and_formation();
|
||||||
}
|
}
|
||||||
|
|
||||||
// we need shorekeeper
|
|
||||||
// TODO: remove this part after implementing team switch
|
|
||||||
if !self.role_list.iter().any(|r| r.role_id == 1603) {
|
|
||||||
let mut camellya = Role::new(1603);
|
|
||||||
camellya.equip_weapon = 21020026;
|
|
||||||
self.role_list.push(camellya);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.formation_list.clear();
|
|
||||||
self.formation_list.push(RoleFormation {
|
|
||||||
id: 1,
|
|
||||||
cur_role: 1603,
|
|
||||||
role_id_set: HashSet::from([1603]),
|
|
||||||
is_current: true,
|
|
||||||
});
|
|
||||||
// End shorekeeper hardcode part
|
|
||||||
|
|
||||||
self.ensure_current_formation();
|
|
||||||
self.ensure_basic_unlock_func();
|
self.ensure_basic_unlock_func();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,24 +76,52 @@ impl Player {
|
||||||
self.notify(ItemPkgOpenNotify {
|
self.notify(ItemPkgOpenNotify {
|
||||||
open_pkg: (0..8).collect(),
|
open_pkg: (0..8).collect(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
self.notify(self.build_update_formation_notify());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_first_enter(&mut self) {
|
fn init_role_and_formation(&mut self) {
|
||||||
self.role_list.push(Self::create_main_character_role(
|
self.role_list.clear();
|
||||||
self.basic_info.name.clone(),
|
let mut role = match self.basic_info.sex {
|
||||||
self.basic_info.sex,
|
0 => Role::new(Role::MAIN_CHARACTER_FEMALE_ID),
|
||||||
));
|
1 => Role::new(Role::MAIN_CHARACTER_MALE_ID),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
let role = &self.role_list[0];
|
role.name = self.basic_info.name.clone();
|
||||||
|
|
||||||
self.formation_list.push(RoleFormation {
|
self.role_list.insert(role.role_id, role);
|
||||||
id: 1,
|
|
||||||
cur_role: role.role_id,
|
let required_role_ids: Vec<i32> = role_info_data::iter()
|
||||||
role_id_set: HashSet::from([role.role_id]),
|
.filter(|role_info| role_info.role_type == 1)
|
||||||
is_current: true,
|
.map(|role_info| role_info.id)
|
||||||
|
.collect();
|
||||||
|
let formation = vec![1603, 1504, 1505];
|
||||||
|
|
||||||
|
required_role_ids.iter().for_each(|&role_id| {
|
||||||
|
if !self.role_list.keys().any(|&k| k == role_id) {
|
||||||
|
self.role_list.insert(role_id, Role::new(role_id));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
self.location = PlayerLocation::default();
|
self.formation_list.insert(
|
||||||
|
1,
|
||||||
|
RoleFormation {
|
||||||
|
id: 1,
|
||||||
|
cur_role: *formation.iter().next().unwrap(),
|
||||||
|
role_ids: formation,
|
||||||
|
is_current: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.cur_formation_id = 1;
|
||||||
|
|
||||||
|
self.formation_list.values_mut().for_each(|formation| {
|
||||||
|
if formation.is_current && formation.id != 1 {
|
||||||
|
formation.is_current = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
self.ensure_current_formation();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure basic functionality is unlocked
|
// Ensure basic functionality is unlocked
|
||||||
|
@ -104,28 +132,38 @@ impl Player {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ensure_current_formation(&mut self) {
|
fn ensure_current_formation(&mut self) {
|
||||||
|
// If the list off formation is empty, add a default formation
|
||||||
if self.formation_list.is_empty() {
|
if self.formation_list.is_empty() {
|
||||||
let role = &self.role_list[0];
|
let mut role_list_clone = self.role_list.iter().clone();
|
||||||
|
|
||||||
self.formation_list.push(RoleFormation {
|
self.formation_list.insert(
|
||||||
id: 1,
|
1,
|
||||||
cur_role: role.role_id,
|
RoleFormation {
|
||||||
role_id_set: HashSet::from([role.role_id]),
|
id: 1,
|
||||||
is_current: true,
|
cur_role: role_list_clone.next().unwrap().1.role_id,
|
||||||
});
|
role_ids: role_list_clone
|
||||||
|
.take(3)
|
||||||
|
.map(|(&role_id, _)| role_id)
|
||||||
|
.collect(),
|
||||||
|
is_current: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.formation_list.iter().any(|rf| rf.is_current) {
|
// If there is no current formation, set the first formation as the current formation
|
||||||
self.formation_list[0].is_current = true;
|
if !self.formation_list.values().any(|rf| rf.is_current) {
|
||||||
|
self.formation_list.get_mut(&1).unwrap().is_current = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(rf) = self.formation_list.iter_mut().find(|rf| rf.is_current) {
|
// Ensure that the set of character IDs for the current formation is not empty and that the current character ID is in the set
|
||||||
if rf.role_id_set.is_empty() {
|
if let Some(rf) = self.formation_list.values_mut().find(|rf| rf.is_current) {
|
||||||
rf.role_id_set.insert(self.role_list[0].role_id);
|
if rf.role_ids.is_empty() {
|
||||||
|
rf.role_ids
|
||||||
|
.push(self.role_list.iter().next().unwrap().1.role_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !rf.role_id_set.contains(&rf.cur_role) {
|
if !rf.role_ids.contains(&rf.cur_role) {
|
||||||
rf.cur_role = *rf.role_id_set.iter().nth(0).unwrap();
|
rf.cur_role = *rf.role_ids.iter().nth(0).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,23 +178,105 @@ impl Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_current_formation_role_list(&self) -> Vec<&Role> {
|
pub fn build_player_entity_add_notify(
|
||||||
self.formation_list
|
&self,
|
||||||
.iter()
|
role_list: Vec<Role>,
|
||||||
.find(|rf| rf.is_current)
|
world: &mut WorldEntity,
|
||||||
.unwrap()
|
) -> EntityAddNotify {
|
||||||
.role_id_set
|
create_player_entity_pb!(
|
||||||
.iter()
|
role_list,
|
||||||
.flat_map(|id| self.role_list.iter().find(|r| r.role_id == *id))
|
self.basic_info.cur_map_id,
|
||||||
.collect()
|
world,
|
||||||
|
self.basic_info.id,
|
||||||
|
self.location.position.clone(),
|
||||||
|
self.explore_tools
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_cur_role_id(&self) -> i32 {
|
pub fn build_player_entity_remove_notify(
|
||||||
self.formation_list
|
&self,
|
||||||
.iter()
|
entities: Vec<i64>,
|
||||||
.find(|rf| rf.is_current)
|
remove_type: ERemoveEntityType,
|
||||||
.unwrap()
|
) -> EntityRemoveNotify {
|
||||||
.cur_role
|
EntityRemoveNotify {
|
||||||
|
remove_infos: entities
|
||||||
|
.iter()
|
||||||
|
.map(|&entity_id| EntityRemoveInfo {
|
||||||
|
entity_id,
|
||||||
|
r#type: remove_type.into(),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
is_remove: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_update_group_formation_notify(
|
||||||
|
&self,
|
||||||
|
cur_formation: RoleFormation,
|
||||||
|
world: &mut WorldEntity,
|
||||||
|
) -> UpdateGroupFormationNotify {
|
||||||
|
let group_type = 1;
|
||||||
|
UpdateGroupFormationNotify {
|
||||||
|
group_formation: vec![GroupFormation {
|
||||||
|
player_id: self.basic_info.id,
|
||||||
|
fight_role_infos: vec![FightRoleInfos {
|
||||||
|
group_type,
|
||||||
|
fight_role_infos: cur_formation
|
||||||
|
.role_ids
|
||||||
|
.iter()
|
||||||
|
.map(|&role_id| FightRoleInfo {
|
||||||
|
role_id,
|
||||||
|
entity_id: world.get_entity_id(role_id),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
cur_role: cur_formation.cur_role,
|
||||||
|
is_retain: false,
|
||||||
|
living_status: LivingStatus::Alive.into(),
|
||||||
|
is_fixed_location: false,
|
||||||
|
}],
|
||||||
|
current_group_type: group_type,
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_update_formation_notify(&self) -> UpdateFormationNotify {
|
||||||
|
let role_map: HashMap<i32, (i32, i32)> = self
|
||||||
|
.role_list
|
||||||
|
.values()
|
||||||
|
.map(|role| (role.role_id, (role.role_id, role.level)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
UpdateFormationNotify {
|
||||||
|
players_formations: vec![PlayerFightFormations {
|
||||||
|
player_id: self.basic_info.id,
|
||||||
|
formations: self
|
||||||
|
.formation_list
|
||||||
|
.iter()
|
||||||
|
.map(|(&formation_id, formation)| FightFormationNotifyInfo {
|
||||||
|
formation_id,
|
||||||
|
cur_role: formation.cur_role,
|
||||||
|
role_infos: formation
|
||||||
|
.role_ids
|
||||||
|
.iter()
|
||||||
|
.map(|role_id| {
|
||||||
|
if !role_map.contains_key(role_id) {
|
||||||
|
tracing::warn!("Role {} not found in use role list", role_id);
|
||||||
|
return Default::default();
|
||||||
|
}
|
||||||
|
let &(role_id, level) = role_map.get(&role_id).unwrap();
|
||||||
|
FormationRoleInfo {
|
||||||
|
role_id,
|
||||||
|
max_hp: 0,
|
||||||
|
cur_hp: 0,
|
||||||
|
level,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
is_current: formation.is_current,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_from_save(save_data: PlayerSaveData) -> Self {
|
pub fn load_from_save(save_data: PlayerSaveData) -> Self {
|
||||||
|
@ -164,17 +284,20 @@ impl Player {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
session: None,
|
session: None,
|
||||||
basic_info: PlayerBasicInfo::load_from_save(save_data.basic_data.unwrap_or_default()),
|
basic_info: PlayerBasicInfo::load_from_save(
|
||||||
|
save_data.basic_data.clone().unwrap_or_default(),
|
||||||
|
),
|
||||||
role_list: role_data
|
role_list: role_data
|
||||||
.role_list
|
.role_list
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(Role::load_from_save)
|
.map(Role::load_from_save)
|
||||||
.collect(),
|
.collect::<HashMap<i32, Role>>(),
|
||||||
formation_list: role_data
|
formation_list: role_data
|
||||||
.role_formation_list
|
.role_formation_list
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(RoleFormation::load_from_save)
|
.map(|(k, v)| (k, RoleFormation::load_from_save(v)))
|
||||||
.collect(),
|
.collect(),
|
||||||
|
cur_formation_id: role_data.cur_formation_id,
|
||||||
location: save_data
|
location: save_data
|
||||||
.location_data
|
.location_data
|
||||||
.map(PlayerLocation::load_from_save)
|
.map(PlayerLocation::load_from_save)
|
||||||
|
@ -196,12 +319,17 @@ impl Player {
|
||||||
PlayerSaveData {
|
PlayerSaveData {
|
||||||
basic_data: Some(self.basic_info.build_save_data()),
|
basic_data: Some(self.basic_info.build_save_data()),
|
||||||
role_data: Some(PlayerRoleData {
|
role_data: Some(PlayerRoleData {
|
||||||
role_list: self.role_list.iter().map(|r| r.build_save_data()).collect(),
|
role_list: self
|
||||||
|
.role_list
|
||||||
|
.iter()
|
||||||
|
.map(|(_, role)| role.build_save_data())
|
||||||
|
.collect(),
|
||||||
role_formation_list: self
|
role_formation_list: self
|
||||||
.formation_list
|
.formation_list
|
||||||
.iter()
|
.iter()
|
||||||
.map(|rf| rf.build_save_data())
|
.map(|(&k, v)| (k, v.build_save_data()))
|
||||||
.collect(),
|
.collect(),
|
||||||
|
cur_formation_id: self.cur_formation_id,
|
||||||
}),
|
}),
|
||||||
location_data: Some(self.location.build_save_data()),
|
location_data: Some(self.location.build_save_data()),
|
||||||
func_data: Some(self.func.build_save_data()),
|
func_data: Some(self.func.build_save_data()),
|
||||||
|
@ -215,7 +343,11 @@ impl Player {
|
||||||
|
|
||||||
pub fn build_role_list_notify(&self) -> PbGetRoleListNotify {
|
pub fn build_role_list_notify(&self) -> PbGetRoleListNotify {
|
||||||
PbGetRoleListNotify {
|
PbGetRoleListNotify {
|
||||||
role_list: self.role_list.iter().map(|r| r.to_protobuf()).collect(),
|
role_list: self
|
||||||
|
.role_list
|
||||||
|
.iter()
|
||||||
|
.map(|(_, role)| role.to_protobuf())
|
||||||
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,18 +372,13 @@ impl Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_main_character_role(name: String, sex: i32) -> Role {
|
pub fn create_default_save_data(id: i32, name: String, sex: i32) -> PlayerSaveData {
|
||||||
let mut role = match sex {
|
let role_id = match sex {
|
||||||
0 => Role::new(Role::MAIN_CHARACTER_FEMALE_ID),
|
0 => Role::MAIN_CHARACTER_FEMALE_ID, // 1502
|
||||||
1 => Role::new(Role::MAIN_CHARACTER_MALE_ID),
|
1 => Role::MAIN_CHARACTER_MALE_ID, // 1501
|
||||||
_ => unreachable!(),
|
_ => Role::MAIN_CHARACTER_MALE_ID, // Default to male
|
||||||
};
|
};
|
||||||
|
|
||||||
role.name = name;
|
|
||||||
role
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_default_save_data(id: i32, name: String, sex: i32) -> PlayerSaveData {
|
|
||||||
PlayerSaveData {
|
PlayerSaveData {
|
||||||
basic_data: Some(PlayerBasicData {
|
basic_data: Some(PlayerBasicData {
|
||||||
id,
|
id,
|
||||||
|
@ -260,6 +387,8 @@ impl Player {
|
||||||
level: 1,
|
level: 1,
|
||||||
head_photo: 1603,
|
head_photo: 1603,
|
||||||
head_frame: 80060009,
|
head_frame: 80060009,
|
||||||
|
cur_map_id: 8,
|
||||||
|
role_show_list: vec![role_id],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
use shorekeeper_protocol::RoleFormationData;
|
use shorekeeper_protocol::RoleFormationData;
|
||||||
|
|
||||||
pub struct RoleFormation {
|
pub struct RoleFormation {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub cur_role: i32,
|
pub cur_role: i32,
|
||||||
pub role_id_set: HashSet<i32>,
|
pub role_ids: Vec<i32>,
|
||||||
pub is_current: bool,
|
pub is_current: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +12,7 @@ impl RoleFormation {
|
||||||
Self {
|
Self {
|
||||||
id: data.formation_id,
|
id: data.formation_id,
|
||||||
cur_role: data.cur_role,
|
cur_role: data.cur_role,
|
||||||
role_id_set: data.role_id_list.into_iter().collect(),
|
role_ids: data.role_id_list,
|
||||||
is_current: data.is_current,
|
is_current: data.is_current,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +21,7 @@ impl RoleFormation {
|
||||||
RoleFormationData {
|
RoleFormationData {
|
||||||
formation_id: self.id,
|
formation_id: self.id,
|
||||||
cur_role: self.cur_role,
|
cur_role: self.cur_role,
|
||||||
role_id_list: self.role_id_set.iter().cloned().collect(),
|
role_id_list: self.role_ids.iter().map(|&role_id| role_id).collect(),
|
||||||
is_current: self.is_current,
|
is_current: self.is_current,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::logic::utils::load_role_info::load_key_value;
|
||||||
use common::time_util;
|
use common::time_util;
|
||||||
pub use formation::RoleFormation;
|
pub use formation::RoleFormation;
|
||||||
use shorekeeper_data::role_info_data;
|
use shorekeeper_data::{base_property_data, role_info_data};
|
||||||
use shorekeeper_protocol::{ArrayIntInt, RoleData, RoleInfo};
|
use shorekeeper_protocol::{ArrayIntInt, RoleData, RoleInfo};
|
||||||
|
|
||||||
mod formation;
|
mod formation;
|
||||||
|
@ -41,6 +42,12 @@ impl Role {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_protobuf(&self) -> RoleInfo {
|
pub fn to_protobuf(&self) -> RoleInfo {
|
||||||
|
let base_prop: HashMap<i32, i32> = load_key_value(
|
||||||
|
base_property_data::iter()
|
||||||
|
.find(|d| d.id == self.role_id)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
RoleInfo {
|
RoleInfo {
|
||||||
role_id: self.role_id,
|
role_id: self.role_id,
|
||||||
name: self.name.clone(),
|
name: self.name.clone(),
|
||||||
|
@ -55,23 +62,30 @@ impl Role {
|
||||||
.collect(),
|
.collect(),
|
||||||
star: self.star,
|
star: self.star,
|
||||||
favor: self.favor,
|
favor: self.favor,
|
||||||
|
base_prop: base_prop
|
||||||
|
.iter()
|
||||||
|
.map(|(&k, &v)| ArrayIntInt { key: k, value: v })
|
||||||
|
.collect(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_from_save(data: RoleData) -> Self {
|
pub fn load_from_save(data: RoleData) -> (i32, Self) {
|
||||||
Self {
|
(
|
||||||
role_id: data.role_id,
|
data.role_id,
|
||||||
name: data.name,
|
Self {
|
||||||
level: data.level,
|
role_id: data.role_id,
|
||||||
exp: data.exp,
|
name: data.name,
|
||||||
breakthrough: data.breakthrough,
|
level: data.level,
|
||||||
skill_map: data.skill_map,
|
exp: data.exp,
|
||||||
star: data.star,
|
breakthrough: data.breakthrough,
|
||||||
favor: data.favor,
|
skill_map: data.skill_map,
|
||||||
create_time: data.create_time,
|
star: data.star,
|
||||||
equip_weapon: data.equip_weapon,
|
favor: data.favor,
|
||||||
}
|
create_time: data.create_time,
|
||||||
|
equip_weapon: data.equip_weapon,
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_save_data(&self) -> RoleData {
|
pub fn build_save_data(&self) -> RoleData {
|
||||||
|
|
|
@ -15,8 +15,9 @@ pub(super) struct MovementSystem;
|
||||||
impl System for MovementSystem {
|
impl System for MovementSystem {
|
||||||
fn tick(&self, world: &mut World, players: &mut [RefMut<Player>]) {
|
fn tick(&self, world: &mut World, players: &mut [RefMut<Player>]) {
|
||||||
let mut notify = MovePackageNotify::default();
|
let mut notify = MovePackageNotify::default();
|
||||||
|
let world_entity = world.get_world_entity();
|
||||||
|
|
||||||
for (entity, mut movement, mut position) in query_with!(world, Movement, Position) {
|
for (entity, mut movement, mut position) in query_with!(world_entity, Movement, Position) {
|
||||||
if movement.pending_movement_vec.is_empty() {
|
if movement.pending_movement_vec.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -46,9 +47,12 @@ impl System for MovementSystem {
|
||||||
|
|
||||||
notify.moving_entities.push(moving_entity_data);
|
notify.moving_entities.push(moving_entity_data);
|
||||||
|
|
||||||
if let (Some(_), Some(owner)) =
|
if let (Some(_), Some(owner)) = query_components!(
|
||||||
query_components!(world, i64::from(entity), PlayerEntityMarker, OwnerPlayer)
|
world_entity,
|
||||||
{
|
i64::from(entity),
|
||||||
|
PlayerEntityMarker,
|
||||||
|
OwnerPlayer
|
||||||
|
) {
|
||||||
if let Some(player) = players.iter_mut().find(|pl| pl.basic_info.id == owner.0) {
|
if let Some(player) = players.iter_mut().find(|pl| pl.basic_info.id == owner.0) {
|
||||||
player.location.position = position.0.clone();
|
player.location.position = position.0.clone();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
use common::time_util;
|
||||||
|
use shorekeeper_protocol::PlayerSaveData;
|
||||||
|
use shorekeeper_protocol::{
|
||||||
|
message::Message, AfterJoinSceneNotify, EnterGameResponse, JoinSceneNotify, JsPatchNotify,
|
||||||
|
TransitionOptionPb,
|
||||||
|
};
|
||||||
|
use std::collections::hash_map::Entry::Vacant;
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
collections::{HashMap, VecDeque},
|
collections::{HashMap, VecDeque},
|
||||||
|
@ -10,20 +17,13 @@ use std::{
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use common::time_util;
|
use super::{ecs::world::World, player::Player, utils::world_util};
|
||||||
use shorekeeper_protocol::PlayerSaveData;
|
use crate::logic::ecs::world::WorldEntity;
|
||||||
use shorekeeper_protocol::{
|
|
||||||
message::Message, AfterJoinSceneNotify, EnterGameResponse, JoinSceneNotify, JsPatchNotify,
|
|
||||||
TransitionOptionPb,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
player_save_task::{self, PlayerSaveReason},
|
player_save_task::{self, PlayerSaveReason},
|
||||||
session::Session,
|
session::Session,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{ecs::world::World, player::Player, utils::world_util};
|
|
||||||
|
|
||||||
const WATER_MASK: &str = include_str!("../../watermask-rr.js");
|
const WATER_MASK: &str = include_str!("../../watermask-rr.js");
|
||||||
const UID_FIX: &str = include_str!("../../uidfix.js");
|
const UID_FIX: &str = include_str!("../../uidfix.js");
|
||||||
const CENSORSHIP_FIX: &str = include_str!("../../censorshipfix.js");
|
const CENSORSHIP_FIX: &str = include_str!("../../censorshipfix.js");
|
||||||
|
@ -141,13 +141,29 @@ fn handle_logic_input(state: &mut LogicState, input: LogicInput) {
|
||||||
session,
|
session,
|
||||||
player_save_data,
|
player_save_data,
|
||||||
} => {
|
} => {
|
||||||
let player = state
|
let (player, is_player) = if let Vacant(e) = state.players.entry(player_id) {
|
||||||
.players
|
(
|
||||||
.entry(player_id)
|
e.insert(RefCell::new(Player::load_from_save(player_save_data))),
|
||||||
.or_insert(RefCell::new(Player::load_from_save(player_save_data)));
|
true,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
if let Some(player) = state.players.get_mut(&player_id) {
|
||||||
|
(player, false)
|
||||||
|
} else {
|
||||||
|
tracing::warn!("logic_thread: get player requested, but player {player_id} with data doesn't exist");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let mut player = player.borrow_mut();
|
let mut player = player.borrow_mut();
|
||||||
state.worlds.insert(player_id, player.world.clone());
|
if is_player {
|
||||||
|
player
|
||||||
|
.world
|
||||||
|
.borrow_mut()
|
||||||
|
.world_entitys
|
||||||
|
.insert(player.basic_info.cur_map_id, WorldEntity::default());
|
||||||
|
state.worlds.insert(player_id, player.world.clone());
|
||||||
|
}
|
||||||
|
|
||||||
player.init();
|
player.init();
|
||||||
player.set_session(session);
|
player.set_session(session);
|
||||||
|
@ -159,16 +175,12 @@ fn handle_logic_input(state: &mut LogicState, input: LogicInput) {
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.set_in_world_player_data(player.build_in_world_player());
|
.set_in_world_player_data(player.build_in_world_player());
|
||||||
|
|
||||||
world_util::add_player_entities(&mut player.world.borrow_mut(), &player);
|
world_util::add_player_entities(&player);
|
||||||
let scene_info = world_util::build_scene_information(
|
let scene_info = world_util::build_scene_information(&player);
|
||||||
&player.world.borrow(),
|
|
||||||
player.location.instance_id,
|
|
||||||
player.basic_info.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
player.notify(JoinSceneNotify {
|
player.notify(JoinSceneNotify {
|
||||||
max_entity_id: i64::MAX,
|
|
||||||
scene_info: Some(scene_info),
|
scene_info: Some(scene_info),
|
||||||
|
max_entity_id: i64::MAX,
|
||||||
transition_option: Some(TransitionOptionPb::default()),
|
transition_option: Some(TransitionOptionPb::default()),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,75 @@
|
||||||
use crate::logic::ecs::component::ComponentContainer;
|
use crate::logic::ecs::component::ComponentContainer;
|
||||||
use shorekeeper_protocol::{EntityPb, PlayerSceneAoiData};
|
use shorekeeper_protocol::{EEntityType, EntityPb, PlayerSceneAoiData};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use crate::{logic::ecs::world::World, query_with};
|
use crate::logic::components::Visibility;
|
||||||
|
use crate::logic::player::Player;
|
||||||
|
use crate::{modify_component, query_hn_with};
|
||||||
|
|
||||||
pub fn build_scene_add_on_init_data(world: &World) -> PlayerSceneAoiData {
|
pub fn build_scene_add_on_init_data(player: &Player) -> PlayerSceneAoiData {
|
||||||
let entities = query_with!(world, PlayerEntityMarker)
|
let mut world_ref = player.world.borrow_mut();
|
||||||
|
let world = world_ref.get_mut_world_entity();
|
||||||
|
|
||||||
|
let entities = query_hn_with!(world, PlayerEntityMarker)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(e, _)| e)
|
.map(|(entity_id, _)| {
|
||||||
.collect::<Vec<_>>();
|
let res_map: (EEntityType, i32);
|
||||||
|
match EEntityType::try_from(
|
||||||
|
world.get_entity(world.get_config_id(entity_id)).entity_type,
|
||||||
|
) {
|
||||||
|
Ok(EEntityType::Player) => {
|
||||||
|
res_map = (EEntityType::Player, entity_id);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
res_map = (EEntityType::default(), -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res_map
|
||||||
|
})
|
||||||
|
.collect::<HashSet<(EEntityType, i32)>>();
|
||||||
|
|
||||||
let mut aoi_data = PlayerSceneAoiData::default();
|
let mut aoi_data = PlayerSceneAoiData::default();
|
||||||
for entity in entities {
|
|
||||||
let mut pb = EntityPb { id: entity.into(), ..Default::default() };
|
|
||||||
|
|
||||||
world
|
entities
|
||||||
.get_entity_components(entity)
|
.iter()
|
||||||
.into_iter()
|
.filter(|&&(_, entity_id)| entity_id != -1)
|
||||||
.for_each(|comp| comp.set_pb_data(&mut pb));
|
.for_each(|&(entity_type, entity_id)| {
|
||||||
|
match entity_type {
|
||||||
|
EEntityType::Player => {
|
||||||
|
let config_id = world.get_config_id(entity_id);
|
||||||
|
modify_component!(
|
||||||
|
world.get_entity_components(entity_id),
|
||||||
|
Visibility,
|
||||||
|
|vis: &mut Visibility| {
|
||||||
|
let cur_role_id = player
|
||||||
|
.formation_list
|
||||||
|
.get(&player.cur_formation_id)
|
||||||
|
.unwrap()
|
||||||
|
.cur_role;
|
||||||
|
vis.0 = if config_id == cur_role_id {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
aoi_data.entities.push(pb);
|
if world.get_entity(config_id).entity_type == EEntityType::Player as i32 {
|
||||||
}
|
let mut pb = EntityPb {
|
||||||
|
id: entity_id as i64,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
world
|
||||||
|
.get_entity_components(entity_id)
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|comp| comp.set_pb_data(&mut pb));
|
||||||
|
|
||||||
|
aoi_data.entities.push(pb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
aoi_data
|
aoi_data
|
||||||
}
|
}
|
||||||
|
|
158
game-server/src/logic/utils/load_role_info.rs
Normal file
158
game-server/src/logic/utils/load_role_info.rs
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
use shorekeeper_data::BasePropertyData;
|
||||||
|
use shorekeeper_protocol::EAttributeType;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! impl_base_prop {
|
||||||
|
($($name:ident),*) => {
|
||||||
|
pub fn load_key_value(base_property: &BasePropertyData) -> HashMap<i32, i32> {
|
||||||
|
HashMap::from([$(
|
||||||
|
::paste::paste!((EAttributeType::[<$name:camel>] as i32, base_property.$name as i32)),
|
||||||
|
)*])
|
||||||
|
}
|
||||||
|
pub fn attribute_from_data(base_property: &BasePropertyData) -> HashMap<EAttributeType, (i32, i32)> {
|
||||||
|
HashMap::from([$(
|
||||||
|
::paste::paste!((EAttributeType::[<$name:camel>], (base_property.$name, 0))),
|
||||||
|
)*])
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_base_prop!(
|
||||||
|
lv,
|
||||||
|
life_max,
|
||||||
|
life,
|
||||||
|
sheild,
|
||||||
|
sheild_damage_change,
|
||||||
|
sheild_damage_reduce,
|
||||||
|
atk,
|
||||||
|
crit,
|
||||||
|
crit_damage,
|
||||||
|
def,
|
||||||
|
energy_efficiency,
|
||||||
|
cd_reduse,
|
||||||
|
element_efficiency,
|
||||||
|
damage_change_normal_skill,
|
||||||
|
damage_change,
|
||||||
|
damage_reduce,
|
||||||
|
damage_change_auto,
|
||||||
|
damage_change_cast,
|
||||||
|
damage_change_ultra,
|
||||||
|
damage_change_qte,
|
||||||
|
damage_change_phys,
|
||||||
|
damage_change_element1,
|
||||||
|
damage_change_element2,
|
||||||
|
damage_change_element3,
|
||||||
|
damage_change_element4,
|
||||||
|
damage_change_element5,
|
||||||
|
damage_change_element6,
|
||||||
|
damage_resistance_phys,
|
||||||
|
damage_resistance_element1,
|
||||||
|
damage_resistance_element2,
|
||||||
|
damage_resistance_element3,
|
||||||
|
damage_resistance_element4,
|
||||||
|
damage_resistance_element5,
|
||||||
|
damage_resistance_element6,
|
||||||
|
heal_change,
|
||||||
|
healed_change,
|
||||||
|
damage_reduce_phys,
|
||||||
|
damage_reduce_element1,
|
||||||
|
damage_reduce_element2,
|
||||||
|
damage_reduce_element3,
|
||||||
|
damage_reduce_element4,
|
||||||
|
damage_reduce_element5,
|
||||||
|
damage_reduce_element6,
|
||||||
|
reaction_change1,
|
||||||
|
reaction_change2,
|
||||||
|
reaction_change3,
|
||||||
|
reaction_change4,
|
||||||
|
reaction_change5,
|
||||||
|
reaction_change6,
|
||||||
|
reaction_change7,
|
||||||
|
reaction_change8,
|
||||||
|
reaction_change9,
|
||||||
|
reaction_change10,
|
||||||
|
reaction_change11,
|
||||||
|
reaction_change12,
|
||||||
|
reaction_change13,
|
||||||
|
reaction_change14,
|
||||||
|
reaction_change15,
|
||||||
|
energy_max,
|
||||||
|
energy,
|
||||||
|
special_energy_1_max,
|
||||||
|
special_energy_1,
|
||||||
|
special_energy_2_max,
|
||||||
|
special_energy_2,
|
||||||
|
special_energy_3_max,
|
||||||
|
special_energy_3,
|
||||||
|
special_energy_4_max,
|
||||||
|
special_energy_4,
|
||||||
|
strength_max,
|
||||||
|
strength,
|
||||||
|
strength_recover,
|
||||||
|
strength_punish_time,
|
||||||
|
strength_run,
|
||||||
|
strength_swim,
|
||||||
|
strength_fast_swim,
|
||||||
|
hardness_max,
|
||||||
|
hardness,
|
||||||
|
hardness_recover,
|
||||||
|
hardness_punish_time,
|
||||||
|
hardness_change,
|
||||||
|
hardness_reduce,
|
||||||
|
rage_max,
|
||||||
|
rage,
|
||||||
|
rage_recover,
|
||||||
|
rage_punish_time,
|
||||||
|
rage_change,
|
||||||
|
rage_reduce,
|
||||||
|
tough_max,
|
||||||
|
tough,
|
||||||
|
tough_recover,
|
||||||
|
tough_change,
|
||||||
|
tough_reduce,
|
||||||
|
tough_recover_delay_time,
|
||||||
|
element_power1,
|
||||||
|
element_power2,
|
||||||
|
element_power3,
|
||||||
|
element_power4,
|
||||||
|
element_power5,
|
||||||
|
element_power6,
|
||||||
|
special_damage_change,
|
||||||
|
strength_fast_climb_cost,
|
||||||
|
element_property_type,
|
||||||
|
weak_time,
|
||||||
|
ignore_def_rate,
|
||||||
|
ignore_damage_resistance_phys,
|
||||||
|
ignore_damage_resistance_element1,
|
||||||
|
ignore_damage_resistance_element2,
|
||||||
|
ignore_damage_resistance_element3,
|
||||||
|
ignore_damage_resistance_element4,
|
||||||
|
ignore_damage_resistance_element5,
|
||||||
|
ignore_damage_resistance_element6,
|
||||||
|
skill_tough_ratio,
|
||||||
|
strength_climb_jump,
|
||||||
|
strength_gliding,
|
||||||
|
mass,
|
||||||
|
braking_friction_factor,
|
||||||
|
gravity_scale,
|
||||||
|
speed_ratio,
|
||||||
|
damage_change_phantom,
|
||||||
|
auto_attack_speed,
|
||||||
|
cast_attack_speed,
|
||||||
|
status_build_up_1_max,
|
||||||
|
status_build_up_1,
|
||||||
|
status_build_up_2_max,
|
||||||
|
status_build_up_2,
|
||||||
|
status_build_up_3_max,
|
||||||
|
status_build_up_3,
|
||||||
|
status_build_up_4_max,
|
||||||
|
status_build_up_4,
|
||||||
|
status_build_up_5_max,
|
||||||
|
status_build_up_5,
|
||||||
|
paralysis_time_max,
|
||||||
|
paralysis_time,
|
||||||
|
paralysis_time_recover,
|
||||||
|
element_energy_max,
|
||||||
|
element_energy
|
||||||
|
);
|
|
@ -1,2 +1,3 @@
|
||||||
pub mod entity_serializer;
|
pub mod entity_serializer;
|
||||||
|
pub mod load_role_info;
|
||||||
pub mod world_util;
|
pub mod world_util;
|
||||||
|
|
|
@ -1,82 +1,151 @@
|
||||||
|
use crate::logic::ecs::world::World;
|
||||||
|
use crate::logic::player::Player;
|
||||||
|
use crate::logic::utils::entity_serializer;
|
||||||
|
use crate::logic::{
|
||||||
|
components::{
|
||||||
|
Attribute, EntityConfig, Equip, Movement, OwnerPlayer, PlayerEntityMarker, Position,
|
||||||
|
Visibility, VisionSkill,
|
||||||
|
},
|
||||||
|
ecs::component::ComponentContainer,
|
||||||
|
};
|
||||||
|
use crate::query_with;
|
||||||
use shorekeeper_data::base_property_data;
|
use shorekeeper_data::base_property_data;
|
||||||
use shorekeeper_protocol::{
|
use shorekeeper_protocol::{
|
||||||
EntityConfigType, FightRoleInfo, FightRoleInfos, LivingStatus, SceneInformation, SceneMode,
|
EEntityType, EntityConfigType, FightRoleInfo, FightRoleInfos, LivingStatus, SceneInformation,
|
||||||
ScenePlayerInformation, SceneTimeInfo,
|
SceneMode, ScenePlayerInformation, SceneTimeInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::entity_serializer;
|
#[macro_export]
|
||||||
use crate::{
|
macro_rules! create_player_entity_pb {
|
||||||
logic::{
|
($role_list:expr, $cur_map_id:expr, $world:expr, $player_id:expr, $position:expr, $explore_tools:expr) => {{
|
||||||
components::{
|
let mut pbs = Vec::new();
|
||||||
Attribute, EntityConfig, Equip, Movement, OwnerPlayer, PlayerEntityMarker, Position,
|
|
||||||
Visibility, VisionSkill,
|
|
||||||
},
|
|
||||||
ecs::{component::ComponentContainer, world::World},
|
|
||||||
player::Player,
|
|
||||||
},
|
|
||||||
query_with,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn add_player_entities(world: &mut World, player: &Player) {
|
for role in $role_list {
|
||||||
let cur_role_id = player.get_cur_role_id();
|
let role_id: i32 = role.role_id;
|
||||||
|
let base_property = base_property_data::iter()
|
||||||
|
.find(|d| d.id == role_id)
|
||||||
|
.expect("macro create_role_entity_pb: Base property data not found");
|
||||||
|
|
||||||
for role in player.get_current_formation_role_list() {
|
let entity = $world
|
||||||
let id = world
|
.create_entity(role_id, EEntityType::Player.into(), $cur_map_id)
|
||||||
.create_entity()
|
.with(ComponentContainer::PlayerEntityMarker(PlayerEntityMarker))
|
||||||
.with(ComponentContainer::PlayerEntityMarker(PlayerEntityMarker))
|
.with(ComponentContainer::EntityConfig(EntityConfig {
|
||||||
.with(ComponentContainer::EntityConfig(EntityConfig {
|
config_id: role_id,
|
||||||
config_id: role.role_id,
|
config_type: EntityConfigType::Character,
|
||||||
config_type: EntityConfigType::Character,
|
}))
|
||||||
}))
|
.with(ComponentContainer::OwnerPlayer(OwnerPlayer($player_id)))
|
||||||
.with(ComponentContainer::OwnerPlayer(OwnerPlayer(
|
.with(ComponentContainer::Position(Position($position)))
|
||||||
player.basic_info.id,
|
.with(ComponentContainer::Visibility(Visibility(
|
||||||
)))
|
role_id == role_id,
|
||||||
.with(ComponentContainer::Position(Position(
|
)))
|
||||||
player.location.position.clone(),
|
.with(ComponentContainer::Attribute(Attribute::from_data(
|
||||||
)))
|
base_property,
|
||||||
.with(ComponentContainer::Visibility(Visibility(
|
)))
|
||||||
role.role_id == cur_role_id,
|
.with(ComponentContainer::Movement(Movement::default()))
|
||||||
)))
|
.with(ComponentContainer::Equip(Equip {
|
||||||
.with(ComponentContainer::Attribute(Attribute::from_data(
|
weapon_id: role.equip_weapon,
|
||||||
base_property_data::iter()
|
weapon_breach_level: 90, // TODO: store this too
|
||||||
.find(|d| d.id == role.role_id)
|
}))
|
||||||
.unwrap(),
|
.with(ComponentContainer::VisionSkill(VisionSkill {
|
||||||
)))
|
skill_id: $explore_tools.active_explore_skill,
|
||||||
.with(ComponentContainer::Movement(Movement::default()))
|
}))
|
||||||
.with(ComponentContainer::Equip(Equip {
|
.build();
|
||||||
weapon_id: role.equip_weapon,
|
|
||||||
weapon_breach_level: 0, // TODO: store this too
|
|
||||||
}))
|
|
||||||
.with(ComponentContainer::VisionSkill(VisionSkill {
|
|
||||||
skill_id: player.explore_tools.active_explore_skill,
|
|
||||||
}))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
tracing::debug!(
|
let mut pb = EntityPb {
|
||||||
"created player entity, id: {}, role_id: {}",
|
id: entity.entity_id as i64,
|
||||||
i64::from(id),
|
..Default::default()
|
||||||
role.role_id
|
};
|
||||||
);
|
|
||||||
|
$world
|
||||||
|
.get_entity_components(entity.entity_id)
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|comp| comp.set_pb_data(&mut pb));
|
||||||
|
pbs.push(pb);
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityAddNotify {
|
||||||
|
entity_pbs: pbs,
|
||||||
|
is_add: true,
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_player_entities(player: &Player) {
|
||||||
|
let mut world_ref = player.world.borrow_mut();
|
||||||
|
let world = world_ref.get_mut_world_entity();
|
||||||
|
|
||||||
|
let current_formation = player.formation_list.get(&player.cur_formation_id).unwrap();
|
||||||
|
|
||||||
|
let role_vec = current_formation
|
||||||
|
.role_ids
|
||||||
|
.iter()
|
||||||
|
.map(|role_id| player.role_list.get(&role_id).unwrap())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let cur_role_id = current_formation.cur_role;
|
||||||
|
|
||||||
|
if world.active_entity_empty() {
|
||||||
|
for role in role_vec {
|
||||||
|
let entity = world
|
||||||
|
.create_entity(
|
||||||
|
role.role_id,
|
||||||
|
EEntityType::Player.into(),
|
||||||
|
player.basic_info.cur_map_id,
|
||||||
|
)
|
||||||
|
.with(ComponentContainer::PlayerEntityMarker(PlayerEntityMarker))
|
||||||
|
.with(ComponentContainer::EntityConfig(EntityConfig {
|
||||||
|
config_id: role.role_id,
|
||||||
|
config_type: EntityConfigType::Character,
|
||||||
|
}))
|
||||||
|
.with(ComponentContainer::OwnerPlayer(OwnerPlayer(
|
||||||
|
player.basic_info.id,
|
||||||
|
)))
|
||||||
|
.with(ComponentContainer::Position(Position(
|
||||||
|
player.location.position.clone(),
|
||||||
|
)))
|
||||||
|
.with(ComponentContainer::Visibility(Visibility(
|
||||||
|
role.role_id == cur_role_id,
|
||||||
|
)))
|
||||||
|
.with(ComponentContainer::Attribute(Attribute::from_data(
|
||||||
|
base_property_data::iter()
|
||||||
|
.find(|d| d.id == role.role_id)
|
||||||
|
.unwrap(),
|
||||||
|
)))
|
||||||
|
.with(ComponentContainer::Movement(Movement::default()))
|
||||||
|
.with(ComponentContainer::Equip(Equip {
|
||||||
|
weapon_id: role.equip_weapon,
|
||||||
|
weapon_breach_level: 0, // TODO: store this too
|
||||||
|
}))
|
||||||
|
.with(ComponentContainer::VisionSkill(VisionSkill {
|
||||||
|
skill_id: player.explore_tools.active_explore_skill,
|
||||||
|
}))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
tracing::debug!(
|
||||||
|
"created player entity, id: {}, role_id: {}",
|
||||||
|
entity.entity_id,
|
||||||
|
role.role_id
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_scene_information(world: &World, instance_id: i32, owner_id: i32) -> SceneInformation {
|
pub fn build_scene_information(player: &Player) -> SceneInformation {
|
||||||
SceneInformation {
|
SceneInformation {
|
||||||
scene_id: String::new(),
|
scene_id: String::new(),
|
||||||
instance_id,
|
instance_id: player.location.instance_id,
|
||||||
owner_id,
|
owner_id: player.basic_info.id,
|
||||||
dynamic_entity_list: Vec::new(),
|
dynamic_entity_list: Vec::new(),
|
||||||
blackboard_params: Vec::new(),
|
blackboard_params: Vec::new(),
|
||||||
end_time: 0,
|
end_time: 0,
|
||||||
aoi_data: Some(entity_serializer::build_scene_add_on_init_data(world)),
|
aoi_data: Some(entity_serializer::build_scene_add_on_init_data(player)),
|
||||||
player_infos: build_player_info_list(world),
|
player_infos: build_player_info_list(&player.world.borrow_mut()),
|
||||||
mode: SceneMode::Single.into(),
|
mode: SceneMode::Single.into(),
|
||||||
time_info: Some(SceneTimeInfo {
|
time_info: Some(SceneTimeInfo {
|
||||||
owner_time_clock_time_span: 0,
|
owner_time_clock_time_span: 0,
|
||||||
hour: 8,
|
hour: 8,
|
||||||
minute: 0,
|
minute: 0,
|
||||||
}),
|
}),
|
||||||
cur_context_id: owner_id as i64,
|
cur_context_id: player.basic_info.id as i64,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,24 +154,33 @@ fn build_player_info_list(world: &World) -> Vec<ScenePlayerInformation> {
|
||||||
world
|
world
|
||||||
.players()
|
.players()
|
||||||
.map(|sp| {
|
.map(|sp| {
|
||||||
let (cur_role_id, transform) = query_with!(
|
let (cur_role_id, transform, _equip) = query_with!(
|
||||||
world,
|
world.get_world_entity(),
|
||||||
PlayerEntityMarker,
|
PlayerEntityMarker,
|
||||||
OwnerPlayer,
|
OwnerPlayer,
|
||||||
Visibility,
|
Visibility,
|
||||||
EntityConfig,
|
EntityConfig,
|
||||||
Position
|
Position,
|
||||||
|
Equip
|
||||||
)
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.find_map(|(_, _, owner, visibility, conf, pos)| {
|
.find_map(|(_, _, owner, visibility, conf, pos, equip)| {
|
||||||
(sp.player_id == owner.0 && visibility.0).then_some((conf.config_id, pos.0.clone()))
|
(sp.player_id == owner.0 && visibility.0).then_some((
|
||||||
|
conf.config_id,
|
||||||
|
pos.0.clone(),
|
||||||
|
equip.weapon_id,
|
||||||
|
))
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let active_characters =
|
let active_characters = query_with!(
|
||||||
query_with!(world, PlayerEntityMarker, OwnerPlayer, EntityConfig)
|
world.get_world_entity(),
|
||||||
.into_iter()
|
PlayerEntityMarker,
|
||||||
.filter(|(_, _, owner, _)| owner.0 == sp.player_id);
|
OwnerPlayer,
|
||||||
|
EntityConfig
|
||||||
|
)
|
||||||
|
.into_iter()
|
||||||
|
.filter(|(_, _, owner, _)| owner.0 == sp.player_id);
|
||||||
|
|
||||||
ScenePlayerInformation {
|
ScenePlayerInformation {
|
||||||
cur_role: cur_role_id,
|
cur_role: cur_role_id,
|
||||||
|
|
|
@ -2,81 +2,84 @@ syntax = "proto3";
|
||||||
package data;
|
package data;
|
||||||
|
|
||||||
message VectorData {
|
message VectorData {
|
||||||
float x = 1;
|
float x = 1;
|
||||||
float y = 2;
|
float y = 2;
|
||||||
float z = 3;
|
float z = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message TransformData {
|
message TransformData {
|
||||||
VectorData position = 1;
|
VectorData position = 1;
|
||||||
VectorData rotation = 2;
|
VectorData rotation = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message PlayerBasicData {
|
message PlayerBasicData {
|
||||||
int32 id = 1;
|
int32 id = 1;
|
||||||
string name = 2;
|
string name = 2;
|
||||||
int32 sex = 3;
|
int32 sex = 3;
|
||||||
int32 level = 4;
|
int32 level = 4;
|
||||||
int32 exp = 5;
|
int32 exp = 5;
|
||||||
int32 head_photo = 6;
|
int32 head_photo = 6;
|
||||||
int32 head_frame = 7;
|
int32 head_frame = 7;
|
||||||
|
int32 cur_map_id = 8;
|
||||||
|
repeated int32 role_show_list = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
message RoleSkillNodeData {
|
message RoleSkillNodeData {
|
||||||
int32 node_id = 1;
|
int32 node_id = 1;
|
||||||
bool is_active = 2;
|
bool is_active = 2;
|
||||||
int32 skill_id = 3;
|
int32 skill_id = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message RoleData {
|
message RoleData {
|
||||||
int32 role_id = 1;
|
int32 role_id = 1;
|
||||||
string name = 2;
|
string name = 2;
|
||||||
int32 level = 3;
|
int32 level = 3;
|
||||||
int32 exp = 4;
|
int32 exp = 4;
|
||||||
int32 breakthrough = 5;
|
int32 breakthrough = 5;
|
||||||
map<int32, int32> skill_map = 6;
|
map<int32, int32> skill_map = 6;
|
||||||
map<int32, int32> phantom_map = 7;
|
map<int32, int32> phantom_map = 7;
|
||||||
int32 star = 8;
|
int32 star = 8;
|
||||||
int32 favor = 9;
|
int32 favor = 9;
|
||||||
uint32 create_time = 10;
|
uint32 create_time = 10;
|
||||||
int32 cur_model = 11;
|
int32 cur_model = 11;
|
||||||
repeated int32 models = 12;
|
repeated int32 models = 12;
|
||||||
repeated RoleSkillNodeData skill_node_state = 13;
|
repeated RoleSkillNodeData skill_node_state = 13;
|
||||||
int32 resonant_chain_group_index = 14;
|
int32 resonant_chain_group_index = 14;
|
||||||
int32 equip_weapon = 15;
|
int32 equip_weapon = 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
message RoleFormationData {
|
message RoleFormationData {
|
||||||
int32 formation_id = 1;
|
int32 formation_id = 1;
|
||||||
int32 cur_role = 2;
|
int32 cur_role = 2;
|
||||||
repeated int32 role_id_list = 3;
|
repeated int32 role_id_list = 3;
|
||||||
bool is_current = 4;
|
bool is_current = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message PlayerRoleData {
|
message PlayerRoleData {
|
||||||
repeated RoleData role_list = 1;
|
repeated RoleData role_list = 1;
|
||||||
repeated RoleFormationData role_formation_list = 2;
|
map<int32, RoleFormationData> role_formation_list = 2;
|
||||||
|
int32 cur_formation_id = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message PlayerLocationData {
|
message PlayerLocationData {
|
||||||
int32 instance_id = 1;
|
int32 instance_id = 1;
|
||||||
TransformData position = 2;
|
TransformData position = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message PlayerFuncData {
|
message PlayerFuncData {
|
||||||
map<int32, int32> func_map = 1;
|
map<int32, int32> func_map = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message PlayerExploreToolsData {
|
message PlayerExploreToolsData {
|
||||||
repeated int32 unlocked_skill_list = 1;
|
repeated int32 unlocked_skill_list = 1;
|
||||||
int32 active_skill_id = 2;
|
int32 active_skill_id = 2;
|
||||||
repeated int32 roulette = 3;
|
repeated int32 roulette = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message PlayerSaveData {
|
message PlayerSaveData {
|
||||||
PlayerBasicData basic_data = 1;
|
PlayerBasicData basic_data = 1;
|
||||||
PlayerRoleData role_data = 2;
|
PlayerRoleData role_data = 2;
|
||||||
PlayerLocationData location_data = 3;
|
PlayerLocationData location_data = 3;
|
||||||
PlayerFuncData func_data = 4;
|
PlayerFuncData func_data = 4;
|
||||||
PlayerExploreToolsData explore_tools_data = 5;
|
PlayerExploreToolsData explore_tools_data = 5;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue