1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-27 22:49:25 +02:00

Refactoring 1st phase - Renaming's mainly

S'more small battle order refactoring.
This commit is contained in:
krs
2023-02-25 00:25:30 +02:00
parent d55ac50e3c
commit 0373febe6f
2 changed files with 122 additions and 99 deletions

View File

@@ -355,57 +355,71 @@ battle::Units CBattleInfoCallback::battleAliveUnits(ui8 side) const
}); });
} }
enum BattlePhases
{
SIEGE, // [0] - turrets/catapult,
NORMAL, // [1] - normal (unmoved) creatures, other war machines,
WAIT_MORALE, // [2] - waited creatures that had morale,
WAIT, // [3] - rest of waited creatures
MAX_NO_OF_PHASES // [4] - number of phases.
};
//T is battle::Unit descendant //T is battle::Unit descendant
template <typename T> template <typename T>
const T * takeOneUnit(std::vector<const T*> & all, const int turn, int8_t & lastMoved, int phase) const T * takeOneUnit(std::vector<const T*> & allUnits, const int turn, int8_t & sideThatLastMoved, int phase)
{ {
const T * returnedUnit = nullptr; const T * returnedUnit = nullptr;
size_t currentUnitIndex = 0; size_t currentUnitIndex = 0;
for(size_t i = 0; i < all.size(); i++) for(size_t i = 0; i < allUnits.size(); i++)
{ {
int32_t currentUnitSpeed = -1; int32_t currentUnitInitiative = -1;
int32_t returnedUnitSpeed = -1; int32_t returnedUnitInitiative = -1;
if(returnedUnit) if(returnedUnit)
returnedUnitSpeed = returnedUnit->getInitiative(turn); returnedUnitInitiative = returnedUnit->getInitiative(turn);
if(all[i])
{ if(!allUnits[i])
currentUnitSpeed = all[i]->getInitiative(turn); continue;
auto currentUnit = allUnits[i];
currentUnitInitiative = currentUnit->getInitiative(turn);
switch(phase) switch(phase)
{ {
case 1: // Faster first, attacker priority, higher slot first case NORMAL: // Faster first, attacker priority, higher slot first
if(returnedUnit == nullptr || currentUnitSpeed > returnedUnitSpeed) if(returnedUnit == nullptr || currentUnitInitiative > returnedUnitInitiative)
{ {
returnedUnit = all[i]; returnedUnit = currentUnit;
currentUnitIndex = i; currentUnitIndex = i;
} }
else if(currentUnitSpeed == returnedUnitSpeed) else if(currentUnitInitiative == returnedUnitInitiative)
{ {
if(lastMoved == -1 && turn <= 0 && all[i]->unitSide() == BattleSide::ATTACKER if(sideThatLastMoved == -1 && turn <= 0 && currentUnit->unitSide() == BattleSide::ATTACKER
&& !(returnedUnit->unitSide() == all[i]->unitSide() && returnedUnit->unitSlot() < all[i]->unitSlot())) // Turn 0 attacker priority && !(returnedUnit->unitSide() == currentUnit->unitSide() && returnedUnit->unitSlot() < currentUnit->unitSlot())) // Turn 0 attacker priority
{ {
returnedUnit = all[i]; returnedUnit = currentUnit;
currentUnitIndex = i; currentUnitIndex = i;
} }
else if(lastMoved != -1 && all[i]->unitSide() != lastMoved else if(sideThatLastMoved != -1 && currentUnit->unitSide() != sideThatLastMoved
&& !(returnedUnit->unitSide() == all[i]->unitSide() && returnedUnit->unitSlot() < all[i]->unitSlot())) // Alternate equal speeds units && !(returnedUnit->unitSide() == currentUnit->unitSide() && returnedUnit->unitSlot() < currentUnit->unitSlot())) // Alternate equal speeds units
{ {
returnedUnit = all[i]; returnedUnit = currentUnit;
currentUnitIndex = i; currentUnitIndex = i;
} }
} }
break; break;
case 2: // Slower first, higher slot first case WAIT_MORALE: // Slower first, higher slot first
case 3: case WAIT:
if(returnedUnit == nullptr || currentUnitSpeed < returnedUnitSpeed) if(returnedUnit == nullptr || currentUnitInitiative < returnedUnitInitiative)
{ {
returnedUnit = all[i]; returnedUnit = currentUnit;
currentUnitIndex = i; currentUnitIndex = i;
} }
else if(currentUnitSpeed == returnedUnitSpeed && lastMoved != -1 && all[i]->unitSide() != lastMoved else if(currentUnitInitiative == returnedUnitInitiative && sideThatLastMoved != -1 && currentUnit->unitSide() != sideThatLastMoved
&& !(returnedUnit->unitSide() == all[i]->unitSide() && returnedUnit->unitSlot() < all[i]->unitSlot())) // Alternate equal speeds units && !(returnedUnit->unitSide() == currentUnit->unitSide() && returnedUnit->unitSlot() < currentUnit->unitSlot())) // Alternate equal speeds units
{ {
returnedUnit = all[i]; returnedUnit = currentUnit;
currentUnitIndex = i; currentUnitIndex = i;
} }
break; break;
@@ -413,15 +427,16 @@ const T * takeOneUnit(std::vector<const T*> & all, const int turn, int8_t & last
break; break;
} }
} }
}
if(!returnedUnit) if(!returnedUnit)
return nullptr; return nullptr;
all[currentUnitIndex] = nullptr;
allUnits[currentUnitIndex] = nullptr;
return returnedUnit; return returnedUnit;
} }
void CBattleInfoCallback::battleGetTurnOrder(std::vector<battle::Units> & out, const size_t maxUnits, const int maxTurns, const int turn, int8_t lastMoved) const void CBattleInfoCallback::battleGetTurnOrder(std::vector<battle::Units> & turns, const size_t maxUnits, const int maxTurns, const int turn, int8_t sideThatLastMoved) const
{ {
RETURN_IF_NOT_BATTLE(); RETURN_IF_NOT_BATTLE();
@@ -433,106 +448,105 @@ void CBattleInfoCallback::battleGetTurnOrder(std::vector<battle::Units> & out, c
auto actualTurn = turn > 0 ? turn : 0; auto actualTurn = turn > 0 ? turn : 0;
auto outputFull = [&]() -> bool auto turnsIsFull = [&]() -> bool
{ {
if(maxUnits == 0) if(maxUnits == 0)
return false;//no limit return false;//no limit
size_t outSize = 0; size_t turnsSize = 0;
for(const auto & oneTurn : out) for(const auto & oneTurn : turns)
outSize += oneTurn.size(); turnsSize += oneTurn.size();
return outSize >= maxUnits; return turnsSize >= maxUnits;
}; };
out.emplace_back(); turns.emplace_back();
//We'll split creatures with remaining movement to 4 buckets // We'll split creatures with remaining movement to 4 buckets
// [0] - turrets/catapult, // [0] - turrets/catapult,
// [1] - normal (unmoved) creatures, other war machines, // [1] - normal (unmoved) creatures, other war machines,
// [2] - waited cres that had morale, // [2] - waited creatures that had morale,
// [3] - rest of waited cres // [3] - rest of waited creatures
std::array<battle::Units, 4> phase; std::array<battle::Units, MAX_NO_OF_PHASES> phases;
const battle::Unit * active = battleActiveUnit(); const battle::Unit * activeUnit = battleActiveUnit();
if(active) if(activeUnit)
{ {
//its first turn and active unit hasn't taken any action yet - must be placed at the beginning of queue, no matter what //its first turn and active unit hasn't taken any action yet - must be placed at the beginning of queue, no matter what
if(turn == 0 && active->willMove() && !active->waited()) if(turn == 0 && activeUnit->willMove() && !activeUnit->waited())
{ {
out.back().push_back(active); turns.back().push_back(activeUnit);
if(outputFull()) if(turnsIsFull())
return; return;
} }
//its first or current turn, turn priority for active stack side //its first or current turn, turn priority for active stack side
//TODO: what if active stack mind-controlled? //TODO: what if active stack mind-controlled?
if(turn <= 0 && lastMoved < 0) if(turn <= 0 && sideThatLastMoved < 0)
lastMoved = active->unitSide(); sideThatLastMoved = activeUnit->unitSide();
} }
auto all = battleGetUnitsIf([](const battle::Unit * unit) auto allUnits = battleGetUnitsIf([](const battle::Unit * unit)
{ {
return !unit->isGhost(); return !unit->isGhost();
}); });
// If no unit will be EVER! able to move, battle is over.
if(!vstd::contains_if(all, [](const battle::Unit * unit) { return unit->willMove(100000); })) //little evil, but 100000 should be enough for all effects to disappear if(!vstd::contains_if(allUnits, [](const battle::Unit * unit) { return unit->willMove(100000); })) //little evil, but 100000 should be enough for all effects to disappear
{ {
//No unit will be able to move, battle is over. turns.clear();
out.clear();
return; return;
} }
for(const auto * one : all) for(const auto * unit : allUnits)
{ {
if((actualTurn == 0 && !one->willMove()) //we are considering current round and unit won't move if((actualTurn == 0 && !unit->willMove()) //we are considering current round and unit won't move
|| (actualTurn > 0 && !one->canMove(turn)) //unit won't be able to move in later rounds || (actualTurn > 0 && !unit->canMove(turn)) //unit won't be able to move in later rounds
|| (actualTurn == 0 && one == active && !out.at(0).empty() && one == out.front().front())) //it's active unit already added at the beginning of queue || (actualTurn == 0 && unit == activeUnit && !turns.at(0).empty() && unit == turns.front().front())) //it's active unit already added at the beginning of queue
{ {
continue; continue;
} }
int p = one->battleQueuePhase(turn); int unitPhase = unit->battleQueuePhase(turn);
phase[p].push_back(one); phases[unitPhase].push_back(unit);
} }
boost::sort(phase[0], CMP_stack(0, actualTurn, lastMoved)); boost::sort(phases[SIEGE], CMP_stack(SIEGE, actualTurn, sideThatLastMoved));
std::copy(phase[0].begin(), phase[0].end(), std::back_inserter(out.back())); std::copy(phases[SIEGE].begin(), phases[SIEGE].end(), std::back_inserter(turns.back()));
if(outputFull()) if(turnsIsFull())
return; return;
for(int i = 1; i < 4; i++) for(int phase = NORMAL; phase < MAX_NO_OF_PHASES; phase++)
boost::sort(phase[i], CMP_stack(i, actualTurn, lastMoved)); boost::sort(phases[phase], CMP_stack(phase, actualTurn, sideThatLastMoved));
int pi = 1; int phase = NORMAL;
while(!outputFull() && pi < 4) while(!turnsIsFull() && phase < MAX_NO_OF_PHASES)
{ {
const battle::Unit * current = nullptr; const battle::Unit * currentUnit = nullptr;
if(phase[pi].empty()) if(phases[phase].empty())
pi++; phase++;
else else
{ {
current = takeOneUnit(phase[pi], actualTurn, lastMoved, pi); currentUnit = takeOneUnit(phases[phase], actualTurn, sideThatLastMoved, phase);
if(!current) if(!currentUnit)
{ {
pi++; phase++;
} }
else else
{ {
out.back().push_back(current); turns.back().push_back(currentUnit);
lastMoved = current->unitSide(); sideThatLastMoved = currentUnit->unitSide();
} }
} }
} }
if(lastMoved < 0) if(sideThatLastMoved < 0)
lastMoved = BattleSide::ATTACKER; sideThatLastMoved = BattleSide::ATTACKER;
if(!outputFull() && (maxTurns == 0 || out.size() < maxTurns)) if(!turnsIsFull() && (maxTurns == 0 || turns.size() < maxTurns))
battleGetTurnOrder(out, maxUnits, maxTurns, actualTurn + 1, lastMoved); battleGetTurnOrder(turns, maxUnits, maxTurns, actualTurn + 1, sideThatLastMoved);
} }
std::vector<BattleHex> CBattleInfoCallback::battleGetAvailableHexes(const battle::Unit * unit) const std::vector<BattleHex> CBattleInfoCallback::battleGetAvailableHexes(const battle::Unit * unit) const

View File

@@ -611,20 +611,29 @@ bool CUnitState::waited(int turn) const
int CUnitState::battleQueuePhase(int turn) const int CUnitState::battleQueuePhase(int turn) const
{ {
enum BattlePhases
{
SIEGE, // [0] - turrets/catapult,
NORMAL, // [1] - normal (unmoved) creatures, other war machines,
WAIT_MORALE, // [2] - waited creatures that had morale,
WAIT, // [3] - rest of waited creatures
MAX_NO_OF_PHASES // [4] - number of phases. Can be used in for loops.
};
if(turn <= 0 && waited()) //consider waiting state only for ongoing round if(turn <= 0 && waited()) //consider waiting state only for ongoing round
{ {
if(hadMorale) if(hadMorale)
return 2; return WAIT_MORALE;
else else
return 3; return WAIT;
} }
else if(creatureIndex() == CreatureID::CATAPULT || isTurret()) //catapult and turrets are first else if(creatureIndex() == CreatureID::CATAPULT || isTurret()) //catapult and turrets are first
{ {
return 0; return SIEGE;
} }
else else
{ {
return 1; return NORMAL;
} }
} }