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.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.splitUnitEqually" : "Split creatures equally",
"vcmi.radialWheel.moveUnit" : "Move creatures to another army",

View File

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

View File

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

View File

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

View File

@ -24,6 +24,7 @@
#include "../gui/CIntObject.h"
#include "../gui/WindowHandler.h"
#include "../windows/CCreatureWindow.h"
#include "../windows/InfoWindows.h"
#include "../../CCallback.h"
#include "../../lib/CConfigHandler.h"
@ -607,10 +608,10 @@ bool BattleActionsController::actionIsLegal(PossiblePlayerBattleAction action, B
return false;
case PossiblePlayerBattleAction::ANY_LOCATION:
return isCastingPossibleHere(action.spell().toSpell(), targetStack, targetHex);
return isCastingPossibleHere(action.spell().toSpell(), nullptr, targetHex);
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:
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::FREE_LOCATION:
return isCastingPossibleHere(action.spell().toSpell(), targetStack, targetHex);
return isCastingPossibleHere(action.spell().toSpell(), nullptr, targetHex);
case PossiblePlayerBattleAction::CATAPULT:
return owner.siegeController && owner.siegeController->isAttackableByCatapult(targetHex);
@ -1003,6 +1004,7 @@ void BattleActionsController::onHexRightClicked(BattleHex clickedHex)
if (spellcastingModeActive() || isCurrentStackInSpellcastMode)
{
endCastingSpell();
CRClickPopup::createAndPush(CGI->generaltexth->translate("core.genrltxt.731")); // spell cancelled
return;
}

View File

@ -129,6 +129,9 @@ BattleFieldController::BattleFieldController(BattleInterface & owner):
attackCursors = GH.renderHandler().loadAnimation(AnimationPath::builtin("CRCOMBAT"));
attackCursors->preload();
spellCursors = GH.renderHandler().loadAnimation(AnimationPath::builtin("CRSPELL"));
spellCursors->preload();
initializeHexEdgeMaskToFrameIndex();
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)
{
auto cursorIndex = CCS->curh->get<Cursor::Combat>();
auto imageIndex = static_cast<size_t>(cursorIndex);
auto combatCursorIndex = CCS->curh->get<Cursor::Combat>();
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> attackCursors;
std::shared_ptr<CAnimation> spellCursors;
/// Canvas that contains background, hex grid (if enabled), absolute obstacles and movement range of active stack
std::unique_ptr<Canvas> backgroundWithHexes;

View File

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

View File

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

View File

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

View File

@ -111,6 +111,8 @@ public:
/// by default, usedEvents inside UI elements are always handled
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 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

View File

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

View File

@ -16,7 +16,7 @@
#include "MouseButton.h"
#include "WindowHandler.h"
#include "../../lib/Point.h"
#include "../../lib/Rect.h"
template<typename Functor>
void EventDispatcher::processLists(ui16 activityFlag, const Functor & cb)
@ -134,28 +134,64 @@ void EventDispatcher::dispatchMouseDoubleClick(const Point & position)
}
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;
for(auto & i : hlp)
{
if(!vstd::contains(rclickable, i))
continue;
if( !i->receiveEvent(position, AEventsReceiver::LCLICK))
if( !i->receiveEvent(position, AEventsReceiver::SHOW_POPUP) && i != nearestElement)
continue;
i->showPopupWindow(position);
@ -170,7 +206,7 @@ void EventDispatcher::dispatchClosePopup(const Point & position)
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
// 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(!)
// 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
AEventsReceiver * nearestElement = findElementInToleranceRange(lclickable, position, AEventsReceiver::LCLICK, tolerance);
auto hlp = lclickable;
for(auto & i : hlp)
{
if(!vstd::contains(lclickable, i))
continue;
if( i->receiveEvent(position, AEventsReceiver::LCLICK))
if( i->receiveEvent(position, AEventsReceiver::LCLICK) || i == nearestElement)
{
if(isPressed)
i->clickPressed(position);

View File

@ -35,8 +35,8 @@ class EventDispatcher
EventReceiversList textInterested;
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>
void processLists(ui16 activityFlag, const Functor & cb);
@ -56,15 +56,15 @@ public:
void dispatchShortcutReleased(const std::vector<EShortcut> & shortcuts);
/// Mouse events
void dispatchMouseLeftButtonPressed(const Point & position);
void dispatchMouseLeftButtonReleased(const Point & position);
void dispatchMouseLeftButtonPressed(const Point & position, int tolerance);
void dispatchMouseLeftButtonReleased(const Point & position, int tolerance);
void dispatchMouseScrolled(const Point & distance, const Point & position);
void dispatchMouseDoubleClick(const Point & position);
void dispatchMouseMoved(const Point & distance, const Point & position);
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 dispatchGesturePanningStarted(const Point & initialPosition);

View File

@ -11,6 +11,7 @@
VCMI_LIB_NAMESPACE_BEGIN
class Point;
class Rect;
VCMI_LIB_NAMESPACE_END
class EventDispatcher;
@ -39,6 +40,8 @@ protected:
/// 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 const Rect & getPosition() const= 0;
public:
virtual void clickPressed(const Point & cursorPosition) {}
virtual void clickReleased(const Point & cursorPosition) {}

View File

@ -9,4 +9,4 @@
*/
#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)
{
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::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)
{
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::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)
return;
if (newState == BLOCKED)
removeUsedEvents(LCLICK | SHOW_POPUP | HOVER | KEYBOARD);
else
addUsedEvents(LCLICK | SHOW_POPUP | HOVER | KEYBOARD);
state = newState;
update();
}

View File

@ -11,12 +11,9 @@
#include "CArtifactsOfHeroBackpack.h"
#include "../gui/CGuiHandler.h"
#include "../gui/Shortcut.h"
#include "Buttons.h"
#include "Images.h"
#include "GameSettings.h"
#include "IHandlerBase.h"
#include "ObjectLists.h"
#include "../CPlayerInterface.h"
@ -37,21 +34,12 @@ CArtifactsOfHeroBackpack::CArtifactsOfHeroBackpack(const Point & position)
backpack.resize(visibleCapacityMax);
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)
{
artPlace = std::make_shared<CHeroArtPlace>(
Point(slotSizeWithMargin * (artPlaceIdx % HERO_BACKPACK_WINDOW_SLOT_COLUMNS), slotSizeWithMargin * (artPlaceIdx / HERO_BACKPACK_WINDOW_SLOT_COLUMNS)));
const auto pos = Point(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->leftClickCallback = std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1);
artPlace->rightClickCallback = std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1);
@ -70,8 +58,18 @@ CArtifactsOfHeroBackpack::CArtifactsOfHeroBackpack(const Point & position)
};
backpackListBox = std::make_shared<CListBoxWithCallback>(
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)
@ -88,7 +86,7 @@ void CArtifactsOfHeroBackpack::pickUpArtifact(CHeroArtPlace & artPlace)
void CArtifactsOfHeroBackpack::scrollBackpack(int offset)
{
if(backpackListBox)
backpackListBox->resize(getActiveSlotLinesNum());
backpackListBox->resize(getActiveSlotRowsNum());
backpackPos += offset;
auto slot = ArtifactPosition::BACKPACK_START + backpackPos;
for(auto artPlace : backpack)
@ -102,11 +100,11 @@ void CArtifactsOfHeroBackpack::scrollBackpack(int offset)
void CArtifactsOfHeroBackpack::updateBackpackSlots()
{
if(backpackListBox)
backpackListBox->resize(getActiveSlotLinesNum());
backpackListBox->resize(getActiveSlotRowsNum());
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;
}

View File

@ -27,11 +27,13 @@ public:
void pickUpArtifact(CHeroArtPlace & artPlace);
void scrollBackpack(int offset) override;
void updateBackpackSlots() override;
size_t getActiveSlotLinesNum();
size_t getActiveSlotRowsNum();
private:
std::shared_ptr<CListBoxWithCallback> backpackListBox;
std::vector<std::shared_ptr<CPicture>> backpackSlotsBackgrounds;
const size_t HERO_BACKPACK_WINDOW_SLOT_COLUMNS = 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 = {
{ 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_EE, hasOwnEmptySlots, "stackSplitEqual", "vcmi.radialWheel.splitUnitEqually", [this](){owner->bulkSmartSplitStack(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();} },
};
if (hasAnyEmptySlots || hasSameUnit)
GH.windows().createAndPushWindow<RadialMenu>(pos.center(), menuElements);
}

View File

@ -17,11 +17,13 @@
#include "../CPlayerInterface.h"
#include "../CGameInfo.h"
#include "../PlayerLocalState.h"
#include "../widgets/TextControls.h"
#include "../widgets/CGarrisonInt.h"
#include "../windows/CCastleInterface.h"
#include "../windows/InfoWindows.h"
#include "../render/Canvas.h"
#include "../render/Graphics.h"
#include "../../CCallback.h"
@ -30,6 +32,7 @@
#include "../../lib/CGeneralTextHandler.h"
#include "../../lib/GameSettings.h"
#include "../../lib/TextOperations.h"
#include "../../lib/mapObjects/CGCreature.h"
#include "../../lib/mapObjects/CGHeroInstance.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)
{
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);

View File

@ -14,6 +14,7 @@
VCMI_LIB_NAMESPACE_BEGIN
class CGGarrison;
class CGCreature;
struct InfoAboutArmy;
struct InfoAboutHero;
struct InfoAboutTown;
@ -25,6 +26,7 @@ class AFactionMember;
VCMI_LIB_NAMESPACE_END
class CLabel;
class CTextBox;
class CGarrisonInt;
class CCreatureAnim;
class CComponent;
@ -151,6 +153,15 @@ public:
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
class CMinorResDataBar : public CIntObject
{

View File

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

View File

@ -15,6 +15,7 @@
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../render/Canvas.h"
#include "../widgets/Buttons.h"
#include "../widgets/CArtifactHolder.h"
#include "../widgets/CComponent.h"
@ -83,7 +84,6 @@ public:
{
}
std::string getName() const
{
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(),
isGrandmasterAbility(isGrandmasterAbility_),
isSelected(false),
callback(callback)
{
pos = object_->pos;
this->isGrandmasterAbility = isGrandmasterAbility_;
setObject(object_);
}
@ -116,6 +119,25 @@ void CCommanderSkillIcon::setObject(std::shared_ptr<CIntObject> newObject)
void CCommanderSkillIcon::clickPressed(const Point & cursorPosition)
{
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)
@ -375,7 +397,7 @@ CStackWindow::CommanderMainSection::CommanderMainSection(CStackWindow * owner, i
{
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));
});
@ -429,7 +451,7 @@ CStackWindow::CommanderMainSection::CommanderMainSection(CStackWindow * owner, i
{
const auto bonus = CGI->creh->skillRequirements[skillID-100].first;
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 = [=]()
{
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->setRedrawParent(true);
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);
@ -896,14 +919,23 @@ void CStackWindow::setSelection(si32 newSkill, std::shared_ptr<CCommanderSkillIc
selectedIcon->setObject(std::make_shared<CPicture>(getSkillImage(oldSelection)));
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->deselect();
}
selectedIcon = newIcon; // update new selection
if(newSkill < 100)
{
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
}
}
}
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?
{
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:
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;
void clickPressed(const Point & cursorPosition) override;
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

View File

@ -25,21 +25,25 @@ CHeroBackpackWindow::CHeroBackpackWindow(const CGHeroInstance * hero)
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
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(-100, -170)*/Point(10, 10));
arts = std::make_shared<CArtifactsOfHeroBackpack>(Point(windowMargin, windowMargin));
arts->setHero(hero);
addSet(arts);
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);
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<CButton> quitButton;
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);
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);
}
else

