diff --git a/lib/battle/BattleInfo.cpp b/lib/battle/BattleInfo.cpp index c2e6a5cce..62e8e0c68 100644 --- a/lib/battle/BattleInfo.cpp +++ b/lib/battle/BattleInfo.cpp @@ -804,7 +804,6 @@ void BattleInfo::addUnit(uint32_t id, const JsonNode & data) void BattleInfo::moveUnit(uint32_t id, BattleHex destination) { auto sta = getStack(id); - if(!sta) { logGlobal->error("Cannot find stack %d", id); @@ -1064,27 +1063,37 @@ bool CMP_stack::operator()(const battle::Unit * a, const battle::Unit * b) if(as != bs) return as > bs; else - return a->unitSlot() < b->unitSlot(); //FIXME: what about summoned stacks? - } + { + if(a->unitSide() == b->unitSide()) + return a->unitSlot() < b->unitSlot(); + else + return a->unitSide() == side ? false : true; + } + //FIXME: what about summoned stacks + } case 2: //fastest last, upper slot first - //TODO: should be replaced with order of receiving morale! case 3: //fastest last, upper slot first { int as = a->getInitiative(turn), bs = b->getInitiative(turn); if(as != bs) - return as < bs; + return as > bs; else - return a->unitSlot() < b->unitSlot(); + { + if(a->unitSide() == b->unitSide()) + return a->unitSlot() < b->unitSlot(); + else + return a->unitSide() == side ? false : true; + } } default: assert(0); return false; } - } -CMP_stack::CMP_stack(int Phase, int Turn) +CMP_stack::CMP_stack(int Phase, int Turn, uint8_t Side) { phase = Phase; turn = Turn; + side = Side; } diff --git a/lib/battle/BattleInfo.h b/lib/battle/BattleInfo.h index a7bc51341..b66acb701 100644 --- a/lib/battle/BattleInfo.h +++ b/lib/battle/BattleInfo.h @@ -144,8 +144,9 @@ class DLL_LINKAGE CMP_stack { int phase; //rules of which phase will be used int turn; + uint8_t side; public: bool operator ()(const battle::Unit * a, const battle::Unit * b); - CMP_stack(int Phase = 1, int Turn = 0); + CMP_stack(int Phase = 1, int Turn = 0, uint8_t Side = BattleSide::ATTACKER); }; diff --git a/lib/battle/CBattleInfoCallback.cpp b/lib/battle/CBattleInfoCallback.cpp index 698d3cf49..fb305b16b 100644 --- a/lib/battle/CBattleInfoCallback.cpp +++ b/lib/battle/CBattleInfoCallback.cpp @@ -353,57 +353,68 @@ battle::Units CBattleInfoCallback::battleAliveUnits(ui8 side) const //T is battle::Unit descendant template -const T * takeOneUnit(std::vector & all, const int turn, int8_t & lastMoved) +const T * takeOneUnit(std::vector & all, const int turn, int8_t & lastMoved, int phase) { - const T * ret = nullptr; - size_t i, //fastest stack - j=0; //fastest stack of the other side - for(i = 0; i < all.size(); i++) + const T * returnedUnit = nullptr; + size_t currentUnitIndex = 0; + + for(size_t i = 0; i < all.size(); i++) + { + int32_t currentUnitSpeed = -1; + int32_t returnedUnitSpeed = -1; + if(returnedUnit) + returnedUnitSpeed = returnedUnit->getInitiative(turn); if(all[i]) - break; - - //no stacks left - if(i == all.size()) - return nullptr; - - const T * fastest = all[i], *other = nullptr; - int bestSpeed = fastest->getInitiative(turn); - - if(fastest->unitSide() == lastMoved) - { - ret = fastest; - } - else - { - for(j = i + 1; j < all.size(); j++) { - if(!all[j]) continue; - if(all[j]->unitSide() != lastMoved || all[j]->getInitiative(turn) != bestSpeed) + currentUnitSpeed = all[i]->getInitiative(turn); + switch(phase) + { + case 1: // Faster first, attacker priority, higher slot first + if(returnedUnit == nullptr || currentUnitSpeed > returnedUnitSpeed) + { + returnedUnit = all[i]; + currentUnitIndex = i; + } + else if(currentUnitSpeed == returnedUnitSpeed) + { + if(lastMoved == -1 && turn <= 0 && all[i]->unitSide() == BattleSide::ATTACKER + && !(returnedUnit->unitSide() == all[i]->unitSide() && returnedUnit->unitSlot() < all[i]->unitSlot())) // Turn 0 attacker priority + { + returnedUnit = all[i]; + currentUnitIndex = i; + } + else if(lastMoved != -1 && all[i]->unitSide() != lastMoved + && !(returnedUnit->unitSide() == all[i]->unitSide() && returnedUnit->unitSlot() < all[i]->unitSlot())) // Alternate equal speeds units + { + returnedUnit = all[i]; + currentUnitIndex = i; + } + } break; - } - - if(j >= all.size()) - { - ret = fastest; - } - else - { - other = all[j]; - if(other->getInitiative(turn) != bestSpeed) - ret = fastest; - else - ret = other; + case 2: // Slower first, higher slot first + case 3: + if(returnedUnit == nullptr || currentUnitSpeed < returnedUnitSpeed) + { + returnedUnit = all[i]; + currentUnitIndex = i; + } + else if(currentUnitSpeed == returnedUnitSpeed && lastMoved != -1 && all[i]->unitSide() != lastMoved + && !(returnedUnit->unitSide() == all[i]->unitSide() && returnedUnit->unitSlot() < all[i]->unitSlot())) // Alternate equal speeds units + { + returnedUnit = all[i]; + currentUnitIndex = i; + } + break; + default: + break; + } } } - assert(ret); - if(ret == fastest) - all[i] = nullptr; - else - all[j] = nullptr; - - lastMoved = ret->unitSide(); - return ret; + if(!returnedUnit) + return nullptr; + all[currentUnitIndex] = nullptr; + return returnedUnit; } void CBattleInfoCallback::battleGetTurnOrder(std::vector & out, const size_t maxUnits, const int maxTurns, const int turn, int8_t lastMoved) const @@ -483,28 +494,39 @@ void CBattleInfoCallback::battleGetTurnOrder(std::vector & out, c phase[p].push_back(one); } - boost::sort(phase[0], CMP_stack(0, actualTurn)); + boost::sort(phase[0], CMP_stack(0, actualTurn, lastMoved)); std::copy(phase[0].begin(), phase[0].end(), std::back_inserter(out.back())); if(outputFull()) return; for(int i = 1; i < 4; i++) - boost::sort(phase[i], CMP_stack(i, actualTurn)); - - if(lastMoved < 0) - lastMoved = BattleSide::ATTACKER; + boost::sort(phase[i], CMP_stack(i, actualTurn, lastMoved)); int pi = 1; while(!outputFull() && pi < 4) { - auto current = takeOneUnit(phase[pi], actualTurn, lastMoved); - if(!current) + const battle::Unit * current = nullptr; + if(phase[pi].empty()) pi++; else - out.back().push_back(current); + { + current = takeOneUnit(phase[pi], actualTurn, lastMoved, pi); + if(!current) + { + pi++; + } + else + { + out.back().push_back(current); + lastMoved = current->unitSide(); + } + } } + if(lastMoved < 0) + lastMoved = BattleSide::ATTACKER; + if(!outputFull() && (maxTurns == 0 || out.size() < maxTurns)) battleGetTurnOrder(out, maxUnits, maxTurns, actualTurn + 1, lastMoved); }