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: Staff, material: Wood, hands: None)),
|
||||||
(1, ModularWeapon(tool: Sceptre, material: Wood, hands: None)),
|
(1, ModularWeapon(tool: Sceptre, material: Wood, hands: None)),
|
||||||
]), None)),
|
]), None)),
|
||||||
|
glider: Item("common.items.glider.basic_white"),
|
||||||
)),
|
)),
|
||||||
items: [
|
items: [
|
||||||
(5, "common.items.consumable.potion_minor"),
|
(5, "common.items.consumable.potion_minor"),
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
(1, ModularWeapon(tool: Staff, material: Bamboo, hands: None)),
|
(1, ModularWeapon(tool: Staff, material: Bamboo, hands: None)),
|
||||||
(1, ModularWeapon(tool: Sceptre, material: Bamboo, hands: None)),
|
(1, ModularWeapon(tool: Sceptre, material: Bamboo, hands: None)),
|
||||||
]), None)),
|
]), None)),
|
||||||
|
glider: Item("common.items.glider.leaves"),
|
||||||
)),
|
)),
|
||||||
items: [
|
items: [
|
||||||
(25, "common.items.consumable.potion_minor"),
|
(25, "common.items.consumable.potion_minor"),
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
(1, ModularWeapon(tool: Staff, material: Ironwood, hands: None)),
|
(1, ModularWeapon(tool: Staff, material: Ironwood, hands: None)),
|
||||||
(1, ModularWeapon(tool: Sceptre, material: Ironwood, hands: None)),
|
(1, ModularWeapon(tool: Sceptre, material: Ironwood, hands: None)),
|
||||||
]), None)),
|
]), None)),
|
||||||
|
glider: Item("common.items.glider.butterfly3"),
|
||||||
)),
|
)),
|
||||||
items: [
|
items: [
|
||||||
(50, "common.items.consumable.potion_med"),
|
(50, "common.items.consumable.potion_med"),
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
(2, Item("common.items.weapons.staff.laevateinn")),
|
(2, Item("common.items.weapons.staff.laevateinn")),
|
||||||
(1, Item("common.items.weapons.sceptre.caduceus")),
|
(1, Item("common.items.weapons.sceptre.caduceus")),
|
||||||
]), None)),
|
]), None)),
|
||||||
|
glider: Item("common.items.glider.sunset"),
|
||||||
)),
|
)),
|
||||||
items: [
|
items: [
|
||||||
(50, "common.items.consumable.potion_big"),
|
(50, "common.items.consumable.potion_big"),
|
||||||
|
@ -50,24 +50,29 @@ impl<'a> AgentData<'a> {
|
|||||||
////////////////////////////////////////
|
////////////////////////////////////////
|
||||||
// Action Nodes
|
// Action Nodes
|
||||||
////////////////////////////////////////
|
////////////////////////////////////////
|
||||||
|
pub fn glider_equip(&self, controller: &mut Controller, read_data: &ReadData) {
|
||||||
pub fn glider_fall(&self, controller: &mut Controller, read_data: &ReadData) {
|
|
||||||
self.dismount(controller, read_data);
|
self.dismount(controller, read_data);
|
||||||
|
|
||||||
controller.push_action(ControlAction::GlideWield);
|
controller.push_action(ControlAction::GlideWield);
|
||||||
|
}
|
||||||
|
|
||||||
let flight_direction =
|
// TODO: add the ability to follow the target?
|
||||||
Vec3::from(self.vel.0.xy().try_normalized().unwrap_or_else(Vec2::zero));
|
pub fn glider_flight(&self, controller: &mut Controller, _read_data: &ReadData) {
|
||||||
let flight_ori = Quaternion::from_scalar_and_vec3((1.0, flight_direction));
|
let Some(fluid) = self.physics_state.in_fluid else {
|
||||||
|
return;
|
||||||
let ori = self.ori.look_vec();
|
};
|
||||||
let look_dir = if ori.z > 0.0 {
|
|
||||||
flight_ori.rotated_x(-0.1)
|
let vel = self.vel;
|
||||||
} else {
|
|
||||||
flight_ori.rotated_x(0.1)
|
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);
|
controller.inputs.look_dir = Dir::from_unnormalized(look_dir).unwrap_or_else(Dir::forward);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -746,6 +746,13 @@ fn handle_make_npc(
|
|||||||
scale,
|
scale,
|
||||||
loot,
|
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
|
let mut entity_builder = server
|
||||||
.state
|
.state
|
||||||
.create_npc(
|
.create_npc(
|
||||||
@ -760,7 +767,7 @@ fn handle_make_npc(
|
|||||||
)
|
)
|
||||||
.with(alignment)
|
.with(alignment)
|
||||||
.with(scale)
|
.with(scale)
|
||||||
.with(comp::Vel(Vec3::new(0.0, 0.0, 0.0)));
|
.with(comp::Vel(vel));
|
||||||
|
|
||||||
if let Some(agent) = agent {
|
if let Some(agent) = agent {
|
||||||
entity_builder = entity_builder.with(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
|
// Default to looking in orientation direction
|
||||||
// (can be overridden below)
|
// (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?
|
// probably not only that, do we really need this at all?
|
||||||
controller.reset();
|
controller.reset();
|
||||||
controller.inputs.look_dir = ori.look_dir();
|
controller.inputs.look_dir = ori.look_dir();
|
||||||
|
@ -5,8 +5,8 @@ use common::{
|
|||||||
TRADE_INTERACTION_TIME,
|
TRADE_INTERACTION_TIME,
|
||||||
},
|
},
|
||||||
dialogue::Subject,
|
dialogue::Subject,
|
||||||
Agent, Alignment, BehaviorCapability, BehaviorState, Body, BuffKind, ControlAction,
|
Agent, Alignment, BehaviorCapability, BehaviorState, Body, BuffKind, CharacterState,
|
||||||
ControlEvent, Controller, InputKind, InventoryEvent, Pos, UtteranceKind,
|
ControlAction, ControlEvent, Controller, InputKind, InventoryEvent, Pos, UtteranceKind,
|
||||||
},
|
},
|
||||||
path::TraversalConfig,
|
path::TraversalConfig,
|
||||||
rtsim::{NpcAction, RtSimEntity},
|
rtsim::{NpcAction, RtSimEntity},
|
||||||
@ -79,6 +79,7 @@ impl BehaviorTree {
|
|||||||
pub fn root() -> Self {
|
pub fn root() -> Self {
|
||||||
Self {
|
Self {
|
||||||
tree: vec![
|
tree: vec![
|
||||||
|
maintain_if_gliding,
|
||||||
react_on_dangerous_fall,
|
react_on_dangerous_fall,
|
||||||
react_if_on_fire,
|
react_if_on_fire,
|
||||||
target_if_attacked,
|
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
|
/// If falling velocity is critical, throw everything
|
||||||
/// and save yourself!
|
/// and save yourself!
|
||||||
///
|
///
|
||||||
@ -200,7 +230,7 @@ fn react_on_dangerous_fall(bdata: &mut BehaviorData) -> bool {
|
|||||||
} else if bdata.agent_data.glider_equipped {
|
} else if bdata.agent_data.glider_equipped {
|
||||||
bdata
|
bdata
|
||||||
.agent_data
|
.agent_data
|
||||||
.glider_fall(bdata.controller, bdata.read_data);
|
.glider_equip(bdata.controller, bdata.read_data);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5417,7 +5417,14 @@ pub fn angle_of_attack_text(
|
|||||||
if v_sq.abs() > 0.0001 {
|
if v_sq.abs() > 0.0001 {
|
||||||
let rel_flow_dir = Dir::new(rel_flow / v_sq.sqrt());
|
let rel_flow_dir = Dir::new(rel_flow / v_sq.sqrt());
|
||||||
let aoe = fluid_dynamics::angle_of_attack(&glider_ori, &rel_flow_dir);
|
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 {
|
} else {
|
||||||
"Angle of Attack: Not moving".to_owned()
|
"Angle of Attack: Not moving".to_owned()
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,8 @@ use common::{
|
|||||||
inventory::slot::{EquipSlot, Slot},
|
inventory::slot::{EquipSlot, Slot},
|
||||||
invite::InviteKind,
|
invite::InviteKind,
|
||||||
item::{tool::ToolKind, ItemDesc},
|
item::{tool::ToolKind, ItemDesc},
|
||||||
CharacterActivity, ChatType, Content, InputKind, InventoryUpdateEvent, Pos, PresenceKind,
|
CharacterActivity, ChatType, Content, Fluid, InputKind, InventoryUpdateEvent, Pos,
|
||||||
Stats, UtteranceKind, Vel,
|
PresenceKind, Stats, UtteranceKind, Vel,
|
||||||
},
|
},
|
||||||
consts::MAX_MOUNT_RANGE,
|
consts::MAX_MOUNT_RANGE,
|
||||||
event::UpdateCharacterMetadata,
|
event::UpdateCharacterMetadata,
|
||||||
@ -1331,33 +1331,12 @@ impl PlayState for SessionState {
|
|||||||
.read_storage::<comp::PhysicsState>()
|
.read_storage::<comp::PhysicsState>()
|
||||||
.get(entity)?
|
.get(entity)?
|
||||||
.in_fluid?;
|
.in_fluid?;
|
||||||
ecs.read_storage::<Vel>()
|
let vel = *ecs.read_storage::<Vel>().get(entity)?;
|
||||||
.get(entity)
|
let free_look = self.free_look;
|
||||||
.map(|vel| fluid.relative_flow(vel).0)
|
let dir_forward_xy = self.scene.camera().forward_xy();
|
||||||
.map(|rel_flow| {
|
let dir_right = self.scene.camera().right();
|
||||||
let is_wind_downwards =
|
|
||||||
rel_flow.dot(Vec3::unit_z()).is_sign_negative();
|
auto_glide(fluid, vel, free_look, dir_forward_xy, dir_right)
|
||||||
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)
|
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
self.key_state.auto_walk = false;
|
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 debug_info = global_state.settings.interface.toggle_debug.then(|| {
|
||||||
let client = self.client.borrow();
|
let client = self.client.borrow();
|
||||||
let ecs = client.state().ecs();
|
let ecs = client.state().ecs();
|
||||||
let entity = client.entity();
|
let client_entity = client.entity();
|
||||||
let coordinates = ecs.read_storage::<Pos>().get(entity).cloned();
|
let coordinates = ecs.read_storage::<Pos>().get(viewpoint_entity).cloned();
|
||||||
let velocity = ecs.read_storage::<Vel>().get(entity).cloned();
|
let velocity = ecs.read_storage::<Vel>().get(viewpoint_entity).cloned();
|
||||||
let ori = ecs.read_storage::<comp::Ori>().get(entity).cloned();
|
let ori = ecs
|
||||||
let look_dir = self.inputs.look_dir;
|
.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
|
let in_fluid = ecs
|
||||||
.read_storage::<comp::PhysicsState>()
|
.read_storage::<comp::PhysicsState>()
|
||||||
.get(entity)
|
.get(viewpoint_entity)
|
||||||
.and_then(|state| state.in_fluid);
|
.and_then(|state| state.in_fluid);
|
||||||
let character_state = ecs
|
let character_state = ecs
|
||||||
.read_storage::<comp::CharacterState>()
|
.read_storage::<comp::CharacterState>()
|
||||||
.get(entity)
|
.get(viewpoint_entity)
|
||||||
.cloned();
|
.cloned();
|
||||||
|
|
||||||
DebugInfo {
|
DebugInfo {
|
||||||
@ -2265,3 +2256,31 @@ fn find_shortest_distance(arr: &[Option<f32>]) -> Option<f32> {
|
|||||||
.filter_map(|x| *x)
|
.filter_map(|x| *x)
|
||||||
.min_by(|d1, d2| OrderedFloat(*d1).cmp(&OrderedFloat(*d2)))
|
.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