diff --git a/python-api-src/lib_unittype.cpp b/python-api-src/lib_unittype.cpp index c23529f32fa6af6b69fef61c56ce5acd822de656..346ffc5015f085bf1ad6fe8fdb4ff479d1e94d5c 100644 --- a/python-api-src/lib_unittype.cpp +++ b/python-api-src/lib_unittype.cpp @@ -8,7 +8,6 @@ void define_unittype(py::module & m) .def(py::init([](const sc2::UnitTypeID & type, IDABot & bot) { return UnitType(type, bot, bot); })) - .def(py::self == py::self) .def_property_readonly("unit_typeid", [](UnitType & unit_type) { return static_cast<sc2::UNIT_TYPEID>(unit_type.getAPIUnitType()); }) .def_property_readonly("name", &UnitType::getName, "The name of the unit as a sting.") .def_property_readonly("race", &UnitType::getRace, "The race the unit belongs to.") @@ -51,8 +50,13 @@ void define_unittype(py::module & m) .def("__hash__", [](const UnitType & unit_type) { return std::hash<CCUnitID>{}(unit_type.getAPIUnitType()); }) .def(py::self == py::self) .def("__repr__", [](const UnitType & unit_type) { return "<UnitType: '" + unit_type.getName() + "'>"; }) + .def("is", &UnitType::is) + .def("__lt__", &UnitType::operator<, py::is_operator()) + .def("__eq__", &UnitType::operator==, py::is_operator()) .doc() = R"( Wrapper for :class:`library.UNIT_TYPEID`. Represents a type of unit in the game. + NOTE: A lot of functions utilize checks that require the game to be running. + Therefore if you get an unexpected segmentation fault, it is likely due to the game not being in a running state. )"; // Not implemented in CommandCenter diff --git a/src/UnitType.cpp b/src/UnitType.cpp index f4fcbd4c3c41d9169eb0558a514e145de0cc9086..688ccb4c70417bf8fd9a737a3e651de2449d65ba 100644 --- a/src/UnitType.cpp +++ b/src/UnitType.cpp @@ -11,9 +11,9 @@ UnitType::UnitType() UnitType::UnitType(const sc2::UnitTypeID & type, sc2::Client & client) : m_client(&client) - , m_type(type) , m_bot(nullptr) , m_observer(nullptr) + , m_type(type) { @@ -21,18 +21,18 @@ UnitType::UnitType(const sc2::UnitTypeID & type, sc2::Client & client) UnitType::UnitType(const sc2::UnitTypeID & type, sc2::Client & client, IDABot & bot) : m_client(&client) - , m_type(type) , m_bot(&bot) , m_observer(nullptr) + , m_type(type) { } UnitType::UnitType(const sc2::UnitTypeID & type, sc2::Client & client, IDAReplayObserver & observer) : m_client(&client) - , m_type(type) - , m_observer(&observer) , m_bot(nullptr) + , m_observer(&observer) + , m_type(type) { @@ -70,7 +70,13 @@ std::string UnitType::getName() const CCRace UnitType::getRace() const { - return m_client->Observation()->GetUnitTypeData()[m_type].race; + auto UTData = m_client->Observation()->GetUnitTypeData(); + if (UTData.empty()) // Check to hopefully avoid segfaults + { + BOT_ASSERT(false, "Failed, try with game running!"); + return CCRace::Random; + } + return UTData[m_type].race; } bool UnitType::isCombatUnit() const @@ -225,7 +231,13 @@ bool UnitType::isWorker() const bool UnitType::canAttackGound() const { #ifdef SC2API - auto & weapons = m_client->Observation()->GetUnitTypeData()[m_type].weapons; + auto UTData = m_client->Observation()->GetUnitTypeData(); + if (UTData.empty()) // Check to hopefully avoid segfaults + { + BOT_ASSERT(false, "Failed, try with game running!"); + return false; + } + auto & weapons = UTData[m_type].weapons; if (weapons.empty()) { @@ -249,7 +261,14 @@ bool UnitType::canAttackGound() const bool UnitType::canAttackAir() const { #ifdef SC2API - auto & weapons = m_client->Observation()->GetUnitTypeData()[m_type].weapons; + + auto UTData = m_client->Observation()->GetUnitTypeData(); + if (UTData.empty()) // Check to hopefully avoid segfaults + { + BOT_ASSERT(false, "Failed, try with game running!"); + return false; + } + auto & weapons = UTData[m_type].weapons; if (weapons.empty()) { @@ -273,7 +292,13 @@ bool UnitType::canAttackAir() const CCPositionType UnitType::getAttackRange() const { #ifdef SC2API - auto & weapons = m_client->Observation()->GetUnitTypeData()[m_type].weapons; + auto UTData = m_client->Observation()->GetUnitTypeData(); + if (UTData.empty()) // Check to hopefully avoid segfaults + { + BOT_ASSERT(false, "Failed, try with game running!"); + return 0.0f; + } + auto & weapons = UTData[m_type].weapons; if (weapons.empty()) { @@ -299,8 +324,13 @@ CCPositionType UnitType::getAttackRange() const float UnitType::getAttackDamage() const { #ifdef SC2API - auto & weapons = m_client->Observation()->GetUnitTypeData()[m_type].weapons; - + auto UTData = m_client->Observation()->GetUnitTypeData(); + if (UTData.empty()) // Check to hopefully avoid segfaults + { + BOT_ASSERT(false, "Failed, try with game running!"); + return 0.0f; + } + auto & weapons = UTData[m_type].weapons; if (weapons.empty()) { return 0.0f; @@ -329,13 +359,20 @@ int UnitType::tileWidth() const if (isMineral()) { return 2; } if (isGeyser()) { return 3; } else { - if (m_bot != nullptr) + auto UTData = m_client->Observation()->GetAbilityData(); + if (UTData.empty()) // Check to hopefully avoid segfaults + { + BOT_ASSERT(false, "Failed, try with game running!"); + return -1; + } + + else if (m_bot != nullptr) { - return (int)(2 * m_client->Observation()->GetAbilityData()[m_bot->Data(*this).buildAbility].footprint_radius); + return (int)(2 * UTData[m_bot->Data(*this).buildAbility].footprint_radius); } else if (m_observer != nullptr) { - return (int)(2 * m_client->Observation()->GetAbilityData()[m_observer->Data(*this).buildAbility].footprint_radius); + return (int)(2 * UTData[m_observer->Data(*this).buildAbility].footprint_radius); } else { @@ -353,13 +390,20 @@ int UnitType::tileHeight() const if (isMineral()) { return 1; } if (isGeyser()) { return 3; } else { - if (m_bot != nullptr) + auto UTData = m_client->Observation()->GetAbilityData(); + if (UTData.empty()) // Check to hopefully avoid segfaults + { + BOT_ASSERT(false, "Failed, try with game running!"); + return -1; + } + + else if (m_bot != nullptr) { - return (int)(2 * m_client->Observation()->GetAbilityData()[m_bot->Data(*this).buildAbility].footprint_radius); + return (int)(2 * UTData[m_bot->Data(*this).buildAbility].footprint_radius); } else if (m_observer != nullptr) { - return (int)(2 * m_client->Observation()->GetAbilityData()[m_observer->Data(*this).buildAbility].footprint_radius); + return (int)(2 * UTData[m_observer->Data(*this).buildAbility].footprint_radius); } else { @@ -417,7 +461,14 @@ bool UnitType::isBuilding() const int UnitType::supplyProvided() const { #ifdef SC2API - return (int)m_client->Observation()->GetUnitTypeData()[m_type].food_provided; + auto UTData = m_client->Observation()->GetUnitTypeData(); + if (UTData.empty()) // Check to hopefully avoid segfaults + { + BOT_ASSERT(false, "Failed, try with game running!"); + return -1; + } + + return (int)UTData[m_type].food_provided; #else return m_type.supplyProvided(); #endif @@ -426,7 +477,14 @@ int UnitType::supplyProvided() const int UnitType::supplyRequired() const { #ifdef SC2API - return (int)m_client->Observation()->GetUnitTypeData()[m_type].food_required; + auto UTData = m_client->Observation()->GetUnitTypeData(); + if (UTData.empty()) // Check to hopefully avoid segfaults + { + BOT_ASSERT(false, "Failed, try with game running!"); + return -1; + } + + return (int)UTData[m_type].food_required; #else return m_type.supplyRequired(); #endif @@ -435,7 +493,13 @@ int UnitType::supplyRequired() const int UnitType::mineralPrice() const { #ifdef SC2API - return (int)m_client->Observation()->GetUnitTypeData()[m_type].mineral_cost; + auto UTData = m_client->Observation()->GetUnitTypeData(); + if (UTData.empty()) // Check to hopefully avoid segfaults + { + BOT_ASSERT(false, "Failed, try with game running!"); + return -1; + } + return (int)UTData[m_type].mineral_cost; #else return m_type.mineralPrice(); #endif @@ -444,7 +508,13 @@ int UnitType::mineralPrice() const int UnitType::gasPrice() const { #ifdef SC2API - return (int)m_client->Observation()->GetUnitTypeData()[m_type].vespene_cost; + auto UTData = m_client->Observation()->GetUnitTypeData(); + if (UTData.empty()) // Check to hopefully avoid segfaults + { + BOT_ASSERT(false, "Failed, try with game running!"); + return -1; + } + return (int)UTData[m_type].vespene_cost; #else return m_type.gasPrice(); #endif @@ -536,30 +606,66 @@ bool UnitType::isMorphedBuilding() const int UnitType::getMovementSpeed() const { - return m_client->Observation()->GetUnitTypeData()[m_type].movement_speed; + auto UTData = m_client->Observation()->GetUnitTypeData(); + if (UTData.empty()) // Check to hopefully avoid segfaults + { + BOT_ASSERT(false, "Failed, try with game running!"); + return -1; + } + return UTData[m_type].movement_speed; } int UnitType::getSightRange() const { - return m_client->Observation()->GetUnitTypeData()[m_type].sight_range; + auto UTData = m_client->Observation()->GetUnitTypeData(); + if (UTData.empty()) // Check to hopefully avoid segfaults + { + BOT_ASSERT(false, "Failed, try with game running!"); + return -1; + } + return UTData[m_type].sight_range; } UnitTypeID UnitType::getRequiredStructure() const { - return m_client->Observation()->GetUnitTypeData()[m_type].tech_requirement; + auto UTData = m_client->Observation()->GetUnitTypeData(); + if (UTData.empty()) // Check to hopefully avoid segfaults + { + BOT_ASSERT(false, "Failed, try with game running!"); + return UnitTypeID(); + } + return UTData[m_type].tech_requirement; } std::vector<sc2::UnitTypeID> UnitType::getEquivalentUnits() const { - return m_client->Observation()->GetUnitTypeData()[m_type].tech_alias; + auto UTData = m_client->Observation()->GetUnitTypeData(); + if (UTData.empty()) // Check to hopefully avoid segfaults + { + BOT_ASSERT(false, "Failed, try with game running!"); + return std::vector<sc2::UnitTypeID>(); + } + return UTData[m_type].tech_alias; } bool UnitType::requiredAttached() const { - return m_client->Observation()->GetUnitTypeData()[m_type].require_attached; + auto UTData = m_client->Observation()->GetUnitTypeData(); + if (UTData.empty()) // Check to hopefully avoid segfaults + { + BOT_ASSERT(false, "Failed, try with game running!"); + return false; + } + return UTData[m_type].require_attached; } float UnitType::getBuildTime() const { - return m_client->Observation()->GetUnitTypeData()[m_type].build_time; + auto UTData = m_client->Observation()->GetUnitTypeData(); + if (UTData.empty()) // Check to hopefully avoid segfaults + { + BOT_ASSERT(false, "Failed, try with game running!"); + return -1; + } + return UTData[m_type].build_time; } \ No newline at end of file