Match counter-strike launch audio and live smoke to replay (#109)
build / mod (push) Successful in 8m46s
build / mod (push) Successful in 8m46s
This commit is contained in:
@@ -535,10 +535,15 @@ impl Player {
|
||||
}
|
||||
}
|
||||
for tr in rec.tracks.iter().filter(|tr| (self.heard..self.t).contains(&(tr.first as f64))) {
|
||||
if let What::Missile(kind, _) = tr.what
|
||||
let event = match tr.what {
|
||||
What::Missile(kind, _) => Some(kind.launch_sound()),
|
||||
What::Strike => Some(DroneKind::Kalibr.launch_sound()),
|
||||
What::Drone(_) => None,
|
||||
};
|
||||
if let Some(event) = event
|
||||
&& let Some(p) = tr.at(rec.origin, tr.first as f64)
|
||||
{
|
||||
out.sound(kind.launch_sound(), heard(p), 1.0, 1.0);
|
||||
out.sound(event, heard(p), 1.0, 1.0);
|
||||
}
|
||||
}
|
||||
self.heard = self.t;
|
||||
@@ -1102,6 +1107,27 @@ mod engine_tests {
|
||||
let _ = rec;
|
||||
p
|
||||
}
|
||||
#[test]
|
||||
fn counter_strike_launch_is_heard_once_and_can_be_replayed_after_seek() {
|
||||
let mut rec = Recording::blank(100);
|
||||
rec.straight(1, What::Strike, 10, 70, DVec3::ZERO, DVec3::new(300.0, 70.0, 0.0));
|
||||
let mut p = player(&rec);
|
||||
p.heard = 9.0;
|
||||
p.t = 11.0;
|
||||
let mut out = Out::default();
|
||||
p.sounds(&rec, DVec3::ZERO, &mut out);
|
||||
assert_eq!(out.sounds.len(), 1);
|
||||
assert_eq!(out.sounds[0].0, DroneKind::Kalibr.launch_sound());
|
||||
out.sounds.clear();
|
||||
p.t = 12.0;
|
||||
p.sounds(&rec, DVec3::ZERO, &mut out);
|
||||
assert!(out.sounds.is_empty(), "the loop engine must not repeat the launch");
|
||||
p.heard = 9.0; // seek/cut resets the one-shot cursor
|
||||
p.t = 11.0;
|
||||
p.sounds(&rec, DVec3::ZERO, &mut out);
|
||||
assert_eq!(out.sounds.len(), 1);
|
||||
}
|
||||
|
||||
/// A host that remembers which sources run.
|
||||
#[derive(Default)]
|
||||
struct Host(std::collections::BTreeMap<u32, [f64; 3]>);
|
||||
|
||||
@@ -247,6 +247,9 @@ impl Smoke {
|
||||
self.trail((1, m.id), m.pos, tick, look);
|
||||
}
|
||||
}
|
||||
for f in &sim.strikes.flights {
|
||||
self.trail((2, f.id), f.pos, tick, drone_look(Class::Cruise, crate::sim::DroneKind::Kalibr.spec().trail_scale()));
|
||||
}
|
||||
// Forget what is gone.
|
||||
if tick % 100 == 0 {
|
||||
let alive: std::collections::HashSet<(u8, u32)> = sim
|
||||
@@ -254,6 +257,7 @@ impl Smoke {
|
||||
.iter()
|
||||
.map(|d| (0, d.id))
|
||||
.chain(sim.missiles.iter().map(|m| (1, m.id)))
|
||||
.chain(sim.strikes.flights.iter().map(|f| (2, f.id)))
|
||||
.collect();
|
||||
self.last.retain(|k, _| alive.contains(k));
|
||||
}
|
||||
@@ -286,6 +290,44 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::sim::{DroneKind, MissileKind};
|
||||
|
||||
#[test]
|
||||
fn live_counter_strike_leaves_a_trail_and_releases_its_source() {
|
||||
use crate::sim::sites::{LaunchSite, SiteClass};
|
||||
use crate::sim::strike::{Estimate, Known, SiteState};
|
||||
use glam::DVec2;
|
||||
let mut sim = Sim::new(11);
|
||||
let id = 1;
|
||||
sim.sites.list.push(LaunchSite {
|
||||
id, side: 0, sector: 0, bearing: 0.0, class: SiteClass::Ramp,
|
||||
pos: DVec3::new(0.0, 64.0, 300.0), range: 300.0, water: false,
|
||||
stock: 0, planned: 0, alive: true, next_free: 0, offmap: false,
|
||||
});
|
||||
sim.strikes.known.push(Known {
|
||||
site: id, class: SiteClass::Ramp, obs: Vec::new(),
|
||||
est: Some(Estimate { pos: DVec2::new(0.0, 300.0), radius: 5.0, fix: true,
|
||||
bearing: 0.0, bearing_err: 0.01, origin: DVec2::ZERO }),
|
||||
launches: 1, last: 0, state: SiteState::Active, cover: None, aimed: false,
|
||||
});
|
||||
sim.order_strike(id, DVec3::new(0.0, 64.0, 0.0), &mut crate::sim::Out::default()).unwrap();
|
||||
let mut smoke = Smoke::default();
|
||||
sim.tick = 1;
|
||||
smoke.update(&sim);
|
||||
let from = sim.strikes.flights[0].pos;
|
||||
sim.tick = 2;
|
||||
sim.strikes.flights[0].pos += DVec3::Z * 8.0;
|
||||
smoke.update(&sim);
|
||||
let segments = smoke.segments(&sim);
|
||||
assert_eq!(segments.len(), 1, "a live strike must emit the cruise trail also seen in replay");
|
||||
assert_eq!(segments[0].az, from.z);
|
||||
assert_eq!(segments[0].bz, from.z + 8.0);
|
||||
let expected = drone_look(Class::Cruise, DroneKind::Kalibr.spec().trail_scale());
|
||||
assert_eq!(segments[0].density, expected.density);
|
||||
sim.strikes.flights.clear();
|
||||
sim.tick = 100;
|
||||
smoke.update(&sim);
|
||||
assert!(smoke.last.is_empty(), "finished strikes must not leave source bookkeeping behind");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trails_follow_one_rule() {
|
||||
assert!(missile_look(MissileKind::P1sun).is_none());
|
||||
|
||||
Reference in New Issue
Block a user