use std::{
collections::HashMap,
error::Error,
f32::NAN,
ffi::CStr,
fmt::{Debug, Formatter, Result as FmtResult},
ptr::null_mut,
};
use itertools::Itertools;
use nalgebra::DMatrix;
use crate::{
ai_interface::{callback::resource::Resource, AIInterface},
get_callback,
};
#[derive(Debug, Copy, Clone)]
pub struct Map {
pub(crate) ai_id: i32,
}
#[derive(Clone)]
pub struct MapAll {
checksum: i32,
start_position: [f32; 3],
mouse_position: [f32; 3],
width: i32,
height: i32,
center_height_map: DMatrix<f32>,
corner_height_map: DMatrix<f32>,
min_height: f32,
max_height: f32,
slope_map: DMatrix<f32>,
los_map: DMatrix<i32>,
air_los_map: DMatrix<i32>,
radar_map: DMatrix<i32>,
sonar_map: DMatrix<i32>,
seismic_map: DMatrix<i32>,
jammer_map: DMatrix<i32>,
sonar_jammer_map: DMatrix<i32>,
resource_raw_map: HashMap<Resource, DMatrix<i16>>,
resource_spots_map: HashMap<Resource, Vec<((f32, f32), f32)>>,
resource_spots_average_income: HashMap<Resource, f32>,
hash: i32,
name: String,
human_name: String,
max_resource: HashMap<Resource, f32>,
extractor_radius: HashMap<Resource, f32>,
min_wind: f32,
max_wind: f32,
current_wind: f32,
tidal_strength: f32,
gravity: f32,
water_damage: f32,
deformable: bool,
hardness: f32,
}
impl Debug for MapAll {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
let center_height_map_temp = self
.center_height_map
.iter()
.take(10)
.cloned()
.collect::<Vec<_>>();
let corner_height_map_temp = self
.corner_height_map
.iter()
.take(10)
.cloned()
.collect::<Vec<_>>();
let slope_map_temp = self.slope_map.iter().take(10).cloned().collect::<Vec<_>>();
let los_map_temp = self.los_map.iter().take(10).cloned().collect::<Vec<_>>();
let air_los_map_temp = self
.air_los_map
.iter()
.take(10)
.cloned()
.collect::<Vec<_>>();
let radar_map_temp = self.radar_map.iter().take(10).cloned().collect::<Vec<_>>();
let sonar_map_temp = self.sonar_map.iter().take(10).cloned().collect::<Vec<_>>();
let seismic_map_temp = self
.seismic_map
.iter()
.take(10)
.cloned()
.collect::<Vec<_>>();
let jammer_map_temp = self.jammer_map.iter().take(10).cloned().collect::<Vec<_>>();
let sonar_jammer_map_temp = self
.sonar_jammer_map
.iter()
.take(10)
.cloned()
.collect::<Vec<_>>();
let mut resource_raw_map_temp = HashMap::new();
for (k, v) in &self.resource_raw_map {
resource_raw_map_temp.insert(k, v.iter().take(10).cloned().collect::<Vec<_>>());
}
let mut resource_spots_map_temp = HashMap::new();
for (k, v) in self.resource_spots_map.clone() {
resource_spots_map_temp.insert(k, v.iter().take(10).cloned().collect::<Vec<_>>());
}
f.debug_struct("MapAll")
.field("checksum", &self.checksum)
.field("start_position", &self.start_position)
.field("mouse_position", &self.mouse_position)
.field("width", &self.width)
.field("height", &self.height)
.field("center_height_map", ¢er_height_map_temp)
.field("center_height_map.shape()", &self.center_height_map.shape())
.field("corner_height_map", &corner_height_map_temp)
.field("corner_height_map.shape()", &self.corner_height_map.shape())
.field("min_height", &self.min_height)
.field("max_height", &self.max_height)
.field("slope_map", &slope_map_temp)
.field("slope_map.shape()", &self.slope_map.shape())
.field("los_map", &los_map_temp)
.field("los_map.shape()", &self.los_map.shape())
.field("air_los_map", &air_los_map_temp)
.field("air_los_map.shape()", &self.air_los_map.shape())
.field("radar_map", &radar_map_temp)
.field("radar_map.shape()", &self.radar_map.shape())
.field("sonar_map", &sonar_map_temp)
.field("sonar_map.shape()", &self.sonar_map.shape())
.field("seismic_map", &seismic_map_temp)
.field("seismic_map.shape()", &self.seismic_map.shape())
.field("jammer_map", &jammer_map_temp)
.field("jammer_map.shape()", &self.jammer_map.shape())
.field("sonar_jammer_map", &sonar_jammer_map_temp)
.field("sonar_jammer_map.shape()", &self.sonar_jammer_map.shape())
.field("resource_raw_map", &resource_raw_map_temp)
.field(
"resource_raw_map.iter().shape()",
&self
.resource_raw_map
.iter()
.map(|(k, v)| (k, v.shape()))
.collect::<Vec<_>>(),
)
.field("resource_spots_map", &resource_spots_map_temp)
.field(
"resource_spots_map.iter().len()",
&self
.resource_spots_map
.iter()
.map(|(k, v)| (k, v.len()))
.collect::<Vec<_>>(),
)
.field(
"resource_spots_average_income",
&self.resource_spots_average_income,
)
.field("hash", &self.hash)
.field("name", &self.name)
.field("human_name", &self.human_name)
.field("max_resource", &self.max_resource)
.field("extractor_radius", &self.extractor_radius)
.field("min_wind", &self.min_wind)
.field("max_wind", &self.max_wind)
.field("current_wind", &self.current_wind)
.field("tidal_strength", &self.tidal_strength)
.field("gravity", &self.gravity)
.field("water_damage", &self.water_damage)
.field("deformable", &self.deformable)
.field("hardness", &self.hardness)
.finish()
}
}
impl AIInterface {
pub fn map(&self) -> Map {
Map { ai_id: self.ai_id }
}
}
thread_local! {
static CENTER_MAP: DMatrix<f32> = DMatrix::<f32>::zeros(0, 0);
}
impl Map {
pub fn checksum(&self) -> Result<i32, Box<dyn Error>> {
let get_checksum = get_callback!(self.ai_id, Map_getChecksum)?;
Ok(unsafe { get_checksum(self.ai_id) })
}
pub fn start_position(&self) -> Result<[f32; 3], Box<dyn Error>> {
let get_start_pos = get_callback!(self.ai_id, Map_getStartPos)?;
let mut ret = [NAN; 3];
unsafe { get_start_pos(self.ai_id, ret.as_mut_ptr()) };
Ok(ret)
}
pub fn mouse_position(&self) -> Result<[f32; 3], Box<dyn Error>> {
let get_mouse_pos = get_callback!(self.ai_id, Map_getMousePos)?;
let mut ret = [NAN; 3];
unsafe { get_mouse_pos(self.ai_id, ret.as_mut_ptr()) };
Ok(ret)
}
pub fn is_position_in_camera(
&self,
position: [f32; 3],
radius: f32,
) -> Result<bool, Box<dyn Error>> {
let get_is_position_in_camera = get_callback!(self.ai_id, Map_isPosInCamera)?;
let mut inner_position = position;
Ok(unsafe { get_is_position_in_camera(self.ai_id, inner_position.as_mut_ptr(), radius) })
}
pub fn width(&self) -> Result<i32, Box<dyn Error>> {
let get_width = get_callback!(self.ai_id, Map_getWidth)?;
Ok(unsafe { get_width(self.ai_id) })
}
pub fn height(&self) -> Result<i32, Box<dyn Error>> {
let get_height = get_callback!(self.ai_id, Map_getHeight)?;
Ok(unsafe { get_height(self.ai_id) })
}
pub fn center_height_map(&self) -> Result<DMatrix<f32>, Box<dyn Error>> {
let mut ret = DMatrix::<f32>::zeros(0, 0);
CENTER_MAP.try_with::<_, Result<(), Box<dyn Error>>>(|cm| {
if cm.shape() == (0, 0) {
let get_height_map = get_callback!(self.ai_id, Map_getHeightMap)?;
let map_size = unsafe { get_height_map(self.ai_id, null_mut(), i32::MAX) };
let mut map_raw = vec![0.0_f32; map_size as usize];
unsafe { get_height_map(self.ai_id, map_raw.as_mut_ptr(), map_size) };
ret = DMatrix::from_row_iterator(
self.width()? as usize,
self.height()? as usize,
map_raw,
);
} else {
ret = cm.clone();
}
Ok(())
})??;
Ok(ret)
}
pub fn corner_height_map(&self) -> Result<DMatrix<f32>, Box<dyn Error>> {
let get_corner_height_map = get_callback!(self.ai_id, Map_getCornersHeightMap)?;
let map_size = unsafe { get_corner_height_map(self.ai_id, null_mut(), i32::MAX) };
let mut map_raw = vec![0.0_f32; map_size as usize];
unsafe { get_corner_height_map(self.ai_id, map_raw.as_mut_ptr(), map_size) };
let map = DMatrix::from_row_iterator(
self.width()? as usize + 1,
self.height()? as usize + 1,
map_raw,
);
Ok(map)
}
pub fn min_height(&self) -> Result<f32, Box<dyn Error>> {
let get_min_height = get_callback!(self.ai_id, Map_getMinHeight)?;
Ok(unsafe { get_min_height(self.ai_id) })
}
pub fn max_height(&self) -> Result<f32, Box<dyn Error>> {
let get_max_height = get_callback!(self.ai_id, Map_getMaxHeight)?;
Ok(unsafe { get_max_height(self.ai_id) })
}
pub fn slope_map(&self) -> Result<DMatrix<f32>, Box<dyn Error>> {
let get_slope_map = get_callback!(self.ai_id, Map_getSlopeMap)?;
let map_size = unsafe { get_slope_map(self.ai_id, null_mut(), i32::MAX) };
let mut map_raw = vec![0.0_f32; map_size as usize];
unsafe { get_slope_map(self.ai_id, map_raw.as_mut_ptr(), map_size) };
let map = DMatrix::from_row_iterator(
self.width()? as usize / 2,
self.height()? as usize / 2,
map_raw,
);
Ok(map)
}
pub fn los_map(&self) -> Result<DMatrix<i32>, Box<dyn Error>> {
let get_los_map = get_callback!(self.ai_id, Map_getLosMap)?;
let map_size = unsafe { get_los_map(self.ai_id, null_mut(), i32::MAX) };
let mut map_raw = vec![0_i32; map_size as usize];
unsafe { get_los_map(self.ai_id, map_raw.as_mut_ptr(), map_size) };
let mip_level = AIInterface::new(self.ai_id).game_mod().mip_level();
let res = (1 << mip_level.los()?).max(1);
let map = DMatrix::from_row_iterator(
self.width()? as usize / res,
self.height()? as usize / res,
map_raw,
);
Ok(map)
}
pub fn air_los_map(&self) -> Result<DMatrix<i32>, Box<dyn Error>> {
let get_air_los_map = get_callback!(self.ai_id, Map_getAirLosMap)?;
let map_size = unsafe { get_air_los_map(self.ai_id, null_mut(), i32::MAX) };
let mut map_raw = vec![0_i32; map_size as usize];
unsafe { get_air_los_map(self.ai_id, map_raw.as_mut_ptr(), map_size) };
let mip_level = AIInterface::new(self.ai_id).game_mod().mip_level();
let res = (1 << mip_level.air()?).max(1);
let map = DMatrix::from_row_iterator(
self.width()? as usize / res,
self.height()? as usize / res,
map_raw,
);
Ok(map)
}
pub fn radar_map(&self) -> Result<DMatrix<i32>, Box<dyn Error>> {
let get_radar_map = get_callback!(self.ai_id, Map_getRadarMap)?;
let map_size = unsafe { get_radar_map(self.ai_id, null_mut(), i32::MAX) };
let mut map_raw = vec![0_i32; map_size as usize];
unsafe { get_radar_map(self.ai_id, map_raw.as_mut_ptr(), map_size) };
let mip_level = AIInterface::new(self.ai_id).game_mod().mip_level();
let res = (1 << mip_level.radar()?).max(1);
let map = DMatrix::from_row_iterator(
self.width()? as usize / res,
self.height()? as usize / res,
map_raw,
);
Ok(map)
}
pub fn sonar_map(&self) -> Result<DMatrix<i32>, Box<dyn Error>> {
let get_sonar_map = get_callback!(self.ai_id, Map_getSonarMap)?;
let map_size = unsafe { get_sonar_map(self.ai_id, null_mut(), i32::MAX) };
let mut map_raw = vec![0_i32; map_size as usize];
unsafe { get_sonar_map(self.ai_id, map_raw.as_mut_ptr(), map_size) };
let mip_level = AIInterface::new(self.ai_id).game_mod().mip_level();
let res = (1 << mip_level.radar()?).max(1);
let map = DMatrix::from_row_iterator(
self.width()? as usize / res,
self.height()? as usize / res,
map_raw,
);
Ok(map)
}
pub fn seismic_map(&self) -> Result<DMatrix<i32>, Box<dyn Error>> {
let get_sonar_map = get_callback!(self.ai_id, Map_getSeismicMap)?;
let map_size = unsafe { get_sonar_map(self.ai_id, null_mut(), i32::MAX) };
let mut map_raw = vec![0_i32; map_size as usize];
unsafe { get_sonar_map(self.ai_id, map_raw.as_mut_ptr(), map_size) };
let mip_level = AIInterface::new(self.ai_id).game_mod().mip_level();
let res = (1 << mip_level.radar()?).max(1);
let map = DMatrix::from_row_iterator(
self.width()? as usize / res,
self.height()? as usize / res,
map_raw,
);
Ok(map)
}
pub fn jammer_map(&self) -> Result<DMatrix<i32>, Box<dyn Error>> {
let get_jammer_map = get_callback!(self.ai_id, Map_getJammerMap)?;
let map_size = unsafe { get_jammer_map(self.ai_id, null_mut(), i32::MAX) };
let mut map_raw = vec![0_i32; map_size as usize];
unsafe { get_jammer_map(self.ai_id, map_raw.as_mut_ptr(), map_size) };
let mip_level = AIInterface::new(self.ai_id).game_mod().mip_level();
let res = (1 << mip_level.radar()?).max(1);
let map = DMatrix::from_row_iterator(
self.width()? as usize / res,
self.height()? as usize / res,
map_raw,
);
Ok(map)
}
pub fn sonar_jammer_map(&self) -> Result<DMatrix<i32>, Box<dyn Error>> {
let get_sonar_jammer_map = get_callback!(self.ai_id, Map_getSonarJammerMap)?;
let map_size = unsafe { get_sonar_jammer_map(self.ai_id, null_mut(), i32::MAX) };
let mut map_raw = vec![0_i32; map_size as usize];
unsafe { get_sonar_jammer_map(self.ai_id, map_raw.as_mut_ptr(), map_size) };
let mip_level = AIInterface::new(self.ai_id).game_mod().mip_level();
let res = (1 << mip_level.radar()?).max(1);
let map = DMatrix::from_row_iterator(
self.width()? as usize / res,
self.height()? as usize / res,
map_raw,
);
Ok(map)
}
pub fn resource_raw_map(&self, resource: Resource) -> Result<DMatrix<i16>, Box<dyn Error>> {
let get_resource_raw_map = get_callback!(self.ai_id, Map_getResourceMapRaw)?;
let map_size =
unsafe { get_resource_raw_map(self.ai_id, resource.resource_id, null_mut(), i32::MAX) };
if map_size == 0 {
Ok(DMatrix::zeros(0, 0))
} else {
let mut map_raw = vec![0_i16; map_size as usize];
unsafe {
get_resource_raw_map(
self.ai_id,
resource.resource_id,
map_raw.as_mut_ptr(),
map_size,
)
};
let map = DMatrix::from_row_iterator(
self.width()? as usize / 2,
self.height()? as usize / 2,
map_raw,
);
Ok(map)
}
}
pub fn resource_spots_map(
&self,
resource: Resource,
) -> Result<Vec<((f32, f32), f32)>, Box<dyn Error>> {
let get_resource_spots_map = get_callback!(self.ai_id, Map_getResourceMapSpotsPositions)?;
let map_size = unsafe {
get_resource_spots_map(self.ai_id, resource.resource_id, null_mut(), i32::MAX)
};
let mut map_raw = vec![0.0_f32; map_size as usize];
unsafe {
get_resource_spots_map(
self.ai_id,
resource.resource_id,
map_raw.as_mut_ptr(),
map_size,
)
};
let map = (&map_raw.into_iter().chunks(3))
.into_iter()
.map(|mut v| {
let x = v.next().unwrap();
let relative_amount = v.next().unwrap();
let z = v.next().unwrap() + 16.0;
((x, z), relative_amount)
})
.collect::<Vec<_>>();
Ok(map)
}
pub fn resource_spots_average_income(&self, resource: Resource) -> Result<f32, Box<dyn Error>> {
let get_resource_spots_average_income =
get_callback!(self.ai_id, Map_getResourceMapSpotsAverageIncome)?;
Ok(unsafe { get_resource_spots_average_income(self.ai_id, resource.resource_id) })
}
pub fn hash(&self) -> Result<i32, Box<dyn Error>> {
let get_hash = get_callback!(self.ai_id, Map_getHash)?;
Ok(unsafe { get_hash(self.ai_id) })
}
pub fn name(&self) -> Result<String, Box<dyn Error>> {
let get_name = get_callback!(self.ai_id, Map_getName)?;
Ok(String::from(
unsafe { CStr::from_ptr(get_name(self.ai_id)) }.to_str()?,
))
}
pub fn human_name(&self) -> Result<String, Box<dyn Error>> {
let get_human_name = get_callback!(self.ai_id, Map_getHumanName)?;
Ok(String::from(
unsafe { CStr::from_ptr(get_human_name(self.ai_id)) }.to_str()?,
))
}
pub fn max_resource(&self, resource: Resource) -> Result<f32, Box<dyn Error>> {
let get_max_resource = get_callback!(self.ai_id, Map_getMaxResource)?;
Ok(unsafe { get_max_resource(self.ai_id, resource.resource_id) })
}
pub fn extractor_radius(&self, resource: Resource) -> Result<f32, Box<dyn Error>> {
let get_extractor_radius = get_callback!(self.ai_id, Map_getExtractorRadius)?;
Ok(unsafe { get_extractor_radius(self.ai_id, resource.resource_id) })
}
pub fn min_wind(&self) -> Result<f32, Box<dyn Error>> {
let get_min_wind = get_callback!(self.ai_id, Map_getMinWind)?;
Ok(unsafe { get_min_wind(self.ai_id) })
}
pub fn max_wind(&self) -> Result<f32, Box<dyn Error>> {
let get_max_wind = get_callback!(self.ai_id, Map_getMaxWind)?;
Ok(unsafe { get_max_wind(self.ai_id) })
}
pub fn current_wind(&self) -> Result<f32, Box<dyn Error>> {
let get_cur_wind = get_callback!(self.ai_id, Map_getCurWind)?;
Ok(unsafe { get_cur_wind(self.ai_id) })
}
pub fn tidal_strength(&self) -> Result<f32, Box<dyn Error>> {
let get_tidal_strength = get_callback!(self.ai_id, Map_getTidalStrength)?;
Ok(unsafe { get_tidal_strength(self.ai_id) })
}
pub fn gravity(&self) -> Result<f32, Box<dyn Error>> {
let get_gravity = get_callback!(self.ai_id, Map_getGravity)?;
Ok(unsafe { get_gravity(self.ai_id) })
}
pub fn water_damage(&self) -> Result<f32, Box<dyn Error>> {
let get_water_damage = get_callback!(self.ai_id, Map_getWaterDamage)?;
Ok(unsafe { get_water_damage(self.ai_id) })
}
pub fn deformable(&self) -> Result<bool, Box<dyn Error>> {
let get_deformable = get_callback!(self.ai_id, Map_isDeformable)?;
Ok(unsafe { get_deformable(self.ai_id) })
}
pub fn hardness(&self) -> Result<f32, Box<dyn Error>> {
let get_hardness = get_callback!(self.ai_id, Map_getHardness)?;
Ok(unsafe { get_hardness(self.ai_id) })
}
pub fn all(&self) -> Result<MapAll, Box<dyn Error>> {
Ok(MapAll {
checksum: self.checksum()?,
start_position: self.start_position()?,
mouse_position: self.mouse_position()?,
width: self.width()?,
height: self.height()?,
center_height_map: self.center_height_map()?,
corner_height_map: self.corner_height_map()?,
min_height: self.min_height()?,
max_height: self.max_height()?,
slope_map: self.slope_map()?,
los_map: self.los_map()?,
air_los_map: self.air_los_map()?,
radar_map: self.radar_map()?,
sonar_map: self.sonar_map()?,
seismic_map: self.seismic_map()?,
jammer_map: self.jammer_map()?,
sonar_jammer_map: self.sonar_jammer_map()?,
resource_raw_map: AIInterface::new(self.ai_id)
.resource_interface()
.get_resources()?
.into_iter()
.filter_map(|resource| {
if let Ok(res) = self.resource_raw_map(resource) {
Some((resource, res))
} else {
None
}
})
.collect(),
resource_spots_map: AIInterface::new(self.ai_id)
.resource_interface()
.get_resources()?
.into_iter()
.filter_map(|resource| {
if let Ok(res) = self.resource_spots_map(resource) {
Some((resource, res))
} else {
None
}
})
.collect(),
resource_spots_average_income: AIInterface::new(self.ai_id)
.resource_interface()
.get_resources()?
.into_iter()
.filter_map(|resource| {
if let Ok(res) = self.resource_spots_average_income(resource) {
Some((resource, res))
} else {
None
}
})
.collect(),
hash: self.hash()?,
name: self.name()?,
human_name: self.human_name()?,
max_resource: AIInterface::new(self.ai_id)
.resource_interface()
.get_resources()?
.into_iter()
.filter_map(|resource| {
if let Ok(res) = self.max_resource(resource) {
Some((resource, res))
} else {
None
}
})
.collect(),
extractor_radius: AIInterface::new(self.ai_id)
.resource_interface()
.get_resources()?
.into_iter()
.filter_map(|resource| {
if let Ok(res) = self.extractor_radius(resource) {
Some((resource, res))
} else {
None
}
})
.collect(),
min_wind: self.min_wind()?,
max_wind: self.max_wind()?,
current_wind: self.current_wind()?,
tidal_strength: self.tidal_strength()?,
gravity: self.gravity()?,
water_damage: self.water_damage()?,
deformable: self.deformable()?,
hardness: self.hardness()?,
})
}
}