[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_protocol::{
|
||||
entity_component_pb::ComponentPb, AttrData, AttributeComponentPb, EAttributeType,
|
||||
EntityComponentPb, LivingStatus,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::logic::ecs::component::Component;
|
||||
use crate::logic::utils::load_role_info::attribute_from_data;
|
||||
|
||||
pub struct Attribute {
|
||||
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 {
|
||||
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() {
|
||||
LivingStatus::Alive
|
||||
} else {
|
||||
LivingStatus::Dead
|
||||
})
|
||||
.into();
|
||||
|
||||
pb.component_pbs.push(EntityComponentPb {
|
||||
component_pb: Some(ComponentPb::AttributeComponent(AttributeComponentPb {
|
||||
attr_data: self
|
||||
.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,
|
||||
})),
|
||||
})
|
||||
component_pb: Some(ComponentPb::AttributeComponent(
|
||||
self.build_entity_attribute(),
|
||||
)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,142 +39,27 @@ impl Attribute {
|
|||
> 0
|
||||
}
|
||||
|
||||
impl_from_data!(
|
||||
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
|
||||
);
|
||||
#[inline(always)]
|
||||
pub fn from_data(base_property: &BasePropertyData) -> Self {
|
||||
Self {
|
||||
attr_map: attribute_from_data(base_property),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn build_entity_attribute(&self) -> AttributeComponentPb {
|
||||
AttributeComponentPb {
|
||||
attr_data: self
|
||||
.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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,34 +1,118 @@
|
|||
use std::{cell::RefCell, collections::HashSet};
|
||||
|
||||
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)]
|
||||
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>>);
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct EntityManager {
|
||||
entity_id_counter: i64,
|
||||
active_entity_set: HashSet<Entity>,
|
||||
active_entity_set: HashMap<i32, Vec<Entity>>,
|
||||
next_id: AtomicI32,
|
||||
recycled_ids: HashMap<i32, VecDeque<i32>>,
|
||||
}
|
||||
|
||||
impl EntityManager {
|
||||
pub fn create(&mut self) -> Entity {
|
||||
self.entity_id_counter += 1;
|
||||
let entity = Entity(self.entity_id_counter);
|
||||
pub fn create(&mut self, config_id: i32, entity_type: i32, map_id: i32) -> Entity {
|
||||
let entity_id = self
|
||||
.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
|
||||
}
|
||||
|
||||
pub fn get(&self, id: i64) -> Option<Entity> {
|
||||
self.active_entity_set.get(&Entity(id)).copied()
|
||||
pub fn get_entity_id(&self, config_id: i32) -> i32 {
|
||||
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 remove(&mut self, entity: Entity) -> bool {
|
||||
self.active_entity_set.remove(&entity)
|
||||
pub fn get_config_id(&self, entity_id: i32) -> i32 {
|
||||
self.active_entity_set
|
||||
.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 {
|
||||
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 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_rules! query_with {
|
||||
($world:expr, $($comp:ident),*) => {
|
||||
$world.components().iter().filter(|(_, comps)| {
|
||||
$(comps.iter().any(|comp| matches!(&*comp.borrow(), ComponentContainer::$comp(_))) && )* true
|
||||
($world_entitys:expr, $($comp:ident),*) => {
|
||||
$world_entitys.components().iter().filter(|(_, comps)| {
|
||||
$(comps.iter().any(|comp| matches!(&*comp.borrow(), ComponentContainer::$comp(_))) && )
|
||||
* true
|
||||
})
|
||||
.map(|(e, comps)| {
|
||||
(*e,
|
||||
$(
|
||||
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
|
||||
}))
|
||||
$crate::find_component!(comps, $comp).unwrap(),
|
||||
)*
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
}).unwrap(),
|
||||
|
||||
// Query specified components from all entities (or)
|
||||
#[macro_export]
|
||||
macro_rules! query_hn_with {
|
||||
($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
|
||||
#[macro_export]
|
||||
macro_rules! query_components {
|
||||
($world:expr, $entity_id:expr, $($comp:ident),*) => {
|
||||
$world.components().iter().find(|(id, _)| $entity_id == i64::from(**id))
|
||||
($world_entitys:expr, $entity_id:expr, $($comp:ident),*) => {
|
||||
$world_entitys.components().iter().find(|(id, _)| $entity_id == i64::from(**id))
|
||||
.map(|(_, comps)| {
|
||||
($(
|
||||
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
|
||||
}
|
||||
}),
|
||||
$crate::find_component!(comps, $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::collections::hash_map::{Keys, Values};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::logic::player::InWorldPlayer;
|
||||
|
||||
use super::component::ComponentContainer;
|
||||
use super::entity::{Entity, EntityBuilder, EntityManager};
|
||||
pub struct WorldEntity {
|
||||
components: HashMap<i32, Vec<RefCell<ComponentContainer>>>,
|
||||
entity_manager: EntityManager,
|
||||
}
|
||||
|
||||
pub struct World {
|
||||
components: HashMap<Entity, Vec<RefCell<ComponentContainer>>>,
|
||||
entity_manager: EntityManager,
|
||||
in_world_players: HashMap<i32, InWorldPlayer>, // joined players metadata
|
||||
pub player_cur_map_id: i32,
|
||||
pub world_entitys: HashMap<i32, WorldEntity>, // i32 -> map_id
|
||||
pub in_world_players: HashMap<i32, InWorldPlayer>, // joined players metadata
|
||||
}
|
||||
|
||||
impl World {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
components: HashMap::new(),
|
||||
entity_manager: EntityManager::default(),
|
||||
player_cur_map_id: 8,
|
||||
world_entitys: 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> {
|
||||
self.in_world_players.keys()
|
||||
}
|
||||
|
@ -55,4 +37,84 @@ impl World {
|
|||
self.in_world_players
|
||||
.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 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(
|
||||
_player: &Player,
|
||||
player: &mut Player,
|
||||
request: CombatSendPackRequest,
|
||||
response: &mut CombatSendPackResponse,
|
||||
) {
|
||||
tracing::debug!("CombatSendPackRequest: for {:?}", request);
|
||||
// TODO: Implement this
|
||||
for data in request.data.iter() {
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -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 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(
|
||||
player: &Player,
|
||||
request: EntityActiveRequest,
|
||||
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!(
|
||||
"EntityActiveRequest: entity with id {} doesn't exist, player_id: {}",
|
||||
request.entity_id,
|
||||
|
@ -22,14 +23,28 @@ pub fn on_entity_active_request(
|
|||
return;
|
||||
};
|
||||
|
||||
if let Some(position) = query_components!(world, request.entity_id, Position).0 {
|
||||
// TODO: proper entity "activation" logic
|
||||
if let (Some(position), Some(attribute)) =
|
||||
query_components!(world, request.entity_id, Position, Attribute)
|
||||
{
|
||||
response.is_visible = true;
|
||||
response.pos = Some(position.0.get_position_protobuf());
|
||||
response.rot = Some(position.0.get_rotation_protobuf());
|
||||
}
|
||||
|
||||
response.component_pbs = Vec::new(); // not implemented
|
||||
response.component_pbs.push(EntityComponentPb {
|
||||
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(
|
||||
|
@ -38,33 +53,43 @@ pub fn on_entity_on_landed_request(
|
|||
_: &mut EntityOnLandedResponse,
|
||||
) {
|
||||
// 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(
|
||||
_: &Player,
|
||||
request: EntityPositionRequest,
|
||||
_: &mut EntityPositionResponse,
|
||||
) {
|
||||
// TODO: Implement this
|
||||
tracing::debug!(
|
||||
"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(
|
||||
_: &Player,
|
||||
request: EntityLoadCompleteRequest,
|
||||
_: &mut EntityLoadCompleteResponse,
|
||||
) {
|
||||
// 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) {
|
||||
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 {
|
||||
if !world.is_in_world(moving_entity.entity_id) {
|
||||
if !world.is_in_all_world_map(moving_entity.entity_id as i32) {
|
||||
tracing::debug!(
|
||||
"MovePackage: entity with id {} doesn't exist",
|
||||
moving_entity.entity_id
|
||||
|
|
|
@ -3,8 +3,8 @@ use shorekeeper_protocol::{GuideInfoRequest, GuideInfoResponse};
|
|||
use crate::logic::player::Player;
|
||||
|
||||
pub fn on_guide_info_request(
|
||||
player: &Player,
|
||||
request: GuideInfoRequest,
|
||||
_player: &Player,
|
||||
_request: GuideInfoRequest,
|
||||
response: &mut GuideInfoResponse,
|
||||
) {
|
||||
// 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;
|
||||
|
||||
|
@ -34,7 +38,7 @@ pub fn on_server_play_station_play_only_state_request(
|
|||
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?
|
||||
tracing::debug!(
|
||||
"Client versions: launcher: {}, app: {}, resources: {}",
|
||||
|
|
|
@ -3,6 +3,7 @@ mod entity;
|
|||
mod guide;
|
||||
mod mail;
|
||||
mod misc;
|
||||
mod role;
|
||||
mod scene;
|
||||
mod skill;
|
||||
|
||||
|
@ -11,6 +12,7 @@ pub use entity::*;
|
|||
pub use guide::*;
|
||||
pub use mail::*;
|
||||
pub use misc::*;
|
||||
pub use role::*;
|
||||
pub use scene::*;
|
||||
pub use skill::*;
|
||||
|
||||
|
@ -89,6 +91,13 @@ handle_request! {
|
|||
// Combat
|
||||
CombatSendPack, combat_message;
|
||||
|
||||
// Role
|
||||
RoleShowListUpdate;
|
||||
ClientCurrentRoleReport;
|
||||
RoleFavorList;
|
||||
FormationAttr;
|
||||
UpdateFormation;
|
||||
|
||||
// Entity
|
||||
EntityActive;
|
||||
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,
|
||||
SceneTraceRequest, SceneTraceResponse, UpdateSceneDateRequest,
|
||||
UpdateSceneDateResponse,
|
||||
use shorekeeper_protocol::{
|
||||
ErrorCode, SceneLoadingFinishRequest, SceneLoadingFinishResponse, SceneTraceRequest,
|
||||
SceneTraceResponse, UpdateSceneDateRequest, UpdateSceneDateResponse,
|
||||
};
|
||||
|
||||
use crate::logic::player::Player;
|
||||
|
@ -15,7 +15,7 @@ pub fn on_scene_trace_request(
|
|||
|
||||
pub fn on_scene_loading_finish_request(
|
||||
_player: &Player,
|
||||
request: SceneLoadingFinishRequest,
|
||||
_request: SceneLoadingFinishRequest,
|
||||
response: &mut SceneLoadingFinishResponse,
|
||||
) {
|
||||
// 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;
|
||||
|
||||
let world = player.world.borrow();
|
||||
for (entity, owner, mut vision_skill) in query_with!(world, OwnerPlayer, VisionSkill) {
|
||||
for (entity, owner, mut vision_skill) in query_with!(
|
||||
player.world.borrow().get_world_entity(),
|
||||
OwnerPlayer,
|
||||
VisionSkill
|
||||
) {
|
||||
if owner.0 == player.basic_info.id {
|
||||
vision_skill.skill_id = request.skill_id;
|
||||
player.notify(VisionSkillChangeNotify {
|
||||
|
|
|
@ -10,6 +10,8 @@ pub struct PlayerBasicInfo {
|
|||
pub exp: i32,
|
||||
pub head_photo: i32,
|
||||
pub head_frame: i32,
|
||||
pub cur_map_id: i32,
|
||||
pub role_show_list: Vec<i32>,
|
||||
}
|
||||
|
||||
impl PlayerBasicInfo {
|
||||
|
@ -37,6 +39,8 @@ impl PlayerBasicInfo {
|
|||
exp: data.exp,
|
||||
head_photo: data.head_photo,
|
||||
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,
|
||||
head_photo: self.head_photo,
|
||||
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 explore_tools::ExploreTools;
|
||||
use location::PlayerLocation;
|
||||
use player_func::PlayerFunc;
|
||||
use shorekeeper_protocol::{
|
||||
message::Message, ItemPkgOpenNotify, PbGetRoleListNotify, PlayerBasicData, PlayerRoleData,
|
||||
PlayerSaveData, ProtocolUnit,
|
||||
EEntityType, ERemoveEntityType, EntityAddNotify, EntityConfigType, EntityPb, EntityRemoveInfo,
|
||||
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 super::{
|
||||
|
@ -23,14 +31,24 @@ mod in_world_player;
|
|||
mod location;
|
||||
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;
|
||||
use shorekeeper_data::base_property_data;
|
||||
use shorekeeper_data::role_info_data;
|
||||
use shorekeeper_protocol::message::Message;
|
||||
|
||||
pub struct Player {
|
||||
session: Option<Arc<Session>>,
|
||||
// Persistent
|
||||
pub basic_info: PlayerBasicInfo,
|
||||
pub role_list: Vec<Role>,
|
||||
pub formation_list: Vec<RoleFormation>,
|
||||
pub role_list: HashMap<i32, Role>,
|
||||
pub formation_list: HashMap<i32, RoleFormation>,
|
||||
pub cur_formation_id: i32,
|
||||
pub location: PlayerLocation,
|
||||
pub func: PlayerFunc,
|
||||
pub explore_tools: ExploreTools,
|
||||
|
@ -41,28 +59,10 @@ pub struct Player {
|
|||
|
||||
impl Player {
|
||||
pub fn init(&mut self) {
|
||||
if self.role_list.is_empty() {
|
||||
self.on_first_enter();
|
||||
if self.role_list.is_empty() || self.formation_list.is_empty() {
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -76,24 +76,52 @@ impl Player {
|
|||
self.notify(ItemPkgOpenNotify {
|
||||
open_pkg: (0..8).collect(),
|
||||
});
|
||||
|
||||
self.notify(self.build_update_formation_notify());
|
||||
}
|
||||
|
||||
fn on_first_enter(&mut self) {
|
||||
self.role_list.push(Self::create_main_character_role(
|
||||
self.basic_info.name.clone(),
|
||||
self.basic_info.sex,
|
||||
));
|
||||
fn init_role_and_formation(&mut self) {
|
||||
self.role_list.clear();
|
||||
let mut role = match 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 {
|
||||
id: 1,
|
||||
cur_role: role.role_id,
|
||||
role_id_set: HashSet::from([role.role_id]),
|
||||
is_current: true,
|
||||
self.role_list.insert(role.role_id, role);
|
||||
|
||||
let required_role_ids: Vec<i32> = role_info_data::iter()
|
||||
.filter(|role_info| role_info.role_type == 1)
|
||||
.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
|
||||
|
@ -104,28 +132,38 @@ impl Player {
|
|||
}
|
||||
|
||||
fn ensure_current_formation(&mut self) {
|
||||
// If the list off formation is empty, add a default formation
|
||||
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(
|
||||
1,
|
||||
RoleFormation {
|
||||
id: 1,
|
||||
cur_role: role.role_id,
|
||||
role_id_set: HashSet::from([role.role_id]),
|
||||
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) {
|
||||
self.formation_list[0].is_current = true;
|
||||
// If there is no current formation, set the first formation as the current formation
|
||||
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) {
|
||||
if rf.role_id_set.is_empty() {
|
||||
rf.role_id_set.insert(self.role_list[0].role_id);
|
||||
// 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 let Some(rf) = self.formation_list.values_mut().find(|rf| rf.is_current) {
|
||||
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) {
|
||||
rf.cur_role = *rf.role_id_set.iter().nth(0).unwrap();
|
||||
if !rf.role_ids.contains(&rf.cur_role) {
|
||||
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> {
|
||||
self.formation_list
|
||||
.iter()
|
||||
.find(|rf| rf.is_current)
|
||||
.unwrap()
|
||||
.role_id_set
|
||||
.iter()
|
||||
.flat_map(|id| self.role_list.iter().find(|r| r.role_id == *id))
|
||||
.collect()
|
||||
pub fn build_player_entity_add_notify(
|
||||
&self,
|
||||
role_list: Vec<Role>,
|
||||
world: &mut WorldEntity,
|
||||
) -> EntityAddNotify {
|
||||
create_player_entity_pb!(
|
||||
role_list,
|
||||
self.basic_info.cur_map_id,
|
||||
world,
|
||||
self.basic_info.id,
|
||||
self.location.position.clone(),
|
||||
self.explore_tools
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_cur_role_id(&self) -> i32 {
|
||||
self.formation_list
|
||||
pub fn build_player_entity_remove_notify(
|
||||
&self,
|
||||
entities: Vec<i64>,
|
||||
remove_type: ERemoveEntityType,
|
||||
) -> EntityRemoveNotify {
|
||||
EntityRemoveNotify {
|
||||
remove_infos: entities
|
||||
.iter()
|
||||
.find(|rf| rf.is_current)
|
||||
.unwrap()
|
||||
.cur_role
|
||||
.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 {
|
||||
|
@ -164,17 +284,20 @@ impl Player {
|
|||
|
||||
Self {
|
||||
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
|
||||
.into_iter()
|
||||
.map(Role::load_from_save)
|
||||
.collect(),
|
||||
.collect::<HashMap<i32, Role>>(),
|
||||
formation_list: role_data
|
||||
.role_formation_list
|
||||
.into_iter()
|
||||
.map(RoleFormation::load_from_save)
|
||||
.map(|(k, v)| (k, RoleFormation::load_from_save(v)))
|
||||
.collect(),
|
||||
cur_formation_id: role_data.cur_formation_id,
|
||||
location: save_data
|
||||
.location_data
|
||||
.map(PlayerLocation::load_from_save)
|
||||
|
@ -196,12 +319,17 @@ impl Player {
|
|||
PlayerSaveData {
|
||||
basic_data: Some(self.basic_info.build_save_data()),
|
||||
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
|
||||
.formation_list
|
||||
.iter()
|
||||
.map(|rf| rf.build_save_data())
|
||||
.map(|(&k, v)| (k, v.build_save_data()))
|
||||
.collect(),
|
||||
cur_formation_id: self.cur_formation_id,
|
||||
}),
|
||||
location_data: Some(self.location.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 {
|
||||
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 {
|
||||
let mut role = match sex {
|
||||
0 => Role::new(Role::MAIN_CHARACTER_FEMALE_ID),
|
||||
1 => Role::new(Role::MAIN_CHARACTER_MALE_ID),
|
||||
_ => unreachable!(),
|
||||
pub fn create_default_save_data(id: i32, name: String, sex: i32) -> PlayerSaveData {
|
||||
let role_id = match sex {
|
||||
0 => Role::MAIN_CHARACTER_FEMALE_ID, // 1502
|
||||
1 => Role::MAIN_CHARACTER_MALE_ID, // 1501
|
||||
_ => 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 {
|
||||
basic_data: Some(PlayerBasicData {
|
||||
id,
|
||||
|
@ -260,6 +387,8 @@ impl Player {
|
|||
level: 1,
|
||||
head_photo: 1603,
|
||||
head_frame: 80060009,
|
||||
cur_map_id: 8,
|
||||
role_show_list: vec![role_id],
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
use std::collections::HashSet;
|
||||
|
||||
use shorekeeper_protocol::RoleFormationData;
|
||||
|
||||
pub struct RoleFormation {
|
||||
pub id: i32,
|
||||
pub cur_role: i32,
|
||||
pub role_id_set: HashSet<i32>,
|
||||
pub role_ids: Vec<i32>,
|
||||
pub is_current: bool,
|
||||
}
|
||||
|
||||
|
@ -14,7 +12,7 @@ impl RoleFormation {
|
|||
Self {
|
||||
id: data.formation_id,
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +21,7 @@ impl RoleFormation {
|
|||
RoleFormationData {
|
||||
formation_id: self.id,
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use crate::logic::utils::load_role_info::load_key_value;
|
||||
use common::time_util;
|
||||
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};
|
||||
|
||||
mod formation;
|
||||
|
@ -41,6 +42,12 @@ impl Role {
|
|||
}
|
||||
|
||||
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 {
|
||||
role_id: self.role_id,
|
||||
name: self.name.clone(),
|
||||
|
@ -55,11 +62,17 @@ impl Role {
|
|||
.collect(),
|
||||
star: self.star,
|
||||
favor: self.favor,
|
||||
base_prop: base_prop
|
||||
.iter()
|
||||
.map(|(&k, &v)| ArrayIntInt { key: k, value: v })
|
||||
.collect(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_from_save(data: RoleData) -> Self {
|
||||
pub fn load_from_save(data: RoleData) -> (i32, Self) {
|
||||
(
|
||||
data.role_id,
|
||||
Self {
|
||||
role_id: data.role_id,
|
||||
name: data.name,
|
||||
|
@ -71,7 +84,8 @@ impl Role {
|
|||
favor: data.favor,
|
||||
create_time: data.create_time,
|
||||
equip_weapon: data.equip_weapon,
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn build_save_data(&self) -> RoleData {
|
||||
|
|
|
@ -15,8 +15,9 @@ pub(super) struct MovementSystem;
|
|||
impl System for MovementSystem {
|
||||
fn tick(&self, world: &mut World, players: &mut [RefMut<Player>]) {
|
||||
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() {
|
||||
continue;
|
||||
}
|
||||
|
@ -46,9 +47,12 @@ impl System for MovementSystem {
|
|||
|
||||
notify.moving_entities.push(moving_entity_data);
|
||||
|
||||
if let (Some(_), Some(owner)) =
|
||||
query_components!(world, i64::from(entity), PlayerEntityMarker, OwnerPlayer)
|
||||
{
|
||||
if let (Some(_), Some(owner)) = query_components!(
|
||||
world_entity,
|
||||
i64::from(entity),
|
||||
PlayerEntityMarker,
|
||||
OwnerPlayer
|
||||
) {
|
||||
if let Some(player) = players.iter_mut().find(|pl| pl.basic_info.id == owner.0) {
|
||||
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::{
|
||||
cell::RefCell,
|
||||
collections::{HashMap, VecDeque},
|
||||
|
@ -10,20 +17,13 @@ use std::{
|
|||
time::Duration,
|
||||
};
|
||||
|
||||
use common::time_util;
|
||||
use shorekeeper_protocol::PlayerSaveData;
|
||||
use shorekeeper_protocol::{
|
||||
message::Message, AfterJoinSceneNotify, EnterGameResponse, JoinSceneNotify, JsPatchNotify,
|
||||
TransitionOptionPb,
|
||||
};
|
||||
|
||||
use super::{ecs::world::World, player::Player, utils::world_util};
|
||||
use crate::logic::ecs::world::WorldEntity;
|
||||
use crate::{
|
||||
player_save_task::{self, PlayerSaveReason},
|
||||
session::Session,
|
||||
};
|
||||
|
||||
use super::{ecs::world::World, player::Player, utils::world_util};
|
||||
|
||||
const WATER_MASK: &str = include_str!("../../watermask-rr.js");
|
||||
const UID_FIX: &str = include_str!("../../uidfix.js");
|
||||
const CENSORSHIP_FIX: &str = include_str!("../../censorshipfix.js");
|
||||
|
@ -141,13 +141,29 @@ fn handle_logic_input(state: &mut LogicState, input: LogicInput) {
|
|||
session,
|
||||
player_save_data,
|
||||
} => {
|
||||
let player = state
|
||||
.players
|
||||
.entry(player_id)
|
||||
.or_insert(RefCell::new(Player::load_from_save(player_save_data)));
|
||||
let (player, is_player) = if let Vacant(e) = state.players.entry(player_id) {
|
||||
(
|
||||
e.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();
|
||||
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.set_session(session);
|
||||
|
@ -159,16 +175,12 @@ fn handle_logic_input(state: &mut LogicState, input: LogicInput) {
|
|||
.borrow_mut()
|
||||
.set_in_world_player_data(player.build_in_world_player());
|
||||
|
||||
world_util::add_player_entities(&mut player.world.borrow_mut(), &player);
|
||||
let scene_info = world_util::build_scene_information(
|
||||
&player.world.borrow(),
|
||||
player.location.instance_id,
|
||||
player.basic_info.id,
|
||||
);
|
||||
world_util::add_player_entities(&player);
|
||||
let scene_info = world_util::build_scene_information(&player);
|
||||
|
||||
player.notify(JoinSceneNotify {
|
||||
max_entity_id: i64::MAX,
|
||||
scene_info: Some(scene_info),
|
||||
max_entity_id: i64::MAX,
|
||||
transition_option: Some(TransitionOptionPb::default()),
|
||||
});
|
||||
|
||||
|
|
|
@ -1,25 +1,75 @@
|
|||
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 {
|
||||
let entities = query_with!(world, PlayerEntityMarker)
|
||||
pub fn build_scene_add_on_init_data(player: &Player) -> PlayerSceneAoiData {
|
||||
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()
|
||||
.map(|(e, _)| e)
|
||||
.collect::<Vec<_>>();
|
||||
.map(|(entity_id, _)| {
|
||||
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();
|
||||
for entity in entities {
|
||||
let mut pb = EntityPb { id: entity.into(), ..Default::default() };
|
||||
|
||||
entities
|
||||
.iter()
|
||||
.filter(|&&(_, entity_id)| entity_id != -1)
|
||||
.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
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
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)
|
||||
.get_entity_components(entity_id)
|
||||
.into_iter()
|
||||
.for_each(|comp| comp.set_pb_data(&mut pb));
|
||||
|
||||
aoi_data.entities.push(pb);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
});
|
||||
|
||||
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 load_role_info;
|
||||
pub mod world_util;
|
||||
|
|
|
@ -1,28 +1,96 @@
|
|||
use shorekeeper_data::base_property_data;
|
||||
use shorekeeper_protocol::{
|
||||
EntityConfigType, FightRoleInfo, FightRoleInfos, LivingStatus, SceneInformation, SceneMode,
|
||||
ScenePlayerInformation, SceneTimeInfo,
|
||||
};
|
||||
|
||||
use super::entity_serializer;
|
||||
use crate::{
|
||||
logic::{
|
||||
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, world::World},
|
||||
player::Player,
|
||||
},
|
||||
query_with,
|
||||
ecs::component::ComponentContainer,
|
||||
};
|
||||
use crate::query_with;
|
||||
use shorekeeper_data::base_property_data;
|
||||
use shorekeeper_protocol::{
|
||||
EEntityType, EntityConfigType, FightRoleInfo, FightRoleInfos, LivingStatus, SceneInformation,
|
||||
SceneMode, ScenePlayerInformation, SceneTimeInfo,
|
||||
};
|
||||
|
||||
pub fn add_player_entities(world: &mut World, player: &Player) {
|
||||
let cur_role_id = player.get_cur_role_id();
|
||||
#[macro_export]
|
||||
macro_rules! create_player_entity_pb {
|
||||
($role_list:expr, $cur_map_id:expr, $world:expr, $player_id:expr, $position:expr, $explore_tools:expr) => {{
|
||||
let mut pbs = Vec::new();
|
||||
|
||||
for role in player.get_current_formation_role_list() {
|
||||
let id = world
|
||||
.create_entity()
|
||||
for role in $role_list {
|
||||
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");
|
||||
|
||||
let entity = $world
|
||||
.create_entity(role_id, EEntityType::Player.into(), $cur_map_id)
|
||||
.with(ComponentContainer::PlayerEntityMarker(PlayerEntityMarker))
|
||||
.with(ComponentContainer::EntityConfig(EntityConfig {
|
||||
config_id: role_id,
|
||||
config_type: EntityConfigType::Character,
|
||||
}))
|
||||
.with(ComponentContainer::OwnerPlayer(OwnerPlayer($player_id)))
|
||||
.with(ComponentContainer::Position(Position($position)))
|
||||
.with(ComponentContainer::Visibility(Visibility(
|
||||
role_id == role_id,
|
||||
)))
|
||||
.with(ComponentContainer::Attribute(Attribute::from_data(
|
||||
base_property,
|
||||
)))
|
||||
.with(ComponentContainer::Movement(Movement::default()))
|
||||
.with(ComponentContainer::Equip(Equip {
|
||||
weapon_id: role.equip_weapon,
|
||||
weapon_breach_level: 90, // TODO: store this too
|
||||
}))
|
||||
.with(ComponentContainer::VisionSkill(VisionSkill {
|
||||
skill_id: $explore_tools.active_explore_skill,
|
||||
}))
|
||||
.build();
|
||||
|
||||
let mut pb = EntityPb {
|
||||
id: entity.entity_id as i64,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
$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,
|
||||
|
@ -54,29 +122,30 @@ pub fn add_player_entities(world: &mut World, player: &Player) {
|
|||
|
||||
tracing::debug!(
|
||||
"created player entity, id: {}, role_id: {}",
|
||||
i64::from(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 {
|
||||
scene_id: String::new(),
|
||||
instance_id,
|
||||
owner_id,
|
||||
instance_id: player.location.instance_id,
|
||||
owner_id: player.basic_info.id,
|
||||
dynamic_entity_list: Vec::new(),
|
||||
blackboard_params: Vec::new(),
|
||||
end_time: 0,
|
||||
aoi_data: Some(entity_serializer::build_scene_add_on_init_data(world)),
|
||||
player_infos: build_player_info_list(world),
|
||||
aoi_data: Some(entity_serializer::build_scene_add_on_init_data(player)),
|
||||
player_infos: build_player_info_list(&player.world.borrow_mut()),
|
||||
mode: SceneMode::Single.into(),
|
||||
time_info: Some(SceneTimeInfo {
|
||||
owner_time_clock_time_span: 0,
|
||||
hour: 8,
|
||||
minute: 0,
|
||||
}),
|
||||
cur_context_id: owner_id as i64,
|
||||
cur_context_id: player.basic_info.id as i64,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
@ -85,22 +154,31 @@ fn build_player_info_list(world: &World) -> Vec<ScenePlayerInformation> {
|
|||
world
|
||||
.players()
|
||||
.map(|sp| {
|
||||
let (cur_role_id, transform) = query_with!(
|
||||
world,
|
||||
let (cur_role_id, transform, _equip) = query_with!(
|
||||
world.get_world_entity(),
|
||||
PlayerEntityMarker,
|
||||
OwnerPlayer,
|
||||
Visibility,
|
||||
EntityConfig,
|
||||
Position
|
||||
Position,
|
||||
Equip
|
||||
)
|
||||
.into_iter()
|
||||
.find_map(|(_, _, owner, visibility, conf, pos)| {
|
||||
(sp.player_id == owner.0 && visibility.0).then_some((conf.config_id, pos.0.clone()))
|
||||
.find_map(|(_, _, owner, visibility, conf, pos, equip)| {
|
||||
(sp.player_id == owner.0 && visibility.0).then_some((
|
||||
conf.config_id,
|
||||
pos.0.clone(),
|
||||
equip.weapon_id,
|
||||
))
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let active_characters =
|
||||
query_with!(world, PlayerEntityMarker, OwnerPlayer, EntityConfig)
|
||||
let active_characters = query_with!(
|
||||
world.get_world_entity(),
|
||||
PlayerEntityMarker,
|
||||
OwnerPlayer,
|
||||
EntityConfig
|
||||
)
|
||||
.into_iter()
|
||||
.filter(|(_, _, owner, _)| owner.0 == sp.player_id);
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ message PlayerBasicData {
|
|||
int32 exp = 5;
|
||||
int32 head_photo = 6;
|
||||
int32 head_frame = 7;
|
||||
int32 cur_map_id = 8;
|
||||
repeated int32 role_show_list = 9;
|
||||
}
|
||||
|
||||
message RoleSkillNodeData {
|
||||
|
@ -55,7 +57,8 @@ message RoleFormationData {
|
|||
|
||||
message PlayerRoleData {
|
||||
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 {
|
||||
|
|
Loading…
Reference in a new issue