use std::{error::Error, hash::Hash};
use serde::{Deserialize, Serialize};
use crate::{
ai_interface::{
callback::{
facing::Facing,
group::Group,
resource::Resource,
teams::Team,
unit::{
command_info::{UnitCurrentCommand, UnitSupportedCommand},
weapon::UnitWeapon,
},
unit_def::UnitDef,
},
AIInterface,
},
get_callback,
};
mod command;
pub mod command_info;
pub mod weapon;
#[derive(Copy, Clone, Debug)]
pub struct UnitInterface {
ai_id: i32,
}
#[derive(Clone, Debug)]
pub struct UnitInterfaceAll {
unit_limit: i32,
unit_max: i32,
unit_definitions: Vec<UnitDef>,
}
#[derive(Debug, Copy, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
pub struct Unit {
pub ai_id: i32,
pub unit_id: i32,
}
#[derive(Clone, Debug)]
pub struct UnitAll {
unit_def: UnitDef,
team: Team,
stockpile: i32,
stockpile_queued: i32,
max_speed: f32,
max_range: f32,
max_health: f32,
max_experience: f32,
health: f32,
speed: f32,
power: f32,
position: [f32; 3],
velocity: [f32; 3],
is_activated: bool,
is_being_built: bool,
is_cloaked: bool,
is_paralyzed: bool,
is_neutral: bool,
facing: Facing,
last_order_frame: i32,
weapons: Vec<UnitWeapon>,
group: Option<Group>,
command_queue_type: i32,
current_commands: Vec<UnitCurrentCommand>,
supported_commands: Vec<UnitSupportedCommand>,
}
impl Unit {
pub fn unit_def(&self) -> Result<UnitDef, Box<dyn Error>> {
let get_def_func = get_callback!(self.ai_id, Unit_getDef)?;
Ok(UnitDef {
ai_id: self.ai_id,
def_id: unsafe { get_def_func(self.ai_id, self.unit_id) },
})
}
pub fn team(&self) -> Result<Team, Box<dyn Error>> {
let get_team_func = get_callback!(self.ai_id, Unit_getTeam)?;
Ok(Team {
ai_id: self.ai_id,
team_id: unsafe { get_team_func(self.ai_id, self.unit_id) },
})
}
pub fn get_stockpile(&self) -> Result<i32, Box<dyn Error>> {
let get_stockpile_func = get_callback!(self.ai_id, Unit_getStockpile)?;
Ok(unsafe { get_stockpile_func(self.ai_id, self.unit_id) })
}
pub fn stockpile_queued(&self) -> Result<i32, Box<dyn Error>> {
let get_stpclpile_queue_func = get_callback!(self.ai_id, Unit_getStockpileQueued)?;
Ok(unsafe { get_stpclpile_queue_func(self.ai_id, self.unit_id) })
}
pub fn max_speed(&self) -> Result<f32, Box<dyn Error>> {
let get_speed_func = get_callback!(self.ai_id, Unit_getMaxSpeed)?;
Ok(unsafe { get_speed_func(self.ai_id, self.unit_id) })
}
pub fn max_range(&self) -> Result<f32, Box<dyn Error>> {
let get_range_func = get_callback!(self.ai_id, Unit_getMaxRange)?;
Ok(unsafe { get_range_func(self.ai_id, self.unit_id) })
}
pub fn max_health(&self) -> Result<f32, Box<dyn Error>> {
let get_health_func = get_callback!(self.ai_id, Unit_getMaxHealth)?;
Ok(unsafe { get_health_func(self.ai_id, self.unit_id) })
}
pub fn max_experience(&self) -> Result<f32, Box<dyn Error>> {
let get_experience_func = get_callback!(self.ai_id, Unit_getExperience)?;
Ok(unsafe { get_experience_func(self.ai_id, self.unit_id) })
}
pub fn health(&self) -> Result<f32, Box<dyn Error>> {
let get_health_func = get_callback!(self.ai_id, Unit_getHealth)?;
Ok(unsafe { get_health_func(self.ai_id, self.unit_id) })
}
pub fn speed(&self) -> Result<f32, Box<dyn Error>> {
let get_speed_func = get_callback!(self.ai_id, Unit_getSpeed)?;
Ok(unsafe { get_speed_func(self.ai_id, self.unit_id) })
}
pub fn power(&self) -> Result<f32, Box<dyn Error>> {
let get_power_func = get_callback!(self.ai_id, Unit_getPower)?;
Ok(unsafe { get_power_func(self.ai_id, self.unit_id) })
}
pub fn resource_use(&self, resource: Resource) -> Result<f32, Box<dyn Error>> {
let get_resource_use_func = get_callback!(self.ai_id, Unit_getResourceUse)?;
Ok(unsafe { get_resource_use_func(self.ai_id, self.unit_id, resource.resource_id) })
}
pub fn resource_make(&self, resource: Resource) -> Result<f32, Box<dyn Error>> {
let get_resource_make_func = get_callback!(self.ai_id, Unit_getResourceMake)?;
Ok(unsafe { get_resource_make_func(self.ai_id, self.unit_id, resource.resource_id) })
}
pub fn position(&self) -> Result<[f32; 3], Box<dyn Error>> {
let get_position_func = get_callback!(self.ai_id, Unit_getPos)?;
let mut ret = [0.0_f32; 3];
unsafe { get_position_func(self.ai_id, self.unit_id, ret.as_mut_ptr()) };
Ok(ret)
}
pub fn velocity(&self) -> Result<[f32; 3], Box<dyn Error>> {
let get_velocity_func = get_callback!(self.ai_id, Unit_getVel)?;
let mut ret = [0.0_f32; 3];
unsafe { get_velocity_func(self.ai_id, self.unit_id, ret.as_mut_ptr()) };
Ok(ret)
}
pub fn is_activated(&self) -> Result<bool, Box<dyn Error>> {
let get_is_activated_func = get_callback!(self.ai_id, Unit_isActivated)?;
Ok(unsafe { get_is_activated_func(self.ai_id, self.unit_id) })
}
pub fn is_being_built(&self) -> Result<bool, Box<dyn Error>> {
let get_is_being_built_func = get_callback!(self.ai_id, Unit_isBeingBuilt)?;
Ok(unsafe { get_is_being_built_func(self.ai_id, self.unit_id) })
}
pub fn is_cloaked(&self) -> Result<bool, Box<dyn Error>> {
let get_is_cloaked_func = get_callback!(self.ai_id, Unit_isCloaked)?;
Ok(unsafe { get_is_cloaked_func(self.ai_id, self.unit_id) })
}
pub fn is_paralyzed(&self) -> Result<bool, Box<dyn Error>> {
let get_is_paralyzed_func = get_callback!(self.ai_id, Unit_isParalyzed)?;
Ok(unsafe { get_is_paralyzed_func(self.ai_id, self.unit_id) })
}
pub fn is_neutral(&self) -> Result<bool, Box<dyn Error>> {
let get_is_neutral_func = get_callback!(self.ai_id, Unit_isNeutral)?;
Ok(unsafe { get_is_neutral_func(self.ai_id, self.unit_id) })
}
pub fn facing(&self) -> Result<Facing, Box<dyn Error>> {
let get_facing_func = get_callback!(self.ai_id, Unit_getBuildingFacing)?;
Ok(unsafe { get_facing_func(self.ai_id, self.unit_id) }.into())
}
pub fn last_order_frame(&self) -> Result<i32, Box<dyn Error>> {
let get_last_order_frame_func = get_callback!(self.ai_id, Unit_getLastUserOrderFrame)?;
Ok(unsafe { get_last_order_frame_func(self.ai_id, self.unit_id) })
}
pub fn weapons(&self) -> Result<Vec<UnitWeapon>, Box<dyn Error>> {
let get_weapons_func = get_callback!(self.ai_id, Unit_getWeapons)?;
let get_weapon_def_func = get_callback!(self.ai_id, Unit_getWeapon)?;
let number_of_weapons = unsafe { get_weapons_func(self.ai_id, self.unit_id) };
Ok((0..number_of_weapons)
.map(|i| UnitWeapon {
ai_id: self.ai_id,
unit_id: self.unit_id,
weapon_id: i,
})
.collect())
}
pub fn group(&self) -> Result<Option<Group>, Box<dyn Error>> {
let get_group_func = get_callback!(self.ai_id, Unit_getGroup)?;
let group_id = unsafe { get_group_func(self.ai_id, self.unit_id) };
Ok(if group_id == -1 {
None
} else {
Some(Group {
ai_id: self.ai_id,
group_id,
})
})
}
pub fn command_queue_type(&self) -> Result<i32, Box<dyn Error>> {
let get_current_command_type_func = get_callback!(self.ai_id, Unit_CurrentCommand_getType)?;
Ok(unsafe { get_current_command_type_func(self.ai_id, self.unit_id) })
}
pub fn current_commands(&self) -> Result<Vec<UnitCurrentCommand>, Box<dyn Error>> {
let get_current_commands_func = get_callback!(self.ai_id, Unit_getCurrentCommands)?;
let number_of_commands = unsafe { get_current_commands_func(self.ai_id, self.unit_id) };
Ok((0..number_of_commands)
.map(|current_command_index| UnitCurrentCommand {
ai_id: self.ai_id,
unit_id: self.unit_id,
current_command_index,
})
.collect())
}
pub fn supported_commands(&self) -> Result<Vec<UnitSupportedCommand>, Box<dyn Error>> {
let get_supported_commands_func = get_callback!(self.ai_id, Unit_getSupportedCommands)?;
let number_of_commands = unsafe { get_supported_commands_func(self.ai_id, self.unit_id) };
Ok((0..number_of_commands)
.map(|supported_command_index| UnitSupportedCommand {
ai_id: self.ai_id,
unit_id: self.unit_id,
supported_command_index,
})
.collect())
}
pub fn get_nearest_enemy(
&self,
radius: f32,
spherical: bool,
) -> Result<Option<Unit>, Box<dyn Error>> {
let mut units = AIInterface { ai_id: self.ai_id }
.unit_interface()
.enemy_units_at(self.position()?, radius, spherical)?;
units.sort_by(|unit1, unit2| {
let self_pos = self.position().unwrap();
let unit1_pos = unit1.position().unwrap();
let unit2_pos = unit2.position().unwrap();
let distance1 = (unit1_pos[0] - self_pos[0]).abs()
+ (unit1_pos[1] - self_pos[1]).abs()
+ (unit1_pos[2] - self_pos[2]).abs();
let distance2 = (unit2_pos[0] - self_pos[0]).abs()
+ (unit2_pos[1] - self_pos[1]).abs()
+ (unit2_pos[2] - self_pos[2]).abs();
distance1.partial_cmp(&distance2).unwrap()
});
Ok(units.first().cloned())
}
pub fn all(&self) -> Result<UnitAll, Box<dyn Error>> {
Ok(UnitAll {
unit_def: self.unit_def()?,
team: self.team()?,
stockpile: self.get_stockpile()?,
stockpile_queued: self.stockpile_queued()?,
max_speed: self.max_speed()?,
max_range: self.max_range()?,
max_health: self.max_health()?,
max_experience: self.max_experience()?,
health: self.health()?,
speed: self.speed()?,
power: self.power()?,
position: self.position()?,
velocity: self.velocity()?,
is_activated: self.is_activated()?,
is_being_built: self.is_being_built()?,
is_cloaked: self.is_cloaked()?,
is_paralyzed: self.is_paralyzed()?,
is_neutral: self.is_neutral()?,
facing: self.facing()?,
last_order_frame: self.last_order_frame()?,
weapons: self.weapons()?,
group: self.group()?,
command_queue_type: self.command_queue_type()?,
current_commands: self.current_commands()?,
supported_commands: self.supported_commands()?,
})
}
}
const UNIT_DEF_MAX: usize = 1024;
const UNIT_LIST_MAX: usize = 1024;
impl AIInterface {
pub fn unit_interface(&self) -> UnitInterface {
UnitInterface { ai_id: self.ai_id }
}
}
impl UnitInterface {
pub fn unit_limit(&self) -> Result<i32, Box<dyn Error>> {
let unit_limit_func = get_callback!(self.ai_id, Unit_getLimit)?;
Ok(unsafe { unit_limit_func(self.ai_id) })
}
pub fn unit_max(&self) -> Result<i32, Box<dyn Error>> {
let unit_max_func = get_callback!(self.ai_id, Unit_getMax)?;
Ok(unsafe { unit_max_func(self.ai_id) })
}
pub fn enemy_units(&self) -> Result<Vec<Unit>, Box<dyn Error>> {
let get_enemy_units_func = get_callback!(self.ai_id, getEnemyUnits)?;
let mut unit_list = [-1_i32; UNIT_LIST_MAX];
unsafe { get_enemy_units_func(self.ai_id, unit_list.as_mut_ptr(), UNIT_LIST_MAX as i32) };
Ok(unit_list
.iter()
.filter_map(|&unit_id| {
if unit_id == -1 {
None
} else {
Some(Unit {
ai_id: self.ai_id,
unit_id,
})
}
})
.collect())
}
pub fn enemy_units_at(
&self,
location: [f32; 3],
radius: f32,
spherical: bool,
) -> Result<Vec<Unit>, Box<dyn Error>> {
let get_enemy_units_func = get_callback!(self.ai_id, getEnemyUnitsIn)?;
let mut unit_list = [-1_i32; UNIT_LIST_MAX];
unsafe {
get_enemy_units_func(
self.ai_id,
location.clone().as_mut_ptr(),
radius,
spherical,
unit_list.as_mut_ptr(),
UNIT_LIST_MAX as i32,
)
};
Ok(unit_list
.iter()
.filter_map(|&unit_id| {
if unit_id == -1 {
None
} else {
Some(Unit {
ai_id: self.ai_id,
unit_id,
})
}
})
.collect())
}
pub fn friendly_units(&self) -> Result<Vec<Unit>, Box<dyn Error>> {
let get_friendly_units_func = get_callback!(self.ai_id, getFriendlyUnits)?;
let mut unit_list = [-1_i32; UNIT_LIST_MAX];
unsafe {
get_friendly_units_func(self.ai_id, unit_list.as_mut_ptr(), UNIT_LIST_MAX as i32)
};
Ok(unit_list
.iter()
.filter_map(|&unit_id| {
if unit_id == -1 {
None
} else {
Some(Unit {
ai_id: self.ai_id,
unit_id,
})
}
})
.collect())
}
pub fn friendly_units_at(
&self,
location: [f32; 3],
radius: f32,
spherical: bool,
) -> Result<Vec<Unit>, Box<dyn Error>> {
let get_friendly_units_func = get_callback!(self.ai_id, getFriendlyUnitsIn)?;
let mut unit_list = [-1_i32; UNIT_LIST_MAX];
unsafe {
get_friendly_units_func(
self.ai_id,
location.clone().as_mut_ptr(),
radius,
spherical,
unit_list.as_mut_ptr(),
UNIT_LIST_MAX as i32,
)
};
Ok(unit_list
.iter()
.filter_map(|&unit_id| {
if unit_id == -1 {
None
} else {
Some(Unit {
ai_id: self.ai_id,
unit_id,
})
}
})
.collect())
}
pub fn neutral_units(&self) -> Result<Vec<Unit>, Box<dyn Error>> {
let get_neutral_units_func = get_callback!(self.ai_id, getNeutralUnits)?;
let mut unit_list = [-1_i32; UNIT_LIST_MAX];
unsafe { get_neutral_units_func(self.ai_id, unit_list.as_mut_ptr(), UNIT_LIST_MAX as i32) };
Ok(unit_list
.iter()
.filter_map(|&unit_id| {
if unit_id == -1 {
None
} else {
Some(Unit {
ai_id: self.ai_id,
unit_id,
})
}
})
.collect())
}
pub fn neutral_units_at(
&self,
location: [f32; 3],
radius: f32,
spherical: bool,
) -> Result<Vec<Unit>, Box<dyn Error>> {
let get_neutral_units_func = get_callback!(self.ai_id, getNeutralUnitsIn)?;
let mut unit_list = [-1_i32; UNIT_LIST_MAX];
unsafe {
get_neutral_units_func(
self.ai_id,
location.clone().as_mut_ptr(),
radius,
spherical,
unit_list.as_mut_ptr(),
UNIT_LIST_MAX as i32,
)
};
Ok(unit_list
.iter()
.filter_map(|&unit_id| {
if unit_id == -1 {
None
} else {
Some(Unit {
ai_id: self.ai_id,
unit_id,
})
}
})
.collect())
}
pub fn team_units(&self) -> Result<Vec<Unit>, Box<dyn Error>> {
let get_team_units_func = get_callback!(self.ai_id, getTeamUnits)?;
let mut unit_list = [-1_i32; UNIT_LIST_MAX];
unsafe { get_team_units_func(self.ai_id, unit_list.as_mut_ptr(), UNIT_LIST_MAX as i32) };
Ok(unit_list
.iter()
.filter_map(|&unit_id| {
if unit_id == -1 {
None
} else {
Some(Unit {
ai_id: self.ai_id,
unit_id,
})
}
})
.collect())
}
pub fn selected_units(&self) -> Result<Vec<Unit>, Box<dyn Error>> {
let get_enemy_units_func = get_callback!(self.ai_id, getEnemyUnits)?;
let mut unit_list = [-1_i32; UNIT_LIST_MAX];
unsafe { get_enemy_units_func(self.ai_id, unit_list.as_mut_ptr(), UNIT_LIST_MAX as i32) };
Ok(unit_list
.iter()
.filter_map(|&unit_id| {
if unit_id == -1 {
None
} else {
Some(Unit {
ai_id: self.ai_id,
unit_id,
})
}
})
.collect())
}
pub fn get_unit_definitions(&self) -> Result<Vec<UnitDef>, Box<dyn Error>> {
let get_unit_defs_func = get_callback!(self.ai_id, getUnitDefs)?;
let mut temp = [-1; UNIT_DEF_MAX];
unsafe { get_unit_defs_func(self.ai_id, temp.as_mut_ptr(), temp.len() as i32) };
Ok(temp
.iter()
.filter(|&&def_id| def_id != -1)
.map(|&def_id| UnitDef {
ai_id: self.ai_id,
def_id,
})
.collect())
}
pub fn all(&self) -> Result<UnitInterfaceAll, Box<dyn Error>> {
Ok(UnitInterfaceAll {
unit_limit: self.unit_limit()?,
unit_max: self.unit_max()?,
unit_definitions: self.get_unit_definitions()?,
})
}
}