mirror of
https://github.com/vcmi/vcmi.git
synced 2025-11-25 22:42:04 +02:00
Feature: Army Management Shortcuts should work as in HD+ Mod
This commit is contained in:
committed by
Andrii Danylchenko
parent
7faa691f42
commit
8cae3398ba
@@ -74,18 +74,23 @@ void CGarrisonSlot::hover (bool on)
|
||||
}
|
||||
else
|
||||
{
|
||||
if(upg == EGarrisonType::UP)
|
||||
const bool isHeroOnMap = owner->armedObjs[0] // Hero is not a visitor and not a garrison defender
|
||||
&& owner->armedObjs[0]->ID == Obj::HERO
|
||||
&& (!owner->armedObjs[1] || owner->armedObjs[1]->ID == Obj::HERO) // one hero or we are in the Heroes exchange window
|
||||
&& !(static_cast<const CGHeroInstance*>(owner->armedObjs[0]))->inTownGarrison;
|
||||
|
||||
if(isHeroOnMap)
|
||||
{
|
||||
temp = CGI->generaltexth->allTexts[481]; //Select %s
|
||||
}
|
||||
else if(upg == EGarrisonType::UP)
|
||||
{
|
||||
temp = CGI->generaltexth->tcommands[12]; //Select %s (in garrison)
|
||||
}
|
||||
else if(owner->armedObjs[0] && (owner->armedObjs[0]->ID == Obj::TOWN || owner->armedObjs[0]->ID == Obj::HERO))
|
||||
else // Hero is visiting some object (town, mine, etc)
|
||||
{
|
||||
temp = CGI->generaltexth->tcommands[32]; //Select %s (visiting)
|
||||
}
|
||||
else
|
||||
{
|
||||
temp = CGI->generaltexth->allTexts[481]; //Select %s
|
||||
}
|
||||
boost::algorithm::replace_first(temp,"%s",creature->nameSing);
|
||||
}
|
||||
}
|
||||
@@ -140,6 +145,18 @@ bool CGarrisonSlot::ally() const
|
||||
return PlayerRelations::ALLIES == LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, getObj()->tempOwner);
|
||||
}
|
||||
|
||||
std::function<void()> CGarrisonSlot::getDismiss() const
|
||||
{
|
||||
const bool canDismiss = getObj()->tempOwner == LOCPLINT->playerID
|
||||
&& (getObj()->stacksCount() > 1 ||
|
||||
!getObj()->needsLastStack());
|
||||
|
||||
return canDismiss ? [=]()
|
||||
{
|
||||
LOCPLINT->cb->dismissCreature(getObj(), ID);
|
||||
} : (std::function<void()>)nullptr;
|
||||
}
|
||||
|
||||
/// The creature slot has been clicked twice, therefore the creature info should be shown
|
||||
/// @return Whether the view should be refreshed
|
||||
bool CGarrisonSlot::viewInfo()
|
||||
@@ -148,11 +165,9 @@ bool CGarrisonSlot::viewInfo()
|
||||
LOCPLINT->cb->getUpgradeInfo(getObj(), ID, pom);
|
||||
|
||||
bool canUpgrade = getObj()->tempOwner == LOCPLINT->playerID && pom.oldID>=0; //upgrade is possible
|
||||
bool canDismiss = getObj()->tempOwner == LOCPLINT->playerID && (getObj()->stacksCount()>1 || !getObj()->needsLastStack());
|
||||
std::function<void(CreatureID)> upgr = nullptr;
|
||||
std::function<void()> dism = nullptr;
|
||||
auto dism = getDismiss();
|
||||
if(canUpgrade) upgr = [=] (CreatureID newID) { LOCPLINT->cb->upgradeCreature(getObj(), ID, newID); };
|
||||
if(canDismiss) dism = [=](){ LOCPLINT->cb->dismissCreature(getObj(), ID); };
|
||||
|
||||
owner->selectSlot(nullptr);
|
||||
owner->setSplittingMode(false);
|
||||
@@ -288,13 +303,17 @@ void CGarrisonSlot::clickLeft(tribool down, bool previousState)
|
||||
{
|
||||
bool refr = false;
|
||||
const CGarrisonSlot * selection = owner->getSelection();
|
||||
|
||||
if(!selection)
|
||||
{
|
||||
refr = highlightOrDropArtifact();
|
||||
refr = highlightOrDropArtifact(); // Affects selection
|
||||
handleSplittingShortcuts();
|
||||
}
|
||||
else if(selection == this)
|
||||
refr = viewInfo();
|
||||
{
|
||||
if(!handleSplittingShortcuts())
|
||||
refr = viewInfo(); // Affects selection
|
||||
}
|
||||
// Re-highlight if troops aren't removable or not ours.
|
||||
else if (mustForceReselection())
|
||||
{
|
||||
@@ -403,34 +422,67 @@ CGarrisonSlot::CGarrisonSlot(CGarrisonInt * Owner, int x, int y, SlotID IID, CGa
|
||||
update();
|
||||
}
|
||||
|
||||
void CGarrisonSlot::splitIntoParts(CGarrisonSlot::EGarrisonType type, int amount, int maxOfSplittedSlots)
|
||||
void CGarrisonSlot::splitIntoParts(CGarrisonSlot::EGarrisonType type, int amount)
|
||||
{
|
||||
auto empty = owner->getEmptySlot(type);
|
||||
|
||||
if(empty == SlotID())
|
||||
return;
|
||||
|
||||
owner->pb = type;
|
||||
for(CGarrisonSlot * slot : owner->getEmptySlots(type))
|
||||
{
|
||||
owner->p2 = slot->ID;
|
||||
owner->splitStacks(1, amount);
|
||||
maxOfSplittedSlots--;
|
||||
if(!maxOfSplittedSlots || owner->getSelection()->myStack->count <= 1)
|
||||
break;
|
||||
}
|
||||
owner->p2 = empty;
|
||||
owner->splitStacks(1, amount);
|
||||
}
|
||||
|
||||
void CGarrisonSlot::handleSplittingShortcuts()
|
||||
bool CGarrisonSlot::handleSplittingShortcuts()
|
||||
{
|
||||
const Uint8 * state = SDL_GetKeyboardState(NULL);
|
||||
if(owner->getSelection() && owner->getEmptySlots(owner->getSelection()->upg).size() && owner->getSelection()->myStack->count > 1)
|
||||
const bool isAlt = !!state[SDL_SCANCODE_LALT];
|
||||
const bool isLShift = !!state[SDL_SCANCODE_LSHIFT];
|
||||
const bool isLCtrl = !!state[SDL_SCANCODE_LCTRL];
|
||||
|
||||
if(!isAlt && !isLShift && !isLCtrl)
|
||||
return false; // This is only case when return false
|
||||
|
||||
auto selected = owner->getSelection();
|
||||
if(!selected)
|
||||
return true; // Some Shortcusts are pressed but there are no appropriate actions
|
||||
|
||||
auto units = selected->myStack->count;
|
||||
if(units < 1)
|
||||
return true;
|
||||
|
||||
if (isLShift && isLCtrl && isAlt)
|
||||
{
|
||||
if(state[SDL_SCANCODE_LCTRL] && state[SDL_SCANCODE_LSHIFT])
|
||||
splitIntoParts(owner->getSelection()->upg, 1, 7);
|
||||
else if(state[SDL_SCANCODE_LCTRL])
|
||||
splitIntoParts(owner->getSelection()->upg, 1, 1);
|
||||
else if(state[SDL_SCANCODE_LSHIFT])
|
||||
splitIntoParts(owner->getSelection()->upg, owner->getSelection()->myStack->count/2 , 1);
|
||||
else
|
||||
return;
|
||||
owner->selectSlot(nullptr);
|
||||
owner->bulkMoveArmy(selected);
|
||||
}
|
||||
else if(isLCtrl && isAlt)
|
||||
{
|
||||
owner->moveStackToAnotherArmy(selected);
|
||||
}
|
||||
else if(isLShift && isAlt)
|
||||
{
|
||||
auto dismiss = getDismiss();
|
||||
if(dismiss)
|
||||
LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[12], dismiss, nullptr);
|
||||
}
|
||||
else if(isAlt)
|
||||
{
|
||||
owner->bulkMergeStacks(selected);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(units <= 1)
|
||||
return true;
|
||||
|
||||
if(isLCtrl && isLShift)
|
||||
owner->bulkSplitStack(selected);
|
||||
else if(isLShift)
|
||||
owner->bulkSmartSplitStack(selected);
|
||||
else
|
||||
splitIntoParts(selected->upg, 1); // LCtrl
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CGarrisonInt::addSplitBtn(std::shared_ptr<CButton> button)
|
||||
@@ -492,6 +544,115 @@ void CGarrisonInt::splitStacks(int, int amountRight)
|
||||
LOCPLINT->cb->splitStack(armedObjs[getSelection()->upg], armedObjs[pb], getSelection()->ID, p2, amountRight);
|
||||
}
|
||||
|
||||
bool CGarrisonInt::checkSelected(const CGarrisonSlot * selected, TQuantity min) const
|
||||
{
|
||||
return selected && selected->myStack && selected->myStack->count > min && selected->creature;
|
||||
}
|
||||
|
||||
void CGarrisonInt::moveStackToAnotherArmy(const CGarrisonSlot * selected)
|
||||
{
|
||||
if(!checkSelected(selected))
|
||||
return;
|
||||
|
||||
const auto srcArmyType = selected->upg;
|
||||
const auto destArmyType = srcArmyType == CGarrisonSlot::UP
|
||||
? CGarrisonSlot::DOWN
|
||||
: CGarrisonSlot::UP;
|
||||
|
||||
auto srcArmy = armedObjs[srcArmyType];
|
||||
auto destArmy = armedObjs[destArmyType];
|
||||
|
||||
if(!destArmy)
|
||||
return;
|
||||
|
||||
auto destSlot = destArmy->getSlotFor(selected->creature);
|
||||
|
||||
if(destSlot == SlotID())
|
||||
return;
|
||||
|
||||
const auto srcSlot = selected->ID;
|
||||
const bool isDestSlotEmpty = !destArmy->getStackCount(destSlot);
|
||||
|
||||
if(isDestSlotEmpty && !destArmy->getStackCount(srcSlot))
|
||||
destSlot = srcSlot; // Same place is more preferable
|
||||
|
||||
const bool isLastStack = srcArmy->stacksCount() == 1 && srcArmy->needsLastStack();
|
||||
auto srcAmount = selected->myStack->count - (isLastStack ? 1 : 0);
|
||||
|
||||
if(!srcAmount)
|
||||
return;
|
||||
|
||||
if(!isDestSlotEmpty || isLastStack)
|
||||
{
|
||||
srcAmount += destArmy->getStackCount(destSlot); // Due to 'split' implementation in the 'CGameHandler::arrangeStacks'
|
||||
LOCPLINT->cb->splitStack(srcArmy, destArmy, srcSlot, destSlot, srcAmount);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOCPLINT->cb->swapCreatures(srcArmy, destArmy, srcSlot, destSlot);
|
||||
}
|
||||
}
|
||||
|
||||
void CGarrisonInt::bulkMoveArmy(const CGarrisonSlot * selected)
|
||||
{
|
||||
if(!checkSelected(selected))
|
||||
return;
|
||||
|
||||
const auto srcArmyType = selected->upg;
|
||||
const auto destArmyType = (srcArmyType == CGarrisonSlot::UP)
|
||||
? CGarrisonSlot::DOWN
|
||||
: CGarrisonSlot::UP;
|
||||
|
||||
auto srcArmy = armedObjs[srcArmyType];
|
||||
auto destArmy = armedObjs[destArmyType];
|
||||
|
||||
if(!destArmy)
|
||||
return;
|
||||
|
||||
const auto srcSlot = selected->ID;
|
||||
LOCPLINT->cb->bulkMoveArmy(srcArmy->id, destArmy->id, srcSlot);
|
||||
}
|
||||
|
||||
void CGarrisonInt::bulkMergeStacks(const CGarrisonSlot * selected)
|
||||
{
|
||||
if(!checkSelected(selected))
|
||||
return;
|
||||
|
||||
const auto type = selected->upg;
|
||||
|
||||
if(!armedObjs[type]->hasCreatureSlots(selected->creature, selected->ID))
|
||||
return;
|
||||
|
||||
LOCPLINT->cb->bulkMergeStacks(armedObjs[type]->id, selected->ID);
|
||||
}
|
||||
|
||||
void CGarrisonInt::bulkSplitStack(const CGarrisonSlot * selected)
|
||||
{
|
||||
if(!checkSelected(selected, 1)) // check if > 1
|
||||
return;
|
||||
|
||||
const auto type = selected->upg;
|
||||
|
||||
if(!hasEmptySlot(type))
|
||||
return;
|
||||
|
||||
LOCPLINT->cb->bulkSplitStack(armedObjs[type]->id, selected->ID);
|
||||
}
|
||||
|
||||
void CGarrisonInt::bulkSmartSplitStack(const CGarrisonSlot * selected)
|
||||
{
|
||||
if(!checkSelected(selected, 1))
|
||||
return;
|
||||
|
||||
const auto type = selected->upg;
|
||||
|
||||
// Do not disturb the server if the creature is already balanced
|
||||
if(!hasEmptySlot(type) && armedObjs[type]->isCreatureBalanced(selected->creature))
|
||||
return;
|
||||
|
||||
LOCPLINT->cb->bulkSmartSplitStack(armedObjs[type]->id, selected->ID);
|
||||
}
|
||||
|
||||
CGarrisonInt::CGarrisonInt(int x, int y, int inx, const Point & garsOffset,
|
||||
const CArmedInstance * s1, const CArmedInstance * s2,
|
||||
bool _removableUnits, bool smallImgs, bool _twoRows)
|
||||
@@ -513,7 +674,7 @@ CGarrisonInt::CGarrisonInt(int x, int y, int inx, const Point & garsOffset,
|
||||
createSlots();
|
||||
}
|
||||
|
||||
const CGarrisonSlot * CGarrisonInt::getSelection()
|
||||
const CGarrisonSlot * CGarrisonInt::getSelection() const
|
||||
{
|
||||
return highlighted;
|
||||
}
|
||||
@@ -554,15 +715,15 @@ bool CGarrisonInt::getSplittingMode()
|
||||
return inSplittingMode;
|
||||
}
|
||||
|
||||
std::vector<CGarrisonSlot *> CGarrisonInt::getEmptySlots(CGarrisonSlot::EGarrisonType type)
|
||||
SlotID CGarrisonInt::getEmptySlot(CGarrisonSlot::EGarrisonType type) const
|
||||
{
|
||||
std::vector<CGarrisonSlot *> emptySlots;
|
||||
for(auto slot : availableSlots)
|
||||
{
|
||||
if(type == slot->upg && ((slot->our() || slot->ally()) && slot->creature == nullptr))
|
||||
emptySlots.push_back(slot.get());
|
||||
}
|
||||
return emptySlots;
|
||||
assert(armedObjs[type]);
|
||||
return armedObjs[type] ? armedObjs[type]->getFreeSlot() : SlotID();
|
||||
}
|
||||
|
||||
bool CGarrisonInt::hasEmptySlot(CGarrisonSlot::EGarrisonType type) const
|
||||
{
|
||||
return getEmptySlot(type) != SlotID();
|
||||
}
|
||||
|
||||
void CGarrisonInt::setArmy(const CArmedInstance * army, bool bottomGarrison)
|
||||
|
||||
Reference in New Issue
Block a user