mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'juliancoffee/gliding-travelers' into 'master'
Better Gliding for NPCs See merge request veloren/veloren!4348
This commit is contained in:
commit
ef5a9663ba
@ -19,6 +19,7 @@
|
||||
(1, ModularWeapon(tool: Staff, material: Wood, hands: None)),
|
||||
(1, ModularWeapon(tool: Sceptre, material: Wood, hands: None)),
|
||||
]), None)),
|
||||
glider: Item("common.items.glider.basic_white"),
|
||||
)),
|
||||
items: [
|
||||
(5, "common.items.consumable.potion_minor"),
|
||||
|
@ -19,6 +19,7 @@
|
||||
(1, ModularWeapon(tool: Staff, material: Bamboo, hands: None)),
|
||||
(1, ModularWeapon(tool: Sceptre, material: Bamboo, hands: None)),
|
||||
]), None)),
|
||||
glider: Item("common.items.glider.leaves"),
|
||||
)),
|
||||
items: [
|
||||
(25, "common.items.consumable.potion_minor"),
|
||||
|
@ -30,6 +30,7 @@
|
||||
(1, ModularWeapon(tool: Staff, material: Ironwood, hands: None)),
|
||||
(1, ModularWeapon(tool: Sceptre, material: Ironwood, hands: None)),
|
||||
]), None)),
|
||||
glider: Item("common.items.glider.butterfly3"),
|
||||
)),
|
||||
items: [
|
||||
(50, "common.items.consumable.potion_med"),
|
||||
|
@ -30,6 +30,7 @@
|
||||
(2, Item("common.items.weapons.staff.laevateinn")),
|
||||
(1, Item("common.items.weapons.sceptre.caduceus")),
|
||||
]), None)),
|
||||
glider: Item("common.items.glider.sunset"),
|
||||
)),
|
||||
items: [
|
||||
(50, "common.items.consumable.potion_big"),
|
||||
|
@ -50,24 +50,29 @@ impl<'a> AgentData<'a> {
|
||||
////////////////////////////////////////
|
||||
// Action Nodes
|
||||
////////////////////////////////////////
|
||||
|
||||
pub fn glider_fall(&self, controller: &mut Controller, read_data: &ReadData) {
|
||||
pub fn glider_equip(&self, controller: &mut Controller, read_data: &ReadData) {
|
||||
self.dismount(controller, read_data);
|
||||
|
||||
controller.push_action(ControlAction::GlideWield);
|
||||
}
|
||||
|
||||
let flight_direction =
|
||||
Vec3::from(self.vel.0.xy().try_normalized().unwrap_or_else(Vec2::zero));
|
||||
let flight_ori = Quaternion::from_scalar_and_vec3((1.0, flight_direction));
|
||||
|
||||
let ori = self.ori.look_vec();
|
||||
let look_dir = if ori.z > 0.0 {
|
||||
flight_ori.rotated_x(-0.1)
|
||||
} else {
|
||||
flight_ori.rotated_x(0.1)
|
||||
// TODO: add the ability to follow the target?
|
||||
pub fn glider_flight(&self, controller: &mut Controller, _read_data: &ReadData) {
|
||||
let Some(fluid) = self.physics_state.in_fluid else {
|
||||
return;
|
||||
};
|
||||
|
||||
let vel = self.vel;
|
||||
|
||||
let comp::Vel(rel_flow) = fluid.relative_flow(vel);
|
||||
|
||||
let is_wind_downwards = rel_flow.z.is_sign_negative();
|
||||
|
||||
let look_dir = if is_wind_downwards {
|
||||
Vec3::from(-rel_flow.xy())
|
||||
} else {
|
||||
-rel_flow
|
||||
};
|
||||
|
||||
let (_, look_dir) = look_dir.into_scalar_and_vec3();
|
||||
controller.inputs.look_dir = Dir::from_unnormalized(look_dir).unwrap_or_else(Dir::forward);
|
||||
}
|
||||
|
||||
|
@ -746,6 +746,13 @@ fn handle_make_npc(
|
||||
scale,
|
||||
loot,
|
||||
} => {
|
||||
// Spread about spawned npcs
|
||||
let vel = Vec3::new(
|
||||
thread_rng().gen_range(-2.0..3.0),
|
||||
thread_rng().gen_range(-2.0..3.0),
|
||||
10.0,
|
||||
);
|
||||
|
||||
let mut entity_builder = server
|
||||
.state
|
||||
.create_npc(
|
||||
@ -760,7 +767,7 @@ fn handle_make_npc(
|
||||
)
|
||||
.with(alignment)
|
||||
.with(scale)
|
||||
.with(comp::Vel(Vec3::new(0.0, 0.0, 0.0)));
|
||||
.with(comp::Vel(vel));
|
||||
|
||||
if let Some(agent) = agent {
|
||||
entity_builder = entity_builder.with(agent);
|
||||
|
@ -137,11 +137,14 @@ impl<'a> System<'a> for Sys {
|
||||
)
|
||||
};
|
||||
|
||||
if !matches!(char_state, CharacterState::LeapMelee(_)) {
|
||||
if !matches!(
|
||||
char_state,
|
||||
CharacterState::LeapMelee(_) | CharacterState::Glide(_)
|
||||
) {
|
||||
// Default to looking in orientation direction
|
||||
// (can be overridden below)
|
||||
//
|
||||
// This definitely breaks LeapMelee and
|
||||
// This definitely breaks LeapMelee, Glide and
|
||||
// probably not only that, do we really need this at all?
|
||||
controller.reset();
|
||||
controller.inputs.look_dir = ori.look_dir();
|
||||
|
@ -5,8 +5,8 @@ use common::{
|
||||
TRADE_INTERACTION_TIME,
|
||||
},
|
||||
dialogue::Subject,
|
||||
Agent, Alignment, BehaviorCapability, BehaviorState, Body, BuffKind, ControlAction,
|
||||
ControlEvent, Controller, InputKind, InventoryEvent, Pos, UtteranceKind,
|
||||
Agent, Alignment, BehaviorCapability, BehaviorState, Body, BuffKind, CharacterState,
|
||||
ControlAction, ControlEvent, Controller, InputKind, InventoryEvent, Pos, UtteranceKind,
|
||||
},
|
||||
path::TraversalConfig,
|
||||
rtsim::{NpcAction, RtSimEntity},
|
||||
@ -79,6 +79,7 @@ impl BehaviorTree {
|
||||
pub fn root() -> Self {
|
||||
Self {
|
||||
tree: vec![
|
||||
maintain_if_gliding,
|
||||
react_on_dangerous_fall,
|
||||
react_if_on_fire,
|
||||
target_if_attacked,
|
||||
@ -177,6 +178,35 @@ impl BehaviorTree {
|
||||
}
|
||||
}
|
||||
|
||||
/// If in gliding, properly maintain it
|
||||
/// If on ground, unwield glider
|
||||
fn maintain_if_gliding(bdata: &mut BehaviorData) -> bool {
|
||||
let Some(char_state) = bdata.read_data.char_states.get(*bdata.agent_data.entity) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
match char_state {
|
||||
CharacterState::Glide(_) => {
|
||||
bdata
|
||||
.agent_data
|
||||
.glider_flight(bdata.controller, bdata.read_data);
|
||||
true
|
||||
},
|
||||
CharacterState::GlideWield(_) => {
|
||||
if bdata.agent_data.physics_state.on_ground.is_some() {
|
||||
bdata.controller.push_action(ControlAction::Unwield);
|
||||
}
|
||||
// Always stop execution if during GlideWield.
|
||||
// - If on ground, the line above will unwield the glider on next
|
||||
// tick
|
||||
// - If in air, we probably wouldn't want to do anything anyway, as
|
||||
// character state code will shift itself to glide on next tick
|
||||
true
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// If falling velocity is critical, throw everything
|
||||
/// and save yourself!
|
||||
///
|
||||
@ -200,7 +230,7 @@ fn react_on_dangerous_fall(bdata: &mut BehaviorData) -> bool {
|
||||
} else if bdata.agent_data.glider_equipped {
|
||||
bdata
|
||||
.agent_data
|
||||
.glider_fall(bdata.controller, bdata.read_data);
|
||||
.glider_equip(bdata.controller, bdata.read_data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -5417,7 +5417,14 @@ pub fn angle_of_attack_text(
|
||||
if v_sq.abs() > 0.0001 {
|
||||
let rel_flow_dir = Dir::new(rel_flow / v_sq.sqrt());
|
||||
let aoe = fluid_dynamics::angle_of_attack(&glider_ori, &rel_flow_dir);
|
||||
format!("Angle of Attack: {:.1}", aoe.to_degrees())
|
||||
let (rel_x, rel_y, rel_z) = (rel_flow.x, rel_flow.y, rel_flow.z);
|
||||
format!(
|
||||
"Angle of Attack: {:.1} ({:.1},{:.1},{:.1})",
|
||||
aoe.to_degrees(),
|
||||
rel_x,
|
||||
rel_y,
|
||||
rel_z
|
||||
)
|
||||
} else {
|
||||
"Angle of Attack: Not moving".to_owned()
|
||||
}
|
||||
|
@ -19,8 +19,8 @@ use common::{
|
||||
inventory::slot::{EquipSlot, Slot},
|
||||
invite::InviteKind,
|
||||
item::{tool::ToolKind, ItemDesc},
|
||||
CharacterActivity, ChatType, Content, InputKind, InventoryUpdateEvent, Pos, PresenceKind,
|
||||
Stats, UtteranceKind, Vel,
|
||||
CharacterActivity, ChatType, Content, Fluid, InputKind, InventoryUpdateEvent, Pos,
|
||||
PresenceKind, Stats, UtteranceKind, Vel,
|
||||
},
|
||||
consts::MAX_MOUNT_RANGE,
|
||||
event::UpdateCharacterMetadata,
|
||||
@ -1331,33 +1331,12 @@ impl PlayState for SessionState {
|
||||
.read_storage::<comp::PhysicsState>()
|
||||
.get(entity)?
|
||||
.in_fluid?;
|
||||
ecs.read_storage::<Vel>()
|
||||
.get(entity)
|
||||
.map(|vel| fluid.relative_flow(vel).0)
|
||||
.map(|rel_flow| {
|
||||
let is_wind_downwards =
|
||||
rel_flow.dot(Vec3::unit_z()).is_sign_negative();
|
||||
if !self.free_look {
|
||||
if is_wind_downwards {
|
||||
self.scene.camera().forward_xy().into()
|
||||
} else {
|
||||
let windwards = rel_flow
|
||||
* self
|
||||
.scene
|
||||
.camera()
|
||||
.forward_xy()
|
||||
.dot(rel_flow.xy())
|
||||
.signum();
|
||||
Plane::from(Dir::new(self.scene.camera().right()))
|
||||
.projection(windwards)
|
||||
}
|
||||
} else if is_wind_downwards {
|
||||
Vec3::from(-rel_flow.xy())
|
||||
} else {
|
||||
-rel_flow
|
||||
}
|
||||
})
|
||||
.and_then(Dir::from_unnormalized)
|
||||
let vel = *ecs.read_storage::<Vel>().get(entity)?;
|
||||
let free_look = self.free_look;
|
||||
let dir_forward_xy = self.scene.camera().forward_xy();
|
||||
let dir_right = self.scene.camera().right();
|
||||
|
||||
auto_glide(fluid, vel, free_look, dir_forward_xy, dir_right)
|
||||
})
|
||||
{
|
||||
self.key_state.auto_walk = false;
|
||||
@ -1567,18 +1546,30 @@ impl PlayState for SessionState {
|
||||
let debug_info = global_state.settings.interface.toggle_debug.then(|| {
|
||||
let client = self.client.borrow();
|
||||
let ecs = client.state().ecs();
|
||||
let entity = client.entity();
|
||||
let coordinates = ecs.read_storage::<Pos>().get(entity).cloned();
|
||||
let velocity = ecs.read_storage::<Vel>().get(entity).cloned();
|
||||
let ori = ecs.read_storage::<comp::Ori>().get(entity).cloned();
|
||||
let look_dir = self.inputs.look_dir;
|
||||
let client_entity = client.entity();
|
||||
let coordinates = ecs.read_storage::<Pos>().get(viewpoint_entity).cloned();
|
||||
let velocity = ecs.read_storage::<Vel>().get(viewpoint_entity).cloned();
|
||||
let ori = ecs
|
||||
.read_storage::<comp::Ori>()
|
||||
.get(viewpoint_entity)
|
||||
.cloned();
|
||||
// NOTE: at the time of writing, it will always output default
|
||||
// look_dir in Specate mode, because Controller isn't synced
|
||||
let look_dir = if viewpoint_entity == client_entity {
|
||||
self.inputs.look_dir
|
||||
} else {
|
||||
ecs.read_storage::<comp::Controller>()
|
||||
.get(viewpoint_entity)
|
||||
.map(|c| c.inputs.look_dir)
|
||||
.unwrap_or_default()
|
||||
};
|
||||
let in_fluid = ecs
|
||||
.read_storage::<comp::PhysicsState>()
|
||||
.get(entity)
|
||||
.get(viewpoint_entity)
|
||||
.and_then(|state| state.in_fluid);
|
||||
let character_state = ecs
|
||||
.read_storage::<comp::CharacterState>()
|
||||
.get(entity)
|
||||
.get(viewpoint_entity)
|
||||
.cloned();
|
||||
|
||||
DebugInfo {
|
||||
@ -2265,3 +2256,31 @@ fn find_shortest_distance(arr: &[Option<f32>]) -> Option<f32> {
|
||||
.filter_map(|x| *x)
|
||||
.min_by(|d1, d2| OrderedFloat(*d1).cmp(&OrderedFloat(*d2)))
|
||||
}
|
||||
|
||||
// TODO: Can probably be exported in some way for AI, somehow
|
||||
fn auto_glide(
|
||||
fluid: Fluid,
|
||||
vel: Vel,
|
||||
free_look: bool,
|
||||
dir_forward_xy: Vec2<f32>,
|
||||
dir_right: Vec3<f32>,
|
||||
) -> Option<Dir> {
|
||||
let Vel(rel_flow) = fluid.relative_flow(&vel);
|
||||
|
||||
let is_wind_downwards = rel_flow.z.is_sign_negative();
|
||||
|
||||
let dir = if free_look {
|
||||
if is_wind_downwards {
|
||||
Vec3::from(-rel_flow.xy())
|
||||
} else {
|
||||
-rel_flow
|
||||
}
|
||||
} else if is_wind_downwards {
|
||||
dir_forward_xy.into()
|
||||
} else {
|
||||
let windwards = rel_flow * dir_forward_xy.dot(rel_flow.xy()).signum();
|
||||
Plane::from(Dir::new(dir_right)).projection(windwards)
|
||||
};
|
||||
|
||||
Dir::from_unnormalized(dir)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user