1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-28 08:48:48 +02:00

Merge remote-tracking branch 'vcmi/beta' into develop

This commit is contained in:
Ivan Savenko 2023-09-08 18:49:06 +03:00
commit e8453916cf
54 changed files with 320 additions and 111 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 628 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 934 B

After

Width:  |  Height:  |  Size: 942 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 914 B

After

Width:  |  Height:  |  Size: 927 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -31,7 +31,7 @@
"vcmi.capitalColors.7" : "Pink", "vcmi.capitalColors.7" : "Pink",
"vcmi.radialWheel.mergeSameUnit" : "Merge same creatures", "vcmi.radialWheel.mergeSameUnit" : "Merge same creatures",
"vcmi.radialWheel.showUnitInformation" : "Show creature information", "vcmi.radialWheel.fillSingleUnit" : "Fill with single creatures",
"vcmi.radialWheel.splitSingleUnit" : "Split off single creature", "vcmi.radialWheel.splitSingleUnit" : "Split off single creature",
"vcmi.radialWheel.splitUnitEqually" : "Split creatures equally", "vcmi.radialWheel.splitUnitEqually" : "Split creatures equally",
"vcmi.radialWheel.moveUnit" : "Move creatures to another army", "vcmi.radialWheel.moveUnit" : "Move creatures to another army",

View File

@ -31,7 +31,6 @@
"vcmi.capitalColors.7" : "Rosa", "vcmi.capitalColors.7" : "Rosa",
"vcmi.radialWheel.mergeSameUnit" : "Gleiche Kreaturen zusammenführen", "vcmi.radialWheel.mergeSameUnit" : "Gleiche Kreaturen zusammenführen",
"vcmi.radialWheel.showUnitInformation" : "Informationen zur Kreatur anzeigen",
"vcmi.radialWheel.splitSingleUnit" : "Wegtrennen einzelner Kreaturen", "vcmi.radialWheel.splitSingleUnit" : "Wegtrennen einzelner Kreaturen",
"vcmi.radialWheel.splitUnitEqually" : "Gleichmäßiges trennen der Kreaturen", "vcmi.radialWheel.splitUnitEqually" : "Gleichmäßiges trennen der Kreaturen",
"vcmi.radialWheel.moveUnit" : "Verschieben der Kreatur in andere Armee", "vcmi.radialWheel.moveUnit" : "Verschieben der Kreatur in andere Armee",

View File

@ -31,7 +31,6 @@
"vcmi.capitalColors.7" : "Różowy", "vcmi.capitalColors.7" : "Różowy",
"vcmi.radialWheel.mergeSameUnit" : "Złącz takie same stworzenia", "vcmi.radialWheel.mergeSameUnit" : "Złącz takie same stworzenia",
"vcmi.radialWheel.showUnitInformation" : "Pokaż informacje o stworzeniu",
"vcmi.radialWheel.splitSingleUnit" : "Wydziel pojedyncze stworzenie", "vcmi.radialWheel.splitSingleUnit" : "Wydziel pojedyncze stworzenie",
"vcmi.radialWheel.splitUnitEqually" : "Podziel stworzenia równo", "vcmi.radialWheel.splitUnitEqually" : "Podziel stworzenia równo",
"vcmi.radialWheel.moveUnit" : "Przenieś stworzenia do innej armii", "vcmi.radialWheel.moveUnit" : "Przenieś stworzenia do innej armii",

View File

@ -31,7 +31,7 @@
"vcmi.capitalColors.7" : "Рожевий", "vcmi.capitalColors.7" : "Рожевий",
"vcmi.radialWheel.mergeSameUnit" : "Об'єднати однакових істот", "vcmi.radialWheel.mergeSameUnit" : "Об'єднати однакових істот",
"vcmi.radialWheel.showUnitInformation" : "Показати відомості про істоту", "vcmi.radialWheel.fillSingleUnit" : "Заповнити одиничними істотами",
"vcmi.radialWheel.splitSingleUnit" : "Відділити одну істоту", "vcmi.radialWheel.splitSingleUnit" : "Відділити одну істоту",
"vcmi.radialWheel.splitUnitEqually" : "Розділити істот порівну", "vcmi.radialWheel.splitUnitEqually" : "Розділити істот порівну",
"vcmi.radialWheel.moveUnit" : "Перемістити істоту до іншої армії", "vcmi.radialWheel.moveUnit" : "Перемістити істоту до іншої армії",

View File

@ -24,6 +24,7 @@
#include "../gui/CIntObject.h" #include "../gui/CIntObject.h"
#include "../gui/WindowHandler.h" #include "../gui/WindowHandler.h"
#include "../windows/CCreatureWindow.h" #include "../windows/CCreatureWindow.h"
#include "../windows/InfoWindows.h"
#include "../../CCallback.h" #include "../../CCallback.h"
#include "../../lib/CConfigHandler.h" #include "../../lib/CConfigHandler.h"
@ -607,10 +608,10 @@ bool BattleActionsController::actionIsLegal(PossiblePlayerBattleAction action, B
return false; return false;
case PossiblePlayerBattleAction::ANY_LOCATION: case PossiblePlayerBattleAction::ANY_LOCATION:
return isCastingPossibleHere(action.spell().toSpell(), targetStack, targetHex); return isCastingPossibleHere(action.spell().toSpell(), nullptr, targetHex);
case PossiblePlayerBattleAction::AIMED_SPELL_CREATURE: case PossiblePlayerBattleAction::AIMED_SPELL_CREATURE:
return !selectedStack && targetStack && isCastingPossibleHere(action.spell().toSpell(), targetStack, targetHex); return !selectedStack && targetStack && isCastingPossibleHere(action.spell().toSpell(), nullptr, targetHex);
case PossiblePlayerBattleAction::RANDOM_GENIE_SPELL: case PossiblePlayerBattleAction::RANDOM_GENIE_SPELL:
if(targetStack && targetStackOwned && targetStack != owner.stacksController->getActiveStack() && targetStack->alive()) //only positive spells for other allied creatures if(targetStack && targetStackOwned && targetStack != owner.stacksController->getActiveStack() && targetStack->alive()) //only positive spells for other allied creatures
@ -628,7 +629,7 @@ bool BattleActionsController::actionIsLegal(PossiblePlayerBattleAction action, B
case PossiblePlayerBattleAction::OBSTACLE: case PossiblePlayerBattleAction::OBSTACLE:
case PossiblePlayerBattleAction::FREE_LOCATION: case PossiblePlayerBattleAction::FREE_LOCATION:
return isCastingPossibleHere(action.spell().toSpell(), targetStack, targetHex); return isCastingPossibleHere(action.spell().toSpell(), nullptr, targetHex);
case PossiblePlayerBattleAction::CATAPULT: case PossiblePlayerBattleAction::CATAPULT:
return owner.siegeController && owner.siegeController->isAttackableByCatapult(targetHex); return owner.siegeController && owner.siegeController->isAttackableByCatapult(targetHex);
@ -1003,6 +1004,7 @@ void BattleActionsController::onHexRightClicked(BattleHex clickedHex)
if (spellcastingModeActive() || isCurrentStackInSpellcastMode) if (spellcastingModeActive() || isCurrentStackInSpellcastMode)
{ {
endCastingSpell(); endCastingSpell();
CRClickPopup::createAndPush(CGI->generaltexth->translate("core.genrltxt.731")); // spell cancelled
return; return;
} }

View File

@ -129,6 +129,9 @@ BattleFieldController::BattleFieldController(BattleInterface & owner):
attackCursors = GH.renderHandler().loadAnimation(AnimationPath::builtin("CRCOMBAT")); attackCursors = GH.renderHandler().loadAnimation(AnimationPath::builtin("CRCOMBAT"));
attackCursors->preload(); attackCursors->preload();
spellCursors = GH.renderHandler().loadAnimation(AnimationPath::builtin("CRSPELL"));
spellCursors->preload();
initializeHexEdgeMaskToFrameIndex(); initializeHexEdgeMaskToFrameIndex();
rangedFullDamageLimitImages = GH.renderHandler().loadAnimation(AnimationPath::builtin("battle/rangeHighlights/rangeHighlightsGreen.json")); rangedFullDamageLimitImages = GH.renderHandler().loadAnimation(AnimationPath::builtin("battle/rangeHighlights/rangeHighlightsGreen.json"));
@ -889,10 +892,22 @@ void BattleFieldController::show(Canvas & to)
if (isActive() && isGesturing() && getHoveredHex() != BattleHex::INVALID) if (isActive() && isGesturing() && getHoveredHex() != BattleHex::INVALID)
{ {
auto cursorIndex = CCS->curh->get<Cursor::Combat>(); auto combatCursorIndex = CCS->curh->get<Cursor::Combat>();
auto imageIndex = static_cast<size_t>(cursorIndex); if (combatCursorIndex)
{
auto combatImageIndex = static_cast<size_t>(*combatCursorIndex);
to.draw(attackCursors->getImage(combatImageIndex), hexPositionAbsolute(getHoveredHex()).center() - CCS->curh->getPivotOffsetCombat(combatImageIndex));
return;
}
auto spellCursorIndex = CCS->curh->get<Cursor::Spellcast>();
if (spellCursorIndex)
{
auto spellImageIndex = static_cast<size_t>(*spellCursorIndex);
to.draw(spellCursors->getImage(spellImageIndex), hexPositionAbsolute(getHoveredHex()).center() - CCS->curh->getPivotOffsetSpellcast());
return;
}
to.draw(attackCursors->getImage(imageIndex), hexPositionAbsolute(getHoveredHex()).center() - CCS->curh->getPivotOffsetCombat(imageIndex));
} }
} }

