veloren_rtsim/data/
airship.rs1use crate::data::Npcs;
2use common::rtsim::NpcId;
3use std::cmp::Ordering;
4#[cfg(debug_assertions)] use tracing::debug;
5use vek::*;
6use world::{
7 World,
8 civ::airship_travel::{AirshipSpawningLocation, Airships},
9 util::DHashMap,
10};
11
12#[derive(Clone, Default, Debug)]
14pub struct AirshipSim {
15 pub assigned_routes: DHashMap<NpcId, (usize, usize)>,
20
21 pub route_pilots: DHashMap<usize, Vec<NpcId>>,
23}
24
25#[cfg(debug_assertions)]
26macro_rules! debug_airships {
27 ($($arg:tt)*) => {
28 debug!($($arg)*);
29 }
30}
31
32#[cfg(not(debug_assertions))]
33macro_rules! debug_airships {
34 ($($arg:tt)*) => {};
35}
36
37impl AirshipSim {
38 pub fn register_airship_captain(
45 &mut self,
46 location: &AirshipSpawningLocation,
47 captain_id: NpcId,
48 airship_id: NpcId,
49 world: &World,
50 npcs: &mut Npcs,
51 ) {
52 self.assigned_routes
53 .insert(captain_id, (location.route_index, location.leg_index));
54
55 assert!(
56 location.dir.is_normalized(),
57 "Airship direction {:?} is not normalized",
58 location.dir
59 );
60 let airship_wpos3d = location.pos.with_z(
61 world
62 .sim()
63 .get_alt_approx(location.pos.map(|e| e as i32))
64 .unwrap_or(0.0)
65 + location.height,
66 );
67
68 let airship_mount_offset = if let Some(airship) = npcs.get_mut(airship_id) {
69 airship.wpos = airship_wpos3d;
70 airship.dir = location.dir;
71 airship.body.mount_offset()
72 } else {
73 tracing::warn!(
74 "Failed to find airship {:?} for captain {:?}",
75 airship_id,
76 captain_id,
77 );
78 Vec3::new(0.0, 0.0, 0.0)
79 };
80 if let Some(captain) = npcs.get_mut(captain_id) {
81 let captain_pos = airship_wpos3d
82 + Vec3::new(
83 location.dir.x * airship_mount_offset.x,
84 location.dir.y * airship_mount_offset.y,
85 airship_mount_offset.z,
86 );
87 captain.wpos = captain_pos;
88 captain.dir = location.dir;
89 }
90
91 debug_airships!(
92 "Registering airship {:?}/{:?} for spawning location {:?}",
93 airship_id,
94 captain_id,
95 location,
96 );
97 }
98
99 pub fn configure_route_pilots(&mut self, airships: &Airships, npcs: &Npcs) {
106 debug_airships!("Airship Assigned Routes: {:?}", self.assigned_routes);
107 for route_index in 0..airships.route_count() {
113 let mut pilots_on_route = Vec::new();
114 for leg_index in 0..airships.docking_site_count_for_route(route_index) {
115 let mut pilots_on_leg: Vec<_> = self
117 .assigned_routes
118 .iter()
119 .filter(|(_, (rti, li))| *rti == route_index && *li == leg_index)
120 .map(|(pilot_id, _)| *pilot_id)
121 .collect();
122
123 if !pilots_on_leg.is_empty() {
124 let start_pos = airships.route_leg_departure_location(route_index, leg_index);
126 pilots_on_leg.sort_by(|&pilot1, &pilot2| {
127 let pilot1_pos = npcs.get(pilot1).map_or(start_pos, |npc| npc.wpos.xy());
128 let pilot2_pos = npcs.get(pilot2).map_or(start_pos, |npc| npc.wpos.xy());
129 start_pos
130 .distance_squared(pilot1_pos)
131 .partial_cmp(&start_pos.distance_squared(pilot2_pos))
132 .unwrap_or(Ordering::Equal)
133 });
134 pilots_on_route.extend(pilots_on_leg);
135 }
136 }
137 if !pilots_on_route.is_empty() {
138 debug_airships!("Route {} pilots: {:?}", route_index, pilots_on_route);
139 self.route_pilots.insert(route_index, pilots_on_route);
140 }
141 }
142 }
143
144 pub fn next_pilot(&self, route_index: usize, pilot_id: NpcId) -> Option<NpcId> {
147 if let Some(pilots) = self.route_pilots.get(&route_index) {
148 if pilots.len() < 2 {
149 tracing::warn!(
151 "Route {} has only one pilot, 'next_pilot' doesn't make sense.",
152 route_index,
153 );
154 return None;
155 }
156 if let Some(pilot_index) = pilots.iter().position(|&p_id| p_id == pilot_id) {
157 if pilot_index == pilots.len() - 1 {
158 return Some(pilots[0]);
160 } else {
161 return Some(pilots[pilot_index + 1]);
163 }
164 }
165 }
166 tracing::warn!(
167 "Failed to find next pilot for route index {} and pilot id {:?}",
168 route_index,
169 pilot_id
170 );
171 None
172 }
173}