mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
review
This commit is contained in:
parent
0c9a942027
commit
e5e26149ed
@ -46,7 +46,6 @@ macro_rules! synced_components {
|
|||||||
beam_segment: BeamSegment,
|
beam_segment: BeamSegment,
|
||||||
alignment: Alignment,
|
alignment: Alignment,
|
||||||
stance: Stance,
|
stance: Stance,
|
||||||
teleporter: Teleporter,
|
|
||||||
// TODO: change this to `SyncFrom::ClientEntity` and sync the bare minimum
|
// TODO: change this to `SyncFrom::ClientEntity` and sync the bare minimum
|
||||||
// from other entities (e.g. just keys needed to show appearance
|
// from other entities (e.g. just keys needed to show appearance
|
||||||
// based on their loadout). Also, it looks like this actually has
|
// based on their loadout). Also, it looks like this actually has
|
||||||
@ -235,10 +234,6 @@ impl NetSync for Stance {
|
|||||||
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NetSync for Teleporter {
|
|
||||||
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
|
||||||
}
|
|
||||||
|
|
||||||
// These are synced only from the client's own entity.
|
// These are synced only from the client's own entity.
|
||||||
|
|
||||||
impl NetSync for Admin {
|
impl NetSync for Admin {
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
use super::item::Reagent;
|
use super::item::Reagent;
|
||||||
use crate::{resources::Time, uid::Uid};
|
use crate::{
|
||||||
|
resources::{Secs, Time},
|
||||||
|
uid::Uid,
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use specs::{Component, DerefFlaggedStorage};
|
use specs::{Component, DerefFlaggedStorage};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use vek::Vec3;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum Object {
|
pub enum Object {
|
||||||
@ -17,8 +21,36 @@ pub enum Object {
|
|||||||
spawned_at: Time,
|
spawned_at: Time,
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
},
|
},
|
||||||
|
Portal {
|
||||||
|
target: Vec3<f32>,
|
||||||
|
requires_no_aggro: bool,
|
||||||
|
buildup_time: Secs,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for Object {
|
impl Component for Object {
|
||||||
type Storage = DerefFlaggedStorage<Self, specs::VecStorage<Self>>;
|
type Storage = DerefFlaggedStorage<Self, specs::VecStorage<Self>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PortalData {
|
||||||
|
pub target: Vec3<f32>,
|
||||||
|
pub requires_no_aggro: bool,
|
||||||
|
pub buildup_time: Secs,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PortalData> for Object {
|
||||||
|
fn from(
|
||||||
|
PortalData {
|
||||||
|
target,
|
||||||
|
requires_no_aggro,
|
||||||
|
buildup_time,
|
||||||
|
}: PortalData,
|
||||||
|
) -> Self {
|
||||||
|
Self::Portal {
|
||||||
|
target,
|
||||||
|
requires_no_aggro,
|
||||||
|
buildup_time,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -23,7 +23,7 @@ mod last;
|
|||||||
mod location;
|
mod location;
|
||||||
pub mod loot_owner;
|
pub mod loot_owner;
|
||||||
pub mod melee;
|
pub mod melee;
|
||||||
mod misc;
|
pub mod misc;
|
||||||
pub mod ori;
|
pub mod ori;
|
||||||
pub mod pet;
|
pub mod pet;
|
||||||
mod phys;
|
mod phys;
|
||||||
@ -104,7 +104,7 @@ pub use self::{
|
|||||||
SkillGroup, SkillGroupKind, SkillSet,
|
SkillGroup, SkillGroupKind, SkillSet,
|
||||||
},
|
},
|
||||||
stats::{Stats, StatsModifier},
|
stats::{Stats, StatsModifier},
|
||||||
teleport::{Teleporter, Teleporting},
|
teleport::Teleporting,
|
||||||
visual::{LightAnimation, LightEmitter},
|
visual::{LightAnimation, LightEmitter},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,19 +1,6 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use specs::{Component, DerefFlaggedStorage, Entity};
|
use specs::{Component, DerefFlaggedStorage, Entity};
|
||||||
use vek::Vec3;
|
|
||||||
|
|
||||||
use crate::resources::{Secs, Time};
|
use crate::resources::Time;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub struct Teleporter {
|
|
||||||
pub target: Vec3<f32>,
|
|
||||||
pub requires_no_aggro: bool,
|
|
||||||
pub buildup_time: Secs,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Component for Teleporter {
|
|
||||||
type Storage = DerefFlaggedStorage<Self, specs::VecStorage<Self>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub struct Teleporting {
|
pub struct Teleporting {
|
||||||
|
@ -32,3 +32,5 @@ pub const SOUND_TRAVEL_DIST_PER_VOLUME: f32 = 3.0;
|
|||||||
// Stat increase per level (multiplied by 10 compared to what you'll see in UI)
|
// Stat increase per level (multiplied by 10 compared to what you'll see in UI)
|
||||||
pub const ENERGY_PER_LEVEL: u16 = 5;
|
pub const ENERGY_PER_LEVEL: u16 = 5;
|
||||||
pub const HP_PER_LEVEL: u16 = 5;
|
pub const HP_PER_LEVEL: u16 = 5;
|
||||||
|
|
||||||
|
pub const TELEPORTER_RADIUS: f32 = 3.;
|
||||||
|
@ -6,7 +6,8 @@ use crate::{
|
|||||||
agent::Sound,
|
agent::Sound,
|
||||||
dialogue::Subject,
|
dialogue::Subject,
|
||||||
invite::{InviteKind, InviteResponse},
|
invite::{InviteKind, InviteResponse},
|
||||||
DisconnectReason, Ori, Pos, Teleporter,
|
misc::PortalData,
|
||||||
|
DisconnectReason, Ori, Pos,
|
||||||
},
|
},
|
||||||
lottery::LootSpec,
|
lottery::LootSpec,
|
||||||
mounting::VolumePos,
|
mounting::VolumePos,
|
||||||
@ -239,7 +240,7 @@ pub enum ServerEvent {
|
|||||||
driver: Option<NpcBuilder>,
|
driver: Option<NpcBuilder>,
|
||||||
},
|
},
|
||||||
CreateWaypoint(Vec3<f32>),
|
CreateWaypoint(Vec3<f32>),
|
||||||
CreateTeleporter(Vec3<f32>, Teleporter),
|
CreateTeleporter(Vec3<f32>, PortalData),
|
||||||
ClientDisconnect(EcsEntity, DisconnectReason),
|
ClientDisconnect(EcsEntity, DisconnectReason),
|
||||||
ClientDisconnectWithoutPersistence(EcsEntity),
|
ClientDisconnectWithoutPersistence(EcsEntity),
|
||||||
Command(EcsEntity, String, Vec<String>),
|
Command(EcsEntity, String, Vec<String>),
|
||||||
|
@ -3,7 +3,8 @@ use crate::{
|
|||||||
comp::{
|
comp::{
|
||||||
self, agent, humanoid,
|
self, agent, humanoid,
|
||||||
inventory::loadout_builder::{LoadoutBuilder, LoadoutSpec},
|
inventory::loadout_builder::{LoadoutBuilder, LoadoutSpec},
|
||||||
Alignment, Body, Item, Teleporter,
|
misc::PortalData,
|
||||||
|
Alignment, Body, Item,
|
||||||
},
|
},
|
||||||
lottery::LootSpec,
|
lottery::LootSpec,
|
||||||
npc::{self, NPC_NAMES},
|
npc::{self, NPC_NAMES},
|
||||||
@ -166,7 +167,7 @@ pub fn try_all_entity_configs() -> Result<Vec<String>, Error> {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum SpecialEntity {
|
pub enum SpecialEntity {
|
||||||
Waypoint,
|
Waypoint,
|
||||||
Teleporter(Teleporter),
|
Teleporter(PortalData),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -219,7 +219,6 @@ impl State {
|
|||||||
ecs.register::<comp::LootOwner>();
|
ecs.register::<comp::LootOwner>();
|
||||||
ecs.register::<comp::Admin>();
|
ecs.register::<comp::Admin>();
|
||||||
ecs.register::<comp::Stance>();
|
ecs.register::<comp::Stance>();
|
||||||
ecs.register::<comp::Teleporter>();
|
|
||||||
ecs.register::<comp::Teleporting>();
|
ecs.register::<comp::Teleporting>();
|
||||||
|
|
||||||
// Register components send from clients -> server
|
// Register components send from clients -> server
|
||||||
|
@ -33,21 +33,20 @@ use common::{
|
|||||||
slot::Slot,
|
slot::Slot,
|
||||||
},
|
},
|
||||||
invite::InviteKind,
|
invite::InviteKind,
|
||||||
AdminRole, ChatType, Inventory, Item, LightEmitter, Presence, PresenceKind, WaypointArea,
|
misc::PortalData,
|
||||||
|
AdminRole, ChatType, Inventory, Item, LightEmitter, WaypointArea,
|
||||||
},
|
},
|
||||||
depot,
|
depot,
|
||||||
effect::Effect,
|
effect::Effect,
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
generation::{EntityConfig, EntityInfo},
|
generation::{EntityConfig, EntityInfo},
|
||||||
link::Is,
|
|
||||||
mounting::{Rider, VolumeRider},
|
|
||||||
npc::{self, get_npc_name},
|
npc::{self, get_npc_name},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
parse_cmd_args,
|
parse_cmd_args,
|
||||||
resources::{BattleMode, PlayerPhysicsSettings, Secs, Time, TimeOfDay, TimeScale},
|
resources::{BattleMode, PlayerPhysicsSettings, Secs, Time, TimeOfDay, TimeScale},
|
||||||
rtsim::{Actor, Role},
|
rtsim::{Actor, Role},
|
||||||
terrain::{Block, BlockKind, CoordinateConversions, SpriteKind, TerrainChunkSize},
|
terrain::{Block, BlockKind, CoordinateConversions, SpriteKind, TerrainChunkSize},
|
||||||
uid::{IdMaps, Uid},
|
uid::Uid,
|
||||||
vol::ReadVol,
|
vol::ReadVol,
|
||||||
weather, Damage, DamageKind, DamageSource, Explosion, LoadoutBuilder, RadiusEffect,
|
weather, Damage, DamageKind, DamageSource, Explosion, LoadoutBuilder, RadiusEffect,
|
||||||
};
|
};
|
||||||
@ -223,89 +222,6 @@ fn position(server: &Server, entity: EcsEntity, descriptor: &str) -> CmdResult<c
|
|||||||
.ok_or_else(|| format!("Cannot get position for {:?}!", descriptor))
|
.ok_or_else(|| format!("Cannot get position for {:?}!", descriptor))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn position_mut<T>(
|
|
||||||
server: &mut Server,
|
|
||||||
entity: EcsEntity,
|
|
||||||
descriptor: &str,
|
|
||||||
dismount_volume: Option<bool>,
|
|
||||||
f: impl for<'a> FnOnce(&'a mut comp::Pos) -> T,
|
|
||||||
) -> CmdResult<T> {
|
|
||||||
if dismount_volume.unwrap_or(true) {
|
|
||||||
server
|
|
||||||
.state
|
|
||||||
.ecs()
|
|
||||||
.write_storage::<Is<VolumeRider>>()
|
|
||||||
.remove(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
let entity = server
|
|
||||||
.state
|
|
||||||
.read_storage::<Is<Rider>>()
|
|
||||||
.get(entity)
|
|
||||||
.and_then(|is_rider| {
|
|
||||||
server
|
|
||||||
.state
|
|
||||||
.ecs()
|
|
||||||
.read_resource::<IdMaps>()
|
|
||||||
.uid_entity(is_rider.mount)
|
|
||||||
})
|
|
||||||
.map(Ok)
|
|
||||||
.or_else(|| {
|
|
||||||
server
|
|
||||||
.state
|
|
||||||
.read_storage::<Is<VolumeRider>>()
|
|
||||||
.get(entity)
|
|
||||||
.and_then(|volume_rider| {
|
|
||||||
Some(match volume_rider.pos.kind {
|
|
||||||
common::mounting::Volume::Terrain => {
|
|
||||||
Err("Tried to move the world.".to_string())
|
|
||||||
},
|
|
||||||
common::mounting::Volume::Entity(uid) => Ok(server
|
|
||||||
.state
|
|
||||||
.ecs()
|
|
||||||
.read_resource::<IdMaps>()
|
|
||||||
.uid_entity(uid)?),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.unwrap_or(Ok(entity))?;
|
|
||||||
|
|
||||||
let mut maybe_pos = None;
|
|
||||||
|
|
||||||
let res = server
|
|
||||||
.state
|
|
||||||
.ecs()
|
|
||||||
.write_storage::<comp::Pos>()
|
|
||||||
.get_mut(entity)
|
|
||||||
.map(|pos| {
|
|
||||||
let res = f(pos);
|
|
||||||
maybe_pos = Some(pos.0);
|
|
||||||
res
|
|
||||||
})
|
|
||||||
.ok_or_else(|| format!("Cannot get position for {:?}!", descriptor));
|
|
||||||
|
|
||||||
if let Some(pos) = maybe_pos {
|
|
||||||
if server
|
|
||||||
.state
|
|
||||||
.ecs()
|
|
||||||
.read_storage::<Presence>()
|
|
||||||
.get(entity)
|
|
||||||
.map(|presence| presence.kind == PresenceKind::Spectator)
|
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
|
||||||
server.notify_client(entity, ServerGeneral::SpectatePosition(pos));
|
|
||||||
} else {
|
|
||||||
server
|
|
||||||
.state
|
|
||||||
.ecs()
|
|
||||||
.write_storage::<comp::ForceUpdate>()
|
|
||||||
.get_mut(entity)
|
|
||||||
.map(|force_update| force_update.update());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
fn insert_or_replace_component<C: specs::Component>(
|
fn insert_or_replace_component<C: specs::Component>(
|
||||||
server: &mut Server,
|
server: &mut Server,
|
||||||
entity: EcsEntity,
|
entity: EcsEntity,
|
||||||
@ -859,7 +775,9 @@ fn handle_jump(
|
|||||||
) -> CmdResult<()> {
|
) -> CmdResult<()> {
|
||||||
if let (Some(x), Some(y), Some(z), dismount_volume) = parse_cmd_args!(args, f32, f32, f32, bool)
|
if let (Some(x), Some(y), Some(z), dismount_volume) = parse_cmd_args!(args, f32, f32, f32, bool)
|
||||||
{
|
{
|
||||||
position_mut(server, target, "target", dismount_volume, |current_pos| {
|
server
|
||||||
|
.state
|
||||||
|
.position_mut(target, dismount_volume, |current_pos| {
|
||||||
current_pos.0 += Vec3::new(x, y, z)
|
current_pos.0 += Vec3::new(x, y, z)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@ -876,7 +794,9 @@ fn handle_goto(
|
|||||||
) -> CmdResult<()> {
|
) -> CmdResult<()> {
|
||||||
if let (Some(x), Some(y), Some(z), dismount_volume) = parse_cmd_args!(args, f32, f32, f32, bool)
|
if let (Some(x), Some(y), Some(z), dismount_volume) = parse_cmd_args!(args, f32, f32, f32, bool)
|
||||||
{
|
{
|
||||||
position_mut(server, target, "target", dismount_volume, |current_pos| {
|
server
|
||||||
|
.state
|
||||||
|
.position_mut(target, dismount_volume, |current_pos| {
|
||||||
current_pos.0 = Vec3::new(x, y, z)
|
current_pos.0 = Vec3::new(x, y, z)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@ -911,7 +831,9 @@ fn handle_site(
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
position_mut(server, target, "target", dismount_volume, |current_pos| {
|
server
|
||||||
|
.state
|
||||||
|
.position_mut(target, dismount_volume, |current_pos| {
|
||||||
current_pos.0 = site_pos
|
current_pos.0 = site_pos
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@ -936,7 +858,9 @@ fn handle_respawn(
|
|||||||
.ok_or("No waypoint set")?
|
.ok_or("No waypoint set")?
|
||||||
.get_pos();
|
.get_pos();
|
||||||
|
|
||||||
position_mut(server, target, "target", Some(true), |current_pos| {
|
server
|
||||||
|
.state
|
||||||
|
.position_mut(target, Some(true), |current_pos| {
|
||||||
current_pos.0 = waypoint;
|
current_pos.0 = waypoint;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1308,7 +1232,9 @@ fn handle_tp(
|
|||||||
return Err(action.help_string());
|
return Err(action.help_string());
|
||||||
};
|
};
|
||||||
let player_pos = position(server, player, "player")?;
|
let player_pos = position(server, player, "player")?;
|
||||||
position_mut(server, target, "target", dismount_volume, |target_pos| {
|
server
|
||||||
|
.state
|
||||||
|
.position_mut(target, dismount_volume, |target_pos| {
|
||||||
*target_pos = player_pos
|
*target_pos = player_pos
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1338,7 +1264,9 @@ fn handle_rtsim_tp(
|
|||||||
} else {
|
} else {
|
||||||
return Err(action.help_string());
|
return Err(action.help_string());
|
||||||
};
|
};
|
||||||
position_mut(server, target, "target", dismount_volume, |target_pos| {
|
server
|
||||||
|
.state
|
||||||
|
.position_mut(target, dismount_volume, |target_pos| {
|
||||||
target_pos.0 = pos;
|
target_pos.0 = pos;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -2058,10 +1986,10 @@ fn handle_spawn_portal(
|
|||||||
let buildup_time = Secs(buildup_time.unwrap_or(7.));
|
let buildup_time = Secs(buildup_time.unwrap_or(7.));
|
||||||
server
|
server
|
||||||
.state
|
.state
|
||||||
.create_teleporter(pos, comp::Teleporter {
|
.create_teleporter(pos, PortalData {
|
||||||
target: Vec3::new(x, y, z),
|
target: Vec3::new(x, y, z),
|
||||||
requires_no_aggro,
|
|
||||||
buildup_time,
|
buildup_time,
|
||||||
|
requires_no_aggro,
|
||||||
})
|
})
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@ -4112,7 +4040,7 @@ fn handle_location(
|
|||||||
if let Some(name) = parse_cmd_args!(args, String) {
|
if let Some(name) = parse_cmd_args!(args, String) {
|
||||||
let loc = server.state.ecs().read_resource::<Locations>().get(&name);
|
let loc = server.state.ecs().read_resource::<Locations>().get(&name);
|
||||||
match loc {
|
match loc {
|
||||||
Ok(loc) => position_mut(server, target, "target", Some(true), |target_pos| {
|
Ok(loc) => server.state.position_mut(target, Some(true), |target_pos| {
|
||||||
target_pos.0 = loc;
|
target_pos.0 = loc;
|
||||||
}),
|
}),
|
||||||
Err(e) => Err(e.to_string()),
|
Err(e) => Err(e.to_string()),
|
||||||
|
@ -9,9 +9,10 @@ use common::{
|
|||||||
aura::{Aura, AuraKind, AuraTarget},
|
aura::{Aura, AuraKind, AuraTarget},
|
||||||
beam,
|
beam,
|
||||||
buff::{BuffCategory, BuffData, BuffKind, BuffSource},
|
buff::{BuffCategory, BuffData, BuffKind, BuffSource},
|
||||||
|
misc::PortalData,
|
||||||
ship::figuredata::VOXEL_COLLIDER_MANIFEST,
|
ship::figuredata::VOXEL_COLLIDER_MANIFEST,
|
||||||
shockwave, Alignment, BehaviorCapability, Body, ItemDrops, LightEmitter, Object, Ori, Pos,
|
shockwave, Alignment, BehaviorCapability, Body, ItemDrops, LightEmitter, Object, Ori, Pos,
|
||||||
Projectile, Teleporter, TradingBehavior, Vel, WaypointArea,
|
Projectile, TradingBehavior, Vel, WaypointArea,
|
||||||
},
|
},
|
||||||
event::{EventBus, NpcBuilder, UpdateCharacterMetadata},
|
event::{EventBus, NpcBuilder, UpdateCharacterMetadata},
|
||||||
mounting::{Mounting, Volume, VolumeMounting, VolumePos},
|
mounting::{Mounting, Volume, VolumeMounting, VolumePos},
|
||||||
@ -419,9 +420,9 @@ pub fn handle_create_waypoint(server: &mut Server, pos: Vec3<f32>) {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_create_teleporter(server: &mut Server, pos: Vec3<f32>, teleporter: Teleporter) {
|
pub fn handle_create_teleporter(server: &mut Server, pos: Vec3<f32>, portal: PortalData) {
|
||||||
server
|
server
|
||||||
.state
|
.state
|
||||||
.create_teleporter(comp::Pos(pos), teleporter)
|
.create_teleporter(comp::Pos(pos), portal)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use crate::{
|
|||||||
BuffKind, BuffSource, PhysicsState,
|
BuffKind, BuffSource, PhysicsState,
|
||||||
},
|
},
|
||||||
rtsim,
|
rtsim,
|
||||||
sys::{teleporter::TELEPORT_RADIUS, terrain::SAFE_ZONE_RADIUS},
|
sys::terrain::SAFE_ZONE_RADIUS,
|
||||||
Server, SpawnPoint, StateExt,
|
Server, SpawnPoint, StateExt,
|
||||||
};
|
};
|
||||||
use authc::Uuid;
|
use authc::Uuid;
|
||||||
@ -22,8 +22,9 @@ use common::{
|
|||||||
item::flatten_counted_items,
|
item::flatten_counted_items,
|
||||||
loot_owner::LootOwnerKind,
|
loot_owner::LootOwnerKind,
|
||||||
Alignment, Auras, Body, CharacterState, Energy, Group, Health, HealthChange, Inventory,
|
Alignment, Auras, Body, CharacterState, Energy, Group, Health, HealthChange, Inventory,
|
||||||
Player, Poise, Pos, SkillSet, Stats,
|
Object, Player, Poise, Pos, SkillSet, Stats,
|
||||||
},
|
},
|
||||||
|
consts::TELEPORTER_RADIUS,
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
lottery::distribute_many,
|
lottery::distribute_many,
|
||||||
outcome::{HealthChangeInfo, Outcome},
|
outcome::{HealthChangeInfo, Outcome},
|
||||||
@ -43,7 +44,7 @@ use hashbrown::HashSet;
|
|||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use specs::{join::Join, Builder, Entity as EcsEntity, Entity, WorldExt};
|
use specs::{join::Join, Builder, Entity as EcsEntity, Entity, WorldExt};
|
||||||
use std::{collections::HashMap, iter, sync::Arc, time::Duration};
|
use std::{collections::HashMap, iter, sync::Arc, time::Duration};
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error, warn};
|
||||||
use vek::{Vec2, Vec3};
|
use vek::{Vec2, Vec3};
|
||||||
|
|
||||||
#[derive(Hash, Eq, PartialEq)]
|
#[derive(Hash, Eq, PartialEq)]
|
||||||
@ -1658,16 +1659,12 @@ pub fn handle_remove_light_emitter(server: &mut Server, entity: EcsEntity) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_teleport_to_position(server: &mut Server, entity: EcsEntity, position: Vec3<f32>) {
|
pub fn handle_teleport_to_position(server: &mut Server, entity: EcsEntity, position: Vec3<f32>) {
|
||||||
let ecs = server.state.ecs();
|
if let Err(error) = server
|
||||||
|
.state
|
||||||
ecs.write_storage::<comp::Pos>()
|
.position_mut(entity, Some(true), |pos| pos.0 = position)
|
||||||
.get_mut(entity)
|
{
|
||||||
.map(|old_position| {
|
warn!("Failed to teleport entity: {error}");
|
||||||
old_position.0 = position;
|
}
|
||||||
});
|
|
||||||
ecs.write_storage::<comp::ForceUpdate>()
|
|
||||||
.get_mut(entity)
|
|
||||||
.map(|forced_update| forced_update.update());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_start_teleporting(server: &mut Server, entity: EcsEntity, portal: EcsEntity) {
|
pub fn handle_start_teleporting(server: &mut Server, entity: EcsEntity, portal: EcsEntity) {
|
||||||
@ -1681,15 +1678,20 @@ pub fn handle_start_teleporting(server: &mut Server, entity: EcsEntity, portal:
|
|||||||
.flatten()
|
.flatten()
|
||||||
.zip(positions.get(portal))
|
.zip(positions.get(portal))
|
||||||
.filter(|(entity_pos, portal_pos)| {
|
.filter(|(entity_pos, portal_pos)| {
|
||||||
entity_pos.0.distance_squared(portal_pos.0) <= TELEPORT_RADIUS.powi(2)
|
entity_pos.0.distance_squared(portal_pos.0) <= TELEPORTER_RADIUS.powi(2)
|
||||||
})
|
})
|
||||||
.and_then(|(_, _)| {
|
.and_then(|(_, _)| {
|
||||||
Some(
|
Some(
|
||||||
now + ecs
|
now + ecs
|
||||||
.read_storage::<comp::Teleporter>()
|
.read_storage::<comp::Object>()
|
||||||
.get(portal)?
|
.get(portal)
|
||||||
.buildup_time
|
.and_then(|object| {
|
||||||
.0,
|
if let Object::Portal { buildup_time, .. } = object {
|
||||||
|
Some(buildup_time.0)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})?,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
|
@ -213,8 +213,8 @@ impl Server {
|
|||||||
driver,
|
driver,
|
||||||
} => handle_create_ship(self, pos, ori, ship, rtsim_entity, driver, Vec::new()),
|
} => handle_create_ship(self, pos, ori, ship, rtsim_entity, driver, Vec::new()),
|
||||||
ServerEvent::CreateWaypoint(pos) => handle_create_waypoint(self, pos),
|
ServerEvent::CreateWaypoint(pos) => handle_create_waypoint(self, pos),
|
||||||
ServerEvent::CreateTeleporter(pos, teleporter) => {
|
ServerEvent::CreateTeleporter(pos, portal) => {
|
||||||
handle_create_teleporter(self, pos, teleporter)
|
handle_create_teleporter(self, pos, portal)
|
||||||
},
|
},
|
||||||
ServerEvent::ClientDisconnect(entity, reason) => {
|
ServerEvent::ClientDisconnect(entity, reason) => {
|
||||||
frontend_events.push(handle_client_disconnect(self, entity, reason, false))
|
frontend_events.push(handle_client_disconnect(self, entity, reason, false))
|
||||||
|
@ -18,13 +18,14 @@ use common::{
|
|||||||
comp::{
|
comp::{
|
||||||
self,
|
self,
|
||||||
item::{ItemKind, MaterialStatManifest},
|
item::{ItemKind, MaterialStatManifest},
|
||||||
|
misc::PortalData,
|
||||||
object,
|
object,
|
||||||
skills::{GeneralSkill, Skill},
|
skills::{GeneralSkill, Skill},
|
||||||
ChatType, Group, Inventory, Item, LootOwner, Object, Player, Poise, Presence, PresenceKind,
|
ChatType, Group, Inventory, Item, LootOwner, Object, Player, Poise, Presence, PresenceKind,
|
||||||
},
|
},
|
||||||
effect::Effect,
|
effect::Effect,
|
||||||
link::{Link, LinkHandle},
|
link::{Is, Link, LinkHandle},
|
||||||
mounting::{Mounting, VolumeMounting},
|
mounting::{Mounting, Rider, VolumeMounting, VolumeRider},
|
||||||
resources::{Secs, Time, TimeOfDay},
|
resources::{Secs, Time, TimeOfDay},
|
||||||
rtsim::{Actor, RtSimEntity},
|
rtsim::{Actor, RtSimEntity},
|
||||||
slowjob::SlowJobPool,
|
slowjob::SlowJobPool,
|
||||||
@ -118,11 +119,7 @@ pub trait StateExt {
|
|||||||
/// Creates a teleporter entity, which allows players to teleport to the
|
/// Creates a teleporter entity, which allows players to teleport to the
|
||||||
/// `target` position. You might want to require the teleporting entity
|
/// `target` position. You might want to require the teleporting entity
|
||||||
/// to not have agro for teleporting.
|
/// to not have agro for teleporting.
|
||||||
fn create_teleporter(
|
fn create_teleporter(&mut self, pos: comp::Pos, portal: PortalData) -> EcsEntityBuilder;
|
||||||
&mut self,
|
|
||||||
pos: comp::Pos,
|
|
||||||
teleporter: comp::Teleporter,
|
|
||||||
) -> EcsEntityBuilder;
|
|
||||||
/// Insert common/default components for a new character joining the server
|
/// Insert common/default components for a new character joining the server
|
||||||
fn initialize_character_data(
|
fn initialize_character_data(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -161,6 +158,12 @@ pub trait StateExt {
|
|||||||
) -> Result<(), specs::error::WrongGeneration>;
|
) -> Result<(), specs::error::WrongGeneration>;
|
||||||
/// Get the given entity as an [`Actor`], if it is one.
|
/// Get the given entity as an [`Actor`], if it is one.
|
||||||
fn entity_as_actor(&self, entity: EcsEntity) -> Option<Actor>;
|
fn entity_as_actor(&self, entity: EcsEntity) -> Option<Actor>;
|
||||||
|
fn position_mut<T>(
|
||||||
|
&mut self,
|
||||||
|
entity: EcsEntity,
|
||||||
|
dismount_volume: Option<bool>,
|
||||||
|
f: impl for<'a> FnOnce(&'a mut comp::Pos) -> T,
|
||||||
|
) -> Result<T, String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StateExt for State {
|
impl StateExt for State {
|
||||||
@ -616,14 +619,10 @@ impl StateExt for State {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_teleporter(
|
fn create_teleporter(&mut self, pos: comp::Pos, portal: PortalData) -> EcsEntityBuilder {
|
||||||
&mut self,
|
|
||||||
pos: comp::Pos,
|
|
||||||
teleporter: comp::Teleporter,
|
|
||||||
) -> EcsEntityBuilder {
|
|
||||||
self.create_object(pos, object::Body::Portal)
|
self.create_object(pos, object::Body::Portal)
|
||||||
.with(comp::Immovable)
|
.with(comp::Immovable)
|
||||||
.with(teleporter)
|
.with(comp::Object::from(portal))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize_character_data(
|
fn initialize_character_data(
|
||||||
@ -1244,6 +1243,76 @@ impl StateExt for State {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn position_mut<T>(
|
||||||
|
&mut self,
|
||||||
|
entity: EcsEntity,
|
||||||
|
dismount_volume: Option<bool>,
|
||||||
|
f: impl for<'a> FnOnce(&'a mut comp::Pos) -> T,
|
||||||
|
) -> Result<T, String> {
|
||||||
|
if dismount_volume.unwrap_or(true) {
|
||||||
|
self.ecs().write_storage::<Is<VolumeRider>>().remove(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
let entity = self
|
||||||
|
.read_storage::<Is<Rider>>()
|
||||||
|
.get(entity)
|
||||||
|
.and_then(|is_rider| {
|
||||||
|
self.ecs()
|
||||||
|
.read_resource::<IdMaps>()
|
||||||
|
.uid_entity(is_rider.mount)
|
||||||
|
})
|
||||||
|
.map(Ok)
|
||||||
|
.or_else(|| {
|
||||||
|
self.read_storage::<Is<VolumeRider>>()
|
||||||
|
.get(entity)
|
||||||
|
.and_then(|volume_rider| {
|
||||||
|
Some(match volume_rider.pos.kind {
|
||||||
|
common::mounting::Volume::Terrain => {
|
||||||
|
Err("Tried to move the world.".to_string())
|
||||||
|
},
|
||||||
|
common::mounting::Volume::Entity(uid) => {
|
||||||
|
Ok(self.ecs().read_resource::<IdMaps>().uid_entity(uid)?)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.unwrap_or(Ok(entity))?;
|
||||||
|
|
||||||
|
let mut maybe_pos = None;
|
||||||
|
|
||||||
|
let res = self
|
||||||
|
.ecs()
|
||||||
|
.write_storage::<comp::Pos>()
|
||||||
|
.get_mut(entity)
|
||||||
|
.map(|pos| {
|
||||||
|
let res = f(pos);
|
||||||
|
maybe_pos = Some(pos.0);
|
||||||
|
res
|
||||||
|
})
|
||||||
|
.ok_or_else(|| "Cannot get position for entity!".to_string());
|
||||||
|
|
||||||
|
if let Some(pos) = maybe_pos {
|
||||||
|
if self
|
||||||
|
.ecs()
|
||||||
|
.read_storage::<Presence>()
|
||||||
|
.get(entity)
|
||||||
|
.map(|presence| presence.kind == PresenceKind::Spectator)
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
self.read_storage::<Client>().get(entity).map(|client| {
|
||||||
|
client.send_fallible(ServerGeneral::SpectatePosition(pos));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
self.ecs()
|
||||||
|
.write_storage::<comp::ForceUpdate>()
|
||||||
|
.get_mut(entity)
|
||||||
|
.map(|force_update| force_update.update());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_to_group(g: &Group, ecs: &specs::World, msg: &comp::ChatMsg) {
|
fn send_to_group(g: &Group, ecs: &specs::World, msg: &comp::ChatMsg) {
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
use common::{
|
use common::{
|
||||||
comp::{Object, PhysicsState, Pos, Vel},
|
comp::{object, Body, Object, PhysicsState, Pos, Teleporting, Vel},
|
||||||
|
consts::TELEPORTER_RADIUS,
|
||||||
effect::Effect,
|
effect::Effect,
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
resources::{DeltaTime, Time},
|
resources::{DeltaTime, Time},
|
||||||
Damage, DamageKind, DamageSource, Explosion, RadiusEffect,
|
CachedSpatialGrid, Damage, DamageKind, DamageSource, Explosion, RadiusEffect,
|
||||||
};
|
};
|
||||||
use common_ecs::{Job, Origin, Phase, System};
|
use common_ecs::{Job, Origin, Phase, System};
|
||||||
use specs::{Entities, Join, Read, ReadStorage};
|
use specs::{Entities, Join, Read, ReadStorage};
|
||||||
@ -18,10 +19,13 @@ impl<'a> System<'a> for Sys {
|
|||||||
Read<'a, DeltaTime>,
|
Read<'a, DeltaTime>,
|
||||||
Read<'a, Time>,
|
Read<'a, Time>,
|
||||||
Read<'a, EventBus<ServerEvent>>,
|
Read<'a, EventBus<ServerEvent>>,
|
||||||
|
Read<'a, CachedSpatialGrid>,
|
||||||
ReadStorage<'a, Pos>,
|
ReadStorage<'a, Pos>,
|
||||||
ReadStorage<'a, Vel>,
|
ReadStorage<'a, Vel>,
|
||||||
ReadStorage<'a, PhysicsState>,
|
ReadStorage<'a, PhysicsState>,
|
||||||
ReadStorage<'a, Object>,
|
ReadStorage<'a, Object>,
|
||||||
|
ReadStorage<'a, Body>,
|
||||||
|
ReadStorage<'a, Teleporting>,
|
||||||
);
|
);
|
||||||
|
|
||||||
const NAME: &'static str = "object";
|
const NAME: &'static str = "object";
|
||||||
@ -30,17 +34,30 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
_job: &mut Job<Self>,
|
_job: &mut Job<Self>,
|
||||||
(entities, _dt, time, server_bus, positions, velocities, physics_states, objects): Self::SystemData,
|
(
|
||||||
|
entities,
|
||||||
|
_dt,
|
||||||
|
time,
|
||||||
|
server_bus,
|
||||||
|
spatial_grid,
|
||||||
|
positions,
|
||||||
|
velocities,
|
||||||
|
physics_states,
|
||||||
|
objects,
|
||||||
|
bodies,
|
||||||
|
teleporting,
|
||||||
|
): Self::SystemData,
|
||||||
) {
|
) {
|
||||||
let mut server_emitter = server_bus.emitter();
|
let mut server_emitter = server_bus.emitter();
|
||||||
|
|
||||||
// Objects
|
// Objects
|
||||||
for (entity, pos, vel, physics, object) in (
|
for (entity, pos, vel, physics, object, body) in (
|
||||||
&entities,
|
&entities,
|
||||||
&positions,
|
&positions,
|
||||||
&velocities,
|
&velocities,
|
||||||
&physics_states,
|
&physics_states,
|
||||||
&objects,
|
&objects,
|
||||||
|
&bodies,
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
{
|
{
|
||||||
@ -73,7 +90,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
const ENABLE_RECURSIVE_FIREWORKS: bool = true;
|
const ENABLE_RECURSIVE_FIREWORKS: bool = true;
|
||||||
if ENABLE_RECURSIVE_FIREWORKS {
|
if ENABLE_RECURSIVE_FIREWORKS {
|
||||||
use common::{
|
use common::{
|
||||||
comp::{object, Body, LightEmitter, Projectile},
|
comp::{LightEmitter, Projectile},
|
||||||
util::Dir,
|
util::Dir,
|
||||||
};
|
};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
@ -169,6 +186,31 @@ impl<'a> System<'a> for Sys {
|
|||||||
server_emitter.emit(ServerEvent::Delete(entity));
|
server_emitter.emit(ServerEvent::Delete(entity));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Object::Portal { .. } => {
|
||||||
|
let is_active = spatial_grid
|
||||||
|
.0
|
||||||
|
.in_circle_aabr(pos.0.xy(), TELEPORTER_RADIUS)
|
||||||
|
.any(|entity| {
|
||||||
|
(&positions, &teleporting)
|
||||||
|
.join()
|
||||||
|
.get(entity, &entities)
|
||||||
|
.map_or(false, |(teleporter_pos, _)| {
|
||||||
|
pos.0.distance_squared(teleporter_pos.0)
|
||||||
|
<= TELEPORTER_RADIUS.powi(2)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (*body == Body::Object(object::Body::PortalActive)) != is_active {
|
||||||
|
server_bus.emit_now(ServerEvent::ChangeBody {
|
||||||
|
entity,
|
||||||
|
new_body: Body::Object(if is_active {
|
||||||
|
object::Body::PortalActive
|
||||||
|
} else {
|
||||||
|
object::Body::Portal
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use common::{
|
use common::{
|
||||||
comp::{object, Agent, Alignment, Body, CharacterState, Pos, Teleporter, Teleporting},
|
comp::{Agent, Alignment, CharacterState, Object, Pos, Teleporting},
|
||||||
|
consts::TELEPORTER_RADIUS,
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
resources::Time,
|
resources::Time,
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
@ -9,7 +10,6 @@ use common_ecs::{Origin, Phase, System};
|
|||||||
use specs::{Entities, Join, Read, ReadStorage, WriteStorage};
|
use specs::{Entities, Join, Read, ReadStorage, WriteStorage};
|
||||||
use vek::Vec3;
|
use vek::Vec3;
|
||||||
|
|
||||||
pub const TELEPORT_RADIUS: f32 = 3.;
|
|
||||||
const MAX_AGGRO_DIST: f32 = 200.; // If an entity further than this is aggroed at a player, the portal will still work
|
const MAX_AGGRO_DIST: f32 = 200.; // If an entity further than this is aggroed at a player, the portal will still work
|
||||||
const PET_TELEPORT_RADIUS: f32 = 20.;
|
const PET_TELEPORT_RADIUS: f32 = 20.;
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ const PET_TELEPORT_RADIUS: f32 = 20.;
|
|||||||
pub struct Sys;
|
pub struct Sys;
|
||||||
|
|
||||||
fn in_portal_range(player_pos: Vec3<f32>, portal_pos: Vec3<f32>) -> bool {
|
fn in_portal_range(player_pos: Vec3<f32>, portal_pos: Vec3<f32>) -> bool {
|
||||||
player_pos.distance_squared(portal_pos) <= (TELEPORT_RADIUS).powi(2)
|
player_pos.distance_squared(portal_pos) <= TELEPORTER_RADIUS.powi(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> System<'a> for Sys {
|
impl<'a> System<'a> for Sys {
|
||||||
@ -25,11 +25,10 @@ impl<'a> System<'a> for Sys {
|
|||||||
Entities<'a>,
|
Entities<'a>,
|
||||||
ReadStorage<'a, Pos>,
|
ReadStorage<'a, Pos>,
|
||||||
ReadStorage<'a, Uid>,
|
ReadStorage<'a, Uid>,
|
||||||
ReadStorage<'a, Teleporter>,
|
|
||||||
ReadStorage<'a, Alignment>,
|
ReadStorage<'a, Alignment>,
|
||||||
ReadStorage<'a, Agent>,
|
ReadStorage<'a, Agent>,
|
||||||
|
ReadStorage<'a, Object>,
|
||||||
WriteStorage<'a, Teleporting>,
|
WriteStorage<'a, Teleporting>,
|
||||||
ReadStorage<'a, Body>,
|
|
||||||
ReadStorage<'a, CharacterState>,
|
ReadStorage<'a, CharacterState>,
|
||||||
Read<'a, CachedSpatialGrid>,
|
Read<'a, CachedSpatialGrid>,
|
||||||
Read<'a, Time>,
|
Read<'a, Time>,
|
||||||
@ -46,11 +45,10 @@ impl<'a> System<'a> for Sys {
|
|||||||
entities,
|
entities,
|
||||||
positions,
|
positions,
|
||||||
uids,
|
uids,
|
||||||
teleporters,
|
|
||||||
alignments,
|
alignments,
|
||||||
agent,
|
agent,
|
||||||
|
objects,
|
||||||
mut teleporting,
|
mut teleporting,
|
||||||
bodies,
|
|
||||||
character_states,
|
character_states,
|
||||||
spatial_grid,
|
spatial_grid,
|
||||||
time,
|
time,
|
||||||
@ -72,31 +70,6 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
let mut cancel_teleporting = Vec::new();
|
let mut cancel_teleporting = Vec::new();
|
||||||
|
|
||||||
for (portal_entity, teleporter_pos, body, _) in
|
|
||||||
(&entities, &positions, &bodies, &teleporters).join()
|
|
||||||
{
|
|
||||||
let is_active = spatial_grid
|
|
||||||
.0
|
|
||||||
.in_circle_aabr(teleporter_pos.0.xy(), TELEPORT_RADIUS)
|
|
||||||
.any(|entity| {
|
|
||||||
(&positions, &teleporting)
|
|
||||||
.join()
|
|
||||||
.get(entity, &entities)
|
|
||||||
.map_or(false, |(pos, _)| in_portal_range(pos.0, teleporter_pos.0))
|
|
||||||
});
|
|
||||||
|
|
||||||
if (*body == Body::Object(object::Body::PortalActive)) != is_active {
|
|
||||||
event_bus.emit_now(ServerEvent::ChangeBody {
|
|
||||||
entity: portal_entity,
|
|
||||||
new_body: Body::Object(if is_active {
|
|
||||||
object::Body::PortalActive
|
|
||||||
} else {
|
|
||||||
object::Body::Portal
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (entity, uid, position, teleporting, character_state) in (
|
for (entity, uid, position, teleporting, character_state) in (
|
||||||
&entities,
|
&entities,
|
||||||
&uids,
|
&uids,
|
||||||
@ -107,14 +80,16 @@ impl<'a> System<'a> for Sys {
|
|||||||
.join()
|
.join()
|
||||||
{
|
{
|
||||||
let portal_pos = positions.get(teleporting.portal);
|
let portal_pos = positions.get(teleporting.portal);
|
||||||
let Some(teleporter) = teleporters.get(teleporting.portal) else {
|
let Some(Object::Portal { target, requires_no_aggro, .. }) = objects
|
||||||
|
.get(teleporting.portal)
|
||||||
|
else {
|
||||||
cancel_teleporting.push(entity);
|
cancel_teleporting.push(entity);
|
||||||
continue
|
continue
|
||||||
};
|
};
|
||||||
|
|
||||||
if portal_pos.map_or(true, |portal_pos| {
|
if portal_pos.map_or(true, |portal_pos| {
|
||||||
!in_portal_range(position.0, portal_pos.0)
|
!in_portal_range(position.0, portal_pos.0)
|
||||||
}) || (teleporter.requires_no_aggro && check_aggro(entity, position.0))
|
}) || (*requires_no_aggro && check_aggro(entity, position.0))
|
||||||
|| !matches!(
|
|| !matches!(
|
||||||
character_state,
|
character_state,
|
||||||
CharacterState::Idle(_) | CharacterState::Wielding(_)
|
CharacterState::Idle(_) | CharacterState::Wielding(_)
|
||||||
@ -142,7 +117,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
cancel_teleporting.push(entity);
|
cancel_teleporting.push(entity);
|
||||||
event_bus.emit_now(ServerEvent::TeleportToPosition {
|
event_bus.emit_now(ServerEvent::TeleportToPosition {
|
||||||
entity,
|
entity,
|
||||||
position: teleporter.target,
|
position: *target,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,8 @@ use crate::{
|
|||||||
use common::{
|
use common::{
|
||||||
calendar::Calendar,
|
calendar::Calendar,
|
||||||
comp::{
|
comp::{
|
||||||
self, agent, biped_small, bird_medium, skillset::skills, BehaviorCapability, ForceUpdate,
|
self, agent, biped_small, bird_medium, misc::PortalData, skillset::skills,
|
||||||
Pos, Presence, Teleporter, Waypoint,
|
BehaviorCapability, ForceUpdate, Pos, Presence, Waypoint,
|
||||||
},
|
},
|
||||||
event::{EventBus, NpcBuilder, ServerEvent},
|
event::{EventBus, NpcBuilder, ServerEvent},
|
||||||
generation::{EntityInfo, SpecialEntity},
|
generation::{EntityInfo, SpecialEntity},
|
||||||
@ -418,7 +418,7 @@ pub enum NpcData {
|
|||||||
loot: LootSpec<String>,
|
loot: LootSpec<String>,
|
||||||
},
|
},
|
||||||
Waypoint(Vec3<f32>),
|
Waypoint(Vec3<f32>),
|
||||||
Teleporter(Vec3<f32>, Teleporter),
|
Teleporter(Vec3<f32>, PortalData),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NpcData {
|
impl NpcData {
|
||||||
|
@ -2233,12 +2233,13 @@ impl Hud {
|
|||||||
.map_or(Vec3::zero(), |e| e.0);
|
.map_or(Vec3::zero(), |e| e.0);
|
||||||
let over_pos = pos + Vec3::unit_z() * 1.5;
|
let over_pos = pos + Vec3::unit_z() * 1.5;
|
||||||
|
|
||||||
let is_campfire = body.is_campfire();
|
|
||||||
overitem::Overitem::new(
|
overitem::Overitem::new(
|
||||||
i18n.get_msg(if is_campfire {
|
i18n.get_msg(if body.is_campfire() {
|
||||||
"hud-crafting-campfire"
|
"hud-crafting-campfire"
|
||||||
} else {
|
} else if body.is_portal() {
|
||||||
"hud-portal"
|
"hud-portal"
|
||||||
|
} else {
|
||||||
|
"hud-use"
|
||||||
}),
|
}),
|
||||||
overitem::TEXT_COLOR,
|
overitem::TEXT_COLOR,
|
||||||
pos.distance_squared(player_pos),
|
pos.distance_squared(player_pos),
|
||||||
@ -2250,7 +2251,13 @@ impl Hud {
|
|||||||
&global_state.window.key_layout,
|
&global_state.window.key_layout,
|
||||||
vec![(
|
vec![(
|
||||||
Some(GameInput::Interact),
|
Some(GameInput::Interact),
|
||||||
i18n.get_msg(if is_campfire { "hud-sit" } else { "hud-activate" }).to_string(),
|
i18n.get_msg(if body.is_campfire() {
|
||||||
|
"hud-sit"
|
||||||
|
} else if body.is_portal() {
|
||||||
|
"hud-activate"
|
||||||
|
} else {
|
||||||
|
"hud-use"
|
||||||
|
}).to_string(),
|
||||||
)],
|
)],
|
||||||
)
|
)
|
||||||
.x_y(0.0, 100.0)
|
.x_y(0.0, 100.0)
|
||||||
|
@ -10,7 +10,7 @@ use client::Client;
|
|||||||
use common::{
|
use common::{
|
||||||
comp,
|
comp,
|
||||||
comp::{ship::figuredata::VOXEL_COLLIDER_MANIFEST, tool::ToolKind, Collider, Content},
|
comp::{ship::figuredata::VOXEL_COLLIDER_MANIFEST, tool::ToolKind, Collider, Content},
|
||||||
consts::{MAX_PICKUP_RANGE, MAX_SPRITE_MOUNT_RANGE},
|
consts::{MAX_PICKUP_RANGE, MAX_SPRITE_MOUNT_RANGE, TELEPORTER_RADIUS},
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::{Mount, Rider, VolumePos, VolumeRider},
|
mounting::{Mount, Rider, VolumePos, VolumeRider},
|
||||||
terrain::{Block, TerrainGrid, UnlockKind},
|
terrain::{Block, TerrainGrid, UnlockKind},
|
||||||
@ -247,7 +247,7 @@ pub(super) fn select_interactable(
|
|||||||
// * Are not riding the player
|
// * Are not riding the player
|
||||||
let not_riding_player = is_rider
|
let not_riding_player = is_rider
|
||||||
.map_or(true, |is_rider| Some(&is_rider.mount) != uids.get(viewpoint_entity));
|
.map_or(true, |is_rider| Some(&is_rider.mount) != uids.get(viewpoint_entity));
|
||||||
let is_interactable = (b.is_campfire() || (b.is_portal() && (p.0.distance_squared(player_pos) <= 3f32.powi(2))) || has_stats_or_item.is_some()) && not_riding_player;
|
let is_interactable = (b.is_campfire() || (b.is_portal() && (p.0.distance_squared(player_pos) <= TELEPORTER_RADIUS.powi(2))) || has_stats_or_item.is_some()) && not_riding_player;
|
||||||
if !is_interactable {
|
if !is_interactable {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
@ -1883,24 +1883,23 @@ impl SiteKind {
|
|||||||
SiteKind::Dungeon => {
|
SiteKind::Dungeon => {
|
||||||
on_land() && {
|
on_land() && {
|
||||||
let land = Land::from_sim(sim);
|
let land = Land::from_sim(sim);
|
||||||
let loc = loc.map(|v| TerrainChunkSize::RECT_SIZE.y as i32 * v);
|
let loc = loc.cpos_to_wpos();
|
||||||
let dungeon_aabr = Aabr {
|
let dungeon_aabr = Aabr {
|
||||||
min: loc - Vec2::broadcast(200),
|
min: loc - Vec2::broadcast(200),
|
||||||
max: loc + Vec2::broadcast(200),
|
max: loc + Vec2::broadcast(200),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get shallow caves under the dungeon
|
// Make sure there are no shallow caves near the dungeon
|
||||||
let mut tunnels = cave::tunnels_at(loc, 1, &land)
|
let collides_with_cave = cave::tunnels_at(loc, 1, &land)
|
||||||
.chain(cave::tunnels_at(loc, 2, &land))
|
.chain(cave::tunnels_at(loc, 2, &land))
|
||||||
.filter(|tunnel| {
|
.all(|tunnel| {
|
||||||
dungeon_aabr.collides_with_aabr(Aabr {
|
!dungeon_aabr.collides_with_aabr(Aabr {
|
||||||
min: tunnel.nodes().0.wpos,
|
min: tunnel.nodes().0.wpos,
|
||||||
max: tunnel.nodes().1.wpos,
|
max: tunnel.nodes().1.wpos,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
// Make sure there are no shallow caves near the dungeon
|
collides_with_cave
|
||||||
tunnels.next().is_none()
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
SiteKind::Refactor | SiteKind::Settlement => suitable_for_town(),
|
SiteKind::Refactor | SiteKind::Settlement => suitable_for_town(),
|
||||||
|
@ -9,7 +9,7 @@ use crate::{
|
|||||||
use common::{
|
use common::{
|
||||||
assets::{self, AssetExt, AssetHandle},
|
assets::{self, AssetExt, AssetHandle},
|
||||||
astar::Astar,
|
astar::Astar,
|
||||||
comp::Teleporter,
|
comp::misc::PortalData,
|
||||||
generation::{ChunkSupplement, EntityInfo, SpecialEntity},
|
generation::{ChunkSupplement, EntityInfo, SpecialEntity},
|
||||||
resources::Secs,
|
resources::Secs,
|
||||||
store::{Id, Store},
|
store::{Id, Store},
|
||||||
@ -666,14 +666,14 @@ impl Floor {
|
|||||||
|
|
||||||
// Move both a bit to the side to prevent teleportation loop, ideally we'd have the portals at another location
|
// Move both a bit to the side to prevent teleportation loop, ideally we'd have the portals at another location
|
||||||
supplement.add_entity(EntityInfo::at(top_pos).into_special(
|
supplement.add_entity(EntityInfo::at(top_pos).into_special(
|
||||||
SpecialEntity::Teleporter(Teleporter {
|
SpecialEntity::Teleporter(PortalData {
|
||||||
target: bottom_pos + Vec3::unit_x() * 5.,
|
target: bottom_pos + Vec3::unit_x() * 5.,
|
||||||
requires_no_aggro: true,
|
requires_no_aggro: true,
|
||||||
buildup_time: Secs(5.),
|
buildup_time: Secs(5.),
|
||||||
}),
|
}),
|
||||||
));
|
));
|
||||||
supplement.add_entity(EntityInfo::at(bottom_pos).into_special(
|
supplement.add_entity(EntityInfo::at(bottom_pos).into_special(
|
||||||
SpecialEntity::Teleporter(Teleporter {
|
SpecialEntity::Teleporter(PortalData {
|
||||||
target: top_pos + Vec3::unit_x() * 5.,
|
target: top_pos + Vec3::unit_x() * 5.,
|
||||||
requires_no_aggro: true,
|
requires_no_aggro: true,
|
||||||
buildup_time: Secs(5.),
|
buildup_time: Secs(5.),
|
||||||
|
Loading…
Reference in New Issue
Block a user