use crate::force::Force;
use crate::game::Game;
use crate::types::c_str_to_str;
use crate::types::Color;
use crate::unit::Unit;
use bwapi_wrapper::prelude::*;
use bwapi_wrapper::*;
use num_traits::FromPrimitive;
use std::fmt;
pub type PlayerId = usize;
#[derive(Clone)]
pub struct Player {
pub id: PlayerId,
game: Game,
data: &'static BWAPI_PlayerData,
}
impl fmt::Debug for Player {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Player").field("id", &self.id).finish()
}
}
impl Player {
pub(crate) fn new(id: PlayerId, game: Game, data: &'static BWAPI_PlayerData) -> Self {
Player { id, game, data }
}
pub fn all_unit_count(&self, unit: UnitType) -> i32 {
self.data.allUnitCount[unit as usize]
}
pub fn armor(&self, unit: UnitType) -> i32 {
let mut armor = unit.armor();
armor += self.get_upgrade_level(unit.armor_upgrade());
if unit == UnitType::Zerg_Ultralisk
&& self.get_upgrade_level(UpgradeType::Chitinous_Plating) > 0
|| unit == UnitType::Hero_Torrasque
{
armor += 2;
}
armor
}
pub fn completed_unit_count(&self, unit: UnitType) -> i32 {
self.data.completedUnitCount[unit as usize]
}
pub fn damage(&self, wpn: WeaponType) -> i32 {
let mut dmg = wpn.damage_amount();
dmg += self.get_upgrade_level(wpn.upgrade_type()) * wpn.damage_bonus();
dmg * wpn.damage_factor()
}
pub fn dead_unit_count(&self, unit: UnitType) -> i32 {
self.data.deadUnitCount[unit as usize]
}
pub fn gas(&self) -> i32 {
self.data.gas
}
pub fn gathered_gas(&self) -> i32 {
self.data.gatheredGas
}
pub fn gathered_minerals(&self) -> i32 {
self.data.gatheredMinerals
}
pub fn get_building_score(&self) -> i32 {
self.data.totalBuildingScore
}
pub fn get_color(&self) -> Color {
Color::from_i32(self.data.color).unwrap()
}
pub fn get_custom_score(&self) -> i32 {
self.data.customScore
}
pub(crate) fn force_id(&self) -> i32 {
self.data.force
}
pub fn get_force(&self) -> Force {
self.game.get_force(self.force_id())
}
pub fn get_id(&self) -> PlayerId {
self.id as PlayerId
}
pub fn get_kill_score(&self) -> i32 {
self.data.totalKillScore
}
pub fn get_max_upgrade_level(&self, upgrade: UpgradeType) -> i32 {
self.data.maxUpgradeLevel[upgrade as usize]
}
pub fn get_name(&self) -> String {
c_str_to_str(&self.data.name)
}
pub fn get_race(&self) -> Race {
Race::new(self.data.race)
}
pub fn get_units(&self) -> Vec<Unit> {
self.game
.get_all_units()
.iter()
.filter(|u| u.get_player() == *self)
.cloned()
.collect()
}
pub fn get_upgrade_level(&self, upgrade_type: UpgradeType) -> i32 {
self.data.upgradeLevel[upgrade_type as usize]
}
pub fn has_researched(&self, tech: TechType) -> bool {
self.data.hasResearched[tech as usize]
}
pub fn has_unit_type_requirement(&self, unit: UnitType, amount: i32) -> bool {
if unit == UnitType::None {
return true;
}
(match unit {
UnitType::Zerg_Hatchery => {
self.completed_unit_count(UnitType::Zerg_Hatchery)
+ self.all_unit_count(UnitType::Zerg_Lair)
+ self.all_unit_count(UnitType::Zerg_Hive)
}
UnitType::Zerg_Lair => {
self.completed_unit_count(UnitType::Zerg_Lair)
+ self.all_unit_count(UnitType::Zerg_Hive)
}
UnitType::Zerg_Spire => {
self.completed_unit_count(UnitType::Zerg_Spire)
+ self.all_unit_count(UnitType::Zerg_Greater_Spire)
}
_ => self.completed_unit_count(unit),
}) >= amount
}
pub fn incomplete_unit_count(&self, unit: UnitType) -> i32 {
self.all_unit_count(unit) - self.completed_unit_count(unit)
}
pub fn is_ally(&self, other: &Player) -> bool {
self.data.isAlly[other.id]
}
pub fn is_defeated(&self) -> bool {
self.data.isDefeated
}
pub fn is_enemy(&self, other: &Player) -> bool {
self.data.isEnemy[other.id]
}
pub fn is_neutral(&self) -> bool {
self.data.isNeutral
}
pub fn is_observer(&self) -> bool {
!self.data.isParticipating
}
pub fn is_research_available(&self, tech: TechType) -> bool {
self.data.isResearchAvailable[tech as usize]
}
pub fn is_researching(&self, tech: TechType) -> bool {
self.data.isResearching[tech as usize]
}
pub fn is_unit_available(&self, unit: UnitType) -> bool {
self.data.isUnitAvailable[unit as usize]
}
pub fn is_upgrading(&self, upgrade: UpgradeType) -> bool {
self.data.isUpgrading[upgrade as usize]
}
pub fn is_victorious(&self) -> bool {
self.data.isVictorious
}
pub fn killed_unit_count(&self, unit: UnitType) -> i32 {
self.data.killedUnitCount[unit as usize]
}
pub fn left_game(&self) -> bool {
self.data.leftGame
}
pub fn max_energy(&self, unit: UnitType) -> i32 {
let mut energy = unit.max_energy();
if (unit == UnitType::Protoss_Arbiter
&& self.get_upgrade_level(UpgradeType::Khaydarin_Core) > 0)
|| (unit == UnitType::Protoss_Corsair
&& self.get_upgrade_level(UpgradeType::Argus_Jewel) > 0)
|| (unit == UnitType::Protoss_Dark_Archon
&& self.get_upgrade_level(UpgradeType::Argus_Talisman) > 0)
|| (unit == UnitType::Protoss_High_Templar
&& self.get_upgrade_level(UpgradeType::Khaydarin_Amulet) > 0)
|| (unit == UnitType::Terran_Ghost
&& self.get_upgrade_level(UpgradeType::Moebius_Reactor) > 0)
|| (unit == UnitType::Terran_Battlecruiser
&& self.get_upgrade_level(UpgradeType::Colossus_Reactor) > 0)
|| (unit == UnitType::Terran_Science_Vessel
&& self.get_upgrade_level(UpgradeType::Titan_Reactor) > 0)
|| (unit == UnitType::Terran_Wraith
&& self.get_upgrade_level(UpgradeType::Apollo_Reactor) > 0)
|| (unit == UnitType::Terran_Medic
&& self.get_upgrade_level(UpgradeType::Caduceus_Reactor) > 0)
|| (unit == UnitType::Zerg_Defiler
&& self.get_upgrade_level(UpgradeType::Metasynaptic_Node) > 0)
|| (unit == UnitType::Zerg_Queen
&& self.get_upgrade_level(UpgradeType::Gamete_Meiosis) > 0)
{
energy += 50
}
energy
}
pub fn minerals(&self) -> i32 {
self.data.minerals
}
pub fn refunded_gas(&self) -> i32 {
self.data.refundedGas
}
pub fn refunded_minerals(&self) -> i32 {
self.data.refundedMinerals
}
pub fn repaired_gas(&self) -> i32 {
self.data.repairedGas
}
pub fn repaired_minerals(&self) -> i32 {
self.data.repairedMinerals
}
pub fn sight_range(&self, unit: UnitType) -> i32 {
let mut range = unit.sight_range();
if (unit == UnitType::Terran_Ghost
&& self.get_upgrade_level(UpgradeType::Ocular_Implants) > 0)
|| (unit == UnitType::Zerg_Overlord
&& self.get_upgrade_level(UpgradeType::Antennae) > 0)
|| (unit == UnitType::Protoss_Observer
&& self.get_upgrade_level(UpgradeType::Sensor_Array) > 0)
|| (unit == UnitType::Protoss_Scout
&& self.get_upgrade_level(UpgradeType::Apial_Sensors) > 0)
{
range = 11 * 32
}
range
}
pub fn spent_gas(&self) -> i32 {
self.gathered_gas() + self.refunded_gas() - self.gas() - self.repaired_gas()
}
pub fn spent_minerals(&self) -> i32 {
self.gathered_minerals() + self.refunded_minerals()
- self.minerals()
- self.repaired_minerals()
}
pub fn supply_total(&self) -> i32 {
self.supply_total_for(self.get_race())
}
pub fn supply_total_for(&self, race: Race) -> i32 {
self.data.supplyTotal[race as usize]
}
pub fn supply_used(&self) -> i32 {
self.supply_used_by(self.get_race())
}
pub fn supply_used_by(&self, race: Race) -> i32 {
self.data.supplyUsed[race as usize]
}
pub fn top_speed(&self, unit: UnitType) -> f64 {
let mut speed = unit.top_speed();
if (unit == UnitType::Terran_Vulture
&& self.get_upgrade_level(UpgradeType::Ion_Thrusters) > 0)
|| (unit == UnitType::Zerg_Overlord
&& self.get_upgrade_level(UpgradeType::Pneumatized_Carapace) > 0)
|| (unit == UnitType::Zerg_Zergling
&& self.get_upgrade_level(UpgradeType::Metabolic_Boost) > 0)
|| (unit == UnitType::Zerg_Hydralisk
&& self.get_upgrade_level(UpgradeType::Muscular_Augments) > 0)
|| (unit == UnitType::Protoss_Zealot
&& self.get_upgrade_level(UpgradeType::Leg_Enhancements) > 0)
|| (unit == UnitType::Protoss_Shuttle
&& self.get_upgrade_level(UpgradeType::Gravitic_Drive) > 0)
|| (unit == UnitType::Protoss_Observer
&& self.get_upgrade_level(UpgradeType::Gravitic_Boosters) > 0)
|| (unit == UnitType::Protoss_Scout
&& self.get_upgrade_level(UpgradeType::Gravitic_Thrusters) > 0)
|| (unit == UnitType::Zerg_Ultralisk
&& self.get_upgrade_level(UpgradeType::Anabolic_Synthesis) > 0)
{
if unit == UnitType::Protoss_Scout {
speed += 427.0 / 256.0;
} else {
speed *= 1.5;
}
if speed < 853.0 / 256.0 {
speed = 853.0 / 256.0;
}
}
speed
}
pub fn visible_unit_count(&self, unit: UnitType) -> i32 {
self.data.visibleUnitCount[unit as usize]
}
pub fn weapon_damage_cooldown(&self, unit: UnitType) -> i32 {
let mut cooldown = unit.ground_weapon().damage_cooldown();
if unit == UnitType::Zerg_Zergling
&& self.get_upgrade_level(UpgradeType::Adrenal_Glands) > 0
{
cooldown /= 2;
cooldown = cooldown.clamp(5, 250);
}
cooldown
}
pub fn weapon_max_range(&self, weapon: WeaponType) -> i32 {
weapon.max_range() + self.weapon_range_extension(weapon)
}
pub fn weapon_range_extension(&self, weapon: WeaponType) -> i32 {
if (weapon == WeaponType::Gauss_Rifle
&& self.get_upgrade_level(UpgradeType::U_238_Shells) > 0)
|| (weapon == WeaponType::Needle_Spines
&& self.get_upgrade_level(UpgradeType::Grooved_Spines) > 0)
{
32
} else if weapon == WeaponType::Phase_Disruptor
&& self.get_upgrade_level(UpgradeType::Singularity_Charge) > 0
{
2 * 32
} else if weapon == WeaponType::Hellfire_Missile_Pack
&& self.get_upgrade_level(UpgradeType::Charon_Boosters) > 0
{
3 * 32
} else {
0
}
}
}
impl PartialEq for Player {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}