From 19063664633d5fc86822d4d176038c16dfc92da2 Mon Sep 17 00:00:00 2001 From: hut Date: Fri, 5 Apr 2024 19:03:50 +0200 Subject: [PATCH] implement targeting based on player orientation --- src/actor.rs | 2 ++ src/commands.rs | 9 ++++---- src/defs.txt | 16 +++++++++++++ src/hud.rs | 60 ++++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 77 insertions(+), 10 deletions(-) diff --git a/src/actor.rs b/src/actor.rs index 9e756c1..547bb9b 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -44,6 +44,7 @@ pub struct VehicleEnterExitEvent { #[derive(Component)] pub struct Actor { pub id: String, + pub name: Option, pub m: f32, // mass pub inside_entity: u32, pub camdistance: f32, @@ -52,6 +53,7 @@ impl Default for Actor { fn default() -> Self { Self { id: "".to_string(), + name: None, m: 100.0, inside_entity: NO_RIDE, camdistance: 15.0, diff --git a/src/commands.rs b/src/commands.rs index 495a879..d59c271 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -31,7 +31,7 @@ struct ParserState { class: DefClass, // Generic fields - name: String, + name: Option, chat: String, // Actor fields @@ -86,7 +86,7 @@ impl Default for ParserState { let default_engine = actor::Engine::default(); Self { class: DefClass::None, - name: "NONAME".to_string(), + name: None, chat: "".to_string(), id: "".to_string(), @@ -148,7 +148,7 @@ impl ParserState { fn as_chatbranch(&self) -> chat::ChatBranch { return chat::ChatBranch { id: self.chat.clone(), - name: self.name.clone(), + name: self.name.clone().unwrap_or("".to_string()), label: self.label.clone(), delay: self.delay.clone(), sound: if self.is_choice { "".to_string() } else { "chat".to_string() }, @@ -474,7 +474,7 @@ pub fn load_defs( } ["name", name] => { debug!("Registering name: {}", name); - state.name = name.to_string(); + state.name = Some(name.to_string()); } ["msg", sleep, text] => { debug!("Registering message (sleep={}): {}", sleep, text); @@ -584,6 +584,7 @@ fn spawn_entities( let mut actor = commands.spawn_empty(); actor.insert(actor::Actor { id: state.id.clone(), + name: state.name.clone(), camdistance: state.camdistance, ..default() }); diff --git a/src/defs.txt b/src/defs.txt index 044d0f3..39c7564 100644 --- a/src/defs.txt +++ b/src/defs.txt @@ -1,5 +1,6 @@ actor 0 0 0 jupiter id jupiter + name Jupiter radius 71492e3 sphere yes physics off @@ -36,6 +37,7 @@ actor 10 -30 20 MeteorAceGT clickable no actor 0 0 0 io + name Io relativeto jupiter orbit 421700e3 0.65 radius 1822e3 @@ -46,6 +48,7 @@ actor 0 0 0 io physics off actor 0 0 0 europa + name Europa relativeto jupiter orbit 670900e3 0.35 radius 1561e3 @@ -56,6 +59,7 @@ actor 0 0 0 europa physics off actor 0 0 0 ganymede + name Ganymede relativeto jupiter orbit 1070400e3 0.93 radius 2634e3 @@ -66,6 +70,7 @@ actor 0 0 0 ganymede physics off actor 0 0 0 callisto + name Callisto relativeto jupiter orbit 1882700e3 0.45 radius 2410e3 @@ -76,6 +81,7 @@ actor 0 0 0 callisto physics off actor 0 0 0 moonlet + name Thebe relativeto jupiter id thebe orbit 221900e3 0.66 @@ -84,12 +90,14 @@ actor 0 0 0 moonlet angularmomentum 0 0.25 0 actor 3000 0 0 moonlet + name Moonlet relativeto player scale 500 mass 10000000 angularmomentum 0 0.15 0 actor -200 -110 1000 satellite + name "Communications Satellite" relativeto player scale 40 wants maxrotation 0 @@ -101,6 +109,7 @@ actor -200 -110 1000 satellite mass 10 actor 1000 20 300 monolith + name "Mysterious Monolith 1" relativeto player scale 2 mass 1000 @@ -110,6 +119,7 @@ actor 1000 20 300 monolith thrust 0 0 0 30 1 actor 10000 2000 -3500 monolith + name "Mysterious Monolith 2" relativeto player scale 2 mass 1000 @@ -119,6 +129,7 @@ actor 10000 2000 -3500 monolith thrust 0 0 0 30 1 actor -8000 -1000 -100 monolith + name "Mysterious Monolith 3" relativeto player scale 2 mass 1000 @@ -128,6 +139,7 @@ actor -8000 -1000 -100 monolith thrust 0 0 0 30 1 actor -3300 10 0 pizzeria + name "Pizzeria Asteroid" relativeto player id pizzeria scale 40 @@ -144,17 +156,21 @@ actor -3300 10 0 pizzeria camdistance 50 mass 3000 angularmomentum 0 0 0.2 + clickable no actor -100 63 -13 pizzasign + name "Pizzeria Sign" relativeto pizzeria scale 20 mass 200 rotationy 0.45 angularmomentum 0 0 0 actor -16 -10 0 lightorb + name "Light Orb" relativeto pizzeria scale 0.5 light FF8F4A 1000000 actor -14 -3 -2 lightorb + name "Light Orb" relativeto pizzeria scale 0.5 light FF8F4A 1000000 diff --git a/src/hud.rs b/src/hud.rs index e01387e..5f65c5c 100644 --- a/src/hud.rs +++ b/src/hud.rs @@ -306,6 +306,24 @@ fn setup( ..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( "\n☢ HAZARD\n\n", TextStyle { @@ -427,7 +445,7 @@ fn update( mut query_chat: Query<&mut Text, (With, Without)>, query_all_actors: Query<&actor::Actor>, settings: Res, - q_target: Query<&Position, With>, + q_target: Query<(&Position, &actor::Actor), With>, ) { // TODO only when hud is actually on if timer.0.tick(time.delta()).just_finished() || log.needs_rerendering { @@ -471,7 +489,7 @@ fn update( // Target display let target: Option; - if let Ok(targetpos) = q_target.get_single() { + if let Ok((targetpos, actr)) = q_target.get_single() { target = Some(targetpos.0); } else if q_target.is_empty() { @@ -488,6 +506,16 @@ fn update( else { text.sections[7].value = format!("ERROR: MULTIPLE TARGETS"); } + if q_target.is_empty() { + text.sections[21].value = "Jupiter".to_string(); + } + else { + let targets: Vec = q_target + .iter() + .map(|(_pos, act)| act.name.clone().unwrap_or("".to_string())) + .collect(); + text.sections[21].value = targets.join(", "); + } } } @@ -570,17 +598,37 @@ fn handle_input( ew_togglemusic.send(audio::ToggleMusicEvent()); } 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 let Ok(camtrans) = q_camera.get_single() { + let mut closest_entity: Option = 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 { // 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. - if trans.translation.distance(camtrans.translation) < 50.0 { - commands.entity(entity).insert(IsTargeted); - break; + let pos_vector = (trans.translation - camtrans.translation) + .normalize_or_zero(); + 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 {