1use std::{
2 collections::HashMap,
3 error::Error,
4 f32::NAN,
5 ffi::CStr,
6 fmt::{Debug, Formatter, Result as FmtResult},
7 ptr::null_mut,
8};
9
10use itertools::Itertools;
11use nalgebra::DMatrix;
12
13use crate::{
14 ai_interface::{callback::resource::Resource, AIInterface},
15 get_callback,
16};
17
18#[derive(Debug, Copy, Clone)]
19pub struct Map {
20 pub(crate) ai_id: i32,
21}
22
23#[derive(Clone)]
24pub struct MapAll {
25 checksum: i32,
26 start_position: [f32; 3],
27 mouse_position: [f32; 3],
28 width: i32,
29 height: i32,
30 center_height_map: DMatrix<f32>,
31 corner_height_map: DMatrix<f32>,
32 min_height: f32,
33 max_height: f32,
34 slope_map: DMatrix<f32>,
35 los_map: DMatrix<i32>,
36 air_los_map: DMatrix<i32>,
37 radar_map: DMatrix<i32>,
38 sonar_map: DMatrix<i32>,
39 seismic_map: DMatrix<i32>,
40 jammer_map: DMatrix<i32>,
41 sonar_jammer_map: DMatrix<i32>,
42 resource_raw_map: HashMap<Resource, DMatrix<i16>>,
43 resource_spots_map: HashMap<Resource, Vec<((f32, f32), f32)>>,
44 resource_spots_average_income: HashMap<Resource, f32>,
45 hash: i32,
46 name: String,
47 human_name: String,
48 max_resource: HashMap<Resource, f32>,
49 extractor_radius: HashMap<Resource, f32>,
50 min_wind: f32,
51 max_wind: f32,
52 current_wind: f32,
53 tidal_strength: f32,
54 gravity: f32,
55 water_damage: f32,
56 deformable: bool,
57 hardness: f32,
58}
59
60impl Debug for MapAll {
61 fn fmt(&self, f: &mut Formatter) -> FmtResult {
62 let center_height_map_temp = self
63 .center_height_map
64 .iter()
65 .take(10)
66 .cloned()
67 .collect::<Vec<_>>();
68 let corner_height_map_temp = self
69 .corner_height_map
70 .iter()
71 .take(10)
72 .cloned()
73 .collect::<Vec<_>>();
74 let slope_map_temp = self.slope_map.iter().take(10).cloned().collect::<Vec<_>>();
75 let los_map_temp = self.los_map.iter().take(10).cloned().collect::<Vec<_>>();
76 let air_los_map_temp = self
77 .air_los_map
78 .iter()
79 .take(10)
80 .cloned()
81 .collect::<Vec<_>>();
82 let radar_map_temp = self.radar_map.iter().take(10).cloned().collect::<Vec<_>>();
83 let sonar_map_temp = self.sonar_map.iter().take(10).cloned().collect::<Vec<_>>();
84 let seismic_map_temp = self
85 .seismic_map
86 .iter()
87 .take(10)
88 .cloned()
89 .collect::<Vec<_>>();
90 let jammer_map_temp = self.jammer_map.iter().take(10).cloned().collect::<Vec<_>>();
91 let sonar_jammer_map_temp = self
92 .sonar_jammer_map
93 .iter()
94 .take(10)
95 .cloned()
96 .collect::<Vec<_>>();
97
98 let mut resource_raw_map_temp = HashMap::new();
99 for (k, v) in &self.resource_raw_map {
100 resource_raw_map_temp.insert(k, v.iter().take(10).cloned().collect::<Vec<_>>());
101 }
102
103 let mut resource_spots_map_temp = HashMap::new();
104 for (k, v) in self.resource_spots_map.clone() {
105 resource_spots_map_temp.insert(k, v.iter().take(10).cloned().collect::<Vec<_>>());
106 }
107
108 f.debug_struct("MapAll")
109 .field("checksum", &self.checksum)
110 .field("start_position", &self.start_position)
111 .field("mouse_position", &self.mouse_position)
112 .field("width", &self.width)
113 .field("height", &self.height)
114 .field("center_height_map", ¢er_height_map_temp)
115 .field("center_height_map.shape()", &self.center_height_map.shape())
116 .field("corner_height_map", &corner_height_map_temp)
117 .field("corner_height_map.shape()", &self.corner_height_map.shape())
118 .field("min_height", &self.min_height)
119 .field("max_height", &self.max_height)
120 .field("slope_map", &slope_map_temp)
121 .field("slope_map.shape()", &self.slope_map.shape())
122 .field("los_map", &los_map_temp)
123 .field("los_map.shape()", &self.los_map.shape())
124 .field("air_los_map", &air_los_map_temp)
125 .field("air_los_map.shape()", &self.air_los_map.shape())
126 .field("radar_map", &radar_map_temp)
127 .field("radar_map.shape()", &self.radar_map.shape())
128 .field("sonar_map", &sonar_map_temp)
129 .field("sonar_map.shape()", &self.sonar_map.shape())
130 .field("seismic_map", &seismic_map_temp)
131 .field("seismic_map.shape()", &self.seismic_map.shape())
132 .field("jammer_map", &jammer_map_temp)
133 .field("jammer_map.shape()", &self.jammer_map.shape())
134 .field("sonar_jammer_map", &sonar_jammer_map_temp)
135 .field("sonar_jammer_map.shape()", &self.sonar_jammer_map.shape())
136 .field("resource_raw_map", &resource_raw_map_temp)
137 .field(
138 "resource_raw_map.iter().shape()",
139 &self
140 .resource_raw_map
141 .iter()
142 .map(|(k, v)| (k, v.shape()))
143 .collect::<Vec<_>>(),
144 )
145 .field("resource_spots_map", &resource_spots_map_temp)
146 .field(
147 "resource_spots_map.iter().len()",
148 &self
149 .resource_spots_map
150 .iter()
151 .map(|(k, v)| (k, v.len()))
152 .collect::<Vec<_>>(),
153 )
154 .field(
155 "resource_spots_average_income",
156 &self.resource_spots_average_income,
157 )
158 .field("hash", &self.hash)
159 .field("name", &self.name)
160 .field("human_name", &self.human_name)
161 .field("max_resource", &self.max_resource)
162 .field("extractor_radius", &self.extractor_radius)
163 .field("min_wind", &self.min_wind)
164 .field("max_wind", &self.max_wind)
165 .field("current_wind", &self.current_wind)
166 .field("tidal_strength", &self.tidal_strength)
167 .field("gravity", &self.gravity)
168 .field("water_damage", &self.water_damage)
169 .field("deformable", &self.deformable)
170 .field("hardness", &self.hardness)
171 .finish()
172 }
173}
174
175impl AIInterface {
176 pub fn map(&self) -> Map {
177 Map { ai_id: self.ai_id }
178 }
179}
180
181thread_local! {
182 static CENTER_MAP: DMatrix<f32> = DMatrix::<f32>::zeros(0, 0);
183}
184
185impl Map {
186 pub fn checksum(&self) -> Result<i32, Box<dyn Error>> {
187 let get_checksum = get_callback!(self.ai_id, Map_getChecksum)?;
188 Ok(unsafe { get_checksum(self.ai_id) })
189 }
190
191 pub fn start_position(&self) -> Result<[f32; 3], Box<dyn Error>> {
192 let get_start_pos = get_callback!(self.ai_id, Map_getStartPos)?;
193
194 let mut ret = [NAN; 3];
195 unsafe { get_start_pos(self.ai_id, ret.as_mut_ptr()) };
196
197 Ok(ret)
198 }
199
200 pub fn mouse_position(&self) -> Result<[f32; 3], Box<dyn Error>> {
201 let get_mouse_pos = get_callback!(self.ai_id, Map_getMousePos)?;
202
203 let mut ret = [NAN; 3];
204 unsafe { get_mouse_pos(self.ai_id, ret.as_mut_ptr()) };
205
206 Ok(ret)
207 }
208
209 pub fn is_position_in_camera(
210 &self,
211 position: [f32; 3],
212 radius: f32,
213 ) -> Result<bool, Box<dyn Error>> {
214 let get_is_position_in_camera = get_callback!(self.ai_id, Map_isPosInCamera)?;
215
216 let mut inner_position = position;
217 Ok(unsafe { get_is_position_in_camera(self.ai_id, inner_position.as_mut_ptr(), radius) })
218 }
219
220 pub fn width(&self) -> Result<i32, Box<dyn Error>> {
221 let get_width = get_callback!(self.ai_id, Map_getWidth)?;
222 Ok(unsafe { get_width(self.ai_id) })
223 }
224
225 pub fn height(&self) -> Result<i32, Box<dyn Error>> {
226 let get_height = get_callback!(self.ai_id, Map_getHeight)?;
227 Ok(unsafe { get_height(self.ai_id) })
228 }
229
230 pub fn center_height_map(&self) -> Result<DMatrix<f32>, Box<dyn Error>> {
231 let mut ret = DMatrix::<f32>::zeros(0, 0);
232 CENTER_MAP.try_with::<_, Result<(), Box<dyn Error>>>(|cm| {
233 if cm.shape() == (0, 0) {
234 let get_height_map = get_callback!(self.ai_id, Map_getHeightMap)?;
235 let map_size = unsafe { get_height_map(self.ai_id, null_mut(), i32::MAX) };
236 let mut map_raw = vec![0.0_f32; map_size as usize];
237 unsafe { get_height_map(self.ai_id, map_raw.as_mut_ptr(), map_size) };
238 ret = DMatrix::from_row_iterator(
239 self.width()? as usize,
240 self.height()? as usize,
241 map_raw,
242 );
243 } else {
244 ret = cm.clone();
245 }
246
247 Ok(())
248 })??;
249
250 Ok(ret)
251 }
252
253 pub fn corner_height_map(&self) -> Result<DMatrix<f32>, Box<dyn Error>> {
254 let get_corner_height_map = get_callback!(self.ai_id, Map_getCornersHeightMap)?;
255 let map_size = unsafe { get_corner_height_map(self.ai_id, null_mut(), i32::MAX) };
256 let mut map_raw = vec![0.0_f32; map_size as usize];
257 unsafe { get_corner_height_map(self.ai_id, map_raw.as_mut_ptr(), map_size) };
258 let map = DMatrix::from_row_iterator(
259 self.width()? as usize + 1,
260 self.height()? as usize + 1,
261 map_raw,
262 );
263 Ok(map)
264 }
265
266 pub fn min_height(&self) -> Result<f32, Box<dyn Error>> {
267 let get_min_height = get_callback!(self.ai_id, Map_getMinHeight)?;
268 Ok(unsafe { get_min_height(self.ai_id) })
269 }
270
271 pub fn max_height(&self) -> Result<f32, Box<dyn Error>> {
272 let get_max_height = get_callback!(self.ai_id, Map_getMaxHeight)?;
273 Ok(unsafe { get_max_height(self.ai_id) })
274 }
275
276 pub fn slope_map(&self) -> Result<DMatrix<f32>, Box<dyn Error>> {
277 let get_slope_map = get_callback!(self.ai_id, Map_getSlopeMap)?;
278 let map_size = unsafe { get_slope_map(self.ai_id, null_mut(), i32::MAX) };
279 let mut map_raw = vec![0.0_f32; map_size as usize];
280 unsafe { get_slope_map(self.ai_id, map_raw.as_mut_ptr(), map_size) };
281 let map = DMatrix::from_row_iterator(
282 self.width()? as usize / 2,
283 self.height()? as usize / 2,
284 map_raw,
285 );
286 Ok(map)
287 }
288
289 pub fn los_map(&self) -> Result<DMatrix<i32>, Box<dyn Error>> {
290 let get_los_map = get_callback!(self.ai_id, Map_getLosMap)?;
291 let map_size = unsafe { get_los_map(self.ai_id, null_mut(), i32::MAX) };
292 let mut map_raw = vec![0_i32; map_size as usize];
293 unsafe { get_los_map(self.ai_id, map_raw.as_mut_ptr(), map_size) };
294 let mip_level = AIInterface::new(self.ai_id).game_mod().mip_level();
295 let res = (1 << mip_level.los()?).max(1);
296 let map = DMatrix::from_row_iterator(
297 self.width()? as usize / res,
298 self.height()? as usize / res,
299 map_raw,
300 );
301 Ok(map)
302 }
303
304 pub fn air_los_map(&self) -> Result<DMatrix<i32>, Box<dyn Error>> {
305 let get_air_los_map = get_callback!(self.ai_id, Map_getAirLosMap)?;
306 let map_size = unsafe { get_air_los_map(self.ai_id, null_mut(), i32::MAX) };
307 let mut map_raw = vec![0_i32; map_size as usize];
308 unsafe { get_air_los_map(self.ai_id, map_raw.as_mut_ptr(), map_size) };
309 let mip_level = AIInterface::new(self.ai_id).game_mod().mip_level();
310 let res = (1 << mip_level.air()?).max(1);
311 let map = DMatrix::from_row_iterator(
312 self.width()? as usize / res,
313 self.height()? as usize / res,
314 map_raw,
315 );
316 Ok(map)
317 }
318
319 pub fn radar_map(&self) -> Result<DMatrix<i32>, Box<dyn Error>> {
320 let get_radar_map = get_callback!(self.ai_id, Map_getRadarMap)?;
321 let map_size = unsafe { get_radar_map(self.ai_id, null_mut(), i32::MAX) };
322 let mut map_raw = vec![0_i32; map_size as usize];
323 unsafe { get_radar_map(self.ai_id, map_raw.as_mut_ptr(), map_size) };
324 let mip_level = AIInterface::new(self.ai_id).game_mod().mip_level();
325 let res = (1 << mip_level.radar()?).max(1);
326 let map = DMatrix::from_row_iterator(
327 self.width()? as usize / res,
328 self.height()? as usize / res,
329 map_raw,
330 );
331 Ok(map)
332 }
333
334 pub fn sonar_map(&self) -> Result<DMatrix<i32>, Box<dyn Error>> {
335 let get_sonar_map = get_callback!(self.ai_id, Map_getSonarMap)?;
336 let map_size = unsafe { get_sonar_map(self.ai_id, null_mut(), i32::MAX) };
337 let mut map_raw = vec![0_i32; map_size as usize];
338 unsafe { get_sonar_map(self.ai_id, map_raw.as_mut_ptr(), map_size) };
339 let mip_level = AIInterface::new(self.ai_id).game_mod().mip_level();
340 let res = (1 << mip_level.radar()?).max(1);
341 let map = DMatrix::from_row_iterator(
342 self.width()? as usize / res,
343 self.height()? as usize / res,
344 map_raw,
345 );
346 Ok(map)
347 }
348
349 pub fn seismic_map(&self) -> Result<DMatrix<i32>, Box<dyn Error>> {
350 let get_sonar_map = get_callback!(self.ai_id, Map_getSeismicMap)?;
351 let map_size = unsafe { get_sonar_map(self.ai_id, null_mut(), i32::MAX) };
352 let mut map_raw = vec![0_i32; map_size as usize];
353 unsafe { get_sonar_map(self.ai_id, map_raw.as_mut_ptr(), map_size) };
354 let mip_level = AIInterface::new(self.ai_id).game_mod().mip_level();
355 let res = (1 << mip_level.radar()?).max(1);
356 let map = DMatrix::from_row_iterator(
357 self.width()? as usize / res,
358 self.height()? as usize / res,
359 map_raw,
360 );
361 Ok(map)
362 }
363
364 pub fn jammer_map(&self) -> Result<DMatrix<i32>, Box<dyn Error>> {
365 let get_jammer_map = get_callback!(self.ai_id, Map_getJammerMap)?;
366 let map_size = unsafe { get_jammer_map(self.ai_id, null_mut(), i32::MAX) };
367 let mut map_raw = vec![0_i32; map_size as usize];
368 unsafe { get_jammer_map(self.ai_id, map_raw.as_mut_ptr(), map_size) };
369 let mip_level = AIInterface::new(self.ai_id).game_mod().mip_level();
370 let res = (1 << mip_level.radar()?).max(1);
371 let map = DMatrix::from_row_iterator(
372 self.width()? as usize / res,
373 self.height()? as usize / res,
374 map_raw,
375 );
376 Ok(map)
377 }
378
379 pub fn sonar_jammer_map(&self) -> Result<DMatrix<i32>, Box<dyn Error>> {
380 let get_sonar_jammer_map = get_callback!(self.ai_id, Map_getSonarJammerMap)?;
381 let map_size = unsafe { get_sonar_jammer_map(self.ai_id, null_mut(), i32::MAX) };
382 let mut map_raw = vec![0_i32; map_size as usize];
383 unsafe { get_sonar_jammer_map(self.ai_id, map_raw.as_mut_ptr(), map_size) };
384 let mip_level = AIInterface::new(self.ai_id).game_mod().mip_level();
385 let res = (1 << mip_level.radar()?).max(1);
386 let map = DMatrix::from_row_iterator(
387 self.width()? as usize / res,
388 self.height()? as usize / res,
389 map_raw,
390 );
391 Ok(map)
392 }
393
394 pub fn resource_raw_map(&self, resource: Resource) -> Result<DMatrix<i16>, Box<dyn Error>> {
395 let get_resource_raw_map = get_callback!(self.ai_id, Map_getResourceMapRaw)?;
396 let map_size =
397 unsafe { get_resource_raw_map(self.ai_id, resource.resource_id, null_mut(), i32::MAX) };
398 if map_size == 0 {
399 Ok(DMatrix::zeros(0, 0))
400 } else {
401 let mut map_raw = vec![0_i16; map_size as usize];
402 unsafe {
403 get_resource_raw_map(
404 self.ai_id,
405 resource.resource_id,
406 map_raw.as_mut_ptr(),
407 map_size,
408 )
409 };
410 let map = DMatrix::from_row_iterator(
411 self.width()? as usize / 2,
412 self.height()? as usize / 2,
413 map_raw,
414 );
415 Ok(map)
416 }
417 }
418
419 pub fn resource_spots_map(
420 &self,
421 resource: Resource,
422 ) -> Result<Vec<((f32, f32), f32)>, Box<dyn Error>> {
423 let get_resource_spots_map = get_callback!(self.ai_id, Map_getResourceMapSpotsPositions)?;
424 let map_size = unsafe {
425 get_resource_spots_map(self.ai_id, resource.resource_id, null_mut(), i32::MAX)
426 };
427 let mut map_raw = vec![0.0_f32; map_size as usize];
428 unsafe {
429 get_resource_spots_map(
430 self.ai_id,
431 resource.resource_id,
432 map_raw.as_mut_ptr(),
433 map_size,
434 )
435 };
436 let map = (&map_raw.into_iter().chunks(3))
437 .into_iter()
438 .map(|mut v| {
439 let x = v.next().unwrap();
440 let relative_amount = v.next().unwrap();
441 let z = v.next().unwrap() + 16.0;
442 ((x, z), relative_amount)
443 })
444 .collect::<Vec<_>>();
445 Ok(map)
446 }
447
448 pub fn resource_spots_average_income(&self, resource: Resource) -> Result<f32, Box<dyn Error>> {
449 let get_resource_spots_average_income =
450 get_callback!(self.ai_id, Map_getResourceMapSpotsAverageIncome)?;
451 Ok(unsafe { get_resource_spots_average_income(self.ai_id, resource.resource_id) })
452 }
453
454 pub fn hash(&self) -> Result<i32, Box<dyn Error>> {
464 let get_hash = get_callback!(self.ai_id, Map_getHash)?;
465 Ok(unsafe { get_hash(self.ai_id) })
466 }
467
468 pub fn name(&self) -> Result<String, Box<dyn Error>> {
469 let get_name = get_callback!(self.ai_id, Map_getName)?;
470 Ok(String::from(
471 unsafe { CStr::from_ptr(get_name(self.ai_id)) }.to_str()?,
472 ))
473 }
474
475 pub fn human_name(&self) -> Result<String, Box<dyn Error>> {
476 let get_human_name = get_callback!(self.ai_id, Map_getHumanName)?;
477 Ok(String::from(
478 unsafe { CStr::from_ptr(get_human_name(self.ai_id)) }.to_str()?,
479 ))
480 }
481
482 pub fn max_resource(&self, resource: Resource) -> Result<f32, Box<dyn Error>> {
489 let get_max_resource = get_callback!(self.ai_id, Map_getMaxResource)?;
490 Ok(unsafe { get_max_resource(self.ai_id, resource.resource_id) })
491 }
492
493 pub fn extractor_radius(&self, resource: Resource) -> Result<f32, Box<dyn Error>> {
494 let get_extractor_radius = get_callback!(self.ai_id, Map_getExtractorRadius)?;
495 Ok(unsafe { get_extractor_radius(self.ai_id, resource.resource_id) })
496 }
497
498 pub fn min_wind(&self) -> Result<f32, Box<dyn Error>> {
499 let get_min_wind = get_callback!(self.ai_id, Map_getMinWind)?;
500 Ok(unsafe { get_min_wind(self.ai_id) })
501 }
502
503 pub fn max_wind(&self) -> Result<f32, Box<dyn Error>> {
504 let get_max_wind = get_callback!(self.ai_id, Map_getMaxWind)?;
505 Ok(unsafe { get_max_wind(self.ai_id) })
506 }
507
508 pub fn current_wind(&self) -> Result<f32, Box<dyn Error>> {
509 let get_cur_wind = get_callback!(self.ai_id, Map_getCurWind)?;
510 Ok(unsafe { get_cur_wind(self.ai_id) })
511 }
512
513 pub fn tidal_strength(&self) -> Result<f32, Box<dyn Error>> {
514 let get_tidal_strength = get_callback!(self.ai_id, Map_getTidalStrength)?;
515 Ok(unsafe { get_tidal_strength(self.ai_id) })
516 }
517
518 pub fn gravity(&self) -> Result<f32, Box<dyn Error>> {
519 let get_gravity = get_callback!(self.ai_id, Map_getGravity)?;
520 Ok(unsafe { get_gravity(self.ai_id) })
521 }
522
523 pub fn water_damage(&self) -> Result<f32, Box<dyn Error>> {
524 let get_water_damage = get_callback!(self.ai_id, Map_getWaterDamage)?;
525 Ok(unsafe { get_water_damage(self.ai_id) })
526 }
527
528 pub fn deformable(&self) -> Result<bool, Box<dyn Error>> {
529 let get_deformable = get_callback!(self.ai_id, Map_isDeformable)?;
530 Ok(unsafe { get_deformable(self.ai_id) })
531 }
532
533 pub fn hardness(&self) -> Result<f32, Box<dyn Error>> {
534 let get_hardness = get_callback!(self.ai_id, Map_getHardness)?;
535 Ok(unsafe { get_hardness(self.ai_id) })
536 }
537
538 pub fn all(&self) -> Result<MapAll, Box<dyn Error>> {
539 Ok(MapAll {
540 checksum: self.checksum()?,
541 start_position: self.start_position()?,
542 mouse_position: self.mouse_position()?,
543 width: self.width()?,
544 height: self.height()?,
545 center_height_map: self.center_height_map()?,
546 corner_height_map: self.corner_height_map()?,
547 min_height: self.min_height()?,
548 max_height: self.max_height()?,
549 slope_map: self.slope_map()?,
550 los_map: self.los_map()?,
551 air_los_map: self.air_los_map()?,
552 radar_map: self.radar_map()?,
553 sonar_map: self.sonar_map()?,
554 seismic_map: self.seismic_map()?,
555 jammer_map: self.jammer_map()?,
556 sonar_jammer_map: self.sonar_jammer_map()?,
557 resource_raw_map: AIInterface::new(self.ai_id)
558 .resource_interface()
559 .get_resources()?
560 .into_iter()
561 .filter_map(|resource| {
562 if let Ok(res) = self.resource_raw_map(resource) {
563 Some((resource, res))
564 } else {
565 None
566 }
567 })
568 .collect(),
569 resource_spots_map: AIInterface::new(self.ai_id)
570 .resource_interface()
571 .get_resources()?
572 .into_iter()
573 .filter_map(|resource| {
574 if let Ok(res) = self.resource_spots_map(resource) {
575 Some((resource, res))
576 } else {
577 None
578 }
579 })
580 .collect(),
581 resource_spots_average_income: AIInterface::new(self.ai_id)
582 .resource_interface()
583 .get_resources()?
584 .into_iter()
585 .filter_map(|resource| {
586 if let Ok(res) = self.resource_spots_average_income(resource) {
587 Some((resource, res))
588 } else {
589 None
590 }
591 })
592 .collect(),
593 hash: self.hash()?,
606 name: self.name()?,
607 human_name: self.human_name()?,
608 max_resource: AIInterface::new(self.ai_id)
609 .resource_interface()
610 .get_resources()?
611 .into_iter()
612 .filter_map(|resource| {
613 if let Ok(res) = self.max_resource(resource) {
614 Some((resource, res))
615 } else {
616 None
617 }
618 })
619 .collect(),
620 extractor_radius: AIInterface::new(self.ai_id)
621 .resource_interface()
622 .get_resources()?
623 .into_iter()
624 .filter_map(|resource| {
625 if let Ok(res) = self.extractor_radius(resource) {
626 Some((resource, res))
627 } else {
628 None
629 }
630 })
631 .collect(),
632 min_wind: self.min_wind()?,
633 max_wind: self.max_wind()?,
634 current_wind: self.current_wind()?,
635 tidal_strength: self.tidal_strength()?,
636 gravity: self.gravity()?,
637 water_damage: self.water_damage()?,
638 deformable: self.deformable()?,
639 hardness: self.hardness()?,
640 })
641 }
642}