View File

@ -35,6 +35,7 @@
#include "../../lib/CConfigHandler.h"
#include "../../lib/CondSh.h"
#include "../../lib/CGeneralTextHandler.h" //for Unicode related stuff
#include "../../lib/mapObjects/CGCreature.h"
#include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/mapObjects/CGTownInstance.h"
#include "../../lib/mapObjects/MiscObjects.h"
@ -243,6 +244,9 @@ CInfoPopup::CInfoPopup(SDL_Surface * Bitmap, const Point &p, ETextAlignment alig
case ETextAlignment::TOPLEFT:
init(p.x, p.y);
break;
case ETextAlignment::TOPCENTER:
init(p.x - Bitmap->w/2, p.y);
break;
default:
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)
{
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)
{
GH.windows().pushWindow(iWin);
@ -401,14 +405,21 @@ CInfoBoxPopup::CInfoBoxPopup(Point position, const CGGarrison * garr)
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)
specific = LOCPLINT->localState->getCurrentArmy();
if(nullptr == specific)
{
logGlobal->error("createInfoWin: no object to describe");
logGlobal->error("createCustomInfoWindow: no object to describe");
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));
case Obj::TOWN:
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::GARRISON2:
return std::make_shared<CInfoBoxPopup>(position, dynamic_cast<const CGGarrison *>(specific));

