Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • stebr364/pycommandcenter
  • starcraft-ai-course/pycommandcenter
  • eriah592/pycommandcenter
  • edvbe696/pycommandcenter
  • dawab699/pycommandcenter
  • hanja189/pycommandcenter
  • teoga849/pycommandcenter
  • musab250/pycommandcenter
  • emibr898/pycommandcenter
  • chrgu102/pycommandcenter
  • axega544/pycommandcenter
  • edvth289/pycommandcenter
  • jonbo278/py-command-center-v-2
13 results
Show changes
Showing
with 4412 additions and 772 deletions
#include "library.h"
namespace py = pybind11;
void define_replay_unit(py::module & m)
{
py::class_<ReplayUnit>(m, "ReplayUnit")
.def_property_readonly("id", &ReplayUnit::getID, "The ID of the unit.")
.def_property_readonly("unit_type", &ReplayUnit::getType, "The :class:`commandcenter.UnitType` of the unit.")
.def_property_readonly("position", &ReplayUnit::getPosition, "The :class:`commandcenter.Point2D` of the unit.")
.def_property_readonly("tile_position", &ReplayUnit::getTilePosition, "The :class:`commandcenter.Point2DI` of the unit.")
.def_property_readonly("hit_points", &ReplayUnit::getHitPoints, "The hit points of the unit.")
.def_property_readonly("shields", &ReplayUnit::getShields, "The shields of the unit.")
.def_property_readonly("energy", &ReplayUnit::getEnergy, "The energy of the unit.")
.def_property_readonly("player", &ReplayUnit::getPlayer, "Returns the constant corresponding to player which this unit belongs to. See :ref:`playerconstants` for more information.")
.def_property_readonly("build_percentage", &ReplayUnit::getBuildPercentage, "The build percentage of the unit.")
.def_property_readonly("weapon_cooldown", &ReplayUnit::getWeaponCooldown, "The weapon cooldown of the unit.")
.def_property_readonly("is_completed", &ReplayUnit::isCompleted, "Whether the unit is completed, returns build_progress >= 1.")
.def_property_readonly("is_being_constructed", &ReplayUnit::isBeingConstructed, "Whether the unit is being constructed, returns build_progress > 0.")
.def_property_readonly("is_cloaked", &ReplayUnit::isCloaked, "Whether the unit is cloaked.")
.def_property_readonly("is_flying", &ReplayUnit::isFlying, "Whether the unit is flying.")
.def_property_readonly("buffs", &ReplayUnit::buffs, "Returns a list of BuffIDs representing the buffs on the unit.")
.def_property_readonly("is_alive", &ReplayUnit::isAlive, "Whether the unit is alive.")
.def_property_readonly("is_powered", &ReplayUnit::isPowered, "Whether the unit is powered.")
.def_property_readonly("is_idle", &ReplayUnit::isIdle, "Whether the unit is idle.")
.def_property_readonly("is_burrowed", &ReplayUnit::isBurrowed, "Whether the unit is burrowed.")
.def_property_readonly("is_valid", &ReplayUnit::isValid, "Whether the unit is valid.")
.def_property_readonly("is_training", &ReplayUnit::isTraining, "Whether the unit is training.")
.def_property_readonly("is_blip", &ReplayUnit::isBlip, "Whether the unit is a blip ie a ping on the map.")
.def_property_readonly("target", &ReplayUnit::getTarget, "The target of the unit.")
.def_property_readonly("has_target", &ReplayUnit::hasTarget, "Whether the unit has a target.")
.def_property_readonly("max_hit_points", &ReplayUnit::getMaxHitPoints, "The maximum hit points of the unit.")
.def_property_readonly("progress", &ReplayUnit::getProgress, "Returns the progress of currently used ability (-1 if not using ability).")
.def_property_readonly("current_ability_id", &ReplayUnit::getCurrentAbilityID, "The AbilityID of currently used ability.")
.def_property_readonly("facing", &ReplayUnit::getFacing, "Returns the direction the unit is facing.")
.def_property_readonly("radius", &ReplayUnit::getRadius, "The radius of the unit.")
.def_property_readonly("is_carrying_minerals", &ReplayUnit::isCarryingMinerals, "Whether the unit is carrying minerals.")
.def("__hash__", [](const ReplayUnit & unit) { return std::hash<const sc2::Unit *>{}(unit.getUnitPtr()); })
.def(py::self == py::self)
.def("__repr__", [](const ReplayUnit & unit) { return "<Unit of type: '" + unit.getType().getName() + "'>"; })
.doc() = R"(
The ReplayUnit class is used to represent a unit in a replay of a game.
A ReplayUnit is a :class:`commandcenter.Unit` with some limitations.
It provides various properties and methods to access information about the unit, such as its ID, type, position, hit points, energy, and more.
It is possible to use ReplayUnit as keys in a dictionary, which might be helpful for bookkeeping.
)";
}
This diff is collapsed.
......@@ -6,24 +6,37 @@ void define_tech_tree(py::module & m)
{
py::class_<TypeData>(m, "TypeData")
.def_readonly("race", &TypeData::race)
.def_readonly("mineral_cost", &TypeData::mineralCost, "mineral cost of the item")
.def_readonly("gas_cost", &TypeData::gasCost, "gas cost of the item")
.def_readonly("supply_cost", &TypeData::supplyCost, "supply cost of the item")
.def_readonly("build_time", &TypeData::buildTime, "build time of the item")
.def_readonly("is_unit", &TypeData::isUnit)
.def_readonly("is_building", &TypeData::isBuilding)
.def_readonly("is_worker", &TypeData::isWorker)
.def_readonly("is_refinery", &TypeData::isRefinery)
.def_readonly("is_supply_provider", &TypeData::isSupplyProvider)
.def_readonly("is_resource_depot", &TypeData::isResourceDepot)
.def_readonly("is_addon", &TypeData::isAddon)
.def_readonly("build_ability", &TypeData::buildAbility, "the ability that creates this item")
.def_readonly("warp_ability", &TypeData::warpAbility, "the ability that creates this item via warp-in")
.def_readonly("what_builds", &TypeData::whatBuilds, "any of these units can build the item")
.def_readonly("required_units", &TypeData::requiredUnits, "owning ONE of these is required to make")
.def_readonly("required_upgrades", &TypeData::requiredUpgrades, "having ALL of these is required to make");
.def_readonly("mineral_cost", &TypeData::mineralCost, "The mineral cost of the item.")
.def_readonly("gas_cost", &TypeData::gasCost, "The gas cost of the item.")
.def_readonly("supply_cost", &TypeData::supplyCost, "The supply cost of the item.")
.def_readonly("build_time", &TypeData::buildTime, "The build time of the item in seconds (should be 32 game updates per tick).")
.def_readonly("is_unit", &TypeData::isUnit, "Whether the item is a unit.")
.def_readonly("is_building", &TypeData::isBuilding, "Whether the item is a building.")
.def_readonly("is_worker", &TypeData::isWorker, "Whether the item is a worker.")
.def_readonly("is_refinery", &TypeData::isRefinery, "Whether the item is a refinery.")
.def_readonly("is_supply_provider", &TypeData::isSupplyProvider, "Whether the item is a supply provider.")
.def_readonly("is_resource_depot", &TypeData::isResourceDepot, "Whether the item is a resource depot.")
.def_readonly("is_addon", &TypeData::isAddon, "Whether the item is an add-on.")
.def_readonly("build_ability", &TypeData::buildAbility, "The ability that creates this item.")
.def_readonly("warp_ability", &TypeData::warpAbility, "The ability that creates this item via warp-in.")
.def_readonly("what_builds", &TypeData::whatBuilds, "The units that can build the item.")
.def_readonly("required_units", &TypeData::requiredUnits, "Owning ONE of these (List[UnitType]) is required to make the item.")
.def_readonly("required_upgrades", &TypeData::requiredUpgrades, "Having ALL of these (List[UPGRADE_ID]) is required to make the item.")
.def_readonly("required_addons", &TypeData::requiredAddons, "A unit of this type (List[UnitType]) must be present next to the producer.")
.doc() = R"(
Allows you to get information about a unit type or upgrade. This is a read-only class.
)";
py::class_<TechTree>(m, "TechTree")
.def("get_data", py::overload_cast<const UnitType &>(&TechTree::getData, py::const_))
.def("get_data", py::overload_cast<const CCUpgrade &>(&TechTree::getData, py::const_));
}
\ No newline at end of file
.def("get_data", py::overload_cast<const CCUpgrade &>(&TechTree::getData, py::const_), "Argument is either an instance of the class :class:`commandcenter.UnitType` or an instance of the class :class:`commandcenter.CCUpgrade`, depending on what information is wanted.")
.def("suppress_warnings", &TechTree::setSuppressWarnings, "Suppress type and upgrade warnings." ,"b"_a)
.doc() = R"(
This class contains all information about units and what is required to
build a certain unit and what builds it. It only has one method, which is
used to look-up unit types properties.
Instead of using TechTree, it's possible to use the functions in UnitType for
structure, etc. In IDABot there is functions for getting data about upgrades.
)";
}
......@@ -5,39 +5,80 @@ namespace py = pybind11;
void define_unit(py::module & m)
{
py::class_<Unit>(m, "Unit")
.def_property_readonly("unit_type", &Unit::getType)
.def_property_readonly("position", &Unit::getPosition)
.def_property_readonly("tile_position", &Unit::getTilePosition)
.def_property_readonly("hit_points", &Unit::getHitPoints)
.def_property_readonly("shields", &Unit::getShields)
.def_property_readonly("energy", &Unit::getEnergy)
.def_property_readonly("player", &Unit::getPlayer)
.def_property_readonly("id", &Unit::getID)
.def_property_readonly("build_percentage", &Unit::getBuildPercentage)
.def_property_readonly("weapon_cooldown", &Unit::getWeaponCooldown)
.def_property_readonly("completed", &Unit::isCompleted)
.def_property_readonly("being_constructed", &Unit::isBeingConstructed)
.def_property_readonly("cloaked", &Unit::isCloaked)
.def_property_readonly("flying", &Unit::isFlying)
.def_property_readonly("alive", &Unit::isAlive)
.def_property_readonly("powered", &Unit::isPowered)
.def_property_readonly("idle", &Unit::isIdle)
.def_property_readonly("burrowed", &Unit::isBurrowed)
.def_property_readonly("valid", &Unit::isValid)
.def_property_readonly("training", &Unit::isTraining)
.def_property_readonly("constructing", &Unit::isConstructing)
.def("stop", &Unit::stop)
.def("attack_unit", &Unit::attackUnit)
.def("attack_move", &Unit::attackMove)
.def_property_readonly("unit_type", &Unit::getType, "The :class:`commandcenter.UnitType` of the unit.")
.def_property_readonly("position", &Unit::getPosition, "The :class:`commandcenter.Point2D` of the unit.")
.def_property_readonly("tile_position", &Unit::getTilePosition, "The :class:`commandcenter.Point2DI` of the unit.")
.def_property_readonly("hit_points", &Unit::getHitPoints, "Health of the unit.")
.def_property_readonly("shields", &Unit::getShields, "Shield of the unit.")
.def_property_readonly("energy", &Unit::getEnergy, "Energy of the unit.")
.def_property_readonly("player", &Unit::getPlayer, "Returns the constant corresponding to player which this unit belongs to. See :ref:`playerconstants` for more information.")
.def_property_readonly("id", &Unit::getID, "Returns an int to identify the unit. This value does not stay the same for units you lose vision of, so maybe not best idea to use as a way of keeping track of them.")
.def_property_readonly("build_percentage", &Unit::getBuildPercentage, "Gives progress under construction. Range [0.0, 1.0]. 1.0 == finished.")
.def_property_readonly("weapon_cooldown", &Unit::getWeaponCooldown, "Time remaining for a weapon on cooldown.")
.def_property_readonly("is_completed", &Unit::isCompleted, "Returns build_progress >= 1.")
.def_property_readonly("is_being_constructed", &Unit::isBeingConstructed, "Returns (build_progress > 0 and not is_completed).")
.def_property_readonly("is_cloaked", &Unit::isCloaked, "If the unit is cloaked.")
.def_property_readonly("is_flying", &Unit::isFlying, "If the unit is flying.")
.def_property_readonly("buffs", &Unit::buffs, "Returns a list buffs on this unit. Only valid for this player's units.")
.def_property_readonly("is_alive", &Unit::isAlive, "Whether the unit is alive or not.")
.def_property_readonly("is_powered", &Unit::isPowered, "Whether the unit is powered by a pylon.")
.def_property_readonly("is_idle", &Unit::isIdle, "Whether the unit has any orders.")
.def_property_readonly("is_burrowed", &Unit::isBurrowed, "Whether the unit is burrowed.")
.def_property_readonly("is_valid", &Unit::isValid, "Whether the unit is valid")
.def_property_readonly("is_training", &Unit::isTraining, "Returns True if the unit is a building and is training another unit.")
.def_property_readonly("is_blip", &Unit::isBlip, "Returns true if unit is a 'blip' - a ping on the map.")
.def_property_readonly("target", &Unit::getTarget, "Returns target unit if one exists, otherwise return itself.")
.def_property_readonly("has_target", &Unit::hasTarget, "Returns True if the target has a valid target and False otherwise.")
.def_property_readonly("max_hit_points", &Unit::getMaxHitPoints, "Returns the max health.")
.def_property_readonly("progress", &Unit::getProgress, "Returns the progress of currently used ability (-1 if not using ability).")
.def_property_readonly("progression_list", &Unit::getAllProgress, "Returns a list containing the progression on all the processes in order. Empty list if no process exists.")
.def_property_readonly("current_ability_id", &Unit::getCurrentAbilityID, "The AbilityID of currently used ability.")
.def_property_readonly("facing", &Unit::getFacing, "Direction the unit faces in radians (1 radian == 57.296 degrees).")
.def_property_readonly("radius", &Unit::getRadius, "Returns the radius of the unit.")
.def_property_readonly("is_carrying_minerals", &Unit::isCarryingMinerals, "Returns if this unit is currently holding minerals.")
.def_property_readonly("is_carrying_gas", &Unit::isCarryingGas, "Returns if this unit is currently holding gas.")
.def_property_readonly("gas_left_in_refinery", &Unit::gasLeftInGeyser, "Amount of vespene left in the the refinery.")
.def_property_readonly("minerals_left_in_mineralfield", &Unit::mineralsLeftInMineralfield, "Amount of minerals if the unit is a mineral field.")
.def_property_readonly("owner", &Unit::getOwner, "Which player owns a unit. Not same as player; 'player' originates from IDA implementation whilst 'owner' originates from StarCraft II.")
.def_property_readonly("max_shields", &Unit::maxShields, "Max shield of the unit.")
.def_property_readonly("max_energy", &Unit::maxEnergy, "Max energy of the unit.")
.def("hold_position", &Unit::holdPosition, "Unit will hold its position.")
.def("patrol", py::overload_cast<const CCPosition &>(&Unit::patrol, py::const_), "Unit will patrol back and forth from its current location to the given Point2D.")
.def("stop_dance", &Unit::stopDance, "Unit will Stop and dance.")
.def("stop", &Unit::stop, "Unit will stop.")
.def("attack_unit", &Unit::attackUnit, "Unit will attack the provided unit.")
.def("attack_move", &Unit::attackMove, "Moves to provided Point2D location. If an enemy is seen on the way it will try to attack it. Will chase after the enemy as long as it is visible.")
.def("ability", py::overload_cast<sc2::AbilityID>(&Unit::ability, py::const_))
.def("ability", py::overload_cast<sc2::AbilityID, const CCPosition &>(&Unit::ability, py::const_))
.def("ability", py::overload_cast<sc2::AbilityID, const Unit &>(&Unit::ability, py::const_), "Call an ability directly, different abilities has different targets. Some target the unit itself (no argument), target a point (Point2D as argument) and some target a Unit (instance of Unit as argument).")
.def("move", py::overload_cast<const CCPosition &>(&Unit::move, py::const_))
.def("move", py::overload_cast<const CCTilePosition &>(&Unit::move, py::const_))
.def("right_click", &Unit::rightClick)
.def("repair", &Unit::repair)
.def("build", &Unit::build)
.def("build_target", &Unit::buildTarget)
.def("train", &Unit::train)
.def("morph", &Unit::morph)
.def("__hash__", [](const Unit & unit) { return std::hash<CCUnitID>{}(unit.getID()); })
.def("move", py::overload_cast<const CCTilePosition &>(&Unit::move, py::const_), "Move the unit to the given point, the point being an instance of either :class:`commandcenter.Point2D` or :class:`commandcenter.Point2DI`.")
.def("right_click", &Unit::rightClick, "Same as right-clicking in the game, for example making workers mine minerals.")
.def("repair", &Unit::repair, "Right-clicks on the provided unit in order to repair it.")
.def("build", &Unit::build, "Build unit of type building_type at given position.", "building_type"_a, "position"_a)
.def("build_target", &Unit::buildTarget, "Build building_type on top of target Unit, useful for building refineries.", "building_type"_a, "target"_a)
.def("train", &Unit::train, "Train unit of type.", "unit_type"_a)
.def("morph", &Unit::morph, "Morph into type of unit_type.", "unit_type"_a)
.def("research", &Unit::research, "Research the given :class:`commandcenter.UPGRADE_ID`.", "upgrade_id"_a)
.def("is_constructing", &Unit::isConstructing, "unit_type"_a, "Returns true if the unit is currently constructing another unit of type `unit_type`. Note that `unit_type` needs to be an instance of :class:`commandcenter.UnitType`.")
.def("__hash__", [](const Unit & unit) { return std::hash<const sc2::Unit *>{}(unit.getUnitPtr()); })
.def(py::self == py::self)
.def("__repr__", [](const Unit & unit) { return "<Unit of type: '" + unit.getType().getName() + "'>"; });
.def("__repr__", [](const Unit & unit) { return "<Unit of type: '" + unit.getType().getName() + "'>"; })
.doc() = R"(
An instance of the class Unit represents one unit in the game. The units are
not limited to moveable units, but every entity which is not part of the
background is a unit. For example, the minerals and geysers are units as
well as all buildings.
For all possible types of units see the enum :class:`commandcenter.UNIT_TYPEID`.
Some types of objects are almost the same, for example there are many types
of mineral deposits, but all of them are mineable. This is one of the
motivations behind the :class:`commandcenter.UnitType` which aims to to make the
list of types more manageable. The UnitType can be accessed by the
:any:`Unit.unit_type` property.
It is possible to use Unit as keys in a dictionary, which might be helpful
for bookkeeping.
)" ;
}
......@@ -5,40 +5,60 @@ namespace py = pybind11;
void define_unittype(py::module & m)
{
py::class_<UnitType>(m, "UnitType")
.def(py::init<const sc2::UnitTypeID &, IDABot &>())
.def("is", &UnitType::is, "Check if UnitType is UnitTypeID")
.def(py::self == py::self)
.def_property_readonly("name", &UnitType::getName)
.def_property_readonly("race", &UnitType::getRace)
.def_property_readonly("is_valid", &UnitType::isValid)
.def_property_readonly("is_building", &UnitType::isBuilding)
.def_property_readonly("is_combat_unit", &UnitType::isCombatUnit)
.def_property_readonly("is_supply_provider", &UnitType::isSupplyProvider)
.def_property_readonly("is_resource_depot", &UnitType::isResourceDepot)
.def_property_readonly("is_refinery", &UnitType::isRefinery)
.def_property_readonly("is_detector", &UnitType::isDetector)
.def_property_readonly("is_geyser", &UnitType::isGeyser)
.def_property_readonly("is_mineral", &UnitType::isMineral)
.def_property_readonly("is_worker", &UnitType::isWorker)
.def_property_readonly("is_morphed_building", &UnitType::isMorphedBuilding)
.def(py::init([](const sc2::UnitTypeID & type, IDABot & bot) {
return UnitType(type, bot, bot);
}))
.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 string.")
.def_property_readonly("race", &UnitType::getRace, "The race the unit belongs to.")
.def_property_readonly("movement_speed", &UnitType::getMovementSpeed, "Movement speed of unit type.")
.def_property_readonly("sight_range", &UnitType::getSightRange, "Range the unit reveals vision.")
.def_property_readonly("required_structure", &UnitType::getRequiredStructure, "Structure required to build this unit. (Or any with the same tech_alias.)")
.def_property_readonly("is_valid", &UnitType::isValid, "Its a valid unit type.")
.def_property_readonly("is_building", &UnitType::isBuilding, "Is this unit type a building or not.")
.def_property_readonly("is_combat_unit", &UnitType::isCombatUnit, "The unit is not any of the following, worker, supply provider, building, larva, egg.")
.def_property_readonly("is_supply_provider", &UnitType::isSupplyProvider, "The unit provides supply.")
.def_property_readonly("is_resource_depot", &UnitType::isResourceDepot, "The unit is one of the following, hatchery, lair, hive, commandcenter, orbialtcommand, planetaryfortress, nexus.")
.def_property_readonly("is_refinery", &UnitType::isRefinery, "The unit is one of the following (depending on race), refinery, assimilator, extractor.")
.def_property_readonly("is_detector", &UnitType::isDetector, "Is this a unit type which is a detector unit.")
.def_property_readonly("is_geyser", &UnitType::isGeyser, "Is the unit type a geyser.")
.def_property_readonly("is_mineral", &UnitType::isMineral, "Is the unit type a mineralfield.")
.def_property_readonly("is_worker", &UnitType::isWorker, "Is the unit type a unit which is a worker.")
.def_property_readonly("is_morphed_building", &UnitType::isMorphedBuilding, "Has this building been morphed.")
// Not implemented in CommandCenter
//.def_property_readonly("can_attack", &UnitType::canAttack)
//.def_property_readonly("can_Move", &UnitType::canMove)
.def_property_readonly("is_addon", &UnitType::isAddon)
.def_property_readonly("attack_range", &UnitType::getAttackRange)
.def_property_readonly("tile_width", &UnitType::tileWidth)
.def_property_readonly("tile_height", &UnitType::tileHeight)
.def_property_readonly("supply_provided", &UnitType::supplyProvided)
.def_property_readonly("supply_required", &UnitType::supplyRequired)
.def_property_readonly("mineral_price", &UnitType::mineralPrice)
.def_property_readonly("gas_price", &UnitType::gasPrice)
.def_property_readonly("is_overlord", &UnitType::isOverlord)
.def_property_readonly("is_larva", &UnitType::isLarva)
.def_property_readonly("is_egg", &UnitType::isEgg)
.def_property_readonly("is_queen", &UnitType::isQueen)
.def_property_readonly("is_tank", &UnitType::isTank)
.def("__repr__", [](const UnitType & unit_type) { return "<UnitType: '" + unit_type.getName() + "'>"; });
.def_property_readonly("is_addon", &UnitType::isAddon, "Is this unit type a add-on.")
.def_property_readonly("can_attack_ground", &UnitType::canAttackGound, "True if this unit can attack ground units.")
.def_property_readonly("can_attack_air", &UnitType::canAttackAir, "True if this unit can attack air units.")
.def_property_readonly("attack_range", &UnitType::getAttackRange, "Returns the attack range of the unit type.")
.def_property_readonly("attack_damage", &UnitType::getAttackDamage, "Returns the base attack damage of the unit type.")
.def_property_readonly("tile_width", &UnitType::tileWidth, "Returns the width of the unit type.")
.def_property_readonly("tile_height", &UnitType::tileHeight, "Returns the height of the unit type.")
.def_property_readonly("supply_provided", &UnitType::supplyProvided, "Amount of supply provided by the unit type.")
.def_property_readonly("supply_required", &UnitType::supplyRequired, "Amount of supply required for the unit type.")
.def_property_readonly("mineral_price", &UnitType::mineralPrice, "Mineral price of the unit type.")
.def_property_readonly("gas_price", &UnitType::gasPrice, "Gas price of the unit type.")
.def_property_readonly("is_overlord", &UnitType::isOverlord, "Is this a ZERG_OVERLORD.")
.def_property_readonly("is_larva", &UnitType::isLarva, "is this a ZERG_LARVA.")
.def_property_readonly("is_egg", &UnitType::isEgg, "is this a ZERG_EGG.")
.def_property_readonly("is_queen", &UnitType::isQueen, "is this a ZERG_QUEEN.")
.def_property_readonly("is_tank", &UnitType::isTank, "is this a TERRAN_SIEGETANK or TERRAN_SIEGETANKSIEGED.")
.def_property_readonly("get_equivalent_units", &UnitType::getEquivalentUnits, "Units this is equivalent to in terms of satisfying tech requirements.")
.def_property_readonly("required_attached", &UnitType::requiredAttached, "Whether tech_requirement is an add-on.")
.def_property_readonly("build_time", &UnitType::getBuildTime, "How long the unit takes to build.")
.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:`commandcenter.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
//.def("whatBuilds", &UnitType::whatBuilds);
}
\ No newline at end of file
}
......@@ -7,7 +7,7 @@ void define_util(py::module & mod)
py::module m = mod.def_submodule("util");
/*
TODO:
TODO: Implement and document these functions
Maybe should be included in Point instead?
CCTilePosition GetTilePosition(const CCPosition & pos);
CCPosition GetPosition(const CCTilePosition & tile);
......@@ -22,4 +22,7 @@ void define_util(py::module & mod)
m.def("dist", py::overload_cast<const Unit &, const Unit &>(&Util::Dist));
m.def("dist", py::overload_cast<const Unit &, const CCPosition &>(&Util::Dist));
m.def("dist", py::overload_cast<const CCPosition &, const CCPosition &>(&Util::Dist));
m.doc() = R"(
The util module provides utility functions for the library.
)";
}
\ No newline at end of file
This diff is collapsed.
......@@ -3,20 +3,38 @@
#include <pybind11/pybind11.h>
#include <sc2api/sc2_api.h>
#include "../src/IDABot.h"
#include "../src/IDAReplayObserver.h"
#include <iostream>
#include <pybind11/stl.h> /* Automatic conversion from std::vector to Python lists */
#include <pybind11/operators.h> /* Convenient operator support */
// Lets us use "x"_a instead of py::arg("x"), great.
using namespace pybind11::literals;
// Wrapper class since the initialization uses pure argc/argv and these cannot be wrapped into Python correctly
class Coordinator : public sc2::Coordinator
{
public:
// TODO: We might not always want default value when we run on Linux
Coordinator() : sc2::Coordinator()
{
char *argv[] = { "executable", NULL};
int argc = sizeof(argv) / sizeof(char*) - 1;
LoadSettings(argc, argv);
std::vector<std::string> arguments = { "pycommandcenter" };
CustomLoadSettings(arguments);
}
Coordinator(std::string path) : sc2::Coordinator()
{
std::vector<std::string> arguments = { "pycommandcenter", "-e", path };
CustomLoadSettings(arguments);
}
void CustomLoadSettings(std::vector<std::string> arguments)
{
std::vector<char*> argv;
for (const auto& arg : arguments)
argv.push_back((char*)arg.data());
argv.push_back(nullptr);
LoadSettings(argv.size() - 1, argv.data());
}
};
......@@ -26,37 +44,99 @@ public:
using IDABot::IDABot;
void OnGameStart() override
{
PYBIND11_OVERLOAD(
PYBIND11_OVERLOAD_NAME(
void,
IDABot,
"on_game_start",
OnGameStart
);
}
void OnStep() override
{
PYBIND11_OVERLOAD(
PYBIND11_OVERLOAD_NAME(
void,
IDABot,
"on_step",
OnStep
);
}
void OnStep_UpdateIDABot() override
{
PYBIND11_OVERLOAD(
void,
IDABot,
OnStep_UpdateIDABot
);
}
void OnGameEnd() override
{
PYBIND11_OVERLOAD_NAME(
void,
IDABot,
"on_game_end",
OnGameEnd
);
}
};
class PyReplayObserver : public IDAReplayObserver
{
public:
using IDAReplayObserver::IDAReplayObserver;
void OnGameStart() override
{
PYBIND11_OVERLOAD_NAME(
void,
IDAReplayObserver,
"on_game_start",
OnGameStart
);
}
void OnStep() override
{
PYBIND11_OVERLOAD_NAME(
void,
IDAReplayObserver,
"on_step",
OnStep
);
}
void OnGameEnd() override
{
PYBIND11_OVERLOAD_NAME(
void,
IDAReplayObserver,
"on_game_end",
OnGameEnd
);
}
void OnReplayUnitDestroyed(const ReplayUnit *unit) override
{
PYBIND11_OVERLOAD_NAME(
void,
IDAReplayObserver,
"on_unit_destroyed",
OnReplayUnitDestroyed,
unit
);
}
void OnReplayUnitCreated(const ReplayUnit *unit) override
{
PYBIND11_OVERLOAD_NAME(
void,
IDAReplayObserver,
"on_unit_created",
OnReplayUnitCreated,
unit
);
}
};
// The functions below are all defined in different .cpp files, in order
// to keep compilation snappy
void define_typeenums(pybind11::module & m);
void define_unit(pybind11::module & m);
void define_replay_unit(pybind11::module & m);
void define_unittype(pybind11::module &m);
void define_util(pybind11::module &m);
void define_point(pybind11::module &m);
void define_base_location(pybind11::module & m);
void define_tech_tree(pybind11::module & m);
void define_map_tools(pybind11::module & m);
\ No newline at end of file
void define_color(pybind11::module & m);
void define_map_tools(pybind11::module & m);
void define_building_placer(pybind11::module & m);
alabaster==0.7.16
Babel==2.15.0
certifi==2024.6.2
charset-normalizer==3.3.2
docutils==0.21.2
idna==3.7
imagesize==1.4.1
Jinja2==3.1.4
MarkupSafe==2.1.5
mypy==1.10.0
mypy-extensions==1.0.0
packaging==24.1
Pygments==2.18.0
requests==2.32.3
snowballstemmer==2.2.0
Sphinx==7.3.7
sphinx_design==0.6.0
sphinxcontrib-applehelp==1.0.8
sphinxcontrib-devhelp==1.0.6
sphinxcontrib-htmlhelp==2.0.5
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.7
sphinxcontrib-serializinghtml==1.1.10
typing_extensions==4.12.2
urllib3==2.2.2
mkdir build
cd build
"C:\Program Files\CMake\bin\cmake" .. -G "Visual Studio 17 2022" -DPYTHON_EXECUTABLE:FILEPATH="C:/Program Files/Python312/python.exe"
"C:\Program Files\CMake\bin\cmake.exe" --build . --config Release --verbose
#!/bin/sh
mkdir build
cd build
"C:\Program Files\CMake\bin\cmake.exe" -G "Visual Studio 17 2022" -DPYTHON_EXECUTABLE:FILEPATH="C:/Program Files/Python312/python.exe" ..
"C:\Program Files\CMake\bin\cmake.exe" --build . --config Release --verbose
#!/bin/sh
mkdir build37
cd build37
"C:\Program Files\CMake\bin\cmake" .. -G "Visual Studio 15 Win64" -DPYTHON_EXECUTABLE:FILEPATH="C:/Program Files/Python37/python.exe"
#!/bin/sh
mkdir build38
cd build38
"C:\Program Files\CMake\bin\cmake" .. -G "Visual Studio 15 Win64" -DPYTHON_EXECUTABLE:FILEPATH="C:/Program Files/Python38/python.exe"
#!/bin/sh
mkdir build
cd build
"C:\Program Files\CMake\bin\cmake" .. -G "Visual Studio 15 Win64" -DPYTHON_EXECUTABLE:FILEPATH="C:/Program Files/Python39/python.exe"
"""
This file inserts the docs of each class and method into the pyi file for autocompletetion.
Before running make sure you have created the pyi according to the README.
You might need to change the pyi_path.
Then simply run this file.
After running all the docs should have been inserted.
Now you're done and can enjoy working autocompletion of this library.
"""
import sys
import inspect
import re
sys.path.append('build/python-api-src') #Unix
sys.path.append('build/python-api-src/Release') #Windows
import commandcenter
def update_pyi_with_docstrings(pyi_path):
"""
Update the .pyi file with docstrings from the corresponding library.
Args:
pyi_path (str): The path to the .pyi file.
Returns:
None
"""
with open(pyi_path, 'r') as file:
lines = file.readlines()
new_lines = []
known_def = {} #To keep track of docs to class methods
for i in range(len(lines)):
line = lines[i]
if line.strip().startswith('class '):
new_lines.append(line)
if not line.strip().endswith("ID:"):
class_name = re.split(r'[\(: ]', line)[1]
docstring = inspect.getdoc(getattr(commandcenter, class_name))
new_lines.append(f' """{docstring}"""\n')
for func in dir(getattr(commandcenter, class_name)):
if func.startswith("__"):
continue
func_docs = inspect.getdoc(getattr(getattr(commandcenter, class_name), func))
known_def[func] = func_docs
else:
class_name = re.split(r'[\(: ]', line)[1]
docstring = inspect.getdoc(getattr(commandcenter, class_name))
docstring = docstring.split("Members:")[0]
new_lines.append(f' """{docstring}"""\n')
elif line.strip().startswith('def '):
new_lines.append(line[:-4])
func_name = re.split(r'[\(: ]', line.strip())[1]
if func_name.startswith("__"):
new_lines.append(f' {line[-4:]}')
continue
elif func_name in known_def:
new_lines.append(f'\n """{known_def[func_name]}"""\n')
new_lines.append(f' {line[-4:]}')
continue
else:
try:
func_doc = inspect.getdoc(getattr(commandcenter, func_name))
if func_name == "create_computer" or func_name == "create_participants": #Special case with indentation
new_lines.append(f'\n """{func_doc}"""\n')
else:
new_lines.append(f'\n """{func_doc}"""\n')
new_lines.append(f' {line[-4:]}')
continue
except AttributeError:
new_lines.append(f'\n """not found"""\n')
new_lines.append(f' {line[-4:]}')
continue
else:
new_lines.append(line)
# Write the updated content back to the .pyi file
with open(pyi_path, 'w') as file:
file.writelines(new_lines)
# Path to the generated .pyi file
pyi_file_path = 'build/python-api-src/commandcenter.pyi' #Unix
#pyi_file_path = 'build/python-api-src/Release/commandcenter.pyi' #Windows
update_pyi_with_docstrings(pyi_file_path)
......@@ -131,6 +131,14 @@ void BaseLocation::setPlayerOccupying(CCPlayer player, bool occupying)
}
}
void BaseLocation::setMinerals(std::vector<Unit> & mineralFields) {
m_minerals = mineralFields;
}
void BaseLocation::setGeysers(std::vector<Unit> & geysers) {
m_geysers = geysers;
}
bool BaseLocation::isInResourceBox(int tileX, int tileY) const
{
CCPositionType px = Util::TileToPosition((float)tileX);
......@@ -202,7 +210,9 @@ void BaseLocation::draw()
{
CCPositionType radius = Util::TileToPosition(1.0f);
m_bot.Map().drawCircle(m_centerOfResources, radius, CCColor(255, 255, 0));
// Take first mineral for the Z-pos.
sc2::Point3D center_pos = sc2::Point3D(m_centerOfResources.x, m_centerOfResources.y, m_minerals[0].getUnitPtr()->pos.z);
m_bot.Map().drawResourceSphere(center_pos, radius, CCColor(255, 255, 0));
std::stringstream ss;
ss << "BaseLocation: " << m_baseID << "\n";
......@@ -237,14 +247,14 @@ void BaseLocation::draw()
//m_bot.Map().drawLine(m_left, y, m_right, y, CCColor(160, 160, 160));
}
for (auto & mineralPos : m_mineralPositions)
for (auto & mineral : m_minerals)
{
m_bot.Map().drawCircle(mineralPos, radius, CCColor(0, 128, 128));
m_bot.Map().drawResourceSphere(mineral.getUnitPtr()->pos, radius, CCColor(0, 128, 128));
}
for (auto & geyserPos : m_geyserPositions)
for (auto & geyser : m_geysers)
{
m_bot.Map().drawCircle(geyserPos, radius, CCColor(0, 255, 0));
m_bot.Map().drawResourceSphere(geyser.getUnitPtr()->pos, radius, CCColor(0, 255, 0));
}
if (m_isStartLocation)
......
......@@ -50,6 +50,8 @@ public:
bool isInResourceBox(int x, int y) const;
void setPlayerOccupying(CCPlayer player, bool occupying);
void setMinerals(std::vector<Unit> & mineralFields);
void setGeysers(std::vector<Unit> & geysers);
const std::vector<CCTilePosition> & getClosestTiles() const;
......
......@@ -138,6 +138,12 @@ void BaseLocationManager::onFrame()
{
drawBaseLocations();
// make sure that mineralfields and geysers are up to date in the different bases
for (BaseLocation & baselocation : m_baseLocationData) {
updateMinerals(baselocation);
updateGeysers(baselocation);
}
// reset the player occupation information for each location
for (auto & baseLocation : m_baseLocationData)
{
......@@ -247,6 +253,32 @@ void BaseLocationManager::onFrame()
}
void BaseLocationManager::updateMinerals(BaseLocation & baseToUpdate) {
std::vector<Unit> newMinerals;
for (Unit oldMineral : baseToUpdate.getMinerals()) {
for (auto & mineral : m_bot.GetAllUnits()) {
if (oldMineral.getPosition() == mineral.getPosition() && mineral.getType().isMineral()) {
newMinerals.push_back(mineral);
break;
}
}
}
baseToUpdate.setMinerals(newMinerals);
}
void BaseLocationManager::updateGeysers(BaseLocation & baseToUpdate) {
std::vector<Unit> newGeysers;
for (Unit oldGeyser : baseToUpdate.getGeysers()) {
for (auto & geyser : m_bot.GetAllUnits()) {
if (oldGeyser.getPosition() == geyser.getPosition() && geyser.getType().isGeyser()) {
newGeysers.push_back(geyser);
break;
}
}
}
baseToUpdate.setGeysers(newGeysers);
}
BaseLocation * BaseLocationManager::getBaseLocation(const CCPosition & pos) const
{
if (!m_bot.Map().isValidPosition(pos)) { return nullptr; }
......@@ -269,7 +301,7 @@ void BaseLocationManager::drawBaseLocations()
const BaseLocation * next_expansion = getNextExpansion(Players::Self);
CCTilePosition nextExpansionPosition = next_expansion->getDepotPosition();
m_bot.Map().drawCircle(Util::GetPosition(nextExpansionPosition), 1, CCColor(255, 0, 255));
m_bot.Map().drawResourceSphere(sc2::Point3D(Util::GetPosition(nextExpansionPosition).x, Util::GetPosition(nextExpansionPosition).y, next_expansion->getMinerals()[0].getUnitPtr()->pos.z), 1, CCColor(255, 0, 255));
m_bot.Map().drawText(Util::GetPosition(nextExpansionPosition), "Next Expansion Location", CCColor(255, 0, 255));
}
......@@ -293,7 +325,6 @@ const std::set<const BaseLocation *> & BaseLocationManager::getOccupiedBaseLocat
return m_occupiedBaseLocations.at(player);
}
const BaseLocation * BaseLocationManager::getNextExpansion(int player) const
{
const BaseLocation * homeBase = getPlayerStartingBaseLocation(player);
......
......@@ -32,4 +32,9 @@ public:
const BaseLocation * getNextExpansion(int player) const;
private:
void updateMinerals(BaseLocation & baseToUpdate);
void updateGeysers(BaseLocation & baseToUpdate);
};
......@@ -14,6 +14,23 @@ void BuildingPlacer::onStart()
m_reserveMap = std::vector< std::vector<bool> >(m_bot.Map().width(), std::vector<bool>(m_bot.Map().height(), false));
}
void BuildingPlacer::updateReserved(const std::vector<Unit> & units)
{
freeAllTiles();
for (Unit unit : units) {
reserveTiles(unit.getTilePosition().x, unit.getTilePosition().y, unit.getType().tileWidth(), unit.getType().tileHeight());
}
}
void BuildingPlacer::freeAllTiles() {
for (int x = 0; x < m_reserveMap.size(); x++) {
for (int y = 0; y < m_reserveMap[x].size(); y++) {
m_reserveMap[x][y] = false;
}
}
}
bool BuildingPlacer::isInResourceBox(int tileX, int tileY) const
{
return m_bot.Bases().getPlayerStartingBaseLocation(Players::Self)->isInResourceBox(tileX, tileY);
......@@ -27,12 +44,17 @@ bool BuildingPlacer::canBuildHere(int bx, int by, const UnitType & type) const
return false;
}
// check the reserve map
for (int x = bx; x < bx + type.tileWidth(); x++)
int width = type.tileWidth();
int height = type.tileHeight();
int xdelta = (int)std::ceil((width - 1.0) / 2);
int ydelta = (int)std::ceil((height - 1.0) / 2);
// check the reserve map, that it is a valid tile and not a wall
for (int x = bx - xdelta; x < bx + width - xdelta; x++)
{
for (int y = by; y < by + type.tileHeight(); y++)
for (int y = by - ydelta; y < by + height - ydelta; y++)
{
if (!m_bot.Map().isValidTile(x, y) || m_reserveMap[x][y])
if (!m_bot.Map().isValidTile(x, y) || isReserved(x, y) || !m_bot.Map().isWalkable(x, y))
{
return false;
}
......@@ -48,6 +70,59 @@ bool BuildingPlacer::canBuildHere(int bx, int by, const UnitType & type) const
return true;
}
bool BuildingPlacer::canBuildHereWithSize(int bx, int by, int width, int height)
{
if (isInResourceBox(bx, by))
{
return false;
}
int xdelta = (int)std::ceil((width - 1.0) / 2);
int ydelta = (int)std::ceil((height - 1.0) / 2);
// check the reserve map, that it is a valid tile and not a wall
for (int x = bx - xdelta; x < bx + width - xdelta; x++)
{
for (int y = by - ydelta; y < by + height - ydelta; y++)
{
if (!m_bot.Map().isValidTile(x, y) || isReserved(x, y) || !m_bot.Map().isWalkable(x, y))
{
return false;
}
}
}
// Check so it doesn't overlap with a baselocation
// dimensions of the proposed location
int tx1 = bx - xdelta;
int ty1 = by - ydelta;
int tx2 = tx1 + width - xdelta;
int ty2 = ty1 + height - ydelta;
// for each base location
for (const BaseLocation * base : m_bot.Bases().getBaseLocations())
{
// dimensions of the base location
int bx1 = (int)base->getDepotPosition().x;
int by1 = (int)base->getDepotPosition().y;
int bx2 = bx1 + Util::GetTownHall(m_bot.GetPlayerRace(Players::Self), m_bot).tileWidth();
int by2 = by1 + Util::GetTownHall(m_bot.GetPlayerRace(Players::Self), m_bot).tileHeight();
// conditions for non-overlap are easy
bool noOverlap = (tx2 < bx1) || (tx1 > bx2) || (ty2 < by1) || (ty1 > by2);
// If there is overlap, return false
if (!noOverlap)
{
return false;
}
}
// otherwise there is no overlap
return true;
}
//returns true if we can build this type of unit here with the specified amount of space.
bool BuildingPlacer::canBuildHereWithSpace(int bx, int by, const UnitType & type, int buildDist) const
{
......@@ -59,24 +134,33 @@ bool BuildingPlacer::canBuildHereWithSpace(int bx, int by, const UnitType & type
// height and width of the building
int width = type.tileWidth();
int height = type.tileHeight();
int xdelta = (int)std::ceil((width - 1.0) / 2);
int ydelta = (int)std::ceil((height - 1.0) / 2);
// TODO: make sure we leave space for add-ons. These types of units can have addons:
// define the rectangle of the building spot
int startx = bx - buildDist;
int starty = by - buildDist;
int endx = bx + width + buildDist;
int endy = by + height + buildDist;
int startx = bx - buildDist - xdelta;
int starty = by - buildDist - ydelta;
int endx = bx + width + buildDist - xdelta;
int endy = by + height + buildDist - ydelta;
// TODO: recalculate start and end positions for addons
// if this rectangle doesn't fit on the map we can't build here
if (startx < 0 || starty < 0 || endx > m_bot.Map().width() || endx < bx + width || endy > m_bot.Map().height())
if (startx < 0 || starty < 0 || endx > m_bot.Map().width() || endx < bx + width - xdelta || endy > m_bot.Map().height() || endy < by + height - ydelta)
{
return false;
}
if (!m_bot.Map().canBuildTypeAtPosition(bx, by, type))
{
return false;
}
// if we can't build here, or space is reserved, or it's in the resource box, we can't build here
for (int x = startx; x < endx; x++)
{
......@@ -84,7 +168,7 @@ bool BuildingPlacer::canBuildHereWithSpace(int bx, int by, const UnitType & type
{
if (!type.isRefinery())
{
if (!buildable(type, x, y) || m_reserveMap[x][y])
if (!buildable(type, x, y) || isReserved(x, y) || !m_bot.Map().isWalkable(x, y)) // added isWalkabale in order to check towards walls
{
return false;
}
......@@ -95,7 +179,9 @@ bool BuildingPlacer::canBuildHereWithSpace(int bx, int by, const UnitType & type
return true;
}
CCTilePosition BuildingPlacer::getBuildLocationNear(const CCTilePosition & p, const UnitType & t, int buildDist) const
// BuildDist is the distance from the position where the building is gonna be placed.
CCTilePosition BuildingPlacer::getBuildLocationNear(const CCTilePosition & p, const UnitType & t, int buildDist, size_t search_count) const
{
//Timer t;
//t.start();
......@@ -106,7 +192,7 @@ CCTilePosition BuildingPlacer::getBuildLocationNear(const CCTilePosition & p, co
//double ms1 = t.getElapsedTimeInMilliSec();
// iterate through the list until we've found a suitable location
for (size_t i(0); i < closestToBuilding.size() && i < 1000; ++i)
for (size_t i(0); i < closestToBuilding.size() && (search_count == 0 || i < search_count); ++i)
{
auto & pos = closestToBuilding[i];
......@@ -121,6 +207,7 @@ CCTilePosition BuildingPlacer::getBuildLocationNear(const CCTilePosition & p, co
//double ms = t.getElapsedTimeInMilliSec();
//printf("Building Placer Failure: %s - Took %lf ms\n", b.type.getName().c_str(), ms);
std::cout << "Warning! Could not find valid placement for " << t.getName() << " near (" << p.x << ", " << p.y << "). Returning (0, 0) instead.";
return CCTilePosition(0, 0);
}
......@@ -134,10 +221,14 @@ bool BuildingPlacer::tileOverlapsBaseLocation(int x, int y, UnitType type) const
}
// dimensions of the proposed location
int tx1 = x;
int ty1 = y;
int tx2 = tx1 + type.tileWidth();
int ty2 = ty1 + type.tileHeight();
int xdelta = (int)std::ceil((type.tileWidth() - 1.0) / 2);
int ydelta = (int)std::ceil((type.tileHeight() - 1.0) / 2);
int tx1 = x - xdelta;
int ty1 = y - ydelta;
int tx2 = tx1 + type.tileWidth() - xdelta;
int ty2 = ty1 + type.tileHeight() - ydelta;
// for each base location
for (const BaseLocation * base : m_bot.Bases().getBaseLocations())
......@@ -165,7 +256,7 @@ bool BuildingPlacer::tileOverlapsBaseLocation(int x, int y, UnitType type) const
bool BuildingPlacer::buildable(const UnitType & type, int x, int y) const
{
// TODO: does this take units on the map into account?
if (!m_bot.Map().isValidTile(x, y) || !m_bot.Map().canBuildTypeAtPosition(x, y, type))
if (!m_bot.Map().isValidTile(x, y))
{
return false;
}
......@@ -177,11 +268,16 @@ bool BuildingPlacer::buildable(const UnitType & type, int x, int y) const
void BuildingPlacer::reserveTiles(int bx, int by, int width, int height)
{
// THIS is never called, that's why spacing doesnt work correctly
int rwidth = (int)m_reserveMap.size();
int rheight = (int)m_reserveMap[0].size();
for (int x = bx; x < bx + width && x < rwidth; x++)
int xdelta = (int)std::ceil((width - 1.0) / 2);
int ydelta = (int)std::ceil((height - 1.0) / 2);
for (int x = bx - xdelta; x < bx + width - xdelta && x < rwidth; x++)
{
for (int y = by; y < by + height && y < rheight; y++)
for (int y = by - ydelta; y < by + height - ydelta && y < rheight; y++)
{
m_reserveMap[x][y] = true;
}
......@@ -190,6 +286,7 @@ void BuildingPlacer::reserveTiles(int bx, int by, int width, int height)
void BuildingPlacer::drawReservedTiles()
{
// Why is there a return here? Should we not use the function? /Hannes Jmtner
return;
int rwidth = (int)m_reserveMap.size();
int rheight = (int)m_reserveMap[0].size();
......@@ -211,9 +308,12 @@ void BuildingPlacer::freeTiles(int bx, int by, int width, int height)
int rwidth = (int)m_reserveMap.size();
int rheight = (int)m_reserveMap[0].size();
for (int x = bx; x < bx + width && x < rwidth; x++)
int xdelta = (int)std::ceil((width - 1.0) / 2);
int ydelta = (int)std::ceil((height - 1.0) / 2);
for (int x = bx - xdelta; x < bx + width - xdelta && x < rwidth; x++)
{
for (int y = by; y < by + height && y < rheight; y++)
for (int y = by - ydelta; y < by + height - ydelta && y < rheight; y++)
{
m_reserveMap[x][y] = false;
}
......@@ -226,8 +326,12 @@ CCTilePosition BuildingPlacer::getRefineryPosition()
double minGeyserDistanceFromHome = std::numeric_limits<double>::max();
CCPosition homePosition = m_bot.GetStartLocation();
for (auto & unit : m_bot.GetAllUnits())
{
UnitType refinery = Util::GetRefinery(m_bot.GetPlayerRace(Players::Self), m_bot);
if (!unit.getType().isGeyser())
{
continue;
......@@ -235,6 +339,13 @@ CCTilePosition BuildingPlacer::getRefineryPosition()
CCPosition geyserPos(unit.getPosition());
// can't build a refinery on top of another
if (!m_bot.Map().canBuildTypeAtPosition((int)geyserPos.x, (int)geyserPos.y, refinery))
{
continue;
}
// check to see if it's next to one of our depots
bool nearDepot = false;
for (auto & unit : m_bot.UnitInfo().getUnits(Players::Self))
......