spring_ai_rs/ai_interface/callback/map/
mod.rs

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", &center_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    // TODO:
455    // pub fn resource_spots_nearest(&self, resource: Resource) -> Result<Vec<f32>, Box<dyn Error>> {
456    //     let get_resource_spots_nearest = get_callback!(self.ai_id, Map_getResourceMapSpotsNearest)?;
457    //     let map_size = unsafe { get_resource_spots_nearest(self.ai_id, resource.resource_id, null_mut(), i32::MAX) };
458    //     let mut map = vec![0.0_f32; map_size as usize];
459    //     unsafe { get_resource_spots_nearest(self.ai_id, resource.resource_id, map.as_mut_ptr(), map_size) };
460    //     Ok(map)
461    // }
462
463    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    // TODO:
483    // pub fn elevation_at(&self, x: f32, z: f32) -> Result<f32, Box<dyn Error>> {
484    //     let get_elevation = get_callback!(self.ai_id, Map_getElevationAt)?;
485    //     Ok(unsafe { get_elevation(self.ai_id, x, z) })
486    // }
487
488    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            // resource_spots_nearest_map: AIInterface::new(self.ai_id)
594            //     .resource_interface()
595            //     .get_resources()?
596            //     .into_iter()
597            //     .filter_map(|resource| {
598            //         if let Ok(res) = self.resource_spots_nearest_map(resource) {
599            //             Some((resource, res))
600            //         } else {
601            //             None
602            //         }
603            //     })
604            //     .collect(),
605            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}