View File

@ -19,6 +19,7 @@ class CGObjectInstance;
class CGTownInstance;
class CGHeroInstance;
class CGGarrison;
class CGCreature;
class Rect;
VCMI_LIB_NAMESPACE_END
@ -81,7 +82,7 @@ public:
virtual void close();
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, std::shared_ptr<CComponent> component);
static void createAndPush(const CGObjectInstance * obj, const Point & p, ETextAlignment alignment = ETextAlignment::BOTTOMRIGHT);
@ -111,15 +112,16 @@ public:
~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
{
std::shared_ptr<CArmyTooltip> tooltip;
std::shared_ptr<CIntObject> tooltip;
Point toScreen(Point pos);
public:
CInfoBoxPopup(Point position, const CGTownInstance * town);
CInfoBoxPopup(Point position, const CGHeroInstance * hero);
CInfoBoxPopup(Point position, const CGGarrison * garr);
CInfoBoxPopup(Point position, const CGCreature * creature);
};
/// component selection window

View File

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

View File

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

View File

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

View File

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

View File

@ -70,9 +70,11 @@ void CModManager::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()

View File

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

View File

@ -142,6 +142,9 @@ public:
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
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.appendLocalString(EMetaText::CRE_PL_NAMES,subID);
ms.appendRawString("\n");
ms.appendRawString("\n\n");
int decision = takenAction(hero, true);

