implement targeting based on player orientation

This commit is contained in:
yuni 2024-04-05 19:03:50 +02:00
parent a37ba60eaf
commit 1906366463
4 changed files with 77 additions and 10 deletions

View file

@ -44,6 +44,7 @@ pub struct VehicleEnterExitEvent {
#[derive(Component)] #[derive(Component)]
pub struct Actor { pub struct Actor {
pub id: String, pub id: String,
pub name: Option<String>,
pub m: f32, // mass pub m: f32, // mass
pub inside_entity: u32, pub inside_entity: u32,
pub camdistance: f32, pub camdistance: f32,
@ -52,6 +53,7 @@ impl Default for Actor {
fn default() -> Self { fn default() -> Self {
Self { Self {
id: "".to_string(), id: "".to_string(),
name: None,
m: 100.0, m: 100.0,
inside_entity: NO_RIDE, inside_entity: NO_RIDE,
camdistance: 15.0, camdistance: 15.0,

View file

@ -31,7 +31,7 @@ struct ParserState {
class: DefClass, class: DefClass,
// Generic fields // Generic fields
name: String, name: Option<String>,
chat: String, chat: String,
// Actor fields // Actor fields
@ -86,7 +86,7 @@ impl Default for ParserState {
let default_engine = actor::Engine::default(); let default_engine = actor::Engine::default();
Self { Self {
class: DefClass::None, class: DefClass::None,
name: "NONAME".to_string(), name: None,
chat: "".to_string(), chat: "".to_string(),
id: "".to_string(), id: "".to_string(),
@ -148,7 +148,7 @@ impl ParserState {
fn as_chatbranch(&self) -> chat::ChatBranch { fn as_chatbranch(&self) -> chat::ChatBranch {
return chat::ChatBranch { return chat::ChatBranch {
id: self.chat.clone(), id: self.chat.clone(),
name: self.name.clone(), name: self.name.clone().unwrap_or("".to_string()),
label: self.label.clone(), label: self.label.clone(),
delay: self.delay.clone(), delay: self.delay.clone(),
sound: if self.is_choice { "".to_string() } else { "chat".to_string() }, sound: if self.is_choice { "".to_string() } else { "chat".to_string() },
@ -474,7 +474,7 @@ pub fn load_defs(
} }
["name", name] => { ["name", name] => {
debug!("Registering name: {}", name); debug!("Registering name: {}", name);
state.name = name.to_string(); state.name = Some(name.to_string());
} }
["msg", sleep, text] => { ["msg", sleep, text] => {
debug!("Registering message (sleep={}): {}", sleep, text); debug!("Registering message (sleep={}): {}", sleep, text);
@ -584,6 +584,7 @@ fn spawn_entities(
let mut actor = commands.spawn_empty(); let mut actor = commands.spawn_empty();
actor.insert(actor::Actor { actor.insert(actor::Actor {
id: state.id.clone(), id: state.id.clone(),
name: state.name.clone(),
camdistance: state.camdistance, camdistance: state.camdistance,
..default() ..default()
}); });

View file

@ -1,5 +1,6 @@
actor 0 0 0 jupiter actor 0 0 0 jupiter
id jupiter id jupiter
name Jupiter
radius 71492e3 radius 71492e3
sphere yes sphere yes
physics off physics off
@ -36,6 +37,7 @@ actor 10 -30 20 MeteorAceGT
clickable no clickable no
actor 0 0 0 io actor 0 0 0 io
name Io
relativeto jupiter relativeto jupiter
orbit 421700e3 0.65 orbit 421700e3 0.65
radius 1822e3 radius 1822e3
@ -46,6 +48,7 @@ actor 0 0 0 io
physics off physics off
actor 0 0 0 europa actor 0 0 0 europa
name Europa
relativeto jupiter relativeto jupiter
orbit 670900e3 0.35 orbit 670900e3 0.35
radius 1561e3 radius 1561e3
@ -56,6 +59,7 @@ actor 0 0 0 europa
physics off physics off
actor 0 0 0 ganymede actor 0 0 0 ganymede
name Ganymede
relativeto jupiter relativeto jupiter
orbit 1070400e3 0.93 orbit 1070400e3 0.93
radius 2634e3 radius 2634e3
@ -66,6 +70,7 @@ actor 0 0 0 ganymede
physics off physics off
actor 0 0 0 callisto actor 0 0 0 callisto
name Callisto
relativeto jupiter relativeto jupiter
orbit 1882700e3 0.45 orbit 1882700e3 0.45
radius 2410e3 radius 2410e3
@ -76,6 +81,7 @@ actor 0 0 0 callisto
physics off physics off
actor 0 0 0 moonlet actor 0 0 0 moonlet
name Thebe
relativeto jupiter relativeto jupiter
id thebe id thebe
orbit 221900e3 0.66 orbit 221900e3 0.66
@ -84,12 +90,14 @@ actor 0 0 0 moonlet
angularmomentum 0 0.25 0 angularmomentum 0 0.25 0
actor 3000 0 0 moonlet actor 3000 0 0 moonlet
name Moonlet
relativeto player relativeto player
scale 500 scale 500
mass 10000000 mass 10000000
angularmomentum 0 0.15 0 angularmomentum 0 0.15 0
actor -200 -110 1000 satellite actor -200 -110 1000 satellite
name "Communications Satellite"
relativeto player relativeto player
scale 40 scale 40
wants maxrotation 0 wants maxrotation 0
@ -101,6 +109,7 @@ actor -200 -110 1000 satellite
mass 10 mass 10
actor 1000 20 300 monolith actor 1000 20 300 monolith
name "Mysterious Monolith 1"
relativeto player relativeto player
scale 2 scale 2
mass 1000 mass 1000
@ -110,6 +119,7 @@ actor 1000 20 300 monolith
thrust 0 0 0 30 1 thrust 0 0 0 30 1
actor 10000 2000 -3500 monolith actor 10000 2000 -3500 monolith
name "Mysterious Monolith 2"
relativeto player relativeto player
scale 2 scale 2
mass 1000 mass 1000
@ -119,6 +129,7 @@ actor 10000 2000 -3500 monolith
thrust 0 0 0 30 1 thrust 0 0 0 30 1
actor -8000 -1000 -100 monolith actor -8000 -1000 -100 monolith
name "Mysterious Monolith 3"
relativeto player relativeto player
scale 2 scale 2
mass 1000 mass 1000
@ -128,6 +139,7 @@ actor -8000 -1000 -100 monolith
thrust 0 0 0 30 1 thrust 0 0 0 30 1
actor -3300 10 0 pizzeria actor -3300 10 0 pizzeria
name "Pizzeria Asteroid"
relativeto player relativeto player
id pizzeria id pizzeria
scale 40 scale 40
@ -144,17 +156,21 @@ actor -3300 10 0 pizzeria
camdistance 50 camdistance 50
mass 3000 mass 3000
angularmomentum 0 0 0.2 angularmomentum 0 0 0.2
clickable no
actor -100 63 -13 pizzasign actor -100 63 -13 pizzasign
name "Pizzeria Sign"
relativeto pizzeria relativeto pizzeria
scale 20 scale 20
mass 200 mass 200
rotationy 0.45 rotationy 0.45
angularmomentum 0 0 0 angularmomentum 0 0 0
actor -16 -10 0 lightorb actor -16 -10 0 lightorb
name "Light Orb"
relativeto pizzeria relativeto pizzeria
scale 0.5 scale 0.5
light FF8F4A 1000000 light FF8F4A 1000000
actor -14 -3 -2 lightorb actor -14 -3 -2 lightorb
name "Light Orb"
relativeto pizzeria relativeto pizzeria
scale 0.5 scale 0.5
light FF8F4A 1000000 light FF8F4A 1000000

View file

@ -306,6 +306,24 @@ fn setup(
..default() ..default()
} }
), ),
TextSection::new(
"\nTarget: ",
TextStyle {
font: asset_server.load(FONT),
font_size: settings.font_size_hud,
color: Color::GRAY,
..default()
},
),
TextSection::new(
"",
TextStyle {
font: asset_server.load(FONT),
font_size: settings.font_size_hud,
color: Color::GRAY,
..default()
}
),
TextSection::new( TextSection::new(
"\n☢ HAZARD\n\n", "\n☢ HAZARD\n\n",
TextStyle { TextStyle {
@ -427,7 +445,7 @@ fn update(
mut query_chat: Query<&mut Text, (With<ChatText>, Without<GaugesText>)>, mut query_chat: Query<&mut Text, (With<ChatText>, Without<GaugesText>)>,
query_all_actors: Query<&actor::Actor>, query_all_actors: Query<&actor::Actor>,
settings: Res<settings::Settings>, settings: Res<settings::Settings>,
q_target: Query<&Position, With<IsTargeted>>, q_target: Query<(&Position, &actor::Actor), With<IsTargeted>>,
) { ) {
// TODO only when hud is actually on // TODO only when hud is actually on
if timer.0.tick(time.delta()).just_finished() || log.needs_rerendering { if timer.0.tick(time.delta()).just_finished() || log.needs_rerendering {
@ -471,7 +489,7 @@ fn update(
// Target display // Target display
let target: Option<DVec3>; let target: Option<DVec3>;
if let Ok(targetpos) = q_target.get_single() { if let Ok((targetpos, actr)) = q_target.get_single() {
target = Some(targetpos.0); target = Some(targetpos.0);
} }
else if q_target.is_empty() { else if q_target.is_empty() {
@ -488,6 +506,16 @@ fn update(
else { else {
text.sections[7].value = format!("ERROR: MULTIPLE TARGETS"); text.sections[7].value = format!("ERROR: MULTIPLE TARGETS");
} }
if q_target.is_empty() {
text.sections[21].value = "Jupiter".to_string();
}
else {
let targets: Vec<String> = q_target
.iter()
.map(|(_pos, act)| act.name.clone().unwrap_or("<unnamed>".to_string()))
.collect();
text.sections[21].value = targets.join(", ");
}
} }
} }
@ -570,18 +598,38 @@ fn handle_input(
ew_togglemusic.send(audio::ToggleMusicEvent()); ew_togglemusic.send(audio::ToggleMusicEvent());
} }
if mouse_input.just_pressed(settings.key_selectobject) { if mouse_input.just_pressed(settings.key_selectobject) {
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Switch)); //ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Switch));
if q_target.is_empty() { if q_target.is_empty() {
if let Ok(camtrans) = q_camera.get_single() { if let Ok(camtrans) = q_camera.get_single() {
let mut closest_entity: Option<Entity> = None;
let mut closest_distance: f32 = f32::MAX;
let target_vector = (camtrans.rotation * Vec3::new(0.0, 0.0, -1.0))
.normalize_or_zero();
let field_of_view = 20.0f32.to_radians();
let max_angle = field_of_view / 2.0;
let max_cosine_of_angle = max_angle.cos();
//let maximum_cosine_of_angle = (field_of_view / 2.0).cos();
for (entity, trans) in &q_objects { for (entity, trans) in &q_objects {
// Use Transform instead of Position because we're basing this // Use Transform instead of Position because we're basing this
// not on the player mesh but on the camera, which doesn't have a position. // not on the player mesh but on the camera, which doesn't have a position.
if trans.translation.distance(camtrans.translation) < 50.0 { let pos_vector = (trans.translation - camtrans.translation)
commands.entity(entity).insert(IsTargeted); .normalize_or_zero();
break; let cosine_of_angle = target_vector.dot(pos_vector);
let angle = cosine_of_angle.acos();
if angle <= max_angle {
// It's in the field of view!
//commands.entity(entity).insert(IsTargeted);
let distance = trans.translation.distance(camtrans.translation);
if distance < closest_distance {
closest_distance = distance;
closest_entity = Some(entity);
} }
} }
} }
if let Some(entity) = closest_entity {
commands.entity(entity).insert(IsTargeted);
}
}
} }
else { else {
for entity in &q_target { for entity in &q_target {