diff --git a/CMakeLists.txt b/CMakeLists.txt index f64b8850a..12eab9ca7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -730,10 +730,12 @@ set(${PROJECT_NAME}_SRCS ai/normalai.cpp ai/normalai.h ai/heavyai/heavyai.cpp ai/heavyai/heavyai.h ai/heavyai/heavyAiSharedData.h - ai/heavyai/situationevaluator.h ai/heavyai/situationevaluator.cpp + ai/heavyai/situationevaluator.h ai/heavyai/situationevaluator.cpp + ai/heavyai/simulationmap.h ai/heavyai/simulationmap.cpp ai/influencefrontmap.cpp ai/influencefrontmap.h ai/heavyai/unittargetedpathfindingsystem.h ai/heavyai/unittargetedpathfindingsystem.cpp ai/dummyai.h ai/dummyai.cpp + ai/capturebuildingselector.h ai/capturebuildingselector.cpp ai/aiprocesspipe.h ai/aiprocesspipe.cpp # production system ai/productionSystem/simpleproductionsystem.h ai/productionSystem/simpleproductionsystem.cpp diff --git a/ai/capturebuildingselector.cpp b/ai/capturebuildingselector.cpp new file mode 100644 index 000000000..39efece3e --- /dev/null +++ b/ai/capturebuildingselector.cpp @@ -0,0 +1,309 @@ +#include +#include + +#include "coreengine/interpreter.h" +#include "coreengine/gameconsole.h" + +#include "ai/capturebuildingselector.h" + +#include "game/gameaction.h" +#include "game/gamemap.h" + +CaptureBuildingSelector::CaptureBuildingSelector(CoreAI & owner) + : m_owner(owner) +{ + +} + +spGameAction CaptureBuildingSelector::getNextCaptureBuilding(std::vector & rOwnUnits, CoreAI::MoveUnitData** targetUnitData) +{ + AI_CONSOLE_PRINT("CaptureBuildingSelector::getNextCaptureBuilding()", GameConsole::eDEBUG); + + spGameAction pAction; + QStringList highPrioBuildings; + Interpreter *pInterpreter = Interpreter::getInstance(); + QJSValueList args({JsThis::getJsThis(&m_owner)}); + const QString func = "getHighPrioBuildings"; + auto erg = pInterpreter->doFunction(m_owner.getAiName(), func, args); + highPrioBuildings = erg.toVariant().toStringList(); + TargetBuildings captureBuildings = getTargetBuildings(rOwnUnits, highPrioBuildings, pAction, targetUnitData); + + if (captureBuildings.size() > 0) + { + Interpreter *pInterpreter = Interpreter::getInstance(); + for (qint32 i = 0; i < rOwnUnits.size(); ++i) + { + auto &unitData = rOwnUnits[i]; + if (unitData.nextAiStep <= m_owner.getAiFunctionStep()) + { + pInterpreter->threadProcessEvents(); + Unit *pUnit = unitData.pUnit.get(); + if (!pUnit->getHasMoved() && + unitData.actions.contains(CoreAI::ACTION_CAPTURE) && + pUnit->getAiMode() == GameEnums::GameAi_Normal) + { + TargetBuildings captures = filterCaptures(captureBuildings, i); + qint32 targetIndex = 0; + bool perform = getNextTarget(captureBuildings, captures, targetIndex); + if (perform) + { + pAction = getPerformingAction(captures, pUnit, unitData, targetIndex); + *targetUnitData = &unitData; + break; + } + } + } + } + } + return pAction; +} + +CaptureBuildingSelector::TargetBuildings CaptureBuildingSelector::filterCaptures(TargetBuildings & captureBuildings, qint32 unitIdx) +{ + TargetBuildings captures; + for (auto &building : captureBuildings) + { + if (building.m_unitIdx == unitIdx) + { + captures.push_back(building); + } + } + return captures; +} + +bool CaptureBuildingSelector::getNextTarget(TargetBuildings & captureBuildings, TargetBuildings & captures, qint32 & targetIndex) +{ + bool perform = false; + if (captures.size() > 0) + { + if (captures.size() == 1) + { + // we have only one target go for it + targetIndex = 0; + perform = true; + } + else + { + perform = findSingleCaptureBuilding(captureBuildings, captures, targetIndex); + // if not we can select a target from the list + if (!perform && + captures.size() > 0) + { + qint32 prio = -1; + targetIndex = 0; + // priorities production buildings over over captures + for (qint32 i2 = 0; i2 < captures.size(); i2++) + { + qint32 testPrio = getPrio(captures[i2]); + if (testPrio > prio) + { + targetIndex = i2; + prio = testPrio; + } + } + perform = true; + } + } + } + return perform; +} + +bool CaptureBuildingSelector::findSingleCaptureBuilding(TargetBuildings & captureBuildings, TargetBuildings & captures, qint32 & targetIndex) +{ + bool perform = false; + qint32 prio = -1; + // check if we have a building only we can capture and capture it + qint32 i2 = 0; + while (i2 < captures.size()) + { + qint32 captureCount = 0; + for (auto &buildingPos2 : captureBuildings) + { + if (buildingPos2.m_x == captures[i2].m_x && + buildingPos2.m_y == captures[i2].m_y) + { + if (buildingPos2.m_farAway == captures[i2].m_farAway || + (buildingPos2.m_farAway == true && captures[i2].m_farAway == false)) + { + ++captureCount; + } + else + { + --captureCount; + } + } + } + if (captureCount <= 0) + { + captures.erase(captures.cbegin() + i2); + } + else + { + bool isProductionBuilding = m_owner.getMap()->getTerrain(captures[i2].m_x, captures[i2].m_y)->getBuilding()->isProductionBuilding(); + if ((captureCount == 1 && perform == false) || + (captureCount == 1 && perform == true && isProductionBuilding)) + { + qint32 testPrio = getPrio(captures[i2]); + if (!perform) + { + prio = testPrio; + targetIndex = i2; + } + else + { + if (testPrio > prio) + { + targetIndex = i2; + prio = testPrio; + } + } + perform = true; + } + ++i2; + } + } + return perform; +} + +qint32 CaptureBuildingSelector::getPrio(CaptureInfo & info) +{ + constexpr qint32 nearbyPrioBonus = 2048; + Building *pBuilding = m_owner.getMap()->getTerrain(info.m_x, info.m_y)->getBuilding(); + qint32 testPrio = std::numeric_limits::min(); + if (pBuilding->isHq()) + { + testPrio = std::numeric_limits::max(); + } + else if (pBuilding->isProductionBuilding()) + { + testPrio = pBuilding->getConstructionList().size(); + } + if (!info.m_farAway && testPrio > 0) + { + testPrio += nearbyPrioBonus; + } + return testPrio; +} + +spGameAction CaptureBuildingSelector::getPerformingAction(TargetBuildings & captures, Unit *pUnit, CoreAI::MoveUnitData &unitData, qint32 targetIndex) +{ + spGameAction pAction; + // perform capturing + if (captures[targetIndex].m_farAway) + { + pAction = MemoryManagement::create(CoreAI::ACTION_WAIT, m_owner.getMap()); + pAction->setTarget(QPoint(pUnit->Unit::getX(), pUnit->Unit::getY())); + QVector movePath({QPoint(captures[targetIndex].m_x, captures[targetIndex].m_y)}); + pAction->setMovepath(movePath, 0); + } + else + { + pAction = MemoryManagement::create(CoreAI::ACTION_CAPTURE, m_owner.getMap()); + pAction->setTarget(QPoint(pUnit->Unit::getX(), pUnit->Unit::getY())); + auto path = unitData.pUnitPfs->getPathFast(captures[targetIndex].m_x, captures[targetIndex].m_y); + pAction->setMovepath(path, unitData.pUnitPfs->getCosts(path)); + if (pAction->canBePerformed()) + { + } + else + { + qint32 cost = 0; + QPoint rocketTarget = m_owner.getPlayer()->getSiloRockettarget(2, 3, cost); + m_owner.addSelectedFieldData(pAction, rocketTarget); + pAction->setActionID(CoreAI::ACTION_MISSILE); + if (pAction->canBePerformed()) + { + } + else + { + pAction = nullptr; + } + } + } + return pAction; +} + +CaptureBuildingSelector::TargetBuildings CaptureBuildingSelector::getTargetBuildings(std::vector & rOwnUnits, QStringList & highPrioBuildings, spGameAction & pAction, CoreAI::MoveUnitData** targetUnitData) +{ + Interpreter *pInterpreter = Interpreter::getInstance(); + bool fireSilos = m_owner.hasMissileTarget(); + TargetBuildings captureBuildings; + for (qint32 i = 0; i < rOwnUnits.size(); ++i) + { + pInterpreter->threadProcessEvents(); + auto &unitData = rOwnUnits[i]; + if (unitData.nextAiStep <= m_owner.getAiFunctionStep()) + { + Unit *pUnit = unitData.pUnit.get(); + if (!pUnit->getHasMoved() && + unitData.actions.contains(CoreAI::ACTION_CAPTURE) && + pUnit->getAiMode() == GameEnums::GameAi_Normal) + { + if (pUnit->getCapturePoints() > 0) + { + pAction = MemoryManagement::create(CoreAI::ACTION_CAPTURE, m_owner.getMap()); + pAction->setTarget(QPoint(pUnit->Unit::getX(), pUnit->Unit::getY())); + if (pAction->canBePerformed()) + { + *targetUnitData = &unitData; + break; + } + else + { + pAction = nullptr; + } + } + GameAction action(CoreAI::ACTION_CAPTURE, m_owner.getMap()); + action.setTarget(QPoint(pUnit->Unit::getX(), pUnit->Unit::getY())); + auto targets = unitData.pUnitPfs->getAllNodePointsFast(); + for (auto &target : targets) + { + action.setActionID(CoreAI::ACTION_CAPTURE); + action.setMovepath(QVector(1, target), 0); + if (unitData.pUnitPfs->getTargetCosts(target.x(), target.y()) < unitData.movementPoints + 1) + { + if (action.canBePerformed()) + { + captureBuildings.push_back(CaptureInfo(target.x(), target.y(), i, false)); + } + else + { + action.setActionID(CoreAI::ACTION_MISSILE); + if (action.canBePerformed() && fireSilos) + { + captureBuildings.push_back(CaptureInfo(target.x(), target.y(), i, false)); + } + } + } + else + { + Terrain *pTerrain = m_owner.getMap()->getTerrain(target.x(), target.y()); + auto *pBuilding = pTerrain->getBuilding(); + if (pBuilding != nullptr && + pBuilding->getOwner() == nullptr && + !m_usedFarAwayBuildings.contains(target) && + highPrioBuildings.contains(pBuilding->getBuildingID()) && + pTerrain->getUnit() == nullptr) + { + if (action.canBePerformed()) + { + captureBuildings.push_back(CaptureInfo(target.x(), target.y(), i, true)); + } + } + } + } + } + } + } + return captureBuildings; +} + +void CaptureBuildingSelector::addUsedFarAwayBuildings(QPoint farAwayTarget) +{ + m_usedFarAwayBuildings.push_back(farAwayTarget); +} + +void CaptureBuildingSelector::resetUsedFarAwayBuildings() +{ + m_usedFarAwayBuildings.clear(); +} diff --git a/ai/capturebuildingselector.h b/ai/capturebuildingselector.h new file mode 100644 index 000000000..a02fbab56 --- /dev/null +++ b/ai/capturebuildingselector.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include "ai/coreai.h" + +class CoreAI; +class GameAction; +using spGameAction = std::shared_ptr; +class QmlVectorUnit; +using spQmlVectorUnit = std::shared_ptr; +class QmlVectorBuilding; +using spQmlVectorBuilding = std::shared_ptr; + + +class CaptureBuildingSelector +{ + struct CaptureInfo + { + CaptureInfo(qint32 x, qint32 y, qint32 unitIdx, bool farAway) + : m_x(x), + m_y(y), + m_unitIdx(unitIdx), + m_farAway(farAway) + { + } + qint32 m_x; + qint32 m_y; + qint32 m_unitIdx; + bool m_farAway{false}; + }; + + using TargetBuildings = std::vector; +public: + CaptureBuildingSelector(CoreAI & owner); + + spGameAction getNextCaptureBuilding(std::vector & rOwnUnits, CoreAI::MoveUnitData** targetUnitData); + + void addUsedFarAwayBuildings(QPoint farAwayTarget); + void resetUsedFarAwayBuildings(); +private: + TargetBuildings getTargetBuildings(std::vector & rOwnUnits, QStringList & highPrioBuildings, spGameAction & pAction, CoreAI::MoveUnitData** targetUnitData); + spGameAction getPerformingAction(TargetBuildings & captures, Unit *pUnit, CoreAI::MoveUnitData &unitData, qint32 targetIndex); + bool getNextTarget(TargetBuildings & captureBuildings, TargetBuildings & captures, qint32 & targetIndex); + qint32 getPrio(CaptureInfo & info); + TargetBuildings filterCaptures(TargetBuildings & captureBuildings, qint32 unitIdx); + bool findSingleCaptureBuilding(TargetBuildings & captureBuildings, TargetBuildings & captures, qint32 & targetIndex); +private: + CoreAI & m_owner; + QVector m_usedFarAwayBuildings; +}; + diff --git a/ai/coreai.h b/ai/coreai.h index 0afe3f5d9..e40c7ba2d 100644 --- a/ai/coreai.h +++ b/ai/coreai.h @@ -599,6 +599,20 @@ public slots: * @brief resetMoveMap */ Q_INVOKABLE void resetMoveMap(); + qint32 getAiFunctionStep() const + { + return m_aiFunctionStep; + } + + /** + * @brief hasMissileTarget + * @return + */ + bool hasMissileTarget() const + { + return m_missileTarget; + } + void addSelectedFieldData(spGameAction & pGameAction, const QPoint & point); protected: /** * @brief prepareEnemieData @@ -609,7 +623,6 @@ public slots: void prepareEnemieData(spQmlVectorUnit & pUnits, spQmlVectorUnit & pEnemyUnits, spQmlVectorBuilding & pEnemyBuildings); void sortUnitsFarFromEnemyFirst(std::vector & pUnits, spQmlVectorUnit & pEnemyUnits); - void addSelectedFieldData(spGameAction & pGameAction, const QPoint & point); /** * @brief isAttackOnTerrainAllowed * @param pTerrain @@ -722,14 +735,6 @@ public slots: * @param pUnits */ void rebuildIsland(spQmlVectorUnit & pUnits); - /** - * @brief hasMissileTarget - * @return - */ - bool hasMissileTarget() - { - return m_missileTarget; - } /** * @brief GetUnitCounts * @param pUnits diff --git a/ai/heavyai/heavyai.cpp b/ai/heavyai/heavyai.cpp index c8be25d5e..d619c8393 100644 --- a/ai/heavyai/heavyai.cpp +++ b/ai/heavyai/heavyai.cpp @@ -108,6 +108,8 @@ void HeavyAi::process() { updateUnitCache(pUnits); updateUnitCache(pEnemyUnits); + + if (pUnits->size() > 0) { m_evaluator->updateInputVector(m_pMap, pUnits->at(0)->getPosition(), true); diff --git a/ai/heavyai/simulationmap.cpp b/ai/heavyai/simulationmap.cpp new file mode 100644 index 000000000..9d7513e19 --- /dev/null +++ b/ai/heavyai/simulationmap.cpp @@ -0,0 +1,105 @@ +#include "ai/heavyai/simulationmap.h" + +SimulationMap::SimulationMap() +{ +#ifdef GRAPHICSUPPORT + setObjectName("SimulationMap"); +#endif +} + +void SimulationMap::loadStartState(GameMap* pMap) +{ + QBuffer data; + data.open(QIODevice::ReadWrite); + QDataStream stream(&data); + pMap->serializeObject(stream); + data.seek(0); + clearMap(); + deserializer(stream, true); +} + +void SimulationMap::restoreInitialState() +{ + using resetCb = void (SimulationMap::*)(SimulationStep & step); + constexpr resetCb resetCbcallbacks[] = + { + &SimulationMap::resetMoveUnit, + &SimulationMap::resetDealDamage, + &SimulationMap::resetIncreaseCapturePoints, + }; + for (qint32 i = m_steps.size() - 1; i >= 0; --i) + { + auto & step = m_steps[i]; + (this->*resetCbcallbacks[static_cast(step.type)])(step); + } + m_steps.clear(); +} + + +void SimulationMap::moveUnit(QPoint currentPos, QPoint newPos) +{ + SimulationStep step; + step.type = SimulationType::Move; + spUnit pUnit = getTerrain(currentPos.x(), currentPos.y())->getSpUnit(); + getTerrain(newPos.x(), newPos.y())->setUnit(pUnit); + step.stepInfo.push_back(currentPos.x()); + step.stepInfo.push_back(currentPos.y()); + step.stepInfo.push_back(newPos.x()); + step.stepInfo.push_back(newPos.y()); + m_steps.push_back(step); +} + +void SimulationMap::resetMoveUnit(SimulationStep & step) +{ + spUnit pUnit = getTerrain(step.stepInfo[2], step.stepInfo[3])->getSpUnit(); + getTerrain(step.stepInfo[0], step.stepInfo[1])->setUnit(pUnit); +} + +void SimulationMap::dealDamage(QPoint position, float damage) +{ + SimulationStep step; + step.type = SimulationType::DealDamage; + spUnit pUnit = getTerrain(position.x(), position.y())->getSpUnit(); + if (pUnit.get() != nullptr) + { + pUnit->setHp(pUnit->getHp() - damage, false); + } + step.stepInfo.push_back(position.x()); + step.stepInfo.push_back(position.y()); + step.stepInfo.push_back(damage); + m_steps.push_back(step); +} + +void SimulationMap::resetDealDamage(SimulationStep & step) +{ + spUnit pUnit = getTerrain(step.stepInfo[0], step.stepInfo[1])->getSpUnit(); + if (pUnit.get() != nullptr) + { + pUnit->setHp(pUnit->getHp() + step.stepInfo[2], false); + } +} + +void SimulationMap::increaseCapturePoints(QPoint position, qint32 points) +{ + SimulationStep step; + step.type = SimulationType::DealDamage; + spUnit pUnit = getTerrain(position.x(), position.y())->getSpUnit(); + if (pUnit.get() != nullptr) + { + pUnit->setCapturePoints(pUnit->getCapturePoints() + points, false); + } + step.stepInfo.push_back(position.x()); + step.stepInfo.push_back(position.y()); + step.stepInfo.push_back(points); + m_steps.push_back(step); +} + +void SimulationMap::resetIncreaseCapturePoints(SimulationStep & step) +{ + spUnit pUnit = getTerrain(step.stepInfo[0], step.stepInfo[1])->getSpUnit(); + if (pUnit.get() != nullptr) + { + pUnit->setHp(pUnit->getHp() + step.stepInfo[2], false); + } + +} diff --git a/ai/heavyai/simulationmap.h b/ai/heavyai/simulationmap.h new file mode 100644 index 000000000..f942689fd --- /dev/null +++ b/ai/heavyai/simulationmap.h @@ -0,0 +1,37 @@ +#pragma once + +#include "game/gamemap.h" + +class SimulationMap final : public GameMap +{ + Q_OBJECT + enum class SimulationType + { + Move, + DealDamage, + CapturePoints + }; + + struct SimulationStep + { + SimulationType type; + std::vector stepInfo; + }; +public: + + SimulationMap(); + + void loadStartState(GameMap* pMap); + void restoreInitialState(); + + void moveUnit(QPoint currentPos, QPoint newPos); + void dealDamage(QPoint position, float damage); + void increaseCapturePoints(QPoint position, qint32 points); +private: + void resetMoveUnit(SimulationStep & step); + void resetDealDamage(SimulationStep & step); + void resetIncreaseCapturePoints(SimulationStep & step); +private: + std::vector m_steps; +}; + diff --git a/ai/normalai.cpp b/ai/normalai.cpp index dc7ec0cd5..c03edd17d 100644 --- a/ai/normalai.cpp +++ b/ai/normalai.cpp @@ -22,7 +22,8 @@ NormalAi::NormalAi(GameMap *pMap, const QString &configurationFile, GameEnums::AiTypes aiType, const QString &jsName) : CoreAI(pMap, aiType, jsName), - m_InfluenceFrontMap(pMap, m_IslandMaps) + m_InfluenceFrontMap(pMap, m_IslandMaps), + m_captureBuildingSelector(*this) { #ifdef GRAPHICSUPPORT setObjectName("NormalAi"); @@ -294,7 +295,7 @@ void NormalAi::resetToTurnStart() { clearUnitData(); m_productionData.clear(); - m_usedFarAwayBuildings.clear(); + m_captureBuildingSelector.resetUsedFarAwayBuildings(); m_secondMoveRound = false; CoreAI::resetToTurnStart(); } @@ -428,290 +429,29 @@ bool NormalAi::isUsingUnit(Unit *pUnit) bool NormalAi::captureBuildings(spQmlVectorUnit &pUnits, spQmlVectorBuilding &pBuildings, spQmlVectorBuilding &pEnemyBuildings) { AI_CONSOLE_PRINT("NormalAi::captureBuildings()", GameConsole::eDEBUG); - struct CaptureInfo - { - CaptureInfo(qint32 x, qint32 y, qint32 unitIdx, bool farAway) - : m_x(x), - m_y(y), - m_unitIdx(unitIdx), - m_farAway(farAway) - { - } - qint32 m_x; - qint32 m_y; - qint32 m_unitIdx; - bool m_farAway{false}; - }; - - QStringList highPrioBuildings; - Interpreter *pInterpreter = Interpreter::getInstance(); - QJSValueList args({m_jsThis}); - const QString func = "getHighPrioBuildings"; - auto erg = pInterpreter->doFunction(getAiName(), func, args); - highPrioBuildings = erg.toVariant().toStringList(); - - std::vector captureBuildings; - qint32 cost = 0; - QPoint rocketTarget = m_pPlayer->getSiloRockettarget(2, 3, cost); - bool fireSilos = hasMissileTarget(); - - for (qint32 i = 0; i < m_OwnUnits.size(); ++i) - { - pInterpreter->threadProcessEvents(); - auto &unitData = m_OwnUnits[i]; - if (unitData.nextAiStep <= m_aiFunctionStep) - { - Unit *pUnit = unitData.pUnit.get(); - if (!pUnit->getHasMoved() && - unitData.actions.contains(ACTION_CAPTURE) && - pUnit->getAiMode() == GameEnums::GameAi_Normal) - { - if (pUnit->getCapturePoints() > 0) - { - spGameAction pAction = MemoryManagement::create(ACTION_CAPTURE, m_pMap); - pAction->setTarget(QPoint(pUnit->Unit::getX(), pUnit->Unit::getY())); - if (pAction->canBePerformed()) - { - ++unitData.nextAiStep; - emit sigPerformAction(pAction); - return true; - } - } - else - { - GameAction action(ACTION_CAPTURE, m_pMap); - action.setTarget(QPoint(pUnit->Unit::getX(), pUnit->Unit::getY())); - auto targets = unitData.pUnitPfs->getAllNodePointsFast(); - for (auto &target : targets) - { - action.setActionID(ACTION_CAPTURE); - action.setMovepath(QVector(1, target), 0); - if (unitData.pUnitPfs->getTargetCosts(target.x(), target.y()) < unitData.movementPoints + 1) - { - if (action.canBePerformed()) - { - captureBuildings.push_back(CaptureInfo(target.x(), target.y(), i, false)); - } - else - { - action.setActionID(ACTION_MISSILE); - if (action.canBePerformed() && fireSilos) - { - captureBuildings.push_back(CaptureInfo(target.x(), target.y(), i, false)); - } - } - } - else - { - Terrain *pTerrain = m_pMap->getTerrain(target.x(), target.y()); - auto *pBuilding = pTerrain->getBuilding(); - if (pBuilding != nullptr && - pBuilding->getOwner() == nullptr && - !m_usedFarAwayBuildings.contains(target) && - highPrioBuildings.contains(pBuilding->getBuildingID()) && - pTerrain->getUnit() == nullptr) - { - if (action.canBePerformed()) - { - captureBuildings.push_back(CaptureInfo(target.x(), target.y(), i, true)); - } - } - } - } - } + MoveUnitData* targetUnitData = nullptr; + spGameAction pAction = m_captureBuildingSelector.getNextCaptureBuilding(m_OwnUnits, &targetUnitData); + if (pAction.get() != nullptr) + { + ++targetUnitData->nextAiStep; + m_updatePoints.push_back(pAction->getTargetUnit()->getPosition()); + m_updatePoints.push_back(pAction->getActionTarget()); + if (pAction->getActionID() == ACTION_WAIT) + { + std::vector targets; + auto actionTarget = pAction->getActionTarget(); + targets.push_back(QVector3D(actionTarget.x(), actionTarget.y(), 1)); + std::vector transporterTargets; + if (moveUnit(pAction, targetUnitData, pUnits, targetUnitData->actions, targets, transporterTargets, true, pBuildings, pEnemyBuildings)) + { + m_captureBuildingSelector.addUsedFarAwayBuildings(actionTarget); + return true; } } - } - if (captureBuildings.size() > 0) - { - constexpr qint32 nearbyPrioBonus = 2048; - Interpreter *pInterpreter = Interpreter::getInstance(); - for (qint32 i = 0; i < m_OwnUnits.size(); ++i) + else { - auto &unitData = m_OwnUnits[i]; - if (unitData.nextAiStep <= m_aiFunctionStep) - { - pInterpreter->threadProcessEvents(); - Unit *pUnit = unitData.pUnit.get(); - if (!pUnit->getHasMoved() && - unitData.actions.contains(ACTION_CAPTURE) && - pUnit->getAiMode() == GameEnums::GameAi_Normal) - { - std::vector captures; - for (auto &building : captureBuildings) - { - if (building.m_unitIdx == i) - { - captures.push_back(building); - } - } - bool perform = false; - qint32 targetIndex = 0; - if (captures.size() > 0) - { - if (captures.size() == 1) - { - // we have only one target go for it - targetIndex = 0; - perform = true; - } - else - { - qint32 prio = -1; - // check if we have a building only we can capture and capture it - qint32 i2 = 0; - while (i2 < captures.size()) - { - qint32 captureCount = 0; - for (auto &buildingPos2 : captureBuildings) - { - if (buildingPos2.m_x == captures[i2].m_x && - buildingPos2.m_y == captures[i2].m_y) - { - if (buildingPos2.m_farAway == captures[i2].m_farAway || - (buildingPos2.m_farAway == true && captures[i2].m_farAway == false)) - { - ++captureCount; - } - else - { - --captureCount; - } - } - } - if (captureCount <= 0) - { - captures.erase(captures.cbegin() + i2); - } - else - { - bool isProductionBuilding = m_pMap->getTerrain(captures[i2].m_x, captures[i2].m_y)->getBuilding()->isProductionBuilding(); - if ((captureCount == 1 && perform == false) || - (captureCount == 1 && perform == true && isProductionBuilding)) - { - Building *pBuilding = m_pMap->getTerrain(captures[i2].m_x, captures[i2].m_y)->getBuilding(); - qint32 testPrio = std::numeric_limits::min(); - if (pBuilding->getBuildingID() == CoreAI::BUILDING_HQ) - { - testPrio = std::numeric_limits::max(); - } - else if (pBuilding->isProductionBuilding()) - { - testPrio = pBuilding->getConstructionList().size(); - } - if (!captures[i2].m_farAway && testPrio > 0) - { - testPrio += nearbyPrioBonus; - } - if (!perform) - { - prio = testPrio; - targetIndex = i2; - } - else - { - if (testPrio > prio) - { - targetIndex = i2; - prio = testPrio; - } - } - perform = true; - } - ++i2; - } - } - // if not we can select a target from the list - if (!perform && - captures.size() > 0) - { - prio = -1; - targetIndex = 0; - // priorities production buildings over over captures - for (qint32 i2 = 0; i2 < captures.size(); i2++) - { - Building *pBuilding = m_pMap->getTerrain(captures[i2].m_x, captures[i2].m_y)->getBuilding(); - qint32 testPrio = std::numeric_limits::min(); - if (pBuilding->isHq()) - { - testPrio = std::numeric_limits::max(); - } - else if (pBuilding->isProductionBuilding()) - { - testPrio = pBuilding->getConstructionList().size(); - } - if (!captures[i2].m_farAway && testPrio > 0) - { - testPrio += nearbyPrioBonus; - } - if (testPrio > prio) - { - targetIndex = i2; - prio = testPrio; - } - } - perform = true; - } - } - } - // perform capturing - if (perform) - { - ++unitData.nextAiStep; - if (captures[targetIndex].m_farAway) - { - spGameAction pAction = MemoryManagement::create(ACTION_WAIT, m_pMap); - pAction->setTarget(QPoint(pUnit->Unit::getX(), pUnit->Unit::getY())); - std::vector targets; - targets.push_back(QVector3D(captures[targetIndex].m_x, captures[targetIndex].m_y, 1)); - std::vector transporterTargets; - - if (moveUnit(pAction, &unitData, pUnits, unitData.actions, targets, transporterTargets, true, pBuildings, pEnemyBuildings)) - { - m_usedFarAwayBuildings.append(QPoint(captures[targetIndex].m_x, captures[targetIndex].m_y)); - return true; - } - } - else - { - spGameAction pAction = MemoryManagement::create(ACTION_CAPTURE, m_pMap); - pAction->setTarget(QPoint(pUnit->Unit::getX(), pUnit->Unit::getY())); - auto path = unitData.pUnitPfs->getPathFast(captures[targetIndex].m_x, captures[targetIndex].m_y); - pAction->setMovepath(path, unitData.pUnitPfs->getCosts(path)); - m_updatePoints.push_back(pUnit->getPosition()); - m_updatePoints.push_back(pAction->getActionTarget()); - if (pAction->canBePerformed()) - { - emit sigPerformAction(pAction); - return true; - } - else - { - CoreAI::addSelectedFieldData(pAction, rocketTarget); - pAction->setActionID(ACTION_MISSILE); - if (pAction->canBePerformed()) - { - emit sigPerformAction(pAction); - return true; - } - } - } - qint32 i2 = 0; - while (i2 < captureBuildings.size()) - { - if (captureBuildings[i2].m_x == captures[targetIndex].m_x && - captureBuildings[i2].m_y == captures[targetIndex].m_y) - { - captureBuildings.erase(captureBuildings.cbegin() + i2); - } - else - { - ++i2; - } - } - } - } - } + emit sigPerformAction(pAction); + return true; } } ++m_aiFunctionStep; diff --git a/ai/normalai.h b/ai/normalai.h index 62380f0ef..946203100 100644 --- a/ai/normalai.h +++ b/ai/normalai.h @@ -4,6 +4,7 @@ #include #include "ai/coreai.h" +#include "ai/capturebuildingselector.h" #include "ai/influencefrontmap.h" @@ -512,9 +513,8 @@ public slots: * @brief m_productionData */ std::vector m_productionData; - QVector m_usedFarAwayBuildings; InfluenceFrontMap m_InfluenceFrontMap; - + CaptureBuildingSelector m_captureBuildingSelector; double m_notAttackableDamage{25.0f}; double m_midDamage{55.0f}; diff --git a/game/gamemap.h b/game/gamemap.h index 4e8a436e9..e69c18772 100644 --- a/game/gamemap.h +++ b/game/gamemap.h @@ -30,7 +30,7 @@ class EditorMenue; class BaseGamemenu; using spBaseGamemenu = std::shared_ptr; -class GameMap final : public QObject, public FileSerializable, public oxygine::Actor, public JsThis +class GameMap : public QObject, public FileSerializable, public oxygine::Actor, public JsThis { Q_OBJECT public: @@ -54,7 +54,10 @@ class GameMap final : public QObject, public FileSerializable, public oxygine::A qint32 m_uniqueIdCounter{0}; mutable GameEnums::MapFilterFlags m_mapFlags{GameEnums::MapFilterFlags_None}; }; - + /** + * @brief GameMap + */ + explicit GameMap() = default; /** * @brief GameMap creates an empty ma (filled with plains) with two players and the given size * @param width diff --git a/game/unit.cpp b/game/unit.cpp index bc4adfd00..fce22a861 100644 --- a/game/unit.cpp +++ b/game/unit.cpp @@ -2034,20 +2034,23 @@ qint32 Unit::getCapturePoints() const return m_capturePoints; } -void Unit::setCapturePoints(const qint32 value) +void Unit::setCapturePoints(const qint32 value, bool updateVisuals) { m_capturePoints = value; if (m_capturePoints < 0) { m_capturePoints = 0; } - if (m_capturePoints > 0) + if (updateVisuals) { - loadIcon("capture", GameMap::getImageSize() / 2, GameMap::getImageSize() / 2); - } - else - { - unloadIcon("capture"); + if (m_capturePoints > 0) + { + loadIcon("capture", GameMap::getImageSize() / 2, GameMap::getImageSize() / 2); + } + else + { + unloadIcon("capture"); + } } } @@ -2343,14 +2346,14 @@ qint32 Unit::getHpRounded() const return GlobalUtils::roundUp(m_hp); } -void Unit::setHp(const qreal value) +void Unit::setHp(const qreal value, bool updateVisuals) { m_hp = GlobalUtils::roundFloor(value, FLOAT_PRECISION); if (m_hp > MAX_UNIT_HP) { m_hp = MAX_UNIT_HP; } - if (m_pMap != nullptr) + if (updateVisuals && m_pMap != nullptr) { updateIcons(m_pMap->getCurrentViewPlayer()); } @@ -3370,16 +3373,6 @@ bool Unit::hasWeapons() return false; } -qint32 Unit::getUniqueID() const -{ - return m_UniqueID; -} - -GameEnums::GameAi Unit::getAiMode() const -{ - return m_AiMode; -} - void Unit::modifyUnit(qint32 hpChange, qint32 ammo1Change, qint32 ammo2Change, qint32 fuelChange) { setHp(getHp() + hpChange); @@ -3724,6 +3717,12 @@ void Unit::serializeObject(QDataStream& pStream, bool forHash) const pStream << m_customRangeInfo[i].color.rgba(); } pStream << m_cursorInfoRange; + size = m_aiCache.size(); + pStream << size; + for (qint32 i = 0; i < size; i++) + { + pStream << m_aiCache[i]; + } } } @@ -4084,6 +4083,19 @@ void Unit::deserializer(QDataStream& pStream, bool fast) pStream >> dummy2; } } + if (version > 23) + { + m_aiCache.clear(); + qint32 size = 0; + pStream >> size; + for (qint32 i = 0; i < size; i++) + { + qint32 info; + pStream >> info; + m_aiCache.push_back(info); + } + + } m_unitIdx = UnitSpriteManager::getInstance()->getIndex(m_UnitID); } diff --git a/game/unit.h b/game/unit.h index 00ad08f18..1c0da03a8 100644 --- a/game/unit.h +++ b/game/unit.h @@ -90,7 +90,7 @@ class Unit final : public Tooltip, public FileSerializable, public JsThis */ inline virtual qint32 getVersion() const override { - return 23; + return 24; } @@ -369,7 +369,10 @@ class Unit final : public Tooltip, public FileSerializable, public JsThis * @brief getUniqueID * @return returns the map wide unique id of this unit. */ - Q_INVOKABLE qint32 getUniqueID() const; + Q_INVOKABLE qint32 getUniqueID() const + { + return m_UniqueID; + } /** * @brief spawnUnit * @param unitID @@ -380,7 +383,10 @@ class Unit final : public Tooltip, public FileSerializable, public JsThis * @brief getAiMode * @return */ - Q_INVOKABLE GameEnums::GameAi getAiMode() const; + Q_INVOKABLE GameEnums::GameAi getAiMode() const + { + return m_AiMode; + } /** * @brief setAiMode * @param AiMode @@ -476,7 +482,7 @@ class Unit final : public Tooltip, public FileSerializable, public JsThis */ Q_INVOKABLE qint32 getMovementCosts(qint32 x, qint32 y, qint32 curX = -1, qint32 curY = -1, bool trapChecking = false); Q_INVOKABLE qreal getHp() const; - Q_INVOKABLE void setHp(const qreal value); + Q_INVOKABLE void setHp(const qreal value, bool updateVisuals = true); Q_INVOKABLE qint32 getHpRounded() const; Q_INVOKABLE bool hasDirectWeapon(); Q_INVOKABLE qint32 getAmmo1() const; @@ -502,7 +508,7 @@ class Unit final : public Tooltip, public FileSerializable, public JsThis Q_INVOKABLE qint32 getMaxFuel() const; Q_INVOKABLE void setMaxFuel(const qint32 value); Q_INVOKABLE qint32 getCapturePoints() const; - Q_INVOKABLE void setCapturePoints(const qint32 value); + Q_INVOKABLE void setCapturePoints(const qint32 value, bool updateVisuals = true); Q_INVOKABLE bool canCapture(); Q_INVOKABLE qint32 getCosts() const; Q_INVOKABLE qint32 getBaseCosts() const; diff --git a/multiplayer/multiplayermenu.cpp b/multiplayer/multiplayermenu.cpp index 18fdb39a0..d248e85f3 100644 --- a/multiplayer/multiplayermenu.cpp +++ b/multiplayer/multiplayermenu.cpp @@ -384,12 +384,7 @@ void Multiplayermenu::acceptNewConnection(quint64 socketID) } else { - QString command = QString(NetworkCommands::SENDINITIALMAPUPDATE); - CONSOLE_PRINT("Sending command " + command, GameConsole::eDEBUG); - QJsonObject data; - data.insert(JsonKeys::JSONKEY_COMMAND, command); - QJsonDocument doc(data); - emit m_pNetworkInterface->sig_sendData(socketID, doc.toJson(QJsonDocument::Compact), NetworkInterface::NetworkSerives::ServerHostingJson, false); + sendMapInfoUpdate(socketID); } CONSOLE_PRINT("Stopping despawn timer", GameConsole::eDEBUG); m_slaveDespawnTimer.stop(); @@ -553,10 +548,6 @@ void Multiplayermenu::recieveData(quint64 socketID, QByteArray data, NetworkInte connect(pDialogMessageBox.get(), &DialogMessageBox::sigOk, this, &Multiplayermenu::buttonBack, Qt::QueuedConnection); addChild(pDialogMessageBox); } - else if (messageType == NetworkCommands::SENDINITIALMAPUPDATE) - { - sendMapInfoUpdate(socketID); - } else if (messageType == NetworkCommands::REQUESTLOGINDDATA) { sendLoginData(socketID, objData); @@ -689,7 +680,14 @@ void Multiplayermenu::sendMapInfoUpdate(quint64 socketID) QCryptographicHash myHash(QCryptographicHash::Sha512); QString file = m_pMapSelectionView->getCurrentFile().filePath(); QString fileName = m_pMapSelectionView->getCurrentFile().fileName(); - QString scriptFile = m_pMapSelectionView->getCurrentMap()->getGameScript()->getScriptFile(); + spGameMap pMap = m_pMapSelectionView->getCurrentMap(); + auto* pGameScript = pMap->getGameScript(); + auto* pGameRules = pMap->getGameRules(); + QString scriptFile; + if (pGameScript != nullptr) + { + scriptFile = pGameScript->getScriptFile(); + } QFile mapFile(file); if (mapFile.exists()) { @@ -707,7 +705,11 @@ void Multiplayermenu::sendMapInfoUpdate(quint64 socketID) stream << Mainapp::getGameVersion(); QStringList mods = Settings::getInstance()->getMods(); QStringList versions = Settings::getInstance()->getActiveModVersions(); - bool filter = m_pMapSelectionView->getCurrentMap()->getGameRules()->getCosmeticModsAllowed(); + bool filter = false; + if (pGameRules != nullptr) + { + filter = pGameRules->getCosmeticModsAllowed(); + } Settings::getInstance()->filterCosmeticMods(mods, versions, filter); stream << filter; stream << static_cast(mods.size()); diff --git a/multiplayer/networkcommands.h b/multiplayer/networkcommands.h index 607dde89d..85ecdd045 100644 --- a/multiplayer/networkcommands.h +++ b/multiplayer/networkcommands.h @@ -293,8 +293,7 @@ namespace NetworkCommands /** * @brief SERVERSENDAUTOMATCHINFO */ - const char* const SERVERSENDAUTOMATCHINFO = "SERVERSENDAUTOMATCHINFO"; - const char* const SENDINITIALMAPUPDATE = "SENDINITIALMAPUPDATE"; + const char* const SERVERSENDAUTOMATCHINFO = "SERVERSENDAUTOMATCHINFO"; const char* const CREATEACCOUNT = "CREATEACCOUNT"; const char* const LOGINACCOUNT = "LOGINACCOUNT"; const char* const RESETPASSWORD = "RESETPASSWORD"; diff --git a/objects/playerselection.cpp b/objects/playerselection.cpp index 75da70719..313eeaf4c 100644 --- a/objects/playerselection.cpp +++ b/objects/playerselection.cpp @@ -443,6 +443,10 @@ void PlayerSelection::initializeMap(bool relaunchedLobby) { allHuman = false; } + else if (ai == GameEnums::AiTypes_Human) + { + pPlayer->setPlayerNameId(Settings::getInstance()->getUsername()); + } pPlayer->setControlType(ai); } else if (pPlayer->getControlType() != GameEnums::AiTypes_Open && @@ -451,6 +455,7 @@ void PlayerSelection::initializeMap(bool relaunchedLobby) CONSOLE_PRINT("PlayerSelection::initializeMap changing player " + QString::number(i) + " to human", GameConsole::eDEBUG); pPlayer->setBaseGameInput(MemoryManagement::create(m_pMap)); pPlayer->setControlType(GameEnums::AiTypes_Human); + pPlayer->setPlayerNameId(Settings::getInstance()->getUsername()); } else {