View File

@ -197,6 +197,8 @@ public:
ui32 producedQuantity;
std::set<GameResID> abandonedMineResources;
bool isAbandoned() const;
private:
void onHeroVisit(const CGHeroInstance * h) const override;
void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
@ -209,7 +211,6 @@ private:
std::string getObjectName() const override;
std::string getHoverText(PlayerColor player) const override;
bool isAbandoned() const;
public:
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.setSpellSchoolLevel(info.reward.spellCast.second);
cb->castSpell(&caster, info.reward.spellCast.first, int3{-1, -1, -1});
}
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))
cb->removeObject(instance);
cb->removeAfterVisit(instance);
}
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
{
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)
return m->adaptProblem(ESpellCastProblem::WRONG_SPELL_TARGET, problem);

View File

@ -118,7 +118,10 @@ void Initializer::initialize(CGHeroInstance * o)
o->tempOwner = defaultPlayer;
if(o->ID == Obj::PRISON)
{
o->subID = 0;
o->tempOwner = PlayerColor::NEUTRAL;
}
if(o->ID == Obj::HERO)
{
@ -184,8 +187,16 @@ void Initializer::initialize(CGMine * o)
if(!o) return;
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->producedQuantity = o->defaultResProduction();
}
}
void Initializer::initialize(CGResource * o)

View File

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

View File

@ -177,6 +177,11 @@ void BattleFlowProcessor::trySummonGuardians(const CBattleInfoCallback & battle,
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)