mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Fixed projectile collisions, hitboxes, better aiming
This commit is contained in:
parent
90735f1ef7
commit
6a4c5a05d0
@ -139,43 +139,62 @@ impl Body {
|
||||
pub fn radius(&self) -> f32 {
|
||||
// TODO: Improve these values (some might be reliant on more info in inner type)
|
||||
match self {
|
||||
Body::Humanoid(_) => 0.2,
|
||||
Body::QuadrupedSmall(_) => 0.3,
|
||||
Body::Humanoid(_) => 0.6,
|
||||
Body::QuadrupedSmall(_) => 0.6,
|
||||
Body::QuadrupedMedium(_) => 0.9,
|
||||
Body::Critter(_) => 0.2,
|
||||
Body::QuadrupedLow(_) => 1.0,
|
||||
Body::Critter(_) => 0.5,
|
||||
Body::BirdMedium(_) => 0.5,
|
||||
Body::FishMedium(_) => 0.5,
|
||||
Body::Dragon(_) => 2.5,
|
||||
Body::BirdSmall(_) => 0.2,
|
||||
Body::FishSmall(_) => 0.2,
|
||||
Body::BipedLarge(_) => 3.0,
|
||||
Body::Golem(_) => 2.5,
|
||||
Body::QuadrupedLow(_) => 1.0,
|
||||
Body::Object(_) => 0.3,
|
||||
Body::BirdSmall(_) => 0.4,
|
||||
Body::FishSmall(_) => 0.4,
|
||||
Body::BipedLarge(_) => 2.0,
|
||||
Body::Golem(_) => 1.75,
|
||||
Body::Object(_) => 0.4,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn height(&self) -> f32 {
|
||||
match self {
|
||||
Body::Humanoid(humanoid) => match humanoid.species {
|
||||
humanoid::Species::Danari => 0.8,
|
||||
humanoid::Species::Dwarf => 0.9,
|
||||
humanoid::Species::Orc => 1.14,
|
||||
humanoid::Species::Undead => 0.95,
|
||||
humanoid::Species::Danari => 0.5,
|
||||
humanoid::Species::Dwarf => 1.55,
|
||||
humanoid::Species::Orc => 1.95,
|
||||
_ => 1.8,
|
||||
},
|
||||
Body::QuadrupedSmall(body) => match body.species {
|
||||
quadruped_small::Species::Dodarock => 1.5,
|
||||
quadruped_small::Species::Holladon => 1.5,
|
||||
quadruped_small::Species::Truffler => 2.0,
|
||||
_ => 1.0,
|
||||
},
|
||||
Body::QuadrupedSmall(_) => 0.6,
|
||||
Body::QuadrupedMedium(_) => 0.5,
|
||||
Body::Critter(_) => 0.4,
|
||||
Body::BirdMedium(_) => 1.2,
|
||||
Body::FishMedium(_) => 1.0,
|
||||
Body::Dragon(_) => 5.0,
|
||||
Body::BirdSmall(_) => 0.4,
|
||||
Body::FishSmall(_) => 0.4,
|
||||
Body::BipedLarge(_) => 5.0,
|
||||
Body::Golem(_) => 5.0,
|
||||
Body::QuadrupedLow(_) => 0.5,
|
||||
Body::Object(_) => 0.6,
|
||||
Body::QuadrupedMedium(body) => match body.species {
|
||||
quadruped_medium::Species::Tarasque => 2.5,
|
||||
quadruped_medium::Species::Lion => 1.8,
|
||||
quadruped_medium::Species::Saber => 1.8,
|
||||
quadruped_medium::Species::Catoblepas => 2.8,
|
||||
_ => 1.6,
|
||||
},
|
||||
Body::QuadrupedLow(body) => match body.species {
|
||||
quadruped_low::Species::Monitor => 1.5,
|
||||
quadruped_low::Species::Tortoise => 2.0,
|
||||
quadruped_low::Species::Rocksnapper => 2.0,
|
||||
quadruped_low::Species::Maneater => 4.0,
|
||||
_ => 1.3,
|
||||
},
|
||||
Body::Critter(_) => 0.7,
|
||||
Body::BirdMedium(body) => match body.species {
|
||||
bird_medium::Species::Cockatrice => 1.8,
|
||||
_ => 1.1,
|
||||
},
|
||||
Body::FishMedium(_) => 1.1,
|
||||
Body::Dragon(_) => 20.0,
|
||||
Body::BirdSmall(_) => 1.1,
|
||||
Body::FishSmall(_) => 0.9,
|
||||
Body::BipedLarge(_) => 4.5,
|
||||
Body::Golem(_) => 5.8,
|
||||
Body::Object(_) => 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,6 +55,22 @@ pub enum Collider {
|
||||
Point,
|
||||
}
|
||||
|
||||
impl Collider {
|
||||
pub fn get_radius(&self) -> f32 {
|
||||
match self {
|
||||
Collider::Box { radius, .. } => *radius,
|
||||
Collider::Point => 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_z_limits(&self) -> (f32, f32) {
|
||||
match self {
|
||||
Collider::Box { z_min, z_max, .. } => (*z_min, *z_max),
|
||||
Collider::Point => (0.0, 0.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Collider {
|
||||
type Storage = FlaggedStorage<Self, IdvStorage<Self>>;
|
||||
}
|
||||
@ -74,16 +90,26 @@ impl Component for Sticky {
|
||||
}
|
||||
|
||||
// PhysicsState
|
||||
#[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct PhysicsState {
|
||||
pub on_ground: bool,
|
||||
pub on_ceiling: bool,
|
||||
pub on_wall: Option<Vec3<f32>>,
|
||||
pub touch_entity: Option<Uid>,
|
||||
pub touch_entities: Vec<Uid>,
|
||||
pub in_fluid: Option<f32>, // Depth
|
||||
}
|
||||
|
||||
impl PhysicsState {
|
||||
pub fn clear(&mut self) {
|
||||
// Avoid allocation overhead!
|
||||
let mut touch_entities = std::mem::take(&mut self.touch_entities);
|
||||
touch_entities.clear();
|
||||
*self = Self {
|
||||
touch_entities,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_surface(&self) -> Option<Vec3<f32>> {
|
||||
self.on_ground
|
||||
.then_some(-Vec3::unit_z())
|
||||
|
@ -33,7 +33,7 @@ impl CharacterBehavior for Data {
|
||||
}
|
||||
}
|
||||
|
||||
if self.buildup_duration != Duration::default() && data.physics.touch_entity.is_none() {
|
||||
if self.buildup_duration != Duration::default() && data.physics.touch_entities.is_empty() {
|
||||
// Build up (this will move you forward)
|
||||
update.vel.0 = Vec3::new(0.0, 0.0, update.vel.0.z)
|
||||
+ (update.vel.0 * Vec3::new(1.0, 1.0, 0.0)
|
||||
|
@ -113,7 +113,7 @@ impl CharacterBehavior for Data {
|
||||
|
||||
// Handling movement
|
||||
if stage_time_active < Duration::from_millis(STAGE_DURATION / 3) {
|
||||
let adjusted_accel = match (self.stage, data.physics.touch_entity.is_none()) {
|
||||
let adjusted_accel = match (self.stage, data.physics.touch_entities.is_empty()) {
|
||||
(Stage::First, true) => INITIAL_ACCEL,
|
||||
(Stage::Second, true) => INITIAL_ACCEL * 0.75,
|
||||
(Stage::Third, true) => INITIAL_ACCEL * 0.75,
|
||||
|
@ -11,8 +11,8 @@ use crate::{
|
||||
};
|
||||
use rayon::iter::ParallelIterator;
|
||||
use specs::{
|
||||
saveload::MarkerAllocator, Entities, Join, ParJoin, Read, ReadExpect, ReadStorage, System,
|
||||
WriteStorage,
|
||||
saveload::MarkerAllocator, storage, Entities, Join, ParJoin, Read, ReadExpect, ReadStorage,
|
||||
System, WriteStorage,
|
||||
};
|
||||
use std::ops::Range;
|
||||
use vek::*;
|
||||
@ -95,7 +95,7 @@ impl<'a> System<'a> for Sys {
|
||||
) {
|
||||
let mut event_emitter = event_bus.emitter();
|
||||
|
||||
// Add physics state components
|
||||
// Add/reset physics state components
|
||||
for entity in (
|
||||
&entities,
|
||||
!&physics_states,
|
||||
@ -108,7 +108,153 @@ impl<'a> System<'a> for Sys {
|
||||
.map(|(e, _, _, _, _, _)| e)
|
||||
.collect::<Vec<_>>()
|
||||
{
|
||||
let _ = physics_states.insert(entity, Default::default());
|
||||
match physics_states.entry(entity) {
|
||||
Ok(storage::StorageEntry::Occupied(mut o)) => o.get_mut().clear(),
|
||||
Ok(storage::StorageEntry::Vacant(v)) => {
|
||||
v.insert(Default::default());
|
||||
},
|
||||
Err(_) => {},
|
||||
}
|
||||
}
|
||||
|
||||
// Apply pushback
|
||||
//
|
||||
// Note: We now do this first because we project velocity ahead. This is slighty
|
||||
// imperfect and implies that we might get edge-cases where entities
|
||||
// standing right next to the edge of a wall may get hit by projectiles
|
||||
// fired into the wall very close to them. However, this sort of thing is
|
||||
// already possible with poorly-defined hitboxes anyway so it's not too
|
||||
// much of a concern.
|
||||
//
|
||||
// Actually, the aforementioned case can't happen, but only because wall
|
||||
// collision is checked prior to entity collision in the projectile
|
||||
// code.
|
||||
//
|
||||
// If this situation becomes a problem, this code should be integrated with the
|
||||
// terrain collision code below, although that's not trivial to do since
|
||||
// it means the step needs to take into account the speeds of both
|
||||
// entities.
|
||||
for (entity, pos, scale, mass, collider, _, _, physics, projectile) in (
|
||||
&entities,
|
||||
&positions,
|
||||
scales.maybe(),
|
||||
masses.maybe(),
|
||||
colliders.maybe(),
|
||||
!&mountings,
|
||||
stickies.maybe(),
|
||||
&mut physics_states,
|
||||
// TODO: if we need to avoid collisions for other things consider moving whether it
|
||||
// should interact into the collider component or into a separate component
|
||||
projectiles.maybe(),
|
||||
)
|
||||
.join()
|
||||
.filter(|(_, _, _, _, _, _, sticky, physics, _)| {
|
||||
sticky.is_none() || (physics.on_wall.is_none() && !physics.on_ground)
|
||||
})
|
||||
{
|
||||
let scale = scale.map(|s| s.0).unwrap_or(1.0);
|
||||
let radius = collider.map(|c| c.get_radius()).unwrap_or(0.5);
|
||||
let z_limits = collider.map(|c| c.get_z_limits()).unwrap_or((-0.5, 0.5));
|
||||
let mass = mass.map(|m| m.0).unwrap_or(scale);
|
||||
|
||||
// Group to ignore collisions with
|
||||
let ignore_group = projectile
|
||||
.and_then(|p| p.owner)
|
||||
.and_then(|uid| uid_allocator.retrieve_entity_internal(uid.into()))
|
||||
.and_then(|e| groups.get(e));
|
||||
|
||||
for (
|
||||
entity_other,
|
||||
other,
|
||||
pos_other,
|
||||
scale_other,
|
||||
mass_other,
|
||||
collider_other,
|
||||
_,
|
||||
group,
|
||||
) in (
|
||||
&entities,
|
||||
&uids,
|
||||
&positions,
|
||||
scales.maybe(),
|
||||
masses.maybe(),
|
||||
colliders.maybe(),
|
||||
!&mountings,
|
||||
groups.maybe(),
|
||||
)
|
||||
.join()
|
||||
{
|
||||
if ignore_group.is_some() && ignore_group == group {
|
||||
continue;
|
||||
}
|
||||
|
||||
let scale_other = scale_other.map(|s| s.0).unwrap_or(1.0);
|
||||
let radius_other = collider_other.map(|c| c.get_radius()).unwrap_or(0.5);
|
||||
let z_limits_other = collider_other
|
||||
.map(|c| c.get_z_limits())
|
||||
.unwrap_or((-0.5, 0.5));
|
||||
let mass_other = mass_other.map(|m| m.0).unwrap_or(scale_other);
|
||||
if mass_other == 0.0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let collision_dist = scale * radius + scale_other * radius_other;
|
||||
|
||||
let vel = velocities.get(entity).copied().unwrap_or_default().0;
|
||||
let vel_other = velocities.get(entity_other).copied().unwrap_or_default().0;
|
||||
|
||||
// Sanity check: don't try colliding entities that are too far from each other
|
||||
// Note: I think this catches all cases. If you get entitiy collision problems,
|
||||
// try removing this!
|
||||
if (pos.0 - pos_other.0).magnitude()
|
||||
> ((vel + vel_other) * dt.0).magnitude() + collision_dist
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let min_collision_dist = 0.3;
|
||||
// Ideally we'd deal with collision speed to minimise work here, but for not
|
||||
// taking the maximum velocity of the two is fine.
|
||||
let increments = (vel
|
||||
.magnitude_squared()
|
||||
.max(vel_other.magnitude_squared())
|
||||
.sqrt()
|
||||
* dt.0
|
||||
/ min_collision_dist)
|
||||
.ceil() as usize;
|
||||
let step_delta = 1.0 / increments as f32;
|
||||
let mut collided = false;
|
||||
for i in 0..increments {
|
||||
let factor = i as f32 * step_delta;
|
||||
let pos = pos.0 + vel * dt.0 * factor;
|
||||
let pos_other = pos_other.0 + vel_other * dt.0 * factor;
|
||||
|
||||
let diff = pos.xy() - pos_other.xy();
|
||||
|
||||
if diff.magnitude_squared() <= collision_dist.powf(2.0)
|
||||
&& pos.z + z_limits.1 * scale
|
||||
>= pos_other.z + z_limits_other.0 * scale_other
|
||||
&& pos.z + z_limits.0 * scale
|
||||
<= pos_other.z + z_limits_other.1 * scale_other
|
||||
{
|
||||
if !collided {
|
||||
physics.touch_entities.push(*other);
|
||||
}
|
||||
|
||||
if diff.magnitude_squared() > 0.0 {
|
||||
let force = 40.0 * (collision_dist - diff.magnitude()) * mass_other
|
||||
/ (mass + mass_other);
|
||||
|
||||
// Change velocity
|
||||
velocities.get_mut(entity).map(|vel| {
|
||||
vel.0 += Vec3::from(diff.normalized()) * force * dt.0 * step_delta
|
||||
});
|
||||
}
|
||||
|
||||
collided = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply movement inputs
|
||||
@ -116,7 +262,7 @@ impl<'a> System<'a> for Sys {
|
||||
&entities,
|
||||
scales.maybe(),
|
||||
stickies.maybe(),
|
||||
&colliders,
|
||||
colliders.maybe(),
|
||||
&mut positions,
|
||||
&mut velocities,
|
||||
&mut orientations,
|
||||
@ -176,16 +322,17 @@ impl<'a> System<'a> for Sys {
|
||||
Vec3::zero()
|
||||
};
|
||||
|
||||
match collider {
|
||||
match collider.copied().unwrap_or(Collider::Point) {
|
||||
Collider::Box {
|
||||
radius,
|
||||
z_min,
|
||||
z_max,
|
||||
} => {
|
||||
// Scale collider
|
||||
let radius = *radius; // * scale;
|
||||
let z_min = *z_min; // * scale;
|
||||
let z_max = *z_max; // * scale;
|
||||
// TODO: Use scale when pathfinding is good enough to manage irregular entity sizes
|
||||
let radius = radius; // * scale;
|
||||
let z_min = z_min; // * scale;
|
||||
let z_max = z_max.max(1.0); // * scale;
|
||||
|
||||
// Probe distances
|
||||
let hdist = radius.ceil() as i32;
|
||||
@ -514,74 +661,5 @@ impl<'a> System<'a> for Sys {
|
||||
land_on_grounds.into_iter().for_each(|(entity, vel)| {
|
||||
event_emitter.emit(ServerEvent::LandOnGround { entity, vel: vel.0 });
|
||||
});
|
||||
|
||||
// Apply pushback
|
||||
for (pos, scale, mass, vel, _, _, _, physics, projectile) in (
|
||||
&positions,
|
||||
scales.maybe(),
|
||||
masses.maybe(),
|
||||
&mut velocities,
|
||||
&colliders,
|
||||
!&mountings,
|
||||
stickies.maybe(),
|
||||
&mut physics_states,
|
||||
// TODO: if we need to avoid collisions for other things consider moving whether it
|
||||
// should interact into the collider component or into a separate component
|
||||
projectiles.maybe(),
|
||||
)
|
||||
.join()
|
||||
.filter(|(_, _, _, _, _, _, sticky, physics, _)| {
|
||||
sticky.is_none() || (physics.on_wall.is_none() && !physics.on_ground)
|
||||
})
|
||||
{
|
||||
physics.touch_entity = None;
|
||||
|
||||
let scale = scale.map(|s| s.0).unwrap_or(1.0);
|
||||
let mass = mass.map(|m| m.0).unwrap_or(scale);
|
||||
|
||||
// Group to ignore collisions with
|
||||
let ignore_group = projectile
|
||||
.and_then(|p| p.owner)
|
||||
.and_then(|uid| uid_allocator.retrieve_entity_internal(uid.into()))
|
||||
.and_then(|e| groups.get(e));
|
||||
|
||||
for (other, pos_other, scale_other, mass_other, _, _, group) in (
|
||||
&uids,
|
||||
&positions,
|
||||
scales.maybe(),
|
||||
masses.maybe(),
|
||||
&colliders,
|
||||
!&mountings,
|
||||
groups.maybe(),
|
||||
)
|
||||
.join()
|
||||
{
|
||||
if ignore_group.is_some() && ignore_group == group {
|
||||
continue;
|
||||
}
|
||||
|
||||
let scale_other = scale_other.map(|s| s.0).unwrap_or(1.0);
|
||||
|
||||
let mass_other = mass_other.map(|m| m.0).unwrap_or(scale_other);
|
||||
if mass_other == 0.0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let diff = Vec2::<f32>::from(pos.0 - pos_other.0);
|
||||
|
||||
let collision_dist = 0.55 * (scale + scale_other);
|
||||
|
||||
if diff.magnitude_squared() > 0.0
|
||||
&& diff.magnitude_squared() < collision_dist.powf(2.0)
|
||||
&& pos.0.z + 1.6 * scale > pos_other.0.z
|
||||
&& pos.0.z < pos_other.0.z + 1.6 * scale_other
|
||||
{
|
||||
let force = (collision_dist - diff.magnitude()) * 2.0 * mass_other
|
||||
/ (mass + mass_other);
|
||||
vel.0 += Vec3::from(diff.normalized()) * force;
|
||||
physics.touch_entity = Some(*other);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -81,81 +81,91 @@ impl<'a> System<'a> for Sys {
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
// Hit entity
|
||||
else if let Some(other) = physics.touch_entity {
|
||||
for effect in projectile.hit_entity.drain(..) {
|
||||
match effect {
|
||||
projectile::Effect::Damage(healthchange) => {
|
||||
let owner_uid = projectile.owner.unwrap();
|
||||
let mut damage = Damage {
|
||||
healthchange: healthchange as f32,
|
||||
source: DamageSource::Projectile,
|
||||
};
|
||||
} else {
|
||||
// Hit entity
|
||||
for other in physics.touch_entities.iter().copied() {
|
||||
if projectile.owner == Some(other) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let other_entity = uid_allocator.retrieve_entity_internal(other.into());
|
||||
if let Some(loadout) = other_entity.and_then(|e| loadouts.get(e)) {
|
||||
damage.modify_damage(false, loadout);
|
||||
}
|
||||
for effect in projectile.hit_entity.iter().cloned() {
|
||||
match effect {
|
||||
projectile::Effect::Damage(healthchange) => {
|
||||
let owner_uid = projectile.owner.unwrap();
|
||||
let mut damage = Damage {
|
||||
healthchange: healthchange as f32,
|
||||
source: DamageSource::Projectile,
|
||||
};
|
||||
|
||||
if other != owner_uid {
|
||||
server_emitter.emit(ServerEvent::Damage {
|
||||
uid: other,
|
||||
change: HealthChange {
|
||||
amount: damage.healthchange as i32,
|
||||
cause: HealthSource::Attack { by: owner_uid },
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
projectile::Effect::Knockback(knockback) => {
|
||||
if let Some(entity) =
|
||||
uid_allocator.retrieve_entity_internal(other.into())
|
||||
{
|
||||
local_emitter.emit(LocalEvent::ApplyForce {
|
||||
entity,
|
||||
force: knockback
|
||||
* *Dir::slerp(ori.0, Dir::new(Vec3::unit_z()), 0.5),
|
||||
});
|
||||
}
|
||||
},
|
||||
projectile::Effect::RewardEnergy(energy) => {
|
||||
if let Some(energy_mut) = projectile
|
||||
.owner
|
||||
.and_then(|o| uid_allocator.retrieve_entity_internal(o.into()))
|
||||
.and_then(|o| energies.get_mut(o))
|
||||
{
|
||||
energy_mut.change_by(energy as i32, EnergySource::HitEnemy);
|
||||
}
|
||||
},
|
||||
projectile::Effect::Explode { power } => {
|
||||
server_emitter.emit(ServerEvent::Explosion {
|
||||
pos: pos.0,
|
||||
power,
|
||||
owner: projectile.owner,
|
||||
friendly_damage: false,
|
||||
reagent: None,
|
||||
})
|
||||
},
|
||||
projectile::Effect::Vanish => server_emitter.emit(ServerEvent::Destroy {
|
||||
entity,
|
||||
cause: HealthSource::World,
|
||||
}),
|
||||
projectile::Effect::Possess => {
|
||||
if other != projectile.owner.unwrap() {
|
||||
if let Some(owner) = projectile.owner {
|
||||
server_emitter.emit(ServerEvent::Possess(owner, other));
|
||||
let other_entity =
|
||||
uid_allocator.retrieve_entity_internal(other.into());
|
||||
if let Some(loadout) = other_entity.and_then(|e| loadouts.get(e)) {
|
||||
damage.modify_damage(false, loadout);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
|
||||
if other != owner_uid {
|
||||
server_emitter.emit(ServerEvent::Damage {
|
||||
uid: other,
|
||||
change: HealthChange {
|
||||
amount: damage.healthchange as i32,
|
||||
cause: HealthSource::Attack { by: owner_uid },
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
projectile::Effect::Knockback(knockback) => {
|
||||
if let Some(entity) =
|
||||
uid_allocator.retrieve_entity_internal(other.into())
|
||||
{
|
||||
local_emitter.emit(LocalEvent::ApplyForce {
|
||||
entity,
|
||||
force: knockback
|
||||
* *Dir::slerp(ori.0, Dir::new(Vec3::unit_z()), 0.5),
|
||||
});
|
||||
}
|
||||
},
|
||||
projectile::Effect::RewardEnergy(energy) => {
|
||||
if let Some(energy_mut) = projectile
|
||||
.owner
|
||||
.and_then(|o| uid_allocator.retrieve_entity_internal(o.into()))
|
||||
.and_then(|o| energies.get_mut(o))
|
||||
{
|
||||
energy_mut.change_by(energy as i32, EnergySource::HitEnemy);
|
||||
}
|
||||
},
|
||||
projectile::Effect::Explode { power } => {
|
||||
server_emitter.emit(ServerEvent::Explosion {
|
||||
pos: pos.0,
|
||||
power,
|
||||
owner: projectile.owner,
|
||||
friendly_damage: false,
|
||||
reagent: None,
|
||||
})
|
||||
},
|
||||
projectile::Effect::Vanish => {
|
||||
server_emitter.emit(ServerEvent::Destroy {
|
||||
entity,
|
||||
cause: HealthSource::World,
|
||||
})
|
||||
},
|
||||
projectile::Effect::Possess => {
|
||||
if other != projectile.owner.unwrap() {
|
||||
if let Some(owner) = projectile.owner {
|
||||
server_emitter.emit(ServerEvent::Possess(owner, other));
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let Some(dir) = velocities
|
||||
.get(entity)
|
||||
.and_then(|vel| vel.0.try_normalized())
|
||||
{
|
||||
ori.0 = dir.into();
|
||||
|
||||
if let Some(dir) = velocities
|
||||
.get(entity)
|
||||
.and_then(|vel| vel.0.try_normalized())
|
||||
{
|
||||
ori.0 = dir.into();
|
||||
}
|
||||
}
|
||||
|
||||
if projectile.time_left == Duration::default() {
|
||||
|
@ -102,20 +102,15 @@ impl StateExt for State {
|
||||
.with(comp::Vel(Vec3::zero()))
|
||||
.with(comp::Ori::default())
|
||||
.with(comp::Collider::Box {
|
||||
radius: 0.4,
|
||||
radius: body.radius(),
|
||||
z_min: 0.0,
|
||||
z_max: 1.75,
|
||||
z_max: body.height(),
|
||||
})
|
||||
.with(comp::Controller::default())
|
||||
.with(body)
|
||||
.with(stats)
|
||||
.with(comp::Alignment::Npc)
|
||||
.with(comp::Energy::new(500))
|
||||
.with(comp::Collider::Box {
|
||||
radius: 0.4,
|
||||
z_min: 0.0,
|
||||
z_max: 1.75,
|
||||
})
|
||||
.with(comp::Gravity(1.0))
|
||||
.with(comp::CharacterState::default())
|
||||
.with(loadout)
|
||||
@ -127,13 +122,13 @@ impl StateExt for State {
|
||||
.with(pos)
|
||||
.with(comp::Vel(Vec3::zero()))
|
||||
.with(comp::Ori::default())
|
||||
.with(comp::Body::Object(object))
|
||||
.with(comp::Mass(5.0))
|
||||
.with(comp::Collider::Box {
|
||||
radius: 0.4,
|
||||
radius: comp::Body::Object(object).radius(),
|
||||
z_min: 0.0,
|
||||
z_max: 0.9,
|
||||
z_max: comp::Body::Object(object).height(),
|
||||
})
|
||||
.with(comp::Body::Object(object))
|
||||
.with(comp::Gravity(1.0))
|
||||
}
|
||||
|
||||
@ -223,6 +218,11 @@ impl StateExt for State {
|
||||
}),
|
||||
));
|
||||
|
||||
self.write_component(entity, comp::Collider::Box {
|
||||
radius: body.radius(),
|
||||
z_min: 0.0,
|
||||
z_max: body.height(),
|
||||
});
|
||||
self.write_component(entity, body);
|
||||
self.write_component(entity, stats);
|
||||
self.write_component(entity, inventory);
|
||||
|
@ -476,7 +476,7 @@ impl Scene {
|
||||
player_scale * 1.65
|
||||
}
|
||||
},
|
||||
CameraMode::ThirdPerson if scene_data.is_aiming => player_scale * 2.1,
|
||||
CameraMode::ThirdPerson if scene_data.is_aiming => player_scale * 2.2,
|
||||
CameraMode::ThirdPerson => player_scale * 1.65,
|
||||
CameraMode::Freefly => 0.0,
|
||||
};
|
||||
|
@ -238,7 +238,7 @@ impl PlayState for SessionState {
|
||||
(
|
||||
is_aiming,
|
||||
if is_aiming {
|
||||
Vec3::unit_z() * 0.025
|
||||
Vec3::unit_z() * 0.05
|
||||
} else {
|
||||
Vec3::zero()
|
||||
},
|
||||
|
@ -361,11 +361,14 @@ impl<'a> BlockGen<'a> {
|
||||
|
||||
Some(Block::new(
|
||||
BlockKind::Rock,
|
||||
stone_col.map2(Rgb::new(
|
||||
field0.get(wpos) as u8 % 16,
|
||||
field1.get(wpos) as u8 % 16,
|
||||
field2.get(wpos) as u8 % 16,
|
||||
), |stone, x| stone.saturating_sub(x)),
|
||||
stone_col.map2(
|
||||
Rgb::new(
|
||||
field0.get(wpos) as u8 % 16,
|
||||
field1.get(wpos) as u8 % 16,
|
||||
field2.get(wpos) as u8 % 16,
|
||||
),
|
||||
|stone, x| stone.saturating_sub(x),
|
||||
),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
|
Loading…
Reference in New Issue
Block a user