mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Fix beam offsets
- Introduce notion of min and max radius for Body instead of old `radius()` function (which is renamed to `max_radius()`).
This commit is contained in:
parent
7d97fe7ec5
commit
c069a3523d
@ -400,9 +400,17 @@ impl Body {
|
||||
// Note: This is used for collisions, but it's not very accurate for shapes that
|
||||
// are very much not cylindrical. Eventually this ought to be replaced by more
|
||||
// accurate collision shapes.
|
||||
pub fn radius(&self) -> f32 {
|
||||
pub fn max_radius(&self) -> f32 {
|
||||
let dim = self.dimensions();
|
||||
dim.x.max(dim.y) / 2.0
|
||||
let (x, y) = (dim.x, dim.y);
|
||||
|
||||
x.max(y) / 2.0
|
||||
}
|
||||
|
||||
pub fn min_radius(&self) -> f32 {
|
||||
let (_p0, _p1, radius) = self.sausage();
|
||||
|
||||
radius
|
||||
}
|
||||
|
||||
/// Base of our Capsule Prism used for collisions.
|
||||
@ -453,7 +461,7 @@ impl Body {
|
||||
// lead to that both entities will try to keep 5.0 units away from each
|
||||
// other.
|
||||
pub fn spacing_radius(&self) -> f32 {
|
||||
self.radius()
|
||||
self.max_radius()
|
||||
+ match self {
|
||||
Body::QuadrupedSmall(body) => match body.species {
|
||||
quadruped_small::Species::Rat => 0.0,
|
||||
|
@ -232,8 +232,9 @@ fn height_offset(body: &Body, look_dir: Dir) -> f32 {
|
||||
}
|
||||
|
||||
pub fn beam_offsets(body: &Body, look_dir: Dir, ori: Vec3<f32>) -> Vec3<f32> {
|
||||
let body_radius = body.radius();
|
||||
let body_radius = body.min_radius();
|
||||
let body_offsets_z = height_offset(body, look_dir);
|
||||
|
||||
Vec3::new(
|
||||
body_radius * ori.x * 1.1,
|
||||
body_radius * ori.y * 1.1,
|
||||
|
@ -196,7 +196,7 @@ impl CharacterBehavior for Data {
|
||||
Outcome::GroundSlam {
|
||||
pos: data.pos.0
|
||||
+ *data.ori.look_dir()
|
||||
* (data.body.radius() + self.static_data.range),
|
||||
* (data.body.max_radius() + self.static_data.range),
|
||||
},
|
||||
));
|
||||
}
|
||||
|
@ -641,7 +641,7 @@ pub fn handle_manipulate_loadout(
|
||||
// MAX_PICKUP_RANGE and the radius of the body
|
||||
let sprite_range_check = |pos: Vec3<f32>| {
|
||||
(sprite_pos_f32 - pos).magnitude_squared()
|
||||
< (MAX_PICKUP_RANGE + data.body.radius()).powi(2)
|
||||
< (MAX_PICKUP_RANGE + data.body.max_radius()).powi(2)
|
||||
};
|
||||
|
||||
// Checks if player's feet or head is near to sprite
|
||||
|
@ -82,174 +82,193 @@ impl<'a> System<'a> for Sys {
|
||||
&beam_segments,
|
||||
)
|
||||
.par_join()
|
||||
.fold(|| (Vec::new(), Vec::new(), Vec::new()),
|
||||
|(mut server_events, mut add_hit_entities, mut outcomes),
|
||||
(entity, pos, ori, beam_segment)|
|
||||
{
|
||||
let creation_time = match beam_segment.creation {
|
||||
Some(time) => time,
|
||||
// Skip newly created beam segments
|
||||
None => return (server_events, add_hit_entities, outcomes),
|
||||
};
|
||||
let end_time = creation_time + beam_segment.duration.as_secs_f64();
|
||||
|
||||
let beam_owner = beam_segment
|
||||
.owner
|
||||
.and_then(|uid| read_data.uid_allocator.retrieve_entity_internal(uid.into()));
|
||||
|
||||
let mut rng = thread_rng();
|
||||
if rng.gen_bool(0.005) {
|
||||
server_events.push(ServerEvent::Sound {
|
||||
sound: Sound::new(SoundKind::Beam, pos.0, 7.0, time),
|
||||
});
|
||||
}
|
||||
|
||||
// If beam segment is out of time emit destroy event but still continue since it
|
||||
// may have traveled and produced effects a bit before reaching its
|
||||
// end point
|
||||
if end_time < time {
|
||||
server_events.push(ServerEvent::Delete(entity));
|
||||
}
|
||||
|
||||
// Determine area that was covered by the beam in the last tick
|
||||
let frame_time = dt.min((end_time - time) as f32);
|
||||
if frame_time <= 0.0 {
|
||||
return (server_events, add_hit_entities, outcomes);
|
||||
}
|
||||
// Note: min() probably uneeded
|
||||
let time_since_creation = (time - creation_time) as f32;
|
||||
let frame_start_dist =
|
||||
(beam_segment.speed * (time_since_creation - frame_time)).max(0.0);
|
||||
let frame_end_dist = (beam_segment.speed * time_since_creation).max(frame_start_dist);
|
||||
|
||||
// Group to ignore collisions with
|
||||
// Might make this more nuanced if beams are used for non damage effects
|
||||
let group = beam_owner.and_then(|e| read_data.groups.get(e));
|
||||
|
||||
let hit_entities = if let Some(beam) = beam_owner.and_then(|e| beams.get(e)) {
|
||||
&beam.hit_entities
|
||||
} else {
|
||||
return (server_events, add_hit_entities, outcomes);
|
||||
};
|
||||
|
||||
// Go through all affectable entities by querying the spatial grid
|
||||
let target_iter = read_data
|
||||
.cached_spatial_grid
|
||||
.0
|
||||
.in_circle_aabr(pos.0.xy(), frame_end_dist - frame_start_dist)
|
||||
.filter_map(|target|{
|
||||
read_data
|
||||
.positions
|
||||
.get(target)
|
||||
.and_then(|l| read_data.healths.get(target).map(|r| (l,r)))
|
||||
.and_then(|l| read_data.uids.get(target).map(|r| (l,r)))
|
||||
.and_then(|l| read_data.bodies.get(target).map(|r| (l,r)))
|
||||
.map(|(((pos_b, health_b), uid_b), body_b)| {
|
||||
(target, uid_b, pos_b, health_b, body_b)
|
||||
})
|
||||
});
|
||||
target_iter.for_each(|(target, uid_b, pos_b, health_b, body_b)| {
|
||||
// Check to see if entity has already been hit recently
|
||||
if hit_entities.iter().any(|&uid| uid == *uid_b) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Scales
|
||||
let scale_b = read_data.scales.get(target).map_or(1.0, |s| s.0);
|
||||
let rad_b = body_b.radius() * scale_b;
|
||||
let height_b = body_b.height() * scale_b;
|
||||
|
||||
// Check if it is a hit
|
||||
let hit = entity != target && !health_b.is_dead
|
||||
// Collision shapes
|
||||
&& sphere_wedge_cylinder_collision(pos.0, frame_start_dist, frame_end_dist, *ori.look_dir(), beam_segment.angle, pos_b.0, rad_b, height_b);
|
||||
|
||||
// Finally, ensure that a hit has actually occurred by performing a raycast. We do this last because
|
||||
// it's likely to be the most expensive operation.
|
||||
let tgt_dist = pos.0.distance(pos_b.0);
|
||||
let hit = hit && read_data.terrain
|
||||
.ray(pos.0, pos.0 + *ori.look_dir() * (tgt_dist + 1.0))
|
||||
.until(|b| b.is_filled())
|
||||
.cast().0 >= tgt_dist;
|
||||
|
||||
if hit {
|
||||
// See if entities are in the same group
|
||||
let same_group = group
|
||||
.map(|group_a| Some(group_a) == read_data.groups.get(target))
|
||||
.unwrap_or(Some(*uid_b) == beam_segment.owner);
|
||||
|
||||
let target_group = if same_group {
|
||||
GroupTarget::InGroup
|
||||
} else {
|
||||
GroupTarget::OutOfGroup
|
||||
.fold(
|
||||
|| (Vec::new(), Vec::new(), Vec::new()),
|
||||
|(mut server_events, mut add_hit_entities, mut outcomes),
|
||||
(entity, pos, ori, beam_segment)| {
|
||||
let creation_time = match beam_segment.creation {
|
||||
Some(time) => time,
|
||||
// Skip newly created beam segments
|
||||
None => return (server_events, add_hit_entities, outcomes),
|
||||
};
|
||||
let end_time = creation_time + beam_segment.duration.as_secs_f64();
|
||||
|
||||
// If owner, shouldn't heal or damage
|
||||
if Some(*uid_b) == beam_segment.owner {
|
||||
return;
|
||||
let beam_owner = beam_segment.owner.and_then(|uid| {
|
||||
read_data.uid_allocator.retrieve_entity_internal(uid.into())
|
||||
});
|
||||
|
||||
let mut rng = thread_rng();
|
||||
if rng.gen_bool(0.005) {
|
||||
server_events.push(ServerEvent::Sound {
|
||||
sound: Sound::new(SoundKind::Beam, pos.0, 7.0, time),
|
||||
});
|
||||
}
|
||||
|
||||
let attacker_info =
|
||||
beam_owner
|
||||
.zip(beam_segment.owner)
|
||||
.map(|(entity, uid)| AttackerInfo {
|
||||
entity,
|
||||
uid,
|
||||
energy: read_data.energies.get(entity),
|
||||
combo: read_data.combos.get(entity),
|
||||
inventory: read_data.inventories.get(entity),
|
||||
});
|
||||
// If beam segment is out of time emit destroy event but still continue since it
|
||||
// may have traveled and produced effects a bit before reaching its
|
||||
// end point
|
||||
if end_time < time {
|
||||
server_events.push(ServerEvent::Delete(entity));
|
||||
}
|
||||
|
||||
let target_info = TargetInfo {
|
||||
entity: target,
|
||||
uid: *uid_b,
|
||||
inventory: read_data.inventories.get(target),
|
||||
stats: read_data.stats.get(target),
|
||||
health: read_data.healths.get(target),
|
||||
pos: pos_b.0,
|
||||
ori: read_data.orientations.get(target),
|
||||
char_state: read_data.character_states.get(target),
|
||||
// Determine area that was covered by the beam in the last tick
|
||||
let frame_time = dt.min((end_time - time) as f32);
|
||||
if frame_time <= 0.0 {
|
||||
return (server_events, add_hit_entities, outcomes);
|
||||
}
|
||||
// Note: min() probably uneeded
|
||||
let time_since_creation = (time - creation_time) as f32;
|
||||
let frame_start_dist =
|
||||
(beam_segment.speed * (time_since_creation - frame_time)).max(0.0);
|
||||
let frame_end_dist =
|
||||
(beam_segment.speed * time_since_creation).max(frame_start_dist);
|
||||
|
||||
// Group to ignore collisions with
|
||||
// Might make this more nuanced if beams are used for non damage effects
|
||||
let group = beam_owner.and_then(|e| read_data.groups.get(e));
|
||||
|
||||
let hit_entities = if let Some(beam) = beam_owner.and_then(|e| beams.get(e)) {
|
||||
&beam.hit_entities
|
||||
} else {
|
||||
return (server_events, add_hit_entities, outcomes);
|
||||
};
|
||||
|
||||
// Go through all affectable entities by querying the spatial grid
|
||||
let target_iter = read_data
|
||||
.cached_spatial_grid
|
||||
.0
|
||||
.in_circle_aabr(pos.0.xy(), frame_end_dist - frame_start_dist)
|
||||
.filter_map(|target| {
|
||||
read_data
|
||||
.positions
|
||||
.get(target)
|
||||
.and_then(|l| read_data.healths.get(target).map(|r| (l, r)))
|
||||
.and_then(|l| read_data.uids.get(target).map(|r| (l, r)))
|
||||
.and_then(|l| read_data.bodies.get(target).map(|r| (l, r)))
|
||||
.map(|(((pos_b, health_b), uid_b), body_b)| {
|
||||
(target, uid_b, pos_b, health_b, body_b)
|
||||
})
|
||||
});
|
||||
target_iter.for_each(|(target, uid_b, pos_b, health_b, body_b)| {
|
||||
// Check to see if entity has already been hit recently
|
||||
if hit_entities.iter().any(|&uid| uid == *uid_b) {
|
||||
return;
|
||||
}
|
||||
|
||||
// PvP check
|
||||
let may_harm = combat::may_harm(
|
||||
&read_data.alignments,
|
||||
&read_data.players,
|
||||
&read_data.uid_allocator,
|
||||
beam_owner,
|
||||
target,
|
||||
);
|
||||
let attack_options = AttackOptions {
|
||||
// No luck with dodging beams
|
||||
target_dodging: false,
|
||||
may_harm,
|
||||
target_group,
|
||||
};
|
||||
// Scales
|
||||
let scale_b = read_data.scales.get(target).map_or(1.0, |s| s.0);
|
||||
let rad_b = body_b.max_radius() * scale_b;
|
||||
let height_b = body_b.height() * scale_b;
|
||||
|
||||
beam_segment.properties.attack.apply_attack(
|
||||
attacker_info,
|
||||
target_info,
|
||||
ori.look_dir(),
|
||||
attack_options,
|
||||
1.0,
|
||||
AttackSource::Beam,
|
||||
|e| server_events.push(e),
|
||||
|o| outcomes.push(o),
|
||||
);
|
||||
// Check if it is a hit
|
||||
// TODO: use Capsule Prism instead of cylinder
|
||||
let hit = entity != target
|
||||
&& !health_b.is_dead
|
||||
&& sphere_wedge_cylinder_collision(
|
||||
pos.0,
|
||||
frame_start_dist,
|
||||
frame_end_dist,
|
||||
*ori.look_dir(),
|
||||
beam_segment.angle,
|
||||
pos_b.0,
|
||||
rad_b,
|
||||
height_b,
|
||||
);
|
||||
|
||||
add_hit_entities.push((beam_owner, *uid_b));
|
||||
}
|
||||
});
|
||||
(server_events, add_hit_entities, outcomes)
|
||||
}).reduce(|| (Vec::new(), Vec::new(), Vec::new()),
|
||||
|(mut events_a, mut hit_entities_a, mut outcomes_a),
|
||||
(mut events_b, mut hit_entities_b, mut outcomes_b)| {
|
||||
events_a.append(&mut events_b);
|
||||
hit_entities_a.append(&mut hit_entities_b);
|
||||
outcomes_a.append(&mut outcomes_b);
|
||||
(events_a, hit_entities_a, outcomes_a)
|
||||
});
|
||||
// Finally, ensure that a hit has actually occurred by performing a raycast.
|
||||
// We do this last because it's likely to be the
|
||||
// most expensive operation.
|
||||
let tgt_dist = pos.0.distance(pos_b.0);
|
||||
let hit = hit
|
||||
&& read_data
|
||||
.terrain
|
||||
.ray(pos.0, pos.0 + *ori.look_dir() * (tgt_dist + 1.0))
|
||||
.until(|b| b.is_filled())
|
||||
.cast()
|
||||
.0
|
||||
>= tgt_dist;
|
||||
|
||||
if hit {
|
||||
// See if entities are in the same group
|
||||
let same_group = group
|
||||
.map(|group_a| Some(group_a) == read_data.groups.get(target))
|
||||
.unwrap_or(Some(*uid_b) == beam_segment.owner);
|
||||
|
||||
let target_group = if same_group {
|
||||
GroupTarget::InGroup
|
||||
} else {
|
||||
GroupTarget::OutOfGroup
|
||||
};
|
||||
|
||||
// If owner, shouldn't heal or damage
|
||||
if Some(*uid_b) == beam_segment.owner {
|
||||
return;
|
||||
}
|
||||
|
||||
let attacker_info =
|
||||
beam_owner.zip(beam_segment.owner).map(|(entity, uid)| {
|
||||
AttackerInfo {
|
||||
entity,
|
||||
uid,
|
||||
energy: read_data.energies.get(entity),
|
||||
combo: read_data.combos.get(entity),
|
||||
inventory: read_data.inventories.get(entity),
|
||||
}
|
||||
});
|
||||
|
||||
let target_info = TargetInfo {
|
||||
entity: target,
|
||||
uid: *uid_b,
|
||||
inventory: read_data.inventories.get(target),
|
||||
stats: read_data.stats.get(target),
|
||||
health: read_data.healths.get(target),
|
||||
pos: pos_b.0,
|
||||
ori: read_data.orientations.get(target),
|
||||
char_state: read_data.character_states.get(target),
|
||||
};
|
||||
|
||||
// PvP check
|
||||
let may_harm = combat::may_harm(
|
||||
&read_data.alignments,
|
||||
&read_data.players,
|
||||
&read_data.uid_allocator,
|
||||
beam_owner,
|
||||
target,
|
||||
);
|
||||
let attack_options = AttackOptions {
|
||||
// No luck with dodging beams
|
||||
target_dodging: false,
|
||||
may_harm,
|
||||
target_group,
|
||||
};
|
||||
|
||||
beam_segment.properties.attack.apply_attack(
|
||||
attacker_info,
|
||||
target_info,
|
||||
ori.look_dir(),
|
||||
attack_options,
|
||||
1.0,
|
||||
AttackSource::Beam,
|
||||
|e| server_events.push(e),
|
||||
|o| outcomes.push(o),
|
||||
);
|
||||
|
||||
add_hit_entities.push((beam_owner, *uid_b));
|
||||
}
|
||||
});
|
||||
(server_events, add_hit_entities, outcomes)
|
||||
},
|
||||
)
|
||||
.reduce(
|
||||
|| (Vec::new(), Vec::new(), Vec::new()),
|
||||
|(mut events_a, mut hit_entities_a, mut outcomes_a),
|
||||
(mut events_b, mut hit_entities_b, mut outcomes_b)| {
|
||||
events_a.append(&mut events_b);
|
||||
hit_entities_a.append(&mut hit_entities_b);
|
||||
outcomes_a.append(&mut outcomes_b);
|
||||
(events_a, hit_entities_a, outcomes_a)
|
||||
},
|
||||
);
|
||||
job.cpu_stats.measure(ParMode::Single);
|
||||
outcomes.append(&mut new_outcomes);
|
||||
|
||||
|
@ -80,7 +80,8 @@ impl<'a> System<'a> for Sys {
|
||||
// Scales
|
||||
let eye_pos = pos.0 + Vec3::unit_z() * body.eye_height();
|
||||
let scale = read_data.scales.get(attacker).map_or(1.0, |s| s.0);
|
||||
let rad = body.radius() * scale;
|
||||
// TODO: use Capsule Prisms instead of Cylinders
|
||||
let rad = body.max_radius() * scale;
|
||||
|
||||
// Mine blocks broken by the attack
|
||||
if let Some((block_pos, tool)) = melee_attack.break_block {
|
||||
@ -115,7 +116,7 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
// Scales
|
||||
let scale_b = read_data.scales.get(target).map_or(1.0, |s| s.0);
|
||||
let rad_b = body_b.radius() * scale_b;
|
||||
let rad_b = body_b.max_radius() * scale_b;
|
||||
|
||||
// Check if entity is dodging
|
||||
let target_dodging = read_data
|
||||
|
@ -228,8 +228,7 @@ impl<'a> PhysicsData<'a> {
|
||||
// Move center to the middle between OLD and OLD+VEL_DT
|
||||
// so that we can reduce the collision_boundary.
|
||||
phys_cache.center = entity_center + phys_cache.velocity_dt / 2.0;
|
||||
phys_cache.collision_boundary = radius
|
||||
+ (phys_cache.velocity_dt / 2.0).magnitude();
|
||||
phys_cache.collision_boundary = radius + (phys_cache.velocity_dt / 2.0).magnitude();
|
||||
phys_cache.scale = scale;
|
||||
phys_cache.scaled_radius = flat_radius;
|
||||
|
||||
@ -459,7 +458,6 @@ impl<'a> PhysicsData<'a> {
|
||||
|
||||
let mut collision_registered = false;
|
||||
|
||||
|
||||
for i in 0..increments {
|
||||
let factor = i as f32 * step_delta;
|
||||
match try_e2e_collision(
|
||||
@ -1919,7 +1917,8 @@ struct ColliderContext<'a> {
|
||||
previous_cache: &'a PreviousPhysCache,
|
||||
}
|
||||
|
||||
/// Find pushback vector and collision_distance we assume between this colliders.
|
||||
/// Find pushback vector and collision_distance we assume between this
|
||||
/// colliders.
|
||||
fn projection_between(c0: ColliderContext, c1: ColliderContext) -> (Vec2<f32>, f32) {
|
||||
// "Proper" way to do this would be handle the case when both our colliders
|
||||
// are capsule prisms by building origins from p0, p1 offsets and our
|
||||
|
@ -152,7 +152,8 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
// Scales
|
||||
let scale_b = read_data.scales.get(target).map_or(1.0, |s| s.0);
|
||||
let rad_b = body_b.radius() * scale_b;
|
||||
// TODO: use Capsule Prism instead of Cylinder
|
||||
let rad_b = body_b.max_radius() * scale_b;
|
||||
|
||||
// Angle checks
|
||||
let pos_b_ground = Vec3::new(pos_b.0.x, pos_b.0.y, pos.0.z);
|
||||
|
@ -715,8 +715,8 @@ pub fn handle_explosion(server: &Server, pos: Vec3<f32>, explosion: Explosion, o
|
||||
cyl_body: Body,
|
||||
) -> f32 {
|
||||
// 2d check
|
||||
let horiz_dist =
|
||||
Vec2::<f32>::from(sphere_pos - cyl_pos).distance(Vec2::default()) - cyl_body.radius();
|
||||
let horiz_dist = Vec2::<f32>::from(sphere_pos - cyl_pos).distance(Vec2::default())
|
||||
- cyl_body.max_radius();
|
||||
// z check
|
||||
let half_body_height = cyl_body.height() / 2.0;
|
||||
let vert_distance =
|
||||
|
@ -249,7 +249,7 @@ impl StateExt for State {
|
||||
.with(body.mass())
|
||||
.with(body.density())
|
||||
.with(comp::Collider::Box {
|
||||
radius: body.radius(),
|
||||
radius: body.max_radius(),
|
||||
z_min: 0.0,
|
||||
z_max: body.height(),
|
||||
})
|
||||
@ -315,7 +315,7 @@ impl StateExt for State {
|
||||
projectile_base = projectile_base.with(comp::Collider::Point)
|
||||
} else {
|
||||
projectile_base = projectile_base.with(comp::Collider::Box {
|
||||
radius: body.radius(),
|
||||
radius: body.max_radius(),
|
||||
z_min: 0.0,
|
||||
z_max: body.height(),
|
||||
})
|
||||
@ -392,7 +392,7 @@ impl StateExt for State {
|
||||
.with(comp::Vel(Vec3::zero()))
|
||||
.with(comp::Ori::default())
|
||||
.with(comp::Collider::Box {
|
||||
radius: comp::Body::Object(object).radius(),
|
||||
radius: comp::Body::Object(object).max_radius(),
|
||||
z_min: 0.0,
|
||||
z_max: comp::Body::Object(object).height()
|
||||
})
|
||||
@ -510,7 +510,7 @@ impl StateExt for State {
|
||||
// commands, so we can assume that all of these calls succeed,
|
||||
// justifying ignoring the result of insertion.
|
||||
self.write_component_ignore_entity_dead(entity, comp::Collider::Box {
|
||||
radius: body.radius(),
|
||||
radius: body.max_radius(),
|
||||
z_min: 0.0,
|
||||
z_max: body.height(),
|
||||
});
|
||||
|
@ -1783,9 +1783,9 @@ impl<'a> AgentData<'a> {
|
||||
// Wield the weapon as running towards the target
|
||||
controller.actions.push(ControlAction::Wield);
|
||||
|
||||
let min_attack_dist = (self.body.map_or(0.5, |b| b.radius()) + DEFAULT_ATTACK_RANGE)
|
||||
let min_attack_dist = (self.body.map_or(0.5, |b| b.max_radius()) + DEFAULT_ATTACK_RANGE)
|
||||
* self.scale
|
||||
+ tgt_data.body.map_or(0.5, |b| b.radius()) * tgt_data.scale.map_or(1.0, |s| s.0);
|
||||
+ tgt_data.body.map_or(0.5, |b| b.max_radius()) * tgt_data.scale.map_or(1.0, |s| s.0);
|
||||
let dist_sqrd = self.pos.0.distance_squared(tgt_data.pos.0);
|
||||
let angle = self
|
||||
.ori
|
||||
@ -3676,7 +3676,7 @@ impl<'a> AgentData<'a> {
|
||||
) {
|
||||
const BIRD_ATTACK_RANGE: f32 = 4.0;
|
||||
const BIRD_CHARGE_DISTANCE: f32 = 15.0;
|
||||
let bird_attack_distance = self.body.map_or(0.0, |b| b.radius()) + BIRD_ATTACK_RANGE;
|
||||
let bird_attack_distance = self.body.map_or(0.0, |b| b.max_radius()) + BIRD_ATTACK_RANGE;
|
||||
// Increase action timer
|
||||
agent.action_state.timer += read_data.dt.0;
|
||||
// If higher than 2 blocks
|
||||
@ -3748,7 +3748,7 @@ impl<'a> AgentData<'a> {
|
||||
const MINOTAUR_ATTACK_RANGE: f32 = 5.0;
|
||||
const MINOTAUR_CHARGE_DISTANCE: f32 = 15.0;
|
||||
let minotaur_attack_distance =
|
||||
self.body.map_or(0.0, |b| b.radius()) + MINOTAUR_ATTACK_RANGE;
|
||||
self.body.map_or(0.0, |b| b.max_radius()) + MINOTAUR_ATTACK_RANGE;
|
||||
let health_fraction = self.health.map_or(1.0, |h| h.fraction());
|
||||
// Sets action counter at start of combat
|
||||
if agent.action_state.counter < MINOTAUR_FRENZY_THRESHOLD
|
||||
@ -3815,7 +3815,7 @@ impl<'a> AgentData<'a> {
|
||||
const GOLEM_LASER_RANGE: f32 = 30.0;
|
||||
const GOLEM_LONG_RANGE: f32 = 50.0;
|
||||
const GOLEM_TARGET_SPEED: f32 = 8.0;
|
||||
let golem_melee_range = self.body.map_or(0.0, |b| b.radius()) + GOLEM_MELEE_RANGE;
|
||||
let golem_melee_range = self.body.map_or(0.0, |b| b.max_radius()) + GOLEM_MELEE_RANGE;
|
||||
// Fraction of health, used for activation of shockwave
|
||||
// If golem don't have health for some reason, assume it's full
|
||||
let health_fraction = self.health.map_or(1.0, |h| h.fraction());
|
||||
|
@ -198,7 +198,7 @@ impl ParticleMgr {
|
||||
0.0,
|
||||
)
|
||||
.normalized()
|
||||
* (body.radius() + 4.0)
|
||||
* (body.max_radius() + 4.0)
|
||||
+ Vec3::unit_z() * (body.height() + 2.0) * rng.gen::<f32>();
|
||||
|
||||
Particle::new_directed(
|
||||
@ -688,7 +688,7 @@ impl ParticleMgr {
|
||||
0.0,
|
||||
)
|
||||
.normalized()
|
||||
* (body.radius() + 2.0)
|
||||
* (body.max_radius() + 2.0)
|
||||
+ Vec3::unit_z() * body.height() * rng.gen::<f32>();
|
||||
|
||||
let (start_pos, end_pos) =
|
||||
@ -720,8 +720,8 @@ impl ParticleMgr {
|
||||
|| {
|
||||
let start_pos = pos.0
|
||||
+ Vec3::new(
|
||||
body.radius(),
|
||||
body.radius(),
|
||||
body.max_radius(),
|
||||
body.max_radius(),
|
||||
body.height() / 2.0,
|
||||
)
|
||||
.map(|d| d * rng.gen_range(-1.0..1.0));
|
||||
@ -1021,8 +1021,12 @@ impl ParticleMgr {
|
||||
+ usize::from(self.scheduler.heartbeats(Duration::from_millis(15))),
|
||||
|| {
|
||||
let start_pos = pos.0
|
||||
+ Vec3::new(body.radius(), body.radius(), body.height() / 2.0)
|
||||
.map(|d| d * rng.gen_range(-1.0..1.0));
|
||||
+ Vec3::new(
|
||||
body.max_radius(),
|
||||
body.max_radius(),
|
||||
body.height() / 2.0,
|
||||
)
|
||||
.map(|d| d * rng.gen_range(-1.0..1.0));
|
||||
let end_pos = start_pos
|
||||
+ Vec3::unit_z() * body.height()
|
||||
+ Vec3::<f32>::zero()
|
||||
|
@ -1612,12 +1612,13 @@ fn under_cursor(
|
||||
.filter_map(|(e, p, s, b, i)| {
|
||||
const RADIUS_SCALE: f32 = 3.0;
|
||||
// TODO: use collider radius instead of body radius?
|
||||
let radius = s.map_or(1.0, |s| s.0) * b.radius() * RADIUS_SCALE;
|
||||
let radius = s.map_or(1.0, |s| s.0) * b.max_radius() * RADIUS_SCALE;
|
||||
// Move position up from the feet
|
||||
let pos = Vec3::new(p.0.x, p.0.y, p.0.z + radius);
|
||||
// Distance squared from camera to the entity
|
||||
let dist_sqr = pos.distance_squared(cam_pos);
|
||||
// We only care about interacting with entities that contain items, or are not inanimate (to trade with)
|
||||
// We only care about interacting with entities that contain items,
|
||||
// or are not inanimate (to trade with)
|
||||
if i.is_some() || !matches!(b, comp::Body::Object(_)) {
|
||||
Some((e, pos, radius, dist_sqr))
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user