View File

@ -38,6 +38,7 @@ class BattleFieldController : public CIntObject
std::shared_ptr<CAnimation> shootingRangeLimitImages; std::shared_ptr<CAnimation> shootingRangeLimitImages;
std::shared_ptr<CAnimation> attackCursors; std::shared_ptr<CAnimation> attackCursors;
std::shared_ptr<CAnimation> spellCursors;
/// Canvas that contains background, hex grid (if enabled), absolute obstacles and movement range of active stack /// Canvas that contains background, hex grid (if enabled), absolute obstacles and movement range of active stack
std::unique_ptr<Canvas> backgroundWithHexes; std::unique_ptr<Canvas> backgroundWithHexes;

View File

@ -50,10 +50,10 @@ void InputSourceMouse::handleEventMouseButtonDown(const SDL_MouseButtonEvent & b
if(button.clicks > 1) if(button.clicks > 1)
GH.events().dispatchMouseDoubleClick(position); GH.events().dispatchMouseDoubleClick(position);
else else
GH.events().dispatchMouseLeftButtonPressed(position); GH.events().dispatchMouseLeftButtonPressed(position, 0);
break; break;
case SDL_BUTTON_RIGHT: case SDL_BUTTON_RIGHT:
GH.events().dispatchShowPopup(position); GH.events().dispatchShowPopup(position, 0);
break; break;
case SDL_BUTTON_MIDDLE: case SDL_BUTTON_MIDDLE:
middleClickPosition = position; middleClickPosition = position;
@ -74,7 +74,7 @@ void InputSourceMouse::handleEventMouseButtonUp(const SDL_MouseButtonEvent & but
switch(button.button) switch(button.button)
{ {
case SDL_BUTTON_LEFT: case SDL_BUTTON_LEFT:
GH.events().dispatchMouseLeftButtonReleased(position); GH.events().dispatchMouseLeftButtonReleased(position, 0);
break; break;
case SDL_BUTTON_RIGHT: case SDL_BUTTON_RIGHT:
GH.events().dispatchClosePopup(position); GH.events().dispatchClosePopup(position);

View File

@ -39,6 +39,7 @@ InputSourceTouch::InputSourceTouch()
params.relativeModeSpeedFactor = settings["general"]["relativePointerSpeedMultiplier"].Float(); params.relativeModeSpeedFactor = settings["general"]["relativePointerSpeedMultiplier"].Float();
params.longTouchTimeMilliseconds = settings["general"]["longTouchTimeMilliseconds"].Float(); params.longTouchTimeMilliseconds = settings["general"]["longTouchTimeMilliseconds"].Float();
params.hapticFeedbackEnabled = settings["general"]["hapticFeedback"].Bool(); params.hapticFeedbackEnabled = settings["general"]["hapticFeedback"].Bool();
params.touchToleranceDistance = settings["input"]["touchToleranceDistance"].Float();
if (params.useRelativeMode) if (params.useRelativeMode)
state = TouchState::RELATIVE_MODE; state = TouchState::RELATIVE_MODE;
@ -121,9 +122,9 @@ void InputSourceTouch::handleEventFingerDown(const SDL_TouchFingerEvent & tfinge
if(tfinger.x > 0.5) if(tfinger.x > 0.5)
{ {
if (tfinger.y < 0.5) if (tfinger.y < 0.5)
GH.events().dispatchShowPopup(GH.getCursorPosition()); GH.events().dispatchShowPopup(GH.getCursorPosition(), params.touchToleranceDistance);
else else
GH.events().dispatchMouseLeftButtonPressed(GH.getCursorPosition()); GH.events().dispatchMouseLeftButtonPressed(GH.getCursorPosition(), params.touchToleranceDistance);
} }
break; break;
} }
@ -168,7 +169,7 @@ void InputSourceTouch::handleEventFingerUp(const SDL_TouchFingerEvent & tfinger)
if (tfinger.y < 0.5) if (tfinger.y < 0.5)
GH.events().dispatchClosePopup(GH.getCursorPosition()); GH.events().dispatchClosePopup(GH.getCursorPosition());
else else
GH.events().dispatchMouseLeftButtonReleased(GH.getCursorPosition()); GH.events().dispatchMouseLeftButtonReleased(GH.getCursorPosition(), params.touchToleranceDistance);
} }
break; break;
} }
@ -180,8 +181,8 @@ void InputSourceTouch::handleEventFingerUp(const SDL_TouchFingerEvent & tfinger)
case TouchState::TAP_DOWN_SHORT: case TouchState::TAP_DOWN_SHORT:
{ {
GH.input().setCursorPosition(convertTouchToMouse(tfinger)); GH.input().setCursorPosition(convertTouchToMouse(tfinger));
GH.events().dispatchMouseLeftButtonPressed(convertTouchToMouse(tfinger)); GH.events().dispatchMouseLeftButtonPressed(convertTouchToMouse(tfinger), params.touchToleranceDistance);
GH.events().dispatchMouseLeftButtonReleased(convertTouchToMouse(tfinger)); GH.events().dispatchMouseLeftButtonReleased(convertTouchToMouse(tfinger), params.touchToleranceDistance);
state = TouchState::IDLE; state = TouchState::IDLE;
break; break;
} }
@ -230,7 +231,7 @@ void InputSourceTouch::handleUpdate()
uint32_t currentTime = SDL_GetTicks(); uint32_t currentTime = SDL_GetTicks();
if (currentTime > lastTapTimeTicks + params.longTouchTimeMilliseconds) if (currentTime > lastTapTimeTicks + params.longTouchTimeMilliseconds)
{ {
GH.events().dispatchShowPopup(GH.getCursorPosition()); GH.events().dispatchShowPopup(GH.getCursorPosition(), params.touchToleranceDistance);
if (GH.windows().isTopWindowPopup()) if (GH.windows().isTopWindowPopup())
{ {

View File

@ -78,6 +78,9 @@ struct TouchInputParameters
/// gesture will be qualified as pinch if distance between fingers is at least specified here /// gesture will be qualified as pinch if distance between fingers is at least specified here
uint32_t pinchSensitivityThreshold = 10; uint32_t pinchSensitivityThreshold = 10;
/// touch event will trigger clicking of elements up to X pixels away from actual touch position
uint32_t touchToleranceDistance = 20;
bool useRelativeMode = false; bool useRelativeMode = false;
bool hapticFeedbackEnabled = false; bool hapticFeedbackEnabled = false;

View File

@ -263,6 +263,11 @@ bool CIntObject::receiveEvent(const Point & position, int eventType) const
return pos.isInside(position); return pos.isInside(position);
} }
const Rect & CIntObject::getPosition() const
{
return pos;
}
void CIntObject::onScreenResize() void CIntObject::onScreenResize()
{ {
center(pos, true); center(pos, true);

View File

@ -111,6 +111,8 @@ public:
/// by default, usedEvents inside UI elements are always handled /// by default, usedEvents inside UI elements are always handled
bool receiveEvent(const Point & position, int eventType) const override; bool receiveEvent(const Point & position, int eventType) const override;
const Rect & getPosition() const override;
const Rect & center(const Rect &r, bool propagate = true); //sets pos so that r will be in the center of screen, assigns sizes of r to pos, returns new position const Rect & center(const Rect &r, bool propagate = true); //sets pos so that r will be in the center of screen, assigns sizes of r to pos, returns new position
const Rect & center(const Point &p, bool propagate = true); //moves object so that point p will be in its center const Rect & center(const Point &p, bool propagate = true); //moves object so that point p will be in its center
const Rect & center(bool propagate = true); //centers when pos.w and pos.h are set, returns new position const Rect & center(bool propagate = true); //centers when pos.w and pos.h are set, returns new position

View File

@ -126,7 +126,6 @@ class CursorHandler final
void changeGraphic(Cursor::Type type, size_t index); void changeGraphic(Cursor::Type type, size_t index);
Point getPivotOffsetSpellcast();
Point getPivotOffset(); Point getPivotOffset();
void updateSpellcastCursor(); void updateSpellcastCursor();
@ -154,7 +153,7 @@ public:
/// Returns current index of cursor /// Returns current index of cursor
template<typename Index> template<typename Index>
Index get() std::optional<Index> get()
{ {
bool typeValid = true; bool typeValid = true;
@ -165,9 +164,10 @@ public:
if (typeValid) if (typeValid)
return static_cast<Index>(frame); return static_cast<Index>(frame);
return Index::POINTER; return std::nullopt;
} }
Point getPivotOffsetSpellcast();
Point getPivotOffsetDefault(size_t index); Point getPivotOffsetDefault(size_t index);
Point getPivotOffsetMap(size_t index); Point getPivotOffsetMap(size_t index);
Point getPivotOffsetCombat(size_t index); Point getPivotOffsetCombat(size_t index);

View File

@ -16,7 +16,7 @@
#include "MouseButton.h" #include "MouseButton.h"
#include "WindowHandler.h" #include "WindowHandler.h"
#include "../../lib/Point.h" #include "../../lib/Rect.h"
template<typename Functor> template<typename Functor>
void EventDispatcher::processLists(ui16 activityFlag, const Functor & cb) void EventDispatcher::processLists(ui16 activityFlag, const Functor & cb)
@ -134,28 +134,64 @@ void EventDispatcher::dispatchMouseDoubleClick(const Point & position)
} }
if(!doubleClicked) if(!doubleClicked)
handleLeftButtonClick(position, true); handleLeftButtonClick(position, 0, true);
} }
void EventDispatcher::dispatchMouseLeftButtonPressed(const Point & position) void EventDispatcher::dispatchMouseLeftButtonPressed(const Point & position, int tolerance)
{ {
handleLeftButtonClick(position, true); handleLeftButtonClick(position, tolerance, true);
} }
void EventDispatcher::dispatchMouseLeftButtonReleased(const Point & position) void EventDispatcher::dispatchMouseLeftButtonReleased(const Point & position, int tolerance)
{ {
handleLeftButtonClick(position, false); handleLeftButtonClick(position, tolerance, false);
} }
void EventDispatcher::dispatchShowPopup(const Point & position) AEventsReceiver * EventDispatcher::findElementInToleranceRange(const EventReceiversList & list, const Point & position, int eventToTest, int tolerance)
{ {
AEventsReceiver * bestElement = nullptr;
int bestDistance = std::numeric_limits<int>::max();
for(auto & i : list)
{
// if there is element that can actually receive event then tolerance clicking is disabled
if( i->receiveEvent(position, eventToTest))
return nullptr;
if (i->getPosition().distanceTo(position) > bestDistance)
continue;
Point center = i->getPosition().center();
Point distance = center - position;
if (distance.lengthSquared() == 0)
continue;
Point moveDelta = distance * tolerance / distance.length();
Point testPosition = position + moveDelta;
if( !i->receiveEvent(testPosition, eventToTest))
continue;
bestElement = i;
bestDistance = i->getPosition().distanceTo(position);
}
return bestElement;
}
void EventDispatcher::dispatchShowPopup(const Point & position, int tolerance)
{
AEventsReceiver * nearestElement = findElementInToleranceRange(rclickable, position, AEventsReceiver::LCLICK, tolerance);
auto hlp = rclickable; auto hlp = rclickable;
for(auto & i : hlp) for(auto & i : hlp)
{ {
if(!vstd::contains(rclickable, i)) if(!vstd::contains(rclickable, i))
continue; continue;
if( !i->receiveEvent(position, AEventsReceiver::LCLICK)) if( !i->receiveEvent(position, AEventsReceiver::SHOW_POPUP) && i != nearestElement)
continue; continue;
i->showPopupWindow(position); i->showPopupWindow(position);
@ -170,7 +206,7 @@ void EventDispatcher::dispatchClosePopup(const Point & position)
assert(!GH.windows().isTopWindowPopup()); assert(!GH.windows().isTopWindowPopup());
} }
void EventDispatcher::handleLeftButtonClick(const Point & position, bool isPressed) void EventDispatcher::handleLeftButtonClick(const Point & position, int tolerance, bool isPressed)
{ {
// WARNING: this approach is NOT SAFE // WARNING: this approach is NOT SAFE
// 1) We allow (un)registering elements when list itself is being processed/iterated // 1) We allow (un)registering elements when list itself is being processed/iterated
@ -181,13 +217,15 @@ void EventDispatcher::handleLeftButtonClick(const Point & position, bool isPress
// 3) new element is created *with exactly same address(!) // 3) new element is created *with exactly same address(!)
// 4) new element is registered and code will incorrectly assume that this element is still registered // 4) new element is registered and code will incorrectly assume that this element is still registered
// POSSIBLE SOLUTION: make EventReceivers inherit from create_shared_from this and store weak_ptr's in lists // POSSIBLE SOLUTION: make EventReceivers inherit from create_shared_from this and store weak_ptr's in lists
AEventsReceiver * nearestElement = findElementInToleranceRange(lclickable, position, AEventsReceiver::LCLICK, tolerance);
auto hlp = lclickable; auto hlp = lclickable;
for(auto & i : hlp) for(auto & i : hlp)
{ {
if(!vstd::contains(lclickable, i)) if(!vstd::contains(lclickable, i))
continue; continue;
if( i->receiveEvent(position, AEventsReceiver::LCLICK)) if( i->receiveEvent(position, AEventsReceiver::LCLICK) || i == nearestElement)
{ {
if(isPressed) if(isPressed)
i->clickPressed(position); i->clickPressed(position);

View File

@ -35,8 +35,8 @@ class EventDispatcher
EventReceiversList textInterested; EventReceiversList textInterested;
EventReceiversList panningInterested; EventReceiversList panningInterested;
void handleLeftButtonClick(const Point & position, bool isPressed); void handleLeftButtonClick(const Point & position, int tolerance, bool isPressed);
AEventsReceiver * findElementInToleranceRange(const EventReceiversList & list, const Point & position, int eventToTest, int tolerance);
template<typename Functor> template<typename Functor>
void processLists(ui16 activityFlag, const Functor & cb); void processLists(ui16 activityFlag, const Functor & cb);
@ -56,15 +56,15 @@ public:
void dispatchShortcutReleased(const std::vector<EShortcut> & shortcuts); void dispatchShortcutReleased(const std::vector<EShortcut> & shortcuts);
/// Mouse events /// Mouse events
void dispatchMouseLeftButtonPressed(const Point & position); void dispatchMouseLeftButtonPressed(const Point & position, int tolerance);
void dispatchMouseLeftButtonReleased(const Point & position); void dispatchMouseLeftButtonReleased(const Point & position, int tolerance);
void dispatchMouseScrolled(const Point & distance, const Point & position); void dispatchMouseScrolled(const Point & distance, const Point & position);
void dispatchMouseDoubleClick(const Point & position); void dispatchMouseDoubleClick(const Point & position);
void dispatchMouseMoved(const Point & distance, const Point & position); void dispatchMouseMoved(const Point & distance, const Point & position);
void dispatchMouseDragged(const Point & currentPosition, const Point & lastUpdateDistance); void dispatchMouseDragged(const Point & currentPosition, const Point & lastUpdateDistance);
void dispatchShowPopup(const Point & position); void dispatchShowPopup(const Point & position, int tolerance);
void dispatchClosePopup(const Point & position); void dispatchClosePopup(const Point & position);
void dispatchGesturePanningStarted(const Point & initialPosition); void dispatchGesturePanningStarted(const Point & initialPosition);

View File

@ -11,6 +11,7 @@
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
class Point; class Point;
class Rect;
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END
class EventDispatcher; class EventDispatcher;
@ -39,6 +40,8 @@ protected:
/// If true, event of selected type in selected position will be processed by this element /// If true, event of selected type in selected position will be processed by this element
virtual bool receiveEvent(const Point & position, int eventType) const= 0; virtual bool receiveEvent(const Point & position, int eventType) const= 0;
virtual const Rect & getPosition() const= 0;
public: public:
virtual void clickPressed(const Point & cursorPosition) {} virtual void clickPressed(const Point & cursorPosition) {}
virtual void clickReleased(const Point & cursorPosition) {} virtual void clickReleased(const Point & cursorPosition) {}

View File

@ -9,4 +9,4 @@
*/ */
#pragma once #pragma once
enum class ETextAlignment {TOPLEFT, CENTER, BOTTOMRIGHT}; enum class ETextAlignment {TOPLEFT, TOPCENTER, CENTER, BOTTOMRIGHT};

View File

@ -151,6 +151,7 @@ void Canvas::drawText(const Point & position, const EFonts & font, const ColorRG
switch (alignment) switch (alignment)
{ {
case ETextAlignment::TOPLEFT: return graphics->fonts[font]->renderTextLeft (surface, text, colorDest, renderArea.topLeft() + position); case ETextAlignment::TOPLEFT: return graphics->fonts[font]->renderTextLeft (surface, text, colorDest, renderArea.topLeft() + position);
case ETextAlignment::TOPCENTER: return graphics->fonts[font]->renderTextCenter(surface, text, colorDest, renderArea.topLeft() + position);
case ETextAlignment::CENTER: return graphics->fonts[font]->renderTextCenter(surface, text, colorDest, renderArea.topLeft() + position); case ETextAlignment::CENTER: return graphics->fonts[font]->renderTextCenter(surface, text, colorDest, renderArea.topLeft() + position);
case ETextAlignment::BOTTOMRIGHT: return graphics->fonts[font]->renderTextRight (surface, text, colorDest, renderArea.topLeft() + position); case ETextAlignment::BOTTOMRIGHT: return graphics->fonts[font]->renderTextRight (surface, text, colorDest, renderArea.topLeft() + position);
} }
@ -161,6 +162,7 @@ void Canvas::drawText(const Point & position, const EFonts & font, const ColorRG
switch (alignment) switch (alignment)
{ {
case ETextAlignment::TOPLEFT: return graphics->fonts[font]->renderTextLinesLeft (surface, text, colorDest, renderArea.topLeft() + position); case ETextAlignment::TOPLEFT: return graphics->fonts[font]->renderTextLinesLeft (surface, text, colorDest, renderArea.topLeft() + position);
case ETextAlignment::TOPCENTER: return graphics->fonts[font]->renderTextLinesCenter(surface, text, colorDest, renderArea.topLeft() + position);
case ETextAlignment::CENTER: return graphics->fonts[font]->renderTextLinesCenter(surface, text, colorDest, renderArea.topLeft() + position); case ETextAlignment::CENTER: return graphics->fonts[font]->renderTextLinesCenter(surface, text, colorDest, renderArea.topLeft() + position);
case ETextAlignment::BOTTOMRIGHT: return graphics->fonts[font]->renderTextLinesRight (surface, text, colorDest, renderArea.topLeft() + position); case ETextAlignment::BOTTOMRIGHT: return graphics->fonts[font]->renderTextLinesRight (surface, text, colorDest, renderArea.topLeft() + position);
} }

View File

@ -117,6 +117,13 @@ void CButton::setState(ButtonState newState)
{ {
if (state == newState) if (state == newState)
return; return;
if (newState == BLOCKED)
removeUsedEvents(LCLICK | SHOW_POPUP | HOVER | KEYBOARD);
else
addUsedEvents(LCLICK | SHOW_POPUP | HOVER | KEYBOARD);
state = newState; state = newState;
update(); update();
} }

View File

@ -11,12 +11,9 @@
#include "CArtifactsOfHeroBackpack.h" #include "CArtifactsOfHeroBackpack.h"
#include "../gui/CGuiHandler.h" #include "../gui/CGuiHandler.h"
#include "../gui/Shortcut.h"
#include "Buttons.h"
#include "Images.h" #include "Images.h"
#include "GameSettings.h" #include "GameSettings.h"
#include "IHandlerBase.h"
#include "ObjectLists.h" #include "ObjectLists.h"
#include "../CPlayerInterface.h" #include "../CPlayerInterface.h"
@ -37,21 +34,12 @@ CArtifactsOfHeroBackpack::CArtifactsOfHeroBackpack(const Point & position)
backpack.resize(visibleCapacityMax); backpack.resize(visibleCapacityMax);
size_t artPlaceIdx = 0; size_t artPlaceIdx = 0;
const int slotSizeWithMargin = 46;
for(int i = 0; i < visibleCapacityMax; i++)
{
auto artifactSlotBackground = std::make_shared<CPicture>( ImagePath::builtin("heroWindow/artifactSlotEmpty"),
Point(slotSizeWithMargin * (i % HERO_BACKPACK_WINDOW_SLOT_COLUMNS), slotSizeWithMargin * (i / HERO_BACKPACK_WINDOW_SLOT_COLUMNS)));
backpackSlotsBackgrounds.emplace_back(artifactSlotBackground);
}
for(auto & artPlace : backpack) for(auto & artPlace : backpack)
{ {
artPlace = std::make_shared<CHeroArtPlace>( const auto pos = Point(slotSizeWithMargin * (artPlaceIdx % HERO_BACKPACK_WINDOW_SLOT_COLUMNS),
Point(slotSizeWithMargin * (artPlaceIdx % HERO_BACKPACK_WINDOW_SLOT_COLUMNS), slotSizeWithMargin * (artPlaceIdx / HERO_BACKPACK_WINDOW_SLOT_COLUMNS))); slotSizeWithMargin * (artPlaceIdx / HERO_BACKPACK_WINDOW_SLOT_COLUMNS));
backpackSlotsBackgrounds.emplace_back(std::make_shared<CPicture>(ImagePath::builtin("heroWindow/artifactSlotEmpty"), pos));
artPlace = std::make_shared<CHeroArtPlace>(pos);
artPlace->setArtifact(nullptr); artPlace->setArtifact(nullptr);
artPlace->leftClickCallback = std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1); artPlace->leftClickCallback = std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1);
artPlace->rightClickCallback = std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1); artPlace->rightClickCallback = std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1);
@ -70,8 +58,18 @@ CArtifactsOfHeroBackpack::CArtifactsOfHeroBackpack(const Point & position)
}; };
backpackListBox = std::make_shared<CListBoxWithCallback>( backpackListBox = std::make_shared<CListBoxWithCallback>(
posMoved, onCreate, Point(0, 0), Point(0, 0), HERO_BACKPACK_WINDOW_SLOT_ROWS, 0, 0, 1, posMoved, onCreate, Point(0, 0), Point(0, 0), HERO_BACKPACK_WINDOW_SLOT_ROWS, 0, 0, 1,
Rect(HERO_BACKPACK_WINDOW_SLOT_COLUMNS * slotSizeWithMargin + 10, 0, HERO_BACKPACK_WINDOW_SLOT_ROWS * slotSizeWithMargin - 5, 0)); Rect(HERO_BACKPACK_WINDOW_SLOT_COLUMNS * slotSizeWithMargin + sliderPosOffsetX, 0, HERO_BACKPACK_WINDOW_SLOT_ROWS * slotSizeWithMargin - 2, 0));
} }
pos.w = visibleCapacityMax > HERO_BACKPACK_WINDOW_SLOT_COLUMNS ? HERO_BACKPACK_WINDOW_SLOT_COLUMNS : visibleCapacityMax;
pos.w *= slotSizeWithMargin;
if(backpackListBox)
pos.w += sliderPosOffsetX + 16; // 16 is slider width. TODO: get it from CListBox directly;
pos.h = (visibleCapacityMax / HERO_BACKPACK_WINDOW_SLOT_COLUMNS);
if(visibleCapacityMax % HERO_BACKPACK_WINDOW_SLOT_COLUMNS != 0)
pos.h += 1;
pos.h *= slotSizeWithMargin;
} }
void CArtifactsOfHeroBackpack::swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc) void CArtifactsOfHeroBackpack::swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc)
@ -88,7 +86,7 @@ void CArtifactsOfHeroBackpack::pickUpArtifact(CHeroArtPlace & artPlace)
void CArtifactsOfHeroBackpack::scrollBackpack(int offset) void CArtifactsOfHeroBackpack::scrollBackpack(int offset)
{ {
if(backpackListBox) if(backpackListBox)
backpackListBox->resize(getActiveSlotLinesNum()); backpackListBox->resize(getActiveSlotRowsNum());
backpackPos += offset; backpackPos += offset;
auto slot = ArtifactPosition::BACKPACK_START + backpackPos; auto slot = ArtifactPosition::BACKPACK_START + backpackPos;
for(auto artPlace : backpack) for(auto artPlace : backpack)
@ -102,11 +100,11 @@ void CArtifactsOfHeroBackpack::scrollBackpack(int offset)
void CArtifactsOfHeroBackpack::updateBackpackSlots() void CArtifactsOfHeroBackpack::updateBackpackSlots()
{ {
if(backpackListBox) if(backpackListBox)
backpackListBox->resize(getActiveSlotLinesNum()); backpackListBox->resize(getActiveSlotRowsNum());
CArtifactsOfHeroBase::updateBackpackSlots(); CArtifactsOfHeroBase::updateBackpackSlots();
} }
size_t CArtifactsOfHeroBackpack::getActiveSlotLinesNum() size_t CArtifactsOfHeroBackpack::getActiveSlotRowsNum()
{ {
return (curHero->artifactsInBackpack.size() + HERO_BACKPACK_WINDOW_SLOT_COLUMNS - 1) / HERO_BACKPACK_WINDOW_SLOT_COLUMNS; return (curHero->artifactsInBackpack.size() + HERO_BACKPACK_WINDOW_SLOT_COLUMNS - 1) / HERO_BACKPACK_WINDOW_SLOT_COLUMNS;
} }

View File

@ -27,11 +27,13 @@ public:
void pickUpArtifact(CHeroArtPlace & artPlace); void pickUpArtifact(CHeroArtPlace & artPlace);
void scrollBackpack(int offset) override; void scrollBackpack(int offset) override;
void updateBackpackSlots() override; void updateBackpackSlots() override;
size_t getActiveSlotLinesNum(); size_t getActiveSlotRowsNum();
private: private:
std::shared_ptr<CListBoxWithCallback> backpackListBox; std::shared_ptr<CListBoxWithCallback> backpackListBox;
std::vector<std::shared_ptr<CPicture>> backpackSlotsBackgrounds; std::vector<std::shared_ptr<CPicture>> backpackSlotsBackgrounds;
const size_t HERO_BACKPACK_WINDOW_SLOT_COLUMNS = 8; const size_t HERO_BACKPACK_WINDOW_SLOT_COLUMNS = 8;
const size_t HERO_BACKPACK_WINDOW_SLOT_ROWS = 8; const size_t HERO_BACKPACK_WINDOW_SLOT_ROWS = 8;
const int slotSizeWithMargin = 46;
const int sliderPosOffsetX = 10;
}; };

View File

@ -369,13 +369,14 @@ void CGarrisonSlot::gesture(bool on, const Point & initialPosition, const Point
std::vector<RadialMenuConfig> menuElements = { std::vector<RadialMenuConfig> menuElements = {
{ RadialMenuConfig::ITEM_NW, hasSameUnit, "stackMerge", "vcmi.radialWheel.mergeSameUnit", [this](){owner->bulkMergeStacks(this);} }, { RadialMenuConfig::ITEM_NW, hasSameUnit, "stackMerge", "vcmi.radialWheel.mergeSameUnit", [this](){owner->bulkMergeStacks(this);} },
{ RadialMenuConfig::ITEM_NE, stackExists, "stackInfo", "vcmi.radialWheel.showUnitInformation", [this](){viewInfo();} }, { RadialMenuConfig::ITEM_NE, hasOwnEmptySlots, "stackFillOne", "vcmi.radialWheel.fillSingleUnit", [this](){owner->bulkSplitStack(this);} },
{ RadialMenuConfig::ITEM_WW, hasOwnEmptySlots, "stackSplitOne", "vcmi.radialWheel.splitSingleUnit", [this](){splitIntoParts(this->getGarrison(), 1); } }, { RadialMenuConfig::ITEM_WW, hasOwnEmptySlots, "stackSplitOne", "vcmi.radialWheel.splitSingleUnit", [this](){splitIntoParts(this->getGarrison(), 1); } },
{ RadialMenuConfig::ITEM_EE, hasOwnEmptySlots, "stackSplitEqual", "vcmi.radialWheel.splitUnitEqually", [this](){owner->bulkSmartSplitStack(this);} }, { RadialMenuConfig::ITEM_EE, hasOwnEmptySlots, "stackSplitEqual", "vcmi.radialWheel.splitUnitEqually", [this](){owner->bulkSmartSplitStack(this);} },
{ RadialMenuConfig::ITEM_SW, hasOtherEmptySlots, "heroMove", "vcmi.radialWheel.moveUnit", [this](){owner->moveStackToAnotherArmy(this);} }, { RadialMenuConfig::ITEM_SW, hasOtherEmptySlots, "heroMove", "vcmi.radialWheel.moveUnit", [this](){owner->moveStackToAnotherArmy(this);} },
{ RadialMenuConfig::ITEM_SE, hasAnyEmptySlots, "heroSwap", "vcmi.radialWheel.splitUnit", [this](){ owner->selectSlot(this); owner->splitClick();} }, { RadialMenuConfig::ITEM_SE, hasAnyEmptySlots, "heroSwap", "vcmi.radialWheel.splitUnit", [this](){ owner->selectSlot(this); owner->splitClick();} },
}; };
if (hasAnyEmptySlots || hasSameUnit)
GH.windows().createAndPushWindow<RadialMenu>(pos.center(), menuElements); GH.windows().createAndPushWindow<RadialMenu>(pos.center(), menuElements);
} }

View File

@ -17,11 +17,13 @@
#include "../CPlayerInterface.h" #include "../CPlayerInterface.h"
#include "../CGameInfo.h" #include "../CGameInfo.h"
#include "../PlayerLocalState.h"
#include "../widgets/TextControls.h" #include "../widgets/TextControls.h"
#include "../widgets/CGarrisonInt.h" #include "../widgets/CGarrisonInt.h"
#include "../windows/CCastleInterface.h" #include "../windows/CCastleInterface.h"
#include "../windows/InfoWindows.h" #include "../windows/InfoWindows.h"
#include "../render/Canvas.h" #include "../render/Canvas.h"
#include "../render/Graphics.h"
#include "../../CCallback.h" #include "../../CCallback.h"
@ -30,6 +32,7 @@
#include "../../lib/CGeneralTextHandler.h" #include "../../lib/CGeneralTextHandler.h"
#include "../../lib/GameSettings.h" #include "../../lib/GameSettings.h"
#include "../../lib/TextOperations.h" #include "../../lib/TextOperations.h"
#include "../../lib/mapObjects/CGCreature.h"
#include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/mapObjects/CGTownInstance.h" #include "../../lib/mapObjects/CGTownInstance.h"
@ -447,6 +450,27 @@ void CInteractableTownTooltip::init(const InfoAboutTown & town)
} }
} }
CreatureTooltip::CreatureTooltip(Point pos, const CGCreature * creature)
{
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
auto creatureData = (*CGI->creh)[creature->stacks.begin()->second->getCreatureID()].get();
creatureImage = std::make_shared<CAnimImage>(graphics->getAnimation(AnimationPath::builtin("TWCRPORT")), creatureData->getIconIndex());
creatureImage->center(Point(parent->pos.x + parent->pos.w / 2, parent->pos.y + creatureImage->pos.h / 2 + 11));
bool isHeroSelected = LOCPLINT->localState->getCurrentHero() != nullptr;
std::string textContent = isHeroSelected
? creature->getHoverText(LOCPLINT->localState->getCurrentHero())
: creature->getHoverText(LOCPLINT->playerID);
//TODO: window is bigger than OH3
//TODO: vertical alignment does not match H3. Commented below example that matches H3 for creatures count but supports only 1 line:
/*std::shared_ptr<CLabel> = std::make_shared<CLabel>(parent->pos.w / 2, 103,
FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, creature->getHoverText(LOCPLINT->playerID));*/
tooltipTextbox = std::make_shared<CTextBox>(textContent, Rect(15, 95, 230, 150), 0, FONT_SMALL, ETextAlignment::TOPCENTER, Colors::WHITE);
}
void MoraleLuckBox::set(const AFactionMember * node) void MoraleLuckBox::set(const AFactionMember * node)
{ {
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);

View File

@ -14,6 +14,7 @@
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
class CGGarrison; class CGGarrison;
class CGCreature;
struct InfoAboutArmy; struct InfoAboutArmy;
struct InfoAboutHero; struct InfoAboutHero;
struct InfoAboutTown; struct InfoAboutTown;
@ -25,6 +26,7 @@ class AFactionMember;
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END
class CLabel; class CLabel;
class CTextBox;
class CGarrisonInt; class CGarrisonInt;
class CCreatureAnim; class CCreatureAnim;
class CComponent; class CComponent;
@ -151,6 +153,15 @@ public:
void setAmount(int newAmount); void setAmount(int newAmount);
}; };
class CreatureTooltip : public CIntObject
{
std::shared_ptr<CAnimImage> creatureImage;
std::shared_ptr<CTextBox> tooltipTextbox;
public:
CreatureTooltip(Point pos, const CGCreature * creature);
};
/// Resource bar like that at the bottom of the adventure map screen /// Resource bar like that at the bottom of the adventure map screen
class CMinorResDataBar : public CIntObject class CMinorResDataBar : public CIntObject
{ {

View File

@ -161,6 +161,12 @@ void CTextContainer::blitLine(Canvas & to, Rect destRect, std::string what)
where.y += getBorderSize().y; where.y += getBorderSize().y;
} }
if(alignment == ETextAlignment::TOPCENTER)
{
where.x += (int(destRect.w) - int(f->getStringWidth(what) - delimitersCount)) / 2;
where.y += getBorderSize().y;
}
if(alignment == ETextAlignment::CENTER) if(alignment == ETextAlignment::CENTER)
{ {
where.x += (int(destRect.w) - int(f->getStringWidth(what) - delimitersCount)) / 2; where.x += (int(destRect.w) - int(f->getStringWidth(what) - delimitersCount)) / 2;
@ -271,6 +277,7 @@ Rect CMultiLineLabel::getTextLocation()
switch(alignment) switch(alignment)
{ {
case ETextAlignment::TOPLEFT: return Rect(pos.topLeft(), textSize); case ETextAlignment::TOPLEFT: return Rect(pos.topLeft(), textSize);
case ETextAlignment::TOPCENTER: return Rect(pos.topLeft(), textSize);
case ETextAlignment::CENTER: return Rect(pos.topLeft() + textOffset / 2, textSize); case ETextAlignment::CENTER: return Rect(pos.topLeft() + textOffset / 2, textSize);
case ETextAlignment::BOTTOMRIGHT: return Rect(pos.topLeft() + textOffset, textSize); case ETextAlignment::BOTTOMRIGHT: return Rect(pos.topLeft() + textOffset, textSize);
} }
@ -480,6 +487,7 @@ Point CGStatusBar::getBorderSize()
switch(alignment) switch(alignment)
{ {
case ETextAlignment::TOPLEFT: return Point(borderSize.x, borderSize.y); case ETextAlignment::TOPLEFT: return Point(borderSize.x, borderSize.y);
case ETextAlignment::TOPCENTER: return Point(pos.w / 2, borderSize.y);
case ETextAlignment::CENTER: return Point(pos.w / 2, pos.h / 2); case ETextAlignment::CENTER: return Point(pos.w / 2, pos.h / 2);
case ETextAlignment::BOTTOMRIGHT: return Point(pos.w - borderSize.x, pos.h - borderSize.y); case ETextAlignment::BOTTOMRIGHT: return Point(pos.w - borderSize.x, pos.h - borderSize.y);
} }

View File

@ -15,6 +15,7 @@
#include "../CGameInfo.h" #include "../CGameInfo.h"
#include "../CPlayerInterface.h" #include "../CPlayerInterface.h"
#include "../render/Canvas.h"
#include "../widgets/Buttons.h" #include "../widgets/Buttons.h"
#include "../widgets/CArtifactHolder.h" #include "../widgets/CArtifactHolder.h"
#include "../widgets/CComponent.h" #include "../widgets/CComponent.h"
@ -83,7 +84,6 @@ public:
{ {
} }
std::string getName() const std::string getName() const
{ {
if(commander) if(commander)
@ -95,11 +95,14 @@ private:
}; };
CCommanderSkillIcon::CCommanderSkillIcon(std::shared_ptr<CIntObject> object_, std::function<void()> callback) CCommanderSkillIcon::CCommanderSkillIcon(std::shared_ptr<CIntObject> object_, bool isGrandmasterAbility_, std::function<void()> callback)
: object(), : object(),
isGrandmasterAbility(isGrandmasterAbility_),
isSelected(false),
callback(callback) callback(callback)
{ {
pos = object_->pos; pos = object_->pos;
this->isGrandmasterAbility = isGrandmasterAbility_;
setObject(object_); setObject(object_);
} }
@ -116,6 +119,25 @@ void CCommanderSkillIcon::setObject(std::shared_ptr<CIntObject> newObject)
void CCommanderSkillIcon::clickPressed(const Point & cursorPosition) void CCommanderSkillIcon::clickPressed(const Point & cursorPosition)
{ {
callback(); callback();
isSelected = true;
}
void CCommanderSkillIcon::deselect()
{
isSelected = false;
}
bool CCommanderSkillIcon::getIsGrandmasterAbility()
{
return isGrandmasterAbility;
}
void CCommanderSkillIcon::show(Canvas &to)
{
CIntObject::show(to);
if(isGrandmasterAbility && isSelected)
to.drawBorder(pos, Colors::YELLOW, 2);
} }
static ImagePath skillToFile(int skill, int level, bool selected) static ImagePath skillToFile(int skill, int level, bool selected)
@ -375,7 +397,7 @@ CStackWindow::CommanderMainSection::CommanderMainSection(CStackWindow * owner, i
{ {
Point skillPos = getSkillPos(index); Point skillPos = getSkillPos(index);
auto icon = std::make_shared<CCommanderSkillIcon>(std::make_shared<CPicture>(getSkillImage(index), skillPos.x, skillPos.y), [=]() auto icon = std::make_shared<CCommanderSkillIcon>(std::make_shared<CPicture>(getSkillImage(index), skillPos.x, skillPos.y), false, [=]()
{ {
LOCPLINT->showInfoDialog(getSkillDescription(index)); LOCPLINT->showInfoDialog(getSkillDescription(index));
}); });
@ -429,7 +451,7 @@ CStackWindow::CommanderMainSection::CommanderMainSection(CStackWindow * owner, i
{ {
const auto bonus = CGI->creh->skillRequirements[skillID-100].first; const auto bonus = CGI->creh->skillRequirements[skillID-100].first;
const CStackInstance * stack = parent->info->commander; const CStackInstance * stack = parent->info->commander;
auto icon = std::make_shared<CCommanderSkillIcon>(std::make_shared<CPicture>(stack->bonusToGraphics(bonus)), [](){}); auto icon = std::make_shared<CCommanderSkillIcon>(std::make_shared<CPicture>(stack->bonusToGraphics(bonus)), true, [](){});
icon->callback = [=]() icon->callback = [=]()
{ {
parent->setSelection(skillID, icon); parent->setSelection(skillID, icon);
@ -446,6 +468,7 @@ CStackWindow::CommanderMainSection::CommanderMainSection(CStackWindow * owner, i
}; };
abilities = std::make_shared<CListBox>(onCreate, Point(38, 3+pos.h), Point(63, 0), 6, abilitiesCount); abilities = std::make_shared<CListBox>(onCreate, Point(38, 3+pos.h), Point(63, 0), 6, abilitiesCount);
abilities->setRedrawParent(true);
leftBtn = std::make_shared<CButton>(Point(10, pos.h + 6), AnimationPath::builtin("hsbtns3.def"), CButton::tooltip(), [=](){ abilities->moveToPrev(); }, EShortcut::MOVE_LEFT); leftBtn = std::make_shared<CButton>(Point(10, pos.h + 6), AnimationPath::builtin("hsbtns3.def"), CButton::tooltip(), [=](){ abilities->moveToPrev(); }, EShortcut::MOVE_LEFT);
rightBtn = std::make_shared<CButton>(Point(411, pos.h + 6), AnimationPath::builtin("hsbtns5.def"), CButton::tooltip(), [=](){ abilities->moveToNext(); }, EShortcut::MOVE_RIGHT); rightBtn = std::make_shared<CButton>(Point(411, pos.h + 6), AnimationPath::builtin("hsbtns5.def"), CButton::tooltip(), [=](){ abilities->moveToNext(); }, EShortcut::MOVE_RIGHT);
@ -896,14 +919,23 @@ void CStackWindow::setSelection(si32 newSkill, std::shared_ptr<CCommanderSkillIc
selectedIcon->setObject(std::make_shared<CPicture>(getSkillImage(oldSelection))); selectedIcon->setObject(std::make_shared<CPicture>(getSkillImage(oldSelection)));
if(selectedIcon) if(selectedIcon)
{
if(!selectedIcon->getIsGrandmasterAbility()) //unlike WoG, in VCMI grandmaster skill descriptions are taken from bonus descriptions
{
selectedIcon->text = getSkillDescription(oldSelection, false); //update previously selected icon's message to existing skill level selectedIcon->text = getSkillDescription(oldSelection, false); //update previously selected icon's message to existing skill level
}
selectedIcon->deselect();
}
selectedIcon = newIcon; // update new selection selectedIcon = newIcon; // update new selection
if(newSkill < 100) if(newSkill < 100)
{ {
newIcon->setObject(std::make_shared<CPicture>(getSkillImage(newSkill))); newIcon->setObject(std::make_shared<CPicture>(getSkillImage(newSkill)));
if(!newIcon->getIsGrandmasterAbility())
{
newIcon->text = getSkillDescription(newSkill, true); //update currently selected icon's message to show upgrade description newIcon->text = getSkillDescription(newSkill, true); //update currently selected icon's message to show upgrade description
} }
}
} }
std::shared_ptr<CIntObject> CStackWindow::switchTab(size_t index) std::shared_ptr<CIntObject> CStackWindow::switchTab(size_t index)

View File

@ -33,14 +33,20 @@ class CCommanderArtPlace;
class CCommanderSkillIcon : public LRClickableAreaWText //TODO: maybe bring commander skill button initialization logic inside? class CCommanderSkillIcon : public LRClickableAreaWText //TODO: maybe bring commander skill button initialization logic inside?
{ {
std::shared_ptr<CIntObject> object; // passive object that will be used to determine clickable area std::shared_ptr<CIntObject> object; // passive object that will be used to determine clickable area
bool isGrandmasterAbility; // refers to WoG abilities obtainable via combining grandmaster skills (for example attack + speed unlocks shoot)
bool isSelected; // used only for programatically created border around selected "grandmaster abilities"
public: public:
CCommanderSkillIcon(std::shared_ptr<CIntObject> object_, std::function<void()> callback); CCommanderSkillIcon(std::shared_ptr<CIntObject> object_, bool isGrandmasterAbility, std::function<void()> callback);
std::function<void()> callback; std::function<void()> callback;
void clickPressed(const Point & cursorPosition) override; void clickPressed(const Point & cursorPosition) override;
void setObject(std::shared_ptr<CIntObject> object); void setObject(std::shared_ptr<CIntObject> object);
void deselect(); //TODO: consider using observer pattern instead?
bool getIsGrandmasterAbility();
void show(Canvas &to) override;
}; };
class CStackWindow : public CWindowObject class CStackWindow : public CWindowObject

View File

@ -25,21 +25,25 @@ CHeroBackpackWindow::CHeroBackpackWindow(const CGHeroInstance * hero)
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
stretchedBackground = std::make_shared<CFilledTexture>(ImagePath::builtin("DIBOXBCK"), Rect(0, 0, 410, 425)); stretchedBackground = std::make_shared<CFilledTexture>(ImagePath::builtin("DIBOXBCK"), Rect(0, 0, 410, 425));
pos.w = stretchedBackground->pos.w;
pos.h = stretchedBackground->pos.h;
center();
arts = std::make_shared<CArtifactsOfHeroBackpack>(Point(windowMargin, windowMargin));
arts = std::make_shared<CArtifactsOfHeroBackpack>(/*Point(-100, -170)*/Point(10, 10));
arts->setHero(hero); arts->setHero(hero);
addSet(arts); addSet(arts);
addCloseCallback(std::bind(&CHeroBackpackWindow::close, this)); addCloseCallback(std::bind(&CHeroBackpackWindow::close, this));
quitButton = std::make_shared<CButton>(Point(173, 385), AnimationPath::builtin("IOKAY32.def"), CButton::tooltip(""), [this]() { close(); }, EShortcut::GLOBAL_RETURN); quitButton = std::make_shared<CButton>(Point(), AnimationPath::builtin("IOKAY32.def"), CButton::tooltip(""), [this]() { close(); }, EShortcut::GLOBAL_RETURN);
stretchedBackground->pos.w = arts->pos.w + 2 * windowMargin;
stretchedBackground->pos.h = arts->pos.h + quitButton->pos.h + 3 * windowMargin;
pos.w = stretchedBackground->pos.w;
pos.h = stretchedBackground->pos.h;
center();
quitButton->moveBy(Point(GH.screenDimensions().x / 2 - quitButton->pos.w / 2 - quitButton->pos.x, arts->pos.h + 2 * windowMargin));
} }
void CHeroBackpackWindow::showAll(Canvas &to) void CHeroBackpackWindow::showAll(Canvas & to)
{ {
CIntObject::showAll(to); CIntObject::showAll(to);
CMessage::drawBorder(PlayerColor(LOCPLINT->playerID), to.getInternalSurface(), pos.w+28, pos.h+29, pos.x-14, pos.y-15); CMessage::drawBorder(PlayerColor(LOCPLINT->playerID), to.getInternalSurface(), pos.w+28, pos.h+29, pos.x-14, pos.y-15);

View File

@ -23,6 +23,7 @@ private:
std::shared_ptr<CArtifactsOfHeroBackpack> arts; std::shared_ptr<CArtifactsOfHeroBackpack> arts;
std::shared_ptr<CButton> quitButton; std::shared_ptr<CButton> quitButton;
std::shared_ptr<CFilledTexture> stretchedBackground; std::shared_ptr<CFilledTexture> stretchedBackground;
const int windowMargin = 10;
void showAll(Canvas &to) override; void showAll(Canvas & to) override;
}; };

View File

@ -89,6 +89,7 @@ CHeroWindow::CHeroWindow(const CGHeroInstance * hero)
{ {
questlogButton = std::make_shared<CButton>(Point(314, 429), AnimationPath::builtin("hsbtns4.def"), CButton::tooltip(heroscrn[0]), [=](){ LOCPLINT->showQuestLog(); }, EShortcut::ADVENTURE_QUEST_LOG); questlogButton = std::make_shared<CButton>(Point(314, 429), AnimationPath::builtin("hsbtns4.def"), CButton::tooltip(heroscrn[0]), [=](){ LOCPLINT->showQuestLog(); }, EShortcut::ADVENTURE_QUEST_LOG);
backpackButton = std::make_shared<CButton>(Point(424, 429), AnimationPath::builtin("buttons/backpack"), CButton::tooltipLocalized("vcmi.heroWindow.Backpack"), [=](){ createBackpackWindow(); }, EShortcut::HERO_BACKPACK); backpackButton = std::make_shared<CButton>(Point(424, 429), AnimationPath::builtin("buttons/backpack"), CButton::tooltipLocalized("vcmi.heroWindow.Backpack"), [=](){ createBackpackWindow(); }, EShortcut::HERO_BACKPACK);
backpackButton->addOverlay(std::make_shared<CPicture>(ImagePath::builtin("buttons/backpackButtonIcon")));
dismissButton = std::make_shared<CButton>(Point(534, 429), AnimationPath::builtin("hsbtns2.def"), CButton::tooltip(heroscrn[28]), [=](){ dismissCurrent(); }, EShortcut::HERO_DISMISS); dismissButton = std::make_shared<CButton>(Point(534, 429), AnimationPath::builtin("hsbtns2.def"), CButton::tooltip(heroscrn[28]), [=](){ dismissCurrent(); }, EShortcut::HERO_DISMISS);
} }
else else

View File

@ -35,6 +35,7 @@
#include "../../lib/CConfigHandler.h" #include "../../lib/CConfigHandler.h"
#include "../../lib/CondSh.h" #include "../../lib/CondSh.h"
#include "../../lib/CGeneralTextHandler.h" //for Unicode related stuff #include "../../lib/CGeneralTextHandler.h" //for Unicode related stuff
#include "../../lib/mapObjects/CGCreature.h"
#include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/mapObjects/CGTownInstance.h" #include "../../lib/mapObjects/CGTownInstance.h"
#include "../../lib/mapObjects/MiscObjects.h" #include "../../lib/mapObjects/MiscObjects.h"
@ -243,6 +244,9 @@ CInfoPopup::CInfoPopup(SDL_Surface * Bitmap, const Point &p, ETextAlignment alig
case ETextAlignment::TOPLEFT: case ETextAlignment::TOPLEFT:
init(p.x, p.y); init(p.x, p.y);
break; break;
case ETextAlignment::TOPCENTER:
init(p.x - Bitmap->w/2, p.y);
break;
default: default:
assert(0); //not implemented assert(0); //not implemented
} }
@ -333,7 +337,7 @@ void CRClickPopup::createAndPush(const std::string & txt, std::shared_ptr<CCompo
void CRClickPopup::createAndPush(const CGObjectInstance * obj, const Point & p, ETextAlignment alignment) void CRClickPopup::createAndPush(const CGObjectInstance * obj, const Point & p, ETextAlignment alignment)
{ {
auto iWin = createInfoWin(p, obj); //try get custom infowindow for this obj auto iWin = createCustomInfoWindow(p, obj); //try get custom infowindow for this obj
if(iWin) if(iWin)
{ {
GH.windows().pushWindow(iWin); GH.windows().pushWindow(iWin);
@ -401,14 +405,21 @@ CInfoBoxPopup::CInfoBoxPopup(Point position, const CGGarrison * garr)
tooltip = std::make_shared<CArmyTooltip>(Point(9, 10), iah); tooltip = std::make_shared<CArmyTooltip>(Point(9, 10), iah);
} }
std::shared_ptr<WindowBase> CRClickPopup::createInfoWin(Point position, const CGObjectInstance * specific) //specific=0 => draws info about selected town/hero CInfoBoxPopup::CInfoBoxPopup(Point position, const CGCreature * creature)
: CWindowObject(RCLICK_POPUP | BORDERED, ImagePath::builtin("DIBOXBCK"), toScreen(position))
{
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
tooltip = std::make_shared<CreatureTooltip>(Point(9, 10), creature);
}
std::shared_ptr<WindowBase> CRClickPopup::createCustomInfoWindow(Point position, const CGObjectInstance * specific) //specific=0 => draws info about selected town/hero
{ {
if(nullptr == specific) if(nullptr == specific)
specific = LOCPLINT->localState->getCurrentArmy(); specific = LOCPLINT->localState->getCurrentArmy();
if(nullptr == specific) if(nullptr == specific)
{ {
logGlobal->error("createInfoWin: no object to describe"); logGlobal->error("createCustomInfoWindow: no object to describe");
return nullptr; return nullptr;
} }
@ -418,6 +429,8 @@ std::shared_ptr<WindowBase> CRClickPopup::createInfoWin(Point position, const CG
return std::make_shared<CInfoBoxPopup>(position, dynamic_cast<const CGHeroInstance *>(specific)); return std::make_shared<CInfoBoxPopup>(position, dynamic_cast<const CGHeroInstance *>(specific));
case Obj::TOWN: case Obj::TOWN:
return std::make_shared<CInfoBoxPopup>(position, dynamic_cast<const CGTownInstance *>(specific)); return std::make_shared<CInfoBoxPopup>(position, dynamic_cast<const CGTownInstance *>(specific));
case Obj::MONSTER:
return std::make_shared<CInfoBoxPopup>(position, dynamic_cast<const CGCreature *>(specific));
case Obj::GARRISON: case Obj::GARRISON:
case Obj::GARRISON2: case Obj::GARRISON2:
return std::make_shared<CInfoBoxPopup>(position, dynamic_cast<const CGGarrison *>(specific)); return std::make_shared<CInfoBoxPopup>(position, dynamic_cast<const CGGarrison *>(specific));

View File

@ -19,6 +19,7 @@ class CGObjectInstance;
class CGTownInstance; class CGTownInstance;
class CGHeroInstance; class CGHeroInstance;
class CGGarrison; class CGGarrison;
class CGCreature;
class Rect; class Rect;
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END
@ -81,7 +82,7 @@ public:
virtual void close(); virtual void close();
bool isPopupWindow() const override; bool isPopupWindow() const override;
static std::shared_ptr<WindowBase> createInfoWin(Point position, const CGObjectInstance * specific); static std::shared_ptr<WindowBase> createCustomInfoWindow(Point position, const CGObjectInstance * specific);
static void createAndPush(const std::string & txt, const CInfoWindow::TCompsInfo &comps = CInfoWindow::TCompsInfo()); static void createAndPush(const std::string & txt, const CInfoWindow::TCompsInfo &comps = CInfoWindow::TCompsInfo());
static void createAndPush(const std::string & txt, std::shared_ptr<CComponent> component); static void createAndPush(const std::string & txt, std::shared_ptr<CComponent> component);
static void createAndPush(const CGObjectInstance * obj, const Point & p, ETextAlignment alignment = ETextAlignment::BOTTOMRIGHT); static void createAndPush(const CGObjectInstance * obj, const Point & p, ETextAlignment alignment = ETextAlignment::BOTTOMRIGHT);
@ -111,15 +112,16 @@ public:
~CInfoPopup(); ~CInfoPopup();
}; };
/// popup on adventure map for town\hero objects /// popup on adventure map for town\hero and other objects with customized popup content
class CInfoBoxPopup : public CWindowObject class CInfoBoxPopup : public CWindowObject
{ {
std::shared_ptr<CArmyTooltip> tooltip; std::shared_ptr<CIntObject> tooltip;
Point toScreen(Point pos); Point toScreen(Point pos);
public: public:
CInfoBoxPopup(Point position, const CGTownInstance * town); CInfoBoxPopup(Point position, const CGTownInstance * town);
CInfoBoxPopup(Point position, const CGHeroInstance * hero); CInfoBoxPopup(Point position, const CGHeroInstance * hero);
CInfoBoxPopup(Point position, const CGGarrison * garr); CInfoBoxPopup(Point position, const CGGarrison * garr);
CInfoBoxPopup(Point position, const CGCreature * creature);
}; };
/// component selection window /// component selection window

View File

@ -214,11 +214,15 @@
"type" : "object", "type" : "object",
"additionalProperties" : false, "additionalProperties" : false,
"default" : {}, "default" : {},
"required" : [ "radialWheelGarrisonSwipe" ], "required" : [ "radialWheelGarrisonSwipe", "touchToleranceDistance" ],
"properties" : { "properties" : {
"radialWheelGarrisonSwipe" : { "radialWheelGarrisonSwipe" : {
"type" : "boolean", "type" : "boolean",
"default" : true "default" : true
},
"touchToleranceDistance" : {
"type" : "number",
"default" : 20
} }
} }
}, },

View File

@ -192,13 +192,6 @@ void CModListModel::resetRepositories()
endResetModel(); endResetModel();
} }
void CModListModel::addRepository(QVariantMap data)
{
beginResetModel();
CModList::addRepository(data);
endResetModel();
}
void CModListModel::modChanged(QString modID) void CModListModel::modChanged(QString modID)
{ {
int index = modNameToID.indexOf(modID); int index = modNameToID.indexOf(modID);

View File

@ -61,7 +61,6 @@ public:
/// CModListContainer overrides /// CModListContainer overrides
void resetRepositories() override; void resetRepositories() override;
void reloadRepositories() override; void reloadRepositories() override;
void addRepository(QVariantMap data) override;
void modChanged(QString modID) override; void modChanged(QString modID) override;
QVariant data(const QModelIndex & index, int role) const override; QVariant data(const QModelIndex & index, int role) const override;

View File

@ -666,6 +666,7 @@ void CModListView::installFiles(QStringList files)
{ {
QStringList mods; QStringList mods;
QStringList images; QStringList images;
QVector<QVariantMap> repositories;
// TODO: some better way to separate zip's with mods and downloaded repository files // TODO: some better way to separate zip's with mods and downloaded repository files
for(QString filename : files) for(QString filename : files)
@ -675,12 +676,12 @@ void CModListView::installFiles(QStringList files)
if(filename.endsWith(".json")) if(filename.endsWith(".json"))
{ {
//download and merge additional files //download and merge additional files
auto repodata = JsonUtils::JsonFromFile(filename).toMap(); auto repoData = JsonUtils::JsonFromFile(filename).toMap();
if(repodata.value("name").isNull()) if(repoData.value("name").isNull())
{ {
for(const auto & key : repodata.keys()) for(const auto & key : repoData.keys())
{ {
auto modjson = repodata[key].toMap().value("mod"); auto modjson = repoData[key].toMap().value("mod");
if(!modjson.isNull()) if(!modjson.isNull())
{ {
downloadFile(key + ".json", modjson.toString(), "repository index"); downloadFile(key + ".json", modjson.toString(), "repository index");
@ -691,14 +692,17 @@ void CModListView::installFiles(QStringList files)
{ {
auto modn = QFileInfo(filename).baseName(); auto modn = QFileInfo(filename).baseName();
QVariantMap temp; QVariantMap temp;
temp[modn] = repodata; temp[modn] = repoData;
repodata = temp; repoData = temp;
} }
manager->loadRepository(repodata); repositories.push_back(repoData);
} }
if(filename.endsWith(".png")) if(filename.endsWith(".png"))
images.push_back(filename); images.push_back(filename);
} }
manager->loadRepositories(repositories);
if(!mods.empty()) if(!mods.empty())
installMods(mods); installMods(mods);

View File

@ -70,9 +70,11 @@ void CModManager::resetRepositories()
modList->resetRepositories(); modList->resetRepositories();
} }
void CModManager::loadRepository(QVariantMap repomap) void CModManager::loadRepositories(QVector<QVariantMap> repomap)
{ {
modList->addRepository(repomap); for (auto const & entry : repomap)
modList->addRepository(entry);
modList->reloadRepositories();
} }
void CModManager::loadMods() void CModManager::loadMods()

View File

@ -35,7 +35,7 @@ public:
CModManager(CModList * modList); CModManager(CModList * modList);
void resetRepositories(); void resetRepositories();
void loadRepository(QVariantMap repomap); void loadRepositories(QVector<QVariantMap> repomap);
void loadModSettings(); void loadModSettings();
void loadMods(); void loadMods();

View File

@ -136,4 +136,12 @@ Rect Rect::intersect(const Rect & other) const
} }
} }
int Rect::distanceTo(const Point & target) const
{
int distanceX = std::max({left() - target.x, 0, target.x - right()});
int distanceY = std::max({top() - target.y, 0, target.y - bottom()});
return Point(distanceX, distanceY).length();
}
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

@ -142,6 +142,9 @@ public:
return x == other.x && y == other.y && w == other.w && h == other.h; return x == other.x && y == other.y && w == other.w && h == other.h;
} }
/// returns distance from this rect to point, or 0 if inside
DLL_LINKAGE int distanceTo(const Point & target) const;
/// returns true if this rect intersects with another rect /// returns true if this rect intersects with another rect
DLL_LINKAGE bool intersectionTest(const Rect & other) const; DLL_LINKAGE bool intersectionTest(const Rect & other) const;

View File

@ -53,7 +53,7 @@ std::string CGCreature::getHoverText(const CGHeroInstance * hero) const
ms.appendRawString(" "); ms.appendRawString(" ");
ms.appendLocalString(EMetaText::CRE_PL_NAMES,subID); ms.appendLocalString(EMetaText::CRE_PL_NAMES,subID);
ms.appendRawString("\n"); ms.appendRawString("\n\n");
int decision = takenAction(hero, true); int decision = takenAction(hero, true);

View File

@ -197,6 +197,8 @@ public:
ui32 producedQuantity; ui32 producedQuantity;
std::set<GameResID> abandonedMineResources; std::set<GameResID> abandonedMineResources;
bool isAbandoned() const;
private: private:
void onHeroVisit(const CGHeroInstance * h) const override; void onHeroVisit(const CGHeroInstance * h) const override;
void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override; void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
@ -209,7 +211,6 @@ private:
std::string getObjectName() const override; std::string getObjectName() const override;
std::string getHoverText(PlayerColor player) const override; std::string getHoverText(PlayerColor player) const override;
bool isAbandoned() const;
public: public:
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {

View File

@ -140,13 +140,11 @@ void Rewardable::Interface::grantRewardAfterLevelup(IGameCallback * cb, const Re
caster.setActualCaster(hero); caster.setActualCaster(hero);
caster.setSpellSchoolLevel(info.reward.spellCast.second); caster.setSpellSchoolLevel(info.reward.spellCast.second);
cb->castSpell(&caster, info.reward.spellCast.first, int3{-1, -1, -1}); cb->castSpell(&caster, info.reward.spellCast.first, int3{-1, -1, -1});
}
if(info.reward.removeObject) if(info.reward.removeObject)
logMod->warn("Removal of object with spell casts is not supported!");
}
else if(info.reward.removeObject) //FIXME: object can't track spell cancel or finish, so removeObject leads to crash
if(auto * instance = dynamic_cast<const CGObjectInstance*>(this)) if(auto * instance = dynamic_cast<const CGObjectInstance*>(this))
cb->removeObject(instance); cb->removeAfterVisit(instance);
} }
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

@ -49,7 +49,7 @@ void Teleport::adjustTargetTypes(std::vector<TargetType> & types) const
bool Teleport::applicable(Problem & problem, const Mechanics * m, const EffectTarget & target) const bool Teleport::applicable(Problem & problem, const Mechanics * m, const EffectTarget & target) const
{ {
if(target.size() == 1) //Assume, this is check only for selecting a unit if(target.size() == 1) //Assume, this is check only for selecting a unit
return UnitEffect::applicable(problem, m); return UnitEffect::applicable(problem, m, target);
if(target.size() != 2) if(target.size() != 2)
return m->adaptProblem(ESpellCastProblem::WRONG_SPELL_TARGET, problem); return m->adaptProblem(ESpellCastProblem::WRONG_SPELL_TARGET, problem);

View File

@ -118,7 +118,10 @@ void Initializer::initialize(CGHeroInstance * o)
o->tempOwner = defaultPlayer; o->tempOwner = defaultPlayer;
if(o->ID == Obj::PRISON) if(o->ID == Obj::PRISON)
{
o->subID = 0;
o->tempOwner = PlayerColor::NEUTRAL; o->tempOwner = PlayerColor::NEUTRAL;
}
if(o->ID == Obj::HERO) if(o->ID == Obj::HERO)
{ {
@ -184,8 +187,16 @@ void Initializer::initialize(CGMine * o)
if(!o) return; if(!o) return;
o->tempOwner = defaultPlayer; o->tempOwner = defaultPlayer;
if(o->isAbandoned())
{
for(auto r = 0; r < GameConstants::RESOURCE_QUANTITY - 1; ++r)
o->abandonedMineResources.insert(GameResID(r));
}
else
{
o->producedResource = GameResID(o->subID); o->producedResource = GameResID(o->subID);
o->producedQuantity = o->defaultResProduction(); o->producedQuantity = o->defaultResProduction();
}
} }
void Initializer::initialize(CGResource * o) void Initializer::initialize(CGResource * o)

View File

@ -142,6 +142,7 @@ void MapController::repairMap()
{ {
nih->typeName = "prison"; nih->typeName = "prison";
nih->subTypeName = "prison"; nih->subTypeName = "prison";
nih->subID = 0;
} }
nih->type = type; nih->type = type;

View File

@ -177,6 +177,11 @@ void BattleFlowProcessor::trySummonGuardians(const CBattleInfoCallback & battle,
gameHandler->sendAndApply(&pack); gameHandler->sendAndApply(&pack);
} }
} }
// send empty event to client
// temporary(?) workaround to force animations to trigger
StacksInjured fakeEvent;
gameHandler->sendAndApply(&fakeEvent);
} }
void BattleFlowProcessor::castOpeningSpells(const CBattleInfoCallback & battle) void BattleFlowProcessor::castOpeningSpells(const CBattleInfoCallback & battle)