From 1f6e0fae7db83cbad0a5183abb9754cdc0443fcd Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Sun, 19 May 2024 03:27:28 +0200 Subject: [PATCH 001/100] quickspell draft --- client/battle/BattleInterfaceClasses.cpp | 49 ++++++++++++++++++++++++ client/battle/BattleInterfaceClasses.h | 17 ++++++++ client/battle/BattleWindow.cpp | 24 ++++++++++++ client/battle/BattleWindow.h | 2 + client/widgets/Buttons.cpp | 19 ++++++++- client/widgets/Buttons.h | 5 +++ 6 files changed, 115 insertions(+), 1 deletion(-) diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index d6e2ca161..0dc794d5b 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -417,6 +417,55 @@ BattleHero::BattleHero(const BattleInterface & owner, const CGHeroInstance * her addUsedEvents(TIME); } +QuickSpellPanel::QuickSpellPanel() + : CIntObject(LCLICK | SHOW_POPUP) +{ + OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); + + setEnabled(false); + + pos = Rect(0, 0, 52, 372); + background = std::make_shared(ImagePath::builtin("DIBOXBCK"), pos); + rect = std::make_shared(Rect(0, 0, pos.w + 1, pos.h + 1), ColorRGBA(0, 0, 0, 0), ColorRGBA(241, 216, 120, 255)); + + for(int i = 0; i < 10; i++) { + SpellID id = 14; + auto button = std::make_shared(Point(2, 1 + 37 * i), AnimationPath::builtin("spellint"), CButton::tooltip(), [&](){ std::cout << "test"; }, EShortcut::HERO_COSTUME_0); + button->setOverlay(std::make_shared(AnimationPath::builtin("spellint"), i > 0 ? id.num : 0)); + + if(i > 3) + { + button->block(true); + buttonsDisabled.push_back(std::make_shared(Rect(2, 1 + 37 * i, 48, 36), ColorRGBA(0, 0, 0, 128))); + } + + buttons.push_back(button); + } +} + +void QuickSpellPanel::show(Canvas & to) +{ + showAll(to); + CIntObject::show(to); +} + +void QuickSpellPanel::clickReleased(const Point & cursorPosition) +{ + if(!pos.isInside(cursorPosition)) + setEnabled(false); +} + +void QuickSpellPanel::showPopupWindow(const Point & cursorPosition) +{ + if(!pos.isInside(cursorPosition)) + setEnabled(false); +} + +bool QuickSpellPanel::receiveEvent(const Point & position, int eventType) const +{ + return true; // capture click also outside of window +} + HeroInfoBasicPanel::HeroInfoBasicPanel(const InfoAboutHero & hero, Point * position, bool initializeBackground) : CIntObject(0) { diff --git a/client/battle/BattleInterfaceClasses.h b/client/battle/BattleInterfaceClasses.h index c385db754..c62f7279c 100644 --- a/client/battle/BattleInterfaceClasses.h +++ b/client/battle/BattleInterfaceClasses.h @@ -147,6 +147,23 @@ public: BattleHero(const BattleInterface & owner, const CGHeroInstance * hero, bool defender); }; +class QuickSpellPanel : public CIntObject +{ +private: + std::shared_ptr background; + std::shared_ptr rect; + std::vector> buttons; + std::vector> buttonsDisabled; + + bool receiveEvent(const Point & position, int eventType) const override; + void clickReleased(const Point & cursorPosition) override; + void showPopupWindow(const Point & cursorPosition) override; +public: + QuickSpellPanel(); + + void show(Canvas & to) override; +}; + class HeroInfoBasicPanel : public CIntObject //extracted from InfoWindow to fit better as non-popup embed element { private: diff --git a/client/battle/BattleWindow.cpp b/client/battle/BattleWindow.cpp index c1a21e044..0b8cddb5e 100644 --- a/client/battle/BattleWindow.cpp +++ b/client/battle/BattleWindow.cpp @@ -98,6 +98,30 @@ BattleWindow::BattleWindow(BattleInterface & owner): createStickyHeroInfoWindows(); createTimerInfoWindows(); + auto w = widget("cast"); + if(w) + { + auto hero = owner.getBattle()->battleGetMyHero(); + if(GH.screenDimensions().x >= 1000 && hero && owner.getBattle()->battleCanCastSpell(hero, spells::Mode::HERO) != ESpellCastProblem::NO_SPELLBOOK && settings["general"]["enableUiEnhancements"].Bool()) + { + quickSpellPanelWindow = std::make_shared(); + quickSpellPanelWindow->moveTo(Point(w->pos.x - 2, w->pos.y - 378)); + w->addHoverCallback([this, w](bool on) + { + if(on) + quickSpellPanelWindow->setEnabled(true); + else + if(GH.getCursorPosition().x <= w->pos.x || GH.getCursorPosition().x >= w->pos.x + w->pos.w || GH.getCursorPosition().y >= w->pos.y + w->pos.h) + quickSpellPanelWindow->setEnabled(false); + }); + w->addPanningCallback([this](const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) + { + if((currentPosition - initialPosition).y < -20) + quickSpellPanelWindow->setEnabled(true); + }); + } + } + if ( owner.tacticsMode ) tacticPhaseStarted(); else diff --git a/client/battle/BattleWindow.h b/client/battle/BattleWindow.h index 5369cca9d..39a714635 100644 --- a/client/battle/BattleWindow.h +++ b/client/battle/BattleWindow.h @@ -27,6 +27,7 @@ class StackQueue; class TurnTimerWidget; class HeroInfoBasicPanel; class StackInfoBasicPanel; +class QuickSpellPanel; /// GUI object that handles functionality of panel at the bottom of combat screen class BattleWindow : public InterfaceObjectConfigurable @@ -39,6 +40,7 @@ class BattleWindow : public InterfaceObjectConfigurable std::shared_ptr defenderHeroWindow; std::shared_ptr attackerStackWindow; std::shared_ptr defenderStackWindow; + std::shared_ptr quickSpellPanelWindow; std::shared_ptr attackerTimerWidget; std::shared_ptr defenderTimerWidget; diff --git a/client/widgets/Buttons.cpp b/client/widgets/Buttons.cpp index 620c08587..dfa6adc12 100644 --- a/client/widgets/Buttons.cpp +++ b/client/widgets/Buttons.cpp @@ -67,6 +67,16 @@ void CButton::addCallback(const std::function & callback) this->callback += callback; } +void CButton::addHoverCallback(const std::function & callback) +{ + this->hoverCallback += callback; +} + +void CButton::addPanningCallback(const std::function & callback) +{ + this->panningCallback += callback; +} + void ButtonBase::setTextOverlay(const std::string & Text, EFonts font, ColorRGBA color) { OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); @@ -295,6 +305,8 @@ void CButton::showPopupWindow(const Point & cursorPosition) void CButton::hover (bool on) { + hoverCallback(on); + if(hoverable && !isBlocked()) { if(on) @@ -319,6 +331,11 @@ void CButton::hover (bool on) } } +void CButton::gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) +{ + panningCallback(initialPosition, currentPosition, lastUpdateDistance); +} + ButtonBase::ButtonBase(Point position, const AnimationPath & defName, EShortcut key, bool playerColoredButton) : CKeyShortcut(key) , stateToIndex({0, 1, 2, 3}) @@ -351,7 +368,7 @@ CButton::CButton(Point position, const AnimationPath &defName, const std::pair callback; + CFunctionList hoverCallback; + CFunctionList panningCallback; std::array hoverTexts; //texts for statusbar, if empty - first entry will be used std::optional borderColor; // mapping of button state to border color @@ -90,6 +92,8 @@ public: /// adds one more callback to on-click actions void addCallback(const std::function & callback); + void addHoverCallback(const std::function & callback); + void addPanningCallback(const std::function & callback); void addHoverText(EButtonState state, const std::string & text); @@ -114,6 +118,7 @@ public: void clickReleased(const Point & cursorPosition) override; void clickCancel(const Point & cursorPosition) override; void hover (bool on) override; + void gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) override; void showAll(Canvas & to) override; /// generates tooltip that can be passed into constructor From 814241e8366ac7f528aeda39428343776a4bb724 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Sun, 19 May 2024 04:23:53 +0200 Subject: [PATCH 002/100] converted to window --- client/battle/BattleInterfaceClasses.cpp | 30 ++++++++++++++++++++---- client/battle/BattleInterfaceClasses.h | 7 ++++-- client/battle/BattleWindow.cpp | 9 +++---- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index 0dc794d5b..f34f3091e 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -417,11 +417,13 @@ BattleHero::BattleHero(const BattleInterface & owner, const CGHeroInstance * her addUsedEvents(TIME); } -QuickSpellPanel::QuickSpellPanel() - : CIntObject(LCLICK | SHOW_POPUP) +QuickSpellPanel::QuickSpellPanel(std::shared_ptr initWidget) + : CWindowObject(NEEDS_ANIMATED_BACKGROUND), initWidget(initWidget) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); + addUsedEvents(LCLICK | SHOW_POPUP | MOVE); + setEnabled(false); pos = Rect(0, 0, 52, 372); @@ -452,13 +454,33 @@ void QuickSpellPanel::show(Canvas & to) void QuickSpellPanel::clickReleased(const Point & cursorPosition) { if(!pos.isInside(cursorPosition)) - setEnabled(false); + close(); + + if(initWidget->pos.isInside(cursorPosition)) + { + initWidget->clickPressed(cursorPosition); + initWidget->clickReleased(cursorPosition); + } } void QuickSpellPanel::showPopupWindow(const Point & cursorPosition) { if(!pos.isInside(cursorPosition)) - setEnabled(false); + close(); + + if(initWidget->pos.isInside(cursorPosition)) + { + initWidget->showPopupWindow(cursorPosition); + } +} + +void QuickSpellPanel::mouseMoved(const Point & cursorPosition, const Point & lastUpdateDistance) +{ + if( cursorPosition.x <= initWidget->pos.x - 20 || + cursorPosition.x >= initWidget->pos.x + initWidget->pos.w + 20 || + cursorPosition.y <= initWidget->pos.y - pos.h - 20 || + (cursorPosition.y >= initWidget->pos.y && !initWidget->pos.isInside(cursorPosition))) + close(); } bool QuickSpellPanel::receiveEvent(const Point & position, int eventType) const diff --git a/client/battle/BattleInterfaceClasses.h b/client/battle/BattleInterfaceClasses.h index c62f7279c..d5c8ceff1 100644 --- a/client/battle/BattleInterfaceClasses.h +++ b/client/battle/BattleInterfaceClasses.h @@ -147,7 +147,7 @@ public: BattleHero(const BattleInterface & owner, const CGHeroInstance * hero, bool defender); }; -class QuickSpellPanel : public CIntObject +class QuickSpellPanel : public CWindowObject { private: std::shared_ptr background; @@ -158,8 +158,11 @@ private: bool receiveEvent(const Point & position, int eventType) const override; void clickReleased(const Point & cursorPosition) override; void showPopupWindow(const Point & cursorPosition) override; + void mouseMoved(const Point & cursorPosition, const Point & lastUpdateDistance) override; + + std::shared_ptr initWidget; public: - QuickSpellPanel(); + QuickSpellPanel(std::shared_ptr initWidget); void show(Canvas & to) override; }; diff --git a/client/battle/BattleWindow.cpp b/client/battle/BattleWindow.cpp index 0b8cddb5e..a2dc2bdc2 100644 --- a/client/battle/BattleWindow.cpp +++ b/client/battle/BattleWindow.cpp @@ -104,20 +104,17 @@ BattleWindow::BattleWindow(BattleInterface & owner): auto hero = owner.getBattle()->battleGetMyHero(); if(GH.screenDimensions().x >= 1000 && hero && owner.getBattle()->battleCanCastSpell(hero, spells::Mode::HERO) != ESpellCastProblem::NO_SPELLBOOK && settings["general"]["enableUiEnhancements"].Bool()) { - quickSpellPanelWindow = std::make_shared(); + quickSpellPanelWindow = std::make_shared(w); quickSpellPanelWindow->moveTo(Point(w->pos.x - 2, w->pos.y - 378)); w->addHoverCallback([this, w](bool on) { if(on) - quickSpellPanelWindow->setEnabled(true); - else - if(GH.getCursorPosition().x <= w->pos.x || GH.getCursorPosition().x >= w->pos.x + w->pos.w || GH.getCursorPosition().y >= w->pos.y + w->pos.h) - quickSpellPanelWindow->setEnabled(false); + GH.windows().pushWindow(quickSpellPanelWindow); }); w->addPanningCallback([this](const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) { if((currentPosition - initialPosition).y < -20) - quickSpellPanelWindow->setEnabled(true); + GH.windows().pushWindow(quickSpellPanelWindow); }); } } From 989394de538a5ddd97743174a6e433c366ea6114 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Sun, 19 May 2024 20:45:55 +0200 Subject: [PATCH 003/100] Selectionwindow --- client/battle/BattleInterfaceClasses.cpp | 48 ++++++++++++++++++++---- client/battle/BattleInterfaceClasses.h | 14 +++++++ client/battle/BattleWindow.cpp | 16 +++++--- client/battle/BattleWindow.h | 1 - client/widgets/Buttons.cpp | 7 ++++ client/widgets/Buttons.h | 2 + 6 files changed, 74 insertions(+), 14 deletions(-) diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index f34f3091e..6bd8f6b3e 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -424,22 +424,26 @@ QuickSpellPanel::QuickSpellPanel(std::shared_ptr initWidget) addUsedEvents(LCLICK | SHOW_POPUP | MOVE); - setEnabled(false); - pos = Rect(0, 0, 52, 372); background = std::make_shared(ImagePath::builtin("DIBOXBCK"), pos); rect = std::make_shared(Rect(0, 0, pos.w + 1, pos.h + 1), ColorRGBA(0, 0, 0, 0), ColorRGBA(241, 216, 120, 255)); + panelSelect = std::make_shared(); + for(int i = 0; i < 10; i++) { SpellID id = 14; - auto button = std::make_shared(Point(2, 1 + 37 * i), AnimationPath::builtin("spellint"), CButton::tooltip(), [&](){ std::cout << "test"; }, EShortcut::HERO_COSTUME_0); + auto button = std::make_shared(Point(2, 1 + 37 * i), AnimationPath::builtin("spellint"), CButton::tooltip(), [&](){ std::cout << "test"; }); button->setOverlay(std::make_shared(AnimationPath::builtin("spellint"), i > 0 ? id.num : 0)); + button->addPopupCallback([this](){ + panelSelect->setEnabled(true); + }); if(i > 3) { button->block(true); buttonsDisabled.push_back(std::make_shared(Rect(2, 1 + 37 * i, 48, 36), ColorRGBA(0, 0, 0, 128))); } + labels.push_back(std::make_shared(7, 4 + 37 * i, EFonts::FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, std::to_string(i))); buttons.push_back(button); } @@ -453,7 +457,7 @@ void QuickSpellPanel::show(Canvas & to) void QuickSpellPanel::clickReleased(const Point & cursorPosition) { - if(!pos.isInside(cursorPosition)) + if(!pos.isInside(cursorPosition) && (!panelSelect->isActive() || !panelSelect->pos.isInside(cursorPosition))) close(); if(initWidget->pos.isInside(cursorPosition)) @@ -465,7 +469,7 @@ void QuickSpellPanel::clickReleased(const Point & cursorPosition) void QuickSpellPanel::showPopupWindow(const Point & cursorPosition) { - if(!pos.isInside(cursorPosition)) + if(!pos.isInside(cursorPosition) && (!panelSelect->isActive() || !panelSelect->pos.isInside(cursorPosition))) close(); if(initWidget->pos.isInside(cursorPosition)) @@ -476,10 +480,12 @@ void QuickSpellPanel::showPopupWindow(const Point & cursorPosition) void QuickSpellPanel::mouseMoved(const Point & cursorPosition, const Point & lastUpdateDistance) { - if( cursorPosition.x <= initWidget->pos.x - 20 || + if( (cursorPosition.x <= initWidget->pos.x - 20 || cursorPosition.x >= initWidget->pos.x + initWidget->pos.w + 20 || cursorPosition.y <= initWidget->pos.y - pos.h - 20 || - (cursorPosition.y >= initWidget->pos.y && !initWidget->pos.isInside(cursorPosition))) + (cursorPosition.y >= initWidget->pos.y && !initWidget->pos.isInside(cursorPosition))) && + (!panelSelect->isActive() || !panelSelect->pos.resize(20).isInside(cursorPosition)) + ) close(); } @@ -488,6 +494,34 @@ bool QuickSpellPanel::receiveEvent(const Point & position, int eventType) const return true; // capture click also outside of window } +QuickSpellPanelSelect::QuickSpellPanelSelect() +{ + OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); + + setEnabled(false); + + std::vector> spellList; + for (auto const & s : VLC->spellh->objects) + if (s->isCombat() && !s->isSpecial() && !s->isCreatureAbility()) + spellList.push_back(s); + + auto ceil = [](int x, int y){ return(x + y - 1) / y; }; + int columnsNeeded = ceil(spellList.size(), 10); + + pos = Rect(-20 - columnsNeeded * 50, 0, columnsNeeded * 50 + 2, 372); + background = std::make_shared(ImagePath::builtin("DIBOXBCK"), Rect(0, 0, pos.w, pos.h)); + rect = std::make_shared(Rect(0, 0, pos.w + 1, pos.h + 1), ColorRGBA(0, 0, 0, 0), ColorRGBA(241, 216, 120, 255)); + + for(int i = 0; i < spellList.size(); i++) + { + int y = i % 10; + int x = i / 10; + auto button = std::make_shared(Point(2 + 50 * x, 1 + 37 * y), AnimationPath::builtin("spellint"), CButton::tooltip(), [&](){ std::cout << "test"; }); + button->setOverlay(std::make_shared(AnimationPath::builtin("spellint"), spellList[i]->getId().num + 1)); + buttons.push_back(button); + } +} + HeroInfoBasicPanel::HeroInfoBasicPanel(const InfoAboutHero & hero, Point * position, bool initializeBackground) : CIntObject(0) { diff --git a/client/battle/BattleInterfaceClasses.h b/client/battle/BattleInterfaceClasses.h index d5c8ceff1..12f036d1a 100644 --- a/client/battle/BattleInterfaceClasses.h +++ b/client/battle/BattleInterfaceClasses.h @@ -147,6 +147,17 @@ public: BattleHero(const BattleInterface & owner, const CGHeroInstance * hero, bool defender); }; +class QuickSpellPanelSelect : public CIntObject +{ +private: + std::shared_ptr background; + std::shared_ptr rect; + std::vector> buttons; + +public: + QuickSpellPanelSelect(); +}; + class QuickSpellPanel : public CWindowObject { private: @@ -154,6 +165,9 @@ private: std::shared_ptr rect; std::vector> buttons; std::vector> buttonsDisabled; + std::vector> labels; + + std::shared_ptr panelSelect; bool receiveEvent(const Point & position, int eventType) const override; void clickReleased(const Point & cursorPosition) override; diff --git a/client/battle/BattleWindow.cpp b/client/battle/BattleWindow.cpp index a2dc2bdc2..9bbaad0cc 100644 --- a/client/battle/BattleWindow.cpp +++ b/client/battle/BattleWindow.cpp @@ -104,17 +104,21 @@ BattleWindow::BattleWindow(BattleInterface & owner): auto hero = owner.getBattle()->battleGetMyHero(); if(GH.screenDimensions().x >= 1000 && hero && owner.getBattle()->battleCanCastSpell(hero, spells::Mode::HERO) != ESpellCastProblem::NO_SPELLBOOK && settings["general"]["enableUiEnhancements"].Bool()) { - quickSpellPanelWindow = std::make_shared(w); - quickSpellPanelWindow->moveTo(Point(w->pos.x - 2, w->pos.y - 378)); - w->addHoverCallback([this, w](bool on) + auto createQuickSpellPanelWindow = [](std::shared_ptr widget){ + std::shared_ptr window = std::make_shared(widget); + window->moveTo(Point(widget->pos.x - 2, widget->pos.y - 378)); + GH.windows().pushWindow(window); + }; + + w->addHoverCallback([this, createQuickSpellPanelWindow, w](bool on) { if(on) - GH.windows().pushWindow(quickSpellPanelWindow); + createQuickSpellPanelWindow(w); }); - w->addPanningCallback([this](const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) + w->addPanningCallback([this, createQuickSpellPanelWindow, w](const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) { if((currentPosition - initialPosition).y < -20) - GH.windows().pushWindow(quickSpellPanelWindow); + createQuickSpellPanelWindow(w); }); } } diff --git a/client/battle/BattleWindow.h b/client/battle/BattleWindow.h index 39a714635..27897df45 100644 --- a/client/battle/BattleWindow.h +++ b/client/battle/BattleWindow.h @@ -40,7 +40,6 @@ class BattleWindow : public InterfaceObjectConfigurable std::shared_ptr defenderHeroWindow; std::shared_ptr attackerStackWindow; std::shared_ptr defenderStackWindow; - std::shared_ptr quickSpellPanelWindow; std::shared_ptr attackerTimerWidget; std::shared_ptr defenderTimerWidget; diff --git a/client/widgets/Buttons.cpp b/client/widgets/Buttons.cpp index dfa6adc12..8491afe3e 100644 --- a/client/widgets/Buttons.cpp +++ b/client/widgets/Buttons.cpp @@ -67,6 +67,11 @@ void CButton::addCallback(const std::function & callback) this->callback += callback; } +void CButton::addPopupCallback(const std::function & callback) +{ + this->callbackPopup += callback; +} + void CButton::addHoverCallback(const std::function & callback) { this->hoverCallback += callback; @@ -299,6 +304,8 @@ void CButton::clickCancel(const Point & cursorPosition) void CButton::showPopupWindow(const Point & cursorPosition) { + callbackPopup(); + if(!helpBox.empty()) //there is no point to show window with nothing inside... CRClickPopup::createAndPush(helpBox); } diff --git a/client/widgets/Buttons.h b/client/widgets/Buttons.h index b15bf5e7b..c551cfdea 100644 --- a/client/widgets/Buttons.h +++ b/client/widgets/Buttons.h @@ -69,6 +69,7 @@ public: class CButton : public ButtonBase { CFunctionList callback; + CFunctionList callbackPopup; CFunctionList hoverCallback; CFunctionList panningCallback; @@ -92,6 +93,7 @@ public: /// adds one more callback to on-click actions void addCallback(const std::function & callback); + void addPopupCallback(const std::function & callback); void addHoverCallback(const std::function & callback); void addPanningCallback(const std::function & callback); From 1a8a14a56deab775a3cfe939b045562411dada30 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Sun, 19 May 2024 21:29:23 +0200 Subject: [PATCH 004/100] selectable fix --- client/battle/BattleInterfaceClasses.cpp | 26 ++++++++++++++++++------ client/battle/BattleInterfaceClasses.h | 8 ++++++-- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index 6bd8f6b3e..f3bab78b5 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -428,8 +428,13 @@ QuickSpellPanel::QuickSpellPanel(std::shared_ptr initWidget) background = std::make_shared(ImagePath::builtin("DIBOXBCK"), pos); rect = std::make_shared(Rect(0, 0, pos.w + 1, pos.h + 1), ColorRGBA(0, 0, 0, 0), ColorRGBA(241, 216, 120, 255)); - panelSelect = std::make_shared(); + panelSelect = std::make_shared(this); + create(); +} + +void QuickSpellPanel::create() +{ for(int i = 0; i < 10; i++) { SpellID id = 14; auto button = std::make_shared(Point(2, 1 + 37 * i), AnimationPath::builtin("spellint"), CButton::tooltip(), [&](){ std::cout << "test"; }); @@ -457,7 +462,7 @@ void QuickSpellPanel::show(Canvas & to) void QuickSpellPanel::clickReleased(const Point & cursorPosition) { - if(!pos.isInside(cursorPosition) && (!panelSelect->isActive() || !panelSelect->pos.isInside(cursorPosition))) + if(!pos.isInside(cursorPosition) && (!(panelSelect->isActive() || panelSelect->wasEnabled) || !panelSelect->pos.isInside(cursorPosition))) close(); if(initWidget->pos.isInside(cursorPosition)) @@ -469,7 +474,7 @@ void QuickSpellPanel::clickReleased(const Point & cursorPosition) void QuickSpellPanel::showPopupWindow(const Point & cursorPosition) { - if(!pos.isInside(cursorPosition) && (!panelSelect->isActive() || !panelSelect->pos.isInside(cursorPosition))) + if(!pos.isInside(cursorPosition) && (!(panelSelect->isActive() || panelSelect->wasEnabled) || !panelSelect->pos.isInside(cursorPosition))) close(); if(initWidget->pos.isInside(cursorPosition)) @@ -484,9 +489,12 @@ void QuickSpellPanel::mouseMoved(const Point & cursorPosition, const Point & las cursorPosition.x >= initWidget->pos.x + initWidget->pos.w + 20 || cursorPosition.y <= initWidget->pos.y - pos.h - 20 || (cursorPosition.y >= initWidget->pos.y && !initWidget->pos.isInside(cursorPosition))) && - (!panelSelect->isActive() || !panelSelect->pos.resize(20).isInside(cursorPosition)) + (!(panelSelect->isActive() || panelSelect->wasEnabled) || !panelSelect->pos.resize(20).isInside(cursorPosition)) ) close(); + + if(initWidget->pos.isInside(cursorPosition)) + panelSelect->wasEnabled = false; } bool QuickSpellPanel::receiveEvent(const Point & position, int eventType) const @@ -494,7 +502,8 @@ bool QuickSpellPanel::receiveEvent(const Point & position, int eventType) const return true; // capture click also outside of window } -QuickSpellPanelSelect::QuickSpellPanelSelect() +QuickSpellPanelSelect::QuickSpellPanelSelect(QuickSpellPanel * Parent) + : parent(Parent), wasEnabled(false) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); @@ -516,7 +525,12 @@ QuickSpellPanelSelect::QuickSpellPanelSelect() { int y = i % 10; int x = i / 10; - auto button = std::make_shared(Point(2 + 50 * x, 1 + 37 * y), AnimationPath::builtin("spellint"), CButton::tooltip(), [&](){ std::cout << "test"; }); + auto button = std::make_shared(Point(2 + 50 * x, 1 + 37 * y), AnimationPath::builtin("spellint"), CButton::tooltip(), [this](){ + setEnabled(false); + GH.windows().totalRedraw(); + wasEnabled = true; + parent->create(); + }); button->setOverlay(std::make_shared(AnimationPath::builtin("spellint"), spellList[i]->getId().num + 1)); buttons.push_back(button); } diff --git a/client/battle/BattleInterfaceClasses.h b/client/battle/BattleInterfaceClasses.h index 12f036d1a..d6b14d68d 100644 --- a/client/battle/BattleInterfaceClasses.h +++ b/client/battle/BattleInterfaceClasses.h @@ -44,6 +44,7 @@ class TransparentFilledRectangle; class CPlayerInterface; class BattleRenderer; class VideoWidget; +class QuickSpellPanel; /// Class which shows the console at the bottom of the battle screen and manages the text of the console class BattleConsole : public CIntObject, public IStatusBar @@ -153,9 +154,10 @@ private: std::shared_ptr background; std::shared_ptr rect; std::vector> buttons; - + QuickSpellPanel * parent; public: - QuickSpellPanelSelect(); + QuickSpellPanelSelect(QuickSpellPanel * Parent); + bool wasEnabled; // was the panel opened? -> don't close window because mouse is not in area }; class QuickSpellPanel : public CWindowObject @@ -178,6 +180,8 @@ private: public: QuickSpellPanel(std::shared_ptr initWidget); + void create(); + void show(Canvas & to) override; }; From e4dfca9f3dd0ea17fb68d39c6ab73d2612fd53fe Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Sun, 19 May 2024 22:31:35 +0200 Subject: [PATCH 005/100] disable correctly --- client/battle/BattleInterfaceClasses.cpp | 31 ++++++++++++++++++------ client/battle/BattleInterfaceClasses.h | 5 +++- client/battle/BattleWindow.cpp | 13 +++++----- 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index f3bab78b5..7ae2f2c6c 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -417,8 +417,8 @@ BattleHero::BattleHero(const BattleInterface & owner, const CGHeroInstance * her addUsedEvents(TIME); } -QuickSpellPanel::QuickSpellPanel(std::shared_ptr initWidget) - : CWindowObject(NEEDS_ANIMATED_BACKGROUND), initWidget(initWidget) +QuickSpellPanel::QuickSpellPanel(std::shared_ptr initWidget, std::shared_ptr battle) + : CWindowObject(NEEDS_ANIMATED_BACKGROUND), initWidget(initWidget), battle(battle) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); @@ -435,17 +435,29 @@ QuickSpellPanel::QuickSpellPanel(std::shared_ptr initWidget) void QuickSpellPanel::create() { + OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); + + labels.clear(); + buttons.clear(); + buttonsDisabled.clear(); + + auto hero = battle->battleGetMyHero(); + if(!hero) + return; + for(int i = 0; i < 10; i++) { - SpellID id = 14; + std::string spellIdentifier = persistentStorage["quickSpell"][std::to_string(i)].String(); + SpellID id = SpellID::decode(spellIdentifier); + auto button = std::make_shared(Point(2, 1 + 37 * i), AnimationPath::builtin("spellint"), CButton::tooltip(), [&](){ std::cout << "test"; }); - button->setOverlay(std::make_shared(AnimationPath::builtin("spellint"), i > 0 ? id.num : 0)); - button->addPopupCallback([this](){ + button->setOverlay(std::make_shared(AnimationPath::builtin("spellint"), !spellIdentifier.empty() ? id.num + 1 : 0)); + button->addPopupCallback([this, i](){ + panelSelect->spellSlot = i; panelSelect->setEnabled(true); }); - if(i > 3) + if(!id.hasValue() || !id.toSpell()->canBeCast(battle.get(), spells::Mode::HERO, hero)) { - button->block(true); buttonsDisabled.push_back(std::make_shared(Rect(2, 1 + 37 * i, 48, 36), ColorRGBA(0, 0, 0, 128))); } labels.push_back(std::make_shared(7, 4 + 37 * i, EFonts::FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, std::to_string(i))); @@ -525,10 +537,13 @@ QuickSpellPanelSelect::QuickSpellPanelSelect(QuickSpellPanel * Parent) { int y = i % 10; int x = i / 10; - auto button = std::make_shared(Point(2 + 50 * x, 1 + 37 * y), AnimationPath::builtin("spellint"), CButton::tooltip(), [this](){ + ConstTransitivePtr spell = spellList[i]; + auto button = std::make_shared(Point(2 + 50 * x, 1 + 37 * y), AnimationPath::builtin("spellint"), CButton::tooltip(), [this, spell](){ setEnabled(false); GH.windows().totalRedraw(); wasEnabled = true; + Settings configID = persistentStorage.write["quickSpell"][std::to_string(spellSlot)]; + configID->String() = spell->identifier; parent->create(); }); button->setOverlay(std::make_shared(AnimationPath::builtin("spellint"), spellList[i]->getId().num + 1)); diff --git a/client/battle/BattleInterfaceClasses.h b/client/battle/BattleInterfaceClasses.h index d6b14d68d..b035d34a3 100644 --- a/client/battle/BattleInterfaceClasses.h +++ b/client/battle/BattleInterfaceClasses.h @@ -45,6 +45,7 @@ class CPlayerInterface; class BattleRenderer; class VideoWidget; class QuickSpellPanel; +class CPlayerBattleCallback; /// Class which shows the console at the bottom of the battle screen and manages the text of the console class BattleConsole : public CIntObject, public IStatusBar @@ -158,6 +159,7 @@ private: public: QuickSpellPanelSelect(QuickSpellPanel * Parent); bool wasEnabled; // was the panel opened? -> don't close window because mouse is not in area + int spellSlot; }; class QuickSpellPanel : public CWindowObject @@ -177,8 +179,9 @@ private: void mouseMoved(const Point & cursorPosition, const Point & lastUpdateDistance) override; std::shared_ptr initWidget; + std::shared_ptr battle; public: - QuickSpellPanel(std::shared_ptr initWidget); + QuickSpellPanel(std::shared_ptr initWidget, std::shared_ptr battle); void create(); diff --git a/client/battle/BattleWindow.cpp b/client/battle/BattleWindow.cpp index 9bbaad0cc..7d80cd6f7 100644 --- a/client/battle/BattleWindow.cpp +++ b/client/battle/BattleWindow.cpp @@ -102,23 +102,24 @@ BattleWindow::BattleWindow(BattleInterface & owner): if(w) { auto hero = owner.getBattle()->battleGetMyHero(); + auto battle = owner.getBattle(); if(GH.screenDimensions().x >= 1000 && hero && owner.getBattle()->battleCanCastSpell(hero, spells::Mode::HERO) != ESpellCastProblem::NO_SPELLBOOK && settings["general"]["enableUiEnhancements"].Bool()) { - auto createQuickSpellPanelWindow = [](std::shared_ptr widget){ - std::shared_ptr window = std::make_shared(widget); + auto createQuickSpellPanelWindow = [](std::shared_ptr widget, std::shared_ptr battle){ + std::shared_ptr window = std::make_shared(widget, battle); window->moveTo(Point(widget->pos.x - 2, widget->pos.y - 378)); GH.windows().pushWindow(window); }; - w->addHoverCallback([this, createQuickSpellPanelWindow, w](bool on) + w->addHoverCallback([this, createQuickSpellPanelWindow, w, battle](bool on) { if(on) - createQuickSpellPanelWindow(w); + createQuickSpellPanelWindow(w, battle); }); - w->addPanningCallback([this, createQuickSpellPanelWindow, w](const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) + w->addPanningCallback([this, createQuickSpellPanelWindow, w, battle](const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) { if((currentPosition - initialPosition).y < -20) - createQuickSpellPanelWindow(w); + createQuickSpellPanelWindow(w, battle); }); } } From 53a1bf2051e9ae0f12d560c5725edf0a78a2e405 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Sun, 19 May 2024 23:12:48 +0200 Subject: [PATCH 006/100] add spell cast --- client/battle/BattleInterfaceClasses.cpp | 16 +++++++---- client/battle/BattleInterfaceClasses.h | 4 +-- client/battle/BattleWindow.cpp | 36 ++++++++++++++++++------ client/gui/Shortcut.h | 10 +++++++ client/gui/ShortcutHandler.cpp | 10 +++++++ config/shortcutsConfig.json | 10 +++++++ 6 files changed, 70 insertions(+), 16 deletions(-) diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index 7ae2f2c6c..2ea4015f0 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -417,8 +417,8 @@ BattleHero::BattleHero(const BattleInterface & owner, const CGHeroInstance * her addUsedEvents(TIME); } -QuickSpellPanel::QuickSpellPanel(std::shared_ptr initWidget, std::shared_ptr battle) - : CWindowObject(NEEDS_ANIMATED_BACKGROUND), initWidget(initWidget), battle(battle) +QuickSpellPanel::QuickSpellPanel(std::shared_ptr initWidget, BattleInterface & owner) + : CWindowObject(NEEDS_ANIMATED_BACKGROUND), initWidget(initWidget), owner(owner) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); @@ -441,7 +441,7 @@ void QuickSpellPanel::create() buttons.clear(); buttonsDisabled.clear(); - auto hero = battle->battleGetMyHero(); + auto hero = owner.getBattle()->battleGetMyHero(); if(!hero) return; @@ -449,14 +449,20 @@ void QuickSpellPanel::create() std::string spellIdentifier = persistentStorage["quickSpell"][std::to_string(i)].String(); SpellID id = SpellID::decode(spellIdentifier); - auto button = std::make_shared(Point(2, 1 + 37 * i), AnimationPath::builtin("spellint"), CButton::tooltip(), [&](){ std::cout << "test"; }); + auto button = std::make_shared(Point(2, 1 + 37 * i), AnimationPath::builtin("spellint"), CButton::tooltip(), [this, id, hero](){ + if(id.hasValue() && id.toSpell()->canBeCast(owner.getBattle().get(), spells::Mode::HERO, hero)) + { + close(); + owner.castThisSpell(id); + } + }); button->setOverlay(std::make_shared(AnimationPath::builtin("spellint"), !spellIdentifier.empty() ? id.num + 1 : 0)); button->addPopupCallback([this, i](){ panelSelect->spellSlot = i; panelSelect->setEnabled(true); }); - if(!id.hasValue() || !id.toSpell()->canBeCast(battle.get(), spells::Mode::HERO, hero)) + if(!id.hasValue() || !id.toSpell()->canBeCast(owner.getBattle().get(), spells::Mode::HERO, hero)) { buttonsDisabled.push_back(std::make_shared(Rect(2, 1 + 37 * i, 48, 36), ColorRGBA(0, 0, 0, 128))); } diff --git a/client/battle/BattleInterfaceClasses.h b/client/battle/BattleInterfaceClasses.h index b035d34a3..6033d983c 100644 --- a/client/battle/BattleInterfaceClasses.h +++ b/client/battle/BattleInterfaceClasses.h @@ -179,9 +179,9 @@ private: void mouseMoved(const Point & cursorPosition, const Point & lastUpdateDistance) override; std::shared_ptr initWidget; - std::shared_ptr battle; + BattleInterface & owner; public: - QuickSpellPanel(std::shared_ptr initWidget, std::shared_ptr battle); + QuickSpellPanel(std::shared_ptr initWidget, BattleInterface & owner); void create(); diff --git a/client/battle/BattleWindow.cpp b/client/battle/BattleWindow.cpp index 7d80cd6f7..76e3ed12f 100644 --- a/client/battle/BattleWindow.cpp +++ b/client/battle/BattleWindow.cpp @@ -45,8 +45,8 @@ #include "../../lib/CPlayerState.h" #include "../windows/settings/SettingsMainWindow.h" -BattleWindow::BattleWindow(BattleInterface & owner): - owner(owner), +BattleWindow::BattleWindow(BattleInterface & Owner): + owner(Owner), defaultAction(PossiblePlayerBattleAction::INVALID) { OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; @@ -64,6 +64,25 @@ BattleWindow::BattleWindow(BattleInterface & owner): const JsonNode config(JsonPath::builtin("config/widgets/BattleWindow2.json")); + auto useSpellIfPossible = [this](int slot){ + std::string spellIdentifier = persistentStorage["quickSpell"][std::to_string(slot)].String(); + SpellID id = SpellID::decode(spellIdentifier); + if(id.hasValue() && owner.getBattle()->battleGetMyHero() && id.toSpell()->canBeCast(owner.getBattle().get(), spells::Mode::HERO, owner.getBattle()->battleGetMyHero())) + { + owner.castThisSpell(id); + } + }; + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_0, [useSpellIfPossible](){ useSpellIfPossible(0); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_1, [useSpellIfPossible](){ useSpellIfPossible(1); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_2, [useSpellIfPossible](){ useSpellIfPossible(2); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_3, [useSpellIfPossible](){ useSpellIfPossible(3); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_4, [useSpellIfPossible](){ useSpellIfPossible(4); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_5, [useSpellIfPossible](){ useSpellIfPossible(5); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_6, [useSpellIfPossible](){ useSpellIfPossible(6); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_7, [useSpellIfPossible](){ useSpellIfPossible(7); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_8, [useSpellIfPossible](){ useSpellIfPossible(8); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_9, [useSpellIfPossible](){ useSpellIfPossible(9); }); + addShortcut(EShortcut::GLOBAL_OPTIONS, std::bind(&BattleWindow::bOptionsf, this)); addShortcut(EShortcut::BATTLE_SURRENDER, std::bind(&BattleWindow::bSurrenderf, this)); addShortcut(EShortcut::BATTLE_RETREAT, std::bind(&BattleWindow::bFleef, this)); @@ -102,24 +121,23 @@ BattleWindow::BattleWindow(BattleInterface & owner): if(w) { auto hero = owner.getBattle()->battleGetMyHero(); - auto battle = owner.getBattle(); if(GH.screenDimensions().x >= 1000 && hero && owner.getBattle()->battleCanCastSpell(hero, spells::Mode::HERO) != ESpellCastProblem::NO_SPELLBOOK && settings["general"]["enableUiEnhancements"].Bool()) { - auto createQuickSpellPanelWindow = [](std::shared_ptr widget, std::shared_ptr battle){ - std::shared_ptr window = std::make_shared(widget, battle); + auto createQuickSpellPanelWindow = [](std::shared_ptr widget, BattleInterface & owner){ + std::shared_ptr window = std::make_shared(widget, owner); window->moveTo(Point(widget->pos.x - 2, widget->pos.y - 378)); GH.windows().pushWindow(window); }; - w->addHoverCallback([this, createQuickSpellPanelWindow, w, battle](bool on) + w->addHoverCallback([this, createQuickSpellPanelWindow, w](bool on) { if(on) - createQuickSpellPanelWindow(w, battle); + createQuickSpellPanelWindow(w, owner); }); - w->addPanningCallback([this, createQuickSpellPanelWindow, w, battle](const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) + w->addPanningCallback([this, createQuickSpellPanelWindow, w](const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) { if((currentPosition - initialPosition).y < -20) - createQuickSpellPanelWindow(w, battle); + createQuickSpellPanelWindow(w, owner); }); } } diff --git a/client/gui/Shortcut.h b/client/gui/Shortcut.h index ebe89e4e2..42b96ac1c 100644 --- a/client/gui/Shortcut.h +++ b/client/gui/Shortcut.h @@ -186,6 +186,16 @@ enum class EShortcut BATTLE_TOGGLE_HEROES_STATS, BATTLE_OPEN_ACTIVE_UNIT, BATTLE_OPEN_HOVERED_UNIT, + BATTLE_SPELL_SHORTCUT_0, + BATTLE_SPELL_SHORTCUT_1, + BATTLE_SPELL_SHORTCUT_2, + BATTLE_SPELL_SHORTCUT_3, + BATTLE_SPELL_SHORTCUT_4, + BATTLE_SPELL_SHORTCUT_5, + BATTLE_SPELL_SHORTCUT_6, + BATTLE_SPELL_SHORTCUT_7, + BATTLE_SPELL_SHORTCUT_8, + BATTLE_SPELL_SHORTCUT_9, MARKET_DEAL, MARKET_MAX_AMOUNT, diff --git a/client/gui/ShortcutHandler.cpp b/client/gui/ShortcutHandler.cpp index 3a5033882..11bafc8ae 100644 --- a/client/gui/ShortcutHandler.cpp +++ b/client/gui/ShortcutHandler.cpp @@ -222,6 +222,16 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const {"battleTacticsNext", EShortcut::BATTLE_TACTICS_NEXT }, {"battleTacticsEnd", EShortcut::BATTLE_TACTICS_END }, {"battleSelectAction", EShortcut::BATTLE_SELECT_ACTION }, + {"battleSpellShortcut0", EShortcut::BATTLE_SPELL_SHORTCUT_0 }, + {"battleSpellShortcut1", EShortcut::BATTLE_SPELL_SHORTCUT_1 }, + {"battleSpellShortcut2", EShortcut::BATTLE_SPELL_SHORTCUT_2 }, + {"battleSpellShortcut3", EShortcut::BATTLE_SPELL_SHORTCUT_3 }, + {"battleSpellShortcut4", EShortcut::BATTLE_SPELL_SHORTCUT_4 }, + {"battleSpellShortcut5", EShortcut::BATTLE_SPELL_SHORTCUT_5 }, + {"battleSpellShortcut6", EShortcut::BATTLE_SPELL_SHORTCUT_6 }, + {"battleSpellShortcut7", EShortcut::BATTLE_SPELL_SHORTCUT_7 }, + {"battleSpellShortcut8", EShortcut::BATTLE_SPELL_SHORTCUT_8 }, + {"battleSpellShortcut9", EShortcut::BATTLE_SPELL_SHORTCUT_9 }, {"spectateTrackHero", EShortcut::SPECTATE_TRACK_HERO }, {"spectateSkipBattle", EShortcut::SPECTATE_SKIP_BATTLE }, {"spectateSkipBattleResult", EShortcut::SPECTATE_SKIP_BATTLE_RESULT }, diff --git a/config/shortcutsConfig.json b/config/shortcutsConfig.json index 4359730be..3502b89d7 100644 --- a/config/shortcutsConfig.json +++ b/config/shortcutsConfig.json @@ -64,6 +64,16 @@ "battleOpenHoveredUnit": "V", "battleRetreat": "R", "battleSelectAction": "S", + "battleSpellShortcut0": "0", + "battleSpellShortcut1": "1", + "battleSpellShortcut2": "2", + "battleSpellShortcut3": "3", + "battleSpellShortcut4": "4", + "battleSpellShortcut5": "5", + "battleSpellShortcut6": "6", + "battleSpellShortcut7": "7", + "battleSpellShortcut8": "8", + "battleSpellShortcut9": "9", "battleSurrender": "S", "battleTacticsEnd": [ "Return", "Keypad Enter"], "battleTacticsNext": "Space", From fe408c203b58f2276c82d33ae946fed8a4057082 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Sun, 19 May 2024 23:30:50 +0200 Subject: [PATCH 007/100] right click + tweak --- client/battle/BattleInterfaceClasses.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index 2ea4015f0..1698e59e1 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -31,6 +31,7 @@ #include "../render/IFont.h" #include "../render/Graphics.h" #include "../widgets/Buttons.h" +#include "../widgets/CComponent.h" #include "../widgets/Images.h" #include "../widgets/Slider.h" #include "../widgets/TextControls.h" @@ -39,6 +40,7 @@ #include "../windows/CMessage.h" #include "../windows/CCreatureWindow.h" #include "../windows/CSpellWindow.h" +#include "../windows/InfoWindows.h" #include "../render/CAnimation.h" #include "../render/IRenderHandler.h" #include "../adventureMap/CInGameConsole.h" @@ -506,7 +508,7 @@ void QuickSpellPanel::mouseMoved(const Point & cursorPosition, const Point & las if( (cursorPosition.x <= initWidget->pos.x - 20 || cursorPosition.x >= initWidget->pos.x + initWidget->pos.w + 20 || cursorPosition.y <= initWidget->pos.y - pos.h - 20 || - (cursorPosition.y >= initWidget->pos.y && !initWidget->pos.isInside(cursorPosition))) && + (cursorPosition.y >= initWidget->pos.y + 5 && !initWidget->pos.isInside(cursorPosition))) && (!(panelSelect->isActive() || panelSelect->wasEnabled) || !panelSelect->pos.resize(20).isInside(cursorPosition)) ) close(); @@ -552,6 +554,9 @@ QuickSpellPanelSelect::QuickSpellPanelSelect(QuickSpellPanel * Parent) configID->String() = spell->identifier; parent->create(); }); + button->addPopupCallback([spell](){ + CRClickPopup::createAndPush(spell->getDescriptionTranslated(0), std::make_shared(ComponentType::SPELL, spell->id)); + }); button->setOverlay(std::make_shared(AnimationPath::builtin("spellint"), spellList[i]->getId().num + 1)); buttons.push_back(button); } From 13ebd026cc0827d382137565c74d605f2ea8a903 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Sun, 19 May 2024 23:43:23 +0200 Subject: [PATCH 008/100] remove unnecassary conditions --- client/battle/BattleWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/battle/BattleWindow.cpp b/client/battle/BattleWindow.cpp index 76e3ed12f..235e98085 100644 --- a/client/battle/BattleWindow.cpp +++ b/client/battle/BattleWindow.cpp @@ -121,7 +121,7 @@ BattleWindow::BattleWindow(BattleInterface & Owner): if(w) { auto hero = owner.getBattle()->battleGetMyHero(); - if(GH.screenDimensions().x >= 1000 && hero && owner.getBattle()->battleCanCastSpell(hero, spells::Mode::HERO) != ESpellCastProblem::NO_SPELLBOOK && settings["general"]["enableUiEnhancements"].Bool()) + if(hero && settings["general"]["enableUiEnhancements"].Bool()) { auto createQuickSpellPanelWindow = [](std::shared_ptr widget, BattleInterface & owner){ std::shared_ptr window = std::make_shared(widget, owner); From 49cb9854521026b8d4f3a55b3c76dc631263792f Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Mon, 20 May 2024 00:28:08 +0200 Subject: [PATCH 009/100] try to fix ci --- client/battle/BattleInterfaceClasses.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/battle/BattleInterfaceClasses.h b/client/battle/BattleInterfaceClasses.h index 6033d983c..e65add84d 100644 --- a/client/battle/BattleInterfaceClasses.h +++ b/client/battle/BattleInterfaceClasses.h @@ -22,6 +22,7 @@ class CGHeroInstance; struct BattleResult; struct InfoAboutHero; class CStack; +class CPlayerBattleCallback; namespace battle { @@ -45,7 +46,6 @@ class CPlayerInterface; class BattleRenderer; class VideoWidget; class QuickSpellPanel; -class CPlayerBattleCallback; /// Class which shows the console at the bottom of the battle screen and manages the text of the console class BattleConsole : public CIntObject, public IStatusBar From 11664b27b804bb9b0312bc0c293685232546566b Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Fri, 21 Jun 2024 22:34:14 +0200 Subject: [PATCH 010/100] fix for develop --- client/battle/BattleInterfaceClasses.cpp | 15 ++++++++++++--- client/battle/BattleWindow.cpp | 10 +++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index 1698e59e1..f71b9a377 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -449,7 +449,16 @@ void QuickSpellPanel::create() for(int i = 0; i < 10; i++) { std::string spellIdentifier = persistentStorage["quickSpell"][std::to_string(i)].String(); - SpellID id = SpellID::decode(spellIdentifier); + + SpellID id; + try + { + id = SpellID::decode(spellIdentifier); + } + catch(const IdentifierResolutionException& e) + { + id = SpellID::NONE; + } auto button = std::make_shared(Point(2, 1 + 37 * i), AnimationPath::builtin("spellint"), CButton::tooltip(), [this, id, hero](){ if(id.hasValue() && id.toSpell()->canBeCast(owner.getBattle().get(), spells::Mode::HERO, hero)) @@ -529,7 +538,7 @@ QuickSpellPanelSelect::QuickSpellPanelSelect(QuickSpellPanel * Parent) setEnabled(false); - std::vector> spellList; + std::vector> spellList; for (auto const & s : VLC->spellh->objects) if (s->isCombat() && !s->isSpecial() && !s->isCreatureAbility()) spellList.push_back(s); @@ -545,7 +554,7 @@ QuickSpellPanelSelect::QuickSpellPanelSelect(QuickSpellPanel * Parent) { int y = i % 10; int x = i / 10; - ConstTransitivePtr spell = spellList[i]; + std::shared_ptr spell = spellList[i]; auto button = std::make_shared(Point(2 + 50 * x, 1 + 37 * y), AnimationPath::builtin("spellint"), CButton::tooltip(), [this, spell](){ setEnabled(false); GH.windows().totalRedraw(); diff --git a/client/battle/BattleWindow.cpp b/client/battle/BattleWindow.cpp index 235e98085..55472fe96 100644 --- a/client/battle/BattleWindow.cpp +++ b/client/battle/BattleWindow.cpp @@ -66,7 +66,15 @@ BattleWindow::BattleWindow(BattleInterface & Owner): auto useSpellIfPossible = [this](int slot){ std::string spellIdentifier = persistentStorage["quickSpell"][std::to_string(slot)].String(); - SpellID id = SpellID::decode(spellIdentifier); + SpellID id; + try + { + id = SpellID::decode(spellIdentifier); + } + catch(const IdentifierResolutionException& e) + { + return; + } if(id.hasValue() && owner.getBattle()->battleGetMyHero() && id.toSpell()->canBeCast(owner.getBattle().get(), spells::Mode::HERO, owner.getBattle()->battleGetMyHero())) { owner.castThisSpell(id); From 39516b275ea16bbcce5dc07cf6360e48ce294244 Mon Sep 17 00:00:00 2001 From: altiereslima Date: Thu, 11 Jul 2024 19:11:54 -0300 Subject: [PATCH 011/100] Update Portuguese Translation --- Mods/vcmi/config/vcmi/portuguese.json | 6 +++--- launcher/translation/portuguese.ts | 19 ++++++++++--------- mapeditor/translation/portuguese.ts | 18 +++++++++--------- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/Mods/vcmi/config/vcmi/portuguese.json b/Mods/vcmi/config/vcmi/portuguese.json index 4405870fc..0e5391e17 100644 --- a/Mods/vcmi/config/vcmi/portuguese.json +++ b/Mods/vcmi/config/vcmi/portuguese.json @@ -33,7 +33,7 @@ "vcmi.heroOverview.startingArmy" : "Unidades Iniciais", "vcmi.heroOverview.warMachine" : "Máquinas de Guerra", - "vcmi.heroOverview.secondarySkills" : "Habilidades Secundárias", + "vcmi.heroOverview.secondarySkills" : "Habilid. Secundárias", "vcmi.heroOverview.spells" : "Feitiços", "vcmi.radialWheel.mergeSameUnit" : "Mesclar criaturas iguais", @@ -259,7 +259,7 @@ "vcmi.battleWindow.damageRetaliation.damage" : "(%DAMAGE).", "vcmi.battleWindow.damageRetaliation.damageKills" : "(%DAMAGE, %KILLS).", - "vcmi.battleWindow.killed" : "Eliminados", + "vcmi.battleWindow.killed" : "Mortos", "vcmi.battleWindow.accurateShot.resultDescription.0" : "%d %s morreram por tiros precisos!", "vcmi.battleWindow.accurateShot.resultDescription.1" : "%d %s morreu com um tiro preciso!", "vcmi.battleWindow.accurateShot.resultDescription.2" : "%d %s morreram por tiros precisos!", @@ -612,7 +612,7 @@ "core.bonus.SPELL_LIKE_ATTACK.description" : "Ataques com ${subtype.spell}", "core.bonus.SPELL_RESISTANCE_AURA.name" : "Aura de Resistência a Feitiços", "core.bonus.SPELL_RESISTANCE_AURA.description" : "Pilhas próximas ganham ${val}% de resistência a magia", - "core.bonus.SUMMON_GUARDIANS.name" : "Invocar Guardiões", + "core.bonus.SUMMON_GUARDIANS.name" : "Invocar Guardas", "core.bonus.SUMMON_GUARDIANS.description" : "No início da batalha, invoca ${subtype.creature} (${val}%)", "core.bonus.SYNERGY_TARGET.name" : "Alvo Sinergizável", "core.bonus.SYNERGY_TARGET.description" : "Esta criatura é vulnerável ao efeito de sinergia", diff --git a/launcher/translation/portuguese.ts b/launcher/translation/portuguese.ts index ce306c2e8..364c674e7 100644 --- a/launcher/translation/portuguese.ts +++ b/launcher/translation/portuguese.ts @@ -461,7 +461,7 @@ Downloading %1. %p% (%v MB out of %m MB) finished - + Baixando %1. %p% (%v MB de %m MB) concluído Downloading %s%. %p% (%v MB out of %m MB) finished @@ -733,12 +733,12 @@ Instalar o download realizado com sucesso? Show Tutorial again - + Mostrar o Tutorial novamente Reset - + Redefinir @@ -940,7 +940,7 @@ Modo de tela cheia exclusivo - o jogo cobrirá toda a sua tela e usará a resolu Autosave - Salvar automaticamente + Salvamento automático @@ -1268,7 +1268,7 @@ O instalador offline consiste em duas partes, .exe e .bin. Certifique-se de baix File cannot opened - + O arquivo não pode ser aberto @@ -1317,23 +1317,24 @@ Por favor, selecione o diretório com os dados do Heroes III instalados. You've provided GOG Galaxy installer! This file doesn't contain the game. Please download the offline backup game installer! - + Você forneceu o instalador do GOG Galaxy! Este arquivo não contém o jogo. Por favor, faça o download do instalador offline de backup do jogo! Stream error while extracting files! error reason: - + Erro de fluxo ao extrair arquivos! +Motivo do erro: Not a supported Inno Setup installer! - + Instalador do Inno Setup não suportado! Extracting error! - + Erro ao extrair! diff --git a/mapeditor/translation/portuguese.ts b/mapeditor/translation/portuguese.ts index 507fda720..d44bc8074 100644 --- a/mapeditor/translation/portuguese.ts +++ b/mapeditor/translation/portuguese.ts @@ -6,17 +6,17 @@ Army settings - Configurações do Exército + Configurações do exército Wide formation - Formação Aberta + Formação aberta Tight formation - Formação Compacta + Formação compacta @@ -29,7 +29,7 @@ Timed events - Eventos Temporizados + Eventos temporizados @@ -44,7 +44,7 @@ New event - Novo Evento + Novo evento @@ -57,12 +57,12 @@ Map name - Nome do Mapa + Nome do mapa Map description - Descrição do Mapa + Descrição do mapa @@ -88,7 +88,7 @@ Hero skills - Habilidades do Herói + Habilidades do herói @@ -172,7 +172,7 @@ Defeat message - Mensagem de Derrota + Mensagem de derrota From 3836f132d3fd84979d77fe453957479bb3aa44a4 Mon Sep 17 00:00:00 2001 From: Dydzio Date: Mon, 15 Jul 2024 00:41:05 +0200 Subject: [PATCH 012/100] Fix alternative actions to support more than 2 actions + simplify logic --- client/battle/BattleActionsController.cpp | 40 ++++++++++++----------- client/battle/BattleActionsController.h | 3 ++ client/battle/BattleWindow.cpp | 31 +++++++++--------- client/battle/BattleWindow.h | 2 +- lib/battle/PossiblePlayerBattleAction.h | 5 +++ 5 files changed, 45 insertions(+), 36 deletions(-) diff --git a/client/battle/BattleActionsController.cpp b/client/battle/BattleActionsController.cpp index 0228334ec..bd1f3fb99 100644 --- a/client/battle/BattleActionsController.cpp +++ b/client/battle/BattleActionsController.cpp @@ -985,26 +985,23 @@ void BattleActionsController::activateStack() std::list actionsToSelect; if(!possibleActions.empty()) { - switch(possibleActions.front().get()) + auto primaryAction = possibleActions.front().get(); + + if(primaryAction == PossiblePlayerBattleAction::SHOOT || primaryAction == PossiblePlayerBattleAction::AIMED_SPELL_CREATURE + || primaryAction == PossiblePlayerBattleAction::ANY_LOCATION || primaryAction == PossiblePlayerBattleAction::ATTACK_AND_RETURN) { - case PossiblePlayerBattleAction::SHOOT: - actionsToSelect.push_back(possibleActions.front()); - actionsToSelect.push_back(PossiblePlayerBattleAction::ATTACK); - break; - - case PossiblePlayerBattleAction::ATTACK_AND_RETURN: - actionsToSelect.push_back(possibleActions.front()); - actionsToSelect.push_back(PossiblePlayerBattleAction::WALK_AND_ATTACK); - break; - - case PossiblePlayerBattleAction::AIMED_SPELL_CREATURE: - actionsToSelect.push_back(possibleActions.front()); - actionsToSelect.push_back(PossiblePlayerBattleAction::ATTACK); - break; - case PossiblePlayerBattleAction::ANY_LOCATION: - actionsToSelect.push_back(possibleActions.front()); - actionsToSelect.push_back(PossiblePlayerBattleAction::ATTACK); - break; + actionsToSelect.push_back(possibleActions.front()); + + auto shootActionPredicate = [](const PossiblePlayerBattleAction& action) + { + return action.get() == PossiblePlayerBattleAction::SHOOT; + }; + bool hasShootSecondaryAction = std::any_of(possibleActions.begin() + 1, possibleActions.end(), shootActionPredicate); + + if(hasShootSecondaryAction) + actionsToSelect.emplace_back(PossiblePlayerBattleAction::SHOOT); + + actionsToSelect.emplace_back(PossiblePlayerBattleAction::ATTACK); //always allow melee attack as last option } } owner.windowObject->setAlternativeActions(actionsToSelect); @@ -1071,3 +1068,8 @@ void BattleActionsController::pushFrontPossibleAction(PossiblePlayerBattleAction { possibleActions.insert(possibleActions.begin(), action); } + +void BattleActionsController::resetCurrentStackPossibleActions() +{ + possibleActions = getPossibleActionsForStack(owner.stacksController->getActiveStack()); +} diff --git a/client/battle/BattleActionsController.h b/client/battle/BattleActionsController.h index 3c9b35660..a22f86251 100644 --- a/client/battle/BattleActionsController.h +++ b/client/battle/BattleActionsController.h @@ -122,4 +122,7 @@ public: /// inserts possible action in the beggining in order to prioritize it void pushFrontPossibleAction(PossiblePlayerBattleAction); + + /// resets possible actions to original state + void resetCurrentStackPossibleActions(); }; diff --git a/client/battle/BattleWindow.cpp b/client/battle/BattleWindow.cpp index 9f874f59e..5b228d82f 100644 --- a/client/battle/BattleWindow.cpp +++ b/client/battle/BattleWindow.cpp @@ -48,7 +48,7 @@ BattleWindow::BattleWindow(BattleInterface & owner): owner(owner), - defaultAction(PossiblePlayerBattleAction::INVALID) + lastAlternativeAction(PossiblePlayerBattleAction::INVALID) { OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; pos.w = 800; @@ -568,14 +568,18 @@ void BattleWindow::showAlternativeActionIcon(PossiblePlayerBattleAction action) void BattleWindow::setAlternativeActions(const std::list & actions) { + assert(actions.size() != 1); + alternativeActions = actions; - defaultAction = PossiblePlayerBattleAction::INVALID; + lastAlternativeAction = PossiblePlayerBattleAction::INVALID; + if(alternativeActions.size() > 1) - defaultAction = alternativeActions.back(); - if(!alternativeActions.empty()) + { + lastAlternativeAction = alternativeActions.back(); showAlternativeActionIcon(alternativeActions.front()); + } else - showAlternativeActionIcon(defaultAction); + showAlternativeActionIcon(PossiblePlayerBattleAction::INVALID); } void BattleWindow::bAutofightf() @@ -670,23 +674,18 @@ void BattleWindow::bSwitchActionf() { if(alternativeActions.empty()) return; - - if(alternativeActions.front() == defaultAction) - { - alternativeActions.push_back(alternativeActions.front()); - alternativeActions.pop_front(); - } - + auto actions = owner.actionsController->getPossibleActions(); - if(!actions.empty() && actions.front() == alternativeActions.front()) + + if(!actions.empty() && actions.front() != lastAlternativeAction) { owner.actionsController->removePossibleAction(alternativeActions.front()); - showAlternativeActionIcon(defaultAction); + showAlternativeActionIcon(*std::next(alternativeActions.begin())); } else { - owner.actionsController->pushFrontPossibleAction(alternativeActions.front()); - showAlternativeActionIcon(alternativeActions.front()); + owner.actionsController->resetCurrentStackPossibleActions(); + showAlternativeActionIcon(owner.actionsController->getPossibleActions().front()); } alternativeActions.push_back(alternativeActions.front()); diff --git a/client/battle/BattleWindow.h b/client/battle/BattleWindow.h index 5369cca9d..8b6b4f30d 100644 --- a/client/battle/BattleWindow.h +++ b/client/battle/BattleWindow.h @@ -65,7 +65,7 @@ class BattleWindow : public InterfaceObjectConfigurable /// management of alternative actions std::list alternativeActions; - PossiblePlayerBattleAction defaultAction; + PossiblePlayerBattleAction lastAlternativeAction; void showAlternativeActionIcon(PossiblePlayerBattleAction); /// flip battle queue visibility to opposite diff --git a/lib/battle/PossiblePlayerBattleAction.h b/lib/battle/PossiblePlayerBattleAction.h index 401c18d00..402980ded 100644 --- a/lib/battle/PossiblePlayerBattleAction.h +++ b/lib/battle/PossiblePlayerBattleAction.h @@ -74,6 +74,11 @@ public: { return action == other.action && spellToCast == other.spellToCast; } + + bool operator != (const PossiblePlayerBattleAction & other) const + { + return action != other.action || spellToCast != other.spellToCast; + } }; VCMI_LIB_NAMESPACE_END From 6119898932a1b5f24c53ef9a01041ef0aae1aa1c Mon Sep 17 00:00:00 2001 From: Dydzio Date: Mon, 15 Jul 2024 09:39:22 +0200 Subject: [PATCH 013/100] Add some useful comments --- client/battle/BattleActionsController.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/battle/BattleActionsController.cpp b/client/battle/BattleActionsController.cpp index bd1f3fb99..a4fe689a1 100644 --- a/client/battle/BattleActionsController.cpp +++ b/client/battle/BattleActionsController.cpp @@ -998,9 +998,13 @@ void BattleActionsController::activateStack() }; bool hasShootSecondaryAction = std::any_of(possibleActions.begin() + 1, possibleActions.end(), shootActionPredicate); - if(hasShootSecondaryAction) + if(hasShootSecondaryAction) //casters may have shooting capabilities, for example storm elementals actionsToSelect.emplace_back(PossiblePlayerBattleAction::SHOOT); + /* TODO: maybe it would also make sense to check spellcast as non-top priority action ("NO_SPELLCAST_BY_DEFAULT" bonus)? + * it would require going beyond this "if" block for melee casters + * F button helps, but some mod creatures may have that bonus and more than 1 castable spell */ + actionsToSelect.emplace_back(PossiblePlayerBattleAction::ATTACK); //always allow melee attack as last option } } From 04a81fee87bc72088137404ebf8cc8c4a0f3faff Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 15 Jul 2024 07:49:04 +0000 Subject: [PATCH 014/100] Reduce size of CGPathNode and AIPathNode --- AI/Nullkiller/Pathfinding/AINodeStorage.cpp | 3 +-- AI/Nullkiller/Pathfinding/AINodeStorage.h | 13 ++++++---- lib/pathfinder/CGPathNode.h | 27 +++++++++++---------- lib/pathfinder/CPathfinder.cpp | 4 +-- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp index b113e27ae..da7abceba 100644 --- a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp +++ b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp @@ -25,7 +25,7 @@ namespace NKAI { std::shared_ptr> AISharedStorage::shared; -uint64_t AISharedStorage::version = 0; +uint32_t AISharedStorage::version = 0; boost::mutex AISharedStorage::locker; std::set committedTiles; std::set committedTilesInitial; @@ -224,7 +224,6 @@ std::vector AINodeStorage::getInitialNodes() AIPathNode * initialNode = allocated.value(); - initialNode->inPQ = false; initialNode->pq = nullptr; initialNode->turns = actor->initialTurn; initialNode->moveRemains = actor->initialMovement; diff --git a/AI/Nullkiller/Pathfinding/AINodeStorage.h b/AI/Nullkiller/Pathfinding/AINodeStorage.h index 7a44de1dd..9e18bd7bd 100644 --- a/AI/Nullkiller/Pathfinding/AINodeStorage.h +++ b/AI/Nullkiller/Pathfinding/AINodeStorage.h @@ -44,14 +44,17 @@ enum DayFlags : ui8 struct AIPathNode : public CGPathNode { + std::shared_ptr specialAction; + + const AIPathNode * chainOther; + const ChainActor * actor; + uint64_t danger; uint64_t armyLoss; + uint32_t version; + int16_t manaCost; DayFlags dayFlags; - const AIPathNode * chainOther; - std::shared_ptr specialAction; - const ChainActor * actor; - uint64_t version; void addSpecialAction(std::shared_ptr action); @@ -152,7 +155,7 @@ class AISharedStorage std::shared_ptr> nodes; public: static boost::mutex locker; - static uint64_t version; + static uint32_t version; AISharedStorage(int3 mapSize); ~AISharedStorage(); diff --git a/lib/pathfinder/CGPathNode.h b/lib/pathfinder/CGPathNode.h index a850761b1..2598633cc 100644 --- a/lib/pathfinder/CGPathNode.h +++ b/lib/pathfinder/CGPathNode.h @@ -59,18 +59,22 @@ enum class EPathNodeAction : ui8 struct DLL_LINKAGE CGPathNode { + using TFibHeap = boost::heap::fibonacci_heap>>; using ELayer = EPathfindingLayer; + TFibHeap::handle_type pqHandle; + TFibHeap * pq; CGPathNode * theNodeBefore; + int3 coord; //coordinates ELayer layer; + + float cost; //total cost of the path to this tile measured in turns with fractions int moveRemains; //remaining movement points after hero reaches the tile ui8 turns; //how many turns we have to wait before reaching the tile - 0 means current turn - EPathAccessibility accessible; EPathNodeAction action; bool locked; - bool inPQ; CGPathNode() : coord(-1), @@ -89,9 +93,14 @@ struct DLL_LINKAGE CGPathNode cost = std::numeric_limits::max(); turns = 255; theNodeBefore = nullptr; - action = EPathNodeAction::UNKNOWN; - inPQ = false; pq = nullptr; + action = EPathNodeAction::UNKNOWN; + } + + STRONG_INLINE + bool inPQ() const + { + return pq != nullptr; } STRONG_INLINE @@ -109,7 +118,7 @@ struct DLL_LINKAGE CGPathNode bool getUpNode = value < cost; cost = value; // If the node is in the heap, update the heap. - if(inPQ && pq != nullptr) + if(inPQ()) { if(getUpNode) { @@ -155,14 +164,6 @@ struct DLL_LINKAGE CGPathNode return true; } - - using TFibHeap = boost::heap::fibonacci_heap>>; - - TFibHeap::handle_type pqHandle; - TFibHeap* pq; - -private: - float cost; //total cost of the path to this tile measured in turns with fractions }; struct DLL_LINKAGE CGPath diff --git a/lib/pathfinder/CPathfinder.cpp b/lib/pathfinder/CPathfinder.cpp index a4a725d35..cefb55673 100644 --- a/lib/pathfinder/CPathfinder.cpp +++ b/lib/pathfinder/CPathfinder.cpp @@ -82,9 +82,8 @@ CPathfinder::CPathfinder(CGameState * _gs, std::shared_ptr con void CPathfinder::push(CGPathNode * node) { - if(node && !node->inPQ) + if(node && !node->inPQ()) { - node->inPQ = true; node->pq = &this->pq; auto handle = pq.push(node); node->pqHandle = handle; @@ -96,7 +95,6 @@ CGPathNode * CPathfinder::topAndPop() auto * node = pq.top(); pq.pop(); - node->inPQ = false; node->pq = nullptr; return node; } From 97c9cd483bf418f75d2b8fd11d54431151b37b91 Mon Sep 17 00:00:00 2001 From: Alexander Wilms Date: Sun, 14 Jul 2024 23:16:30 +0200 Subject: [PATCH 015/100] Improve Linux developer documentation * List CMake options in table * Improve headline hierarchy * Add .clangd config file so compile_commands.json gets found out-of-the-box --- .clangd | 2 + docs/developers/Building_Linux.md | 59 ++++++++++++++++------------- docs/developers/Building_Windows.md | 2 + docs/developers/Building_iOS.md | 2 + docs/developers/Building_macOS.md | 2 + 5 files changed, 41 insertions(+), 26 deletions(-) create mode 100644 .clangd diff --git a/.clangd b/.clangd new file mode 100644 index 000000000..18890244f --- /dev/null +++ b/.clangd @@ -0,0 +1,2 @@ +CompileFlags: + CompilationDatabase: ../vcmi-build diff --git a/docs/developers/Building_Linux.md b/docs/developers/Building_Linux.md index 68284a98a..9b5144890 100644 --- a/docs/developers/Building_Linux.md +++ b/docs/developers/Building_Linux.md @@ -1,13 +1,13 @@ -# Compiling VCMI +# Building VCMI for Linux - Current baseline requirement for building is Ubuntu 20.04 - Supported C++ compilers for UNIX-like systems are GCC 9+ and Clang 13+ Older distributions and compilers might work, but they aren't tested by Github CI (Actions) -# Installing dependencies +# Prerequisites -## Prerequisites +## Installing dependencies To compile, the following packages (and their development counterparts) are needed to build: @@ -21,7 +21,7 @@ To compile, the following packages (and their development counterparts) are need - if you want to build scripting modules: LuaJIT - to speed up recompilation: Ccache -## On Debian-based systems (e.g. Ubuntu) +### On Debian-based systems (e.g. Ubuntu) For Ubuntu and Debian you need to install this list of packages: @@ -31,17 +31,17 @@ Alternatively if you have VCMI installed from repository or PPA you can use: `sudo apt-get build-dep vcmi` -## On RPM-based distributions (e.g. Fedora) +### On RPM-based distributions (e.g. Fedora) `sudo yum install cmake gcc-c++ SDL2-devel SDL2_image-devel SDL2_ttf-devel SDL2_mixer-devel boost boost-devel boost-filesystem boost-system boost-thread boost-program-options boost-locale boost-iostreams zlib-devel ffmpeg-devel ffmpeg-libs qt5-qtbase-devel tbb-devel luajit-devel liblzma-devel libsqlite3-devel fuzzylite-devel ccache` NOTE: `fuzzylite-devel` package is no longer available in recent version of Fedora, for example Fedora 38. It's not a blocker because VCMI bundles fuzzylite lib in its source code. -## On Arch-based distributions +### On Arch-based distributions On Arch-based distributions, there is a development package available for VCMI on the AUR. -It can be found at: +It can be found at https://aur.archlinux.org/packages/vcmi-git/ Information about building packages from the Arch User Repository (AUR) can be found at the Arch wiki. @@ -49,13 +49,13 @@ Information about building packages from the Arch User Repository (AUR) can be f We recommend the following directory structure: - . - ├── vcmi -> contains sources and is under git control - └── build -> contains build output, makefiles, object files,... +``` +. +├── vcmi -> contains sources and is under git control +└── vcmi-build -> contains build output, makefiles, object files,... +``` -Out-of-source builds keep the local repository clean so one doesn't have to manually exclude files generated during the build from commits. - -You can get latest sources with: +You can get the latest source code with: `git clone -b develop --recursive https://github.com/vcmi/vcmi.git` @@ -64,28 +64,34 @@ You can get latest sources with: ## Configuring Makefiles ```sh -mkdir build && cd build +mkdir vcmi-build +cd vcmi-build cmake -S ../vcmi ``` -# Additional options that you may want to use: +> [!NOTE] +> The `../vcmi` is not a typo, it will place Makefiles into the build dir as the build dir is your working dir when calling CMake. -## To enable debugging: -`cmake -S ../vcmi -D CMAKE_BUILD_TYPE=Debug` +### Additional options that you may want to use: -**Notice**: The ../vcmi/ is not a typo, it will place makefile scripts into the build dir as the build dir is your working dir when calling CMake. +| Option | Effect | +|--------|--------| +| -D CMAKE_BUILD_TYPE=Debug | Debug info and no optimizations | +| -D ENABLE_CCACHE:BOOL=ON | Speeds up recompilation | +| -D CMAKE_EXPORT_COMPILE_COMMANDS=ON | Creates `compile_commands.json` for `clangd` language server | +| -G Ninja | Use Ninja build system instead of make, which speeds up the build and doesn't require a `-j` flag | -## To use ccache: -`cmake -S ../vcmi -D ENABLE_CCACHE:BOOL=ON` +## Building -## Trigger build +``` +cmake --build . -j8 +``` -`cmake --build . -- -j2` -(-j2 = compile with 2 threads, you can specify any value) +(-j8 = compile with 8 threads, you can specify any value. ) -That will generate vcmiclient, vcmiserver, vcmilauncher as well as .so libraries in the **build/bin/** directory. +This will generate `vcmiclient`, `vcmiserver`, `vcmilauncher` as well as .so libraries in the `build/bin/` directory. -# Package building +# Packaging ## RPM package @@ -97,7 +103,8 @@ The first step is to prepare a RPM build environment. On Fedora systems you can sudo dnf install https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm ``` -NOTE: the stock ffmpeg from Fedora repo is no good as it has stripped lots of codecs +> [!NOTE] +> The stock ffmpeg from Fedora repo is no good as it lacks a lots of codecs 1. Perform a git clone from a tagged branch for the right Fedora version from https://github.com/rpmfusion/vcmi; for example for Fedora 38:
git clone -b f38 --single-branch https://github.com/rpmfusion/vcmi.git
diff --git a/docs/developers/Building_Windows.md b/docs/developers/Building_Windows.md index 257ef14ba..1ad4cc5f7 100644 --- a/docs/developers/Building_Windows.md +++ b/docs/developers/Building_Windows.md @@ -1,3 +1,5 @@ +# Building VCMI for Windows + # Preparations Windows builds can be made in more than one way and with more than one tool. This guide focuses on the simplest building process using Microsoft Visual Studio 2022 diff --git a/docs/developers/Building_iOS.md b/docs/developers/Building_iOS.md index 8383f59ae..a922e44a1 100644 --- a/docs/developers/Building_iOS.md +++ b/docs/developers/Building_iOS.md @@ -1,3 +1,5 @@ +# Building VCMI for iOS + ## Requirements 1. **macOS** diff --git a/docs/developers/Building_macOS.md b/docs/developers/Building_macOS.md index 67571f74d..71a1ae644 100644 --- a/docs/developers/Building_macOS.md +++ b/docs/developers/Building_macOS.md @@ -1,3 +1,5 @@ +# Building VCMI for macOS + ## Requirements 1. C++ toolchain, either of: From 775b20bfe2b42f578a66ce6a6435420455641dca Mon Sep 17 00:00:00 2001 From: Alexander Wilms Date: Mon, 15 Jul 2024 11:27:23 +0200 Subject: [PATCH 016/100] Remove .clangd config and add separate markdown file explaining some CMake options --- .clangd | 2 -- docs/developers/Building_Linux.md | 14 ++++-------- docs/developers/CMake.md | 37 +++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 12 deletions(-) delete mode 100644 .clangd create mode 100644 docs/developers/CMake.md diff --git a/.clangd b/.clangd deleted file mode 100644 index 18890244f..000000000 --- a/.clangd +++ /dev/null @@ -1,2 +0,0 @@ -CompileFlags: - CompilationDatabase: ../vcmi-build diff --git a/docs/developers/Building_Linux.md b/docs/developers/Building_Linux.md index 9b5144890..a0b72f5e4 100644 --- a/docs/developers/Building_Linux.md +++ b/docs/developers/Building_Linux.md @@ -52,7 +52,7 @@ We recommend the following directory structure: ``` . ├── vcmi -> contains sources and is under git control -└── vcmi-build -> contains build output, makefiles, object files,... +└── build -> contains build output, makefiles, object files,... ``` You can get the latest source code with: @@ -64,22 +64,16 @@ You can get the latest source code with: ## Configuring Makefiles ```sh -mkdir vcmi-build -cd vcmi-build +mkdir build +cd build cmake -S ../vcmi ``` > [!NOTE] > The `../vcmi` is not a typo, it will place Makefiles into the build dir as the build dir is your working dir when calling CMake. -### Additional options that you may want to use: +See [CMake](CMake.md) for a list of options -| Option | Effect | -|--------|--------| -| -D CMAKE_BUILD_TYPE=Debug | Debug info and no optimizations | -| -D ENABLE_CCACHE:BOOL=ON | Speeds up recompilation | -| -D CMAKE_EXPORT_COMPILE_COMMANDS=ON | Creates `compile_commands.json` for `clangd` language server | -| -G Ninja | Use Ninja build system instead of make, which speeds up the build and doesn't require a `-j` flag | ## Building diff --git a/docs/developers/CMake.md b/docs/developers/CMake.md new file mode 100644 index 000000000..720491e4b --- /dev/null +++ b/docs/developers/CMake.md @@ -0,0 +1,37 @@ +# CMake options + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OptionEffect
-D CMAKE_BUILD_TYPE=DebugEnables debug info and disables optimizations
-D CMAKE_EXPORT_COMPILE_COMMANDS=ONCreates compile_commands.json for clangd language server +

+ For clangd to find the JSON, create a file named .clangd +
.
+├── vcmi -> contains sources and is under git control
+├── build -> contains build output, makefiles, object files,...
+└── .clangd
+ with the following content +
CompileFlags:
CompilationDatabase: build
-D ENABLE_CCACHE:BOOL=ONSpeeds up recompilation
-G NinjaUse Ninja build system instead of make, which speeds up the build and doesn't require a -j flag
From 4b91d6e6b0ba6bed8fd4847cbaa4b019843139d2 Mon Sep 17 00:00:00 2001 From: Dydzio Date: Mon, 15 Jul 2024 18:47:29 +0200 Subject: [PATCH 017/100] Change shortcuts to those that non-VCMI players are used to --- config/shortcutsConfig.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/shortcutsConfig.json b/config/shortcutsConfig.json index 4359730be..ebf1463fb 100644 --- a/config/shortcutsConfig.json +++ b/config/shortcutsConfig.json @@ -55,7 +55,7 @@ "adventureZoomOut": "Keypad -", "adventureZoomReset": "Backspace", "battleAutocombat": "A", - "battleAutocombatEnd": "E", + "battleAutocombatEnd": "Q", "battleCastSpell": "C", "battleConsoleDown": "Down", "battleConsoleUp": "Up", @@ -68,8 +68,8 @@ "battleTacticsEnd": [ "Return", "Keypad Enter"], "battleTacticsNext": "Space", "battleToggleHeroesStats": [], - "battleToggleQueue": "Q", - "battleUseCreatureSpell": "F", + "battleToggleQueue": "Z", + "battleUseCreatureSpell": ["F", "G"], "battleWait": "W", "exchangeArmySwap": "F10", "exchangeArmyToLeft": [], From 037ee4395eeef623be93fea4424806ab112913da Mon Sep 17 00:00:00 2001 From: Dydzio Date: Mon, 15 Jul 2024 20:22:19 +0200 Subject: [PATCH 018/100] Allow opening single hero when two heroes in town via shift+click --- client/windows/CCastleInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 077269bc8..c8b36a4cd 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -428,7 +428,7 @@ void CHeroGSlot::clickPressed(const Point & cursorPosition) { setHighlight(false); - if(other->hero) + if(other->hero && !GH.isKeyboardShiftDown()) LOCPLINT->showHeroExchange(hero->id, other->hero->id); else LOCPLINT->openHeroWindow(hero); From 358008fca957b2cc4c3e30ecdb91346b72b3fe2b Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 15 Jul 2024 21:45:51 +0000 Subject: [PATCH 019/100] It is now possible to define multiple town themes --- client/media/CMusicHandler.cpp | 13 ++++++++++++- client/windows/CCastleInterface.cpp | 2 +- config/factions/castle.json | 2 +- config/factions/conflux.json | 2 +- config/factions/dungeon.json | 2 +- config/factions/fortress.json | 2 +- config/factions/inferno.json | 2 +- config/factions/necropolis.json | 2 +- config/factions/rampart.json | 2 +- config/factions/stronghold.json | 2 +- config/factions/tower.json | 2 +- config/schemas/faction.json | 10 +++++++--- docs/modders/Entities_Format/Faction_Format.md | 7 ++++--- lib/CTownHandler.cpp | 11 ++++++++++- lib/CTownHandler.h | 2 +- 15 files changed, 44 insertions(+), 19 deletions(-) diff --git a/client/media/CMusicHandler.cpp b/client/media/CMusicHandler.cpp index 0721b9354..913a95ef6 100644 --- a/client/media/CMusicHandler.cpp +++ b/client/media/CMusicHandler.cpp @@ -16,6 +16,7 @@ #include "../renderSDL/SDLRWwrapper.h" #include "../../lib/CRandomGenerator.h" +#include "../../lib/CTownHandler.h" #include "../../lib/TerrainHandler.h" #include "../../lib/filesystem/Filesystem.h" @@ -64,7 +65,17 @@ void CMusicHandler::loadTerrainMusicThemes() { for(const auto & terrain : CGI->terrainTypeHandler->objects) { - addEntryToSet("terrain_" + terrain->getJsonKey(), terrain->musicFilename); + for(const auto & filename : terrain->musicFilename) + addEntryToSet("terrain_" + terrain->getJsonKey(), filename); + } + + for(const auto & faction : CGI->townh->objects) + { + if (!faction || !faction->hasTown()) + continue; + + for(const auto & filename : faction->town->clientInfo.musicTheme) + addEntryToSet("faction_" + faction->getJsonKey(), filename); } } diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 3b805687f..1a5ba635c 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -1283,7 +1283,7 @@ CCastleInterface::CCastleInterface(const CGTownInstance * Town, const CGTownInst recreateIcons(); if (!from) adventureInt->onAudioPaused(); - CCS->musich->playMusic(town->town->clientInfo.musicTheme, true, false); + CCS->musich->playMusicFromSet("faction", town->town->faction->getJsonKey(), true, false); } CCastleInterface::~CCastleInterface() diff --git a/config/factions/castle.json b/config/factions/castle.json index 5026120a1..1d8c02d65 100644 --- a/config/factions/castle.json +++ b/config/factions/castle.json @@ -119,7 +119,7 @@ "dwellingUpLvl7": { "animation" : "TBCSUP_6.def", "x" : 303, "y" : 0, "z" : -1, "border" : "TOCSANG2.bmp", "area" : "TZCSANG2.bmp" } }, - "musicTheme" : "music/CstleTown", + "musicTheme" : [ "music/CstleTown" ], "defaultTavern" : 5, "tavernVideo" : "TAVERN.BIK", "guildBackground" : "TPMAGE.bmp", diff --git a/config/factions/conflux.json b/config/factions/conflux.json index fd431f6b1..69307372c 100644 --- a/config/factions/conflux.json +++ b/config/factions/conflux.json @@ -123,7 +123,7 @@ "dwellingUpLvl7": { "animation" : "TBELUP_6.def", "x" : 43, "y" : 0, "z" : -2, "border" : "TOELUP_6.bmp", "area" : "TZELUP_6.bmp" } }, - "musicTheme" : "music/ElemTown", + "musicTheme" : [ "music/ElemTown" ], "defaultTavern" : 5, "tavernVideo" : "TAVERN.BIK", "guildBackground" : "TPMAGE.bmp", diff --git a/config/factions/dungeon.json b/config/factions/dungeon.json index f864b282e..84a4d1b13 100644 --- a/config/factions/dungeon.json +++ b/config/factions/dungeon.json @@ -119,7 +119,7 @@ "dwellingUpLvl7": { "animation" : "TBDNUP_6.def", "x" : 550, "y" : 0, "z" : -1, "border" : "TODDRA2A.bmp", "area" : "TZDDRA2A.bmp" } }, - "musicTheme" : "music/Dungeon", + "musicTheme" : [ "music/Dungeon" ], "defaultTavern" : 5, "tavernVideo" : "TAVERN.BIK", "guildBackground" : "TPMAGE.bmp", diff --git a/config/factions/fortress.json b/config/factions/fortress.json index 38002253a..082dfd38f 100644 --- a/config/factions/fortress.json +++ b/config/factions/fortress.json @@ -119,7 +119,7 @@ "dwellingUpLvl7": { "animation" : "TBFRUP_6.def", "x" : 587, "y" : 263, "z" : 5, "border" : "TOFHYD2A.bmp", "area" : "TZFHYD2A.bmp" } }, - "musicTheme" : "music/FortressTown", + "musicTheme" : [ "music/FortressTown" ], "defaultTavern" : 5, "tavernVideo" : "TAVERN.BIK", "guildBackground" : "TPMAGE.bmp", diff --git a/config/factions/inferno.json b/config/factions/inferno.json index 9a8c86912..db1b7675a 100644 --- a/config/factions/inferno.json +++ b/config/factions/inferno.json @@ -120,7 +120,7 @@ "dwellingUpLvl7": { "animation" : "TBINUP_6.def", "x" : 420, "y" : 105, "z" : -1, "border" : "TOIDVL2.bmp", "area" : "TZIDVL2.bmp" } }, - "musicTheme" : "music/InfernoTown", + "musicTheme" : [ "music/InfernoTown" ], "defaultTavern" : 5, "tavernVideo" : "TAVERN.BIK", "guildBackground" : "TPMAGE.bmp", diff --git a/config/factions/necropolis.json b/config/factions/necropolis.json index 29951540d..425a3a550 100644 --- a/config/factions/necropolis.json +++ b/config/factions/necropolis.json @@ -124,7 +124,7 @@ "dwellingUpLvl7": { "animation" : "TBNCUP_6.def", "x" : 662, "y" : 23, "border" : "TONBON2.bmp", "area" : "TZNBON2.bmp" } }, - "musicTheme" : "music/NecroTown", + "musicTheme" : [ "music/NecroTown" ], "defaultTavern" : 5, "tavernVideo" : "TAVERN.BIK", "guildBackground" : "TPMAGE.bmp", diff --git a/config/factions/rampart.json b/config/factions/rampart.json index 8893fe56a..4c2ba145d 100644 --- a/config/factions/rampart.json +++ b/config/factions/rampart.json @@ -123,7 +123,7 @@ "dwellingUpLvl7": { "animation" : "TBRMUP_6.def", "x" : 502, "y" : 5, "z" : -5, "border" : "TORDR2AA.bmp", "area" : "TZRDR2AA.bmp" } }, - "musicTheme" : "music/Rampart", + "musicTheme" : [ "music/Rampart" ], "defaultTavern" : 5, "tavernVideo" : "TAVERN.BIK", "guildBackground" : "TPMAGE.bmp", diff --git a/config/factions/stronghold.json b/config/factions/stronghold.json index 5e8afda84..c1eeb8397 100644 --- a/config/factions/stronghold.json +++ b/config/factions/stronghold.json @@ -117,7 +117,7 @@ "dwellingUpLvl7": { "animation" : "TBSTUP_6.def", "x" : 604, "y" : 0, "border" : "TOSBEH2A.bmp", "area" : "TZSBEH2A.bmp" } }, - "musicTheme" : "music/Stronghold", + "musicTheme" : [ "music/Stronghold" ], "defaultTavern" : 5, "tavernVideo" : "TAVERN.BIK", "guildBackground" : "TPMAGE.bmp", diff --git a/config/factions/tower.json b/config/factions/tower.json index 488759b91..edc3ab29f 100644 --- a/config/factions/tower.json +++ b/config/factions/tower.json @@ -118,7 +118,7 @@ "dwellingUpLvl7": { "animation" : "TBTWUP_6.def", "x" : 75, "y" : 91, "z" : -1, "border" : "TOTTIT2.bmp", "area" : "TZTTIT2.bmp" } }, - "musicTheme" : "music/TowerTown", + "musicTheme" : [ "music/TowerTown" ], "defaultTavern" : 5, "tavernVideo" : "TAVERN.BIK", "guildBackground" : "TPMAGE.bmp", diff --git a/config/schemas/faction.json b/config/schemas/faction.json index 38f9f9e85..18bfabcd1 100644 --- a/config/schemas/faction.json +++ b/config/schemas/faction.json @@ -151,9 +151,13 @@ "$ref" : "townSiege.json" }, "musicTheme" : { - "type" : "string", - "description" : "Path to town music theme", - "format" : "musicFile" + "type" : "array", + "description" : "Path to town music themes", + "minItems" : 1, + "items" : { + "type" : "string", + "format" : "musicFile" + } }, "tavernVideo" : { "type" : "string", diff --git a/docs/modders/Entities_Format/Faction_Format.md b/docs/modders/Entities_Format/Faction_Format.md index 6fe6c4236..3ec136f5c 100644 --- a/docs/modders/Entities_Format/Faction_Format.md +++ b/docs/modders/Entities_Format/Faction_Format.md @@ -30,7 +30,7 @@ In order to make functional town you also need: ### Music -- Town theme music track (1 music file) +- Town theme music track (at least 1 music file) ### Buildings @@ -150,8 +150,9 @@ Each town requires a set of buildings (Around 30-45 buildings) } } }, - // Path to town music theme, e.g. "music/castleTheme" - "musicTheme" : "", + // List of town music themes, e.g. [ "music/castleTheme" ] + // At least one music file is required + "musicTheme" : [ "" ], // List of structures which represents visible graphical objects on town screen. // See detailed description below diff --git a/lib/CTownHandler.cpp b/lib/CTownHandler.cpp index e1469c526..2c118d1f2 100644 --- a/lib/CTownHandler.cpp +++ b/lib/CTownHandler.cpp @@ -902,8 +902,17 @@ void CTownHandler::loadClientData(CTown &town, const JsonNode & source) const readIcon(source["icons"]["fort"]["normal"], info.iconSmall[1][0], info.iconLarge[1][0]); readIcon(source["icons"]["fort"]["built"], info.iconSmall[1][1], info.iconLarge[1][1]); + if (source["musicTheme"].isVector()) + { + for (auto const & entry : source["musicTheme"].Vector()) + info.musicTheme.push_back(AudioPath::fromJson(entry)); + } + else + { + info.musicTheme.push_back(AudioPath::fromJson(source["musicTheme"])); + } + info.hallBackground = ImagePath::fromJson(source["hallBackground"]); - info.musicTheme = AudioPath::fromJson(source["musicTheme"]); info.townBackground = ImagePath::fromJson(source["townBackground"]); info.guildWindow = ImagePath::fromJson(source["guildWindow"]); info.buildingsIcons = AnimationPath::fromJson(source["buildingsIcons"]); diff --git a/lib/CTownHandler.h b/lib/CTownHandler.h index 758eac6fe..12c8200b7 100644 --- a/lib/CTownHandler.h +++ b/lib/CTownHandler.h @@ -253,7 +253,7 @@ public: std::string iconSmall[2][2]; /// icon names used during loading std::string iconLarge[2][2]; VideoPath tavernVideo; - AudioPath musicTheme; + std::vector musicTheme; ImagePath townBackground; ImagePath guildBackground; ImagePath guildWindow; From 9c5d5d7c5a9305b3640789e5340b9f5dccd702d3 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 15 Jul 2024 21:46:23 +0000 Subject: [PATCH 020/100] It is now possible to define custom battle opening sound and custom music for a battlefield --- client/battle/BattleInterface.cpp | 20 +++++++++++++++++-- config/schemas/battlefield.json | 12 +++++++++++ .../Entities_Format/Battlefield_Format.md | 16 ++++++++++----- lib/BattleFieldHandler.cpp | 3 +++ lib/BattleFieldHandler.h | 2 ++ 5 files changed, 46 insertions(+), 7 deletions(-) diff --git a/client/battle/BattleInterface.cpp b/client/battle/BattleInterface.cpp index f9411a9fb..8309add13 100644 --- a/client/battle/BattleInterface.cpp +++ b/client/battle/BattleInterface.cpp @@ -35,6 +35,7 @@ #include "../adventureMap/AdventureMapInterface.h" #include "../../CCallback.h" +#include "../../lib/BattleFieldHandler.h" #include "../../lib/CStack.h" #include "../../lib/CConfigHandler.h" #include "../../lib/CGeneralTextHandler.h" @@ -113,6 +114,9 @@ void BattleInterface::playIntroSoundAndUnlockInterface() onIntroSoundPlayed(); }; + auto bfieldType = getBattle()->battleGetBattlefieldType(); + const auto & battlefieldSound = bfieldType.getInfo()->musicFilename; + std::vector battleIntroSounds = { soundBase::battle00, soundBase::battle01, @@ -120,7 +124,13 @@ void BattleInterface::playIntroSoundAndUnlockInterface() soundBase::battle05, soundBase::battle06, soundBase::battle07 }; - int battleIntroSoundChannel = CCS->soundh->playSoundFromSet(battleIntroSounds); + int battleIntroSoundChannel = -1; + + if (!battlefieldSound.empty()) + battleIntroSoundChannel = CCS->soundh->playSound(battlefieldSound); + else + battleIntroSoundChannel = CCS->soundh->playSoundFromSet(battleIntroSounds); + if (battleIntroSoundChannel != -1) { CCS->soundh->setCallback(battleIntroSoundChannel, onIntroPlayed); @@ -144,7 +154,13 @@ void BattleInterface::onIntroSoundPlayed() if (openingPlaying()) openingEnd(); - CCS->musich->playMusicFromSet("battle", true, true); + auto bfieldType = getBattle()->battleGetBattlefieldType(); + const auto & battlefieldMusic = bfieldType.getInfo()->musicFilename; + + if (!battlefieldMusic.empty()) + CCS->musich->playMusic(battlefieldMusic, true, true); + else + CCS->musich->playMusicFromSet("battle", true, true); } void BattleInterface::openingEnd() diff --git a/config/schemas/battlefield.json b/config/schemas/battlefield.json index 3135c2fb4..0a19116ae 100644 --- a/config/schemas/battlefield.json +++ b/config/schemas/battlefield.json @@ -24,6 +24,18 @@ "format" : "imageFile", "description" : "Background image for this battlefield" }, + "music" : + { + "description" : "Optional, filename for custom music to play during combat on this terrain", + "type" : "string", + "format" : "musicFile" + }, + "openingSound" : + { + "description" : "Optional, filename for custom sound to play during combat opening on this terrain", + "type" : "string", + "format" : "musicFile" + }, "impassableHexes" : { "type" : "array", "description" : "List of battle hexes that will be always blocked on this battlefield (e.g. ship to ship battles)", diff --git a/docs/modders/Entities_Format/Battlefield_Format.md b/docs/modders/Entities_Format/Battlefield_Format.md index 2d8164228..e6f7a5eb0 100644 --- a/docs/modders/Entities_Format/Battlefield_Format.md +++ b/docs/modders/Entities_Format/Battlefield_Format.md @@ -1,17 +1,23 @@ ```jsonc // Human-readable name of the battlefield - "name" : "" + "name" : "", // If set to true, obstacles will be taken from "specialBattlefields" property of an obstacle // If set to false, obstacles will be taken from "allowedTerrains" instead - "isSpecial" : false + "isSpecial" : false, // List of bonuses that will affect all battles on this battlefield - "bonuses" : { BONUS_FORMAT } + "bonuses" : { BONUS_FORMAT }, // Background image for this battlefield - "graphics" : "" + "graphics" : "", + + // Optional, filename for custom music to play during combat on this terrain + "music" : "", + + // Optional, filename for custom sound to play during combat opening on this terrain + "openingSound" : "", // List of battle hexes that will be always blocked on this battlefield (e.g. ship to ship battles) - "impassableHexes" : [ 10, 20, 50 ] + "impassableHexes" : [ 10, 20, 50 ], ``` \ No newline at end of file diff --git a/lib/BattleFieldHandler.cpp b/lib/BattleFieldHandler.cpp index 7482eff5a..98a6a9a58 100644 --- a/lib/BattleFieldHandler.cpp +++ b/lib/BattleFieldHandler.cpp @@ -40,6 +40,9 @@ std::shared_ptr BattleFieldHandler::loadFromJson(const std::str for(auto node : json["impassableHexes"].Vector()) info->impassableHexes.emplace_back(node.Integer()); + info->openingSoundFilename = AudioPath::fromJson(json["openingSound"]); + info->musicFilename = AudioPath::fromJson(json["music"]); + return info; } diff --git a/lib/BattleFieldHandler.h b/lib/BattleFieldHandler.h index ca2ddc8ce..f8b814f50 100644 --- a/lib/BattleFieldHandler.h +++ b/lib/BattleFieldHandler.h @@ -32,6 +32,8 @@ public: std::string icon; si32 iconIndex; std::vector impassableHexes; + AudioPath openingSoundFilename; + AudioPath musicFilename; BattleFieldInfo() : BattleFieldInfo(BattleField::NONE, "") From d59744f26f0a57405c7d62cc6f71b951b1a8c404 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 15 Jul 2024 21:46:41 +0000 Subject: [PATCH 021/100] It is now possible to define multiple music themes for terrains --- config/schemas/terrain.json | 10 +++++++--- config/terrains.json | 20 +++++++++---------- .../modders/Entities_Format/Terrain_Format.md | 4 ++-- lib/TerrainHandler.cpp | 11 +++++++++- lib/TerrainHandler.h | 2 +- 5 files changed, 30 insertions(+), 17 deletions(-) diff --git a/config/schemas/terrain.json b/config/schemas/terrain.json index 23aa598a3..980193db9 100644 --- a/config/schemas/terrain.json +++ b/config/schemas/terrain.json @@ -97,9 +97,13 @@ }, "music" : { - "type" : "string", - "description" : "Music filename to play on this terrain on adventure map", - "format" : "musicFile" + "description" : "Music filenames to play on this terrain on adventure map", + "type" : "array", + "minItems" : 1, + "items" : { + "type" : "string", + "format" : "musicFile" + } }, "sounds" : { diff --git a/config/terrains.json b/config/terrains.json index e649fcdb2..4bbf8f5ad 100644 --- a/config/terrains.json +++ b/config/terrains.json @@ -5,7 +5,7 @@ "moveCost" : 100, "minimapUnblocked" : [ 82, 56, 8 ], "minimapBlocked" : [ 57, 40, 8 ], - "music" : "Dirt.mp3", + "music" : [ "Dirt.mp3" ], "tiles" : "DIRTTL", "type" : ["SURFACE"], "shortIdentifier" : "dt", @@ -21,7 +21,7 @@ "moveCost" : 150, "minimapUnblocked" : [ 222, 207, 140 ], "minimapBlocked" : [ 165, 158, 107 ], - "music" : "Sand.mp3", + "music" : [ "Sand.mp3" ], "tiles" : "SANDTL", "type" : ["SURFACE"], "shortIdentifier" : "sa", @@ -38,7 +38,7 @@ "moveCost" : 100, "minimapUnblocked" : [ 0, 65, 0 ], "minimapBlocked" : [ 0, 48, 0 ], - "music" : "Grass.mp3", + "music" : [ "Grass.mp3" ], "tiles" : "GRASTL", "type" : ["SURFACE"], "shortIdentifier" : "gr", @@ -53,7 +53,7 @@ "moveCost" : 150, "minimapUnblocked" : [ 181, 199, 198 ], "minimapBlocked" : [ 140, 158, 156 ], - "music" : "Snow.mp3", + "music" : [ "Snow.mp3" ], "tiles" : "SNOWTL", "type" : ["SURFACE"], "shortIdentifier" : "sn", @@ -68,7 +68,7 @@ "moveCost" : 175, "minimapUnblocked" : [ 74, 134, 107 ], "minimapBlocked" : [ 33, 89, 66 ], - "music" : "Swamp.mp3", + "music" : [ "Swamp.mp3" ], "tiles" : "SWMPTL", "type" : ["SURFACE"], "shortIdentifier" : "sw", @@ -83,7 +83,7 @@ "moveCost" : 125, "minimapUnblocked" : [ 132, 113, 49 ], "minimapBlocked" : [ 99, 81, 33 ], - "music" : "Rough.mp3", + "music" : [ "Rough.mp3" ], "tiles" : "ROUGTL", "type" : ["SURFACE"], "shortIdentifier" : "rg", @@ -98,7 +98,7 @@ "moveCost" : 100, "minimapUnblocked" : [ 132, 48, 0 ], "minimapBlocked" : [ 90, 8, 0 ], - "music" : "Underground.mp3", + "music" : [ "Underground.mp3" ], "tiles" : "SUBBTL", "type" : [ "SUB" ], "shortIdentifier" : "sb", @@ -114,7 +114,7 @@ "moveCost" : 100, "minimapUnblocked" : [ 74, 73, 74 ], "minimapBlocked" : [ 41, 40, 41 ], - "music" : "Lava.mp3", + "music" : [ "Lava.mp3" ], "tiles" : "LAVATL", "type" : ["SUB", "SURFACE"], "shortIdentifier" : "lv", @@ -133,7 +133,7 @@ "moveCost" : 100, "minimapUnblocked" : [ 8, 81, 148 ], "minimapBlocked" : [ 8, 81, 148 ], - "music" : "Water.mp3", + "music" : [ "Water.mp3" ], "tiles" : "WATRTL", "type" : [ "WATER" ], "shortIdentifier" : "wt", @@ -156,7 +156,7 @@ "moveCost" : -1, "minimapUnblocked" : [ 0, 0, 0 ], "minimapBlocked" : [ 0, 0, 0 ], - "music" : "Underground.mp3", // Impossible in H3 + "music" : [ "Underground.mp3" ], // Impossible in H3 "tiles" : "ROCKTL", "type" : [ "ROCK" ], "shortIdentifier" : "rc", diff --git a/docs/modders/Entities_Format/Terrain_Format.md b/docs/modders/Entities_Format/Terrain_Format.md index 287ff4386..aff78b580 100644 --- a/docs/modders/Entities_Format/Terrain_Format.md +++ b/docs/modders/Entities_Format/Terrain_Format.md @@ -48,8 +48,8 @@ // Color of terrain on minimap with unpassable objects. RGB triplet, 0-255 range "minimapBlocked" : [ 150, 100, 50 ], - // Music filename to play on this terrain on adventure map - "music" : "", + // List of music files to play on this terrain on adventure map. At least one file is required + "music" : [ "" ], "sounds" : { // List of ambient sounds for this terrain diff --git a/lib/TerrainHandler.cpp b/lib/TerrainHandler.cpp index d66bf521d..13789cacf 100644 --- a/lib/TerrainHandler.cpp +++ b/lib/TerrainHandler.cpp @@ -28,7 +28,16 @@ std::shared_ptr TerrainTypeHandler::loadFromJson( const std::string info->identifier = identifier; info->modScope = scope; info->moveCost = static_cast(json["moveCost"].Integer()); - info->musicFilename = AudioPath::fromJson(json["music"]); + if (json["music"].isVector()) + { + for (auto const & entry : json["music"].Vector()) + info->musicFilename.push_back(AudioPath::fromJson(entry)); + } + else + { + info->musicFilename.push_back(AudioPath::fromJson(json["music"])); + } + info->tilesFilename = AnimationPath::fromJson(json["tiles"]); info->horseSound = AudioPath::fromJson(json["horseSound"]); info->horseSoundPenalty = AudioPath::fromJson(json["horseSoundPenalty"]); diff --git a/lib/TerrainHandler.h b/lib/TerrainHandler.h index ae2bed46d..9d2e71d11 100644 --- a/lib/TerrainHandler.h +++ b/lib/TerrainHandler.h @@ -67,7 +67,7 @@ public: ColorRGBA minimapBlocked; ColorRGBA minimapUnblocked; std::string shortIdentifier; - AudioPath musicFilename; + std::vector musicFilename; AnimationPath tilesFilename; std::string terrainViewPatterns; AudioPath horseSound; From a273eb6413893791f9edf20b790684b25baf863c Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 16 Jul 2024 09:53:16 +0000 Subject: [PATCH 022/100] Fix crash on attempt to play audio from video without such stream --- client/media/CVideoHandler.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/media/CVideoHandler.cpp b/client/media/CVideoHandler.cpp index e8537cf14..f2d929f02 100644 --- a/client/media/CVideoHandler.cpp +++ b/client/media/CVideoHandler.cpp @@ -494,7 +494,11 @@ std::pair, si64> CAudioInstance::extractAudio(const Vide if (!openInput(videoToOpen)) return { nullptr, 0}; openContext(); - openCodec(findAudioStream()); + + int audioStreamIndex = findAudioStream(); + if (audioStreamIndex == -1) + return { nullptr, 0}; + openCodec(audioStreamIndex); const auto * codecpar = getCodecParameters(); From e59880a37c1c51dd13bf3dc8c5da4380972af990 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 16 Jul 2024 09:53:32 +0000 Subject: [PATCH 023/100] Remove unnecessary logging --- client/media/CVideoHandler.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/client/media/CVideoHandler.cpp b/client/media/CVideoHandler.cpp index f2d929f02..5e0c86303 100644 --- a/client/media/CVideoHandler.cpp +++ b/client/media/CVideoHandler.cpp @@ -627,7 +627,6 @@ bool CVideoPlayer::openAndPlayVideoImpl(const VideoPath & name, const Point & po auto timePointAfterPresent = boost::chrono::steady_clock::now(); auto timeSpentBusy = boost::chrono::duration_cast(timePointAfterPresent - lastTimePoint); - logGlobal->info("Sleeping for %d", (targetFrameTime - timeSpentBusy).count()); if(targetFrameTime > timeSpentBusy) boost::this_thread::sleep_for(targetFrameTime - timeSpentBusy); From 29d3de2807bc4e4c3baf5413cd143533d423e367 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 16 Jul 2024 11:41:40 +0000 Subject: [PATCH 024/100] Fix missing video on winning the battle --- client/battle/BattleInterfaceClasses.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index d6e2ca161..97c62659a 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -741,7 +741,7 @@ BattleResultResources BattleResultWindow::getResources(const BattleResult & br) else { resources.musicName = AudioPath::builtin("Music/Win Battle"); - resources.prologueVideo = VideoPath(); + resources.prologueVideo = VideoPath::builtin("WIN3.BIK"); resources.loopedVideo = VideoPath::builtin("WIN3.BIK"); } From e4be4c306157a53728bf715f38ed6cbfc272899a Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 16 Jul 2024 12:49:24 +0000 Subject: [PATCH 025/100] Fix playback of 8-bit audio found in h3intro.smk in some version not from gog.com --- client/media/CVideoHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/media/CVideoHandler.cpp b/client/media/CVideoHandler.cpp index 5e0c86303..82777e996 100644 --- a/client/media/CVideoHandler.cpp +++ b/client/media/CVideoHandler.cpp @@ -551,7 +551,7 @@ std::pair, si64> CAudioInstance::extractAudio(const Vide ui16 NumOfChan = 2; ui32 SamplesPerSec = 22050; ui32 bytesPerSec = 22050 * 2; - ui16 blockAlign = 2; + ui16 blockAlign = 1; ui16 bitsPerSample = 32; ui8 Subchunk2ID[4] = {'d', 'a', 't', 'a'}; ui32 Subchunk2Size; From cbf094db68c5c43954514f969227dfd96f25b1ca Mon Sep 17 00:00:00 2001 From: kdmcser Date: Tue, 16 Jul 2024 20:53:41 +0800 Subject: [PATCH 026/100] update Chinese translation --- launcher/translation/chinese.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/launcher/translation/chinese.ts b/launcher/translation/chinese.ts index 2b245d6cd..26518f982 100644 --- a/launcher/translation/chinese.ts +++ b/launcher/translation/chinese.ts @@ -465,11 +465,11 @@ Downloading %1. %p% (%v MB out of %m MB) finished - + 正在下载 %1. %p% (%v MB 共 %m MB) 已完成 Downloading %s%. %p% (%v MB out of %m MB) finished - 下载进度 %s%. %p% (%v MB 共 %m MB) 已完成 + 正在下载 %s%. %p% (%v MB 共 %m MB) 已完成 @@ -738,12 +738,12 @@ Install successfully downloaded? Show Tutorial again - + 重新显示教程 Reset - + 重置 @@ -1275,7 +1275,7 @@ Offline installer consists of two parts, .exe and .bin. Make sure you download b File cannot opened - + 打开文件失败 @@ -1324,23 +1324,24 @@ Please select directory with installed Heroes III data. You've provided GOG Galaxy installer! This file doesn't contain the game. Please download the offline backup game installer! - + 您提供的是GOG Galaxy安装器!这个文件不包含游戏内容,请下载离线游戏安装器! Stream error while extracting files! error reason: - + 提取文件时遭遇文件流错误! +错误原因: Not a supported Inno Setup installer! - + 这不是一个支持的Inno Setup安装器! Extracting error! - + 提取错误! From 7328c9ffd609641fe4729b01c0d0979d5ec8b253 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 13 Jul 2024 19:18:59 +0000 Subject: [PATCH 027/100] Fix path and music not updating on new turn --- client/adventureMap/AdventureMapInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/adventureMap/AdventureMapInterface.cpp b/client/adventureMap/AdventureMapInterface.cpp index 43f005199..2a4dbe0be 100644 --- a/client/adventureMap/AdventureMapInterface.cpp +++ b/client/adventureMap/AdventureMapInterface.cpp @@ -444,7 +444,7 @@ void AdventureMapInterface::onPlayerTurnStarted(PlayerColor playerID) LOCPLINT->localState->setSelection(LOCPLINT->localState->getWanderingHero(0)); } - centerOnObject(LOCPLINT->localState->getCurrentArmy()); + onSelectionChanged(LOCPLINT->localState->getCurrentArmy()); //show new day animation and sound on infobar, except for 1st day of the game if (LOCPLINT->cb->getDate(Date::DAY) != 1) From 4ad81dcaf8dec8213ac6f2c537c6aa290b1768db Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 15 Jul 2024 07:11:11 +0000 Subject: [PATCH 028/100] Version bump to 1.5.5 --- android/vcmi-app/build.gradle | 4 ++-- cmake_modules/VersionDefinition.cmake | 2 +- debian/changelog | 6 ++++++ docs/Readme.md | 2 +- launcher/eu.vcmi.VCMI.metainfo.xml | 1 + 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/android/vcmi-app/build.gradle b/android/vcmi-app/build.gradle index a586c43c1..f8f7596b8 100644 --- a/android/vcmi-app/build.gradle +++ b/android/vcmi-app/build.gradle @@ -26,8 +26,8 @@ android { minSdk = qtMinSdkVersion as Integer targetSdk = qtTargetSdkVersion as Integer // ANDROID_TARGET_SDK_VERSION in the CMake project - versionCode 1540 - versionName "1.5.4" + versionCode 1550 + versionName "1.5.5" setProperty("archivesBaseName", "vcmi") } diff --git a/cmake_modules/VersionDefinition.cmake b/cmake_modules/VersionDefinition.cmake index 4d843b099..67002ded8 100644 --- a/cmake_modules/VersionDefinition.cmake +++ b/cmake_modules/VersionDefinition.cmake @@ -1,6 +1,6 @@ set(VCMI_VERSION_MAJOR 1) set(VCMI_VERSION_MINOR 5) -set(VCMI_VERSION_PATCH 4) +set(VCMI_VERSION_PATCH 5) add_definitions( -DVCMI_VERSION_MAJOR=${VCMI_VERSION_MAJOR} -DVCMI_VERSION_MINOR=${VCMI_VERSION_MINOR} diff --git a/debian/changelog b/debian/changelog index ca23efafb..1f04918d0 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +vcmi (1.5.5) jammy; urgency=medium + + * New upstream release + + -- Ivan Savenko Fri, 19 Jul 2024 12:00:00 +0200 + vcmi (1.5.4) jammy; urgency=medium * New upstream release diff --git a/docs/Readme.md b/docs/Readme.md index 2344f8a09..8ceb7a55d 100644 --- a/docs/Readme.md +++ b/docs/Readme.md @@ -1,7 +1,7 @@ [![VCMI](https://github.com/vcmi/vcmi/actions/workflows/github.yml/badge.svg?branch=develop&event=push)](https://github.com/vcmi/vcmi/actions/workflows/github.yml?query=branch%3Adevelop+event%3Apush) [![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/1.5.0/total)](https://github.com/vcmi/vcmi/releases/tag/1.5.0) -[![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/1.5.3/total)](https://github.com/vcmi/vcmi/releases/tag/1.5.3) [![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/1.5.4/total)](https://github.com/vcmi/vcmi/releases/tag/1.5.4) +[![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/1.5.5/total)](https://github.com/vcmi/vcmi/releases/tag/1.5.5) [![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/total)](https://github.com/vcmi/vcmi/releases) # VCMI Project diff --git a/launcher/eu.vcmi.VCMI.metainfo.xml b/launcher/eu.vcmi.VCMI.metainfo.xml index 8fa19bf1d..1f6c55cdc 100644 --- a/launcher/eu.vcmi.VCMI.metainfo.xml +++ b/launcher/eu.vcmi.VCMI.metainfo.xml @@ -90,6 +90,7 @@ vcmilauncher.desktop + From 69379b6e1b0d4301cfe1ce6d576df74861d06145 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 15 Jul 2024 07:15:09 +0000 Subject: [PATCH 029/100] Update changelog --- ChangeLog.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 2ffef722a..4124de750 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,13 @@ +# 1.5.4 -> 1.5.5 + +* Fixed crash when advancing to the next scenario in campaigns when the hero not transferring has a combination artefact that can be transferred to the next scenario. +* Fixed game not updating information such as hero path and current music on new day +* Changed default battle queue hotkey from Q to Z to match HD Mod / HotA +* Changed default hotkey for finishing battle with quick combat from E to Z to match HD Mod / HotA +* Creature casting now uses both F and G keyboard hotkeys +* Shift+left click now directly opens the hero window when two heroes are in town +* Fixed handling of alternative actions for creatures that have more than two potential actions, such as move, shoot, and cast spells. + # 1.5.3 -> 1.5.4 ### Stability From a09a0d41c37d4123e5e68cb02a61a699849df92b Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 15 Jul 2024 16:42:57 +0000 Subject: [PATCH 030/100] Added missing option to schemas and docs --- config/schemas/objectType.json | 6 ++++++ docs/modders/Map_Object_Format.md | 3 +++ 2 files changed, 9 insertions(+) diff --git a/config/schemas/objectType.json b/config/schemas/objectType.json index 272e5d266..b470bc1cc 100644 --- a/config/schemas/objectType.json +++ b/config/schemas/objectType.json @@ -40,6 +40,12 @@ "$ref" : "objectTemplate.json" } }, + + "battleground" : { + "description" : "Battleground that will be used for combats in this object. Overrides terrain this object was placed on", + "type" : "string" + }, + "sounds" : { "type" : "object", "additionalProperties" : false, diff --git a/docs/modders/Map_Object_Format.md b/docs/modders/Map_Object_Format.md index 7183c25e8..0078b883b 100644 --- a/docs/modders/Map_Object_Format.md +++ b/docs/modders/Map_Object_Format.md @@ -58,6 +58,9 @@ Full object consists from 3 parts: // How valuable this object is to AI "aiValue" : 1000, + // Battleground that will be used for combats in this object. Overrides terrain this object was placed on + "battleground" : "cursed_ground", + // Sounds assigned to this object "sounds" : { // Ambient sounds that plays when current hero is near this object From 30569a112cc2aa2c1efc0ecd2361a1e2602307f7 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 16 Jul 2024 10:42:25 +0000 Subject: [PATCH 031/100] Fix crash on attempt to transfer component of a combined artifact --- lib/gameState/CGameStateCampaign.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/gameState/CGameStateCampaign.cpp b/lib/gameState/CGameStateCampaign.cpp index ea4f6d72e..a016cc6ee 100644 --- a/lib/gameState/CGameStateCampaign.cpp +++ b/lib/gameState/CGameStateCampaign.cpp @@ -120,6 +120,11 @@ void CGameStateCampaign::trimCrossoverHeroesParameters(const CampaignTravel & tr if(!info) return false; + // FIXME: double-check how H3 handles case of transferring components of a combined artifact if entire combined artifact is not transferrable + // For example, what happens if hero has assembled Angelic Alliance, AA is not marked is transferrable, but Sandals can be transferred? Should artifact be disassembled? + if (info->locked) + return false; + // TODO: why would there be nullptr artifacts? const CArtifactInstance *art = info->artifact; if(!art) From 0e57862f9405bfa3873d0ec456cc26cf58fd5d81 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 16 Jul 2024 10:49:46 +0000 Subject: [PATCH 032/100] Set release date to July, 17th --- debian/changelog | 2 +- launcher/eu.vcmi.VCMI.metainfo.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 1f04918d0..592ea716b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,7 +2,7 @@ vcmi (1.5.5) jammy; urgency=medium * New upstream release - -- Ivan Savenko Fri, 19 Jul 2024 12:00:00 +0200 + -- Ivan Savenko Wed, 17 Jul 2024 12:00:00 +0200 vcmi (1.5.4) jammy; urgency=medium diff --git a/launcher/eu.vcmi.VCMI.metainfo.xml b/launcher/eu.vcmi.VCMI.metainfo.xml index 1f6c55cdc..94e15aeb3 100644 --- a/launcher/eu.vcmi.VCMI.metainfo.xml +++ b/launcher/eu.vcmi.VCMI.metainfo.xml @@ -90,7 +90,7 @@ vcmilauncher.desktop - + From 60a51e98de58b1764e1dc5853cd72afe5acc500a Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 1 Jun 2024 13:36:38 +0000 Subject: [PATCH 033/100] Remove usage of std::function from CRandomGenerator --- AI/BattleAI/StackWithBonuses.h | 19 ++++------ include/vstd/RNG.h | 18 ++++----- lib/CRandomGenerator.cpp | 44 +++++++++------------- lib/CRandomGenerator.h | 24 +++--------- lib/CStack.cpp | 4 +- lib/battle/BattleInfo.cpp | 3 +- lib/json/JsonRandom.cpp | 4 +- lib/rewardable/Info.cpp | 2 +- lib/rmg/modificators/ConnectionsPlacer.cpp | 2 +- lib/spells/AdventureSpellMechanics.cpp | 4 +- lib/spells/BattleSpellMechanics.cpp | 4 +- lib/spells/ISpellMechanics.cpp | 4 +- lib/spells/effects/Catapult.cpp | 4 +- server/battles/BattleActionProcessor.cpp | 4 +- test/mock/mock_vstd_RNG.h | 5 ++- test/spells/effects/EffectFixture.cpp | 18 +++------ 16 files changed, 61 insertions(+), 102 deletions(-) diff --git a/AI/BattleAI/StackWithBonuses.h b/AI/BattleAI/StackWithBonuses.h index 6a50f6f84..bd960f295 100644 --- a/AI/BattleAI/StackWithBonuses.h +++ b/AI/BattleAI/StackWithBonuses.h @@ -24,20 +24,17 @@ class HypotheticBattle; class RNGStub : public vstd::RNG { public: - vstd::TRandI64 getInt64Range(int64_t lower, int64_t upper) override + int nextInt(int lower, int upper) override { - return [=]()->int64_t - { - return (lower + upper)/2; - }; + return (lower + upper) / 2; } - - vstd::TRand getDoubleRange(double lower, double upper) override + int64_t nextInt64(int64_t lower, int64_t upper) override { - return [=]()->double - { - return (lower + upper)/2; - }; + return (lower + upper) / 2; + } + double nextDouble(double lower, double upper) override + { + return (lower + upper) / 2; } }; diff --git a/include/vstd/RNG.h b/include/vstd/RNG.h index 60e1dedc0..a230c2773 100644 --- a/include/vstd/RNG.h +++ b/include/vstd/RNG.h @@ -15,18 +15,14 @@ VCMI_LIB_NAMESPACE_BEGIN namespace vstd { -using TRandI64 = std::function; -using TRand = std::function; - class DLL_LINKAGE RNG { public: - virtual ~RNG() = default; - virtual TRandI64 getInt64Range(int64_t lower, int64_t upper) = 0; - - virtual TRand getDoubleRange(double lower, double upper) = 0; + virtual int nextInt(int lower, int upper) = 0; + virtual int64_t nextInt64(int64_t lower, int64_t upper) = 0; + virtual double nextDouble(double lower, double upper) = 0; }; } @@ -39,7 +35,7 @@ namespace RandomGeneratorUtil if(container.empty()) throw std::runtime_error("Unable to select random item from empty container!"); - return std::next(container.begin(), rand.getInt64Range(0, container.size() - 1)()); + return std::next(container.begin(), rand.nextInt64(0, container.size() - 1)); } template @@ -48,7 +44,7 @@ namespace RandomGeneratorUtil if(container.empty()) throw std::runtime_error("Unable to select random item from empty container!"); - return std::next(container.begin(), rand.getInt64Range(0, container.size() - 1)()); + return std::next(container.begin(), rand.nextInt64(0, container.size() - 1)); } template @@ -59,7 +55,7 @@ namespace RandomGeneratorUtil int64_t totalWeight = std::accumulate(container.begin(), container.end(), 0); assert(totalWeight > 0); - int64_t roll = rand.getInt64Range(0, totalWeight - 1)(); + int64_t roll = rand.nextInt64(0, totalWeight - 1); for (size_t i = 0; i < container.size(); ++i) { @@ -77,7 +73,7 @@ namespace RandomGeneratorUtil for(int64_t i = n-1; i>0; --i) { - std::swap(container.begin()[i],container.begin()[rand.getInt64Range(0, i)()]); + std::swap(container.begin()[i],container.begin()[rand.nextInt64(0, i)]); } } } diff --git a/lib/CRandomGenerator.cpp b/lib/CRandomGenerator.cpp index 22ff8c78e..4bb17dd27 100644 --- a/lib/CRandomGenerator.cpp +++ b/lib/CRandomGenerator.cpp @@ -35,28 +35,17 @@ void CRandomGenerator::resetSeed() setSeed(static_cast(threadIdHash * std::time(nullptr))); } -TRandI CRandomGenerator::getIntRange(int lower, int upper) -{ - if (lower <= upper) - return std::bind(TIntDist(lower, upper), std::ref(rand)); - throw std::runtime_error("Invalid range provided: " + std::to_string(lower) + " ... " + std::to_string(upper)); -} - -vstd::TRandI64 CRandomGenerator::getInt64Range(int64_t lower, int64_t upper) -{ - if(lower <= upper) - return std::bind(TInt64Dist(lower, upper), std::ref(rand)); - throw std::runtime_error("Invalid range provided: " + std::to_string(lower) + " ... " + std::to_string(upper)); -} - int CRandomGenerator::nextInt(int upper) { - return getIntRange(0, upper)(); + return nextInt(0, upper); } int CRandomGenerator::nextInt(int lower, int upper) { - return getIntRange(lower, upper)(); + if (lower > upper) + throw std::runtime_error("Invalid range provided: " + std::to_string(lower) + " ... " + std::to_string(upper)); + + return TIntDist(lower, upper)(rand); } int CRandomGenerator::nextInt() @@ -64,28 +53,31 @@ int CRandomGenerator::nextInt() return TIntDist()(rand); } -vstd::TRand CRandomGenerator::getDoubleRange(double lower, double upper) +int64_t CRandomGenerator::nextInt64(int64_t lower, int64_t upper) { - if(lower <= upper) - return std::bind(TRealDist(lower, upper), std::ref(rand)); - throw std::runtime_error("Invalid range provided: " + std::to_string(lower) + " ... " + std::to_string(upper)); + if (lower > upper) + throw std::runtime_error("Invalid range provided: " + std::to_string(lower) + " ... " + std::to_string(upper)); + return TInt64Dist(lower, upper)(rand); } double CRandomGenerator::nextDouble(double upper) { - return getDoubleRange(0, upper)(); + return nextDouble(0, upper); } double CRandomGenerator::nextDouble(double lower, double upper) { - return getDoubleRange(lower, upper)(); + if(lower > upper) + throw std::runtime_error("Invalid range provided: " + std::to_string(lower) + " ... " + std::to_string(upper)); + + return TRealDist(lower, upper)(rand); } -double CRandomGenerator::nextDouble() -{ - return TRealDist()(rand); -} +//double CRandomGenerator::nextDouble() +//{ +// return TRealDist()(rand); +//} CRandomGenerator & CRandomGenerator::getDefault() { diff --git a/lib/CRandomGenerator.h b/lib/CRandomGenerator.h index fe0dd2070..434c63180 100644 --- a/lib/CRandomGenerator.h +++ b/lib/CRandomGenerator.h @@ -15,6 +15,7 @@ VCMI_LIB_NAMESPACE_BEGIN + /// Generator to use for all randomization in game /// minstd_rand is selected due to following reasons: /// 1. Its randomization quality is below mt_19937 however this is unlikely to be noticeable in game @@ -23,12 +24,11 @@ using TGenerator = std::minstd_rand; using TIntDist = std::uniform_int_distribution; using TInt64Dist = std::uniform_int_distribution; using TRealDist = std::uniform_real_distribution; -using TRandI = std::function; /// The random generator randomly generates integers and real numbers("doubles") between /// a given range. This is a header only class and mainly a wrapper for /// convenient usage of the standard random API. An instance of this RNG is not thread safe. -class DLL_LINKAGE CRandomGenerator : public vstd::RNG, boost::noncopyable, public Serializeable +class DLL_LINKAGE CRandomGenerator final : public vstd::RNG, boost::noncopyable, public Serializeable { public: /// Seeds the generator by default with the product of the current time in milliseconds and the @@ -44,37 +44,23 @@ public: /// current thread ID. void resetSeed(); - /// Generate several integer numbers within the same range. - /// e.g.: auto a = gen.getIntRange(0,10); a(); a(); a(); - /// requires: lower <= upper - TRandI getIntRange(int lower, int upper); - - vstd::TRandI64 getInt64Range(int64_t lower, int64_t upper) override; - /// Generates an integer between 0 and upper. /// requires: 0 <= upper int nextInt(int upper); /// requires: lower <= upper - int nextInt(int lower, int upper); + int nextInt(int lower, int upper) override; + int64_t nextInt64(int64_t lower, int64_t upper) override; /// Generates an integer between 0 and the maximum value it can hold. int nextInt(); - /// Generate several double/real numbers within the same range. - /// e.g.: auto a = gen.getDoubleRange(4.5,10.2); a(); a(); a(); - /// requires: lower <= upper - vstd::TRand getDoubleRange(double lower, double upper) override; - /// Generates a double between 0 and upper. /// requires: 0 <= upper double nextDouble(double upper); /// requires: lower <= upper - double nextDouble(double lower, double upper); - - /// Generates a double between 0.0 and 1.0. - double nextDouble(); + double nextDouble(double lower, double upper) override; /// Gets a globally accessible RNG which will be constructed once per thread. For the /// seed a combination of the thread ID and current time in milliseconds will be used. diff --git a/lib/CStack.cpp b/lib/CStack.cpp index a84f8cd0f..21747bfd2 100644 --- a/lib/CStack.cpp +++ b/lib/CStack.cpp @@ -212,11 +212,9 @@ void CStack::prepareAttacked(BattleStackAttacked & bsa, vstd::RNG & rand, const auto resurrectedAdd = static_cast(baseAmount - (resurrectedCount / resurrectFactor)); - auto rangeGen = rand.getInt64Range(0, 99); - for(int32_t i = 0; i < resurrectedAdd; i++) { - if(resurrectValue > rangeGen()) + if(resurrectValue > rand.nextInt(0, 99)) resurrectedCount += 1; } diff --git a/lib/battle/BattleInfo.cpp b/lib/battle/BattleInfo.cpp index 946499a4a..86dcb4f3e 100644 --- a/lib/battle/BattleInfo.cpp +++ b/lib/battle/BattleInfo.cpp @@ -662,10 +662,9 @@ int64_t BattleInfo::getActualDamage(const DamageRange & damage, int32_t attacker int64_t sum = 0; auto howManyToAv = std::min(10, attackerCount); - auto rangeGen = rng.getInt64Range(damage.min, damage.max); for(int32_t g = 0; g < howManyToAv; ++g) - sum += rangeGen(); + sum += rng.nextInt64(damage.min, damage.max); return sum / howManyToAv; } diff --git a/lib/json/JsonRandom.cpp b/lib/json/JsonRandom.cpp index df71283b2..3fbda28ef 100644 --- a/lib/json/JsonRandom.cpp +++ b/lib/json/JsonRandom.cpp @@ -63,7 +63,7 @@ VCMI_LIB_NAMESPACE_BEGIN { const auto & vector = value.Vector(); - size_t index= rng.getIntRange(0, vector.size()-1)(); + size_t index= rng.nextInt64(0, vector.size()-1); return loadValue(vector[index], rng, variables, 0); } if(value.isStruct()) @@ -72,7 +72,7 @@ VCMI_LIB_NAMESPACE_BEGIN return loadValue(value["amount"], rng, variables, defaultValue); si32 min = loadValue(value["min"], rng, variables, 0); si32 max = loadValue(value["max"], rng, variables, 0); - return rng.getIntRange(min, max)(); + return rng.nextInt64(min, max); } return defaultValue; } diff --git a/lib/rewardable/Info.cpp b/lib/rewardable/Info.cpp index 14b73fa6c..00323daf9 100644 --- a/lib/rewardable/Info.cpp +++ b/lib/rewardable/Info.cpp @@ -298,7 +298,7 @@ void Rewardable::Info::configureRewards( { const JsonNode & preset = object.getPresetVariable("dice", diceID); if (preset.isNull()) - object.initVariable("dice", diceID, rng.getIntRange(0, 99)()); + object.initVariable("dice", diceID, rng.nextInt(0, 99)); else object.initVariable("dice", diceID, preset.Integer()); diff --git a/lib/rmg/modificators/ConnectionsPlacer.cpp b/lib/rmg/modificators/ConnectionsPlacer.cpp index e6692e260..f9a34c25c 100644 --- a/lib/rmg/modificators/ConnectionsPlacer.cpp +++ b/lib/rmg/modificators/ConnectionsPlacer.cpp @@ -444,7 +444,7 @@ void ConnectionsPlacer::collectNeighbourZones() bool ConnectionsPlacer::shouldGenerateRoad(const rmg::ZoneConnection& connection) const { return connection.getRoadOption() == rmg::ERoadOption::ROAD_TRUE || - (connection.getRoadOption() == rmg::ERoadOption::ROAD_RANDOM && zone.getRand().nextDouble() >= 0.5f); + (connection.getRoadOption() == rmg::ERoadOption::ROAD_RANDOM && zone.getRand().nextDouble(0, 1) >= 0.5f); } void ConnectionsPlacer::createBorder() diff --git a/lib/spells/AdventureSpellMechanics.cpp b/lib/spells/AdventureSpellMechanics.cpp index db3e97ab1..6d7699ee4 100644 --- a/lib/spells/AdventureSpellMechanics.cpp +++ b/lib/spells/AdventureSpellMechanics.cpp @@ -187,7 +187,7 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner); //check if spell works at all - if(env->getRNG()->getInt64Range(0, 99)() >= owner->getLevelPower(schoolLevel)) //power is % chance of success + if(env->getRNG()->nextInt(0, 99) >= owner->getLevelPower(schoolLevel)) //power is % chance of success { InfoWindow iw; iw.player = parameters.caster->getCasterOwner(); @@ -280,7 +280,7 @@ ESpellCastResult ScuttleBoatMechanics::applyAdventureEffects(SpellCastEnvironmen { const auto schoolLevel = parameters.caster->getSpellSchoolLevel(owner); //check if spell works at all - if(env->getRNG()->getInt64Range(0, 99)() >= owner->getLevelPower(schoolLevel)) //power is % chance of success + if(env->getRNG()->nextInt(0, 99) >= owner->getLevelPower(schoolLevel)) //power is % chance of success { InfoWindow iw; iw.player = parameters.caster->getCasterOwner(); diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index 8ad548bfa..d586bc785 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -377,15 +377,13 @@ void BattleSpellMechanics::beforeCast(BattleSpellCast & sc, vstd::RNG & rng, con std::vector resisted; - auto rangeGen = rng.getInt64Range(0, 99); - auto filterResisted = [&, this](const battle::Unit * unit) -> bool { if(isNegativeSpell() && isMagicalEffect()) { //magic resistance const int prob = std::min(unit->magicResistance(), 100); //probability of resistance in % - if(rangeGen() < prob) + if(rng.nextInt(0, 99) < prob) return true; } return false; diff --git a/lib/spells/ISpellMechanics.cpp b/lib/spells/ISpellMechanics.cpp index 6e260347f..3cec1dd72 100644 --- a/lib/spells/ISpellMechanics.cpp +++ b/lib/spells/ISpellMechanics.cpp @@ -268,11 +268,9 @@ void BattleCast::cast(ServerCallback * server, Target target) const std::string magicMirrorCacheStr = "type_MAGIC_MIRROR"; static const auto magicMirrorSelector = Selector::type()(BonusType::MAGIC_MIRROR); - auto rangeGen = server->getRNG()->getInt64Range(0, 99); - const int mirrorChance = mainTarget->valOfBonuses(magicMirrorSelector, magicMirrorCacheStr); - if(rangeGen() < mirrorChance) + if(server->getRNG()->nextInt(0, 99) < mirrorChance) { auto mirrorTargets = cb->battleGetUnitsIf([this](const battle::Unit * unit) { diff --git a/lib/spells/effects/Catapult.cpp b/lib/spells/effects/Catapult.cpp index 167aaabd6..2a511935d 100644 --- a/lib/spells/effects/Catapult.cpp +++ b/lib/spells/effects/Catapult.cpp @@ -118,7 +118,7 @@ void Catapult::applyTargeted(ServerCallback * server, const Mechanics * m, const auto actualTarget = EWallPart::INVALID; if ( m->battle()->isWallPartAttackable(desiredTarget) && - server->getRNG()->getInt64Range(0, 99)() < getCatapultHitChance(desiredTarget)) + server->getRNG()->nextInt(0, 99) < getCatapultHitChance(desiredTarget)) { actualTarget = desiredTarget; } @@ -172,7 +172,7 @@ int Catapult::getRandomDamage (ServerCallback * server) const { std::array damageChances = { noDmg, hit, crit }; //dmgChance[i] - chance for doing i dmg when hit is successful int totalChance = std::accumulate(damageChances.begin(), damageChances.end(), 0); - int damageRandom = server->getRNG()->getInt64Range(0, totalChance - 1)(); + int damageRandom = server->getRNG()->nextInt(0, totalChance - 1); int dealtDamage = 0; //calculating dealt damage diff --git a/server/battles/BattleActionProcessor.cpp b/server/battles/BattleActionProcessor.cpp index 61e13c26a..0fb275960 100644 --- a/server/battles/BattleActionProcessor.cpp +++ b/server/battles/BattleActionProcessor.cpp @@ -1341,7 +1341,7 @@ void BattleActionProcessor::handleAfterAttackCasting(const CBattleInfoCallback & double chanceToTrigger = attacker->valOfBonuses(BonusType::TRANSMUTATION) / 100.0f; vstd::amin(chanceToTrigger, 1); //cap at 100% - if(gameHandler->getRandomGenerator().getDoubleRange(0, 1)() > chanceToTrigger) + if(gameHandler->getRandomGenerator().nextDouble(0, 1) > chanceToTrigger) return; int bonusAdditionalInfo = attacker->getBonus(Selector::type()(BonusType::TRANSMUTATION))->additionalInfo[0]; @@ -1405,7 +1405,7 @@ void BattleActionProcessor::handleAfterAttackCasting(const CBattleInfoCallback & vstd::amin(chanceToTrigger, 1); //cap trigger chance at 100% - if(gameHandler->getRandomGenerator().getDoubleRange(0, 1)() > chanceToTrigger) + if(gameHandler->getRandomGenerator().nextDouble(0, 1) > chanceToTrigger) return; BattleStackAttacked bsa; diff --git a/test/mock/mock_vstd_RNG.h b/test/mock/mock_vstd_RNG.h index 2fe2b04e1..0064503ad 100644 --- a/test/mock/mock_vstd_RNG.h +++ b/test/mock/mock_vstd_RNG.h @@ -18,8 +18,9 @@ namespace vstd class RNGMock : public RNG { public: - MOCK_METHOD2(getInt64Range, TRandI64(int64_t, int64_t)); - MOCK_METHOD2(getDoubleRange, TRand(double, double)); + MOCK_METHOD2(nextInt, int(int lower, int upper)); + MOCK_METHOD2(nextInt64, int64_t(int64_t lower, int64_t upper)); + MOCK_METHOD2(nextDouble, double(double lower, double upper)); }; } diff --git a/test/spells/effects/EffectFixture.cpp b/test/spells/effects/EffectFixture.cpp index 5cf558a44..cf4478786 100644 --- a/test/spells/effects/EffectFixture.cpp +++ b/test/spells/effects/EffectFixture.cpp @@ -101,27 +101,21 @@ void EffectFixture::setUp() ON_CALL(serverMock, apply(Matcher(_))).WillByDefault(Invoke(battleFake.get(), &battle::BattleFake::accept)); } -static vstd::TRandI64 getInt64RangeDef(int64_t lower, int64_t upper) +static int64_t getInt64Range(int64_t lower, int64_t upper) { - return [=]()->int64_t - { - return (lower + upper)/2; - }; + return (lower + upper)/2; } -static vstd::TRand getDoubleRangeDef(double lower, double upper) +static double getDoubleRange(double lower, double upper) { - return [=]()->double - { - return (lower + upper)/2; - }; + return (lower + upper)/2; } void EffectFixture::setupDefaultRNG() { EXPECT_CALL(serverMock, getRNG()).Times(AtLeast(0)); - EXPECT_CALL(rngMock, getInt64Range(_,_)).WillRepeatedly(Invoke(&getInt64RangeDef)); - EXPECT_CALL(rngMock, getDoubleRange(_,_)).WillRepeatedly(Invoke(&getDoubleRangeDef)); + EXPECT_CALL(rngMock, nextInt64(_,_)).WillRepeatedly(Invoke(&getInt64Range)); + EXPECT_CALL(rngMock, nextDouble(_,_)).WillRepeatedly(Invoke(&getDoubleRange)); } } From 63bcf7d83cc1e9af205da998942e064c59b00344 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 1 Jun 2024 15:28:17 +0000 Subject: [PATCH 034/100] Replaced most of usages of CRandomGenerator with vstd::RNG in library --- AI/BattleAI/StackWithBonuses.h | 25 +++++++++++- client/CPlayerInterface.cpp | 16 +++++--- client/HeroMovementController.cpp | 9 ++++- client/adventureMap/MapAudioPlayer.cpp | 9 ++++- client/windows/GUIClasses.cpp | 1 + include/vstd/RNG.h | 22 +++++++++++ lib/CArtHandler.h | 1 - lib/CCreatureHandler.cpp | 5 ++- lib/CCreatureHandler.h | 8 +++- lib/CHeroHandler.cpp | 5 ++- lib/CHeroHandler.h | 8 +++- lib/CRandomGenerator.cpp | 20 +++++----- lib/CRandomGenerator.h | 14 ++++--- lib/IGameCallback.cpp | 5 ++- lib/IGameCallback.h | 8 +++- lib/battle/BattleInfo.cpp | 2 +- lib/battle/CBattleInfoCallback.cpp | 6 +-- lib/battle/CBattleInfoCallback.h | 10 +++-- lib/gameState/CGameState.cpp | 19 ++++++---- lib/gameState/CGameState.h | 22 +++++------ lib/gameState/CGameStateCampaign.cpp | 2 + lib/gameState/TavernHeroesPool.h | 1 - lib/json/JsonRandom.cpp | 34 ++++++++--------- lib/json/JsonRandom.h | 38 ++++++++++--------- .../AObjectTypeHandler.h | 8 +++- .../CBankInstanceConstructor.cpp | 7 ++-- .../CBankInstanceConstructor.h | 4 +- .../CDefaultObjectTypeHandler.h | 4 +- .../CObjectClassesHandler.h | 1 - .../CRewardableConstructor.cpp | 2 +- .../CRewardableConstructor.h | 2 +- .../CommonConstructors.cpp | 6 +-- .../CommonConstructors.h | 6 +-- .../DwellingInstanceConstructor.cpp | 2 +- .../DwellingInstanceConstructor.h | 2 +- lib/mapObjects/CBank.cpp | 4 +- lib/mapObjects/CBank.h | 4 +- lib/mapObjects/CGCreature.cpp | 12 +++--- lib/mapObjects/CGCreature.h | 6 +-- lib/mapObjects/CGDwelling.cpp | 12 +++--- lib/mapObjects/CGDwelling.h | 10 ++--- lib/mapObjects/CGHeroInstance.cpp | 22 +++++------ lib/mapObjects/CGHeroInstance.h | 22 +++++------ lib/mapObjects/CGMarket.cpp | 4 +- lib/mapObjects/CGMarket.h | 4 +- lib/mapObjects/CGObjectInstance.cpp | 16 ++++---- lib/mapObjects/CGObjectInstance.h | 10 ++--- lib/mapObjects/CGPandoraBox.cpp | 2 +- lib/mapObjects/CGPandoraBox.h | 2 +- lib/mapObjects/CGTownBuilding.cpp | 8 ++-- lib/mapObjects/CGTownBuilding.h | 6 +-- lib/mapObjects/CGTownInstance.cpp | 12 +++--- lib/mapObjects/CGTownInstance.h | 10 ++--- lib/mapObjects/CQuest.cpp | 13 ++++--- lib/mapObjects/CQuest.h | 10 ++--- lib/mapObjects/CRewardableObject.cpp | 6 ++- lib/mapObjects/CRewardableObject.h | 4 +- lib/mapObjects/IObjectInterface.cpp | 6 +-- lib/mapObjects/IObjectInterface.h | 12 ++++-- lib/mapObjects/MiscObjects.cpp | 36 +++++++++--------- lib/mapObjects/MiscObjects.h | 28 +++++++------- lib/mapping/CDrawRoadsOperation.cpp | 9 +++-- lib/mapping/CDrawRoadsOperation.h | 8 ++-- lib/mapping/CMap.cpp | 2 + lib/mapping/CMapEditManager.cpp | 18 +++++---- lib/mapping/CMapEditManager.h | 16 +++++--- lib/mapping/CMapOperation.cpp | 7 ++-- lib/mapping/CMapOperation.h | 12 ++++-- lib/mapping/ObstacleProxy.cpp | 10 +++-- lib/mapping/ObstacleProxy.h | 9 ++--- lib/registerTypes/RegisterTypesLobbyPacks.h | 1 + lib/rewardable/Info.cpp | 17 +++++---- lib/rewardable/Info.h | 21 +++++----- lib/rmg/CMapGenOptions.cpp | 10 ++--- lib/rmg/CMapGenOptions.h | 9 +++-- lib/rmg/CMapGenerator.cpp | 21 +++++----- lib/rmg/CMapGenerator.h | 3 +- lib/rmg/CZonePlacer.cpp | 23 ++++++----- lib/rmg/CZonePlacer.h | 14 ++++--- lib/rmg/Functions.cpp | 4 +- lib/rmg/Functions.h | 3 +- lib/rmg/PenroseTiling.cpp | 4 +- lib/rmg/PenroseTiling.h | 10 +++-- lib/rmg/RmgMap.cpp | 2 +- lib/rmg/RmgMap.h | 8 +++- lib/rmg/RmgObject.cpp | 12 +++--- lib/rmg/RmgObject.h | 16 +++++--- lib/rmg/Zone.cpp | 14 +++++-- lib/rmg/Zone.h | 9 ++--- lib/rmg/modificators/ConnectionsPlacer.cpp | 2 + lib/rmg/modificators/MinePlacer.cpp | 2 + lib/rmg/modificators/ObjectDistributor.cpp | 2 + lib/rmg/modificators/ObjectManager.cpp | 2 + lib/rmg/modificators/ObstaclePlacer.cpp | 1 - lib/rmg/modificators/PrisonHeroPlacer.cpp | 4 +- lib/rmg/modificators/PrisonHeroPlacer.h | 2 - lib/rmg/modificators/QuestArtifactPlacer.cpp | 6 ++- lib/rmg/modificators/QuestArtifactPlacer.h | 6 +-- lib/rmg/modificators/RiverPlacer.cpp | 2 + lib/rmg/modificators/RockFiller.cpp | 1 - lib/rmg/modificators/RockPlacer.cpp | 1 - lib/rmg/modificators/TerrainPainter.cpp | 2 + lib/rmg/modificators/TownPlacer.cpp | 2 + lib/rmg/modificators/TreasurePlacer.cpp | 4 +- lib/rmg/modificators/TreasurePlacer.h | 1 - lib/rmg/modificators/WaterAdopter.cpp | 2 + lib/rmg/modificators/WaterProxy.cpp | 2 + lib/rmg/threadpool/MapProxy.cpp | 6 +-- lib/rmg/threadpool/MapProxy.h | 6 +-- lib/spells/AdventureSpellMechanics.cpp | 3 +- lib/spells/BattleSpellMechanics.cpp | 3 +- lib/spells/ISpellMechanics.cpp | 3 +- lib/spells/ISpellMechanics.h | 1 - lib/spells/effects/Catapult.cpp | 3 +- lib/spells/effects/Obstacle.cpp | 3 +- mapeditor/mapcontroller.cpp | 1 + mapeditor/windownewmap.cpp | 1 + server/CGameHandler.cpp | 10 +++-- server/CGameHandler.h | 6 ++- server/battles/BattleActionProcessor.cpp | 5 ++- server/battles/BattleFlowProcessor.cpp | 2 + server/battles/BattleResultProcessor.cpp | 2 + server/processors/HeroPoolProcessor.cpp | 5 ++- server/processors/HeroPoolProcessor.h | 11 ++++-- test/mock/mock_vstd_RNG.h | 7 ++++ 125 files changed, 620 insertions(+), 409 deletions(-) diff --git a/AI/BattleAI/StackWithBonuses.h b/AI/BattleAI/StackWithBonuses.h index bd960f295..8eacd7221 100644 --- a/AI/BattleAI/StackWithBonuses.h +++ b/AI/BattleAI/StackWithBonuses.h @@ -21,9 +21,19 @@ class HypotheticBattle; ///Fake random generator, used by AI to evaluate random server behavior -class RNGStub : public vstd::RNG +class RNGStub final : public vstd::RNG { public: + virtual int nextInt() override + { + return 0; + } + + int nextBinomialInt(int coinsCount, double coinChance) override + { + return coinsCount * coinChance; + } + int nextInt(int lower, int upper) override { return (lower + upper) / 2; @@ -36,6 +46,19 @@ public: { return (lower + upper) / 2; } + + int nextInt(int upper) override + { + return upper / 2; + } + int64_t nextInt64(int64_t upper) override + { + return upper / 2; + } + double nextDouble(double upper) override + { + return upper / 2; + } }; class StackWithBonuses : public battle::CUnitState, public virtual IBonusBearer diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 55efb470a..8d19f2373 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -70,6 +70,7 @@ #include "../lib/CGeneralTextHandler.h" #include "../lib/CHeroHandler.h" #include "../lib/CPlayerState.h" +#include "../lib/CRandomGenerator.h" #include "../lib/CStack.h" #include "../lib/CStopWatch.h" #include "../lib/CThreadHelper.h" @@ -413,8 +414,9 @@ void CPlayerInterface::heroVisit(const CGHeroInstance * visitor, const CGObjectI EVENT_HANDLER_CALLED_BY_CLIENT; if(start && visitedObj) { - if(visitedObj->getVisitSound()) - CCS->soundh->playSound(visitedObj->getVisitSound().value()); + auto visitSound = visitedObj->getVisitSound(CRandomGenerator::getDefault()); + if (visitSound) + CCS->soundh->playSound(visitSound.value()); } } @@ -1410,10 +1412,14 @@ void CPlayerInterface::centerView (int3 pos, int focusTime) void CPlayerInterface::objectRemoved(const CGObjectInstance * obj, const PlayerColor & initiator) { EVENT_HANDLER_CALLED_BY_CLIENT; - if(playerID == initiator && obj->getRemovalSound()) + if(playerID == initiator) { - waitWhileDialog(); - CCS->soundh->playSound(obj->getRemovalSound().value()); + auto removalSound = obj->getRemovalSound(CRandomGenerator::getDefault()); + if (removalSound) + { + waitWhileDialog(); + CCS->soundh->playSound(removalSound.value()); + } } CGI->mh->waitForOngoingAnimations(); diff --git a/client/HeroMovementController.cpp b/client/HeroMovementController.cpp index 2d0efe2eb..d402413e5 100644 --- a/client/HeroMovementController.cpp +++ b/client/HeroMovementController.cpp @@ -24,6 +24,7 @@ #include "ConditionalWait.h" #include "../lib/CConfigHandler.h" +#include "../lib/CRandomGenerator.h" #include "../lib/pathfinder/CGPathNode.h" #include "../lib/mapObjects/CGHeroInstance.h" #include "../lib/networkPacks/PacksForClient.h" @@ -153,8 +154,12 @@ void HeroMovementController::onTryMoveHero(const CGHeroInstance * hero, const Tr if(details.result == TryMoveHero::EMBARK || details.result == TryMoveHero::DISEMBARK) { - if(hero->getRemovalSound() && hero->tempOwner == LOCPLINT->playerID) - CCS->soundh->playSound(hero->getRemovalSound().value()); + if (hero->tempOwner == LOCPLINT->playerID) + { + auto removalSound = hero->getRemovalSound(CRandomGenerator::getDefault()); + if (removalSound) + CCS->soundh->playSound(removalSound.value()); + } } bool directlyAttackingCreature = diff --git a/client/adventureMap/MapAudioPlayer.cpp b/client/adventureMap/MapAudioPlayer.cpp index bca944448..bac234103 100644 --- a/client/adventureMap/MapAudioPlayer.cpp +++ b/client/adventureMap/MapAudioPlayer.cpp @@ -17,6 +17,7 @@ #include "../media/IMusicPlayer.h" #include "../media/ISoundPlayer.h" +#include "../../lib/CRandomGenerator.h" #include "../../lib/TerrainHandler.h" #include "../../lib/mapObjects/CArmedInstance.h" #include "../../lib/mapObjects/CGHeroInstance.h" @@ -136,8 +137,12 @@ std::vector MapAudioPlayer::getAmbientSounds(const int3 & tile) if (!object) logGlobal->warn("Already removed object %d found on tile! (%d %d %d)", objectID.getNum(), tile.x, tile.y, tile.z); - if(object && object->getAmbientSound()) - result.push_back(object->getAmbientSound().value()); + if(object) + { + auto ambientSound = object->getAmbientSound(CRandomGenerator::getDefault()); + if (ambientSound) + result.push_back(ambientSound.value()); + } } if(CGI->mh->getMap()->isCoastalTile(tile)) diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index 3b2fb85d9..0fd5b7169 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -54,6 +54,7 @@ #include "../lib/CHeroHandler.h" #include "../lib/GameSettings.h" #include "ConditionalWait.h" +#include "../lib/CRandomGenerator.h" #include "../lib/CSkillHandler.h" #include "../lib/CSoundBase.h" diff --git a/include/vstd/RNG.h b/include/vstd/RNG.h index a230c2773..9a4e54a06 100644 --- a/include/vstd/RNG.h +++ b/include/vstd/RNG.h @@ -20,9 +20,31 @@ class DLL_LINKAGE RNG public: virtual ~RNG() = default; + /// Returns random number in range [lower, upper] virtual int nextInt(int lower, int upper) = 0; + + /// Returns random number in range [lower, upper] virtual int64_t nextInt64(int64_t lower, int64_t upper) = 0; + + /// Returns random number in range [lower, upper] virtual double nextDouble(double lower, double upper) = 0; + + /// Returns random number in range [0, upper] + virtual int nextInt(int upper) = 0; + + /// Returns random number in range [0, upper] + virtual int64_t nextInt64(int64_t upper) = 0; + + /// Returns random number in range [0, upper] + virtual double nextDouble(double upper) = 0; + + /// Generates an integer between 0 and the maximum value it can hold. + /// Should be only used for seeding other generators + virtual int nextInt() = 0; + + /// Returns integer using binomial distribution + /// returned value is number of successfull coin flips with chance 'coinChance' out of 'coinsCount' attempts + virtual int nextBinomialInt(int coinsCount, double coinChance) = 0; }; } diff --git a/lib/CArtHandler.h b/lib/CArtHandler.h index bd57aa59e..127afe112 100644 --- a/lib/CArtHandler.h +++ b/lib/CArtHandler.h @@ -25,7 +25,6 @@ class CArtHandler; class CGHeroInstance; class CArtifactSet; class CArtifactInstance; -class CRandomGenerator; class CMap; class JsonSerializeFormat; diff --git a/lib/CCreatureHandler.cpp b/lib/CCreatureHandler.cpp index 0f259f563..7b29e1f6f 100644 --- a/lib/CCreatureHandler.cpp +++ b/lib/CCreatureHandler.cpp @@ -14,7 +14,6 @@ #include "ResourceSet.h" #include "filesystem/Filesystem.h" #include "VCMI_Lib.h" -#include "CRandomGenerator.h" #include "CTownHandler.h" #include "GameSettings.h" #include "constants/StringConstants.h" @@ -28,6 +27,8 @@ #include "modding/CModHandler.h" #include "ExceptionsCommon.h" +#include + VCMI_LIB_NAMESPACE_BEGIN const std::map CCreature::creatureQuantityRanges = @@ -1362,7 +1363,7 @@ CCreatureHandler::~CCreatureHandler() p.first = nullptr; } -CreatureID CCreatureHandler::pickRandomMonster(CRandomGenerator & rand, int tier) const +CreatureID CCreatureHandler::pickRandomMonster(vstd::RNG & rand, int tier) const { std::vector allowed; for(const auto & creature : objects) diff --git a/lib/CCreatureHandler.h b/lib/CCreatureHandler.h index 782f3adf8..5347855f0 100644 --- a/lib/CCreatureHandler.h +++ b/lib/CCreatureHandler.h @@ -23,11 +23,15 @@ VCMI_LIB_NAMESPACE_BEGIN +namespace vstd +{ +class RNG; +} + class CLegacyConfigParser; class CCreatureHandler; class CCreature; class JsonSerializeFormat; -class CRandomGenerator; class DLL_LINKAGE CCreature : public Creature, public CBonusSystemNode { @@ -225,7 +229,7 @@ public: std::vector< std::vector > skillLevels; //how much of a bonus will be given to commander with every level. SPELL_POWER also gives CASTS and RESISTANCE std::vector , std::pair > > skillRequirements; // first - Bonus, second - which two skills are needed to use it - CreatureID pickRandomMonster(CRandomGenerator & rand, int tier = -1) const; //tier <1 - CREATURES_PER_TOWN> or -1 for any + CreatureID pickRandomMonster(vstd::RNG & rand, int tier = -1) const; //tier <1 - CREATURES_PER_TOWN> or -1 for any CCreatureHandler(); ~CCreatureHandler(); diff --git a/lib/CHeroHandler.cpp b/lib/CHeroHandler.cpp index 91a4a5c5a..b2bdc41b9 100644 --- a/lib/CHeroHandler.cpp +++ b/lib/CHeroHandler.cpp @@ -17,7 +17,6 @@ #include "battle/BattleHex.h" #include "CCreatureHandler.h" #include "GameSettings.h" -#include "CRandomGenerator.h" #include "CTownHandler.h" #include "CSkillHandler.h" #include "BattleFieldHandler.h" @@ -29,6 +28,8 @@ #include "mapObjectConstructors/CObjectClassesHandler.h" #include "modding/IdentifierStorage.h" +#include + VCMI_LIB_NAMESPACE_BEGIN CHero::CHero() = default; @@ -123,7 +124,7 @@ void CHero::serializeJson(JsonSerializeFormat & handler) } -SecondarySkill CHeroClass::chooseSecSkill(const std::set & possibles, CRandomGenerator & rand) const //picks secondary skill out from given possibilities +SecondarySkill CHeroClass::chooseSecSkill(const std::set & possibles, vstd::RNG & rand) const //picks secondary skill out from given possibilities { assert(!possibles.empty()); diff --git a/lib/CHeroHandler.h b/lib/CHeroHandler.h index e3a477964..323b978e6 100644 --- a/lib/CHeroHandler.h +++ b/lib/CHeroHandler.h @@ -23,11 +23,15 @@ VCMI_LIB_NAMESPACE_BEGIN +namespace vstd +{ +class RNG; +} + class CHeroClass; class CGHeroInstance; struct BattleHex; class JsonNode; -class CRandomGenerator; class JsonSerializeFormat; class BattleField; @@ -148,7 +152,7 @@ public: std::string getNameTextID() const override; bool isMagicHero() const; - SecondarySkill chooseSecSkill(const std::set & possibles, CRandomGenerator & rand) const; //picks secondary skill out from given possibilities + SecondarySkill chooseSecSkill(const std::set & possibles, vstd::RNG & rand) const; //picks secondary skill out from given possibilities void updateFrom(const JsonNode & data); void serializeJson(JsonSerializeFormat & handler); diff --git a/lib/CRandomGenerator.cpp b/lib/CRandomGenerator.cpp index 4bb17dd27..3a7ac2320 100644 --- a/lib/CRandomGenerator.cpp +++ b/lib/CRandomGenerator.cpp @@ -40,6 +40,11 @@ int CRandomGenerator::nextInt(int upper) return nextInt(0, upper); } +int64_t CRandomGenerator::nextInt64(int64_t upper) +{ + return nextInt64(0, upper); +} + int CRandomGenerator::nextInt(int lower, int upper) { if (lower > upper) @@ -53,6 +58,12 @@ int CRandomGenerator::nextInt() return TIntDist()(rand); } +int CRandomGenerator::nextBinomialInt(int coinsCount, double coinChance) +{ + std::binomial_distribution<> distribution(coinsCount, coinChance); + return distribution(rand); +} + int64_t CRandomGenerator::nextInt64(int64_t lower, int64_t upper) { if (lower > upper) @@ -74,20 +85,11 @@ double CRandomGenerator::nextDouble(double lower, double upper) return TRealDist(lower, upper)(rand); } -//double CRandomGenerator::nextDouble() -//{ -// return TRealDist()(rand); -//} - CRandomGenerator & CRandomGenerator::getDefault() { static thread_local CRandomGenerator defaultRand; return defaultRand; } -TGenerator & CRandomGenerator::getStdGenerator() -{ - return rand; -} VCMI_LIB_NAMESPACE_END diff --git a/lib/CRandomGenerator.h b/lib/CRandomGenerator.h index 434c63180..c24daaf14 100644 --- a/lib/CRandomGenerator.h +++ b/lib/CRandomGenerator.h @@ -46,18 +46,23 @@ public: /// Generates an integer between 0 and upper. /// requires: 0 <= upper - int nextInt(int upper); + int nextInt(int upper) override; + int64_t nextInt64(int64_t upper) override; /// requires: lower <= upper int nextInt(int lower, int upper) override; int64_t nextInt64(int64_t lower, int64_t upper) override; /// Generates an integer between 0 and the maximum value it can hold. - int nextInt(); + int nextInt() override; + + /// + int nextBinomialInt(int coinsCount, double coinChance) override; + /// Generates a double between 0 and upper. /// requires: 0 <= upper - double nextDouble(double upper); + double nextDouble(double upper) override; /// requires: lower <= upper double nextDouble(double lower, double upper) override; @@ -66,9 +71,6 @@ public: /// seed a combination of the thread ID and current time in milliseconds will be used. static CRandomGenerator & getDefault(); - /// Provide method so that this RNG can be used with legacy std:: API - TGenerator & getStdGenerator(); - private: TGenerator rand; diff --git a/lib/IGameCallback.cpp b/lib/IGameCallback.cpp index acef3ca0f..443000de3 100644 --- a/lib/IGameCallback.cpp +++ b/lib/IGameCallback.cpp @@ -14,6 +14,7 @@ #include "spells/CSpellHandler.h"// for CSpell #include "CSkillHandler.h"// for CSkill #include "CBonusTypeHandler.h" +#include "CRandomGenerator.h" #include "BattleFieldHandler.h" #include "ObstacleHandler.h" #include "bonuses/Limiters.h" @@ -51,6 +52,8 @@ #include "RiverHandler.h" #include "TerrainHandler.h" +#include + VCMI_LIB_NAMESPACE_BEGIN void CPrivilegedInfoCallback::getFreeTiles(std::vector & tiles) const @@ -146,7 +149,7 @@ void CPrivilegedInfoCallback::getAllTiles(std::unordered_set & tiles, std: } } -void CPrivilegedInfoCallback::pickAllowedArtsSet(std::vector & out, CRandomGenerator & rand) +void CPrivilegedInfoCallback::pickAllowedArtsSet(std::vector & out, vstd::RNG & rand) { for (int j = 0; j < 3 ; j++) out.push_back(gameState()->pickRandomArtifact(rand, CArtifact::ART_TREASURE).toArtifact()); diff --git a/lib/IGameCallback.h b/lib/IGameCallback.h index b3d121294..a42530adc 100644 --- a/lib/IGameCallback.h +++ b/lib/IGameCallback.h @@ -16,13 +16,17 @@ VCMI_LIB_NAMESPACE_BEGIN +namespace vstd +{ +class RNG; +} + struct SetMovePoints; struct GiveBonus; struct BlockingDialog; struct TeleportDialog; struct StackLocation; struct ArtifactLocation; -class CRandomGenerator; class CCreatureSet; class CStackBasicDescriptor; class CGCreature; @@ -61,7 +65,7 @@ public: void getAllTiles(std::unordered_set &tiles, std::optional player, int level, std::function filter) const; //gives 3 treasures, 3 minors, 1 major -> used by Black Market and Artifact Merchant - void pickAllowedArtsSet(std::vector & out, CRandomGenerator & rand); + void pickAllowedArtsSet(std::vector & out, vstd::RNG & rand); void getAllowedSpells(std::vector &out, std::optional level = std::nullopt); template diff --git a/lib/battle/BattleInfo.cpp b/lib/battle/BattleInfo.cpp index 86dcb4f3e..3ecb0bb83 100644 --- a/lib/battle/BattleInfo.cpp +++ b/lib/battle/BattleInfo.cpp @@ -12,7 +12,6 @@ #include "CObstacleInstance.h" #include "bonuses/Limiters.h" #include "bonuses/Updaters.h" -#include "../CRandomGenerator.h" #include "../CStack.h" #include "../CHeroHandler.h" #include "../filesystem/Filesystem.h" @@ -21,6 +20,7 @@ #include "../BattleFieldHandler.h" #include "../ObstacleHandler.h" +#include //TODO: remove #include "../IGameCallback.h" diff --git a/lib/battle/CBattleInfoCallback.cpp b/lib/battle/CBattleInfoCallback.cpp index dd090d7d2..be1e6cc1d 100644 --- a/lib/battle/CBattleInfoCallback.cpp +++ b/lib/battle/CBattleInfoCallback.cpp @@ -11,6 +11,7 @@ #include "CBattleInfoCallback.h" #include +#include #include "../CStack.h" #include "BattleInfo.h" @@ -25,7 +26,6 @@ #include "../networkPacks/PacksForClientBattle.h" #include "../BattleFieldHandler.h" #include "../Rect.h" -#include "../CRandomGenerator.h" VCMI_LIB_NAMESPACE_BEGIN @@ -1602,7 +1602,7 @@ std::set CBattleInfoCallback::battleAdjacentUnits(const ba return ret; } -SpellID CBattleInfoCallback::getRandomBeneficialSpell(CRandomGenerator & rand, const battle::Unit * caster, const battle::Unit * subject) const +SpellID CBattleInfoCallback::getRandomBeneficialSpell(vstd::RNG & rand, const battle::Unit * caster, const battle::Unit * subject) const { RETURN_IF_NOT_BATTLE(SpellID::NONE); //This is complete list. No spells from mods. @@ -1748,7 +1748,7 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(CRandomGenerator & rand, c } } -SpellID CBattleInfoCallback::getRandomCastedSpell(CRandomGenerator & rand,const CStack * caster) const +SpellID CBattleInfoCallback::getRandomCastedSpell(vstd::RNG & rand,const CStack * caster) const { RETURN_IF_NOT_BATTLE(SpellID::NONE); diff --git a/lib/battle/CBattleInfoCallback.h b/lib/battle/CBattleInfoCallback.h index f9fe241a7..1ee127522 100644 --- a/lib/battle/CBattleInfoCallback.h +++ b/lib/battle/CBattleInfoCallback.h @@ -23,9 +23,13 @@ class SpellCastEnvironment; class CSpell; struct CObstacleInstance; class IBonusBearer; -class CRandomGenerator; class PossiblePlayerBattleAction; +namespace vstd +{ +class RNG; +} + namespace spells { class Caster; @@ -116,8 +120,8 @@ public: int32_t battleGetSpellCost(const spells::Spell * sp, const CGHeroInstance * caster) const; //returns cost of given spell ESpellCastProblem battleCanCastSpell(const spells::Caster * caster, spells::Mode mode) const; //returns true if there are no general issues preventing from casting a spell - SpellID getRandomBeneficialSpell(CRandomGenerator & rand, const battle::Unit * caster, const battle::Unit * target) const; - SpellID getRandomCastedSpell(CRandomGenerator & rand, const CStack * caster) const; //called at the beginning of turn for Faerie Dragon + SpellID getRandomBeneficialSpell(vstd::RNG & rand, const battle::Unit * caster, const battle::Unit * target) const; + SpellID getRandomCastedSpell(vstd::RNG & rand, const CStack * caster) const; //called at the beginning of turn for Faerie Dragon std::vector getClientActionsForStack(const CStack * stack, const BattleClientInterfaceData & data); PossiblePlayerBattleAction getCasterAction(const CSpell * spell, const spells::Caster * caster, spells::Mode mode) const; diff --git a/lib/gameState/CGameState.cpp b/lib/gameState/CGameState.cpp index a6c7f4591..225eb4566 100644 --- a/lib/gameState/CGameState.cpp +++ b/lib/gameState/CGameState.cpp @@ -21,6 +21,7 @@ #include "../CGeneralTextHandler.h" #include "../CHeroHandler.h" #include "../CPlayerState.h" +#include "../CRandomGenerator.h" #include "../CStopWatch.h" #include "../GameSettings.h" #include "../StartInfo.h" @@ -53,6 +54,8 @@ #include "../serializer/CTypeList.h" #include "../spells/CSpellHandler.h" +#include + VCMI_LIB_NAMESPACE_BEGIN boost::shared_mutex CGameState::mutex; @@ -183,7 +186,7 @@ void CGameState::init(const IMapService * mapService, StartInfo * si, Load::Prog assert(services); assert(callback); logGlobal->info("\tUsing random seed: %d", si->seedToBeUsed); - getRandomGenerator().setSeed(si->seedToBeUsed); + rand = std::make_unique(si->seedToBeUsed); scenarioOps = CMemorySerializer::deepCopy(*si).release(); initialOpts = CMemorySerializer::deepCopy(*si).release(); si = nullptr; @@ -1072,7 +1075,7 @@ BattleInfo * CGameState::getBattle(const BattleID & battle) return nullptr; } -BattleField CGameState::battleGetBattlefieldType(int3 tile, CRandomGenerator & rand) +BattleField CGameState::battleGetBattlefieldType(int3 tile, vstd::RNG & rand) { assert(tile.valid()); @@ -1961,12 +1964,12 @@ TeamState::TeamState() setNodeType(TEAM); } -CRandomGenerator & CGameState::getRandomGenerator() +vstd::RNG & CGameState::getRandomGenerator() { - return rand; + return *rand; } -ArtifactID CGameState::pickRandomArtifact(CRandomGenerator & rand, int flags, std::function accepts) +ArtifactID CGameState::pickRandomArtifact(vstd::RNG & rand, int flags, std::function accepts) { std::set potentialPicks; @@ -2001,7 +2004,7 @@ ArtifactID CGameState::pickRandomArtifact(CRandomGenerator & rand, int flags, st return pickRandomArtifact(rand, potentialPicks); } -ArtifactID CGameState::pickRandomArtifact(CRandomGenerator & rand, std::set potentialPicks) +ArtifactID CGameState::pickRandomArtifact(vstd::RNG & rand, std::set potentialPicks) { // No allowed artifacts at all - give Grail - this can't be banned (hopefully) // FIXME: investigate how such cases are handled by H3 - some heavily customized user-made maps likely rely on H3 behavior @@ -2030,12 +2033,12 @@ ArtifactID CGameState::pickRandomArtifact(CRandomGenerator & rand, std::set accepts) +ArtifactID CGameState::pickRandomArtifact(vstd::RNG & rand, std::function accepts) { return pickRandomArtifact(rand, 0xff, std::move(accepts)); } -ArtifactID CGameState::pickRandomArtifact(CRandomGenerator & rand, int flags) +ArtifactID CGameState::pickRandomArtifact(vstd::RNG & rand, int flags) { return pickRandomArtifact(rand, flags, [](const ArtifactID &) { return true; }); } diff --git a/lib/gameState/CGameState.h b/lib/gameState/CGameState.h index 9cd3fc312..d31f41f16 100644 --- a/lib/gameState/CGameState.h +++ b/lib/gameState/CGameState.h @@ -13,7 +13,6 @@ #include "IGameCallback.h" #include "LoadProgress.h" #include "ConstTransitivePtr.h" -#include "../CRandomGenerator.h" namespace boost { @@ -34,6 +33,7 @@ class CStackInstance; class CGameStateCampaign; class TavernHeroesPool; struct SThievesGuildInfo; +class CRandomGenerator; template class CApplier; class CBaseForGSApply; @@ -126,7 +126,7 @@ public: HeroTypeID pickNextHeroType(const PlayerColor & owner); void apply(CPack *pack); - BattleField battleGetBattlefieldType(int3 tile, CRandomGenerator & rand); + BattleField battleGetBattlefieldType(int3 tile, vstd::RNG & rand); void fillUpgradeInfo(const CArmedInstance *obj, SlotID stackPos, UpgradeInfo &out) const override; PlayerRelations getPlayerRelations(PlayerColor color1, PlayerColor color2) const override; @@ -138,10 +138,10 @@ public: void updateRumor(); /// Gets a artifact ID randomly and removes the selected artifact from this handler. - ArtifactID pickRandomArtifact(CRandomGenerator & rand, int flags); - ArtifactID pickRandomArtifact(CRandomGenerator & rand, std::function accepts); - ArtifactID pickRandomArtifact(CRandomGenerator & rand, int flags, std::function accepts); - ArtifactID pickRandomArtifact(CRandomGenerator & rand, std::set filtered); + ArtifactID pickRandomArtifact(vstd::RNG & rand, int flags); + ArtifactID pickRandomArtifact(vstd::RNG & rand, std::function accepts); + ArtifactID pickRandomArtifact(vstd::RNG & rand, int flags, std::function accepts); + ArtifactID pickRandomArtifact(vstd::RNG & rand, std::set filtered); /// Returns battle in which selected player is engaged, or nullptr if none. /// Can NOT be used with neutral player, use battle by ID instead @@ -169,11 +169,11 @@ public: /// This RNG should only be used inside GS or CPackForClient-derived applyGs /// If this doesn't work for your code that mean you need a new netpack /// - /// Client-side must use CRandomGenerator::getDefault which is not serialized + /// Client-side must use vstd::RNG::getDefault which is not serialized /// - /// CGameHandler have it's own getter for CRandomGenerator::getDefault - /// Any server-side code outside of GH must use CRandomGenerator::getDefault - CRandomGenerator & getRandomGenerator(); + /// CGameHandler have it's own getter for vstd::RNG::getDefault + /// Any server-side code outside of GH must use vstd::RNG::getDefault + vstd::RNG & getRandomGenerator(); template void serialize(Handler &h) { @@ -234,7 +234,7 @@ private: // ---- data ----- std::shared_ptr> applier; - CRandomGenerator rand; + std::unique_ptr rand; Services * services; /// Pointer to campaign state manager. Nullptr for single scenarios diff --git a/lib/gameState/CGameStateCampaign.cpp b/lib/gameState/CGameStateCampaign.cpp index c496db471..4ce7c11ae 100644 --- a/lib/gameState/CGameStateCampaign.cpp +++ b/lib/gameState/CGameStateCampaign.cpp @@ -28,6 +28,8 @@ #include "../CPlayerState.h" #include "../serializer/CMemorySerializer.h" +#include + VCMI_LIB_NAMESPACE_BEGIN CampaignHeroReplacement::CampaignHeroReplacement(CGHeroInstance * hero, const ObjectInstanceID & heroPlaceholderId): diff --git a/lib/gameState/TavernHeroesPool.h b/lib/gameState/TavernHeroesPool.h index ec92dfe52..250cfb0a8 100644 --- a/lib/gameState/TavernHeroesPool.h +++ b/lib/gameState/TavernHeroesPool.h @@ -18,7 +18,6 @@ VCMI_LIB_NAMESPACE_BEGIN class CGHeroInstance; class CTown; -class CRandomGenerator; class CHeroClass; class CGameState; class CSimpleArmy; diff --git a/lib/json/JsonRandom.cpp b/lib/json/JsonRandom.cpp index 3fbda28ef..57f7a97a2 100644 --- a/lib/json/JsonRandom.cpp +++ b/lib/json/JsonRandom.cpp @@ -12,10 +12,10 @@ #include "JsonRandom.h" #include +#include #include "JsonBonus.h" -#include "../CRandomGenerator.h" #include "../constants/StringConstants.h" #include "../VCMI_Lib.h" #include "../CArtHandler.h" @@ -50,7 +50,7 @@ VCMI_LIB_NAMESPACE_BEGIN return variables.at(variableID); } - si32 JsonRandom::loadValue(const JsonNode & value, CRandomGenerator & rng, const Variables & variables, si32 defaultValue) + si32 JsonRandom::loadValue(const JsonNode & value, vstd::RNG & rng, const Variables & variables, si32 defaultValue) { if(value.isNull()) return defaultValue; @@ -256,7 +256,7 @@ VCMI_LIB_NAMESPACE_BEGIN return valuesSet; } - TResources JsonRandom::loadResources(const JsonNode & value, CRandomGenerator & rng, const Variables & variables) + TResources JsonRandom::loadResources(const JsonNode & value, vstd::RNG & rng, const Variables & variables) { TResources ret; @@ -274,7 +274,7 @@ VCMI_LIB_NAMESPACE_BEGIN return ret; } - TResources JsonRandom::loadResource(const JsonNode & value, CRandomGenerator & rng, const Variables & variables) + TResources JsonRandom::loadResource(const JsonNode & value, vstd::RNG & rng, const Variables & variables) { std::set defaultResources{ GameResID::WOOD, @@ -295,7 +295,7 @@ VCMI_LIB_NAMESPACE_BEGIN return ret; } - PrimarySkill JsonRandom::loadPrimary(const JsonNode & value, CRandomGenerator & rng, const Variables & variables) + PrimarySkill JsonRandom::loadPrimary(const JsonNode & value, vstd::RNG & rng, const Variables & variables) { std::set defaultSkills{ PrimarySkill::ATTACK, @@ -307,7 +307,7 @@ VCMI_LIB_NAMESPACE_BEGIN return *RandomGeneratorUtil::nextItem(potentialPicks, rng); } - std::vector JsonRandom::loadPrimaries(const JsonNode & value, CRandomGenerator & rng, const Variables & variables) + std::vector JsonRandom::loadPrimaries(const JsonNode & value, vstd::RNG & rng, const Variables & variables) { std::vector ret(GameConstants::PRIMARY_SKILLS, 0); std::set defaultSkills{ @@ -339,7 +339,7 @@ VCMI_LIB_NAMESPACE_BEGIN return ret; } - SecondarySkill JsonRandom::loadSecondary(const JsonNode & value, CRandomGenerator & rng, const Variables & variables) + SecondarySkill JsonRandom::loadSecondary(const JsonNode & value, vstd::RNG & rng, const Variables & variables) { std::set defaultSkills; for(const auto & skill : VLC->skillh->objects) @@ -350,7 +350,7 @@ VCMI_LIB_NAMESPACE_BEGIN return *RandomGeneratorUtil::nextItem(potentialPicks, rng); } - std::map JsonRandom::loadSecondaries(const JsonNode & value, CRandomGenerator & rng, const Variables & variables) + std::map JsonRandom::loadSecondaries(const JsonNode & value, vstd::RNG & rng, const Variables & variables) { std::map ret; if(value.isStruct()) @@ -380,7 +380,7 @@ VCMI_LIB_NAMESPACE_BEGIN return ret; } - ArtifactID JsonRandom::loadArtifact(const JsonNode & value, CRandomGenerator & rng, const Variables & variables) + ArtifactID JsonRandom::loadArtifact(const JsonNode & value, vstd::RNG & rng, const Variables & variables) { std::set allowedArts; for(const auto & artifact : VLC->arth->objects) @@ -392,7 +392,7 @@ VCMI_LIB_NAMESPACE_BEGIN return cb->gameState()->pickRandomArtifact(rng, potentialPicks); } - std::vector JsonRandom::loadArtifacts(const JsonNode & value, CRandomGenerator & rng, const Variables & variables) + std::vector JsonRandom::loadArtifacts(const JsonNode & value, vstd::RNG & rng, const Variables & variables) { std::vector ret; for (const JsonNode & entry : value.Vector()) @@ -402,7 +402,7 @@ VCMI_LIB_NAMESPACE_BEGIN return ret; } - SpellID JsonRandom::loadSpell(const JsonNode & value, CRandomGenerator & rng, const Variables & variables) + SpellID JsonRandom::loadSpell(const JsonNode & value, vstd::RNG & rng, const Variables & variables) { std::set defaultSpells; for(const auto & spell : VLC->spellh->objects) @@ -419,7 +419,7 @@ VCMI_LIB_NAMESPACE_BEGIN return *RandomGeneratorUtil::nextItem(potentialPicks, rng); } - std::vector JsonRandom::loadSpells(const JsonNode & value, CRandomGenerator & rng, const Variables & variables) + std::vector JsonRandom::loadSpells(const JsonNode & value, vstd::RNG & rng, const Variables & variables) { std::vector ret; for (const JsonNode & entry : value.Vector()) @@ -429,7 +429,7 @@ VCMI_LIB_NAMESPACE_BEGIN return ret; } - std::vector JsonRandom::loadColors(const JsonNode & value, CRandomGenerator & rng, const Variables & variables) + std::vector JsonRandom::loadColors(const JsonNode & value, vstd::RNG & rng, const Variables & variables) { std::vector ret; std::set defaultPlayers; @@ -445,7 +445,7 @@ VCMI_LIB_NAMESPACE_BEGIN return ret; } - std::vector JsonRandom::loadHeroes(const JsonNode & value, CRandomGenerator & rng) + std::vector JsonRandom::loadHeroes(const JsonNode & value, vstd::RNG & rng) { std::vector ret; for(auto & entry : value.Vector()) @@ -455,7 +455,7 @@ VCMI_LIB_NAMESPACE_BEGIN return ret; } - std::vector JsonRandom::loadHeroClasses(const JsonNode & value, CRandomGenerator & rng) + std::vector JsonRandom::loadHeroClasses(const JsonNode & value, vstd::RNG & rng) { std::vector ret; for(auto & entry : value.Vector()) @@ -465,7 +465,7 @@ VCMI_LIB_NAMESPACE_BEGIN return ret; } - CStackBasicDescriptor JsonRandom::loadCreature(const JsonNode & value, CRandomGenerator & rng, const Variables & variables) + CStackBasicDescriptor JsonRandom::loadCreature(const JsonNode & value, vstd::RNG & rng, const Variables & variables) { CStackBasicDescriptor stack; @@ -494,7 +494,7 @@ VCMI_LIB_NAMESPACE_BEGIN return stack; } - std::vector JsonRandom::loadCreatures(const JsonNode & value, CRandomGenerator & rng, const Variables & variables) + std::vector JsonRandom::loadCreatures(const JsonNode & value, vstd::RNG & rng, const Variables & variables) { std::vector ret; for (const JsonNode & node : value.Vector()) diff --git a/lib/json/JsonRandom.h b/lib/json/JsonRandom.h index 8129c11bb..5098dc306 100644 --- a/lib/json/JsonRandom.h +++ b/lib/json/JsonRandom.h @@ -15,9 +15,13 @@ VCMI_LIB_NAMESPACE_BEGIN +namespace vstd +{ +class RNG; +} + class JsonNode; using JsonVector = std::vector; -class CRandomGenerator; struct Bonus; struct Component; @@ -53,28 +57,28 @@ public: si32 maxAmount; }; - si32 loadValue(const JsonNode & value, CRandomGenerator & rng, const Variables & variables, si32 defaultValue = 0); + si32 loadValue(const JsonNode & value, vstd::RNG & rng, const Variables & variables, si32 defaultValue = 0); - TResources loadResources(const JsonNode & value, CRandomGenerator & rng, const Variables & variables); - TResources loadResource(const JsonNode & value, CRandomGenerator & rng, const Variables & variables); - PrimarySkill loadPrimary(const JsonNode & value, CRandomGenerator & rng, const Variables & variables); - std::vector loadPrimaries(const JsonNode & value, CRandomGenerator & rng, const Variables & variables); - SecondarySkill loadSecondary(const JsonNode & value, CRandomGenerator & rng, const Variables & variables); - std::map loadSecondaries(const JsonNode & value, CRandomGenerator & rng, const Variables & variables); + TResources loadResources(const JsonNode & value, vstd::RNG & rng, const Variables & variables); + TResources loadResource(const JsonNode & value, vstd::RNG & rng, const Variables & variables); + PrimarySkill loadPrimary(const JsonNode & value, vstd::RNG & rng, const Variables & variables); + std::vector loadPrimaries(const JsonNode & value, vstd::RNG & rng, const Variables & variables); + SecondarySkill loadSecondary(const JsonNode & value, vstd::RNG & rng, const Variables & variables); + std::map loadSecondaries(const JsonNode & value, vstd::RNG & rng, const Variables & variables); - ArtifactID loadArtifact(const JsonNode & value, CRandomGenerator & rng, const Variables & variables); - std::vector loadArtifacts(const JsonNode & value, CRandomGenerator & rng, const Variables & variables); + ArtifactID loadArtifact(const JsonNode & value, vstd::RNG & rng, const Variables & variables); + std::vector loadArtifacts(const JsonNode & value, vstd::RNG & rng, const Variables & variables); - SpellID loadSpell(const JsonNode & value, CRandomGenerator & rng, const Variables & variables); - std::vector loadSpells(const JsonNode & value, CRandomGenerator & rng, const Variables & variables); + SpellID loadSpell(const JsonNode & value, vstd::RNG & rng, const Variables & variables); + std::vector loadSpells(const JsonNode & value, vstd::RNG & rng, const Variables & variables); - CStackBasicDescriptor loadCreature(const JsonNode & value, CRandomGenerator & rng, const Variables & variables); - std::vector loadCreatures(const JsonNode & value, CRandomGenerator & rng, const Variables & variables); + CStackBasicDescriptor loadCreature(const JsonNode & value, vstd::RNG & rng, const Variables & variables); + std::vector loadCreatures(const JsonNode & value, vstd::RNG & rng, const Variables & variables); std::vector evaluateCreatures(const JsonNode & value, const Variables & variables); - std::vector loadColors(const JsonNode & value, CRandomGenerator & rng, const Variables & variables); - std::vector loadHeroes(const JsonNode & value, CRandomGenerator & rng); - std::vector loadHeroClasses(const JsonNode & value, CRandomGenerator & rng); + std::vector loadColors(const JsonNode & value, vstd::RNG & rng, const Variables & variables); + std::vector loadHeroes(const JsonNode & value, vstd::RNG & rng); + std::vector loadHeroClasses(const JsonNode & value, vstd::RNG & rng); static std::vector loadBonuses(const JsonNode & value); }; diff --git a/lib/mapObjectConstructors/AObjectTypeHandler.h b/lib/mapObjectConstructors/AObjectTypeHandler.h index ba298d13f..d555244b2 100644 --- a/lib/mapObjectConstructors/AObjectTypeHandler.h +++ b/lib/mapObjectConstructors/AObjectTypeHandler.h @@ -15,9 +15,13 @@ VCMI_LIB_NAMESPACE_BEGIN +namespace vstd +{ +class RNG; +} + class ObjectTemplate; class CGObjectInstance; -class CRandomGenerator; class IObjectInfo; class IGameCallback; @@ -114,7 +118,7 @@ public: /// Configures object properties. Should be re-entrable, resetting state of the object if necessarily /// This should set remaining properties, including randomized or depending on map - virtual void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const = 0; + virtual void configureObject(CGObjectInstance * object, vstd::RNG & rng) const = 0; /// Returns object configuration, if available. Otherwise returns NULL virtual std::unique_ptr getObjectInfo(std::shared_ptr tmpl) const; diff --git a/lib/mapObjectConstructors/CBankInstanceConstructor.cpp b/lib/mapObjectConstructors/CBankInstanceConstructor.cpp index f1f6a5c84..fccd3a870 100644 --- a/lib/mapObjectConstructors/CBankInstanceConstructor.cpp +++ b/lib/mapObjectConstructors/CBankInstanceConstructor.cpp @@ -13,7 +13,8 @@ #include "../json/JsonRandom.h" #include "../CGeneralTextHandler.h" #include "../IGameCallback.h" -#include "../CRandomGenerator.h" + +#include VCMI_LIB_NAMESPACE_BEGIN @@ -36,7 +37,7 @@ void CBankInstanceConstructor::initTypeData(const JsonNode & input) regularUnitPlacement = input["regularUnitPlacement"].Bool(); } -BankConfig CBankInstanceConstructor::generateConfig(IGameCallback * cb, const JsonNode & level, CRandomGenerator & rng) const +BankConfig CBankInstanceConstructor::generateConfig(IGameCallback * cb, const JsonNode & level, vstd::RNG & rng) const { BankConfig bc; JsonRandom randomizer(cb); @@ -53,7 +54,7 @@ BankConfig CBankInstanceConstructor::generateConfig(IGameCallback * cb, const Js return bc; } -void CBankInstanceConstructor::randomizeObject(CBank * bank, CRandomGenerator & rng) const +void CBankInstanceConstructor::randomizeObject(CBank * bank, vstd::RNG & rng) const { bank->resetDuration = bankResetDuration; bank->blockVisit = blockVisit; diff --git a/lib/mapObjectConstructors/CBankInstanceConstructor.h b/lib/mapObjectConstructors/CBankInstanceConstructor.h index 246da89b6..3fe706782 100644 --- a/lib/mapObjectConstructors/CBankInstanceConstructor.h +++ b/lib/mapObjectConstructors/CBankInstanceConstructor.h @@ -69,7 +69,7 @@ public: class CBankInstanceConstructor : public CDefaultObjectTypeHandler { - BankConfig generateConfig(IGameCallback * cb, const JsonNode & conf, CRandomGenerator & rng) const; + BankConfig generateConfig(IGameCallback * cb, const JsonNode & conf, vstd::RNG & rng) const; JsonVector levels; @@ -87,7 +87,7 @@ protected: public: - void randomizeObject(CBank * object, CRandomGenerator & rng) const override; + void randomizeObject(CBank * object, vstd::RNG & rng) const override; bool hasNameTextID() const override; diff --git a/lib/mapObjectConstructors/CDefaultObjectTypeHandler.h b/lib/mapObjectConstructors/CDefaultObjectTypeHandler.h index c56e5eee5..e0d37469d 100644 --- a/lib/mapObjectConstructors/CDefaultObjectTypeHandler.h +++ b/lib/mapObjectConstructors/CDefaultObjectTypeHandler.h @@ -17,7 +17,7 @@ VCMI_LIB_NAMESPACE_BEGIN template class CDefaultObjectTypeHandler : public AObjectTypeHandler { - void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const final + void configureObject(CGObjectInstance * object, vstd::RNG & rng) const final { ObjectType * castedObject = dynamic_cast(object); @@ -43,7 +43,7 @@ class CDefaultObjectTypeHandler : public AObjectTypeHandler protected: virtual void initializeObject(ObjectType * object) const {} - virtual void randomizeObject(ObjectType * object, CRandomGenerator & rng) const {} + virtual void randomizeObject(ObjectType * object, vstd::RNG & rng) const {} virtual ObjectType * createObject(IGameCallback * cb) const { return new ObjectType(cb); diff --git a/lib/mapObjectConstructors/CObjectClassesHandler.h b/lib/mapObjectConstructors/CObjectClassesHandler.h index d65d2b38c..97521688c 100644 --- a/lib/mapObjectConstructors/CObjectClassesHandler.h +++ b/lib/mapObjectConstructors/CObjectClassesHandler.h @@ -15,7 +15,6 @@ VCMI_LIB_NAMESPACE_BEGIN -class CRandomGenerator; class AObjectTypeHandler; class ObjectTemplate; struct SObjectSounds; diff --git a/lib/mapObjectConstructors/CRewardableConstructor.cpp b/lib/mapObjectConstructors/CRewardableConstructor.cpp index abe3552b5..c2e7422e9 100644 --- a/lib/mapObjectConstructors/CRewardableConstructor.cpp +++ b/lib/mapObjectConstructors/CRewardableConstructor.cpp @@ -40,7 +40,7 @@ CGObjectInstance * CRewardableConstructor::create(IGameCallback * cb, std::share return ret; } -void CRewardableConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const +void CRewardableConstructor::configureObject(CGObjectInstance * object, vstd::RNG & rng) const { if(auto * rewardableObject = dynamic_cast(object)) { diff --git a/lib/mapObjectConstructors/CRewardableConstructor.h b/lib/mapObjectConstructors/CRewardableConstructor.h index 81fd286e6..a5b9d5423 100644 --- a/lib/mapObjectConstructors/CRewardableConstructor.h +++ b/lib/mapObjectConstructors/CRewardableConstructor.h @@ -27,7 +27,7 @@ public: CGObjectInstance * create(IGameCallback * cb, std::shared_ptr tmpl = nullptr) const override; - void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override; + void configureObject(CGObjectInstance * object, vstd::RNG & rng) const override; std::unique_ptr getObjectInfo(std::shared_ptr tmpl) const override; }; diff --git a/lib/mapObjectConstructors/CommonConstructors.cpp b/lib/mapObjectConstructors/CommonConstructors.cpp index 806b32194..94aade233 100644 --- a/lib/mapObjectConstructors/CommonConstructors.cpp +++ b/lib/mapObjectConstructors/CommonConstructors.cpp @@ -100,7 +100,7 @@ void CTownInstanceConstructor::initializeObject(CGTownInstance * obj) const obj->tempOwner = PlayerColor::NEUTRAL; } -void CTownInstanceConstructor::randomizeObject(CGTownInstance * object, CRandomGenerator & rng) const +void CTownInstanceConstructor::randomizeObject(CGTownInstance * object, vstd::RNG & rng) const { auto templ = getOverride(object->cb->getTile(object->pos)->terType->getId(), object); if(templ) @@ -159,7 +159,7 @@ void CHeroInstanceConstructor::initializeObject(CGHeroInstance * obj) const obj->type = nullptr; //FIXME: set to valid value. somehow. } -void CHeroInstanceConstructor::randomizeObject(CGHeroInstance * object, CRandomGenerator & rng) const +void CHeroInstanceConstructor::randomizeObject(CGHeroInstance * object, vstd::RNG & rng) const { } @@ -259,7 +259,7 @@ void MarketInstanceConstructor::initializeObject(CGMarket * market) const market->speech = VLC->generaltexth->translate(speech); } -void MarketInstanceConstructor::randomizeObject(CGMarket * object, CRandomGenerator & rng) const +void MarketInstanceConstructor::randomizeObject(CGMarket * object, vstd::RNG & rng) const { JsonRandom randomizer(object->cb); JsonRandom::Variables emptyVariables; diff --git a/lib/mapObjectConstructors/CommonConstructors.h b/lib/mapObjectConstructors/CommonConstructors.h index ea5ef3d72..81cc4e3a0 100644 --- a/lib/mapObjectConstructors/CommonConstructors.h +++ b/lib/mapObjectConstructors/CommonConstructors.h @@ -63,7 +63,7 @@ public: std::map> filters; void initializeObject(CGTownInstance * object) const override; - void randomizeObject(CGTownInstance * object, CRandomGenerator & rng) const override; + void randomizeObject(CGTownInstance * object, vstd::RNG & rng) const override; void afterLoadFinalization() override; bool hasNameTextID() const override; @@ -82,7 +82,7 @@ public: std::map> filters; void initializeObject(CGHeroInstance * object) const override; - void randomizeObject(CGHeroInstance * object, CRandomGenerator & rng) const override; + void randomizeObject(CGHeroInstance * object, vstd::RNG & rng) const override; void afterLoadFinalization() override; bool hasNameTextID() const override; @@ -125,7 +125,7 @@ protected: public: CGMarket * createObject(IGameCallback * cb) const override; void initializeObject(CGMarket * object) const override; - void randomizeObject(CGMarket * object, CRandomGenerator & rng) const override; + void randomizeObject(CGMarket * object, vstd::RNG & rng) const override; }; diff --git a/lib/mapObjectConstructors/DwellingInstanceConstructor.cpp b/lib/mapObjectConstructors/DwellingInstanceConstructor.cpp index b6fe21715..12c2a0d93 100644 --- a/lib/mapObjectConstructors/DwellingInstanceConstructor.cpp +++ b/lib/mapObjectConstructors/DwellingInstanceConstructor.cpp @@ -74,7 +74,7 @@ void DwellingInstanceConstructor::initializeObject(CGDwelling * obj) const } } -void DwellingInstanceConstructor::randomizeObject(CGDwelling * dwelling, CRandomGenerator &rng) const +void DwellingInstanceConstructor::randomizeObject(CGDwelling * dwelling, vstd::RNG &rng) const { JsonRandom randomizer(dwelling->cb); diff --git a/lib/mapObjectConstructors/DwellingInstanceConstructor.h b/lib/mapObjectConstructors/DwellingInstanceConstructor.h index ac82e2e87..99b61093c 100644 --- a/lib/mapObjectConstructors/DwellingInstanceConstructor.h +++ b/lib/mapObjectConstructors/DwellingInstanceConstructor.h @@ -33,7 +33,7 @@ public: bool hasNameTextID() const override; void initializeObject(CGDwelling * object) const override; - void randomizeObject(CGDwelling * object, CRandomGenerator & rng) const override; + void randomizeObject(CGDwelling * object, vstd::RNG & rng) const override; bool isBannedForRandomDwelling() const; bool producesCreature(const CCreature * crea) const; diff --git a/lib/mapObjects/CBank.cpp b/lib/mapObjects/CBank.cpp index 75ac1bcaa..e7dba0aca 100644 --- a/lib/mapObjects/CBank.cpp +++ b/lib/mapObjects/CBank.cpp @@ -44,7 +44,7 @@ CBank::CBank(IGameCallback *cb) //must be instantiated in .cpp file for access to complete types of all member fields CBank::~CBank() = default; -void CBank::initObj(CRandomGenerator & rand) +void CBank::initObj(vstd::RNG & rand) { daycounter = 0; resetDuration = 0; @@ -117,7 +117,7 @@ void CBank::setPropertyDer (ObjProperty what, ObjPropertyID identifier) } } -void CBank::newTurn(CRandomGenerator & rand) const +void CBank::newTurn(vstd::RNG & rand) const { if (bankConfig == nullptr) { diff --git a/lib/mapObjects/CBank.h b/lib/mapObjects/CBank.h index 9c9dd5059..d2fe226f1 100644 --- a/lib/mapObjects/CBank.h +++ b/lib/mapObjects/CBank.h @@ -33,9 +33,9 @@ public: void setConfig(const BankConfig & bc); - void initObj(CRandomGenerator & rand) override; + void initObj(vstd::RNG & rand) override; std::string getHoverText(PlayerColor player) const override; - void newTurn(CRandomGenerator & rand) const override; + void newTurn(vstd::RNG & rand) const override; bool wasVisited (PlayerColor player) const override; bool isCoastVisitable() const override; void onHeroVisit(const CGHeroInstance * h) const override; diff --git a/lib/mapObjects/CGCreature.cpp b/lib/mapObjects/CGCreature.cpp index a2da0ef3c..12e6ef77c 100644 --- a/lib/mapObjects/CGCreature.cpp +++ b/lib/mapObjects/CGCreature.cpp @@ -16,12 +16,14 @@ #include "../CConfigHandler.h" #include "../GameSettings.h" #include "../IGameCallback.h" +#include "../gameState/CGameState.h" #include "../mapObjectConstructors/CObjectClassesHandler.h" #include "../networkPacks/PacksForClient.h" #include "../networkPacks/PacksForClientBattle.h" #include "../networkPacks/StackLocation.h" #include "../serializer/JsonSerializeFormat.h" -#include "../CRandomGenerator.h" + +#include VCMI_LIB_NAMESPACE_BEGIN @@ -189,7 +191,7 @@ CreatureID CGCreature::getCreature() const return CreatureID(getObjTypeIndex().getNum()); } -void CGCreature::pickRandomObject(CRandomGenerator & rand) +void CGCreature::pickRandomObject(vstd::RNG & rand) { switch(ID.toEnum()) { @@ -234,7 +236,7 @@ void CGCreature::pickRandomObject(CRandomGenerator & rand) setType(ID, subID); } -void CGCreature::initObj(CRandomGenerator & rand) +void CGCreature::initObj(vstd::RNG & rand) { blockVisit = true; switch(character) @@ -274,7 +276,7 @@ void CGCreature::initObj(CRandomGenerator & rand) refusedJoining = false; } -void CGCreature::newTurn(CRandomGenerator & rand) const +void CGCreature::newTurn(vstd::RNG & rand) const {//Works only for stacks of single type of size up to 2 millions if (!notGrowingTeam) { @@ -457,7 +459,7 @@ void CGCreature::fight( const CGHeroInstance *h ) const const auto & upgrades = getStack(slotID).type->upgrades; if(!upgrades.empty()) { - auto it = RandomGeneratorUtil::nextItem(upgrades, CRandomGenerator::getDefault()); + auto it = RandomGeneratorUtil::nextItem(upgrades, cb->gameState()->getRandomGenerator()); cb->changeStackType(StackLocation(this, slotID), it->toCreature()); } } diff --git a/lib/mapObjects/CGCreature.h b/lib/mapObjects/CGCreature.h index 90a75cd77..34374ff49 100644 --- a/lib/mapObjects/CGCreature.h +++ b/lib/mapObjects/CGCreature.h @@ -45,9 +45,9 @@ public: std::string getPopupText(PlayerColor player) const override; std::string getPopupText(const CGHeroInstance * hero) const override; std::vector getPopupComponents(PlayerColor player) const override; - void initObj(CRandomGenerator & rand) override; - void pickRandomObject(CRandomGenerator & rand) override; - void newTurn(CRandomGenerator & rand) const override; + void initObj(vstd::RNG & rand) override; + void pickRandomObject(vstd::RNG & rand) override; + void newTurn(vstd::RNG & rand) const override; void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override; void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override; CreatureID getCreature() const; diff --git a/lib/mapObjects/CGDwelling.cpp b/lib/mapObjects/CGDwelling.cpp index 4b67b2c76..aa0d85452 100644 --- a/lib/mapObjects/CGDwelling.cpp +++ b/lib/mapObjects/CGDwelling.cpp @@ -27,6 +27,8 @@ #include "../GameSettings.h" #include "../CConfigHandler.h" +#include + VCMI_LIB_NAMESPACE_BEGIN void CGDwellingRandomizationInfo::serializeJson(JsonSerializeFormat & handler) @@ -50,7 +52,7 @@ CGDwelling::CGDwelling(IGameCallback *cb): CGDwelling::~CGDwelling() = default; -FactionID CGDwelling::randomizeFaction(CRandomGenerator & rand) +FactionID CGDwelling::randomizeFaction(vstd::RNG & rand) { if (ID == Obj::RANDOM_DWELLING_FACTION) return FactionID(subID.getNum()); @@ -108,7 +110,7 @@ FactionID CGDwelling::randomizeFaction(CRandomGenerator & rand) return *RandomGeneratorUtil::nextItem(potentialPicks, rand); } -int CGDwelling::randomizeLevel(CRandomGenerator & rand) +int CGDwelling::randomizeLevel(vstd::RNG & rand) { if (ID == Obj::RANDOM_DWELLING_LVL) return subID.getNum(); @@ -125,7 +127,7 @@ int CGDwelling::randomizeLevel(CRandomGenerator & rand) return rand.nextInt(randomizationInfo->minLevel, randomizationInfo->maxLevel) - 1; } -void CGDwelling::pickRandomObject(CRandomGenerator & rand) +void CGDwelling::pickRandomObject(vstd::RNG & rand) { if (ID == Obj::RANDOM_DWELLING || ID == Obj::RANDOM_DWELLING_LVL || ID == Obj::RANDOM_DWELLING_FACTION) { @@ -172,7 +174,7 @@ void CGDwelling::pickRandomObject(CRandomGenerator & rand) } } -void CGDwelling::initObj(CRandomGenerator & rand) +void CGDwelling::initObj(vstd::RNG & rand) { switch(ID.toEnum()) { @@ -298,7 +300,7 @@ void CGDwelling::onHeroVisit( const CGHeroInstance * h ) const cb->showBlockingDialog(&bd); } -void CGDwelling::newTurn(CRandomGenerator & rand) const +void CGDwelling::newTurn(vstd::RNG & rand) const { if(cb->getDate(Date::DAY_OF_WEEK) != 1) //not first day of week return; diff --git a/lib/mapObjects/CGDwelling.h b/lib/mapObjects/CGDwelling.h index da85542d2..4afa3907e 100644 --- a/lib/mapObjects/CGDwelling.h +++ b/lib/mapObjects/CGDwelling.h @@ -45,13 +45,13 @@ protected: void serializeJsonOptions(JsonSerializeFormat & handler) override; private: - FactionID randomizeFaction(CRandomGenerator & rand); - int randomizeLevel(CRandomGenerator & rand); + FactionID randomizeFaction(vstd::RNG & rand); + int randomizeLevel(vstd::RNG & rand); - void pickRandomObject(CRandomGenerator & rand) override; - void initObj(CRandomGenerator & rand) override; + void pickRandomObject(vstd::RNG & rand) override; + void initObj(vstd::RNG & rand) override; void onHeroVisit(const CGHeroInstance * h) const override; - void newTurn(CRandomGenerator & rand) const override; + void newTurn(vstd::RNG & rand) const override; void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override; void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override; void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override; diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index c99667f58..e33fb711f 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -313,13 +313,13 @@ void CGHeroInstance::setHeroType(HeroTypeID heroType) subID = heroType; } -void CGHeroInstance::initHero(CRandomGenerator & rand, const HeroTypeID & SUBID) +void CGHeroInstance::initHero(vstd::RNG & rand, const HeroTypeID & SUBID) { subID = SUBID.getNum(); initHero(rand); } -void CGHeroInstance::initHero(CRandomGenerator & rand) +void CGHeroInstance::initHero(vstd::RNG & rand) { assert(validTypes(true)); if(!type) @@ -422,7 +422,7 @@ void CGHeroInstance::initHero(CRandomGenerator & rand) mana = manaLimit(); //after all bonuses are taken into account, make sure this line is the last one } -void CGHeroInstance::initArmy(CRandomGenerator & rand, IArmyDescriptor * dst) +void CGHeroInstance::initArmy(vstd::RNG & rand, IArmyDescriptor * dst) { if(!dst) dst = this; @@ -589,7 +589,7 @@ void CGHeroInstance::SecondarySkillsInfo::resetWisdomCounter() wisdomCounter = 0; } -void CGHeroInstance::pickRandomObject(CRandomGenerator & rand) +void CGHeroInstance::pickRandomObject(vstd::RNG & rand) { assert(ID == Obj::HERO || ID == Obj::PRISON || ID == Obj::RANDOM_HERO); @@ -614,7 +614,7 @@ void CGHeroInstance::pickRandomObject(CRandomGenerator & rand) this->subID = oldSubID; } -void CGHeroInstance::initObj(CRandomGenerator & rand) +void CGHeroInstance::initObj(vstd::RNG & rand) { } @@ -959,7 +959,7 @@ CStackBasicDescriptor CGHeroInstance::calculateNecromancy (const BattleResult &b * @param raisedStack Pair where the first element represents ID of the raised creature * and the second element the amount. */ -void CGHeroInstance::showNecromancyDialog(const CStackBasicDescriptor &raisedStack, CRandomGenerator & rand) const +void CGHeroInstance::showNecromancyDialog(const CStackBasicDescriptor &raisedStack, vstd::RNG & rand) const { InfoWindow iw; iw.type = EInfoWindowMode::AUTO; @@ -1068,7 +1068,7 @@ EAlignment CGHeroInstance::getAlignment() const return type->heroClass->getAlignment(); } -void CGHeroInstance::initExp(CRandomGenerator & rand) +void CGHeroInstance::initExp(vstd::RNG & rand) { exp = rand.nextInt(40, 89); } @@ -1286,7 +1286,7 @@ ArtBearer::ArtBearer CGHeroInstance::bearerType() const return ArtBearer::HERO; } -std::vector CGHeroInstance::getLevelUpProposedSecondarySkills(CRandomGenerator & rand) const +std::vector CGHeroInstance::getLevelUpProposedSecondarySkills(vstd::RNG & rand) const { auto getObligatorySkills = [](CSkill::Obligatory obl){ std::set obligatory; @@ -1365,7 +1365,7 @@ std::vector CGHeroInstance::getLevelUpProposedSecondarySkills(CR return skills; } -PrimarySkill CGHeroInstance::nextPrimarySkill(CRandomGenerator & rand) const +PrimarySkill CGHeroInstance::nextPrimarySkill(vstd::RNG & rand) const { assert(gainsLevel()); const auto isLowLevelHero = level < GameConstants::HERO_HIGH_LEVEL; @@ -1381,7 +1381,7 @@ PrimarySkill CGHeroInstance::nextPrimarySkill(CRandomGenerator & rand) const return static_cast(RandomGeneratorUtil::nextItemWeighted(skillChances, rand)); } -std::optional CGHeroInstance::nextSecondarySkill(CRandomGenerator & rand) const +std::optional CGHeroInstance::nextSecondarySkill(vstd::RNG & rand) const { assert(gainsLevel()); @@ -1469,7 +1469,7 @@ void CGHeroInstance::levelUp(const std::vector & skills) treeHasChanged(); } -void CGHeroInstance::levelUpAutomatically(CRandomGenerator & rand) +void CGHeroInstance::levelUpAutomatically(vstd::RNG & rand) { while(gainsLevel()) { diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index b929ef7e0..cc8dcbe8a 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -187,13 +187,13 @@ public: bool gainsLevel() const; /// Returns the next primary skill on level up. Can only be called if hero can gain a level up. - PrimarySkill nextPrimarySkill(CRandomGenerator & rand) const; + PrimarySkill nextPrimarySkill(vstd::RNG & rand) const; /// Returns the next secondary skill randomly on level up. Can only be called if hero can gain a level up. - std::optional nextSecondarySkill(CRandomGenerator & rand) const; + std::optional nextSecondarySkill(vstd::RNG & rand) const; /// Gets 0, 1 or 2 secondary skills which are proposed on hero level up. - std::vector getLevelUpProposedSecondarySkills(CRandomGenerator & rand) const; + std::vector getLevelUpProposedSecondarySkills(vstd::RNG & rand) const; ui8 getSecSkillLevel(const SecondarySkill & skill) const; //0 - no skill @@ -225,7 +225,7 @@ public: TExpType calculateXp(TExpType exp) const; //apply learning skill CStackBasicDescriptor calculateNecromancy (const BattleResult &battleResult) const; - void showNecromancyDialog(const CStackBasicDescriptor &raisedStack, CRandomGenerator & rand) const; + void showNecromancyDialog(const CStackBasicDescriptor &raisedStack, vstd::RNG & rand) const; EDiggingStatus diggingStatus() const; ////////////////////////////////////////////////////////////////////////// @@ -233,13 +233,13 @@ public: HeroTypeID getHeroType() const; void setHeroType(HeroTypeID type); - void initHero(CRandomGenerator & rand); - void initHero(CRandomGenerator & rand, const HeroTypeID & SUBID); + void initHero(vstd::RNG & rand); + void initHero(vstd::RNG & rand, const HeroTypeID & SUBID); ArtPlacementMap putArtifact(ArtifactPosition pos, CArtifactInstance * art) override; void removeArtifact(ArtifactPosition pos) override; - void initExp(CRandomGenerator & rand); - void initArmy(CRandomGenerator & rand, IArmyDescriptor *dst = nullptr); + void initExp(vstd::RNG & rand); + void initArmy(vstd::RNG & rand, IArmyDescriptor *dst = nullptr); void pushPrimSkill(PrimarySkill which, int val); ui8 maxlevelsToMagicSchool() const; ui8 maxlevelsToWisdom() const; @@ -293,8 +293,8 @@ public: void boatDeserializationFix(); void deserializationFix(); - void initObj(CRandomGenerator & rand) override; - void pickRandomObject(CRandomGenerator & rand) override; + void initObj(vstd::RNG & rand) override; + void pickRandomObject(vstd::RNG & rand) override; void onHeroVisit(const CGHeroInstance * h) const override; std::string getObjectName() const override; @@ -318,7 +318,7 @@ protected: void serializeJsonOptions(JsonSerializeFormat & handler) override; private: - void levelUpAutomatically(CRandomGenerator & rand); + void levelUpAutomatically(vstd::RNG & rand); public: std::string getHeroTypeName() const; diff --git a/lib/mapObjects/CGMarket.cpp b/lib/mapObjects/CGMarket.cpp index dfc072366..3bca00646 100644 --- a/lib/mapObjects/CGMarket.cpp +++ b/lib/mapObjects/CGMarket.cpp @@ -23,7 +23,7 @@ VCMI_LIB_NAMESPACE_BEGIN -void CGMarket::initObj(CRandomGenerator & rand) +void CGMarket::initObj(vstd::RNG & rand) { getObjectHandler()->configureObject(this, rand); } @@ -80,7 +80,7 @@ std::vector CGBlackMarket::availableItemsIds(EMarketMode mode) con } } -void CGBlackMarket::newTurn(CRandomGenerator & rand) const +void CGBlackMarket::newTurn(vstd::RNG & rand) const { int resetPeriod = VLC->settings()->getInteger(EGameSettings::MARKETS_BLACK_MARKET_RESTOCK_PERIOD); diff --git a/lib/mapObjects/CGMarket.h b/lib/mapObjects/CGMarket.h index c0a589f3b..956d15daa 100644 --- a/lib/mapObjects/CGMarket.h +++ b/lib/mapObjects/CGMarket.h @@ -29,7 +29,7 @@ public: CGMarket(IGameCallback *cb); ///IObjectInterface void onHeroVisit(const CGHeroInstance * h) const override; //open trading window - void initObj(CRandomGenerator & rand) override;//set skills for trade + void initObj(vstd::RNG & rand) override;//set skills for trade ///IMarket int getMarketEfficiency() const override; @@ -54,7 +54,7 @@ public: std::vector artifacts; //available artifacts - void newTurn(CRandomGenerator & rand) const override; //reset artifacts for black market every month + void newTurn(vstd::RNG & rand) const override; //reset artifacts for black market every month std::vector availableItemsIds(EMarketMode mode) const override; template void serialize(Handler &h) diff --git a/lib/mapObjects/CGObjectInstance.cpp b/lib/mapObjects/CGObjectInstance.cpp index f23f3a44a..b659c34d1 100644 --- a/lib/mapObjects/CGObjectInstance.cpp +++ b/lib/mapObjects/CGObjectInstance.cpp @@ -25,6 +25,8 @@ #include "../networkPacks/PacksForClient.h" #include "../serializer/JsonSerializeFormat.h" +#include + VCMI_LIB_NAMESPACE_BEGIN //TODO: remove constructor @@ -164,12 +166,12 @@ void CGObjectInstance::setType(MapObjectID newID, MapObjectSubID newSubID) cb->gameState()->map->addBlockVisTiles(this); } -void CGObjectInstance::pickRandomObject(CRandomGenerator & rand) +void CGObjectInstance::pickRandomObject(vstd::RNG & rand) { // no-op } -void CGObjectInstance::initObj(CRandomGenerator & rand) +void CGObjectInstance::initObj(vstd::RNG & rand) { // no-op } @@ -232,7 +234,7 @@ std::string CGObjectInstance::getObjectName() const return VLC->objtypeh->getObjectName(ID, subID); } -std::optional CGObjectInstance::getAmbientSound() const +std::optional CGObjectInstance::getAmbientSound(vstd::RNG & rng) const { const auto & sounds = VLC->objtypeh->getObjectSounds(ID, subID).ambient; if(!sounds.empty()) @@ -241,20 +243,20 @@ std::optional CGObjectInstance::getAmbientSound() const return std::nullopt; } -std::optional CGObjectInstance::getVisitSound() const +std::optional CGObjectInstance::getVisitSound(vstd::RNG & rng) const { const auto & sounds = VLC->objtypeh->getObjectSounds(ID, subID).visit; if(!sounds.empty()) - return *RandomGeneratorUtil::nextItem(sounds, CRandomGenerator::getDefault()); + return *RandomGeneratorUtil::nextItem(sounds, rng); return std::nullopt; } -std::optional CGObjectInstance::getRemovalSound() const +std::optional CGObjectInstance::getRemovalSound(vstd::RNG & rng) const { const auto & sounds = VLC->objtypeh->getObjectSounds(ID, subID).removal; if(!sounds.empty()) - return *RandomGeneratorUtil::nextItem(sounds, CRandomGenerator::getDefault()); + return *RandomGeneratorUtil::nextItem(sounds, rng); return std::nullopt; } diff --git a/lib/mapObjects/CGObjectInstance.h b/lib/mapObjects/CGObjectInstance.h index 143118727..36a330142 100644 --- a/lib/mapObjects/CGObjectInstance.h +++ b/lib/mapObjects/CGObjectInstance.h @@ -97,9 +97,9 @@ public: virtual bool isTile2Terrain() const { return false; } - std::optional getAmbientSound() const; - std::optional getVisitSound() const; - std::optional getRemovalSound() const; + std::optional getAmbientSound(vstd::RNG & rng) const; + std::optional getVisitSound(vstd::RNG & rng) const; + std::optional getRemovalSound(vstd::RNG & rng) const; TObjectTypeHandler getObjectHandler() const; @@ -128,8 +128,8 @@ public: /** OVERRIDES OF IObjectInterface **/ - void initObj(CRandomGenerator & rand) override; - void pickRandomObject(CRandomGenerator & rand) override; + void initObj(vstd::RNG & rand) override; + void pickRandomObject(vstd::RNG & rand) override; void onHeroVisit(const CGHeroInstance * h) const override; /// method for synchronous update. Note: For new properties classes should override setPropertyDer instead void setProperty(ObjProperty what, ObjPropertyID identifier) final; diff --git a/lib/mapObjects/CGPandoraBox.cpp b/lib/mapObjects/CGPandoraBox.cpp index b79077948..faa49360b 100644 --- a/lib/mapObjects/CGPandoraBox.cpp +++ b/lib/mapObjects/CGPandoraBox.cpp @@ -41,7 +41,7 @@ void CGPandoraBox::init() } } -void CGPandoraBox::initObj(CRandomGenerator & rand) +void CGPandoraBox::initObj(vstd::RNG & rand) { init(); diff --git a/lib/mapObjects/CGPandoraBox.h b/lib/mapObjects/CGPandoraBox.h index f739f1fb9..a1e5e8394 100644 --- a/lib/mapObjects/CGPandoraBox.h +++ b/lib/mapObjects/CGPandoraBox.h @@ -23,7 +23,7 @@ public: MetaString message; - void initObj(CRandomGenerator & rand) override; + void initObj(vstd::RNG & rand) override; void onHeroVisit(const CGHeroInstance * h) const override; void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override; void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override; diff --git a/lib/mapObjects/CGTownBuilding.cpp b/lib/mapObjects/CGTownBuilding.cpp index e87988df4..483187c0a 100644 --- a/lib/mapObjects/CGTownBuilding.cpp +++ b/lib/mapObjects/CGTownBuilding.cpp @@ -17,6 +17,8 @@ #include "../mapObjects/CGHeroInstance.h" #include "../networkPacks/PacksForClient.h" +#include + VCMI_LIB_NAMESPACE_BEGIN CGTownBuilding::CGTownBuilding(IGameCallback * cb) @@ -314,7 +316,7 @@ CTownRewardableBuilding::CTownRewardableBuilding(IGameCallback *cb) : CGTownBuilding(cb) {} -CTownRewardableBuilding::CTownRewardableBuilding(const BuildingID & index, BuildingSubID::EBuildingSubID subId, CGTownInstance * cgTown, CRandomGenerator & rand) +CTownRewardableBuilding::CTownRewardableBuilding(const BuildingID & index, BuildingSubID::EBuildingSubID subId, CGTownInstance * cgTown, vstd::RNG & rand) : CGTownBuilding(cgTown) { bID = index; @@ -323,7 +325,7 @@ CTownRewardableBuilding::CTownRewardableBuilding(const BuildingID & index, Build initObj(rand); } -void CTownRewardableBuilding::initObj(CRandomGenerator & rand) +void CTownRewardableBuilding::initObj(vstd::RNG & rand) { assert(town && town->town); @@ -340,7 +342,7 @@ void CTownRewardableBuilding::initObj(CRandomGenerator & rand) } } -void CTownRewardableBuilding::newTurn(CRandomGenerator & rand) const +void CTownRewardableBuilding::newTurn(vstd::RNG & rand) const { if (configuration.resetParameters.period != 0 && cb->getDate(Date::DAY) > 1 && ((cb->getDate(Date::DAY)-1) % configuration.resetParameters.period) == 0) { diff --git a/lib/mapObjects/CGTownBuilding.h b/lib/mapObjects/CGTownBuilding.h index 9b620a6ac..3ec7c11b3 100644 --- a/lib/mapObjects/CGTownBuilding.h +++ b/lib/mapObjects/CGTownBuilding.h @@ -123,17 +123,17 @@ public: void setProperty(ObjProperty what, ObjPropertyID identifier) override; void onHeroVisit(const CGHeroInstance * h) const override; - void newTurn(CRandomGenerator & rand) const override; + void newTurn(vstd::RNG & rand) const override; /// gives second part of reward after hero level-ups for proper granting of spells/mana void heroLevelUpDone(const CGHeroInstance *hero) const override; - void initObj(CRandomGenerator & rand) override; + void initObj(vstd::RNG & rand) override; /// applies player selection of reward void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override; - CTownRewardableBuilding(const BuildingID & index, BuildingSubID::EBuildingSubID subId, CGTownInstance * town, CRandomGenerator & rand); + CTownRewardableBuilding(const BuildingID & index, BuildingSubID::EBuildingSubID subId, CGTownInstance * town, vstd::RNG & rand); CTownRewardableBuilding(IGameCallback *cb); template void serialize(Handler &h) diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index f61e84abc..1bdc99b18 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -31,6 +31,8 @@ #include "../networkPacks/PacksForClientBattle.h" #include "../serializer/JsonSerializeFormat.h" +#include + VCMI_LIB_NAMESPACE_BEGIN int CGTownInstance::getSightRadius() const //returns sight distance @@ -379,7 +381,7 @@ bool CGTownInstance::isBonusingBuildingAdded(BuildingID bid) const return present != bonusingBuildings.end(); } -void CGTownInstance::addTownBonuses(CRandomGenerator & rand) +void CGTownInstance::addTownBonuses(vstd::RNG & rand) { for(const auto & kvp : town->buildings) { @@ -461,7 +463,7 @@ void CGTownInstance::deleteTownBonus(BuildingID bid) delete freeIt; } -FactionID CGTownInstance::randomizeFaction(CRandomGenerator & rand) +FactionID CGTownInstance::randomizeFaction(vstd::RNG & rand) { if(getOwner().isValidPlayer()) return cb->gameState()->scenarioOps->getIthPlayersSettings(getOwner()).castle; @@ -479,7 +481,7 @@ FactionID CGTownInstance::randomizeFaction(CRandomGenerator & rand) return *RandomGeneratorUtil::nextItem(potentialPicks, rand); } -void CGTownInstance::pickRandomObject(CRandomGenerator & rand) +void CGTownInstance::pickRandomObject(vstd::RNG & rand) { assert(ID == MapObjectID::TOWN || ID == MapObjectID::RANDOM_TOWN); if (ID == MapObjectID::RANDOM_TOWN) @@ -495,7 +497,7 @@ void CGTownInstance::pickRandomObject(CRandomGenerator & rand) updateAppearance(); } -void CGTownInstance::initObj(CRandomGenerator & rand) ///initialize town structures +void CGTownInstance::initObj(vstd::RNG & rand) ///initialize town structures { blockVisit = true; @@ -521,7 +523,7 @@ void CGTownInstance::initObj(CRandomGenerator & rand) ///initialize town structu updateAppearance(); } -void CGTownInstance::newTurn(CRandomGenerator & rand) const +void CGTownInstance::newTurn(vstd::RNG & rand) const { if (cb->getDate(Date::DAY_OF_WEEK) == 1) //reset on new week { diff --git a/lib/mapObjects/CGTownInstance.h b/lib/mapObjects/CGTownInstance.h index 4eeb5a069..ce0ee5e0a 100644 --- a/lib/mapObjects/CGTownInstance.h +++ b/lib/mapObjects/CGTownInstance.h @@ -201,11 +201,11 @@ public: virtual ~CGTownInstance(); ///IObjectInterface overrides - void newTurn(CRandomGenerator & rand) const override; + void newTurn(vstd::RNG & rand) const override; void onHeroVisit(const CGHeroInstance * h) const override; void onHeroLeave(const CGHeroInstance * h) const override; - void initObj(CRandomGenerator & rand) override; - void pickRandomObject(CRandomGenerator & rand) override; + void initObj(vstd::RNG & rand) override; + void pickRandomObject(vstd::RNG & rand) override; void battleFinished(const CGHeroInstance * hero, const BattleResult & result) const override; std::string getObjectName() const override; @@ -224,14 +224,14 @@ protected: void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override; private: - FactionID randomizeFaction(CRandomGenerator & rand); + FactionID randomizeFaction(vstd::RNG & rand); void setOwner(const PlayerColor & owner) const; void onTownCaptured(const PlayerColor & winner) const; int getDwellingBonus(const std::vector& creatureIds, const std::vector >& dwellings) const; bool townEnvisagesBuilding(BuildingSubID::EBuildingSubID bid) const; bool isBonusingBuildingAdded(BuildingID bid) const; void initOverriddenBids(); - void addTownBonuses(CRandomGenerator & rand); + void addTownBonuses(vstd::RNG & rand); }; VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/CQuest.cpp b/lib/mapObjects/CQuest.cpp index dfb97696b..af7fc5bf3 100644 --- a/lib/mapObjects/CQuest.cpp +++ b/lib/mapObjects/CQuest.cpp @@ -31,7 +31,8 @@ #include "../modding/ModUtility.h" #include "../networkPacks/PacksForClient.h" #include "../spells/CSpellHandler.h" -#include "../CRandomGenerator.h" + +#include VCMI_LIB_NAMESPACE_BEGIN @@ -441,7 +442,7 @@ void CGSeerHut::setObjToKill() } } -void CGSeerHut::init(CRandomGenerator & rand) +void CGSeerHut::init(vstd::RNG & rand) { auto names = VLC->generaltexth->findStringsWithPrefix("core.seerhut.names"); @@ -455,7 +456,7 @@ void CGSeerHut::init(CRandomGenerator & rand) configuration.selectMode = Rewardable::ESelectMode::SELECT_PLAYER; } -void CGSeerHut::initObj(CRandomGenerator & rand) +void CGSeerHut::initObj(vstd::RNG & rand) { init(rand); @@ -562,7 +563,7 @@ void CGSeerHut::setPropertyDer(ObjProperty what, ObjPropertyID identifier) } } -void CGSeerHut::newTurn(CRandomGenerator & rand) const +void CGSeerHut::newTurn(vstd::RNG & rand) const { CRewardableObject::newTurn(rand); if(quest->lastDay >= 0 && quest->lastDay <= cb->getDate() - 1) //time is up @@ -750,7 +751,7 @@ void CGSeerHut::serializeJsonOptions(JsonSerializeFormat & handler) } } -void CGQuestGuard::init(CRandomGenerator & rand) +void CGQuestGuard::init(vstd::RNG & rand) { blockVisit = true; quest->textOption = rand.nextInt(3, 5); @@ -818,7 +819,7 @@ void CGKeymasterTent::onHeroVisit( const CGHeroInstance * h ) const h->showInfoDialog(txt_id); } -void CGBorderGuard::initObj(CRandomGenerator & rand) +void CGBorderGuard::initObj(vstd::RNG & rand) { blockVisit = true; } diff --git a/lib/mapObjects/CQuest.h b/lib/mapObjects/CQuest.h index 22748ecb0..caebee439 100644 --- a/lib/mapObjects/CQuest.h +++ b/lib/mapObjects/CQuest.h @@ -141,19 +141,19 @@ public: std::string seerName; - void initObj(CRandomGenerator & rand) override; + void initObj(vstd::RNG & rand) override; std::string getHoverText(PlayerColor player) const override; std::string getHoverText(const CGHeroInstance * hero) const override; std::string getPopupText(PlayerColor player) const override; std::string getPopupText(const CGHeroInstance * hero) const override; std::vector getPopupComponents(PlayerColor player) const override; std::vector getPopupComponents(const CGHeroInstance * hero) const override; - void newTurn(CRandomGenerator & rand) const override; + void newTurn(vstd::RNG & rand) const override; void onHeroVisit(const CGHeroInstance * h) const override; void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override; void getVisitText (MetaString &text, std::vector &components, bool FirstVisit, const CGHeroInstance * h = nullptr) const override; - virtual void init(CRandomGenerator & rand); + virtual void init(vstd::RNG & rand); int checkDirection() const; //calculates the region of map where monster is placed void setObjToKill(); //remember creatures / heroes to kill after they are initialized const CGHeroInstance *getHeroToKill(bool allowNull) const; @@ -179,7 +179,7 @@ class DLL_LINKAGE CGQuestGuard : public CGSeerHut public: using CGSeerHut::CGSeerHut; - void init(CRandomGenerator & rand) override; + void init(vstd::RNG & rand) override; void onHeroVisit(const CGHeroInstance * h) const override; bool passableFor(PlayerColor color) const override; @@ -227,7 +227,7 @@ class DLL_LINKAGE CGBorderGuard : public CGKeys, public IQuestObject public: using CGKeys::CGKeys; - void initObj(CRandomGenerator & rand) override; + void initObj(vstd::RNG & rand) override; void onHeroVisit(const CGHeroInstance * h) const override; void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override; diff --git a/lib/mapObjects/CRewardableObject.cpp b/lib/mapObjects/CRewardableObject.cpp index ab05a36de..396e93637 100644 --- a/lib/mapObjects/CRewardableObject.cpp +++ b/lib/mapObjects/CRewardableObject.cpp @@ -20,6 +20,8 @@ #include "../networkPacks/PacksForClient.h" #include "../serializer/JsonSerializeFormat.h" +#include + VCMI_LIB_NAMESPACE_BEGIN void CRewardableObject::grantRewardWithMessage(const CGHeroInstance * contextHero, int index, bool markAsVisit) const @@ -387,7 +389,7 @@ void CRewardableObject::setPropertyDer(ObjProperty what, ObjPropertyID identifie } } -void CRewardableObject::newTurn(CRandomGenerator & rand) const +void CRewardableObject::newTurn(vstd::RNG & rand) const { if (configuration.resetParameters.period != 0 && cb->getDate(Date::DAY) > 1 && ((cb->getDate(Date::DAY)-1) % configuration.resetParameters.period) == 0) { @@ -404,7 +406,7 @@ void CRewardableObject::newTurn(CRandomGenerator & rand) const } } -void CRewardableObject::initObj(CRandomGenerator & rand) +void CRewardableObject::initObj(vstd::RNG & rand) { getObjectHandler()->configureObject(this, rand); } diff --git a/lib/mapObjects/CRewardableObject.h b/lib/mapObjects/CRewardableObject.h index 0b0ddf5ff..a3fb7c2e2 100644 --- a/lib/mapObjects/CRewardableObject.h +++ b/lib/mapObjects/CRewardableObject.h @@ -57,7 +57,7 @@ public: void onHeroVisit(const CGHeroInstance *h) const override; ///possibly resets object state - void newTurn(CRandomGenerator & rand) const override; + void newTurn(vstd::RNG & rand) const override; /// gives second part of reward after hero level-ups for proper granting of spells/mana void heroLevelUpDone(const CGHeroInstance *hero) const override; @@ -65,7 +65,7 @@ public: /// applies player selection of reward void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override; - void initObj(CRandomGenerator & rand) override; + void initObj(vstd::RNG & rand) override; void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override; diff --git a/lib/mapObjects/IObjectInterface.cpp b/lib/mapObjects/IObjectInterface.cpp index e7dac1ca0..b3101155d 100644 --- a/lib/mapObjects/IObjectInterface.cpp +++ b/lib/mapObjects/IObjectInterface.cpp @@ -38,13 +38,13 @@ void IObjectInterface::onHeroVisit(const CGHeroInstance * h) const void IObjectInterface::onHeroLeave(const CGHeroInstance * h) const {} -void IObjectInterface::newTurn(CRandomGenerator & rand) const +void IObjectInterface::newTurn(vstd::RNG & rand) const {} -void IObjectInterface::initObj(CRandomGenerator & rand) +void IObjectInterface::initObj(vstd::RNG & rand) {} -void IObjectInterface::pickRandomObject(CRandomGenerator & rand) +void IObjectInterface::pickRandomObject(vstd::RNG & rand) {} void IObjectInterface::setProperty(ObjProperty what, ObjPropertyID identifier) diff --git a/lib/mapObjects/IObjectInterface.h b/lib/mapObjects/IObjectInterface.h index 9a4e365b6..376b72118 100644 --- a/lib/mapObjects/IObjectInterface.h +++ b/lib/mapObjects/IObjectInterface.h @@ -17,11 +17,15 @@ VCMI_LIB_NAMESPACE_BEGIN +namespace vstd +{ +class RNG; +} + struct BattleResult; struct UpgradeInfo; class BoatId; class CGObjectInstance; -class CRandomGenerator; class CStackInstance; class CGHeroInstance; class IGameCallback; @@ -46,9 +50,9 @@ public: virtual void onHeroVisit(const CGHeroInstance * h) const; virtual void onHeroLeave(const CGHeroInstance * h) const; - virtual void newTurn(CRandomGenerator & rand) const; - virtual void initObj(CRandomGenerator & rand); //synchr - virtual void pickRandomObject(CRandomGenerator & rand); + virtual void newTurn(vstd::RNG & rand) const; + virtual void initObj(vstd::RNG & rand); //synchr + virtual void pickRandomObject(vstd::RNG & rand); virtual void setProperty(ObjProperty what, ObjPropertyID identifier);//synchr //Called when queries created DURING HERO VISIT are resolved diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index a7cef0720..0951edce7 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -32,6 +32,8 @@ #include "../networkPacks/PacksForClientBattle.h" #include "../networkPacks/StackLocation.h" +#include + VCMI_LIB_NAMESPACE_BEGIN ///helpers @@ -93,7 +95,7 @@ void CGMine::onHeroVisit( const CGHeroInstance * h ) const } -void CGMine::newTurn(CRandomGenerator & rand) const +void CGMine::newTurn(vstd::RNG & rand) const { if(cb->getDate() == 1) return; @@ -104,7 +106,7 @@ void CGMine::newTurn(CRandomGenerator & rand) const cb->giveResource(tempOwner, producedResource, producedQuantity); } -void CGMine::initObj(CRandomGenerator & rand) +void CGMine::initObj(vstd::RNG & rand) { if(isAbandoned()) { @@ -254,7 +256,7 @@ std::string CGResource::getHoverText(PlayerColor player) const return VLC->generaltexth->restypes[resourceID().getNum()]; } -void CGResource::pickRandomObject(CRandomGenerator & rand) +void CGResource::pickRandomObject(vstd::RNG & rand) { assert(ID == Obj::RESOURCE || ID == Obj::RANDOM_RESOURCE); @@ -269,7 +271,7 @@ void CGResource::pickRandomObject(CRandomGenerator & rand) } } -void CGResource::initObj(CRandomGenerator & rand) +void CGResource::initObj(vstd::RNG & rand) { blockVisit = true; @@ -327,7 +329,7 @@ void CGResource::collectRes(const PlayerColor & player) const sii.text.replaceName(resourceID()); } sii.components.emplace_back(ComponentType::RESOURCE, resourceID(), amount); - sii.soundID = soundBase::pickup01 + CRandomGenerator::getDefault().nextInt(6); + sii.soundID = soundBase::pickup01 + cb->gameState()->getRandomGenerator().nextInt(6); cb->showInfoDialog(&sii); cb->removeObject(this, player); } @@ -395,7 +397,7 @@ ObjectInstanceID CGTeleport::getRandomExit(const CGHeroInstance * h) const { auto passableExits = getPassableExits(cb->gameState(), h, getAllExits(true)); if(!passableExits.empty()) - return *RandomGeneratorUtil::nextItem(passableExits, CRandomGenerator::getDefault()); + return *RandomGeneratorUtil::nextItem(passableExits, cb->gameState()->getRandomGenerator()); return ObjectInstanceID(); } @@ -530,7 +532,7 @@ void CGMonolith::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, cb->moveHero(hero->id, hero->convertFromVisitablePos(dPos), EMovementMode::MONOLITH); } -void CGMonolith::initObj(CRandomGenerator & rand) +void CGMonolith::initObj(vstd::RNG & rand) { std::vector IDs; IDs.push_back(ID); @@ -575,7 +577,7 @@ void CGSubterraneanGate::onHeroVisit( const CGHeroInstance * h ) const cb->showTeleportDialog(&td); } -void CGSubterraneanGate::initObj(CRandomGenerator & rand) +void CGSubterraneanGate::initObj(vstd::RNG & rand) { type = BOTH; } @@ -703,7 +705,7 @@ void CGWhirlpool::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer const auto * obj = cb->getObj(exit); std::set tiles = obj->getBlockedPos(); - dPos = *RandomGeneratorUtil::nextItem(tiles, CRandomGenerator::getDefault()); + dPos = *RandomGeneratorUtil::nextItem(tiles, cb->gameState()->getRandomGenerator())); } cb->moveHero(hero->id, hero->convertFromVisitablePos(dPos), EMovementMode::MONOLITH); @@ -724,7 +726,7 @@ ArtifactID CGArtifact::getArtifact() const return getObjTypeIndex().getNum(); } -void CGArtifact::pickRandomObject(CRandomGenerator & rand) +void CGArtifact::pickRandomObject(vstd::RNG & rand) { switch(ID.toEnum()) { @@ -754,7 +756,7 @@ void CGArtifact::pickRandomObject(CRandomGenerator & rand) ID = MapObjectID::ARTIFACT; } -void CGArtifact::initObj(CRandomGenerator & rand) +void CGArtifact::initObj(vstd::RNG & rand) { blockVisit = true; if(ID == Obj::ARTIFACT) @@ -936,7 +938,7 @@ void CGArtifact::serializeJsonOptions(JsonSerializeFormat& handler) } } -void CGSignBottle::initObj(CRandomGenerator & rand) +void CGSignBottle::initObj(vstd::RNG & rand) { //if no text is set than we pick random from the predefined ones if(message.empty()) @@ -1011,7 +1013,7 @@ void CGGarrison::serializeJsonOptions(JsonSerializeFormat& handler) CArmedInstance::serializeJsonOptions(handler); } -void CGGarrison::initObj(CRandomGenerator &rand) +void CGGarrison::initObj(vstd::RNG &rand) { if(this->subID == MapObjectSubID::decode(this->ID, "antiMagic")) addAntimagicGarrisonBonus(); @@ -1028,7 +1030,7 @@ void CGGarrison::addAntimagicGarrisonBonus() this->addNewBonus(bonus); } -void CGMagi::initObj(CRandomGenerator & rand) +void CGMagi::initObj(vstd::RNG & rand) { if (ID == Obj::EYE_OF_MAGI) blockVisit = true; @@ -1091,7 +1093,7 @@ bool CGBoat::isCoastVisitable() const return true; } -void CGSirens::initObj(CRandomGenerator & rand) +void CGSirens::initObj(vstd::RNG & rand) { blockVisit = true; } @@ -1238,7 +1240,7 @@ void CGObelisk::onHeroVisit( const CGHeroInstance * h ) const } -void CGObelisk::initObj(CRandomGenerator & rand) +void CGObelisk::initObj(vstd::RNG & rand) { cb->gameState()->map->obeliskCount++; } @@ -1291,7 +1293,7 @@ void CGLighthouse::onHeroVisit( const CGHeroInstance * h ) const } } -void CGLighthouse::initObj(CRandomGenerator & rand) +void CGLighthouse::initObj(vstd::RNG & rand) { if(tempOwner.isValidPlayer()) { diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index 173735499..0ab01cb18 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -48,7 +48,7 @@ public: MetaString message; void onHeroVisit(const CGHeroInstance * h) const override; - void initObj(CRandomGenerator & rand) override; + void initObj(vstd::RNG & rand) override; template void serialize(Handler &h) { @@ -66,7 +66,7 @@ public: bool removableUnits; - void initObj(CRandomGenerator &rand) override; + void initObj(vstd::RNG &rand) override; bool passableFor(PlayerColor color) const override; void onHeroVisit(const CGHeroInstance * h) const override; void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override; @@ -99,8 +99,8 @@ public: std::vector getPopupComponents(PlayerColor player) const override; void pick( const CGHeroInstance * h ) const; - void initObj(CRandomGenerator & rand) override; - void pickRandomObject(CRandomGenerator & rand) override; + void initObj(vstd::RNG & rand) override; + void pickRandomObject(vstd::RNG & rand) override; void afterAddToMap(CMap * map) override; BattleField getBattlefield() const override; @@ -129,8 +129,8 @@ public: MetaString message; void onHeroVisit(const CGHeroInstance * h) const override; - void initObj(CRandomGenerator & rand) override; - void pickRandomObject(CRandomGenerator & rand) override; + void initObj(vstd::RNG & rand) override; + void pickRandomObject(vstd::RNG & rand) override; void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override; void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override; std::string getHoverText(PlayerColor player) const override; @@ -166,8 +166,8 @@ private: void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override; void flagMine(const PlayerColor & player) const; - void newTurn(CRandomGenerator & rand) const override; - void initObj(CRandomGenerator & rand) override; + void newTurn(vstd::RNG & rand) const override; + void initObj(vstd::RNG & rand) override; std::string getObjectName() const override; std::string getHoverText(PlayerColor player) const override; @@ -248,7 +248,7 @@ class DLL_LINKAGE CGMonolith : public CGTeleport protected: void onHeroVisit(const CGHeroInstance * h) const override; void teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer, TTeleportExitsList exits) const override; - void initObj(CRandomGenerator & rand) override; + void initObj(vstd::RNG & rand) override; public: using CGTeleport::CGTeleport; @@ -262,7 +262,7 @@ public: class DLL_LINKAGE CGSubterraneanGate : public CGMonolith { void onHeroVisit(const CGHeroInstance * h) const override; - void initObj(CRandomGenerator & rand) override; + void initObj(vstd::RNG & rand) override; public: using CGMonolith::CGMonolith; @@ -297,7 +297,7 @@ public: void onHeroVisit(const CGHeroInstance * h) const override; std::string getHoverText(const CGHeroInstance * hero) const override; - void initObj(CRandomGenerator & rand) override; + void initObj(vstd::RNG & rand) override; template void serialize(Handler &h) { @@ -369,7 +369,7 @@ class DLL_LINKAGE CGMagi : public CGObjectInstance public: using CGObjectInstance::CGObjectInstance; - void initObj(CRandomGenerator & rand) override; + void initObj(vstd::RNG & rand) override; void onHeroVisit(const CGHeroInstance * h) const override; template void serialize(Handler &h) @@ -391,7 +391,7 @@ public: using CTeamVisited::CTeamVisited; void onHeroVisit(const CGHeroInstance * h) const override; - void initObj(CRandomGenerator & rand) override; + void initObj(vstd::RNG & rand) override; std::string getHoverText(PlayerColor player) const override; template void serialize(Handler &h) @@ -408,7 +408,7 @@ public: using CGObjectInstance::CGObjectInstance; void onHeroVisit(const CGHeroInstance * h) const override; - void initObj(CRandomGenerator & rand) override; + void initObj(vstd::RNG & rand) override; template void serialize(Handler &h) { diff --git a/lib/mapping/CDrawRoadsOperation.cpp b/lib/mapping/CDrawRoadsOperation.cpp index 2cf1bbc94..c67684ceb 100644 --- a/lib/mapping/CDrawRoadsOperation.cpp +++ b/lib/mapping/CDrawRoadsOperation.cpp @@ -12,11 +12,12 @@ #include "CDrawRoadsOperation.h" #include "CMap.h" -#include "../CRandomGenerator.h" #include "../RoadHandler.h" #include "../RiverHandler.h" #include "../VCMI_Lib.h" +#include + VCMI_LIB_NAMESPACE_BEGIN const std::vector CDrawLinesOperation::patterns = @@ -155,7 +156,7 @@ static bool ruleIsAny(const std::string & rule) #endif ///CDrawLinesOperation -CDrawLinesOperation::CDrawLinesOperation(CMap * map, CTerrainSelection terrainSel, CRandomGenerator * gen): +CDrawLinesOperation::CDrawLinesOperation(CMap * map, CTerrainSelection terrainSel, vstd::RNG * gen): CMapOperation(map), terrainSel(std::move(terrainSel)), gen(gen) @@ -163,14 +164,14 @@ CDrawLinesOperation::CDrawLinesOperation(CMap * map, CTerrainSelection terrainSe } ///CDrawRoadsOperation -CDrawRoadsOperation::CDrawRoadsOperation(CMap * map, const CTerrainSelection & terrainSel, RoadId roadType, CRandomGenerator * gen): +CDrawRoadsOperation::CDrawRoadsOperation(CMap * map, const CTerrainSelection & terrainSel, RoadId roadType, vstd::RNG * gen): CDrawLinesOperation(map, terrainSel,gen), roadType(roadType) { } ///CDrawRiversOperation -CDrawRiversOperation::CDrawRiversOperation(CMap * map, const CTerrainSelection & terrainSel, RiverId riverType, CRandomGenerator * gen): +CDrawRiversOperation::CDrawRiversOperation(CMap * map, const CTerrainSelection & terrainSel, RiverId riverType, vstd::RNG * gen): CDrawLinesOperation(map, terrainSel, gen), riverType(riverType) { diff --git a/lib/mapping/CDrawRoadsOperation.h b/lib/mapping/CDrawRoadsOperation.h index 16e2ad070..7e9ea754d 100644 --- a/lib/mapping/CDrawRoadsOperation.h +++ b/lib/mapping/CDrawRoadsOperation.h @@ -41,7 +41,7 @@ protected: int flip; }; - CDrawLinesOperation(CMap * map, CTerrainSelection terrainSel, CRandomGenerator * gen); + CDrawLinesOperation(CMap * map, CTerrainSelection terrainSel, vstd::RNG * gen); virtual void executeTile(TerrainTile & tile) = 0; virtual bool canApplyPattern(const CDrawLinesOperation::LinePattern & pattern) const = 0; @@ -58,13 +58,13 @@ protected: ValidationResult validateTile(const LinePattern & pattern, const int3 & pos); CTerrainSelection terrainSel; - CRandomGenerator * gen; + vstd::RNG * gen; }; class CDrawRoadsOperation : public CDrawLinesOperation { public: - CDrawRoadsOperation(CMap * map, const CTerrainSelection & terrainSel, RoadId roadType, CRandomGenerator * gen); + CDrawRoadsOperation(CMap * map, const CTerrainSelection & terrainSel, RoadId roadType, vstd::RNG * gen); std::string getLabel() const override; protected: @@ -81,7 +81,7 @@ private: class CDrawRiversOperation : public CDrawLinesOperation { public: - CDrawRiversOperation(CMap * map, const CTerrainSelection & terrainSel, RiverId roadType, CRandomGenerator * gen); + CDrawRiversOperation(CMap * map, const CTerrainSelection & terrainSel, RiverId roadType, vstd::RNG * gen); std::string getLabel() const override; protected: diff --git a/lib/mapping/CMap.cpp b/lib/mapping/CMap.cpp index cb0f5dbf1..b91e335d3 100644 --- a/lib/mapping/CMap.cpp +++ b/lib/mapping/CMap.cpp @@ -29,6 +29,8 @@ #include "CMapOperation.h" #include "../serializer/JsonSerializeFormat.h" +#include + VCMI_LIB_NAMESPACE_BEGIN void Rumor::serializeJson(JsonSerializeFormat & handler) diff --git a/lib/mapping/CMapEditManager.cpp b/lib/mapping/CMapEditManager.cpp index f97b11125..27866b0ae 100644 --- a/lib/mapping/CMapEditManager.cpp +++ b/lib/mapping/CMapEditManager.cpp @@ -15,6 +15,8 @@ #include "CDrawRoadsOperation.h" #include "CMapOperation.h" +#include + VCMI_LIB_NAMESPACE_BEGIN CMapUndoManager::CMapUndoManager() : @@ -121,26 +123,26 @@ CMap * CMapEditManager::getMap() return map; } -void CMapEditManager::clearTerrain(CRandomGenerator * gen) +void CMapEditManager::clearTerrain(vstd::RNG * gen) { - execute(std::make_unique(map, gen ? gen : &(this->gen))); + execute(std::make_unique(map, gen ? gen : this->gen.get())); } -void CMapEditManager::drawTerrain(TerrainId terType, int decorationsPercentage, CRandomGenerator * gen) +void CMapEditManager::drawTerrain(TerrainId terType, int decorationsPercentage, vstd::RNG * gen) { - execute(std::make_unique(map, terrainSel, terType, decorationsPercentage, gen ? gen : &(this->gen))); + execute(std::make_unique(map, terrainSel, terType, decorationsPercentage, gen ? gen : this->gen.get())); terrainSel.clearSelection(); } -void CMapEditManager::drawRoad(RoadId roadType, CRandomGenerator* gen) +void CMapEditManager::drawRoad(RoadId roadType, vstd::RNG* gen) { - execute(std::make_unique(map, terrainSel, roadType, gen ? gen : &(this->gen))); + execute(std::make_unique(map, terrainSel, roadType, gen ? gen : this->gen.get())); terrainSel.clearSelection(); } -void CMapEditManager::drawRiver(RiverId riverType, CRandomGenerator* gen) +void CMapEditManager::drawRiver(RiverId riverType, vstd::RNG* gen) { - execute(std::make_unique(map, terrainSel, riverType, gen ? gen : &(this->gen))); + execute(std::make_unique(map, terrainSel, riverType, gen ? gen : this->gen.get())); terrainSel.clearSelection(); } diff --git a/lib/mapping/CMapEditManager.h b/lib/mapping/CMapEditManager.h index e380db826..812196efa 100644 --- a/lib/mapping/CMapEditManager.h +++ b/lib/mapping/CMapEditManager.h @@ -11,13 +11,17 @@ #pragma once #include "../GameConstants.h" -#include "../CRandomGenerator.h" #include "MapEditUtils.h" VCMI_LIB_NAMESPACE_BEGIN class CMapOperation; +namespace vstd +{ +class RNG; +} + /// The CMapUndoManager provides the functionality to save operations and undo/redo them. class DLL_LINKAGE CMapUndoManager : boost::noncopyable { @@ -67,16 +71,16 @@ public: CMap * getMap(); /// Clears the terrain. The free level is filled with water and the underground level with rock. - void clearTerrain(CRandomGenerator * gen = nullptr); + void clearTerrain(vstd::RNG * gen = nullptr); /// Draws terrain at the current terrain selection. The selection will be cleared automatically. - void drawTerrain(TerrainId terType, int decorationsPercentage, CRandomGenerator * gen = nullptr); + void drawTerrain(TerrainId terType, int decorationsPercentage, vstd::RNG * gen = nullptr); /// Draws roads at the current terrain selection. The selection will be cleared automatically. - void drawRoad(RoadId roadType, CRandomGenerator * gen = nullptr); + void drawRoad(RoadId roadType, vstd::RNG * gen = nullptr); /// Draws rivers at the current terrain selection. The selection will be cleared automatically. - void drawRiver(RiverId riverType, CRandomGenerator * gen = nullptr); + void drawRiver(RiverId riverType, vstd::RNG * gen = nullptr); void insertObject(CGObjectInstance * obj); void insertObjects(std::set & objects); @@ -94,7 +98,7 @@ private: CMap * map; CMapUndoManager undoManager; - CRandomGenerator gen; + std::unique_ptr gen; CTerrainSelection terrainSel; CObjectSelection objectSel; }; diff --git a/lib/mapping/CMapOperation.cpp b/lib/mapping/CMapOperation.cpp index 7c8e76c7c..7aaf268fc 100644 --- a/lib/mapping/CMapOperation.cpp +++ b/lib/mapping/CMapOperation.cpp @@ -12,12 +12,13 @@ #include "CMapOperation.h" #include "../VCMI_Lib.h" -#include "../CRandomGenerator.h" #include "../TerrainHandler.h" #include "../mapObjects/CGObjectInstance.h" #include "CMap.h" #include "MapEditUtils.h" +#include + VCMI_LIB_NAMESPACE_BEGIN CMapOperation::CMapOperation(CMap* map) : map(map) @@ -87,7 +88,7 @@ void CComposedOperation::addOperation(std::unique_ptr&& operation operations.push_back(std::move(operation)); } -CDrawTerrainOperation::CDrawTerrainOperation(CMap * map, CTerrainSelection terrainSel, TerrainId terType, int decorationsPercentage, CRandomGenerator * gen): +CDrawTerrainOperation::CDrawTerrainOperation(CMap * map, CTerrainSelection terrainSel, TerrainId terType, int decorationsPercentage, vstd::RNG * gen): CMapOperation(map), terrainSel(std::move(terrainSel)), terType(terType), @@ -560,7 +561,7 @@ CDrawTerrainOperation::ValidationResult::ValidationResult(bool result, std::stri } -CClearTerrainOperation::CClearTerrainOperation(CMap* map, CRandomGenerator* gen) : CComposedOperation(map) +CClearTerrainOperation::CClearTerrainOperation(CMap* map, vstd::RNG* gen) : CComposedOperation(map) { CTerrainSelection terrainSel(map); terrainSel.selectRange(MapRect(int3(0, 0, 0), map->width, map->height)); diff --git a/lib/mapping/CMapOperation.h b/lib/mapping/CMapOperation.h index a9c36fe27..b081c8b58 100644 --- a/lib/mapping/CMapOperation.h +++ b/lib/mapping/CMapOperation.h @@ -17,7 +17,11 @@ VCMI_LIB_NAMESPACE_BEGIN class CGObjectInstance; class CMap; -class CRandomGenerator; + +namespace vstd +{ +class RNG; +} /// The abstract base class CMapOperation defines an operation that can be executed, undone and redone. class DLL_LINKAGE CMapOperation : public boost::noncopyable @@ -63,7 +67,7 @@ private: class CDrawTerrainOperation : public CMapOperation { public: - CDrawTerrainOperation(CMap * map, CTerrainSelection terrainSel, TerrainId terType, int decorationsPercentage, CRandomGenerator * gen); + CDrawTerrainOperation(CMap * map, CTerrainSelection terrainSel, TerrainId terType, int decorationsPercentage, vstd::RNG * gen); void execute() override; void undo() override; @@ -103,7 +107,7 @@ private: CTerrainSelection terrainSel; TerrainId terType; int decorationsPercentage; - CRandomGenerator* gen; + vstd::RNG* gen; std::set invalidatedTerViews; }; @@ -111,7 +115,7 @@ private: class CClearTerrainOperation : public CComposedOperation { public: - CClearTerrainOperation(CMap * map, CRandomGenerator * gen); + CClearTerrainOperation(CMap * map, vstd::RNG * gen); std::string getLabel() const override; }; diff --git a/lib/mapping/ObstacleProxy.cpp b/lib/mapping/ObstacleProxy.cpp index e1f9fe024..10a22d508 100644 --- a/lib/mapping/ObstacleProxy.cpp +++ b/lib/mapping/ObstacleProxy.cpp @@ -18,6 +18,8 @@ #include "../mapObjects/ObstacleSetHandler.h" #include "../VCMI_Lib.h" +#include + VCMI_LIB_NAMESPACE_BEGIN void ObstacleProxy::collectPossibleObstacles(TerrainId terrain) @@ -53,7 +55,7 @@ void ObstacleProxy::sortObstacles() }); } -bool ObstacleProxy::prepareBiome(const ObstacleSetFilter & filter, CRandomGenerator & rand) +bool ObstacleProxy::prepareBiome(const ObstacleSetFilter & filter, vstd::RNG & rand) { possibleObstacles.clear(); @@ -228,7 +230,7 @@ bool ObstacleProxy::isProhibited(const rmg::Area& objArea) const return false; }; -int ObstacleProxy::getWeightedObjects(const int3 & tile, CRandomGenerator & rand, IGameCallback * cb, std::list & allObjects, std::vector> & weightedObjects) +int ObstacleProxy::getWeightedObjects(const int3 & tile, vstd::RNG & rand, IGameCallback * cb, std::list & allObjects, std::vector> & weightedObjects) { int maxWeight = std::numeric_limits::min(); for(auto & possibleObstacle : possibleObstacles) @@ -309,7 +311,7 @@ int ObstacleProxy::getWeightedObjects(const int3 & tile, CRandomGenerator & rand return maxWeight; } -std::set ObstacleProxy::createObstacles(CRandomGenerator & rand, IGameCallback * cb) +std::set ObstacleProxy::createObstacles(vstd::RNG & rand, IGameCallback * cb) { //reverse order, since obstacles begin in bottom-right corner, while the map coordinates begin in top-left auto blockedTiles = blockedArea.getTilesVector(); @@ -382,7 +384,7 @@ bool EditorObstaclePlacer::isInTheMap(const int3& tile) return map->isInTheMap(tile); } -std::set EditorObstaclePlacer::placeObstacles(CRandomGenerator & rand) +std::set EditorObstaclePlacer::placeObstacles(vstd::RNG & rand) { auto obstacles = createObstacles(rand, map->cb); finalInsertion(map->getEditManager(), obstacles); diff --git a/lib/mapping/ObstacleProxy.h b/lib/mapping/ObstacleProxy.h index 1bf4e4b72..b3dd2f35e 100644 --- a/lib/mapping/ObstacleProxy.h +++ b/lib/mapping/ObstacleProxy.h @@ -18,7 +18,6 @@ VCMI_LIB_NAMESPACE_BEGIN class CMapEditManager; class CGObjectInstance; class ObjectTemplate; -class CRandomGenerator; class IGameCallback; class ObstacleSetFilter; @@ -30,7 +29,7 @@ public: virtual ~ObstacleProxy() = default; void collectPossibleObstacles(TerrainId terrain); - bool prepareBiome(const ObstacleSetFilter & filter, CRandomGenerator & rand); + bool prepareBiome(const ObstacleSetFilter & filter, vstd::RNG & rand); void addBlockedTile(const int3 & tile); @@ -44,7 +43,7 @@ public: virtual void placeObject(rmg::Object & object, std::set & instances); - virtual std::set createObstacles(CRandomGenerator & rand, IGameCallback * cb); + virtual std::set createObstacles(vstd::RNG & rand, IGameCallback * cb); virtual bool isInTheMap(const int3& tile) = 0; @@ -53,7 +52,7 @@ public: virtual void postProcess(const rmg::Object& object) {}; protected: - int getWeightedObjects(const int3& tile, CRandomGenerator& rand, IGameCallback * cb, std::list& allObjects, std::vector>& weightedObjects); + int getWeightedObjects(const int3& tile, vstd::RNG& rand, IGameCallback * cb, std::list& allObjects, std::vector>& weightedObjects); void sortObstacles(); rmg::Area blockedArea; @@ -71,7 +70,7 @@ public: bool isInTheMap(const int3& tile) override; - std::set placeObstacles(CRandomGenerator& rand); + std::set placeObstacles(vstd::RNG& rand); private: CMap* map; diff --git a/lib/registerTypes/RegisterTypesLobbyPacks.h b/lib/registerTypes/RegisterTypesLobbyPacks.h index 6859f5d9f..c70c74eca 100644 --- a/lib/registerTypes/RegisterTypesLobbyPacks.h +++ b/lib/registerTypes/RegisterTypesLobbyPacks.h @@ -17,6 +17,7 @@ #include "../gameState/TavernHeroesPool.h" #include "../gameState/CGameStateCampaign.h" #include "../mapping/CMap.h" +#include "../CRandomGenerator.h" #include "../TerrainHandler.h" #include "../RiverHandler.h" #include "../RoadHandler.h" diff --git a/lib/rewardable/Info.cpp b/lib/rewardable/Info.cpp index 00323daf9..4e5a40d2a 100644 --- a/lib/rewardable/Info.cpp +++ b/lib/rewardable/Info.cpp @@ -20,7 +20,8 @@ #include "../json/JsonRandom.h" #include "../mapObjects/IObjectInterface.h" #include "../modding/IdentifierStorage.h" -#include "../CRandomGenerator.h" + +#include VCMI_LIB_NAMESPACE_BEGIN @@ -106,7 +107,7 @@ void Rewardable::Info::init(const JsonNode & objectConfig, const std::string & o loadString(parameters["onEmptyMessage"], TextIdentifier(objectName, "onEmpty")); } -Rewardable::LimitersList Rewardable::Info::configureSublimiters(Rewardable::Configuration & object, CRandomGenerator & rng, IGameCallback * cb, const JsonNode & source) const +Rewardable::LimitersList Rewardable::Info::configureSublimiters(Rewardable::Configuration & object, vstd::RNG & rng, IGameCallback * cb, const JsonNode & source) const { Rewardable::LimitersList result; for (const auto & input : source.Vector()) @@ -121,7 +122,7 @@ Rewardable::LimitersList Rewardable::Info::configureSublimiters(Rewardable::Conf return result; } -void Rewardable::Info::configureLimiter(Rewardable::Configuration & object, CRandomGenerator & rng, IGameCallback * cb, Rewardable::Limiter & limiter, const JsonNode & source) const +void Rewardable::Info::configureLimiter(Rewardable::Configuration & object, vstd::RNG & rng, IGameCallback * cb, Rewardable::Limiter & limiter, const JsonNode & source) const { auto const & variables = object.variables.values; JsonRandom randomizer(cb); @@ -153,7 +154,7 @@ void Rewardable::Info::configureLimiter(Rewardable::Configuration & object, CRan limiter.noneOf = configureSublimiters(object, rng, cb, source["noneOf"] ); } -void Rewardable::Info::configureReward(Rewardable::Configuration & object, CRandomGenerator & rng, IGameCallback * cb, Rewardable::Reward & reward, const JsonNode & source) const +void Rewardable::Info::configureReward(Rewardable::Configuration & object, vstd::RNG & rng, IGameCallback * cb, Rewardable::Reward & reward, const JsonNode & source) const { auto const & variables = object.variables.values; JsonRandom randomizer(cb); @@ -210,14 +211,14 @@ void Rewardable::Info::configureReward(Rewardable::Configuration & object, CRand } } -void Rewardable::Info::configureResetInfo(Rewardable::Configuration & object, CRandomGenerator & rng, Rewardable::ResetInfo & resetParameters, const JsonNode & source) const +void Rewardable::Info::configureResetInfo(Rewardable::Configuration & object, vstd::RNG & rng, Rewardable::ResetInfo & resetParameters, const JsonNode & source) const { resetParameters.period = static_cast(source["period"].Float()); resetParameters.visitors = source["visitors"].Bool(); resetParameters.rewards = source["rewards"].Bool(); } -void Rewardable::Info::configureVariables(Rewardable::Configuration & object, CRandomGenerator & rng, IGameCallback * cb, const JsonNode & source) const +void Rewardable::Info::configureVariables(Rewardable::Configuration & object, vstd::RNG & rng, IGameCallback * cb, const JsonNode & source) const { JsonRandom randomizer(cb); @@ -277,7 +278,7 @@ void Rewardable::Info::replaceTextPlaceholders(MetaString & target, const Variab void Rewardable::Info::configureRewards( Rewardable::Configuration & object, - CRandomGenerator & rng, + vstd::RNG & rng, IGameCallback * cb, const JsonNode & source, Rewardable::EEventType event, @@ -335,7 +336,7 @@ void Rewardable::Info::configureRewards( } } -void Rewardable::Info::configureObject(Rewardable::Configuration & object, CRandomGenerator & rng, IGameCallback * cb) const +void Rewardable::Info::configureObject(Rewardable::Configuration & object, vstd::RNG & rng, IGameCallback * cb) const { object.info.clear(); diff --git a/lib/rewardable/Info.h b/lib/rewardable/Info.h index e2bb4322f..bad4f5031 100644 --- a/lib/rewardable/Info.h +++ b/lib/rewardable/Info.h @@ -15,13 +15,16 @@ VCMI_LIB_NAMESPACE_BEGIN -class CRandomGenerator; +namespace vstd +{ +class RNG; +} + class MetaString; class IGameCallback; namespace Rewardable { - struct Limiter; using LimitersList = std::vector>; struct Reward; @@ -39,14 +42,14 @@ class DLL_LINKAGE Info : public IObjectInfo void replaceTextPlaceholders(MetaString & target, const Variables & variables) const; void replaceTextPlaceholders(MetaString & target, const Variables & variables, const VisitInfo & info) const; - void configureVariables(Rewardable::Configuration & object, CRandomGenerator & rng, IGameCallback * cb, const JsonNode & source) const; - void configureRewards(Rewardable::Configuration & object, CRandomGenerator & rng, IGameCallback * cb, const JsonNode & source, Rewardable::EEventType mode, const std::string & textPrefix) const; + void configureVariables(Rewardable::Configuration & object, vstd::RNG & rng, IGameCallback * cb, const JsonNode & source) const; + void configureRewards(Rewardable::Configuration & object, vstd::RNG & rng, IGameCallback * cb, const JsonNode & source, Rewardable::EEventType mode, const std::string & textPrefix) const; - void configureLimiter(Rewardable::Configuration & object, CRandomGenerator & rng, IGameCallback * cb, Rewardable::Limiter & limiter, const JsonNode & source) const; - Rewardable::LimitersList configureSublimiters(Rewardable::Configuration & object, CRandomGenerator & rng, IGameCallback * cb, const JsonNode & source) const; + void configureLimiter(Rewardable::Configuration & object, vstd::RNG & rng, IGameCallback * cb, Rewardable::Limiter & limiter, const JsonNode & source) const; + Rewardable::LimitersList configureSublimiters(Rewardable::Configuration & object, vstd::RNG & rng, IGameCallback * cb, const JsonNode & source) const; - void configureReward(Rewardable::Configuration & object, CRandomGenerator & rng, IGameCallback * cb, Rewardable::Reward & info, const JsonNode & source) const; - void configureResetInfo(Rewardable::Configuration & object, CRandomGenerator & rng, Rewardable::ResetInfo & info, const JsonNode & source) const; + void configureReward(Rewardable::Configuration & object, vstd::RNG & rng, IGameCallback * cb, Rewardable::Reward & info, const JsonNode & source) const; + void configureResetInfo(Rewardable::Configuration & object, vstd::RNG & rng, Rewardable::ResetInfo & info, const JsonNode & source) const; public: const JsonNode & getParameters() const; @@ -65,7 +68,7 @@ public: bool givesBonuses() const override; - void configureObject(Rewardable::Configuration & object, CRandomGenerator & rng, IGameCallback * cb) const; + void configureObject(Rewardable::Configuration & object, vstd::RNG & rng, IGameCallback * cb) const; void init(const JsonNode & objectConfig, const std::string & objectTextID); diff --git a/lib/rmg/CMapGenOptions.cpp b/lib/rmg/CMapGenOptions.cpp index 622bbdc3f..a12178d3b 100644 --- a/lib/rmg/CMapGenOptions.cpp +++ b/lib/rmg/CMapGenOptions.cpp @@ -14,11 +14,12 @@ #include "../mapping/CMapHeader.h" #include "CRmgTemplateStorage.h" #include "CRmgTemplate.h" -#include "CRandomGenerator.h" #include "../VCMI_Lib.h" #include "../CTownHandler.h" #include "serializer/JsonSerializeFormat.h" +#include + VCMI_LIB_NAMESPACE_BEGIN CMapGenOptions::CMapGenOptions() @@ -487,7 +488,7 @@ void CMapGenOptions::setPlayerTeam(const PlayerColor & color, const TeamID & tea customizedPlayers = true; } -void CMapGenOptions::finalize(CRandomGenerator & rand) +void CMapGenOptions::finalize(vstd::RNG & rand) { logGlobal->info("RMG map: %dx%d, %s underground", getWidth(), getHeight(), getHasTwoLevels() ? "WITH" : "NO"); logGlobal->info("RMG settings: players %d, teams %d, computer players %d, computer teams %d, water %d, monsters %d", @@ -690,8 +691,7 @@ bool CMapGenOptions::checkOptions() const } else { - CRandomGenerator gen; - return getPossibleTemplate(gen) != nullptr; + return !getPossibleTemplates().empty(); } } @@ -750,7 +750,7 @@ std::vector CMapGenOptions::getPossibleTemplates() const return templates; } -const CRmgTemplate * CMapGenOptions::getPossibleTemplate(CRandomGenerator & rand) const +const CRmgTemplate * CMapGenOptions::getPossibleTemplate(vstd::RNG & rand) const { auto templates = getPossibleTemplates(); diff --git a/lib/rmg/CMapGenOptions.h b/lib/rmg/CMapGenOptions.h index b4c613b56..d82083553 100644 --- a/lib/rmg/CMapGenOptions.h +++ b/lib/rmg/CMapGenOptions.h @@ -16,7 +16,10 @@ VCMI_LIB_NAMESPACE_BEGIN -class CRandomGenerator; +namespace vstd +{ +class RNG; +} enum class EPlayerType { @@ -148,7 +151,7 @@ public: /// Finalizes the options. All random sizes for various properties will be overwritten by numbers from /// a random number generator by keeping the options in a valid state. Check options should return true, otherwise /// this function fails. - void finalize(CRandomGenerator & rand); + void finalize(vstd::RNG & rand); /// Returns false if there is no template available which fits to the currently selected options. bool checkOptions() const; @@ -166,7 +169,7 @@ private: PlayerColor getNextPlayerColor() const; void updateCompOnlyPlayers(); void updatePlayers(); - const CRmgTemplate * getPossibleTemplate(CRandomGenerator & rand) const; + const CRmgTemplate * getPossibleTemplate(vstd::RNG & rand) const; si32 width; si32 height; diff --git a/lib/rmg/CMapGenerator.cpp b/lib/rmg/CMapGenerator.cpp index d7d41aa2e..9807d60d6 100644 --- a/lib/rmg/CMapGenerator.cpp +++ b/lib/rmg/CMapGenerator.cpp @@ -14,6 +14,7 @@ #include "../mapping/MapFormat.h" #include "../VCMI_Lib.h" #include "../CGeneralTextHandler.h" +#include "../CRandomGenerator.h" #include "../mapObjectConstructors/AObjectTypeHandler.h" #include "../mapObjectConstructors/CObjectClassesHandler.h" #include "../mapping/CMapEditManager.h" @@ -32,15 +33,17 @@ #include "modificators/TreasurePlacer.h" #include "modificators/RoadPlacer.h" +#include + VCMI_LIB_NAMESPACE_BEGIN CMapGenerator::CMapGenerator(CMapGenOptions& mapGenOptions, IGameCallback * cb, int RandomSeed) : mapGenOptions(mapGenOptions), randomSeed(RandomSeed), - monolithIndex(0) + monolithIndex(0), + rand(std::make_unique(RandomSeed)) { loadConfig(); - rand.setSeed(this->randomSeed); - mapGenOptions.finalize(rand); + mapGenOptions.finalize(*rand); map = std::make_unique(mapGenOptions, cb); placer = std::make_shared(*map); } @@ -116,7 +119,7 @@ std::unique_ptr CMapGenerator::generate() try { addHeaderInfo(); - map->initTiles(*this, rand); + map->initTiles(*this, *rand); Load::Progress::step(); initQuestArtsRemaining(); genZones(); @@ -286,7 +289,7 @@ void CMapGenerator::addPlayerInfo() logGlobal->error("Not enough places in team for %s player", ((j == CPUONLY) ? "CPU" : "CPU or human")); assert (teamNumbers[j].size()); } - auto itTeam = RandomGeneratorUtil::nextItem(teamNumbers[j], rand); + auto itTeam = RandomGeneratorUtil::nextItem(teamNumbers[j], *rand); player.team = TeamID(*itTeam); teamNumbers[j].erase(itTeam); } @@ -306,8 +309,8 @@ void CMapGenerator::addPlayerInfo() void CMapGenerator::genZones() { - placer->placeZones(&rand); - placer->assignZones(&rand); + placer->placeZones(rand.get()); + placer->assignZones(rand.get()); logGlobal->info("Zones generated successfully"); } @@ -428,9 +431,9 @@ void CMapGenerator::fillZones() if (it.second->getType() != ETemplateZoneType::WATER) treasureZones.push_back(it.second); } - auto grailZone = *RandomGeneratorUtil::nextItem(treasureZones, rand); + auto grailZone = *RandomGeneratorUtil::nextItem(treasureZones, *rand); - map->getMap(this).grailPos = *RandomGeneratorUtil::nextItem(grailZone->freePaths()->getTiles(), rand); + map->getMap(this).grailPos = *RandomGeneratorUtil::nextItem(grailZone->freePaths()->getTiles(), *rand); map->getMap(this).reindexObjects(); logGlobal->info("Zones filled successfully"); diff --git a/lib/rmg/CMapGenerator.h b/lib/rmg/CMapGenerator.h index e500016fa..c1971bdba 100644 --- a/lib/rmg/CMapGenerator.h +++ b/lib/rmg/CMapGenerator.h @@ -11,7 +11,6 @@ #pragma once #include "../GameConstants.h" -#include "../CRandomGenerator.h" #include "CMapGenOptions.h" #include "../int3.h" #include "CRmgTemplate.h" @@ -79,7 +78,7 @@ public: int getRandomSeed() const; private: - CRandomGenerator rand; + std::unique_ptr rand; int randomSeed; CMapGenOptions& mapGenOptions; Config config; diff --git a/lib/rmg/CZonePlacer.cpp b/lib/rmg/CZonePlacer.cpp index e2af8582a..645a87276 100644 --- a/lib/rmg/CZonePlacer.cpp +++ b/lib/rmg/CZonePlacer.cpp @@ -11,7 +11,6 @@ #include "StdInc.h" #include "CZonePlacer.h" -#include "../CRandomGenerator.h" #include "../CTownHandler.h" #include "../TerrainHandler.h" #include "../mapping/CMap.h" @@ -23,12 +22,12 @@ #include "Functions.h" #include "PenroseTiling.h" +#include + VCMI_LIB_NAMESPACE_BEGIN //#define ZONE_PLACEMENT_LOG true -class CRandomGenerator; - CZonePlacer::CZonePlacer(RmgMap & map) : width(0), height(0), mapSize(0), gravityConstant(1e-3f), @@ -97,7 +96,7 @@ void CZonePlacer::findPathsBetweenZones() } } -void CZonePlacer::placeOnGrid(CRandomGenerator* rand) +void CZonePlacer::placeOnGrid(vstd::RNG* rand) { auto zones = map.getZones(); assert(zones.size()); @@ -118,7 +117,7 @@ void CZonePlacer::placeOnGrid(CRandomGenerator* rand) auto getRandomEdge = [rand, gridSize](size_t& x, size_t& y) { - switch (rand->nextInt() % 4) + switch (rand->nextInt(0, 3) % 4) { case 0: x = 0; @@ -150,7 +149,7 @@ void CZonePlacer::placeOnGrid(CRandomGenerator* rand) else { //Random corner - if (rand->nextInt() % 2) + if (rand->nextInt(0, 1) == 1) { x = 0; } @@ -158,7 +157,7 @@ void CZonePlacer::placeOnGrid(CRandomGenerator* rand) { x = gridSize - 1; } - if (rand->nextInt() % 2) + if (rand->nextInt(0, 1) == 1) { y = 0; } @@ -176,8 +175,8 @@ void CZonePlacer::placeOnGrid(CRandomGenerator* rand) else { //One of 4 squares in the middle - x = (gridSize / 2) - 1 + rand->nextInt() % 2; - y = (gridSize / 2) - 1 + rand->nextInt() % 2; + x = (gridSize / 2) - 1 + rand->nextInt(0, 1); + y = (gridSize / 2) - 1 + rand->nextInt(0, 1); } break; case ETemplateZoneType::JUNCTION: @@ -308,7 +307,7 @@ float CZonePlacer::scaleForceBetweenZones(const std::shared_ptr zoneA, con } } -void CZonePlacer::placeZones(CRandomGenerator * rand) +void CZonePlacer::placeZones(vstd::RNG * rand) { logGlobal->info("Starting zone placement"); @@ -432,7 +431,7 @@ void CZonePlacer::placeZones(CRandomGenerator * rand) } } -void CZonePlacer::prepareZones(TZoneMap &zones, TZoneVector &zonesVector, const bool underground, CRandomGenerator * rand) +void CZonePlacer::prepareZones(TZoneMap &zones, TZoneVector &zonesVector, const bool underground, vstd::RNG * rand) { std::vector totalSize = { 0, 0 }; //make sure that sum of zone sizes on surface and uderground match size of the map @@ -824,7 +823,7 @@ float CZonePlacer::metric (const int3 &A, const int3 &B) const } -void CZonePlacer::assignZones(CRandomGenerator * rand) +void CZonePlacer::assignZones(vstd::RNG * rand) { logGlobal->info("Starting zone colouring"); diff --git a/lib/rmg/CZonePlacer.h b/lib/rmg/CZonePlacer.h index 414f79b3c..43e3479df 100644 --- a/lib/rmg/CZonePlacer.h +++ b/lib/rmg/CZonePlacer.h @@ -16,9 +16,13 @@ VCMI_LIB_NAMESPACE_BEGIN +namespace vstd +{ +class RNG; +} + class CZoneGraph; class CMap; -class CRandomGenerator; class RmgMap; class Zone; @@ -37,16 +41,16 @@ public: float getDistance(float distance) const; //additional scaling without 0 division ~CZonePlacer() = default; - void placeZones(CRandomGenerator * rand); + void placeZones(vstd::RNG * rand); void findPathsBetweenZones(); - void placeOnGrid(CRandomGenerator* rand); + void placeOnGrid(vstd::RNG* rand); float scaleForceBetweenZones(const std::shared_ptr zoneA, const std::shared_ptr zoneB) const; - void assignZones(CRandomGenerator * rand); + void assignZones(vstd::RNG * rand); const TDistanceMap & getDistanceMap(); private: - void prepareZones(TZoneMap &zones, TZoneVector &zonesVector, const bool underground, CRandomGenerator * rand); + void prepareZones(TZoneMap &zones, TZoneVector &zonesVector, const bool underground, vstd::RNG * rand); void attractConnectedZones(TZoneMap & zones, TForceVector & forces, TDistanceVector & distances) const; void separateOverlappingZones(TZoneMap &zones, TForceVector &forces, TDistanceVector &overlaps); void moveOneZone(TZoneMap & zones, TForceVector & totalForces, TDistanceVector & distances, TDistanceVector & overlaps); diff --git a/lib/rmg/Functions.cpp b/lib/rmg/Functions.cpp index 4147aafb6..208877109 100644 --- a/lib/rmg/Functions.cpp +++ b/lib/rmg/Functions.cpp @@ -21,6 +21,8 @@ #include "../mapObjectConstructors/CObjectClassesHandler.h" #include "../VCMI_Lib.h" +#include + VCMI_LIB_NAMESPACE_BEGIN rmg::Tileset collectDistantTiles(const Zone& zone, int distance) @@ -34,7 +36,7 @@ rmg::Tileset collectDistantTiles(const Zone& zone, int distance) return subarea.getTiles(); } -int chooseRandomAppearance(CRandomGenerator & generator, si32 ObjID, TerrainId terrain) +int chooseRandomAppearance(vstd::RNG & generator, si32 ObjID, TerrainId terrain) { auto factories = VLC->objtypeh->knownSubObjects(ObjID); vstd::erase_if(factories, [ObjID, &terrain](si32 f) diff --git a/lib/rmg/Functions.h b/lib/rmg/Functions.h index 0bcaee0d7..2756d0897 100644 --- a/lib/rmg/Functions.h +++ b/lib/rmg/Functions.h @@ -19,7 +19,6 @@ class RmgMap; class ObjectManager; class ObjectTemplate; class CMapGenerator; -class CRandomGenerator; class rmgException : public std::exception { @@ -37,7 +36,7 @@ public: rmg::Tileset collectDistantTiles(const Zone & zone, int distance); -int chooseRandomAppearance(CRandomGenerator & generator, si32 ObjID, TerrainId terrain); +int chooseRandomAppearance(vstd::RNG & generator, si32 ObjID, TerrainId terrain); VCMI_LIB_NAMESPACE_END diff --git a/lib/rmg/PenroseTiling.cpp b/lib/rmg/PenroseTiling.cpp index 8f4091cd2..9df43f866 100644 --- a/lib/rmg/PenroseTiling.cpp +++ b/lib/rmg/PenroseTiling.cpp @@ -13,6 +13,8 @@ #include "StdInc.h" #include "PenroseTiling.h" +#include + VCMI_LIB_NAMESPACE_BEGIN @@ -143,7 +145,7 @@ void PenroseTiling::split(Triangle& p, std::vector& points, return; } -std::set PenroseTiling::generatePenroseTiling(size_t numZones, CRandomGenerator * rand) +std::set PenroseTiling::generatePenroseTiling(size_t numZones, vstd::RNG * rand) { float scale = 173.2f / (numZones * 1.5f + 20); float polyAngle = (2 * PI_CONSTANT) / POLY; diff --git a/lib/rmg/PenroseTiling.h b/lib/rmg/PenroseTiling.h index 8b6ace8ed..18a8a3f40 100644 --- a/lib/rmg/PenroseTiling.h +++ b/lib/rmg/PenroseTiling.h @@ -11,12 +11,16 @@ #pragma once #include "../GameConstants.h" -#include "../CRandomGenerator.h" #include #include VCMI_LIB_NAMESPACE_BEGIN +namespace vstd +{ +class RNG; +} + using namespace boost::geometry; typedef std::array TIndices; @@ -66,11 +70,11 @@ public: const bool P2 = false; // Tiling type - std::set generatePenroseTiling(size_t numZones, CRandomGenerator * rand); + std::set generatePenroseTiling(size_t numZones, vstd::RNG * rand); private: void split(Triangle& p, std::vector& points, std::array, 5>& indices, uint32_t depth); }; -VCMI_LIB_NAMESPACE_END \ No newline at end of file +VCMI_LIB_NAMESPACE_END diff --git a/lib/rmg/RmgMap.cpp b/lib/rmg/RmgMap.cpp index 790a09a24..f83655f51 100644 --- a/lib/rmg/RmgMap.cpp +++ b/lib/rmg/RmgMap.cpp @@ -84,7 +84,7 @@ void RmgMap::foreachDiagonalNeighbour(const int3 & pos, const std::functioninitTerrain(); diff --git a/lib/rmg/RmgMap.h b/lib/rmg/RmgMap.h index 6caff8236..24510dee5 100644 --- a/lib/rmg/RmgMap.h +++ b/lib/rmg/RmgMap.h @@ -17,7 +17,6 @@ VCMI_LIB_NAMESPACE_BEGIN class CMap; class CMapEditManager; -class CRandomGenerator; class TileInfo; class CMapGenOptions; class Zone; @@ -25,6 +24,11 @@ class CMapGenerator; class MapProxy; class playerInfo; +namespace vstd +{ +class RNG; +} + class RmgMap { public: @@ -79,7 +83,7 @@ public: void registerZone(FactionID faction); ui32 getZoneCount(FactionID faction); ui32 getTotalZoneCount() const; - void initTiles(CMapGenerator & generator, CRandomGenerator & rand); + void initTiles(CMapGenerator & generator, vstd::RNG & rand); void addModificators(); bool isAllowedSpell(const SpellID & sid) const; diff --git a/lib/rmg/RmgObject.cpp b/lib/rmg/RmgObject.cpp index 0d89e1b74..1004072cb 100644 --- a/lib/rmg/RmgObject.cpp +++ b/lib/rmg/RmgObject.cpp @@ -21,6 +21,8 @@ #include "Functions.h" #include "../TerrainHandler.h" +#include + VCMI_LIB_NAMESPACE_BEGIN using namespace rmg; @@ -111,7 +113,7 @@ void Object::Instance::setPositionRaw(const int3 & position) dObject.pos = dPosition + dParent.getPosition(); } -void Object::Instance::setAnyTemplate(CRandomGenerator & rng) +void Object::Instance::setAnyTemplate(vstd::RNG & rng) { auto templates = dObject.getObjectHandler()->getTemplates(); if(templates.empty()) @@ -122,7 +124,7 @@ void Object::Instance::setAnyTemplate(CRandomGenerator & rng) setPosition(getPosition(false)); } -void Object::Instance::setTemplate(TerrainId terrain, CRandomGenerator & rng) +void Object::Instance::setTemplate(TerrainId terrain, vstd::RNG & rng) { auto templates = dObject.getObjectHandler()->getMostSpecificTemplates(terrain); @@ -366,7 +368,7 @@ void Object::setPosition(const int3 & position) i.setPositionRaw(i.getPosition()); } -void Object::setTemplate(const TerrainId & terrain, CRandomGenerator & rng) +void Object::setTemplate(const TerrainId & terrain, vstd::RNG & rng) { for(auto& i : dInstances) i.setTemplate(terrain, rng); @@ -474,7 +476,7 @@ rmg::Area Object::Instance::getBorderAbove() const return borderAbove; } -void Object::Instance::finalize(RmgMap & map, CRandomGenerator & rng) +void Object::Instance::finalize(RmgMap & map, vstd::RNG & rng) { if(!map.isOnMap(getPosition(true))) throw rmgException(boost::str(boost::format("Position of object %d at %s is outside the map") % dObject.id % getPosition(true).toString())); @@ -511,7 +513,7 @@ void Object::Instance::finalize(RmgMap & map, CRandomGenerator & rng) map.getMapProxy()->insertObject(&dObject); } -void Object::finalize(RmgMap & map, CRandomGenerator & rng) +void Object::finalize(RmgMap & map, vstd::RNG & rng) { if(dInstances.empty()) throw rmgException("Cannot finalize object without instances"); diff --git a/lib/rmg/RmgObject.h b/lib/rmg/RmgObject.h index ffdfb0a89..c15256fb2 100644 --- a/lib/rmg/RmgObject.h +++ b/lib/rmg/RmgObject.h @@ -16,8 +16,12 @@ VCMI_LIB_NAMESPACE_BEGIN +namespace vstd +{ +class RNG; +} + class CGObjectInstance; -class CRandomGenerator; class RmgMap; namespace rmg { @@ -39,8 +43,8 @@ public: bool isRemovable() const; const Area & getAccessibleArea() const; Area getBorderAbove() const; - void setTemplate(TerrainId terrain, CRandomGenerator &); //cache invalidation - void setAnyTemplate(CRandomGenerator &); //cache invalidation + void setTemplate(TerrainId terrain, vstd::RNG &); //cache invalidation + void setAnyTemplate(vstd::RNG &); //cache invalidation int3 getTopTile() const; int3 getPosition(bool isAbsolute = false) const; @@ -49,7 +53,7 @@ public: const CGObjectInstance & object() const; CGObjectInstance & object(); - void finalize(RmgMap & map, CRandomGenerator &); //cache invalidation + void finalize(RmgMap & map, vstd::RNG &); //cache invalidation void clear(); std::function onCleared; @@ -83,7 +87,7 @@ public: const int3 & getPosition() const; void setPosition(const int3 & position); - void setTemplate(const TerrainId & terrain, CRandomGenerator &); + void setTemplate(const TerrainId & terrain, vstd::RNG &); const Area & getArea() const; //lazy cache invalidation const int3 getVisibleTop() const; @@ -94,7 +98,7 @@ public: void setValue(uint32_t value); uint32_t getValue() const; - void finalize(RmgMap & map, CRandomGenerator &); + void finalize(RmgMap & map, vstd::RNG &); void clearCachedArea() const; void clear(); diff --git a/lib/rmg/Zone.cpp b/lib/rmg/Zone.cpp index 5bc88a8d7..273dc226e 100644 --- a/lib/rmg/Zone.cpp +++ b/lib/rmg/Zone.cpp @@ -17,6 +17,10 @@ #include "RmgPath.h" #include "modificators/ObjectManager.h" +#include "../CRandomGenerator.h" + +#include + VCMI_LIB_NAMESPACE_BEGIN const std::function AREA_NO_FILTER = [](const int3 & t) @@ -24,16 +28,18 @@ const std::function AREA_NO_FILTER = [](const int3 & t) return true; }; -Zone::Zone(RmgMap & map, CMapGenerator & generator, CRandomGenerator & r) +Zone::Zone(RmgMap & map, CMapGenerator & generator, vstd::RNG & r) : finished(false) , townType(ETownType::NEUTRAL) , terrainType(ETerrainId::GRASS) , map(map) + , rand(std::make_unique(r.nextInt())) , generator(generator) { - rand.setSeed(r.nextInt()); } +Zone::~Zone() = default; + bool Zone::isUnderground() const { return getPos().z; @@ -401,9 +407,9 @@ void Zone::initModificators() } } -CRandomGenerator& Zone::getRand() +vstd::RNG& Zone::getRand() { - return rand; + return *rand; } VCMI_LIB_NAMESPACE_END diff --git a/lib/rmg/Zone.h b/lib/rmg/Zone.h index 73a985363..4ae2c7a13 100644 --- a/lib/rmg/Zone.h +++ b/lib/rmg/Zone.h @@ -13,7 +13,6 @@ #include "../GameConstants.h" #include "float3.h" #include "../int3.h" -#include "../CRandomGenerator.h" #include "CRmgTemplate.h" #include "RmgArea.h" #include "RmgPath.h" @@ -28,7 +27,6 @@ VCMI_LIB_NAMESPACE_BEGIN class RmgMap; class CMapGenerator; class Modificator; -class CRandomGenerator; extern const std::function AREA_NO_FILTER; @@ -74,8 +72,9 @@ private: class Zone : public rmg::ZoneOptions { public: - Zone(RmgMap & map, CMapGenerator & generator, CRandomGenerator & rand); + Zone(RmgMap & map, CMapGenerator & generator, vstd::RNG & rand); Zone(const Zone &) = delete; + ~Zone(); void setOptions(const rmg::ZoneOptions & options); bool isUnderground() const; @@ -127,14 +126,14 @@ public: void initModificators(); - CRandomGenerator & getRand(); + vstd::RNG & getRand(); public: mutable boost::recursive_mutex areaMutex; using Lock = boost::unique_lock; protected: CMapGenerator & generator; - CRandomGenerator rand; + std::unique_ptr rand; RmgMap & map; TModificators modificators; bool finished; diff --git a/lib/rmg/modificators/ConnectionsPlacer.cpp b/lib/rmg/modificators/ConnectionsPlacer.cpp index f9a34c25c..d6bc3c2c5 100644 --- a/lib/rmg/modificators/ConnectionsPlacer.cpp +++ b/lib/rmg/modificators/ConnectionsPlacer.cpp @@ -26,6 +26,8 @@ #include "WaterProxy.h" #include "TownPlacer.h" +#include + VCMI_LIB_NAMESPACE_BEGIN std::pair ConnectionsPlacer::lockZones(std::shared_ptr otherZone) diff --git a/lib/rmg/modificators/MinePlacer.cpp b/lib/rmg/modificators/MinePlacer.cpp index b0dc77a9e..4dcc2b41a 100644 --- a/lib/rmg/modificators/MinePlacer.cpp +++ b/lib/rmg/modificators/MinePlacer.cpp @@ -22,6 +22,8 @@ #include "WaterAdopter.h" #include "../TileInfo.h" +#include + VCMI_LIB_NAMESPACE_BEGIN void MinePlacer::process() diff --git a/lib/rmg/modificators/ObjectDistributor.cpp b/lib/rmg/modificators/ObjectDistributor.cpp index df4723cd3..ae6c42f65 100644 --- a/lib/rmg/modificators/ObjectDistributor.cpp +++ b/lib/rmg/modificators/ObjectDistributor.cpp @@ -25,6 +25,8 @@ #include "../Functions.h" #include "../RmgObject.h" +#include + VCMI_LIB_NAMESPACE_BEGIN void ObjectDistributor::process() diff --git a/lib/rmg/modificators/ObjectManager.cpp b/lib/rmg/modificators/ObjectManager.cpp index c55bd60b5..a15ef0937 100644 --- a/lib/rmg/modificators/ObjectManager.cpp +++ b/lib/rmg/modificators/ObjectManager.cpp @@ -29,6 +29,8 @@ #include "../Functions.h" #include "../RmgObject.h" +#include + VCMI_LIB_NAMESPACE_BEGIN void ObjectManager::process() diff --git a/lib/rmg/modificators/ObstaclePlacer.cpp b/lib/rmg/modificators/ObstaclePlacer.cpp index 37afc1477..621b985e7 100644 --- a/lib/rmg/modificators/ObstaclePlacer.cpp +++ b/lib/rmg/modificators/ObstaclePlacer.cpp @@ -19,7 +19,6 @@ #include "RiverPlacer.h" #include "../RmgMap.h" #include "../CMapGenerator.h" -#include "../../CRandomGenerator.h" #include "../Functions.h" #include "../../mapping/CMapEditManager.h" #include "../../mapping/CMap.h" diff --git a/lib/rmg/modificators/PrisonHeroPlacer.cpp b/lib/rmg/modificators/PrisonHeroPlacer.cpp index 3aafc0eda..9d801e098 100644 --- a/lib/rmg/modificators/PrisonHeroPlacer.cpp +++ b/lib/rmg/modificators/PrisonHeroPlacer.cpp @@ -17,7 +17,9 @@ #include "../../VCMI_Lib.h" #include "../../mapObjectConstructors/AObjectTypeHandler.h" #include "../../mapObjectConstructors/CObjectClassesHandler.h" -#include "../../mapObjects/MapObjects.h" +#include "../../mapObjects/MapObjects.h" + +#include VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/rmg/modificators/PrisonHeroPlacer.h b/lib/rmg/modificators/PrisonHeroPlacer.h index c302daa4e..6fc379da6 100644 --- a/lib/rmg/modificators/PrisonHeroPlacer.h +++ b/lib/rmg/modificators/PrisonHeroPlacer.h @@ -15,8 +15,6 @@ VCMI_LIB_NAMESPACE_BEGIN -class CRandomGenerator; - class PrisonHeroPlacer : public Modificator { public: diff --git a/lib/rmg/modificators/QuestArtifactPlacer.cpp b/lib/rmg/modificators/QuestArtifactPlacer.cpp index 912ee58c9..9117ab34c 100644 --- a/lib/rmg/modificators/QuestArtifactPlacer.cpp +++ b/lib/rmg/modificators/QuestArtifactPlacer.cpp @@ -17,7 +17,9 @@ #include "../../VCMI_Lib.h" #include "../../mapObjectConstructors/AObjectTypeHandler.h" #include "../../mapObjectConstructors/CObjectClassesHandler.h" -#include "../../mapObjects/MapObjects.h" +#include "../../mapObjects/MapObjects.h" + +#include VCMI_LIB_NAMESPACE_BEGIN @@ -95,7 +97,7 @@ void QuestArtifactPlacer::findZonesForQuestArts() logGlobal->trace("Number of nearby zones suitable for quest artifacts: %d", questArtZones.size()); } -void QuestArtifactPlacer::placeQuestArtifacts(CRandomGenerator & rand) +void QuestArtifactPlacer::placeQuestArtifacts(vstd::RNG & rand) { for (const auto & artifactToPlace : questArtifactsToPlace) { diff --git a/lib/rmg/modificators/QuestArtifactPlacer.h b/lib/rmg/modificators/QuestArtifactPlacer.h index 28896b711..6800bcb48 100644 --- a/lib/rmg/modificators/QuestArtifactPlacer.h +++ b/lib/rmg/modificators/QuestArtifactPlacer.h @@ -15,8 +15,6 @@ VCMI_LIB_NAMESPACE_BEGIN -class CRandomGenerator; - class QuestArtifactPlacer : public Modificator { public: @@ -33,7 +31,7 @@ public: void rememberPotentialArtifactToReplace(CGObjectInstance* obj); CGObjectInstance * drawObjectToReplace(); std::vector getPossibleArtifactsToReplace() const; - void placeQuestArtifacts(CRandomGenerator & rand); + void placeQuestArtifacts(vstd::RNG & rand); void dropReplacedArtifact(CGObjectInstance* obj); size_t getMaxQuestArtifactCount() const; @@ -50,4 +48,4 @@ protected: std::vector questArtifacts; }; -VCMI_LIB_NAMESPACE_END \ No newline at end of file +VCMI_LIB_NAMESPACE_END diff --git a/lib/rmg/modificators/RiverPlacer.cpp b/lib/rmg/modificators/RiverPlacer.cpp index 303613803..dd4150627 100644 --- a/lib/rmg/modificators/RiverPlacer.cpp +++ b/lib/rmg/modificators/RiverPlacer.cpp @@ -27,6 +27,8 @@ #include "WaterProxy.h" #include "RoadPlacer.h" +#include + VCMI_LIB_NAMESPACE_BEGIN const int RIVER_DELTA_ID = 143; diff --git a/lib/rmg/modificators/RockFiller.cpp b/lib/rmg/modificators/RockFiller.cpp index db422ec00..116885706 100644 --- a/lib/rmg/modificators/RockFiller.cpp +++ b/lib/rmg/modificators/RockFiller.cpp @@ -19,7 +19,6 @@ #include "../CMapGenerator.h" #include "../Functions.h" #include "../../TerrainHandler.h" -#include "../../CRandomGenerator.h" #include "../lib/mapping/CMapEditManager.h" #include "../TileInfo.h" #include "../threadpool/MapProxy.h" diff --git a/lib/rmg/modificators/RockPlacer.cpp b/lib/rmg/modificators/RockPlacer.cpp index 6798156bf..3fefee83b 100644 --- a/lib/rmg/modificators/RockPlacer.cpp +++ b/lib/rmg/modificators/RockPlacer.cpp @@ -18,7 +18,6 @@ #include "../CMapGenerator.h" #include "../Functions.h" #include "../../TerrainHandler.h" -#include "../../CRandomGenerator.h" #include "../../mapping/CMapEditManager.h" #include "../../VCMI_Lib.h" #include "../TileInfo.h" diff --git a/lib/rmg/modificators/TerrainPainter.cpp b/lib/rmg/modificators/TerrainPainter.cpp index 3648fb8a8..b12fb4586 100644 --- a/lib/rmg/modificators/TerrainPainter.cpp +++ b/lib/rmg/modificators/TerrainPainter.cpp @@ -22,6 +22,8 @@ #include "../../TerrainHandler.h" #include "../../CTownHandler.h" +#include + VCMI_LIB_NAMESPACE_BEGIN void TerrainPainter::process() diff --git a/lib/rmg/modificators/TownPlacer.cpp b/lib/rmg/modificators/TownPlacer.cpp index cb26f2c89..c4d3d1803 100644 --- a/lib/rmg/modificators/TownPlacer.cpp +++ b/lib/rmg/modificators/TownPlacer.cpp @@ -27,6 +27,8 @@ #include "WaterAdopter.h" #include "../TileInfo.h" +#include + VCMI_LIB_NAMESPACE_BEGIN void TownPlacer::process() diff --git a/lib/rmg/modificators/TreasurePlacer.cpp b/lib/rmg/modificators/TreasurePlacer.cpp index 96a093f61..13de65b39 100644 --- a/lib/rmg/modificators/TreasurePlacer.cpp +++ b/lib/rmg/modificators/TreasurePlacer.cpp @@ -33,6 +33,8 @@ #include "../../mapping/CMap.h" #include "../../mapping/CMapEditManager.h" +#include + VCMI_LIB_NAMESPACE_BEGIN ObjectInfo::ObjectInfo(): @@ -649,7 +651,7 @@ std::vector TreasurePlacer::prepareTreasurePile(const CTreasureInfo if (currentValue >= minValue) { // 50% chance to end right here - if (zone.getRand().nextInt() & 1) + if (zone.getRand().nextInt(0, 1) == 1) break; } } diff --git a/lib/rmg/modificators/TreasurePlacer.h b/lib/rmg/modificators/TreasurePlacer.h index 8c6a6c316..450c812b7 100644 --- a/lib/rmg/modificators/TreasurePlacer.h +++ b/lib/rmg/modificators/TreasurePlacer.h @@ -18,7 +18,6 @@ class CGObjectInstance; class ObjectManager; class RmgMap; class CMapGenerator; -class CRandomGenerator; struct ObjectInfo { diff --git a/lib/rmg/modificators/WaterAdopter.cpp b/lib/rmg/modificators/WaterAdopter.cpp index af3fb68bf..642a2c5aa 100644 --- a/lib/rmg/modificators/WaterAdopter.cpp +++ b/lib/rmg/modificators/WaterAdopter.cpp @@ -23,6 +23,8 @@ #include "ConnectionsPlacer.h" #include "../TileInfo.h" +#include + VCMI_LIB_NAMESPACE_BEGIN void WaterAdopter::process() diff --git a/lib/rmg/modificators/WaterProxy.cpp b/lib/rmg/modificators/WaterProxy.cpp index c9678f406..f50c1c555 100644 --- a/lib/rmg/modificators/WaterProxy.cpp +++ b/lib/rmg/modificators/WaterProxy.cpp @@ -30,6 +30,8 @@ #include "WaterAdopter.h" #include "../RmgArea.h" +#include + VCMI_LIB_NAMESPACE_BEGIN void WaterProxy::process() diff --git a/lib/rmg/threadpool/MapProxy.cpp b/lib/rmg/threadpool/MapProxy.cpp index 87cb42455..1f29b1bd3 100644 --- a/lib/rmg/threadpool/MapProxy.cpp +++ b/lib/rmg/threadpool/MapProxy.cpp @@ -37,21 +37,21 @@ void MapProxy::removeObject(CGObjectInstance * obj) map.getEditManager()->removeObject(obj); } -void MapProxy::drawTerrain(CRandomGenerator & generator, std::vector & tiles, TerrainId terrain) +void MapProxy::drawTerrain(vstd::RNG & generator, std::vector & tiles, TerrainId terrain) { Lock lock(mx); map.getEditManager()->getTerrainSelection().setSelection(tiles); map.getEditManager()->drawTerrain(terrain, map.getDecorationsPercentage(), &generator); } -void MapProxy::drawRivers(CRandomGenerator & generator, std::vector & tiles, TerrainId terrain) +void MapProxy::drawRivers(vstd::RNG & generator, std::vector & tiles, TerrainId terrain) { Lock lock(mx); map.getEditManager()->getTerrainSelection().setSelection(tiles); map.getEditManager()->drawRiver(VLC->terrainTypeHandler->getById(terrain)->river, &generator); } -void MapProxy::drawRoads(CRandomGenerator & generator, std::vector & tiles, RoadId roadType) +void MapProxy::drawRoads(vstd::RNG & generator, std::vector & tiles, RoadId roadType) { Lock lock(mx); map.getEditManager()->getTerrainSelection().setSelection(tiles); diff --git a/lib/rmg/threadpool/MapProxy.h b/lib/rmg/threadpool/MapProxy.h index 9cb265f8f..5158a97fa 100644 --- a/lib/rmg/threadpool/MapProxy.h +++ b/lib/rmg/threadpool/MapProxy.h @@ -28,9 +28,9 @@ public: void insertObjects(std::set& objects); void removeObject(CGObjectInstance* obj); - void drawTerrain(CRandomGenerator & generator, std::vector & tiles, TerrainId terrain); - void drawRivers(CRandomGenerator & generator, std::vector & tiles, TerrainId terrain); - void drawRoads(CRandomGenerator & generator, std::vector & tiles, RoadId roadType); + void drawTerrain(vstd::RNG & generator, std::vector & tiles, TerrainId terrain); + void drawRivers(vstd::RNG & generator, std::vector & tiles, TerrainId terrain); + void drawRoads(vstd::RNG & generator, std::vector & tiles, RoadId roadType); private: mutable boost::shared_mutex mx; diff --git a/lib/spells/AdventureSpellMechanics.cpp b/lib/spells/AdventureSpellMechanics.cpp index 6d7699ee4..dc38db794 100644 --- a/lib/spells/AdventureSpellMechanics.cpp +++ b/lib/spells/AdventureSpellMechanics.cpp @@ -17,7 +17,6 @@ #include "../CGameInfoCallback.h" #include "../CPlayerState.h" -#include "../CRandomGenerator.h" #include "../GameSettings.h" #include "../mapObjects/CGHeroInstance.h" #include "../mapObjects/CGTownInstance.h" @@ -25,6 +24,8 @@ #include "../mapping/CMap.h" #include "../networkPacks/PacksForClient.h" +#include + VCMI_LIB_NAMESPACE_BEGIN ///AdventureSpellMechanics diff --git a/lib/spells/BattleSpellMechanics.cpp b/lib/spells/BattleSpellMechanics.cpp index d586bc785..c9d6b70b1 100644 --- a/lib/spells/BattleSpellMechanics.cpp +++ b/lib/spells/BattleSpellMechanics.cpp @@ -19,7 +19,8 @@ #include "../networkPacks/PacksForClientBattle.h" #include "../networkPacks/SetStackEffect.h" #include "../CStack.h" -#include "../CRandomGenerator.h" + +#include VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/spells/ISpellMechanics.cpp b/lib/spells/ISpellMechanics.cpp index 3cec1dd72..8a1593a73 100644 --- a/lib/spells/ISpellMechanics.cpp +++ b/lib/spells/ISpellMechanics.cpp @@ -11,7 +11,6 @@ #include "StdInc.h" #include "ISpellMechanics.h" -#include "../CRandomGenerator.h" #include "../VCMI_Lib.h" #include "../bonuses/Bonus.h" @@ -41,6 +40,8 @@ #include "../IGameCallback.h"//todo: remove #include "../BattleFieldHandler.h" +#include + VCMI_LIB_NAMESPACE_BEGIN namespace spells diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index 0f9dc9646..5bd118dac 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.h @@ -22,7 +22,6 @@ VCMI_LIB_NAMESPACE_BEGIN struct Query; class IBattleState; -class CRandomGenerator; class CreatureService; class CMap; class CGameInfoCallback; diff --git a/lib/spells/effects/Catapult.cpp b/lib/spells/effects/Catapult.cpp index 2a511935d..4fa46b2b7 100644 --- a/lib/spells/effects/Catapult.cpp +++ b/lib/spells/effects/Catapult.cpp @@ -20,7 +20,8 @@ #include "../../mapObjects/CGTownInstance.h" #include "../../networkPacks/PacksForClientBattle.h" #include "../../serializer/JsonSerializeFormat.h" -#include "../../CRandomGenerator.h" + +#include VCMI_LIB_NAMESPACE_BEGIN diff --git a/lib/spells/effects/Obstacle.cpp b/lib/spells/effects/Obstacle.cpp index eb9fa5be7..e75052684 100644 --- a/lib/spells/effects/Obstacle.cpp +++ b/lib/spells/effects/Obstacle.cpp @@ -18,7 +18,8 @@ #include "../../battle/CBattleInfoCallback.h" #include "../../networkPacks/PacksForClientBattle.h" #include "../../serializer/JsonSerializeFormat.h" -#include "../../CRandomGenerator.h" + +#include VCMI_LIB_NAMESPACE_BEGIN diff --git a/mapeditor/mapcontroller.cpp b/mapeditor/mapcontroller.cpp index a44b2d1af..9e869ff75 100644 --- a/mapeditor/mapcontroller.cpp +++ b/mapeditor/mapcontroller.cpp @@ -26,6 +26,7 @@ #include "../lib/CSkillHandler.h" #include "../lib/spells/CSpellHandler.h" #include "../lib/CHeroHandler.h" +#include "../lib/CRandomGenerator.h" #include "../lib/serializer/CMemorySerializer.h" #include "mapview.h" #include "scenelayer.h" diff --git a/mapeditor/windownewmap.cpp b/mapeditor/windownewmap.cpp index 75aad118a..92e6a19fa 100644 --- a/mapeditor/windownewmap.cpp +++ b/mapeditor/windownewmap.cpp @@ -17,6 +17,7 @@ #include "../lib/mapping/CMapEditManager.h" #include "../lib/mapping/MapFormat.h" #include "../lib/CGeneralTextHandler.h" +#include "../lib/CRandomGenerator.h" #include "../lib/serializer/JsonSerializer.h" #include "../lib/serializer/JsonDeserializer.h" diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 8221db459..ecd945e2b 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -29,6 +29,7 @@ #include "../lib/CGeneralTextHandler.h" #include "../lib/CHeroHandler.h" #include "../lib/CPlayerState.h" +#include "../lib/CRandomGenerator.h" #include "../lib/CSoundBase.h" #include "../lib/CThreadHelper.h" #include "../lib/CTownHandler.h" @@ -68,7 +69,8 @@ #include "../lib/spells/CSpellHandler.h" -#include "vstd/CLoggerBase.h" +#include +#include #include #include #include @@ -558,7 +560,7 @@ void CGameHandler::init(StartInfo *si, Load::ProgressAccumulator & progressTrack logGlobal->info("Gamestate initialized!"); // reset seed, so that clients can't predict any following random values - getRandomGenerator().resetSeed(); + randomNumberGenerator = std::make_unique(); for (auto & elem : gs->players) turnOrder->addPlayer(elem.first); @@ -4378,9 +4380,9 @@ void CGameHandler::showInfoDialog(InfoWindow * iw) sendAndApply(iw); } -CRandomGenerator & CGameHandler::getRandomGenerator() +vstd::RNG & CGameHandler::getRandomGenerator() { - return CRandomGenerator::getDefault(); + return *randomNumberGenerator; } #if SCRIPTING_ENABLED diff --git a/server/CGameHandler.h b/server/CGameHandler.h index eebb21f68..4cd5fc573 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -23,6 +23,7 @@ class SpellCastEnvironment; class CConnection; class CCommanderInstance; class EVictoryLossCheckResult; +class CRandomGenerator; struct CPack; struct CPackForServer; @@ -63,6 +64,7 @@ public: std::unique_ptr queries; std::unique_ptr turnOrder; std::unique_ptr turnTimerHandler; + std::unique_ptr randomNumberGenerator; //use enums as parameters, because doMove(sth, true, false, true) is not readable enum EGuardLook {CHECK_FOR_GUARDS, IGNORE_GUARDS}; @@ -226,7 +228,7 @@ public: template void serialize(Handler &h) { h & QID; - h & getRandomGenerator(); + h & randomNumberGenerator; h & *battles; h & *heroPool; h & *playerMessages; @@ -275,7 +277,7 @@ public: void checkVictoryLossConditions(const std::set & playerColors); void checkVictoryLossConditionsForAll(); - CRandomGenerator & getRandomGenerator(); + vstd::RNG & getRandomGenerator(); #if SCRIPTING_ENABLED scripting::Pool * getGlobalContextPool() const override; diff --git a/server/battles/BattleActionProcessor.cpp b/server/battles/BattleActionProcessor.cpp index 0fb275960..98d16493a 100644 --- a/server/battles/BattleActionProcessor.cpp +++ b/server/battles/BattleActionProcessor.cpp @@ -29,6 +29,8 @@ #include "../../lib/spells/ISpellMechanics.h" #include "../../lib/spells/Problem.h" +#include + BattleActionProcessor::BattleActionProcessor(BattleProcessor * owner, CGameHandler * newGameHandler) : owner(owner) , gameHandler(newGameHandler) @@ -1263,8 +1265,7 @@ void BattleActionProcessor::handleDeathStare(const CBattleInfoCallback & battle, int singleCreatureKillChancePercent = attacker->valOfBonuses(BonusType::DEATH_STARE, subtype); double chanceToKill = singleCreatureKillChancePercent / 100.0; vstd::amin(chanceToKill, 1); //cap at 100% - std::binomial_distribution<> distribution(attacker->getCount(), chanceToKill); - int killedCreatures = distribution(gameHandler->getRandomGenerator().getStdGenerator()); + int killedCreatures = gameHandler->getRandomGenerator().nextBinomialInt(attacker->getCount(), chanceToKill); int maxToKill = (attacker->getCount() * singleCreatureKillChancePercent + 99) / 100; vstd::amin(killedCreatures, maxToKill); diff --git a/server/battles/BattleFlowProcessor.cpp b/server/battles/BattleFlowProcessor.cpp index eadf4e65e..920ee1a4a 100644 --- a/server/battles/BattleFlowProcessor.cpp +++ b/server/battles/BattleFlowProcessor.cpp @@ -26,6 +26,8 @@ #include "../../lib/spells/ISpellMechanics.h" #include "../../lib/spells/ObstacleCasterProxy.h" +#include + BattleFlowProcessor::BattleFlowProcessor(BattleProcessor * owner, CGameHandler * newGameHandler) : owner(owner) , gameHandler(newGameHandler) diff --git a/server/battles/BattleResultProcessor.cpp b/server/battles/BattleResultProcessor.cpp index 6daf68a47..90ce8fbc7 100644 --- a/server/battles/BattleResultProcessor.cpp +++ b/server/battles/BattleResultProcessor.cpp @@ -30,6 +30,8 @@ #include "../../lib/serializer/Cast.h" #include "../../lib/spells/CSpellHandler.h" +#include + BattleResultProcessor::BattleResultProcessor(BattleProcessor * owner, CGameHandler * newGameHandler) // : owner(owner) : gameHandler(newGameHandler) diff --git a/server/processors/HeroPoolProcessor.cpp b/server/processors/HeroPoolProcessor.cpp index 32bedb747..c1f4f8382 100644 --- a/server/processors/HeroPoolProcessor.cpp +++ b/server/processors/HeroPoolProcessor.cpp @@ -13,6 +13,7 @@ #include "TurnOrderProcessor.h" #include "../CGameHandler.h" +#include "../../lib/CRandomGenerator.h" #include "../../lib/CHeroHandler.h" #include "../../lib/CPlayerState.h" #include "../../lib/GameSettings.h" @@ -368,7 +369,7 @@ CGHeroInstance * HeroPoolProcessor::pickHeroFor(bool isNative, const PlayerColor return *RandomGeneratorUtil::nextItem(possibleHeroes, getRandomGenerator(player)); } -CRandomGenerator & HeroPoolProcessor::getHeroSkillsRandomGenerator(const HeroTypeID & hero) +vstd::RNG & HeroPoolProcessor::getHeroSkillsRandomGenerator(const HeroTypeID & hero) { if (heroSeed.count(hero) == 0) { @@ -379,7 +380,7 @@ CRandomGenerator & HeroPoolProcessor::getHeroSkillsRandomGenerator(const HeroTyp return *heroSeed.at(hero); } -CRandomGenerator & HeroPoolProcessor::getRandomGenerator(const PlayerColor & player) +vstd::RNG & HeroPoolProcessor::getRandomGenerator(const PlayerColor & player) { if (playerSeed.count(player) == 0) { diff --git a/server/processors/HeroPoolProcessor.h b/server/processors/HeroPoolProcessor.h index 8d0d8552d..0a2159d58 100644 --- a/server/processors/HeroPoolProcessor.h +++ b/server/processors/HeroPoolProcessor.h @@ -19,8 +19,13 @@ class PlayerColor; class CGHeroInstance; class HeroTypeID; class ObjectInstanceID; -class CRandomGenerator; class CHeroClass; +class CRandomGenerator; + +namespace vstd +{ +class RNG; +} VCMI_LIB_NAMESPACE_END @@ -46,7 +51,7 @@ class HeroPoolProcessor : boost::noncopyable CGHeroInstance * pickHeroFor(bool isNative, const PlayerColor & player); - CRandomGenerator & getRandomGenerator(const PlayerColor & player); + vstd::RNG & getRandomGenerator(const PlayerColor & player); TavernHeroSlot selectSlotForRole(const PlayerColor & player, TavernSlotRole roleID); @@ -58,7 +63,7 @@ public: void onNewWeek(const PlayerColor & color); - CRandomGenerator & getHeroSkillsRandomGenerator(const HeroTypeID & hero); + vstd::RNG & getHeroSkillsRandomGenerator(const HeroTypeID & hero); /// Incoming net pack handling bool hireHero(const ObjectInstanceID & objectID, const HeroTypeID & hid, const PlayerColor & player, const HeroTypeID & nextHero); diff --git a/test/mock/mock_vstd_RNG.h b/test/mock/mock_vstd_RNG.h index 0064503ad..f3723eb19 100644 --- a/test/mock/mock_vstd_RNG.h +++ b/test/mock/mock_vstd_RNG.h @@ -21,6 +21,13 @@ public: MOCK_METHOD2(nextInt, int(int lower, int upper)); MOCK_METHOD2(nextInt64, int64_t(int64_t lower, int64_t upper)); MOCK_METHOD2(nextDouble, double(double lower, double upper)); + + MOCK_METHOD1(nextInt, int(int upper)); + MOCK_METHOD1(nextInt64, int64_t(int64_t upper)); + MOCK_METHOD1(nextDouble, double(double upper)); + + MOCK_METHOD0(nextInt, int()); + MOCK_METHOD2(nextBinomialInt, int(int coinsCount, double coinChance)); }; } From 0d66ddbeec5d51a08313fdb83e7d5d9ee4a59eb9 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 1 Jun 2024 16:09:14 +0000 Subject: [PATCH 035/100] GameState now uses random generator from server. Blocked access to rmg on client --- client/Client.cpp | 7 +++++++ client/Client.h | 2 ++ lib/IGameCallback.h | 3 +++ lib/gameState/CGameState.cpp | 4 +--- lib/gameState/CGameState.h | 15 +++++++++------ lib/serializer/ESerializationVersion.h | 3 ++- server/CGameHandler.cpp | 6 ++++-- server/CGameHandler.h | 2 +- test/mock/mock_IGameCallback.cpp | 5 +++++ test/mock/mock_IGameCallback.h | 2 ++ 10 files changed, 36 insertions(+), 13 deletions(-) diff --git a/client/Client.cpp b/client/Client.cpp index 15f7c47a0..174acdb39 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -556,6 +556,13 @@ void CClient::invalidatePaths() pathCache.clear(); } +vstd::RNG & CClient::getRandomGenerator() +{ + // Client should use CRandomGenerator::getDefault() for UI logic + // Gamestate should never call this method on client! + throw std::runtime_error("Illegal access to random number generator from client code!"); +} + std::shared_ptr CClient::getPathsInfo(const CGHeroInstance * h) { assert(h); diff --git a/client/Client.h b/client/Client.h index a775ad732..68e116603 100644 --- a/client/Client.h +++ b/client/Client.h @@ -218,6 +218,8 @@ public: void showInfoDialog(InfoWindow * iw) override {}; void removeGUI() const; + vstd::RNG & getRandomGenerator() override; + #if SCRIPTING_ENABLED scripting::Pool * getGlobalContextPool() const override; #endif diff --git a/lib/IGameCallback.h b/lib/IGameCallback.h index a42530adc..493091422 100644 --- a/lib/IGameCallback.h +++ b/lib/IGameCallback.h @@ -135,6 +135,9 @@ public: virtual void changeFogOfWar(std::unordered_set &tiles, PlayerColor player, ETileVisibility mode) = 0; virtual void castSpell(const spells::Caster * caster, SpellID spellID, const int3 &pos) = 0; + + virtual vstd::RNG & getRandomGenerator() = 0; + }; class DLL_LINKAGE CNonConstInfoCallback : public CPrivilegedInfoCallback diff --git a/lib/gameState/CGameState.cpp b/lib/gameState/CGameState.cpp index 225eb4566..1be6e9241 100644 --- a/lib/gameState/CGameState.cpp +++ b/lib/gameState/CGameState.cpp @@ -185,8 +185,6 @@ void CGameState::init(const IMapService * mapService, StartInfo * si, Load::Prog { assert(services); assert(callback); - logGlobal->info("\tUsing random seed: %d", si->seedToBeUsed); - rand = std::make_unique(si->seedToBeUsed); scenarioOps = CMemorySerializer::deepCopy(*si).release(); initialOpts = CMemorySerializer::deepCopy(*si).release(); si = nullptr; @@ -1966,7 +1964,7 @@ TeamState::TeamState() vstd::RNG & CGameState::getRandomGenerator() { - return *rand; + return callback->getRandomGenerator(); } ArtifactID CGameState::pickRandomArtifact(vstd::RNG & rand, int flags, std::function accepts) diff --git a/lib/gameState/CGameState.h b/lib/gameState/CGameState.h index d31f41f16..f5818d59b 100644 --- a/lib/gameState/CGameState.h +++ b/lib/gameState/CGameState.h @@ -9,10 +9,10 @@ */ #pragma once -#include "bonuses/CBonusSystemNode.h" -#include "IGameCallback.h" -#include "LoadProgress.h" -#include "ConstTransitivePtr.h" +#include "../bonuses/CBonusSystemNode.h" +#include "../IGameCallback.h" +#include "../LoadProgress.h" +#include "../ConstTransitivePtr.h" namespace boost { @@ -186,7 +186,11 @@ public: h & teams; h & heroesPool; h & globalEffects; - h & rand; + if (h.version < Handler::Version::REMOVE_LIB_RNG) + { + std::string oldStateOfRNG; + h & oldStateOfRNG; + } h & rumor; h & campaign; h & allocatedArtifacts; @@ -234,7 +238,6 @@ private: // ---- data ----- std::shared_ptr> applier; - std::unique_ptr rand; Services * services; /// Pointer to campaign state manager. Nullptr for single scenarios diff --git a/lib/serializer/ESerializationVersion.h b/lib/serializer/ESerializationVersion.h index d7d830afc..68e3e1f5a 100644 --- a/lib/serializer/ESerializationVersion.h +++ b/lib/serializer/ESerializationVersion.h @@ -56,6 +56,7 @@ enum class ESerializationVersion : int32_t REMOVE_FOG_OF_WAR_POINTER, // 846 - fog of war is serialized as reference instead of pointer SIMPLE_TEXT_CONTAINER_SERIALIZATION, // 847 - text container is serialized using common routine instead of custom approach MAP_FORMAT_ADDITIONAL_INFOS, // 848 - serialize new infos in map format + REMOVE_LIB_RNG, // 849 - removed random number generators from library classes - CURRENT = MAP_FORMAT_ADDITIONAL_INFOS + CURRENT = REMOVE_LIB_RNG }; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index ecd945e2b..a7f303981 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -549,9 +549,11 @@ void CGameHandler::reinitScripting() void CGameHandler::init(StartInfo *si, Load::ProgressAccumulator & progressTracking) { if (si->seedToBeUsed == 0) - { si->seedToBeUsed = CRandomGenerator::getDefault().nextInt(); - } + + logGlobal->info("Using random seed: %d", si->seedToBeUsed); + randomNumberGenerator = std::make_unique(si->seedToBeUsed); + CMapService mapService; gs = new CGameState(); gs->preInit(VLC, this); diff --git a/server/CGameHandler.h b/server/CGameHandler.h index 4cd5fc573..d6db0a878 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -277,7 +277,7 @@ public: void checkVictoryLossConditions(const std::set & playerColors); void checkVictoryLossConditionsForAll(); - vstd::RNG & getRandomGenerator(); + vstd::RNG & getRandomGenerator() override; #if SCRIPTING_ENABLED scripting::Pool * getGlobalContextPool() const override; diff --git a/test/mock/mock_IGameCallback.cpp b/test/mock/mock_IGameCallback.cpp index 13a6ea0ff..43428b698 100644 --- a/test/mock/mock_IGameCallback.cpp +++ b/test/mock/mock_IGameCallback.cpp @@ -31,3 +31,8 @@ void GameCallbackMock::sendAndApply(CPackForClient * pack) { upperCallback->apply(pack); } + +vstd::RNG & GameCallbackMock::getRandomGenerator() +{ + throw std::runtime_error("Not implemented!"); +} diff --git a/test/mock/mock_IGameCallback.h b/test/mock/mock_IGameCallback.h index 9ab8675f1..78e11699c 100644 --- a/test/mock/mock_IGameCallback.h +++ b/test/mock/mock_IGameCallback.h @@ -93,6 +93,8 @@ public: ///useful callback methods void sendAndApply(CPackForClient * pack) override; + vstd::RNG & getRandomGenerator() override; + #if SCRIPTING_ENABLED MOCK_CONST_METHOD0(getGlobalContextPool, scripting::Pool *()); #endif From 55bf75c43e4563091b40b1bf918bb7f002d61945 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 1 Jun 2024 16:20:32 +0000 Subject: [PATCH 036/100] Remove no longer used random seeds from StartInfo --- lib/StartInfo.h | 19 ++++++++++-------- lib/gameState/CGameState.cpp | 37 ++---------------------------------- lib/gameState/CGameState.h | 1 - server/CGameHandler.cpp | 10 ++-------- server/CVCMIServer.cpp | 1 - test/game/CGameStateTest.cpp | 2 -- 6 files changed, 15 insertions(+), 55 deletions(-) diff --git a/lib/StartInfo.h b/lib/StartInfo.h index 0bcd64972..68ed79642 100644 --- a/lib/StartInfo.h +++ b/lib/StartInfo.h @@ -123,9 +123,6 @@ struct DLL_LINKAGE StartInfo : public Serializeable using TPlayerInfos = std::map; TPlayerInfos playerInfos; //color indexed - ui32 seedToBeUsed; //0 if not sure (client requests server to decide, will be send in reply pack) - ui32 seedPostInit; //so we know that game is correctly synced at the start; 0 if not known yet - ui32 mapfileChecksum; //0 if not relevant std::string startTimeIso8601; std::string fileURI; SimturnsInfo simturnsInfo; @@ -153,9 +150,13 @@ struct DLL_LINKAGE StartInfo : public Serializeable h & mode; h & difficulty; h & playerInfos; - h & seedToBeUsed; - h & seedPostInit; - h & mapfileChecksum; + if (h.version < Handler::Version::REMOVE_LIB_RNG) + { + uint32_t oldSeeds; + h & oldSeeds; + h & oldSeeds; + h & oldSeeds; + } h & startTimeIso8601; h & fileURI; h & simturnsInfo; @@ -169,8 +170,10 @@ struct DLL_LINKAGE StartInfo : public Serializeable h & campState; } - StartInfo() : mode(EStartMode::INVALID), difficulty(1), seedToBeUsed(0), seedPostInit(0), - mapfileChecksum(0), startTimeIso8601(vstd::getDateTimeISO8601Basic(std::time(nullptr))), fileURI("") + StartInfo() + : mode(EStartMode::INVALID) + , difficulty(1) + , startTimeIso8601(vstd::getDateTimeISO8601Basic(std::time(nullptr))) { } diff --git a/lib/gameState/CGameState.cpp b/lib/gameState/CGameState.cpp index 1be6e9241..73d91053e 100644 --- a/lib/gameState/CGameState.cpp +++ b/lib/gameState/CGameState.cpp @@ -203,8 +203,6 @@ void CGameState::init(const IMapService * mapService, StartInfo * si, Load::Prog } logGlobal->info("Map loaded!"); - checkMapChecksum(); - day = 0; logGlobal->debug("Initialization:"); @@ -236,18 +234,6 @@ void CGameState::init(const IMapService * mapService, StartInfo * si, Load::Prog logGlobal->debug("\tChecking objectives"); map->checkForObjectives(); //needs to be run when all objects are properly placed - - auto seedAfterInit = getRandomGenerator().nextInt(); - logGlobal->info("Seed after init is %d (before was %d)", seedAfterInit, scenarioOps->seedToBeUsed); - if(scenarioOps->seedPostInit > 0) - { - //RNG must be in the same state on all machines when initialization is done (otherwise we have desync) - assert(scenarioOps->seedPostInit == seedAfterInit); - } - else - { - scenarioOps->seedPostInit = seedAfterInit; //store the post init "seed" - } } void CGameState::updateEntity(Metatype metatype, int32_t index, const JsonNode & data) @@ -307,7 +293,7 @@ void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRan CStopWatch sw; // Gen map - CMapGenerator mapGenerator(*scenarioOps->mapGenOptions, callback, scenarioOps->seedToBeUsed); + CMapGenerator mapGenerator(*scenarioOps->mapGenOptions, callback, getRandomGenerator().nextInt()); progressTracking.include(mapGenerator); std::unique_ptr randomMap = mapGenerator.generate(); @@ -323,10 +309,9 @@ void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRan std::shared_ptr options = scenarioOps->mapGenOptions; const std::string templateName = options->getMapTemplate()->getName(); - const ui32 seed = scenarioOps->seedToBeUsed; const std::string dt = vstd::getDateTimeISO8601Basic(std::time(nullptr)); - const std::string fileName = boost::str(boost::format("%s_%s_%d.vmap") % dt % templateName % seed ); + const std::string fileName = boost::str(boost::format("%s_%s_%d.vmap") % dt % templateName ); const auto fullPath = path / fileName; randomMap->name.appendRawString(boost::str(boost::format(" %s") % dt)); @@ -380,24 +365,6 @@ void CGameState::initCampaign() map = campaign->getCurrentMap().release(); } -void CGameState::checkMapChecksum() -{ - logGlobal->info("\tOur checksum for the map: %d", map->checksum); - if(scenarioOps->mapfileChecksum) - { - logGlobal->info("\tServer checksum for %s: %d", scenarioOps->mapname, scenarioOps->mapfileChecksum); - if(map->checksum != scenarioOps->mapfileChecksum) - { - logGlobal->error("Wrong map checksum!!!"); - throw std::runtime_error("Wrong checksum"); - } - } - else - { - scenarioOps->mapfileChecksum = map->checksum; - } -} - void CGameState::initGlobalBonuses() { const JsonNode & baseBonuses = VLC->settings()->getValue(EGameSettings::BONUSES_GLOBAL); diff --git a/lib/gameState/CGameState.h b/lib/gameState/CGameState.h index f5818d59b..efe61f927 100644 --- a/lib/gameState/CGameState.h +++ b/lib/gameState/CGameState.h @@ -201,7 +201,6 @@ public: private: // ----- initialization ----- void initNewGame(const IMapService * mapService, bool allowSavingRandomMap, Load::ProgressAccumulator & progressTracking); - void checkMapChecksum(); void initGlobalBonuses(); void initGrailPosition(); void initRandomFactionsForPlayers(); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index a7f303981..b54ea81f7 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -548,11 +548,8 @@ void CGameHandler::reinitScripting() void CGameHandler::init(StartInfo *si, Load::ProgressAccumulator & progressTracking) { - if (si->seedToBeUsed == 0) - si->seedToBeUsed = CRandomGenerator::getDefault().nextInt(); - - logGlobal->info("Using random seed: %d", si->seedToBeUsed); - randomNumberGenerator = std::make_unique(si->seedToBeUsed); + randomNumberGenerator = std::make_unique(); + logGlobal->info("Using random seed: %d", randomNumberGenerator->nextInt()); CMapService mapService; gs = new CGameState(); @@ -561,9 +558,6 @@ void CGameHandler::init(StartInfo *si, Load::ProgressAccumulator & progressTrack gs->init(&mapService, si, progressTracking); logGlobal->info("Gamestate initialized!"); - // reset seed, so that clients can't predict any following random values - randomNumberGenerator = std::make_unique(); - for (auto & elem : gs->players) turnOrder->addPlayer(elem.first); diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index 9ce49099d..375568880 100644 --- a/server/CVCMIServer.cpp +++ b/server/CVCMIServer.cpp @@ -253,7 +253,6 @@ void CVCMIServer::prepareToRestart() } * si = * gh->gs->initialOpts; - si->seedToBeUsed = si->seedPostInit = 0; setState(EServerState::LOBBY); if (si->campState) { diff --git a/test/game/CGameStateTest.cpp b/test/game/CGameStateTest.cpp index 00da8456f..af42ad9c8 100644 --- a/test/game/CGameStateTest.cpp +++ b/test/game/CGameStateTest.cpp @@ -142,9 +142,7 @@ public: StartInfo si; si.mapname = "anything";//does not matter, map service mocked si.difficulty = 0; - si.mapfileChecksum = 0; si.mode = EStartMode::NEW_GAME; - si.seedToBeUsed = 42; std::unique_ptr header = mapService.loadMapHeader(ResourcePath(si.mapname)); From 5178e4842e82f931075552d480053401d4c951d5 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 1 Jun 2024 16:38:41 +0000 Subject: [PATCH 037/100] Moved generation of new rumors to server --- lib/CGameInfoCallback.cpp | 6 ++--- lib/CMakeLists.txt | 2 ++ lib/gameState/CGameState.cpp | 32 +++++++---------------- lib/gameState/CGameState.h | 37 ++++---------------------- lib/gameState/RumorState.cpp | 29 +++++++++++++++++++++ lib/gameState/RumorState.h | 43 +++++++++++++++++++++++++++++++ lib/networkPacks/NetPacksLib.cpp | 4 +-- lib/networkPacks/PacksForClient.h | 3 +++ server/CGameHandler.cpp | 3 +++ 9 files changed, 100 insertions(+), 59 deletions(-) create mode 100644 lib/gameState/RumorState.cpp create mode 100644 lib/gameState/RumorState.h diff --git a/lib/CGameInfoCallback.cpp b/lib/CGameInfoCallback.cpp index 0d2833539..379db73fd 100644 --- a/lib/CGameInfoCallback.cpp +++ b/lib/CGameInfoCallback.cpp @@ -658,11 +658,11 @@ std::string CGameInfoCallback::getTavernRumor(const CGObjectInstance * townOrTav text.appendLocalString(EMetaText::GENERAL_TXT, 216); std::string extraText; - if(gs->rumor.type == RumorState::TYPE_NONE) + if(gs->currentRumor.type == RumorState::TYPE_NONE) return text.toString(); - auto rumor = gs->rumor.last[gs->rumor.type]; - switch(gs->rumor.type) + auto rumor = gs->currentRumor.last[gs->currentRumor.type]; + switch(gs->currentRumor.type) { case RumorState::TYPE_SPECIAL: text.replaceLocalString(EMetaText::GENERAL_TXT, rumor.first); diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 6eff0035c..42fc7531f 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -93,6 +93,7 @@ set(lib_MAIN_SRCS gameState/CGameState.cpp gameState/CGameStateCampaign.cpp gameState/InfoAboutArmy.cpp + gameState/RumorState.cpp gameState/TavernHeroesPool.cpp mapObjectConstructors/AObjectTypeHandler.cpp @@ -448,6 +449,7 @@ set(lib_MAIN_HEADERS gameState/CGameStateCampaign.h gameState/EVictoryLossCheckResult.h gameState/InfoAboutArmy.h + gameState/RumorState.h gameState/SThievesGuildInfo.h gameState/TavernHeroesPool.h gameState/TavernSlot.h diff --git a/lib/gameState/CGameState.cpp b/lib/gameState/CGameState.cpp index 73d91053e..fe4e4f414 100644 --- a/lib/gameState/CGameState.cpp +++ b/lib/gameState/CGameState.cpp @@ -1213,8 +1213,10 @@ int3 CGameState::guardingCreaturePosition (int3 pos) const return gs->map->guardingCreaturePositions[pos.z][pos.x][pos.y]; } -void CGameState::updateRumor() +RumorState CGameState::pickNewRumor() { + RumorState newRumor; + static const std::vector rumorTypes = {RumorState::TYPE_MAP, RumorState::TYPE_SPECIAL, RumorState::TYPE_RAND, RumorState::TYPE_RAND}; std::vector sRumorTypes = { RumorState::RUMOR_OBELISKS, RumorState::RUMOR_ARTIFACTS, RumorState::RUMOR_ARMY, RumorState::RUMOR_INCOME}; @@ -1224,11 +1226,11 @@ void CGameState::updateRumor() int rumorId = -1; int rumorExtra = -1; auto & rand = getRandomGenerator(); - rumor.type = *RandomGeneratorUtil::nextItem(rumorTypes, rand); + newRumor.type = *RandomGeneratorUtil::nextItem(rumorTypes, rand); do { - switch(rumor.type) + switch(newRumor.type) { case RumorState::TYPE_SPECIAL: { @@ -1266,13 +1268,13 @@ void CGameState::updateRumor() } case RumorState::TYPE_MAP: // Makes sure that map rumors only used if there enough rumors too choose from - if(!map->rumors.empty() && (map->rumors.size() > 1 || !rumor.last.count(RumorState::TYPE_MAP))) + if(!map->rumors.empty() && (map->rumors.size() > 1 || !currentRumor.last.count(RumorState::TYPE_MAP))) { rumorId = rand.nextInt((int)map->rumors.size() - 1); break; } else - rumor.type = RumorState::TYPE_RAND; + newRumor.type = RumorState::TYPE_RAND; [[fallthrough]]; case RumorState::TYPE_RAND: @@ -1282,7 +1284,9 @@ void CGameState::updateRumor() break; } } - while(!rumor.update(rumorId, rumorExtra)); + while(!newRumor.update(rumorId, rumorExtra)); + + return newRumor; } bool CGameState::isVisible(int3 pos, const std::optional & player) const @@ -1906,23 +1910,7 @@ CGHeroInstance * CGameState::getUsedHero(const HeroTypeID & hid) const return nullptr; } -bool RumorState::update(int id, int extra) -{ - if(vstd::contains(last, type)) - { - if(last[type].first != id) - { - last[type].first = id; - last[type].second = extra; - } - else - return false; - } - else - last[type] = std::make_pair(id, extra); - return true; -} TeamState::TeamState() { diff --git a/lib/gameState/CGameState.h b/lib/gameState/CGameState.h index efe61f927..f7814fde9 100644 --- a/lib/gameState/CGameState.h +++ b/lib/gameState/CGameState.h @@ -14,6 +14,8 @@ #include "../LoadProgress.h" #include "../ConstTransitivePtr.h" +#include "RumorState.h" + namespace boost { class shared_mutex; @@ -38,35 +40,6 @@ class CRandomGenerator; template class CApplier; class CBaseForGSApply; -struct DLL_LINKAGE RumorState -{ - enum ERumorType : ui8 - { - TYPE_NONE = 0, TYPE_RAND, TYPE_SPECIAL, TYPE_MAP - }; - - enum ERumorTypeSpecial : ui8 - { - RUMOR_OBELISKS = 208, - RUMOR_ARTIFACTS = 209, - RUMOR_ARMY = 210, - RUMOR_INCOME = 211, - RUMOR_GRAIL = 212 - }; - - ERumorType type; - std::map> last; - - RumorState(){type = TYPE_NONE;}; - bool update(int id, int extra); - - template void serialize(Handler &h) - { - h & type; - h & last; - } -}; - struct UpgradeInfo { CreatureID oldID; //creature to be upgraded @@ -115,7 +88,7 @@ public: std::map players; std::map teams; CBonusSystemNode globalEffects; - RumorState rumor; + RumorState currentRumor; static boost::shared_mutex mutex; @@ -135,7 +108,7 @@ public: void calculatePaths(const std::shared_ptr & config) override; int3 guardingCreaturePosition (int3 pos) const override; std::vector guardingCreatures (int3 pos) const; - void updateRumor(); + RumorState pickNewRumor(); /// Gets a artifact ID randomly and removes the selected artifact from this handler. ArtifactID pickRandomArtifact(vstd::RNG & rand, int flags); @@ -191,7 +164,7 @@ public: std::string oldStateOfRNG; h & oldStateOfRNG; } - h & rumor; + h & currentRumor; h & campaign; h & allocatedArtifacts; diff --git a/lib/gameState/RumorState.cpp b/lib/gameState/RumorState.cpp new file mode 100644 index 000000000..a424d81e5 --- /dev/null +++ b/lib/gameState/RumorState.cpp @@ -0,0 +1,29 @@ +/* + * RumorState.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#include "StdInc.h" +#include "RumorState.h" + +bool RumorState::update(int id, int extra) +{ + if(vstd::contains(last, type)) + { + if(last[type].first != id) + { + last[type].first = id; + last[type].second = extra; + } + else + return false; + } + else + last[type] = std::make_pair(id, extra); + + return true; +} diff --git a/lib/gameState/RumorState.h b/lib/gameState/RumorState.h new file mode 100644 index 000000000..571bd49c2 --- /dev/null +++ b/lib/gameState/RumorState.h @@ -0,0 +1,43 @@ +/* + * RumorState.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#pragma once + +VCMI_LIB_NAMESPACE_BEGIN + +struct DLL_LINKAGE RumorState +{ + enum ERumorType : ui8 + { + TYPE_NONE = 0, TYPE_RAND, TYPE_SPECIAL, TYPE_MAP + }; + + enum ERumorTypeSpecial : ui8 + { + RUMOR_OBELISKS = 208, + RUMOR_ARTIFACTS = 209, + RUMOR_ARMY = 210, + RUMOR_INCOME = 211, + RUMOR_GRAIL = 212 + }; + + ERumorType type; + std::map> last; + + RumorState(){type = TYPE_NONE;}; + bool update(int id, int extra); + + template void serialize(Handler &h) + { + h & type; + h & last; + } +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/networkPacks/NetPacksLib.cpp b/lib/networkPacks/NetPacksLib.cpp index 4b0557d28..279e05957 100644 --- a/lib/networkPacks/NetPacksLib.cpp +++ b/lib/networkPacks/NetPacksLib.cpp @@ -1984,8 +1984,8 @@ void NewTurn::applyGs(CGameState *gs) for(CGTownInstance* t : gs->map->towns) t->built = 0; - if(gs->getDate(Date::DAY_OF_WEEK) == 1) - gs->updateRumor(); + if(newRumor) + gs->currentRumor = *newRumor; } void SetObjectProperty::applyGs(CGameState * gs) const diff --git a/lib/networkPacks/PacksForClient.h b/lib/networkPacks/PacksForClient.h index d01ac40a7..d81a024c9 100644 --- a/lib/networkPacks/PacksForClient.h +++ b/lib/networkPacks/PacksForClient.h @@ -22,6 +22,7 @@ #include "../ResourceSet.h" #include "../TurnTimerInfo.h" #include "../gameState/EVictoryLossCheckResult.h" +#include "../gameState/RumorState.h" #include "../gameState/QuestInfo.h" #include "../gameState/TavernSlot.h" #include "../int3.h" @@ -1169,6 +1170,7 @@ struct DLL_LINKAGE NewTurn : public CPackForClient ui32 day = 0; ui8 specialWeek = 0; //weekType CreatureID creatureid; //for creature weeks + std::optional newRumor; // only on new weeks NewTurn() = default; @@ -1180,6 +1182,7 @@ struct DLL_LINKAGE NewTurn : public CPackForClient h & day; h & specialWeek; h & creatureid; + h & newRumor; } }; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index b54ea81f7..062a0da76 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -908,6 +908,9 @@ void CGameHandler::onNewTurn() } } + if (newWeek) + n.newRumor = gameState()->pickNewRumor(); + if (newMonth) { SetAvailableArtifacts saa; From c4b8778e8bcd16ee7b1e1482e158b18c4bfce7c8 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 1 Jun 2024 16:53:56 +0000 Subject: [PATCH 038/100] Remove unused code --- lib/IGameCallback.cpp | 1 - lib/gameState/CGameState.cpp | 3 +-- lib/registerTypes/RegisterTypesLobbyPacks.h | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/IGameCallback.cpp b/lib/IGameCallback.cpp index 443000de3..ce3a3fa98 100644 --- a/lib/IGameCallback.cpp +++ b/lib/IGameCallback.cpp @@ -14,7 +14,6 @@ #include "spells/CSpellHandler.h"// for CSpell #include "CSkillHandler.h"// for CSkill #include "CBonusTypeHandler.h" -#include "CRandomGenerator.h" #include "BattleFieldHandler.h" #include "ObstacleHandler.h" #include "bonuses/Limiters.h" diff --git a/lib/gameState/CGameState.cpp b/lib/gameState/CGameState.cpp index fe4e4f414..cc48de519 100644 --- a/lib/gameState/CGameState.cpp +++ b/lib/gameState/CGameState.cpp @@ -21,7 +21,6 @@ #include "../CGeneralTextHandler.h" #include "../CHeroHandler.h" #include "../CPlayerState.h" -#include "../CRandomGenerator.h" #include "../CStopWatch.h" #include "../GameSettings.h" #include "../StartInfo.h" @@ -311,7 +310,7 @@ void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRan const std::string templateName = options->getMapTemplate()->getName(); const std::string dt = vstd::getDateTimeISO8601Basic(std::time(nullptr)); - const std::string fileName = boost::str(boost::format("%s_%s_%d.vmap") % dt % templateName ); + const std::string fileName = boost::str(boost::format("%s_%s.vmap") % dt % templateName ); const auto fullPath = path / fileName; randomMap->name.appendRawString(boost::str(boost::format(" %s") % dt)); diff --git a/lib/registerTypes/RegisterTypesLobbyPacks.h b/lib/registerTypes/RegisterTypesLobbyPacks.h index c70c74eca..6859f5d9f 100644 --- a/lib/registerTypes/RegisterTypesLobbyPacks.h +++ b/lib/registerTypes/RegisterTypesLobbyPacks.h @@ -17,7 +17,6 @@ #include "../gameState/TavernHeroesPool.h" #include "../gameState/CGameStateCampaign.h" #include "../mapping/CMap.h" -#include "../CRandomGenerator.h" #include "../TerrainHandler.h" #include "../RiverHandler.h" #include "../RoadHandler.h" From 388d65d6b2486b225ec79e2f31b84e1bfbc195bf Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 12 Jul 2024 12:15:30 +0000 Subject: [PATCH 039/100] Fix merge --- lib/StartInfo.h | 2 +- lib/gameState/RumorState.cpp | 4 ++++ lib/mapObjects/MiscObjects.cpp | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/StartInfo.h b/lib/StartInfo.h index 68ed79642..bfef5b344 100644 --- a/lib/StartInfo.h +++ b/lib/StartInfo.h @@ -152,7 +152,7 @@ struct DLL_LINKAGE StartInfo : public Serializeable h & playerInfos; if (h.version < Handler::Version::REMOVE_LIB_RNG) { - uint32_t oldSeeds; + uint32_t oldSeeds = 0; h & oldSeeds; h & oldSeeds; h & oldSeeds; diff --git a/lib/gameState/RumorState.cpp b/lib/gameState/RumorState.cpp index a424d81e5..b4c1caeb2 100644 --- a/lib/gameState/RumorState.cpp +++ b/lib/gameState/RumorState.cpp @@ -10,6 +10,8 @@ #include "StdInc.h" #include "RumorState.h" +VCMI_LIB_NAMESPACE_BEGIN + bool RumorState::update(int id, int extra) { if(vstd::contains(last, type)) @@ -27,3 +29,5 @@ bool RumorState::update(int id, int extra) return true; } + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 0951edce7..03e2e1079 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -705,7 +705,7 @@ void CGWhirlpool::teleportDialogAnswered(const CGHeroInstance *hero, ui32 answer const auto * obj = cb->getObj(exit); std::set tiles = obj->getBlockedPos(); - dPos = *RandomGeneratorUtil::nextItem(tiles, cb->gameState()->getRandomGenerator())); + dPos = *RandomGeneratorUtil::nextItem(tiles, cb->gameState()->getRandomGenerator()); } cb->moveHero(hero->id, hero->convertFromVisitablePos(dPos), EMovementMode::MONOLITH); From d2839c8e52b4fc79b3d663d8e1e14a31c045e6a3 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 12 Jul 2024 15:10:03 +0000 Subject: [PATCH 040/100] Rewardable object randomization is now always server-sided --- client/Client.h | 2 ++ lib/CMakeLists.txt | 1 + lib/IGameCallback.h | 7 ++++ .../CRewardableConstructor.cpp | 28 ++++++++++----- .../CRewardableConstructor.h | 2 ++ lib/mapObjects/CGTownBuilding.cpp | 20 ++++++----- lib/mapObjects/CGTownBuilding.h | 2 ++ lib/mapObjects/CGTownInstance.cpp | 3 -- lib/mapObjects/CRewardableObject.cpp | 8 ++--- lib/mapObjects/IObjectInterface.h | 3 ++ lib/networkPacks/NetPackVisitor.h | 2 ++ lib/networkPacks/NetPacksLib.cpp | 16 +++++++++ lib/networkPacks/ObjProperty.h | 1 - lib/networkPacks/SetRewardableConfiguration.h | 35 +++++++++++++++++++ lib/registerTypes/RegisterTypesClientPacks.h | 3 ++ server/CGameHandler.cpp | 17 +++++++++ server/CGameHandler.h | 2 ++ 17 files changed, 126 insertions(+), 26 deletions(-) create mode 100644 lib/networkPacks/SetRewardableConfiguration.h diff --git a/client/Client.h b/client/Client.h index 68e116603..36bd516e5 100644 --- a/client/Client.h +++ b/client/Client.h @@ -214,6 +214,8 @@ public: void setObjPropertyValue(ObjectInstanceID objid, ObjProperty prop, int32_t value) override {}; void setObjPropertyID(ObjectInstanceID objid, ObjProperty prop, ObjPropertyID identifier) override {}; + void setRewardableObjectConfiguration(ObjectInstanceID objid, const Rewardable::Configuration & configuration) override {}; + void setRewardableObjectConfiguration(ObjectInstanceID townInstanceID, BuildingID buildingID, const Rewardable::Configuration & configuration) override{}; void showInfoDialog(InfoWindow * iw) override {}; void removeGUI() const; diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 42fc7531f..4b84c8411 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -529,6 +529,7 @@ set(lib_MAIN_HEADERS networkPacks/PacksForClientBattle.h networkPacks/PacksForLobby.h networkPacks/PacksForServer.h + networkPacks/SetRewardableConfiguration.h networkPacks/SetStackEffect.h networkPacks/StackLocation.h networkPacks/TradeItem.h diff --git a/lib/IGameCallback.h b/lib/IGameCallback.h index 493091422..5948b83a0 100644 --- a/lib/IGameCallback.h +++ b/lib/IGameCallback.h @@ -37,6 +37,11 @@ namespace spells class Caster; } +namespace Rewardable +{ + struct Configuration; +} + #if SCRIPTING_ENABLED namespace scripting { @@ -79,6 +84,8 @@ class DLL_LINKAGE IGameEventCallback { public: virtual void setObjPropertyValue(ObjectInstanceID objid, ObjProperty prop, int32_t value = 0) = 0; + virtual void setRewardableObjectConfiguration(ObjectInstanceID mapObjectID, const Rewardable::Configuration & configuration) = 0; + virtual void setRewardableObjectConfiguration(ObjectInstanceID townInstanceID, BuildingID buildingID, const Rewardable::Configuration & configuration) = 0; virtual void setObjPropertyID(ObjectInstanceID objid, ObjProperty prop, ObjPropertyID identifier) = 0; virtual void showInfoDialog(InfoWindow * iw) = 0; diff --git a/lib/mapObjectConstructors/CRewardableConstructor.cpp b/lib/mapObjectConstructors/CRewardableConstructor.cpp index c2e7422e9..28d98d50a 100644 --- a/lib/mapObjectConstructors/CRewardableConstructor.cpp +++ b/lib/mapObjectConstructors/CRewardableConstructor.cpp @@ -40,19 +40,29 @@ CGObjectInstance * CRewardableConstructor::create(IGameCallback * cb, std::share return ret; } +Rewardable::Configuration CRewardableConstructor::generateConfiguration(IGameCallback * cb, vstd::RNG & rand, MapObjectID objectID) const +{ + Rewardable::Configuration result; + objectInfo.configureObject(result, rand, cb); + + for(auto & rewardInfo : result.info) + { + for (auto & bonus : rewardInfo.reward.bonuses) + { + bonus.source = BonusSource::OBJECT_TYPE; + bonus.sid = BonusSourceID(objectID); + } + } + + return result; +} + void CRewardableConstructor::configureObject(CGObjectInstance * object, vstd::RNG & rng) const { if(auto * rewardableObject = dynamic_cast(object)) { - objectInfo.configureObject(rewardableObject->configuration, rng, object->cb); - for(auto & rewardInfo : rewardableObject->configuration.info) - { - for (auto & bonus : rewardInfo.reward.bonuses) - { - bonus.source = BonusSource::OBJECT_TYPE; - bonus.sid = BonusSourceID(rewardableObject->ID); - } - } + rewardableObject->configuration = generateConfiguration(object->cb, rng, object->ID); + if (rewardableObject->configuration.info.empty()) { if (objectInfo.getParameters()["rewards"].isNull()) diff --git a/lib/mapObjectConstructors/CRewardableConstructor.h b/lib/mapObjectConstructors/CRewardableConstructor.h index a5b9d5423..eab0e500a 100644 --- a/lib/mapObjectConstructors/CRewardableConstructor.h +++ b/lib/mapObjectConstructors/CRewardableConstructor.h @@ -30,6 +30,8 @@ public: void configureObject(CGObjectInstance * object, vstd::RNG & rng) const override; std::unique_ptr getObjectInfo(std::shared_ptr tmpl) const override; + + Rewardable::Configuration generateConfiguration(IGameCallback * cb, vstd::RNG & rand, MapObjectID objectID) const; }; VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/CGTownBuilding.cpp b/lib/mapObjects/CGTownBuilding.cpp index 483187c0a..8e58f1a57 100644 --- a/lib/mapObjects/CGTownBuilding.cpp +++ b/lib/mapObjects/CGTownBuilding.cpp @@ -328,11 +328,16 @@ CTownRewardableBuilding::CTownRewardableBuilding(const BuildingID & index, Build void CTownRewardableBuilding::initObj(vstd::RNG & rand) { assert(town && town->town); + configuration = generateConfiguration(rand); +} +Rewardable::Configuration CTownRewardableBuilding::generateConfiguration(vstd::RNG & rand) const +{ + Rewardable::Configuration result; auto building = town->town->buildings.at(bID); - building->rewardableObjectInfo.configureObject(configuration, rand, cb); - for(auto & rewardInfo : configuration.info) + building->rewardableObjectInfo.configureObject(result, rand, cb); + for(auto & rewardInfo : result.info) { for (auto & bonus : rewardInfo.reward.bonuses) { @@ -340,16 +345,16 @@ void CTownRewardableBuilding::initObj(vstd::RNG & rand) bonus.sid = BonusSourceID(building->getUniqueTypeID()); } } + return result; } void CTownRewardableBuilding::newTurn(vstd::RNG & rand) const { if (configuration.resetParameters.period != 0 && cb->getDate(Date::DAY) > 1 && ((cb->getDate(Date::DAY)-1) % configuration.resetParameters.period) == 0) { - if(configuration.resetParameters.rewards) - { - cb->setObjPropertyValue(town->id, ObjProperty::REWARD_RANDOMIZE, indexOnTV); - } + auto newConfiguration = generateConfiguration(rand); + cb->setRewardableObjectConfiguration(town->id, bID, newConfiguration); + if(configuration.resetParameters.visitors) { cb->setObjPropertyValue(town->id, ObjProperty::STRUCTURE_CLEAR_VISITORS, indexOnTV); @@ -367,9 +372,6 @@ void CTownRewardableBuilding::setProperty(ObjProperty what, ObjPropertyID identi case ObjProperty::STRUCTURE_CLEAR_VISITORS: visitors.clear(); break; - case ObjProperty::REWARD_RANDOMIZE: - initObj(cb->gameState()->getRandomGenerator()); - break; case ObjProperty::REWARD_SELECT: selectedReward = identifier.getNum(); break; diff --git a/lib/mapObjects/CGTownBuilding.h b/lib/mapObjects/CGTownBuilding.h index 3ec7c11b3..9ddf4e616 100644 --- a/lib/mapObjects/CGTownBuilding.h +++ b/lib/mapObjects/CGTownBuilding.h @@ -118,6 +118,8 @@ class DLL_LINKAGE CTownRewardableBuilding : public CGTownBuilding, public Reward bool wasVisitedBefore(const CGHeroInstance * contextHero) const; void grantReward(ui32 rewardID, const CGHeroInstance * hero) const; + + Rewardable::Configuration generateConfiguration(vstd::RNG & rand) const; public: void setProperty(ObjProperty what, ObjPropertyID identifier) override; diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index 1bdc99b18..35eeecd9f 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -71,9 +71,6 @@ void CGTownInstance::setPropertyDer(ObjProperty what, ObjPropertyID identifier) case ObjProperty::BONUS_VALUE_SECOND: bonusValue.second = identifier.getNum(); break; - case ObjProperty::REWARD_RANDOMIZE: - bonusingBuildings[identifier.getNum()]->setProperty(ObjProperty::REWARD_RANDOMIZE, NumericID(0)); - break; } } CGTownInstance::EFortLevel CGTownInstance::fortLevel() const //0 - none, 1 - fort, 2 - citadel, 3 - castle diff --git a/lib/mapObjects/CRewardableObject.cpp b/lib/mapObjects/CRewardableObject.cpp index 396e93637..80f852c08 100644 --- a/lib/mapObjects/CRewardableObject.cpp +++ b/lib/mapObjects/CRewardableObject.cpp @@ -16,6 +16,7 @@ #include "../IGameCallback.h" #include "../mapObjectConstructors/AObjectTypeHandler.h" #include "../mapObjectConstructors/CObjectClassesHandler.h" +#include "../mapObjectConstructors/CRewardableConstructor.h" #include "../mapObjects/CGHeroInstance.h" #include "../networkPacks/PacksForClient.h" #include "../serializer/JsonSerializeFormat.h" @@ -377,9 +378,6 @@ void CRewardableObject::setPropertyDer(ObjProperty what, ObjPropertyID identifie { switch (what) { - case ObjProperty::REWARD_RANDOMIZE: - initObj(cb->gameState()->getRandomGenerator()); - break; case ObjProperty::REWARD_SELECT: selectedReward = identifier.getNum(); break; @@ -395,7 +393,9 @@ void CRewardableObject::newTurn(vstd::RNG & rand) const { if (configuration.resetParameters.rewards) { - cb->setObjPropertyValue(id, ObjProperty::REWARD_RANDOMIZE, 0); + auto handler = std::dynamic_pointer_cast(getObjectHandler()); + auto newConfiguration = handler->generateConfiguration(cb, rand, ID); + cb->setRewardableObjectConfiguration(id, newConfiguration); } if (configuration.resetParameters.visitors) { diff --git a/lib/mapObjects/IObjectInterface.h b/lib/mapObjects/IObjectInterface.h index 376b72118..82a798e72 100644 --- a/lib/mapObjects/IObjectInterface.h +++ b/lib/mapObjects/IObjectInterface.h @@ -50,6 +50,9 @@ public: virtual void onHeroVisit(const CGHeroInstance * h) const; virtual void onHeroLeave(const CGHeroInstance * h) const; + + /// Called on new turn by server. This method can not modify object state on its own + /// Instead all changes must be propagated via netpacks virtual void newTurn(vstd::RNG & rand) const; virtual void initObj(vstd::RNG & rand); //synchr virtual void pickRandomObject(vstd::RNG & rand); diff --git a/lib/networkPacks/NetPackVisitor.h b/lib/networkPacks/NetPackVisitor.h index 73d3d8d04..0f277982b 100644 --- a/lib/networkPacks/NetPackVisitor.h +++ b/lib/networkPacks/NetPackVisitor.h @@ -13,6 +13,7 @@ #include "PacksForClientBattle.h" #include "PacksForServer.h" #include "PacksForLobby.h" +#include "SetRewardableConfiguration.h" #include "SetStackEffect.h" VCMI_LIB_NAMESPACE_BEGIN @@ -34,6 +35,7 @@ public: virtual void visitTurnTimeUpdate(TurnTimeUpdate & pack) {} virtual void visitGamePause(GamePause & pack) {} virtual void visitEntitiesChanged(EntitiesChanged & pack) {} + virtual void visitSetRewardableConfiguration(SetRewardableConfiguration & pack) {} virtual void visitSetResources(SetResources & pack) {} virtual void visitSetPrimSkill(SetPrimSkill & pack) {} virtual void visitSetSecSkill(SetSecSkill & pack) {} diff --git a/lib/networkPacks/NetPacksLib.cpp b/lib/networkPacks/NetPacksLib.cpp index 279e05957..c2b6946c8 100644 --- a/lib/networkPacks/NetPacksLib.cpp +++ b/lib/networkPacks/NetPacksLib.cpp @@ -12,6 +12,7 @@ #include "PacksForClient.h" #include "PacksForClientBattle.h" #include "PacksForServer.h" +#include "SetRewardableConfiguration.h" #include "StackLocation.h" #include "PacksForLobby.h" #include "SetStackEffect.h" @@ -123,6 +124,11 @@ void EntitiesChanged::visitTyped(ICPackVisitor & visitor) visitor.visitEntitiesChanged(*this); } +void SetRewardableConfiguration::visitTyped(ICPackVisitor & visitor) +{ + visitor.visitSetRewardableConfiguration(*this); +} + void SetResources::visitTyped(ICPackVisitor & visitor) { visitor.visitSetResources(*this); @@ -2479,6 +2485,16 @@ void EntitiesChanged::applyGs(CGameState * gs) gs->updateEntity(change.metatype, change.entityIndex, change.data); } +void SetRewardableConfiguration::applyGs(CGameState * gs) +{ + auto * objectPtr = gs->getObjInstance(objectID); + auto * rewardablePtr = dynamic_cast(objectPtr); + + assert(rewardablePtr); + + rewardablePtr->configuration = configuration; +} + const CArtifactInstance * ArtSlotInfo::getArt() const { if(locked) diff --git a/lib/networkPacks/ObjProperty.h b/lib/networkPacks/ObjProperty.h index 67029d375..a57c68120 100644 --- a/lib/networkPacks/ObjProperty.h +++ b/lib/networkPacks/ObjProperty.h @@ -47,7 +47,6 @@ enum class ObjProperty : int8_t BANK_CLEAR, //object with reward - REWARD_RANDOMIZE, REWARD_SELECT, REWARD_CLEARED }; diff --git a/lib/networkPacks/SetRewardableConfiguration.h b/lib/networkPacks/SetRewardableConfiguration.h new file mode 100644 index 000000000..57d9972dc --- /dev/null +++ b/lib/networkPacks/SetRewardableConfiguration.h @@ -0,0 +1,35 @@ +/* + * SetRewardableConfiguration.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#pragma once + +#include "NetPacksBase.h" + +#include "../rewardable/Configuration.h" + +VCMI_LIB_NAMESPACE_BEGIN + +struct DLL_LINKAGE SetRewardableConfiguration : public CPackForClient +{ + void applyGs(CGameState * gs); + void visitTyped(ICPackVisitor & visitor) override; + + ObjectInstanceID objectID; + BuildingID buildingID; + Rewardable::Configuration configuration; + + template void serialize(Handler & h) + { + h & objectID; + h & buildingID; + h & configuration; + } +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/registerTypes/RegisterTypesClientPacks.h b/lib/registerTypes/RegisterTypesClientPacks.h index d989facd6..77124eacf 100644 --- a/lib/registerTypes/RegisterTypesClientPacks.h +++ b/lib/registerTypes/RegisterTypesClientPacks.h @@ -12,6 +12,7 @@ #include "../networkPacks/PacksForClient.h" #include "../networkPacks/PacksForClientBattle.h" #include "../networkPacks/SetStackEffect.h" +#include "../networkPacks/SetRewardableConfiguration.h" VCMI_LIB_NAMESPACE_BEGIN @@ -119,6 +120,8 @@ void registerTypesClientPacks(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); + + s.template registerType(); } VCMI_LIB_NAMESPACE_END diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 062a0da76..ed5fe35dd 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -4374,6 +4374,23 @@ void CGameHandler::setObjPropertyID(ObjectInstanceID objid, ObjProperty prop, Ob sendAndApply(&sob); } +void CGameHandler::setRewardableObjectConfiguration(ObjectInstanceID objid, const Rewardable::Configuration & configuration) +{ + SetRewardableConfiguration srb; + srb.objectID = objid; + srb.configuration = configuration; + sendAndApply(&srb); +} + +void CGameHandler::setRewardableObjectConfiguration(ObjectInstanceID townInstanceID, BuildingID buildingID, const Rewardable::Configuration & configuration) +{ + SetRewardableConfiguration srb; + srb.objectID = townInstanceID; + srb.buildingID = buildingID; + srb.configuration = configuration; + sendAndApply(&srb); +} + void CGameHandler::showInfoDialog(InfoWindow * iw) { sendAndApply(iw); diff --git a/server/CGameHandler.h b/server/CGameHandler.h index d6db0a878..ed413521c 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -162,6 +162,8 @@ public: bool isVisitCoveredByAnotherQuery(const CGObjectInstance *obj, const CGHeroInstance *hero) override; void setObjPropertyValue(ObjectInstanceID objid, ObjProperty prop, int32_t value) override; void setObjPropertyID(ObjectInstanceID objid, ObjProperty prop, ObjPropertyID identifier) override; + void setRewardableObjectConfiguration(ObjectInstanceID objid, const Rewardable::Configuration & configuration) override; + void setRewardableObjectConfiguration(ObjectInstanceID townInstanceID, BuildingID buildingID, const Rewardable::Configuration & configuration) override; void showInfoDialog(InfoWindow * iw) override; ////////////////////////////////////////////////////////////////////////// From b07408e9842edbde1079e2cbd98b36377249c086 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 12 Jul 2024 16:51:27 +0000 Subject: [PATCH 041/100] New objects are now created and initialized on server and sent to client --- client/Client.h | 2 +- client/NetPacksClient.cpp | 2 +- lib/IGameCallback.h | 2 +- lib/mapObjects/CGHeroInstance.cpp | 7 +-- lib/mapObjects/CGHeroInstance.h | 1 - lib/networkPacks/NetPacksLib.cpp | 55 ++---------------- lib/networkPacks/PacksForClient.h | 12 +--- lib/spells/AdventureSpellMechanics.cpp | 7 +-- lib/spells/ISpellMechanics.h | 1 + server/CGameHandler.cpp | 75 ++++++++++++++++++++++--- server/CGameHandler.h | 9 ++- server/ServerSpellCastEnvironment.cpp | 5 ++ server/ServerSpellCastEnvironment.h | 1 + server/processors/HeroPoolProcessor.cpp | 3 +- 14 files changed, 95 insertions(+), 87 deletions(-) diff --git a/client/Client.h b/client/Client.h index 36bd516e5..49a3bcc47 100644 --- a/client/Client.h +++ b/client/Client.h @@ -161,7 +161,7 @@ public: void changeSpells(const CGHeroInstance * hero, bool give, const std::set & spells) override {}; bool removeObject(const CGObjectInstance * obj, const PlayerColor & initiator) override {return false;}; - void createObject(const int3 & visitablePosition, const PlayerColor & initiator, MapObjectID type, MapObjectSubID subtype) override {}; + void createBoat(const int3 & visitablePosition, BoatId type, PlayerColor initiator) override {}; void setOwner(const CGObjectInstance * obj, PlayerColor owner) override {}; void giveExperience(const CGHeroInstance * hero, TExpType val) override {}; void changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs = false) override {}; diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index 3b1f31beb..c7755dd82 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -1048,7 +1048,7 @@ void ApplyClientNetPackVisitor::visitNewObject(NewObject & pack) { cl.invalidatePaths(); - const CGObjectInstance *obj = cl.getObj(pack.createdObjectID); + const CGObjectInstance *obj = pack.newObject; if(CGI->mh) CGI->mh->onObjectFadeIn(obj, pack.initiator); diff --git a/lib/IGameCallback.h b/lib/IGameCallback.h index 5948b83a0..887f98818 100644 --- a/lib/IGameCallback.h +++ b/lib/IGameCallback.h @@ -92,7 +92,7 @@ public: virtual void changeSpells(const CGHeroInstance * hero, bool give, const std::set &spells)=0; virtual bool removeObject(const CGObjectInstance * obj, const PlayerColor & initiator) = 0; - virtual void createObject(const int3 & visitablePosition, const PlayerColor & initiator, MapObjectID type, MapObjectSubID subtype) = 0; + virtual void createBoat(const int3 & visitablePosition, BoatId type, PlayerColor initiator) = 0; virtual void setOwner(const CGObjectInstance * objid, PlayerColor owner)=0; virtual void giveExperience(const CGHeroInstance * hero, TExpType val) =0; virtual void changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs=false)=0; diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index e33fb711f..6d62dd31f 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -532,7 +532,7 @@ void CGHeroInstance::onHeroVisit(const CGHeroInstance * h) const if (!boat) { //Create a new boat for hero - cb->createObject(boatPos, h->getOwner(), Obj::BOAT, getBoatType().getNum()); + cb->createBoat(boatPos, getBoatType(), h->getOwner()); boatId = cb->getTopObj(boatPos)->id; } } @@ -614,11 +614,6 @@ void CGHeroInstance::pickRandomObject(vstd::RNG & rand) this->subID = oldSubID; } -void CGHeroInstance::initObj(vstd::RNG & rand) -{ - -} - void CGHeroInstance::recreateSecondarySkillsBonuses() { auto secondarySkillsBonuses = getBonuses(Selector::sourceType()(BonusSource::SECONDARY_SKILL)); diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index cc8dcbe8a..ff2ea4281 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -293,7 +293,6 @@ public: void boatDeserializationFix(); void deserializationFix(); - void initObj(vstd::RNG & rand) override; void pickRandomObject(vstd::RNG & rand) override; void onHeroVisit(const CGHeroInstance * h) const override; std::string getObjectName() const override; diff --git a/lib/networkPacks/NetPacksLib.cpp b/lib/networkPacks/NetPacksLib.cpp index c2b6946c8..879499e60 100644 --- a/lib/networkPacks/NetPacksLib.cpp +++ b/lib/networkPacks/NetPacksLib.cpp @@ -1421,7 +1421,6 @@ void HeroRecruited::applyGs(CGameState * gs) const h->setOwner(player); h->pos = tile; - h->initObj(gs->getRandomGenerator()); if(h->id == ObjectInstanceID()) { @@ -1475,59 +1474,13 @@ void GiveHero::applyGs(CGameState * gs) const void NewObject::applyGs(CGameState *gs) { - TerrainId terrainType = ETerrainId::NONE; + newObject->id = ObjectInstanceID(static_cast(gs->map->objects.size())); - if (!gs->isInTheMap(targetPos)) - { - logGlobal->error("Attempt to create object outside map at %s!", targetPos.toString()); - return; - } - - const TerrainTile & t = gs->map->getTile(targetPos); - terrainType = t.terType->getId(); - - auto handler = VLC->objtypeh->getHandlerFor(ID, subID); - - CGObjectInstance * o = handler->create(gs->callback, nullptr); - handler->configureObject(o, gs->getRandomGenerator()); - assert(o->ID == this->ID); - - if (ID == Obj::MONSTER) //probably more options will be needed - { - //CStackInstance hlp; - auto * cre = dynamic_cast(o); - //cre->slots[0] = hlp; - assert(cre); - cre->notGrowingTeam = cre->neverFlees = false; - cre->character = 2; - cre->gainedArtifact = ArtifactID::NONE; - cre->identifier = -1; - cre->addToSlot(SlotID(0), new CStackInstance(subID.getNum(), -1)); //add placeholder stack - } - - assert(!handler->getTemplates(terrainType).empty()); - if (handler->getTemplates().empty()) - { - logGlobal->error("Attempt to create object (%d %d) with no templates!", ID, subID.getNum()); - return; - } - - if (!handler->getTemplates(terrainType).empty()) - o->appearance = handler->getTemplates(terrainType).front(); - else - o->appearance = handler->getTemplates().front(); - - o->id = ObjectInstanceID(static_cast(gs->map->objects.size())); - o->pos = targetPos + o->getVisitableOffset(); - - gs->map->objects.emplace_back(o); - gs->map->addBlockVisTiles(o); - o->initObj(gs->getRandomGenerator()); + gs->map->objects.emplace_back(newObject); + gs->map->addBlockVisTiles(newObject); gs->map->calculateGuardingGreaturePositions(); - createdObjectID = o->id; - - logGlobal->debug("Added object id=%d; address=%x; name=%s", o->id, (intptr_t)o, o->getObjectName()); + logGlobal->debug("Added object id=%d; address=%x; name=%s", newObject->id, (intptr_t)newObject, newObject->getObjectName()); } void NewArtifact::applyGs(CGameState *gs) diff --git a/lib/networkPacks/PacksForClient.h b/lib/networkPacks/PacksForClient.h index d81a024c9..7ad75413b 100644 --- a/lib/networkPacks/PacksForClient.h +++ b/lib/networkPacks/PacksForClient.h @@ -787,23 +787,15 @@ struct DLL_LINKAGE NewObject : public CPackForClient void applyGs(CGameState * gs); /// Object ID to create - MapObjectID ID; - /// Object secondary ID to create - MapObjectSubID subID; - /// Position of visitable tile of created object - int3 targetPos; + CGObjectInstance * newObject; /// Which player initiated creation of this object PlayerColor initiator; - ObjectInstanceID createdObjectID; //used locally, filled during applyGs - void visitTyped(ICPackVisitor & visitor) override; template void serialize(Handler & h) { - h & ID; - subID.serializeIdentifier(h, ID); - h & targetPos; + h & newObject; h & initiator; } }; diff --git a/lib/spells/AdventureSpellMechanics.cpp b/lib/spells/AdventureSpellMechanics.cpp index dc38db794..1d559f0d9 100644 --- a/lib/spells/AdventureSpellMechanics.cpp +++ b/lib/spells/AdventureSpellMechanics.cpp @@ -238,12 +238,7 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment } else //create boat { - NewObject no; - no.ID = Obj::BOAT; - no.subID = BoatId::NECROPOLIS; - no.targetPos = summonPos; - no.initiator = parameters.caster->getCasterOwner(); - env->apply(&no); + env->createBoat(summonPos, BoatId::NECROPOLIS, parameters.caster->getCasterOwner()); } return ESpellCastResult::OK; } diff --git a/lib/spells/ISpellMechanics.h b/lib/spells/ISpellMechanics.h index 5bd118dac..ea150497d 100644 --- a/lib/spells/ISpellMechanics.h +++ b/lib/spells/ISpellMechanics.h @@ -58,6 +58,7 @@ public: virtual const CMap * getMap() const = 0; virtual const CGameInfoCallback * getCb() const = 0; + virtual void createBoat(const int3 & visitablePosition, BoatId type, PlayerColor initiator) = 0; virtual bool moveHero(ObjectInstanceID hid, int3 dst, EMovementMode mode) = 0; //TODO: remove virtual void genericQuery(Query * request, PlayerColor color, std::function)> callback) = 0;//TODO: type safety on query, use generic query packet when implemented diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index ed5fe35dd..615b209be 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -50,9 +50,12 @@ #include "../lib/mapping/CMap.h" #include "../lib/mapping/CMapService.h" +#include "../lib/mapObjects/CGCreature.h" #include "../lib/mapObjects/CGMarket.h" #include "../lib/mapObjects/CGTownInstance.h" #include "../lib/mapObjects/MiscObjects.h" +#include "../lib/mapObjectConstructors/AObjectTypeHandler.h" +#include "../lib/mapObjectConstructors/CObjectClassesHandler.h" #include "../lib/modding/ModIncompatibility.h" #include "../lib/networkPacks/StackLocation.h" #include "../lib/pathfinder/CPathfinder.h" @@ -3684,7 +3687,7 @@ bool CGameHandler::buildBoat(ObjectInstanceID objid, PlayerColor playerID) } giveResources(playerID, -boatCost); - createObject(tile, playerID, Obj::BOAT, obj->getBoatType().getNum()); + createBoat(tile, obj->getBoatType(), playerID); return true; } @@ -3809,7 +3812,7 @@ bool CGameHandler::dig(const CGHeroInstance *h) if (h->diggingStatus() != EDiggingStatus::CAN_DIG) //checks for terrain and movement COMPLAIN_RETF("Hero cannot dig (error code %d)!", static_cast(h->diggingStatus())); - createObject(h->visitablePos(), h->getOwner(), Obj::HOLE, 0 ); + createHole(h->visitablePos(), h->getOwner()); //take MPs SetMovePoints smp; @@ -4214,7 +4217,7 @@ void CGameHandler::spawnWanderingMonsters(CreatureID creatureID) { auto count = cre->getRandomAmount(std::rand); - createObject(*tile, PlayerColor::NEUTRAL, Obj::MONSTER, creatureID); + createWanderingMonster(*tile, creatureID); auto monsterId = getTopObj(*tile)->id; setObjPropertyValue(monsterId, ObjProperty::MONSTER_COUNT, count); @@ -4413,13 +4416,71 @@ scripting::Pool * CGameHandler::getGlobalContextPool() const //} #endif -void CGameHandler::createObject(const int3 & visitablePosition, const PlayerColor & initiator, MapObjectID type, MapObjectSubID subtype) + +CGObjectInstance * CGameHandler::createNewObject(const int3 & visitablePosition, MapObjectID objectID, MapObjectSubID subID) { + TerrainId terrainType = ETerrainId::NONE; + + if (!gs->isInTheMap(visitablePosition)) + throw std::runtime_error("Attempt to create object outside map at " + visitablePosition.toString()); + + const TerrainTile & t = gs->map->getTile(visitablePosition); + terrainType = t.terType->getId(); + + auto handler = VLC->objtypeh->getHandlerFor(objectID, subID); + + CGObjectInstance * o = handler->create(gs->callback, nullptr); + handler->configureObject(o, getRandomGenerator()); + assert(o->ID == objectID); + + assert(!handler->getTemplates(terrainType).empty()); + if (handler->getTemplates().empty()) + throw std::runtime_error("Attempt to create object (" + std::to_string(objectID) + ", " + std::to_string(subID.getNum()) + ") with no templates!"); + + if (!handler->getTemplates(terrainType).empty()) + o->appearance = handler->getTemplates(terrainType).front(); + else + o->appearance = handler->getTemplates().front(); + + + o->pos = visitablePosition + o->getVisitableOffset(); + return o; +} + +void CGameHandler::createWanderingMonster(const int3 & visitablePosition, CreatureID creature) +{ + auto createdObject = createNewObject(visitablePosition, Obj::MONSTER, creature); + + auto * cre = dynamic_cast(createdObject); + assert(cre); + cre->notGrowingTeam = cre->neverFlees = false; + cre->character = 2; + cre->gainedArtifact = ArtifactID::NONE; + cre->identifier = -1; + cre->addToSlot(SlotID(0), new CStackInstance(creature, -1)); //add placeholder stack + + newObject(createdObject, PlayerColor::NEUTRAL); +} + +void CGameHandler::createBoat(const int3 & visitablePosition, BoatId type, PlayerColor initiator) +{ + auto createdObject = createNewObject(visitablePosition, Obj::BOAT, type); + newObject(createdObject, initiator); +} + +void CGameHandler::createHole(const int3 & visitablePosition, PlayerColor initiator) +{ + auto createdObject = createNewObject(visitablePosition, Obj::HOLE, 0); + newObject(createdObject, initiator); +} + +void CGameHandler::newObject(CGObjectInstance * object, PlayerColor initiator) +{ + object->initObj(gs->getRandomGenerator()); + NewObject no; - no.ID = type; - no.subID = subtype; + no.newObject = object; no.initiator = initiator; - no.targetPos = visitablePosition; sendAndApply(&no); } diff --git a/server/CGameHandler.h b/server/CGameHandler.h index ed413521c..b58f2ae5e 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -92,6 +92,14 @@ public: bool isAllowedExchange(ObjectInstanceID id1, ObjectInstanceID id2); void giveSpells(const CGTownInstance *t, const CGHeroInstance *h); + // Helpers to create new object of specified type + + CGObjectInstance * createNewObject(const int3 & visitablePosition, MapObjectID objectID, MapObjectSubID subID); + void createWanderingMonster(const int3 & visitablePosition, CreatureID creature); + void createBoat(const int3 & visitablePosition, BoatId type, PlayerColor initiator) override; + void createHole(const int3 & visitablePosition, PlayerColor initiator); + void newObject(CGObjectInstance * object, PlayerColor initiator); + explicit CGameHandler(CVCMIServer * lobby); ~CGameHandler(); @@ -100,7 +108,6 @@ public: //do sth void changeSpells(const CGHeroInstance * hero, bool give, const std::set &spells) override; bool removeObject(const CGObjectInstance * obj, const PlayerColor & initiator) override; - void createObject(const int3 & visitablePosition, const PlayerColor & initiator, MapObjectID type, MapObjectSubID subtype) override; void setOwner(const CGObjectInstance * obj, PlayerColor owner) override; void giveExperience(const CGHeroInstance * hero, TExpType val) override; void changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs=false) override; diff --git a/server/ServerSpellCastEnvironment.cpp b/server/ServerSpellCastEnvironment.cpp index 035617d6e..9213aef7b 100644 --- a/server/ServerSpellCastEnvironment.cpp +++ b/server/ServerSpellCastEnvironment.cpp @@ -94,6 +94,11 @@ bool ServerSpellCastEnvironment::moveHero(ObjectInstanceID hid, int3 dst, EMovem return gh->moveHero(hid, dst, mode, false); } +void ServerSpellCastEnvironment::createBoat(const int3 & visitablePosition, BoatId type, PlayerColor initiator) +{ + return gh->createBoat(visitablePosition, type, initiator); +} + void ServerSpellCastEnvironment::genericQuery(Query * request, PlayerColor color, std::function)> callback) { auto query = std::make_shared(gh, color, callback); diff --git a/server/ServerSpellCastEnvironment.h b/server/ServerSpellCastEnvironment.h index 307f858ea..e2aa9b796 100644 --- a/server/ServerSpellCastEnvironment.h +++ b/server/ServerSpellCastEnvironment.h @@ -37,6 +37,7 @@ public: const CMap * getMap() const override; const CGameInfoCallback * getCb() const override; bool moveHero(ObjectInstanceID hid, int3 dst, EMovementMode mode) override; + void createBoat(const int3 & visitablePosition, BoatId type, PlayerColor initiator) override; void genericQuery(Query * request, PlayerColor color, std::function)> callback) override; private: CGameHandler * gh; diff --git a/server/processors/HeroPoolProcessor.cpp b/server/processors/HeroPoolProcessor.cpp index c1f4f8382..7e8d327ba 100644 --- a/server/processors/HeroPoolProcessor.cpp +++ b/server/processors/HeroPoolProcessor.cpp @@ -227,8 +227,7 @@ bool HeroPoolProcessor::hireHero(const ObjectInstanceID & objectID, const HeroTy if(gameHandler->getTile(targetPos)->isWater() && !recruitedHero->boat) { //Create a new boat for hero - gameHandler->createObject(targetPos, player, Obj::BOAT, recruitedHero->getBoatType().getNum()); - + gameHandler->createBoat(targetPos, recruitedHero->getBoatType(), player); hr.boatId = gameHandler->getTopObj(targetPos)->id; } From 1c63fefe0270ee783830952c46728d6fcb640c2b Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 13 Jul 2024 15:46:55 +0000 Subject: [PATCH 042/100] Moved banks randomization to server-side with client netpack --- client/Client.h | 2 ++ lib/IGameCallback.h | 2 ++ .../CBankInstanceConstructor.cpp | 13 ++++++++----- .../CBankInstanceConstructor.h | 4 +++- lib/mapObjects/CBank.cpp | 13 +++++++------ lib/mapping/CMapEditManager.cpp | 3 ++- lib/mapping/CMapEditManager.h | 1 + lib/networkPacks/NetPackVisitor.h | 1 + lib/networkPacks/NetPacksLib.cpp | 16 ++++++++++++++++ lib/networkPacks/ObjProperty.h | 1 - lib/networkPacks/SetRewardableConfiguration.h | 16 ++++++++++++++++ lib/registerTypes/RegisterTypesClientPacks.h | 1 + server/CGameHandler.cpp | 8 ++++++++ server/CGameHandler.h | 1 + 14 files changed, 68 insertions(+), 14 deletions(-) diff --git a/client/Client.h b/client/Client.h index 49a3bcc47..276aafb51 100644 --- a/client/Client.h +++ b/client/Client.h @@ -25,6 +25,7 @@ class BinaryDeserializer; class BinarySerializer; class BattleAction; class BattleInfo; +struct BankConfig; template class CApplier; @@ -214,6 +215,7 @@ public: void setObjPropertyValue(ObjectInstanceID objid, ObjProperty prop, int32_t value) override {}; void setObjPropertyID(ObjectInstanceID objid, ObjProperty prop, ObjPropertyID identifier) override {}; + void setBankObjectConfiguration(ObjectInstanceID objid, const BankConfig & configuration) override {}; void setRewardableObjectConfiguration(ObjectInstanceID objid, const Rewardable::Configuration & configuration) override {}; void setRewardableObjectConfiguration(ObjectInstanceID townInstanceID, BuildingID buildingID, const Rewardable::Configuration & configuration) override{}; diff --git a/lib/IGameCallback.h b/lib/IGameCallback.h index 887f98818..e1a32b505 100644 --- a/lib/IGameCallback.h +++ b/lib/IGameCallback.h @@ -27,6 +27,7 @@ struct BlockingDialog; struct TeleportDialog; struct StackLocation; struct ArtifactLocation; +struct BankConfig; class CCreatureSet; class CStackBasicDescriptor; class CGCreature; @@ -84,6 +85,7 @@ class DLL_LINKAGE IGameEventCallback { public: virtual void setObjPropertyValue(ObjectInstanceID objid, ObjProperty prop, int32_t value = 0) = 0; + virtual void setBankObjectConfiguration(ObjectInstanceID objid, const BankConfig & configuration) = 0; virtual void setRewardableObjectConfiguration(ObjectInstanceID mapObjectID, const Rewardable::Configuration & configuration) = 0; virtual void setRewardableObjectConfiguration(ObjectInstanceID townInstanceID, BuildingID buildingID, const Rewardable::Configuration & configuration) = 0; virtual void setObjPropertyID(ObjectInstanceID objid, ObjProperty prop, ObjPropertyID identifier) = 0; diff --git a/lib/mapObjectConstructors/CBankInstanceConstructor.cpp b/lib/mapObjectConstructors/CBankInstanceConstructor.cpp index fccd3a870..94e186058 100644 --- a/lib/mapObjectConstructors/CBankInstanceConstructor.cpp +++ b/lib/mapObjectConstructors/CBankInstanceConstructor.cpp @@ -37,7 +37,7 @@ void CBankInstanceConstructor::initTypeData(const JsonNode & input) regularUnitPlacement = input["regularUnitPlacement"].Bool(); } -BankConfig CBankInstanceConstructor::generateConfig(IGameCallback * cb, const JsonNode & level, vstd::RNG & rng) const +BankConfig CBankInstanceConstructor::generateLevelConfiguration(IGameCallback * cb, const JsonNode & level, vstd::RNG & rng) const { BankConfig bc; JsonRandom randomizer(cb); @@ -60,7 +60,11 @@ void CBankInstanceConstructor::randomizeObject(CBank * bank, vstd::RNG & rng) co bank->blockVisit = blockVisit; bank->coastVisitable = coastVisitable; bank->regularUnitPlacement = regularUnitPlacement; + bank->setConfig(generateConfiguration(bank->cb, rng, bank->ID)); +} +BankConfig CBankInstanceConstructor::generateConfiguration(IGameCallback * cb, vstd::RNG & rng, MapObjectID objectID) const +{ si32 totalChance = 0; for(const auto & node : levels) totalChance += static_cast(node["chance"].Float()); @@ -74,11 +78,10 @@ void CBankInstanceConstructor::randomizeObject(CBank * bank, vstd::RNG & rng) co { cumulativeChance += static_cast(node["chance"].Float()); if(selectedChance < cumulativeChance) - { - bank->setConfig(generateConfig(bank->cb, node, rng)); - break; - } + return generateLevelConfiguration(cb, node, rng); } + + throw std::runtime_error("Failed to select bank configuration"); } CBankInfo::CBankInfo(const JsonVector & Config) : diff --git a/lib/mapObjectConstructors/CBankInstanceConstructor.h b/lib/mapObjectConstructors/CBankInstanceConstructor.h index 3fe706782..8a30d4bcd 100644 --- a/lib/mapObjectConstructors/CBankInstanceConstructor.h +++ b/lib/mapObjectConstructors/CBankInstanceConstructor.h @@ -69,7 +69,7 @@ public: class CBankInstanceConstructor : public CDefaultObjectTypeHandler { - BankConfig generateConfig(IGameCallback * cb, const JsonNode & conf, vstd::RNG & rng) const; + BankConfig generateLevelConfiguration(IGameCallback * cb, const JsonNode & conf, vstd::RNG & rng) const; JsonVector levels; @@ -92,6 +92,8 @@ public: bool hasNameTextID() const override; std::unique_ptr getObjectInfo(std::shared_ptr tmpl) const override; + + BankConfig generateConfiguration(IGameCallback * cb, vstd::RNG & rand, MapObjectID objectID) const; }; VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/CBank.cpp b/lib/mapObjects/CBank.cpp index e7dba0aca..cceca0a6f 100644 --- a/lib/mapObjects/CBank.cpp +++ b/lib/mapObjects/CBank.cpp @@ -97,6 +97,8 @@ void CBank::setConfig(const BankConfig & config) for(const auto & stack : config.guards) setCreature (SlotID(stacksCount()), stack.type->getId(), stack.count); + + daycounter = 1; //yes, 1 since "today" daycounter won't be incremented } void CBank::setPropertyDer (ObjProperty what, ObjPropertyID identifier) @@ -106,11 +108,6 @@ void CBank::setPropertyDer (ObjProperty what, ObjPropertyID identifier) case ObjProperty::BANK_DAYCOUNTER: //daycounter daycounter+= identifier.getNum(); break; - case ObjProperty::BANK_RESET: - // FIXME: Object reset must be done by separate netpack from server - initObj(cb->gameState()->getRandomGenerator()); - daycounter = 1; //yes, 1 since "today" daycounter won't be incremented - break; case ObjProperty::BANK_CLEAR: bankConfig.reset(); break; @@ -124,7 +121,11 @@ void CBank::newTurn(vstd::RNG & rand) const if (resetDuration != 0) { if (daycounter >= resetDuration) - cb->setObjPropertyValue(id, ObjProperty::BANK_RESET); //daycounter 0 + { + auto handler = std::dynamic_pointer_cast(getObjectHandler()); + auto config = handler->generateConfiguration(cb, rand, ID); + cb->setBankObjectConfiguration(id, config); + } else cb->setObjPropertyValue(id, ObjProperty::BANK_DAYCOUNTER, 1); //daycounter++ } diff --git a/lib/mapping/CMapEditManager.cpp b/lib/mapping/CMapEditManager.cpp index 27866b0ae..2884dbb12 100644 --- a/lib/mapping/CMapEditManager.cpp +++ b/lib/mapping/CMapEditManager.cpp @@ -115,9 +115,10 @@ void CMapUndoManager::setUndoCallback(std::function functor) CMapEditManager::CMapEditManager(CMap * map) : map(map), terrainSel(map), objectSel(map) { - } +CMapEditManager::~CMapEditManager() = default; + CMap * CMapEditManager::getMap() { return map; diff --git a/lib/mapping/CMapEditManager.h b/lib/mapping/CMapEditManager.h index 812196efa..1d952598b 100644 --- a/lib/mapping/CMapEditManager.h +++ b/lib/mapping/CMapEditManager.h @@ -68,6 +68,7 @@ class DLL_LINKAGE CMapEditManager : boost::noncopyable { public: CMapEditManager(CMap * map); + ~CMapEditManager(); CMap * getMap(); /// Clears the terrain. The free level is filled with water and the underground level with rock. diff --git a/lib/networkPacks/NetPackVisitor.h b/lib/networkPacks/NetPackVisitor.h index 0f277982b..442a470bb 100644 --- a/lib/networkPacks/NetPackVisitor.h +++ b/lib/networkPacks/NetPackVisitor.h @@ -36,6 +36,7 @@ public: virtual void visitGamePause(GamePause & pack) {} virtual void visitEntitiesChanged(EntitiesChanged & pack) {} virtual void visitSetRewardableConfiguration(SetRewardableConfiguration & pack) {} + virtual void visitSetBankConfiguration(SetBankConfiguration & pack) {} virtual void visitSetResources(SetResources & pack) {} virtual void visitSetPrimSkill(SetPrimSkill & pack) {} virtual void visitSetSecSkill(SetSecSkill & pack) {} diff --git a/lib/networkPacks/NetPacksLib.cpp b/lib/networkPacks/NetPacksLib.cpp index 879499e60..8fcc759d7 100644 --- a/lib/networkPacks/NetPacksLib.cpp +++ b/lib/networkPacks/NetPacksLib.cpp @@ -33,6 +33,7 @@ #include "StartInfo.h" #include "CPlayerState.h" #include "TerrainHandler.h" +#include "mapObjects/CBank.h" #include "mapObjects/CGCreature.h" #include "mapObjects/CGMarket.h" #include "mapObjects/CGTownInstance.h" @@ -129,6 +130,11 @@ void SetRewardableConfiguration::visitTyped(ICPackVisitor & visitor) visitor.visitSetRewardableConfiguration(*this); } +void SetBankConfiguration::visitTyped(ICPackVisitor & visitor) +{ + visitor.visitSetBankConfiguration(*this); +} + void SetResources::visitTyped(ICPackVisitor & visitor) { visitor.visitSetResources(*this); @@ -2448,6 +2454,16 @@ void SetRewardableConfiguration::applyGs(CGameState * gs) rewardablePtr->configuration = configuration; } +void SetBankConfiguration::applyGs(CGameState * gs) +{ + auto * objectPtr = gs->getObjInstance(objectID); + auto * bankPtr = dynamic_cast(objectPtr); + + assert(bankPtr); + + bankPtr->setConfig(configuration); +} + const CArtifactInstance * ArtSlotInfo::getArt() const { if(locked) diff --git a/lib/networkPacks/ObjProperty.h b/lib/networkPacks/ObjProperty.h index a57c68120..1e94c24c6 100644 --- a/lib/networkPacks/ObjProperty.h +++ b/lib/networkPacks/ObjProperty.h @@ -43,7 +43,6 @@ enum class ObjProperty : int8_t //creature-bank specific BANK_DAYCOUNTER, - BANK_RESET, BANK_CLEAR, //object with reward diff --git a/lib/networkPacks/SetRewardableConfiguration.h b/lib/networkPacks/SetRewardableConfiguration.h index 57d9972dc..1749e6f66 100644 --- a/lib/networkPacks/SetRewardableConfiguration.h +++ b/lib/networkPacks/SetRewardableConfiguration.h @@ -12,6 +12,7 @@ #include "NetPacksBase.h" #include "../rewardable/Configuration.h" +#include "../mapObjectConstructors/CBankInstanceConstructor.h" VCMI_LIB_NAMESPACE_BEGIN @@ -32,4 +33,19 @@ struct DLL_LINKAGE SetRewardableConfiguration : public CPackForClient } }; +struct DLL_LINKAGE SetBankConfiguration : public CPackForClient +{ + void applyGs(CGameState * gs); + void visitTyped(ICPackVisitor & visitor) override; + + ObjectInstanceID objectID; + BankConfig configuration; + + template void serialize(Handler & h) + { + h & objectID; + h & configuration; + } +}; + VCMI_LIB_NAMESPACE_END diff --git a/lib/registerTypes/RegisterTypesClientPacks.h b/lib/registerTypes/RegisterTypesClientPacks.h index 77124eacf..af64194ab 100644 --- a/lib/registerTypes/RegisterTypesClientPacks.h +++ b/lib/registerTypes/RegisterTypesClientPacks.h @@ -122,6 +122,7 @@ void registerTypesClientPacks(Serializer &s) s.template registerType(); s.template registerType(); + s.template registerType(); } VCMI_LIB_NAMESPACE_END diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 615b209be..ee4dc93fb 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -4377,6 +4377,14 @@ void CGameHandler::setObjPropertyID(ObjectInstanceID objid, ObjProperty prop, Ob sendAndApply(&sob); } +void CGameHandler::setBankObjectConfiguration(ObjectInstanceID objid, const BankConfig & configuration) +{ + SetBankConfiguration srb; + srb.objectID = objid; + srb.configuration = configuration; + sendAndApply(&srb); +} + void CGameHandler::setRewardableObjectConfiguration(ObjectInstanceID objid, const Rewardable::Configuration & configuration) { SetRewardableConfiguration srb; diff --git a/server/CGameHandler.h b/server/CGameHandler.h index b58f2ae5e..60e7d8be0 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -169,6 +169,7 @@ public: bool isVisitCoveredByAnotherQuery(const CGObjectInstance *obj, const CGHeroInstance *hero) override; void setObjPropertyValue(ObjectInstanceID objid, ObjProperty prop, int32_t value) override; void setObjPropertyID(ObjectInstanceID objid, ObjProperty prop, ObjPropertyID identifier) override; + void setBankObjectConfiguration(ObjectInstanceID objid, const BankConfig & configuration) override; void setRewardableObjectConfiguration(ObjectInstanceID objid, const Rewardable::Configuration & configuration) override; void setRewardableObjectConfiguration(ObjectInstanceID townInstanceID, BuildingID buildingID, const Rewardable::Configuration & configuration) override; void showInfoDialog(InfoWindow * iw) override; From c91f8cd511680660e725cbf233617c99b0780ffb Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sun, 14 Jul 2024 07:56:02 +0000 Subject: [PATCH 043/100] Fix tests --- test/game/CGameStateTest.cpp | 4 ++++ test/mock/mock_IGameCallback.h | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/test/game/CGameStateTest.cpp b/test/game/CGameStateTest.cpp index af42ad9c8..c5f774ed1 100644 --- a/test/game/CGameStateTest.cpp +++ b/test/game/CGameStateTest.cpp @@ -121,6 +121,10 @@ public: return gameState.get(); } + void createBoat(const int3 & visitablePosition, BoatId type, PlayerColor initiator) override + { + } + bool moveHero(ObjectInstanceID hid, int3 dst, EMovementMode movementMode) override { return false; diff --git a/test/mock/mock_IGameCallback.h b/test/mock/mock_IGameCallback.h index 78e11699c..81f84d3c3 100644 --- a/test/mock/mock_IGameCallback.h +++ b/test/mock/mock_IGameCallback.h @@ -37,11 +37,15 @@ public: void setObjPropertyValue(ObjectInstanceID objid, ObjProperty prop, int32_t value = 0) override {} void setObjPropertyID(ObjectInstanceID objid, ObjProperty prop, ObjPropertyID identifier) override {} + void setBankObjectConfiguration(ObjectInstanceID objid, const BankConfig & configuration) override {} + void setRewardableObjectConfiguration(ObjectInstanceID mapObjectID, const Rewardable::Configuration & configuration) override {} + void setRewardableObjectConfiguration(ObjectInstanceID townInstanceID, BuildingID buildingID, const Rewardable::Configuration & configuration) override {} + void showInfoDialog(InfoWindow * iw) override {} void changeSpells(const CGHeroInstance * hero, bool give, const std::set &spells) override {} bool removeObject(const CGObjectInstance * obj, const PlayerColor & initiator) override {return false;} - void createObject(const int3 & visitablePosition, const PlayerColor & initiator, MapObjectID type, MapObjectSubID subtype) override {}; + void createBoat(const int3 & visitablePosition, BoatId type, PlayerColor initiator) override {} void setOwner(const CGObjectInstance * objid, PlayerColor owner) override {} void giveExperience(const CGHeroInstance * hero, TExpType val) override {} void changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs=false) override {} From 65eeceb7d7feeae95ac295216c729795c274046f Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 16 Jul 2024 15:12:02 +0000 Subject: [PATCH 044/100] Integrating changes from PR #4251 - allow defining seed for server --- config/schemas/settings.json | 8 ++++++-- server/CGameHandler.cpp | 4 ++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/config/schemas/settings.json b/config/schemas/settings.json index 002500054..c1b84a469 100644 --- a/config/schemas/settings.json +++ b/config/schemas/settings.json @@ -437,7 +437,7 @@ "type" : "object", "additionalProperties" : false, "default" : {}, - "required" : [ "localHostname", "localPort", "remoteHostname", "remotePort", "playerAI", "alliedAI", "friendlyAI", "neutralAI", "enemyAI" ], + "required" : [ "localHostname", "localPort", "remoteHostname", "remotePort", "seed", "playerAI", "alliedAI", "friendlyAI", "neutralAI", "enemyAI" ], "properties" : { "localHostname" : { "type" : "string", @@ -455,6 +455,10 @@ "type" : "number", "default" : 3030 }, + "seed" : { + "type" : "number", + "default" : 0 + }, "playerAI" : { "type" : "string", "default" : "Nullkiller" @@ -537,7 +541,7 @@ }, "loggers" : { "type" : "array", - "default" : [ { "domain" : "global", "level" : "trace" } ], + "default" : [ { "domain" : "global", "level" : "trace" }, { "domain" : "rng", "level" : "info" } ], "items" : { "type" : "object", "additionalProperties" : false, diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index ee4dc93fb..c4e0264b3 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -24,6 +24,7 @@ #include "../lib/ArtifactUtils.h" #include "../lib/CArtHandler.h" #include "../lib/CBuildingHandler.h" +#include "../lib/CConfigHandler.h" #include "../lib/CCreatureHandler.h" #include "../lib/CCreatureSet.h" #include "../lib/CGeneralTextHandler.h" @@ -552,6 +553,9 @@ void CGameHandler::reinitScripting() void CGameHandler::init(StartInfo *si, Load::ProgressAccumulator & progressTracking) { randomNumberGenerator = std::make_unique(); + int requestedSeed = settings["server"]["seed"].Integer(); + if (requestedSeed != 0) + randomNumberGenerator->setSeed(requestedSeed); logGlobal->info("Using random seed: %d", randomNumberGenerator->nextInt()); CMapService mapService; From 34b6eaa25e0aa46b17d19edb45bb1f3909f9efe7 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 16 Jul 2024 15:30:31 +0000 Subject: [PATCH 045/100] Integrating changes from PR #4251 - add logging for rng --- include/vstd/CLoggerBase.h | 1 + lib/CRandomGenerator.cpp | 13 +++++++++++++ lib/logging/CLogger.cpp | 1 + 3 files changed, 15 insertions(+) diff --git a/include/vstd/CLoggerBase.h b/include/vstd/CLoggerBase.h index 546b3d352..a1cf6edba 100644 --- a/include/vstd/CLoggerBase.h +++ b/include/vstd/CLoggerBase.h @@ -193,5 +193,6 @@ extern DLL_LINKAGE vstd::CLoggerBase * logNetwork; extern DLL_LINKAGE vstd::CLoggerBase * logAi; extern DLL_LINKAGE vstd::CLoggerBase * logAnim; extern DLL_LINKAGE vstd::CLoggerBase * logMod; +extern DLL_LINKAGE vstd::CLoggerBase * logRng; VCMI_LIB_NAMESPACE_END diff --git a/lib/CRandomGenerator.cpp b/lib/CRandomGenerator.cpp index 3a7ac2320..0cec4fa31 100644 --- a/lib/CRandomGenerator.cpp +++ b/lib/CRandomGenerator.cpp @@ -15,21 +15,25 @@ VCMI_LIB_NAMESPACE_BEGIN CRandomGenerator::CRandomGenerator() { + logRng->trace("CRandomGenerator constructed"); resetSeed(); } CRandomGenerator::CRandomGenerator(int seed) { + logRng->trace("CRandomGenerator constructed (%d)", seed); setSeed(seed); } void CRandomGenerator::setSeed(int seed) { + logRng->trace("CRandomGenerator::setSeed (%d)", seed); rand.seed(seed); } void CRandomGenerator::resetSeed() { + logRng->trace("CRandomGenerator::resetSeed"); boost::hash stringHash; auto threadIdHash = stringHash(boost::lexical_cast(boost::this_thread::get_id())); setSeed(static_cast(threadIdHash * std::time(nullptr))); @@ -37,16 +41,20 @@ void CRandomGenerator::resetSeed() int CRandomGenerator::nextInt(int upper) { + logRng->trace("CRandomGenerator::nextInt (%d)", upper); return nextInt(0, upper); } int64_t CRandomGenerator::nextInt64(int64_t upper) { + logRng->trace("CRandomGenerator::nextInt64 (%d)", upper); return nextInt64(0, upper); } int CRandomGenerator::nextInt(int lower, int upper) { + logRng->trace("CRandomGenerator::nextInt64 (%d, %d)", lower, upper); + if (lower > upper) throw std::runtime_error("Invalid range provided: " + std::to_string(lower) + " ... " + std::to_string(upper)); @@ -55,17 +63,20 @@ int CRandomGenerator::nextInt(int lower, int upper) int CRandomGenerator::nextInt() { + logRng->trace("CRandomGenerator::nextInt64"); return TIntDist()(rand); } int CRandomGenerator::nextBinomialInt(int coinsCount, double coinChance) { + logRng->trace("CRandomGenerator::nextBinomialInt (%d, %f)", coinsCount, coinChance); std::binomial_distribution<> distribution(coinsCount, coinChance); return distribution(rand); } int64_t CRandomGenerator::nextInt64(int64_t lower, int64_t upper) { + logRng->trace("CRandomGenerator::nextInt64 (%d, %d)", lower, upper); if (lower > upper) throw std::runtime_error("Invalid range provided: " + std::to_string(lower) + " ... " + std::to_string(upper)); @@ -74,11 +85,13 @@ int64_t CRandomGenerator::nextInt64(int64_t lower, int64_t upper) double CRandomGenerator::nextDouble(double upper) { + logRng->trace("CRandomGenerator::nextDouble (%f)", upper); return nextDouble(0, upper); } double CRandomGenerator::nextDouble(double lower, double upper) { + logRng->trace("CRandomGenerator::nextDouble (%f, %f)", lower, upper); if(lower > upper) throw std::runtime_error("Invalid range provided: " + std::to_string(lower) + " ... " + std::to_string(upper)); diff --git a/lib/logging/CLogger.cpp b/lib/logging/CLogger.cpp index 6d7c43ed3..e33c6425e 100644 --- a/lib/logging/CLogger.cpp +++ b/lib/logging/CLogger.cpp @@ -96,6 +96,7 @@ DLL_LINKAGE vstd::CLoggerBase * logNetwork = CLogger::getLogger(CLoggerDomain("n DLL_LINKAGE vstd::CLoggerBase * logAi = CLogger::getLogger(CLoggerDomain("ai")); DLL_LINKAGE vstd::CLoggerBase * logAnim = CLogger::getLogger(CLoggerDomain("animation")); DLL_LINKAGE vstd::CLoggerBase * logMod = CLogger::getLogger(CLoggerDomain("mod")); +DLL_LINKAGE vstd::CLoggerBase * logRng = CLogger::getLogger(CLoggerDomain("rng")); CLogger * CLogger::getLogger(const CLoggerDomain & domain) { From 8f407311fdde1c60bafe76d70d510208d3a95563 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Tue, 16 Jul 2024 20:29:20 +0200 Subject: [PATCH 046/100] fix headings of md files --- docs/developers/AI.md | 10 ++- docs/developers/Bonus_System.md | 2 + docs/developers/Building_Android.md | 2 + docs/developers/Building_Linux.md | 34 +++---- docs/developers/Building_Windows.md | 38 ++++---- docs/developers/Building_iOS.md | 2 + docs/developers/Building_macOS.md | 2 + docs/developers/Code_Structure.md | 54 +++++------ docs/developers/Coding_Guidelines.md | 2 + docs/developers/Conan.md | 2 +- .../developers/Development_with_Qt_Creator.md | 2 +- docs/developers/Logging_API.md | 22 ++--- docs/developers/Lua_Scripting_System.md | 32 +++---- docs/developers/Networking.md | 28 +++--- docs/developers/RMG_Description.md | 36 ++++---- docs/developers/Serialization.md | 50 ++++++----- docs/maintainers/Release_Process.md | 2 + docs/maintainers/Ubuntu_PPA.md | 2 + docs/modders/Animation_Format.md | 2 + docs/modders/Bonus/Bonus_Duration_Types.md | 2 + docs/modders/Bonus/Bonus_Limiters.md | 2 + docs/modders/Bonus/Bonus_Propagators.md | 2 + docs/modders/Bonus/Bonus_Range_Types.md | 2 + docs/modders/Bonus/Bonus_Sources.md | 2 + docs/modders/Bonus/Bonus_Types.md | 2 + docs/modders/Bonus/Bonus_Updaters.md | 2 + docs/modders/Bonus/Bonus_Value_Types.md | 2 + docs/modders/Bonus_Format.md | 2 + docs/modders/Building_Bonuses.md | 40 +++++---- docs/modders/Campaign_Format.md | 26 +++--- docs/modders/Configurable_Widgets.md | 90 ++++++++++--------- docs/modders/Difficulty.md | 2 + .../Entities_Format/Artifact_Format.md | 2 + .../Entities_Format/Battle_Obstacle_Format.md | 2 + .../Entities_Format/Battlefield_Format.md | 2 + docs/modders/Entities_Format/Biome_Format.md | 2 + .../Entities_Format/Creature_Format.md | 2 + .../modders/Entities_Format/Faction_Format.md | 2 + .../Entities_Format/Hero_Class_Format.md | 2 + .../Entities_Format/Hero_Type_Format.md | 2 + docs/modders/Entities_Format/River_Format.md | 2 + docs/modders/Entities_Format/Road_Format.md | 2 + .../Entities_Format/Secondary_Skill_Format.md | 2 + docs/modders/Entities_Format/Spell_Format.md | 2 + .../modders/Entities_Format/Terrain_Format.md | 2 + docs/modders/Game_Identifiers.md | 2 + docs/modders/Map_Editor.md | 64 ++++++------- docs/modders/Map_Object_Format.md | 2 + docs/modders/Map_Objects/Boat.md | 2 + docs/modders/Map_Objects/Creature_Bank.md | 2 + docs/modders/Map_Objects/Dwelling.md | 2 + docs/modders/Map_Objects/Market.md | 2 + docs/modders/Map_Objects/Rewardable.md | 2 + docs/modders/Mod_File_Format.md | 2 + docs/modders/Random_Map_Template.md | 2 + docs/modders/Readme.md | 2 + docs/modders/Translations.md | 2 + docs/players/Bug_Reporting_Guidelines.md | 2 + docs/players/Cheat_Codes.md | 4 +- docs/players/Game_Mechanics.md | 2 + docs/players/Installation_Android.md | 2 + docs/players/Installation_Linux.md | 4 +- docs/players/Installation_Windows.md | 2 + docs/players/Installation_iOS.md | 2 + docs/players/Installation_macOS.md | 2 + docs/players/Privacy_Policy.md | 8 +- 66 files changed, 384 insertions(+), 256 deletions(-) diff --git a/docs/developers/AI.md b/docs/developers/AI.md index ebcd30924..b75ba539a 100644 --- a/docs/developers/AI.md +++ b/docs/developers/AI.md @@ -1,3 +1,5 @@ +# AI + There are two types of AI: adventure and battle. **Adventure AIs** are responsible for moving heroes across the map and developing towns @@ -15,7 +17,7 @@ When you call unit->getAttack() it summarizes all these bonuses and returns the One important class is HypotheticBattle. It is used to evaluate the effects of an action without changing the actual gamestate. It is a wrapper around CPlayerSpecificCallback or another HypotheticBattle so it can provide you data, Internally it has a set of modified unit states and intercepts some calls to underlying callback and returns these internal states instead. These states in turn are wrappers around original units and contain modified bonuses (CStackWithBonuses). So if you need to emulate an attack you can call hypotheticbattle.getforupdate() and it will return the CStackWithBonuses which you can safely change. -# BattleAI +## BattleAI BattleAI's most important classes are the following: @@ -31,11 +33,11 @@ BattleAI's most important classes are the following: BattleAI itself handles all the rest and issues actual commands -# Nullkiller AI +## Nullkiller AI Adventure AI responsible for moving heroes on map, gathering things, developing town. Main idea is to gather all possible tasks on map, prioritize them and select the best one for each heroes. Initially was a fork of VCAI -## Parts +### Parts Gateway - a callback for server used to invoke AI actions when server thinks it is time to do something. Through this callback AI is informed about various events like hero level up, tile revialed, blocking dialogs and so on. In order to do this Gaateway implements specific interface. The interface is exactly the same for human and AI Another important actor for server interaction is CCallback * cb. This one is used to retrieve gamestate information and ask server to do things like hero moving, spell casting and so on. Each AI has own instance of Gateway and it is a root object which holds all AI state. Gateway has an event method yourTurn which invokes makeTurn in another thread. The last passes control to Nullkiller engine. @@ -58,7 +60,7 @@ Analyzer - a module gathering data from CCallback *. Its goal to make some stati * FuzzyEngines - looks like some legacy from VCAI * PriorityEvaluator - gathers information on task rewards, evaluates their priority using Fuzzy Light library (fuzzy logic) -## Goals +### Goals Units of activity in AI. Can be AbstractGoal, Task, Marker and Behavior Task - simple thing which can be done right away in order to gain some reward. Or a composition of simple things in case if more than one action is needed to gain the reward. diff --git a/docs/developers/Bonus_System.md b/docs/developers/Bonus_System.md index 370bf9ea4..e594fff8e 100644 --- a/docs/developers/Bonus_System.md +++ b/docs/developers/Bonus_System.md @@ -1,3 +1,5 @@ +# Bonus System + The bonus system of VCMI is a set of mechanisms that make handling of different bonuses for heroes, towns, players and units easier. The system consists of a set of nodes representing objects that can be a source or a subject of a bonus and two directed acyclic graphs (DAGs) representing inheritance and propagation of bonuses. Core of bonus system is defined in HeroBonus.h file. ## Propagation and inheritance diff --git a/docs/developers/Building_Android.md b/docs/developers/Building_Android.md index 13db490aa..5ce865f7b 100644 --- a/docs/developers/Building_Android.md +++ b/docs/developers/Building_Android.md @@ -1,3 +1,5 @@ +# Building Android + The following instructions apply to **v1.2 and later**. For earlier versions the best documentation is https://github.com/vcmi/vcmi-android/blob/master/building.txt (and reading scripts in that repo), however very limited to no support will be provided from our side if you wish to go down that rabbit hole. *Note*: building has been tested only on Linux and macOS. It may or may not work on Windows out of the box. diff --git a/docs/developers/Building_Linux.md b/docs/developers/Building_Linux.md index 68284a98a..5822464eb 100644 --- a/docs/developers/Building_Linux.md +++ b/docs/developers/Building_Linux.md @@ -1,13 +1,15 @@ -# Compiling VCMI +# Building Linux + +## Compiling VCMI - Current baseline requirement for building is Ubuntu 20.04 - Supported C++ compilers for UNIX-like systems are GCC 9+ and Clang 13+ Older distributions and compilers might work, but they aren't tested by Github CI (Actions) -# Installing dependencies +## Installing dependencies -## Prerequisites +### Prerequisites To compile, the following packages (and their development counterparts) are needed to build: @@ -21,7 +23,7 @@ To compile, the following packages (and their development counterparts) are need - if you want to build scripting modules: LuaJIT - to speed up recompilation: Ccache -## On Debian-based systems (e.g. Ubuntu) +### On Debian-based systems (e.g. Ubuntu) For Ubuntu and Debian you need to install this list of packages: @@ -31,13 +33,13 @@ Alternatively if you have VCMI installed from repository or PPA you can use: `sudo apt-get build-dep vcmi` -## On RPM-based distributions (e.g. Fedora) +### On RPM-based distributions (e.g. Fedora) `sudo yum install cmake gcc-c++ SDL2-devel SDL2_image-devel SDL2_ttf-devel SDL2_mixer-devel boost boost-devel boost-filesystem boost-system boost-thread boost-program-options boost-locale boost-iostreams zlib-devel ffmpeg-devel ffmpeg-libs qt5-qtbase-devel tbb-devel luajit-devel liblzma-devel libsqlite3-devel fuzzylite-devel ccache` NOTE: `fuzzylite-devel` package is no longer available in recent version of Fedora, for example Fedora 38. It's not a blocker because VCMI bundles fuzzylite lib in its source code. -## On Arch-based distributions +### On Arch-based distributions On Arch-based distributions, there is a development package available for VCMI on the AUR. @@ -45,7 +47,7 @@ It can be found at: Information about building packages from the Arch User Repository (AUR) can be found at the Arch wiki. -# Getting the sources +## Getting the sources We recommend the following directory structure: @@ -59,35 +61,35 @@ You can get latest sources with: `git clone -b develop --recursive https://github.com/vcmi/vcmi.git` -# Compilation +## Compilation -## Configuring Makefiles +### Configuring Makefiles ```sh mkdir build && cd build cmake -S ../vcmi ``` -# Additional options that you may want to use: +## Additional options that you may want to use: -## To enable debugging: +### To enable debugging: `cmake -S ../vcmi -D CMAKE_BUILD_TYPE=Debug` **Notice**: The ../vcmi/ is not a typo, it will place makefile scripts into the build dir as the build dir is your working dir when calling CMake. -## To use ccache: +### To use ccache: `cmake -S ../vcmi -D ENABLE_CCACHE:BOOL=ON` -## Trigger build +### Trigger build `cmake --build . -- -j2` (-j2 = compile with 2 threads, you can specify any value) That will generate vcmiclient, vcmiserver, vcmilauncher as well as .so libraries in the **build/bin/** directory. -# Package building +## Package building -## RPM package +### RPM package The first step is to prepare a RPM build environment. On Fedora systems you can follow this guide: http://fedoraproject.org/wiki/How_to_create_an_RPM_package#SPEC_file_overview @@ -136,7 +138,7 @@ For other distributions that uses RPM, chances are there might be a spec file fo Available root environments and their names are listed in /etc/mock. -## Debian/Ubuntu +### Debian/Ubuntu 1. Install debhelper and devscripts packages diff --git a/docs/developers/Building_Windows.md b/docs/developers/Building_Windows.md index 257ef14ba..ef08d25e7 100644 --- a/docs/developers/Building_Windows.md +++ b/docs/developers/Building_Windows.md @@ -1,7 +1,9 @@ -# Preparations +# Building Windows + +## Preparations Windows builds can be made in more than one way and with more than one tool. This guide focuses on the simplest building process using Microsoft Visual Studio 2022 -# Prerequisites +## Prerequisites - Windows Vista or newer. - [Microsoft Visual Studio](https://visualstudio.microsoft.com/downloads/) @@ -12,7 +14,7 @@ Windows builds can be made in more than one way and with more than one tool. Thi - To create installer: [NSIS](http://nsis.sourceforge.net/Main_Page) - To speed up recompilation: [CCache](https://github.com/ccache/ccache/releases) -## Choose an installation directory +### Choose an installation directory Create a directory for VCMI development, eg. `C:\VCMI` We will call this directory `%VCMI_DIR%` @@ -27,13 +29,13 @@ Bad locations: - `C:\Users\Michał\VCMI (non-ascii character)` - `C:\Program Files (x86)\VCMI (write protection)` -# Install VCMI dependencies +## Install VCMI dependencies You have two options: to use pre-built libraries or build your own. We strongly recommend start with using pre-built ones. -## Option A. Use pre-built Vcpkg +### Option A. Use pre-built Vcpkg -### Download and unpack archive +#### Download and unpack archive Vcpkg Archives are available at our GitHub: https://github.com/vcmi/vcmi-deps-windows/releases @@ -41,18 +43,18 @@ Vcpkg Archives are available at our GitHub: https://github.com/vcmi/vcmi-deps-wi EG: v1.6 assets - [vcpkg-export-x64-windows-v143.7z](https://github.com/vcmi/vcmi-deps-windows/releases/download/v1.6/vcpkg-export-x64-windows-v143.7z) - Extract archive by right clicking on it and choosing "7-zip -> Extract Here". -### Move dependencies to target directory +#### Move dependencies to target directory Once extracted, a `vcpkg` directory will appear with `installed` and `scripts` subfolders inside. Move extracted `vcpkg` directory into your `%VCMI_DIR%` -## Option B. Build Vcpkg on your own +### Option B. Build Vcpkg on your own Please be aware that if you're running 32-bit Windows version, then this is impossible due to Be aware that building Vcpkg might take a lot of time depend on your CPU model and 10-20GB of disk space. -### Create initial directory +#### Create initial directory -### Clone vcpkg +#### Clone vcpkg 1. open SourceTree 2. File -\> Clone @@ -64,7 +66,7 @@ From command line use: git clone https://github.com/microsoft/vcpkg.git %VCMI_DIR%/vcpkg -### Build vcpkg and dependencies +#### Build vcpkg and dependencies - Run `%VCMI_DIR%/vcpkg/bootstrap-vcpkg.bat` @@ -75,11 +77,11 @@ From command line use: For the list of the packages used you can also consult [vcmi-deps-windows readme](https://github.com/vcmi/vcmi-deps-windows) in case this article gets outdated a bit. -# Install CCache +## Install CCache Extract `ccache` to a folder of your choosing, add the folder to the `PATH` environment variable and log out and back in. -# Build VCMI +## Build VCMI #### From GIT GUI - Open SourceTree @@ -93,13 +95,13 @@ Extract `ccache` to a folder of your choosing, add the folder to the `PATH` envi #### From command line - `git clone --recursive https://github.com/vcmi/vcmi.git %VCMI_DIR%/source` -## Generate solution for VCMI +### Generate solution for VCMI - Create `%VCMI_DIR%/build` folder - Open a command line prompt at `%VCMI_DIR%/build` - Execute `cd %VCMI_DIR%/build` - Create solution (Visual Studio 2022 64-bit) `cmake %VCMI_DIR%/source -DCMAKE_TOOLCHAIN_FILE=%VCMI_DIR%/vcpkg/scripts/buildsystems/vcpkg.cmake -G "Visual Studio 17 2022" -A x64` -## Compile VCMI with Visual Studio +### Compile VCMI with Visual Studio - Open `%VCMI_DIR%/build/VCMI.sln` in Visual Studio - Select `Release` build type in the combobox - If you want to use ccache: @@ -109,7 +111,7 @@ Extract `ccache` to a folder of your choosing, add the folder to the `PATH` envi - Right click on `BUILD_ALL` project. This `BUILD_ALL` project should be in `CMakePredefinedTargets` tree in Solution Explorer. - VCMI will be built in `%VCMI_DIR%/build/bin` folder! -## Compile VCMI with MinGW via MSYS2 +### Compile VCMI with MinGW via MSYS2 - Install MSYS2 from https://www.msys2.org/ - Start the `MSYS MinGW x64`-shell - Install dependencies: `pacman -S mingw-w64-x86_64-SDL2 mingw-w64-x86_64-SDL2_image mingw-w64-x86_64-SDL2_mixer mingw-w64-x86_64-SDL2_ttf mingw-w64-x86_64-boost mingw-w64-x86_64-gcc mingw-w64-x86_64-ninja mingw-w64-x86_64-qt5-static` @@ -117,7 +119,7 @@ Extract `ccache` to a folder of your choosing, add the folder to the `PATH` envi **NOTE:** This will link Qt5 statically to `VCMI_launcher.exe` and `VCMI_Mapeditor.exe`. See [PR #3421](https://github.com/vcmi/vcmi/pull/3421) for some background. -# Create VCMI installer (This step is not required for just building & development) +## Create VCMI installer (This step is not required for just building & development) Make sure NSIS is installed to default directory or have registry entry so CMake can find it. After you build VCMI execute following commands from `%VCMI_DIR%/build`. @@ -125,7 +127,7 @@ After you build VCMI execute following commands from `%VCMI_DIR%/build`. - for release build: `cpack` - for debug build: `cpack -C Debug` -# Troubleshooting and workarounds +## Troubleshooting and workarounds Vcpkg might be very unstable due to limited popularity and fact of using bleeding edge packages (such as most recent Boost). Using latest version of dependencies could also expose both problems in VCMI code or library interface changes that developers not checked yet. So if you're built Vcpkg yourself and can't get it working please try to use binary package. diff --git a/docs/developers/Building_iOS.md b/docs/developers/Building_iOS.md index 8383f59ae..6516aacb6 100644 --- a/docs/developers/Building_iOS.md +++ b/docs/developers/Building_iOS.md @@ -1,3 +1,5 @@ +# Building iOS + ## Requirements 1. **macOS** diff --git a/docs/developers/Building_macOS.md b/docs/developers/Building_macOS.md index 67571f74d..8505ce9e8 100644 --- a/docs/developers/Building_macOS.md +++ b/docs/developers/Building_macOS.md @@ -1,3 +1,5 @@ +# Building macOS + ## Requirements 1. C++ toolchain, either of: diff --git a/docs/developers/Code_Structure.md b/docs/developers/Code_Structure.md index cae75b71d..9f3e2c802 100644 --- a/docs/developers/Code_Structure.md +++ b/docs/developers/Code_Structure.md @@ -1,6 +1,8 @@ +# Code Structure + The code of VCMI is divided into several main parts: client, server, lib and AIs, each one in a separate binary file. -# The big picture +## The big picture VCMI contains three core projects: VCMI_lib (dll / so), VCMI_client (executable) and VCMI_server (executable). Server handles all game mechanics and events. Client presents game state and events to player and collects input from him. @@ -8,37 +10,37 @@ During the game, we have one (and only one) server and one or more (one for each Important: State of the game and its mechanics are synchronized between clients and server. All changes to the game state or mechanics must be done by server which will send appropriate notices to clients. -## Game state +### Game state It's basically CGameState class object and everything that's accessible from it: map (with objects), player statuses, game options, etc. -## Bonus system +### Bonus system One of the more important pieces of VCMI is the [bonus system](Bonus_System.md). It's described in a separate article. -## Configuration +### Configuration Most of VCMI configuration files uses Json format and located in "config" directory -### Json parser and writer +#### Json parser and writer -# Client +## Client -## Main purposes of client +### Main purposes of client Client is responsible for: - displaying state of game to human player - capturing player's actions and sending requests to server - displaying changes in state of game indicated by server -## Rendering of graphics +### Rendering of graphics Rendering of graphics relies heavily on SDL. Currently we do not have any wrapper for SDL internal structures and most of rendering is about blitting surfaces using SDL_BlitSurface. We have a few function that make rendering easier or make specific parts of rendering (like printing text). They are places in client/SDL_Extensions and client/SDL_Framerate (the second one contains code responsible for keeping appropriate framerate, it should work more smart than just SDL_Delay(milliseconds)). In rendering, Interface object system is quite helpful. Its base is CIntObject class that is basically a base class for our library of GUI components and other objects. -# Server +## Server -## Main purposes of server +### Main purposes of server Server is responsible for: @@ -47,9 +49,9 @@ Server is responsible for: - informing all clients about changes in state of the game that are visible to them -# Lib +## Lib -## Main purposes of lib +### Main purposes of lib VCMI_Lib is a library that contains code common to server and client, so we avoid it's duplication. Important: the library code is common for client and server and used by them, but the library instance (in opposition to the library as file) is not shared by them! Both client and server create their own "copies" of lib with all its class instances. @@ -63,16 +65,16 @@ Lib contains code responsible for: - handling general game mechanics and related actions (only adventure map objects; it's an unwanted remnant of past development - all game mechanics should be handled by the server) - networking and serialization -### Serialization +#### Serialization The serialization framework can serialize basic types, several standard containers among smart pointers and custom objects. Its design is based on the [boost serialization libraries](http://www.boost.org/doc/libs/1_52_0/libs/serialization/doc/index.html). In addition to the basic functionality it provides light-weight transfer of CGObjectInstance objects by sending only the index/id. Serialization page for all the details. -### Wrapped namespace examples +#### Wrapped namespace examples -#### Inside the lib +##### Inside the lib Both header and implementation of a new class inside the lib should have the following structure: @@ -81,7 +83,7 @@ Both header and implementation of a new class inside the lib should have the fol `` `VCMI_LIB_NAMESPACE_END` -#### Headers outside the lib +##### Headers outside the lib Forward declarations of the lib in headers of other parts of the project need to be wrapped in the macros: @@ -93,30 +95,30 @@ Forward declarations of the lib in headers of other parts of the project need to `` -#### New project part +##### New project part If you're creating new project part, place `VCMI_LIB_USING_NAMESPACE` in its `StdInc.h` to be able to use lib classes without explicit namespace in implementation files. Example: -# Artificial Intelligence +## Artificial Intelligence -## StupidAI +### StupidAI Stupid AI is recent and used battle AI. -## Adventure AI +### Adventure AI VCAI module is currently developed agent-based system driven by goals and heroes. -## Programming challenge +### Programming challenge -## Fuzzy logic +### Fuzzy logic VCMI includes [FuzzyLite](http://code.google.com/p/fuzzy-lite/) library to make use of fuzzy rule-based algorithms. They are useful to handle uncertainty and resemble human behaviour who takes decisions based on rough observations. FuzzyLite is linked as separate static library in AI/FuzzyLite.lib file. -# Utilities +## Utilities -## Launcher +### Launcher -## Duels +### Duels -## ERM parser \ No newline at end of file +### ERM parser \ No newline at end of file diff --git a/docs/developers/Coding_Guidelines.md b/docs/developers/Coding_Guidelines.md index 4f10d9864..89bad05a6 100644 --- a/docs/developers/Coding_Guidelines.md +++ b/docs/developers/Coding_Guidelines.md @@ -1,3 +1,5 @@ +# Coding Guidelines + ## C++ Standard VCMI implementation bases on C++17 standard. Any feature is acceptable as long as it's will pass build on our CI, but there is list below on what is already being used. diff --git a/docs/developers/Conan.md b/docs/developers/Conan.md index 185c85af5..af99c5495 100644 --- a/docs/developers/Conan.md +++ b/docs/developers/Conan.md @@ -1,4 +1,4 @@ -# Using dependencies from Conan +# Conan Dependencies [Conan](https://conan.io/) is a package manager for C/C++. We provide prebuilt binary dependencies for some platforms that are used by our CI, but they can also be consumed by users to build VCMI. However, it's not required to use only the prebuilt binaries: you can build them from source as well. diff --git a/docs/developers/Development_with_Qt_Creator.md b/docs/developers/Development_with_Qt_Creator.md index 1153ae016..4e3c96f8e 100644 --- a/docs/developers/Development_with_Qt_Creator.md +++ b/docs/developers/Development_with_Qt_Creator.md @@ -1,4 +1,4 @@ -# Introduction to Qt Creator +# Development with Qt Creator Qt Creator is the recommended IDE for VCMI development on Linux distributions, but it may be used on other operating systems as well. It has the following advantages compared to other IDEs: diff --git a/docs/developers/Logging_API.md b/docs/developers/Logging_API.md index 6b53234b4..a3ce23b76 100644 --- a/docs/developers/Logging_API.md +++ b/docs/developers/Logging_API.md @@ -1,4 +1,6 @@ -# Features +# Logging API + +## Features - A logger belongs to a "domain", this enables us to change log level settings more selectively - The log format can be customized @@ -9,7 +11,7 @@ - Macros for tracing the application flow - Provides stream-like and function-like logging -# Class diagram +## Class diagram ![Logging_Class_Diagram](https://github.com/IvanSavenko/vcmi/assets/1576820/31c9b14e-a055-4b07-87fe-00da43430a2b) @@ -18,9 +20,9 @@ Some notes: - There are two methods `configure` and `configureDefault` of the class `CBasicLogConfigurator` to initialize and setup the logging system. The latter one setups default logging and isn't dependent on VCMI's filesystem, whereas the first one setups logging based on the user's settings which can be configured in the settings.json. - The methods `isDebugEnabled` and `isTraceEnabled` return true if a log record of level debug respectively trace will be logged. This can be useful if composing the log message is a expensive task and performance is important. -# Usage +## Usage -## Setup settings.json +### Setup settings.json ``` javascript { @@ -51,7 +53,7 @@ Some notes: The above code is an example on how to configure logging. It sets the log level to debug globally and the log level of the domain ai to trace. In addition, it tells the console to log debug messages as well with the threshold attribute. Finally, it configures the console so that it logs network trace messages in magenta. -## Configuration +### Configuration The following code shows how the logging system can be configured: @@ -82,14 +84,14 @@ Format: %d %l %n \[%t\] - %m global -\> info -## How to get a logger +### How to get a logger There exist only one logger object per domain. A logger object cannot be copied. You can get access to a logger object by using the globally defined ones like `logGlobal` or `logAi`, etc... or by getting one manually: ```cpp Logger * logger = CLogger::getLogger(CLoggerDomain("rmg")); ``` -## Logging +### Logging Logging can be done via two ways, stream-like or function-like. @@ -119,7 +121,7 @@ The following colors are available for console output: - gray - teal -## How to trace execution +### How to trace execution The program execution can be traced by using the macros TRACE_BEGIN, TRACE_END and their \_PARAMS counterparts. This can be important if you want to analyze the operations/internal workings of the AI or the communication of the client-server. In addition, it can help you to find bugs on a foreign VCMI installation with a custom mod configuration. @@ -135,9 +137,9 @@ The program execution can be traced by using the macros TRACE_BEGIN, TRACE_END a } ``` -# Concepts +## Concepts -## Domain +### Domain A domain is a specific part of the software. In VCMI there exist several domains: diff --git a/docs/developers/Lua_Scripting_System.md b/docs/developers/Lua_Scripting_System.md index 2c4be7387..16124c800 100644 --- a/docs/developers/Lua_Scripting_System.md +++ b/docs/developers/Lua_Scripting_System.md @@ -1,4 +1,6 @@ -# Configuration +# Lua Scripting System + +## Configuration ``` javascript { @@ -32,13 +34,13 @@ } ``` -# Lua +## Lua -## API Reference +### API Reference TODO **In near future Lua API may change drastically several times. Information here may be outdated** -### Globals +#### Globals - DATA - persistent table - - DATA.ERM contains ERM state, anything else is free to use. @@ -61,7 +63,7 @@ TODO **In near future Lua API may change drastically several times. Information - -TODO require(":relative.path.to.module") - loads module from same mod - logError(text) - backup error log function -### Low level events API +#### Low level events API ``` Lua @@ -79,7 +81,7 @@ sub2 = PlayerGotTurn.subscribeBefore(EVENT_BUS, function(event) end) ``` -### Lua standard library +#### Lua standard library VCMI uses LuaJIT, which is Lua 5.1 API, see [upstream documentation](https://www.lua.org/manual/5.1/manual.html) @@ -91,19 +93,19 @@ Following libraries are supported - math - bit -# ERM +## ERM -## Features +### Features - no strict limit on function/variable numbers (technical limit 32 bit integer except 0)) - TODO semi compare - DONE macros -## Bugs +### Bugs - TODO Broken XOR support (clashes with \`X\` option) -## Triggers +### Triggers - TODO **!?AE** Equip/Unequip artifact - WIP **!?BA** when any battle occurs @@ -134,22 +136,22 @@ Following libraries are supported - TODO **!?TL** Real-Time Timer - TODO **!?TM** timed events -## Receivers +### Receivers -### VCMI +#### VCMI - **!!MC:S@varName@** - declare new "normal" variable (technically v-var with string key) - TODO Identifier resolver - WIP Bonus system -### ERA +#### ERA - DONE !!if !!el !!en - TODO !!br !!co - TODO !!SN:X -### WoG +#### WoG - TODO !!AR Артефакт (ресурс) в определенной позиции - TODO !!BA Битва @@ -199,4 +201,4 @@ Following libraries are supported - *!#VC Контроль переменных* - WIP !!VR Установка переменных -## Persistence \ No newline at end of file +### Persistence \ No newline at end of file diff --git a/docs/developers/Networking.md b/docs/developers/Networking.md index 7bcadc437..5c3736abe 100644 --- a/docs/developers/Networking.md +++ b/docs/developers/Networking.md @@ -1,4 +1,6 @@ -# The big picture +# Networking + +## The big picture For implementation details see files located at `lib/network` directory. @@ -19,11 +21,11 @@ Following connections can be established during game lifetime: - game client -> lobby server: This connection is used to access global lobby, for multiplayer over Internet. Created when player logs into a lobby (Multiplayer -> Connect to global service) - match server -> lobby server: This connection is established when player creates new multiplayer room via lobby. It is used by lobby server to send commands to match server -# Gameplay communication +## Gameplay communication For gameplay, VCMI serializes data into a binary stream. See [Serialization](Serialization.md) for more information. -# Global lobby communication +## Global lobby communication For implementation details see: - game client: `client/globalLobby/GlobalLobbyClient.h @@ -38,29 +40,29 @@ char jsonString[messageSize]; Every message must be a struct (json object) that contains "type" field. Unlike rest of VCMI codebase, this message is validated as strict json, without any extensions, such as comments. -## Communication flow +### Communication flow Notes: - invalid message, such as corrupted json format or failure to validate message will result in no reply from server - in addition to specified messages, match server will send `operationFailed` message on failure to apply player request -### New Account Creation +#### New Account Creation - client -> lobby: `clientRegister` - lobby -> client: `accountCreated` -### Login +#### Login - client -> lobby: `clientLogin` - lobby -> client: `loginSuccess` - lobby -> client: `chatHistory` - lobby -> client: `activeAccounts` - lobby -> client: `activeGameRooms` -### Chat Message +#### Chat Message - client -> lobby: `sendChatMessage` - lobby -> every client: `chatMessage` -### New Game Room +#### New Game Room - client starts match server instance - match -> lobby: `serverLogin` - lobby -> match: `loginSuccess` @@ -70,24 +72,24 @@ Notes: - lobby -> every client: `activeAccounts` - lobby -> every client: `activeGameRooms` -### Joining a game room +#### Joining a game room See [#Proxy mode](proxy-mode) -### Leaving a game room +#### Leaving a game room - client closes connection to match server - match -> lobby: `leaveGameRoom` -### Sending an invite: +#### Sending an invite: - client -> lobby: `sendInvite` - lobby -> target client: `inviteReceived` Note: there is no dedicated procedure to accept an invite. Instead, invited player will use same flow as when joining public game room -### Logout +#### Logout - client closes connection - lobby -> every client: `activeAccounts` -## Proxy mode +### Proxy mode In order to connect players located behind NAT, VCMI lobby can operate in "proxy" mode. In this mode, connection will be act as proxy and will transmit gameplay data from client to a match server, without any data processing on lobby server. diff --git a/docs/developers/RMG_Description.md b/docs/developers/RMG_Description.md index 7a9d7810c..59a6b683a 100644 --- a/docs/developers/RMG_Description.md +++ b/docs/developers/RMG_Description.md @@ -1,46 +1,48 @@ -# Fundamentals +# RMG Description + +## Fundamentals Random maps are represented by undirected graph of zones linked with connections. On maps with water, a single extra water zone is created. -## Modifiers +### Modifiers Zone filling process is split into multiple phases, each of them represented as a modifier. A modifier can require other modifiers to finish their job before launching. A modifier might be preceded by other modifier from every zone, or many modifiers from all zones. For instance, placing underground rock requires all underground zones to finish treasure placement first. -## Thread pool +### Thread pool A queue of Modifiers jobs is created in roughly topological order, so that Modificators with no dependencies are placed first. The queue is iterated in a circular manner and if Modificator with no remaining preceders is found, it is picked for execution in a separate thread. After job completion, Modifier is erased from dependencies of Modifiers which depend on it. -# Placing Zones +## Placing Zones -## Generating distance graph +### Generating distance graph Based on zone connections, a simple distance graph is created using Dijkstra algorithm. -## Initial zone placement +### Initial zone placement Based on distance graph, zones are placed one by one on N x N grid of size just enough to fit all the zones (for instance, 5 zones are placed on a 3 x 3 grid and 24 zones on 5 x 5 grid). Adjacent zones are placed close while distant zones are placed as far away from each other as possible. -## Iterative optimization +### Iterative optimization Finally, zones are moved from their initial positions using Fruchterman-Reingold algorithm. It assumes all the zones are soft spheres, which attract connected zones as springs but push back not overlapping zones crossing their borders. These forces are summend and determine the vector shift of the zone position. The algorithm uses classic "simulated annealing" approach - zones start with high "temperature" (are very soft and squishy) and then gradually become colder (harder) and push away overlapping zones with stronger force. To prevent getting stuck in local minima, sometimes most misplaced zones swap placed manually. -## Penrose Tiling +### Penrose Tiling Using iterative subdivision, a set of Penrose tiling vertices ic created at random orientation and centered over middle of the map. All the tiles on a map are assigned to the closes vertex. Then, every vertex is assigned to the closest zone, creating irregular shapes. -# Zone connections +## Zone connections Directly adjacent zones are connected with a guard and a road, and overlapping zones on different levels are connected with Subterranean Gate. Zones which are not directly adjacent might be covered through water zone. If a zone shouldn't be connected with nearby zones adjacent to body of water, it's coast is sealed with obstacles. -## Water routes +### Water routes **TODO** -## Fractalization +### Fractalization Every zone starts with at least one free tile in the center. Now, from every tiles at a distant greater than some number, a random tile is chosen. From it, algorithm routes a free path connected to already existing free paths. Process is repetaed until no tiles distant from free paths are left. Tiles used for connections are marked `free` and nothing else can be placed on them. @@ -48,28 +50,28 @@ Zones with type `junction` are not fractalized. The remaining tiles that are not obstacles (such as zone edges) are marked as `possible"` so they can be either filled with treasures or left `free.` -# Treasures +## Treasures Every object or treasure pile in the zone is placed as far away from existing objects as possible. This includes towns and zone guards placed first. Zone keeps a priority queue of tiles sorted by their distance to closest object. Whenever an object is placed, these distances are updated. -## Treasure generation +### Treasure generation Treasures are separated in value ranges. The highest range is picked first. A large number of treasure piles is generated, then RMG tries to fit each of treasure piles into a zone. Then, lower treasure ranges are generated -## Treasure placement +### Treasure placement New treasure pile can't be closer to any previous object than some distance determined by total treasure density and value. Lower value piles and placed with lower minimal distance. Any new treasure is placed so that it can't join two previously separated blocked islands, to prevent sealing a gap and ensuring entire zone is passable. -# Obstacles +## Obstacles After all the treasures are placed, tiles marked as `possible` are iteratively stripped and cleared from lose appendages, leaving `free` space. Then remaining ones are marked as `blocked`, and covered with obstacles. -## Biomes +### Biomes For every zone, a few random obstacle sets are selected. [Details](https://github.com/vcmi/vcmi/blob/develop/docs/modders/Entities_Format/Biome_Format.md) -## Filling space +### Filling space Tiles which need to be `blocked` but are not `used` are filled with obstacles. Largest obstacles which cover the most tiles are picked first, other than that they are chosen randomly. \ No newline at end of file diff --git a/docs/developers/Serialization.md b/docs/developers/Serialization.md index 2b582f227..143eaf0ad 100644 --- a/docs/developers/Serialization.md +++ b/docs/developers/Serialization.md @@ -1,24 +1,26 @@ -# Introduction +# Serialization + +## Introduction The serializer translates between objects living in our code (like int or CGameState\*) and stream of bytes. Having objects represented as a stream of bytes is useful. Such bytes can send through the network connection (so client and server can communicate) or written to the disk (savegames). VCMI uses binary format. The primitive types are simply copied from memory, more complex structures are represented as a sequence of primitives. -## Typical tasks +### Typical tasks -### Bumping a version number +#### Bumping a version number Different major version of VCMI likely change the format of the save game. Every save game needs a version identifier, that loading can work properly. Backward compatibility isn't supported for now. The version identifier is a constant named version in Connection.h and should be updated every major VCMI version or development version if the format has been changed. Do not change this constant if it's not required as it leads to full rebuilds. Why should the version be updated? If VCMI cannot detect "invalid" save games the program behaviour is random and undefined. It mostly results in a crash. The reason can be anything from null pointer exceptions, index out of bounds exceptions(ok, they aren't available in c++, but you know what I mean:) or invalid objects loading(too much elements in a vector, etc...) This should be avoided at least for public VCMI releases. -### Adding a new class +#### Adding a new class If you want your class to be serializable (eg. being storable in a savegame) you need to define a serialize method template, as described in [#User types](#user-types) Additionally, if your class is part of one of registered object hierarchies (basically: if it derives from CGObjectInstance, IPropagator, ILimiter, CBonusSystemNode, CPack) it needs to be registered. Just add an appropriate entry in the `RegisterTypes.h` file. See polymorphic serialization for more information. -# How does it work +## How does it work -## Primitive types +### Primitive types They are simply stored in a binary form, as in memory. Compatibility is ensued through the following means: @@ -27,21 +29,21 @@ They are simply stored in a binary form, as in memory. Compatibility is ensued t It's not "really" portable, yet it works properly across all platforms we currently support. -## Dependant types +### Dependant types -### Pointers +#### Pointers Storing pointers mechanics can be and almost always is customized. See [#Additional features](additional-features). In the most basic form storing pointer simply sends the object state and loading pointer allocates an object (using "new" operator) and fills its state with the stored data. -### Arrays +#### Arrays Serializing array is simply serializing all its elements. -## Standard library types +### Standard library types -### STL Containers +#### STL Containers First the container size is stored, then every single contained element. @@ -56,7 +58,7 @@ Supported STL types include: `pair` `map` -### Smart pointers +#### Smart pointers Smart pointers at the moment are treated as the raw C-style pointers. This is very bad and dangerous for shared_ptr and is expected to be fixed somewhen in the future. @@ -65,14 +67,14 @@ The list of supported data types from standard library: `shared_ptr (partial!!!)` `unique_ptr` -### Boost +#### Boost Additionally, a few types for Boost are supported as well: `variant` `optional` -## User types +### User types To make the user-defined type serializable, it has to provide a template method serialize. The first argument (typed as template parameter) is a reference to serializer. The second one is version number. @@ -98,7 +100,7 @@ struct DLL_LINKAGE Rumor }; ``` -## Backwards compatibility +### Backwards compatibility Serializer, before sending any data, stores its version number. It is passed as the parameter to the serialize method, so conditional code ensuring backwards compatibility can be added. @@ -126,21 +128,21 @@ struct DLL_LINKAGE Rumor }; ``` -## Serializer classes +### Serializer classes -### Common information +#### Common information Serializer classes provide iostream-like interface with operator `<<` for serialization and operator `>>` for deserialization. Serializer upon creation will retrieve/store some metadata (version number, endianness), so even if no object is actually serialized, some data will be passed. -### Serialization to file +#### Serialization to file CLoadFile/CSaveFile classes allow to read data to file and store data to file. They take filename as the first parameter in constructor and, optionally, the minimum supported version number (default to the current version). If the construction fails (no file or wrong file) the exception is thrown. -### Networking +#### Networking See [Networking](Networking.md) -## Additional features +### Additional features Here is the list of additional custom features serialzier provides. Most of them can be turned on and off. @@ -149,7 +151,7 @@ Here is the list of additional custom features serialzier provides. Most of them - Stack instance serialization — enabled by sendStackInstanceByIds flag. - Smart pointer serialization — enabled by smartPointerSerialization flag. -### Polymorphic serialization +#### Polymorphic serialization Serializer is to recognize the true type of object under the pointer if classes of that hierarchy were previously registered. @@ -170,7 +172,7 @@ Class hierarchies that are now registered to benefit from this feature are mostl It is crucial that classes are registered in the same order in the both serializers (storing and loading). -### Vectorized list member serialization +#### Vectorized list member serialization Both client and server store their own copies of game state and VLC (handlers with data from config). Many game logic objects are stored in the vectors and possess a unique id number that represent also their position in such vector. @@ -219,7 +221,7 @@ Important: this means that the object state is not serialized. This feature makes sense only for server-client network communication. -### Stack instance serialization +#### Stack instance serialization This feature works very much like the vectorised object serialization. It is like its special case for stack instances that are not vectorised (each hero owns its map). When this option is turned on, sending CStackInstance\* will actually send an owning object (town, hero, garrison, etc) id and the stack slot position. @@ -227,7 +229,7 @@ For this to work, obviously, both sides of the connection need to have exactly t This feature depends on vectorised member serialization being turned on. (Sending owning object by id.) -### Smart pointer serialization +#### Smart pointer serialization Note: name is unfortunate, this feature is not about smart pointers (like shared-ptr and unique_ptr). It is for raw C-style pointers, that happen to point to the same object. diff --git a/docs/maintainers/Release_Process.md b/docs/maintainers/Release_Process.md index eaa6108e0..51d7a8e1a 100644 --- a/docs/maintainers/Release_Process.md +++ b/docs/maintainers/Release_Process.md @@ -1,3 +1,5 @@ +# Release Process + ## Versioning For releases VCMI uses version numbering in form "1.X.Y", where: - 'X' indicates major release. Different major versions are generally not compatible with each other. Save format is different, network protocol is different, mod format likely different. diff --git a/docs/maintainers/Ubuntu_PPA.md b/docs/maintainers/Ubuntu_PPA.md index 220df1bff..8ada53d86 100644 --- a/docs/maintainers/Ubuntu_PPA.md +++ b/docs/maintainers/Ubuntu_PPA.md @@ -1,3 +1,5 @@ +# Ubuntu PPA + ## Main links - [Team](https://launchpad.net/~vcmi) - [Project](https://launchpad.net/vcmi) diff --git a/docs/modders/Animation_Format.md b/docs/modders/Animation_Format.md index a4a0d9896..2aece5165 100644 --- a/docs/modders/Animation_Format.md +++ b/docs/modders/Animation_Format.md @@ -1,3 +1,5 @@ +# Animation Format + VCMI allows overriding HoMM3 .def files with .json replacement. Compared to .def this format allows: - Overriding individual frames from json file (e.g. icons) diff --git a/docs/modders/Bonus/Bonus_Duration_Types.md b/docs/modders/Bonus/Bonus_Duration_Types.md index cf276ed7a..3c20d0e7c 100644 --- a/docs/modders/Bonus/Bonus_Duration_Types.md +++ b/docs/modders/Bonus/Bonus_Duration_Types.md @@ -1,3 +1,5 @@ +# Bonus Duration Types + Bonus may have any of these durations. They acts in disjunction. ## List of all bonus duration types diff --git a/docs/modders/Bonus/Bonus_Limiters.md b/docs/modders/Bonus/Bonus_Limiters.md index 6c422acb8..5dc9ee46a 100644 --- a/docs/modders/Bonus/Bonus_Limiters.md +++ b/docs/modders/Bonus/Bonus_Limiters.md @@ -1,3 +1,5 @@ +# Bonus Limiters + ## Predefined Limiters The limiters take no parameters: diff --git a/docs/modders/Bonus/Bonus_Propagators.md b/docs/modders/Bonus/Bonus_Propagators.md index 73b8b3004..90f09cf84 100644 --- a/docs/modders/Bonus/Bonus_Propagators.md +++ b/docs/modders/Bonus/Bonus_Propagators.md @@ -1,3 +1,5 @@ +# Bonus Propagators + ## Available propagators - BATTLE_WIDE: Affects both sides during battle diff --git a/docs/modders/Bonus/Bonus_Range_Types.md b/docs/modders/Bonus/Bonus_Range_Types.md index 447632847..5be67c077 100644 --- a/docs/modders/Bonus/Bonus_Range_Types.md +++ b/docs/modders/Bonus/Bonus_Range_Types.md @@ -1,3 +1,5 @@ +# Bonus Range Types + ## List of all Bonus range types - NO_LIMIT diff --git a/docs/modders/Bonus/Bonus_Sources.md b/docs/modders/Bonus/Bonus_Sources.md index 44fd6ed10..86754e407 100644 --- a/docs/modders/Bonus/Bonus_Sources.md +++ b/docs/modders/Bonus/Bonus_Sources.md @@ -1,3 +1,5 @@ +# Bonus Sources + ## List of all possible bonus sources - ARTIFACT diff --git a/docs/modders/Bonus/Bonus_Types.md b/docs/modders/Bonus/Bonus_Types.md index d897d7a7f..82f389103 100644 --- a/docs/modders/Bonus/Bonus_Types.md +++ b/docs/modders/Bonus/Bonus_Types.md @@ -1,3 +1,5 @@ +# Bonus Types + The bonuses were grouped according to their original purpose. The bonus system allows them to propagate freely between the nodes, however they may not be recognized properly beyond the scope of original use. ## General-purpose bonuses diff --git a/docs/modders/Bonus/Bonus_Updaters.md b/docs/modders/Bonus/Bonus_Updaters.md index d8a33b6de..f0d95f940 100644 --- a/docs/modders/Bonus/Bonus_Updaters.md +++ b/docs/modders/Bonus/Bonus_Updaters.md @@ -1,3 +1,5 @@ +# Bonus Updaters + TODO: this page may be incorrect or outdated Updaters come in two forms: simple and complex. Simple updaters take no diff --git a/docs/modders/Bonus/Bonus_Value_Types.md b/docs/modders/Bonus/Bonus_Value_Types.md index f33be82c4..aca624516 100644 --- a/docs/modders/Bonus/Bonus_Value_Types.md +++ b/docs/modders/Bonus/Bonus_Value_Types.md @@ -1,3 +1,5 @@ +# Bonus Value Types + Total value of Bonus is calculated using the following: - For each bonus source type we calculate new source value (for all bonus value types except PERCENT_TO_SOURCE and PERCENT_TO_TARGET_TYPE) using the following: diff --git a/docs/modders/Bonus_Format.md b/docs/modders/Bonus_Format.md index f3a7c671c..5dce1c1d0 100644 --- a/docs/modders/Bonus_Format.md +++ b/docs/modders/Bonus_Format.md @@ -1,3 +1,5 @@ +# Bonus Format + ## Full format All parameters but type are optional. diff --git a/docs/modders/Building_Bonuses.md b/docs/modders/Building_Bonuses.md index cee6a9cb5..ac4e0c7d9 100644 --- a/docs/modders/Building_Bonuses.md +++ b/docs/modders/Building_Bonuses.md @@ -1,9 +1,11 @@ +# Building Bonuses + Work-in-progress page do describe all bonuses provided by town buildings for future configuration. TODO: This page is outdated and may not represent VCMI 1.3 state -## unique buildings +### unique buildings Hardcoded functionalities, selectable but not configurable. In future should be moved to scripting. @@ -24,7 +26,7 @@ Function of all of these objects can be enabled by this: "function" : "castleGates" ``` -## trade-related +### trade-related Hardcoded functionality for now due to complexity of these objects. Temporary can be handles as unique buildings. Includes: @@ -37,7 +39,7 @@ Temporary can be handles as unique buildings. Includes: - resource - skills - creature - skeleton -## hero visitables +### hero visitables Buildings that give one or another bonus to visiting hero. All should be handled via configurable objects system. @@ -49,76 +51,76 @@ Includes: - give bonus to visitor - permanent bonus to hero -## generic functions +### generic functions Generic town-specific functions that can be implemented as part of CBuilding class. -### unlock guild level +#### unlock guild level ``` javascript "guildLevels" : 1 ``` -### unlock hero recruitment +#### unlock hero recruitment ``` javascript "allowsHeroPurchase" : true ``` -### unlock ship purchase +#### unlock ship purchase ``` javascript "allowsShipPurchase" : true ``` -### unlock building purchase +#### unlock building purchase ``` javascript "allowsBuildingPurchase" : true ``` -### unlocks creatures +#### unlocks creatures ``` javascript "dwelling" : { "level" : 1, "creature" : "archer" } ``` -### creature growth bonus +#### creature growth bonus Turn into town bonus? What about creature-specific bonuses from hordes? -### gives resources +#### gives resources ``` javascript "provides" : { "gold" : 500 } ``` -### gives guild spells +#### gives guild spells ``` javascript "guildSpells" : [5, 0, 0, 0, 0] ``` -### gives thieves guild +#### gives thieves guild ``` javascript "thievesGuildLevels" : 1 ``` -### gives fortifications +#### gives fortifications ``` javascript "fortificationLevels" : 1 ``` -### gives war machine +#### gives war machine ``` javascript "warMachine" : "ballista" ``` -## simple bonuses +### simple bonuses Bonuses that can be made part of CBuilding. Note that due to how bonus system works this bonuses won't be stackable. @@ -149,12 +151,12 @@ Includes: } ``` -## misc +### misc Some other properties of town building that does not fall under "bonus" category. -### unique building +#### unique building Possible issue - with removing of fixed ID's buildings in different town may no longer share same ID. However Capitol must be unique across all @@ -164,7 +166,7 @@ town. Should be fixed somehow. "onePerPlayer" : true ``` -### chance to be built on start +#### chance to be built on start ``` javascript "prebuiltChance" : 75 diff --git a/docs/modders/Campaign_Format.md b/docs/modders/Campaign_Format.md index e020531cd..554e98566 100644 --- a/docs/modders/Campaign_Format.md +++ b/docs/modders/Campaign_Format.md @@ -1,4 +1,6 @@ -# Introduction +# Campaign Format + +## Introduction Starting from version 1.3, VCMI supports its own campaign format. Campaigns have *.vcmp file format and it consists from campaign json and set of scenarios (can be both *.vmap and *.h3m) @@ -27,7 +29,7 @@ Basic structure of this file is here, each section is described in details below `"version"` defines version of campaign file. Larger versions should have more features and flexibility, but may not be supported by older VCMI engines. See [compatibility table](#compatibility-table) -# Header properties +## Header properties In header are parameters describing campaign properties ```js @@ -51,7 +53,7 @@ In header are parameters describing campaign properties - `"creationDateTime"` unix time of campaign creation - `"allowDifficultySelection"` is a boolean field (`true`/`false`) which allows or disallows to choose difficulty before scenario start -# Scenario description +## Scenario description Scenario description looks like follow: ```js @@ -87,7 +89,7 @@ Scenario description looks like follow: - `"playerColor"` defines color id of flag which player will play for. Possible values are `0: red, 1: blue, tan: 2, green: 3, orange: 4, purple: 5, teal: 6, pink: 7` - "bonuses" array of possible bonus objects, format depends on `"startOptions"` parameter -## Prolog/Epilog +### Prolog/Epilog Prolog and epilog properties are optional ```js @@ -99,13 +101,13 @@ Prolog and epilog properties are optional } ``` -## Start options and bonuses +### Start options and bonuses -### None start option +#### None start option If `startOptions` is `none`, `bonuses` field will be ignored -### Bonus start option +#### Bonus start option If `startOptions` is `bonus`, bonus format may vary depending on its type. @@ -148,7 +150,7 @@ If `startOptions` is `bonus`, bonus format may vary depending on its type. - `"amount"`: amount of resources - `"hero"` can be specified as explicit hero name and as one of keywords: `strongest`, `generated` -### Crossover start option +#### Crossover start option If `startOptions` is `crossover`, heroes from specific scenario will be moved to this scenario. Bonus format is following @@ -161,7 +163,7 @@ If `startOptions` is `crossover`, heroes from specific scenario will be moved to - `"playerColor"` from what player color heroes shall be taken. Possible values are `0: red, 1: blue, tan: 2, green: 3, orange: 4, purple: 5, teal: 6, pink: 7` - `"scenario"` from which scenario heroes shall be taken. 0 means first scenario -### Hero start option +#### Hero start option If `startOptions` is `hero`, hero can be chosen as a starting bonus. Bonus format is following ```js @@ -174,7 +176,7 @@ If `startOptions` is `hero`, hero can be chosen as a starting bonus. Bonus forma - `"playerColor"` from what player color heroes shall be taken. Possible values are `0: red, 1: blue, tan: 2, green: 3, orange: 4, purple: 5, teal: 6, pink: 7` - `"hero"` can be specified as explicit hero name and as one of keywords: `random` -## Regions description +### Regions description Predefined campaign regions are located in file `campaign_regions.json` @@ -194,7 +196,7 @@ Predefined campaign regions are located in file `campaign_regions.json` - `"inflix"` ised to identify all images related to region. In this example, it will be pictures starting from `G3A_..., G3B_..., G3C_..."` - `"color_suffix_length"` identifies suffix length for region colourful frames. 1 is used for `R, B, N, G, O, V, T, P`, value 2 is used for `Re, Bl, Br, Gr, Or, Vi, Te, Pi` -# Packing campaign +## Packing campaign After campaign scenarios and campaign description are ready, you should pack them into *.vcmp file. This file is basically headless gz archive. @@ -216,7 +218,7 @@ gzip -c -n ./* >> my-campaign.vcmp If you are using Windows system, try this https://gnuwin32.sourceforge.net/packages/gzip.htm -# Compatibility table +## Compatibility table | Version | Min VCMI | Max VCMI | Description | |---------|----------|----------|-------------| | 1 | 1.3 | | Initial release | \ No newline at end of file diff --git a/docs/modders/Configurable_Widgets.md b/docs/modders/Configurable_Widgets.md index 0649b866b..7a1ea901a 100644 --- a/docs/modders/Configurable_Widgets.md +++ b/docs/modders/Configurable_Widgets.md @@ -1,10 +1,12 @@ -# Introduction +# Configurable Widgets + +## Introduction VCMI has capabilities to change some UI elements in your mods beyond only replacing one image with another. Not all UI elements are possible to modify currently, but development team is expanding them. Elements possible to modify are located in `config/widgets`. -# Tutorial +## Tutorial Let's take `extendedLobby` mod from `vcmi-extras` as an example for VCMI-1.4. [Example sources](https://github.com/vcmi-mods/vcmi-extras/tree/vcmi-1.4/Mods/extendedLobby). @@ -16,7 +18,7 @@ For options tab it introduces UI for chess timers. In this tutorial we will recreate options tab to support chess timers UI. -## Creating mod structure +### Creating mod structure To start making mod, create following folders structure; ``` @@ -44,7 +46,7 @@ File `mod.json` is generic and could look like this: After that you can copy `extendedLobby/ folder to `mods/` folder and your mod will immediately appear in launcher but it does nothing for now. -## Making layout for timer +### Making layout for timer Let's copy `config/widgets/optionsTab.json` file from VCMI folder to `content/config/widgets/` folder from our mod. It defines UI for options tab as it designed in original game, we will keep everything related to player settings and will modify only timer area. @@ -111,7 +113,7 @@ In order to make it work, add file `RmgTTBk.bmp` to `content/sprites/` Elements named `labelTurnDurationValue` and `sliderTurnDuration` we will keep without change - they are needed to configure classic timer. -## Adding combo box +### Adding combo box Now, let's add combo box. @@ -264,7 +266,7 @@ Now specify items inside `dropDown` field Now we can press drop-down menu and even select elements. -## Switching timer modes +### Switching timer modes After view part is done, let's make behavioural part. Let's hide elements, related to classic timer when chess timer is selected and show them back if classic selected. @@ -322,7 +324,7 @@ This background must be visible for chess timer and hidden for classic timer. Ju It works and can switch elements, the only missing part is chess timer configuration. -## Chess timer configuration +### Chess timer configuration We should add text input fields, to specify different timers. We will use background for them `timerField.bmp`, copy it to `content/sprites/` folder of your mod. @@ -352,39 +354,39 @@ And what we want to do is to hide/show those fields when classic/chess times is We are done! You can find more information about configurable UI elements in documentation section. -# Documentation +## Documentation -## Types +### Types All fields have format `"key": value` There are different basic types, which can be used as value. -### Primitive types +#### Primitive types Read JSON documentation for primitive types description: https://www.json.org/json-en.html -### Text +#### Text Load predefined text which can be localised, examples: `"vcmi.otherOptions.availableCreaturesAsDwellingLabel"` `"core.genrltxt.738"` -### Position +#### Position Point with two coordinates, example: `{ "x": 43, "y": -28 }` -### Rect +#### Rect Rectangle ares, example: `{ "x": 28, "y": 220, "w": 108, "h": 50 }` -### Text alignment +#### Text alignment Defines text alignment, can be one of values: `"center"`, `"left"`, `"right"` -### Color +#### Color Predefined colors: `"yellow"`, `"white"`, `"gold"`, `"green"`, `"orange"`, `"bright-yellow"` @@ -392,12 +394,12 @@ Predefined colors: To have custom color make an array of four elements in RGBA notation: `[255, 128, 0, 255]` -### Font +#### Font Predefined fonts: `"big"`, `"medium"`, `"small"`, `"tiny"`, `"calisto"` -### Hint text +#### Hint text Hint text is a pair of strings, one is usually shown in status bar when cursor hovers element, another hint while right button pressed. Each of elements is a [Text](#text) @@ -413,19 +415,19 @@ If one string specified, it will be applied for both hover and help. `"text"` -### Shortcut +#### Shortcut String value defines shortcut. Some examples of shortcuts: `"globalAccept", "globalCancel", "globalReturn","globalFullscreen", "globalOptions", "globalBackspace", "globalMoveFocus"` Full list is TBD -### [VCMI-1.4] Player color +#### [VCMI-1.4] Player color One of predefined values: `"red"`, `"blue"`, `"tan"`, `"green"`, `"orange"`, `"purple"`, `"teal"`, `"pink"` -## Configurable objects +### Configurable objects Configurable object has following structure: ```json @@ -445,9 +447,9 @@ Configurable object has following structure: `library` - same as above, but custom widgets are described in separate json, this parameter should contain path to library json is specified -## Basic widgets +### Basic widgets -### Label +#### Label `"type": "label"` @@ -463,7 +465,7 @@ Configurable object has following structure: `"position"`: [position](#position) -### [VCMI-1.4] Multi-line label +#### [VCMI-1.4] Multi-line label `"type": "multiLineLabel"` @@ -483,7 +485,7 @@ Configurable object has following structure: `"adoptHeight": bool` //if true, text area height will be adopted automatically based on content -### Label group +#### Label group `"type": "labelGroup"` @@ -503,7 +505,7 @@ Configurable object has following structure: `"text"`: [text](#text), -### TextBox +#### TextBox `"type": "textBox"` @@ -519,7 +521,7 @@ Configurable object has following structure: `"rect"`: [rect](#rect) -### Picture +#### Picture `"type": "picture"` @@ -533,7 +535,7 @@ Configurable object has following structure: `"playerColored", bool`, optional, if true will be colorised to current player -### Image +#### Image Use to show single frame from animation @@ -549,7 +551,7 @@ Use to show single frame from animation `"frame": integer` optional, specify animation frame -### Texture +#### Texture Filling area with texture @@ -561,7 +563,7 @@ Filling area with texture `"rect"`: [rect](#rect) -### TransparentFilledRectangle +#### TransparentFilledRectangle `"type": "transparentFilledRectangle"` @@ -573,7 +575,7 @@ Filling area with texture `"rect"`: [rect](#rect) -### Animation +#### Animation `"type": "animation"` @@ -599,7 +601,7 @@ Filling area with texture `"end": integer`, last frame -### [VCMI-1.4] Text input +#### [VCMI-1.4] Text input `"type": "textInput"` @@ -625,7 +627,7 @@ Filling area with texture `"callback": string` optional, callback to be called on text changed. Input text is passed to callback function as an argument. -### Button +#### Button `"type": "button"` @@ -647,7 +649,7 @@ Filling area with texture `"items": []` array of widgets to be shown as overlay (caption [label](#label), for example) -### Toggle button +#### Toggle button `"type": "toggleButton"` @@ -667,7 +669,7 @@ Filling area with texture `"items": []` array of widgets to be shown as overlay (caption [label](#label), for example) -### Toggle group +#### Toggle group Group of [toggle buttons](#toggle-button), when one is selected, other will be de-selected @@ -681,7 +683,7 @@ Group of [toggle buttons](#toggle-button), when one is selected, other will be d `"items": []` array of [toggle buttons](#toggle-button) -### Slider +#### Slider `"type": "slider"` @@ -707,7 +709,7 @@ Group of [toggle buttons](#toggle-button), when one is selected, other will be d `"panningStep": integer`, optional -### Combo box +#### Combo box `"type": "comboBox"` @@ -729,7 +731,7 @@ Group of [toggle buttons](#toggle-button), when one is selected, other will be d `"dropDown" : {}` description of [drop down](#drop-down) menu widget -### Drop down +#### Drop down Used only as special object for [combo box](#combo-box) @@ -750,17 +752,17 @@ Used only as special object for [combo box](#combo-box) **Callbacks** - `sliderMove` connect to slider callback to correctly navigate over elements -### Layout +#### Layout `"type": "layout"` `"name": "string"` optional, object name -## High-level widgets +### High-level widgets -## Custom widgets +### Custom widgets -# For developers +## For developers While designing a new element, you can make it configurable to reuse all functionality described above. It will provide flexibility to further changes as well as modding capabilities. @@ -798,9 +800,9 @@ MyYesNoDialog::MyYesNoDialog(const JsonNode & config): } ``` -## Callbacks +### Callbacks -## Custom widgets +### Custom widgets You can build custom widgets, related to your UI element specifically. Like in example above, there is Item widget, which can be also used on JSON config. @@ -832,7 +834,7 @@ After that, if your JSON file has items with type "MyItem", the new Item element } ``` -## Variables +### Variables After calling `build(config)` variables defined in config JSON file become available. You can interpret them and use in callbacks or in element code diff --git a/docs/modders/Difficulty.md b/docs/modders/Difficulty.md index e70a09ca3..91f78af9c 100644 --- a/docs/modders/Difficulty.md +++ b/docs/modders/Difficulty.md @@ -1,3 +1,5 @@ +# Difficulty + Since VCMI 1.4.0 there are more capabilities to configure difficulty parameters. It means, that modders can give different bonuses to AI or human players depending on selected difficulty diff --git a/docs/modders/Entities_Format/Artifact_Format.md b/docs/modders/Entities_Format/Artifact_Format.md index 72299d2b9..928496dc7 100644 --- a/docs/modders/Entities_Format/Artifact_Format.md +++ b/docs/modders/Entities_Format/Artifact_Format.md @@ -1,3 +1,5 @@ +# Artifact Format + Artifact bonuses use [Bonus Format](../Bonus_Format.md) ## Required data diff --git a/docs/modders/Entities_Format/Battle_Obstacle_Format.md b/docs/modders/Entities_Format/Battle_Obstacle_Format.md index 9d769ec29..e9b00d3d6 100644 --- a/docs/modders/Entities_Format/Battle_Obstacle_Format.md +++ b/docs/modders/Entities_Format/Battle_Obstacle_Format.md @@ -1,3 +1,5 @@ +# Battle Obstacle Format + ```jsonc // List of terrains on which this obstacle can be used "allowedTerrains" : [] diff --git a/docs/modders/Entities_Format/Battlefield_Format.md b/docs/modders/Entities_Format/Battlefield_Format.md index 2d8164228..5c8729e81 100644 --- a/docs/modders/Entities_Format/Battlefield_Format.md +++ b/docs/modders/Entities_Format/Battlefield_Format.md @@ -1,3 +1,5 @@ +# Battlefield Format + ```jsonc // Human-readable name of the battlefield "name" : "" diff --git a/docs/modders/Entities_Format/Biome_Format.md b/docs/modders/Entities_Format/Biome_Format.md index da50664a9..a0d2a3689 100644 --- a/docs/modders/Entities_Format/Biome_Format.md +++ b/docs/modders/Entities_Format/Biome_Format.md @@ -1,3 +1,5 @@ +# Biome Format + ## General description Biome is a new entity type added in VCMI 1.5.0. It defines a set of random map obstacles which will be generated together. For each zone different obstacle sets is randomized and then only obstacles from that set will be used to fill this zone. diff --git a/docs/modders/Entities_Format/Creature_Format.md b/docs/modders/Entities_Format/Creature_Format.md index ad58c66ad..ecbb2b78e 100644 --- a/docs/modders/Entities_Format/Creature_Format.md +++ b/docs/modders/Entities_Format/Creature_Format.md @@ -1,3 +1,5 @@ +# Creature Format + ## Required data In order to make functional creature you also need: diff --git a/docs/modders/Entities_Format/Faction_Format.md b/docs/modders/Entities_Format/Faction_Format.md index 6fe6c4236..133c9e6a1 100644 --- a/docs/modders/Entities_Format/Faction_Format.md +++ b/docs/modders/Entities_Format/Faction_Format.md @@ -1,3 +1,5 @@ +# Faction Format + ## Required data In order to make functional town you also need: diff --git a/docs/modders/Entities_Format/Hero_Class_Format.md b/docs/modders/Entities_Format/Hero_Class_Format.md index 6bfb54efe..68833fa5d 100644 --- a/docs/modders/Entities_Format/Hero_Class_Format.md +++ b/docs/modders/Entities_Format/Hero_Class_Format.md @@ -1,3 +1,5 @@ +# Hero Class Format + ## Required data In order to make functional hero class you also need: diff --git a/docs/modders/Entities_Format/Hero_Type_Format.md b/docs/modders/Entities_Format/Hero_Type_Format.md index 54c2f9b8f..b646c0e89 100644 --- a/docs/modders/Entities_Format/Hero_Type_Format.md +++ b/docs/modders/Entities_Format/Hero_Type_Format.md @@ -1,3 +1,5 @@ +# Hero Type Format + ## Required data In order to make functional hero you also need: diff --git a/docs/modders/Entities_Format/River_Format.md b/docs/modders/Entities_Format/River_Format.md index 48f29e889..35635768d 100644 --- a/docs/modders/Entities_Format/River_Format.md +++ b/docs/modders/Entities_Format/River_Format.md @@ -1,3 +1,5 @@ +# River Format + ## Format ```jsonc diff --git a/docs/modders/Entities_Format/Road_Format.md b/docs/modders/Entities_Format/Road_Format.md index 564264fc0..d78de9b10 100644 --- a/docs/modders/Entities_Format/Road_Format.md +++ b/docs/modders/Entities_Format/Road_Format.md @@ -1,3 +1,5 @@ +# Road Format + ## Format ```jsonc diff --git a/docs/modders/Entities_Format/Secondary_Skill_Format.md b/docs/modders/Entities_Format/Secondary_Skill_Format.md index 90bbf757c..367ca775f 100644 --- a/docs/modders/Entities_Format/Secondary_Skill_Format.md +++ b/docs/modders/Entities_Format/Secondary_Skill_Format.md @@ -1,3 +1,5 @@ +# Secondary Skill Format + ## Main format ```jsonc diff --git a/docs/modders/Entities_Format/Spell_Format.md b/docs/modders/Entities_Format/Spell_Format.md index 32d68afa0..5e17e817e 100644 --- a/docs/modders/Entities_Format/Spell_Format.md +++ b/docs/modders/Entities_Format/Spell_Format.md @@ -1,3 +1,5 @@ +# Spell Format + ## Main format ``` javascript diff --git a/docs/modders/Entities_Format/Terrain_Format.md b/docs/modders/Entities_Format/Terrain_Format.md index 287ff4386..f8d7e4000 100644 --- a/docs/modders/Entities_Format/Terrain_Format.md +++ b/docs/modders/Entities_Format/Terrain_Format.md @@ -1,3 +1,5 @@ +# Terrain Format + ## Format ```jsonc diff --git a/docs/modders/Game_Identifiers.md b/docs/modders/Game_Identifiers.md index 6317339d1..4e212fe01 100644 --- a/docs/modders/Game_Identifiers.md +++ b/docs/modders/Game_Identifiers.md @@ -1,3 +1,5 @@ +# Game Identifiers + ## List of all game identifiers This is a list of all game identifiers available to modders. Note that only identifiers from base game have been included. For identifiers from mods please look up corresponding mod diff --git a/docs/modders/Map_Editor.md b/docs/modders/Map_Editor.md index 5373adca0..3c472d8a3 100644 --- a/docs/modders/Map_Editor.md +++ b/docs/modders/Map_Editor.md @@ -1,16 +1,18 @@ -# Interface +# Map Editor + +## Interface -# Create the map +## Create the map -## New map +### New map Create the new map by pressing **New** button from the toolbar -### Empty map +#### Empty map To create empty map, define its size by choosing option from drop-down list or enter required size manually in the text fields and press Ok button. Check **Two level map** option to create map with underground. `Note: there are no limits on map size but be careful with sizes larger predefined XL size. It will be processed quite long to create even empty map. Also, it will be difficult to work with the huge maps because of possible performance issues` @@ -19,7 +21,7 @@ Other parameters won't be used for empty map. -### Random map +#### Random map To generate random map, check the **Random map** option and configure map parameters. You can select template from the drop-down list. @@ -32,27 +34,27 @@ Templates are dynamically filtered depending on parameters you choose. -## Map load & save +### Map load & save To load the map, press open and select map file from the browser. You can load both *.h3m and *.vmap formats but for saving *.vmap is allowed only. -# Views +## Views There are 3 buttons switching views Снимок экрана 2022-09-07 в 06 48 08 -### Ground/underground +#### Ground/underground **"U/G"** switches you between ground and underground -### Grid view +#### Grid view **Grid** show/hide grid -### Passability view +#### Passability view **Pass** show/hide passability map @@ -73,7 +75,7 @@ There are 3 buttons switching views Снимок экра
 
 <img width= -### Drawing roads and rivers +#### Drawing roads and rivers Actually, the process to draw rivers or roads is exactly the same as for terrains. You need to select tiles and then choose road/river type from the panel. @@ -85,13 +87,13 @@ To erase roads or rivers, you need to select tiles to be cleaned and press empty _Erasing works either for roads or for rivers, e.g. empty button from the roads tab erases roads only, but not rivers. You also can safely select bigger area, because it won't erase anything on tiles without roads/rivers accordingly_ -## About brushes +### About brushes * Buttons "1", "2", "4" - 1x1, 2x2, 4x4 brush sizes accordingly * Button "[]" - non-additive rectangle selection * Button "O" - lasso brush (not implemented yet) * Button "E" - object erase, not a brush -## Fill obstacles +### Fill obstacles Map editor supports automatic obstacle placement. Obstacle types are automatically selected for appropriate terrain types @@ -105,9 +107,9 @@ To do that, select area (see Setup terrains) and press **Fill** button from the `Note: obstacle placer may occupy few neighbour tiles outside of selected area` -# Manipulating objects +## Manipulating objects -## Adding new objects +### Adding new objects 1. Find the object you'd like to place in the object browser @@ -125,7 +127,7 @@ To do that, select area (see Setup terrains) and press **Fill** button from the **Right click over the scene - cancel object placement** -## Removing objects +### Removing objects 1. **Make sure that no one terrain brush is selected.** To de-select brush click on selected brush again. @@ -134,7 +136,7 @@ To do that, select area (see Setup terrains) and press **Fill** button from the 3. Press **"E"** button from the brush panel or press **delete** on keyboard -## Changing object's properties +### Changing object's properties 1. **Make sure that no one terrain brush is selected.** To de-select brush click on selected brush again. @@ -147,15 +149,15 @@ To do that, select area (see Setup terrains) and press **Fill** button from the 4. You are able to modify properties which are not gray `Note: sometimes there are empty editable fields` -### Assigning player to the object +#### Assigning player to the object Objects with flags can be assigned to the player. Find Owner property in the inspector for selected object, press twice to modify right cell. Type player number from **0 to 7 or type NEUTRAL** for neutral objects. -# Set up the map +## Set up the map You can modify general properties of the map -## Map name and description +### Map name and description 1. Open **Map** menu on the top and select **General** @@ -167,7 +169,7 @@ You can modify general properties of the map -# Player settings +## Player settings Open **Map** menu on the top and select **Player settings" @@ -179,29 +181,29 @@ You will see a window with player settings. Combobox players defines amount of p -# Compatibility questions +## Compatibility questions -## Platform compatibility +### Platform compatibility vcmieditor is a cross-platform application, so in general can support all platforms, supported by VCMI. However, currently it doesn't support mobile platforms. -## Engine compatibility +### Engine compatibility vcmieditor is independent application so potentially it can be installed just in the folder with existing stable vcmi. However, on the initial stages of development compatibility was not preserved because major changes were needed to introduce into vcmi library. So it's recommended to download full package to use editor. -## Map compatibility +### Map compatibility vcmieditor haven't introduced any change into map format yet, so all maps made by vcmieditor can be easily played with any version of vcmi. At the same time, those maps can be open and read in the old map editor and vice verse - maps from old editor can be imported in the new editor. So, full compatibility is ensured here. -## Mod compatibility +### Mod compatibility vcmieditor loads set of mods using exactly same mechanism as game uses and mod manipulations can be done using vcmilaucnher application, just enable or disable mods you want and open editor to use content from those mods. In regards on compatibility, of course you need to play maps with same set of mods as you used in the editor. Good part is that is maps don't use content from the mods (even mods were enabled), it can be played on vcmi without mods as well -# Working With Mods +## Working With Mods -## Enabling and disabling mods +### Enabling and disabling mods The mods mechanism used in map editor is the same as in game. @@ -212,7 +214,7 @@ To enable or disable mods There is no button to start map editor directly from launcher, however you may use this approach to control active mods from any version of vcmi. -## Placing objects from mods +### Placing objects from mods * All objects from mods will be automatically added into objects Browser. You can type mod name into filter field to find them. @@ -222,13 +224,13 @@ There is no button to start map editor directly from launcher, however you may u -## Playing maps with mods +### Playing maps with mods If you place any kind of objects from the mods, obviously, you need those mods to be installed to play the map. Also, you need to activate them. You also may have other mods being activated in addition to what was used during map designing. -### Mod versions +#### Mod versions In the future, the will be support of mods versioning so map will contain information about mods used and game can automatically search and activate required mods or let user know which are required. However, it's not implemented yet \ No newline at end of file diff --git a/docs/modders/Map_Object_Format.md b/docs/modders/Map_Object_Format.md index b2daf6288..b2f5cd01a 100644 --- a/docs/modders/Map_Object_Format.md +++ b/docs/modders/Map_Object_Format.md @@ -1,3 +1,5 @@ +# Map Object Format + ## Description Full object consists from 3 parts: diff --git a/docs/modders/Map_Objects/Boat.md b/docs/modders/Map_Objects/Boat.md index 1f17090f3..8d67581a5 100644 --- a/docs/modders/Map_Objects/Boat.md +++ b/docs/modders/Map_Objects/Boat.md @@ -1,3 +1,5 @@ +# Boat + ``` javascript { // Layer on which this boat moves. Possible values: diff --git a/docs/modders/Map_Objects/Creature_Bank.md b/docs/modders/Map_Objects/Creature_Bank.md index 4a583f270..36a3b6a3d 100644 --- a/docs/modders/Map_Objects/Creature_Bank.md +++ b/docs/modders/Map_Objects/Creature_Bank.md @@ -1,3 +1,5 @@ +# Creature Bank + Reward types for clearing creature bank are limited to resources, creatures, artifacts and spell. Format of rewards is same as in [Rewardable Objects](Rewardable.md) diff --git a/docs/modders/Map_Objects/Dwelling.md b/docs/modders/Map_Objects/Dwelling.md index 84823d0b1..df369c502 100644 --- a/docs/modders/Map_Objects/Dwelling.md +++ b/docs/modders/Map_Objects/Dwelling.md @@ -1,3 +1,5 @@ +# Dwelling + ``` javascript { /// List of creatures in this bank. Each list represents one "level" of bank diff --git a/docs/modders/Map_Objects/Market.md b/docs/modders/Map_Objects/Market.md index 3861db879..2b411dfb8 100644 --- a/docs/modders/Map_Objects/Market.md +++ b/docs/modders/Map_Objects/Market.md @@ -1,3 +1,5 @@ +# Market + ## Market schema Since VCMI-1.3 it's possible to create customizable markets on adventure map. diff --git a/docs/modders/Map_Objects/Rewardable.md b/docs/modders/Map_Objects/Rewardable.md index 6be3e3abe..48426cf00 100644 --- a/docs/modders/Map_Objects/Rewardable.md +++ b/docs/modders/Map_Objects/Rewardable.md @@ -1,3 +1,5 @@ +# Rewardable + ## Base object definition Rewardable object is defined similarly to other objects, with key difference being `handler`. This field must be set to `"handler" : "configurable"` in order for vcmi to use this mode. ```jsonc diff --git a/docs/modders/Mod_File_Format.md b/docs/modders/Mod_File_Format.md index 29e04f2b9..f0ac58070 100644 --- a/docs/modders/Mod_File_Format.md +++ b/docs/modders/Mod_File_Format.md @@ -1,3 +1,5 @@ +# Mod File Format + ## Fields with description of mod ``` javascript diff --git a/docs/modders/Random_Map_Template.md b/docs/modders/Random_Map_Template.md index 10c4eb141..c133b80d8 100644 --- a/docs/modders/Random_Map_Template.md +++ b/docs/modders/Random_Map_Template.md @@ -1,3 +1,5 @@ +# Random Map Template + ## Template format ``` javascript diff --git a/docs/modders/Readme.md b/docs/modders/Readme.md index 33dee4330..5c788b539 100644 --- a/docs/modders/Readme.md +++ b/docs/modders/Readme.md @@ -1,3 +1,5 @@ +# Modding Readme + ## Creating mod To make your own mod you need to create subdirectory in **/Mods/** with name that will be used as identifier for your mod. diff --git a/docs/modders/Translations.md b/docs/modders/Translations.md index 170e4fa54..3915986c5 100644 --- a/docs/modders/Translations.md +++ b/docs/modders/Translations.md @@ -1,3 +1,5 @@ +# Translations + ## List of currently supported languages This is list of all languages that are currently supported by VCMI. If your languages is missing from the list and you wish to translate VCMI - please contact our team and we'll add support for your language in next release. diff --git a/docs/players/Bug_Reporting_Guidelines.md b/docs/players/Bug_Reporting_Guidelines.md index 6ed39fd52..eabe6b6af 100644 --- a/docs/players/Bug_Reporting_Guidelines.md +++ b/docs/players/Bug_Reporting_Guidelines.md @@ -1,3 +1,5 @@ +# Bug Reporting Guidelines + First of all, thanks for your support! If you report a bug we can fix it. But keep in mind that reporting your bugs appropriately makes our (developers') lives easier. Here are a few guidelines that will help you write good bug reports. ## Github bugtracker diff --git a/docs/players/Cheat_Codes.md b/docs/players/Cheat_Codes.md index b8d8c6387..925a9fdef 100644 --- a/docs/players/Cheat_Codes.md +++ b/docs/players/Cheat_Codes.md @@ -1,4 +1,6 @@ -## Cheat Codes +# Cheat Codes + +## Codes Similar to H3, VCMI provides cheat codes to make testing game more convenient. diff --git a/docs/players/Game_Mechanics.md b/docs/players/Game_Mechanics.md index 561d3fdfc..e64f7e94e 100644 --- a/docs/players/Game_Mechanics.md +++ b/docs/players/Game_Mechanics.md @@ -1,3 +1,5 @@ +# Game Mechanics + ## List of features added in VCMI ### High resolutions diff --git a/docs/players/Installation_Android.md b/docs/players/Installation_Android.md index cbbff127d..a28290a1b 100644 --- a/docs/players/Installation_Android.md +++ b/docs/players/Installation_Android.md @@ -1,3 +1,5 @@ +# Installation Android + ## Step 1: Download and install VCMI **This app requires original heroes 3 sod / complete files to operate, they are not supplied with this installer. it is recommended to purchase version from gog.com. Heroes 3 "hd edition" (steam version) files are not supported !!!** diff --git a/docs/players/Installation_Linux.md b/docs/players/Installation_Linux.md index 73ea09077..e68dadc2c 100644 --- a/docs/players/Installation_Linux.md +++ b/docs/players/Installation_Linux.md @@ -1,6 +1,8 @@ +# Installation Linux + VCMI requires data from original Heroes 3: Shadow of Death or Complete editions. Data from native Linux version made by LOKI will not work. -# Step 1: Binaries installation +## Step 1: Binaries installation ### Ubuntu - Latest stable build from PPA (recommended) diff --git a/docs/players/Installation_Windows.md b/docs/players/Installation_Windows.md index 06c0e86ea..b7fbd66d1 100644 --- a/docs/players/Installation_Windows.md +++ b/docs/players/Installation_Windows.md @@ -1,3 +1,5 @@ +# Installation Windows + ## Prerequisites As of VCMI 1.2 and newer Windows 10 or newer is required since our automated system uses elements incompatible with older Windows. diff --git a/docs/players/Installation_iOS.md b/docs/players/Installation_iOS.md index 2c2b9ddf7..c30cfd421 100644 --- a/docs/players/Installation_iOS.md +++ b/docs/players/Installation_iOS.md @@ -1,3 +1,5 @@ +# Installation iOS + You can run VCMI on iOS 12.0 and later, all devices are supported. If you wish to run on iOS 10 or 11, you should build from source, see [How to build VCMI (iOS)](../developers/Building_iOS.md). ## Step 1: Download and install VCMI diff --git a/docs/players/Installation_macOS.md b/docs/players/Installation_macOS.md index f550d6e94..1ce876d59 100644 --- a/docs/players/Installation_macOS.md +++ b/docs/players/Installation_macOS.md @@ -1,3 +1,5 @@ +# Installation macOS + For iOS installation look here: (Installation on iOS)[Installation_iOS.md] ## Step 1: Download and install VCMI diff --git a/docs/players/Privacy_Policy.md b/docs/players/Privacy_Policy.md index 25a884435..5bd841505 100644 --- a/docs/players/Privacy_Policy.md +++ b/docs/players/Privacy_Policy.md @@ -1,14 +1,16 @@ +# Privacy Policy + **Last Updated: 24th December, 2022** -### Glossary +## Glossary * VCMI team - a community of VCMI developers, mod makers and testers. It is not some officially registered organization. * VCMI app - an application provided by VCMI team. -### Single player +## Single player VCMI team does not collect any data produced by VCMI app. All game files, logs, saves, mods are stored in app's internal directory and will be removed upon app uninstallation. It should be possible to backup this data by standard ways provided by your device. -### Multiplayer +## Multiplayer If you decide to play with other users via Internet there are two roles. The host is the one who provides the game server. The clients are the other players who connect to the host. The host provides to the client its IP address in order to establish connections. The clients and the host during the gameplay exchange their usernames, messages and other game activity. All this data is collected and stored by the host. VCMI team does not collect and store any multiplayer data. \ No newline at end of file From 37b5de689a56837962d9ae6b6f87cf1f73bf0acd Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Tue, 16 Jul 2024 22:47:40 +0200 Subject: [PATCH 047/100] move to left side --- client/battle/BattleInterfaceClasses.cpp | 89 ++++++++---------------- client/battle/BattleInterfaceClasses.h | 19 +++-- client/battle/BattleWindow.cpp | 44 +++++------- client/battle/BattleWindow.h | 3 + client/gui/Shortcut.h | 9 +++ client/gui/ShortcutHandler.cpp | 9 +++ config/shortcutsConfig.json | 9 +++ 7 files changed, 85 insertions(+), 97 deletions(-) diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index f71b9a377..cf98b47e7 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -419,19 +419,17 @@ BattleHero::BattleHero(const BattleInterface & owner, const CGHeroInstance * her addUsedEvents(TIME); } -QuickSpellPanel::QuickSpellPanel(std::shared_ptr initWidget, BattleInterface & owner) - : CWindowObject(NEEDS_ANIMATED_BACKGROUND), initWidget(initWidget), owner(owner) +QuickSpellPanel::QuickSpellPanel(BattleInterface & owner) + : CWindowObject(NEEDS_ANIMATED_BACKGROUND), owner(owner) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); addUsedEvents(LCLICK | SHOW_POPUP | MOVE); - pos = Rect(0, 0, 52, 372); + pos = Rect(0, 0, 52, 713); background = std::make_shared(ImagePath::builtin("DIBOXBCK"), pos); rect = std::make_shared(Rect(0, 0, pos.w + 1, pos.h + 1), ColorRGBA(0, 0, 0, 0), ColorRGBA(241, 216, 120, 255)); - panelSelect = std::make_shared(this); - create(); } @@ -447,7 +445,7 @@ void QuickSpellPanel::create() if(!hero) return; - for(int i = 0; i < 10; i++) { + for(int i = 0; i < 19; i++) { std::string spellIdentifier = persistentStorage["quickSpell"][std::to_string(i)].String(); SpellID id; @@ -460,24 +458,25 @@ void QuickSpellPanel::create() id = SpellID::NONE; } - auto button = std::make_shared(Point(2, 1 + 37 * i), AnimationPath::builtin("spellint"), CButton::tooltip(), [this, id, hero](){ + auto button = std::make_shared(Point(2, 6 + 37 * i), AnimationPath::builtin("spellint"), CButton::tooltip(), [this, id, hero](){ if(id.hasValue() && id.toSpell()->canBeCast(owner.getBattle().get(), spells::Mode::HERO, hero)) { - close(); owner.castThisSpell(id); } }); button->setOverlay(std::make_shared(AnimationPath::builtin("spellint"), !spellIdentifier.empty() ? id.num + 1 : 0)); button->addPopupCallback([this, i](){ + auto panelSelect = std::make_shared(this); panelSelect->spellSlot = i; - panelSelect->setEnabled(true); + panelSelect->moveTo(Point(pos.x + 54, pos.y + 5)); + GH.windows().pushWindow(panelSelect); }); if(!id.hasValue() || !id.toSpell()->canBeCast(owner.getBattle().get(), spells::Mode::HERO, hero)) { - buttonsDisabled.push_back(std::make_shared(Rect(2, 1 + 37 * i, 48, 36), ColorRGBA(0, 0, 0, 128))); + buttonsDisabled.push_back(std::make_shared(Rect(2, 6 + 37 * i, 48, 36), ColorRGBA(0, 0, 0, 172))); } - labels.push_back(std::make_shared(7, 4 + 37 * i, EFonts::FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, std::to_string(i))); + labels.push_back(std::make_shared(7, 9 + 37 * i, EFonts::FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, std::to_string(i))); buttons.push_back(button); } @@ -489,54 +488,12 @@ void QuickSpellPanel::show(Canvas & to) CIntObject::show(to); } -void QuickSpellPanel::clickReleased(const Point & cursorPosition) -{ - if(!pos.isInside(cursorPosition) && (!(panelSelect->isActive() || panelSelect->wasEnabled) || !panelSelect->pos.isInside(cursorPosition))) - close(); - - if(initWidget->pos.isInside(cursorPosition)) - { - initWidget->clickPressed(cursorPosition); - initWidget->clickReleased(cursorPosition); - } -} - -void QuickSpellPanel::showPopupWindow(const Point & cursorPosition) -{ - if(!pos.isInside(cursorPosition) && (!(panelSelect->isActive() || panelSelect->wasEnabled) || !panelSelect->pos.isInside(cursorPosition))) - close(); - - if(initWidget->pos.isInside(cursorPosition)) - { - initWidget->showPopupWindow(cursorPosition); - } -} - -void QuickSpellPanel::mouseMoved(const Point & cursorPosition, const Point & lastUpdateDistance) -{ - if( (cursorPosition.x <= initWidget->pos.x - 20 || - cursorPosition.x >= initWidget->pos.x + initWidget->pos.w + 20 || - cursorPosition.y <= initWidget->pos.y - pos.h - 20 || - (cursorPosition.y >= initWidget->pos.y + 5 && !initWidget->pos.isInside(cursorPosition))) && - (!(panelSelect->isActive() || panelSelect->wasEnabled) || !panelSelect->pos.resize(20).isInside(cursorPosition)) - ) - close(); - - if(initWidget->pos.isInside(cursorPosition)) - panelSelect->wasEnabled = false; -} - -bool QuickSpellPanel::receiveEvent(const Point & position, int eventType) const -{ - return true; // capture click also outside of window -} - QuickSpellPanelSelect::QuickSpellPanelSelect(QuickSpellPanel * Parent) - : parent(Parent), wasEnabled(false) + : parent(Parent) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); - setEnabled(false); + addUsedEvents(LCLICK); std::vector> spellList; for (auto const & s : VLC->spellh->objects) @@ -544,21 +501,20 @@ QuickSpellPanelSelect::QuickSpellPanelSelect(QuickSpellPanel * Parent) spellList.push_back(s); auto ceil = [](int x, int y){ return(x + y - 1) / y; }; - int columnsNeeded = ceil(spellList.size(), 10); + int columnsNeeded = ceil(spellList.size(), NUM_PER_COLUMN); - pos = Rect(-20 - columnsNeeded * 50, 0, columnsNeeded * 50 + 2, 372); + pos = Rect(pos.x, pos.y, columnsNeeded * 50 + 2, 2 + 37 * NUM_PER_COLUMN); background = std::make_shared(ImagePath::builtin("DIBOXBCK"), Rect(0, 0, pos.w, pos.h)); rect = std::make_shared(Rect(0, 0, pos.w + 1, pos.h + 1), ColorRGBA(0, 0, 0, 0), ColorRGBA(241, 216, 120, 255)); for(int i = 0; i < spellList.size(); i++) { - int y = i % 10; - int x = i / 10; + int y = i % NUM_PER_COLUMN; + int x = i / NUM_PER_COLUMN; std::shared_ptr spell = spellList[i]; auto button = std::make_shared(Point(2 + 50 * x, 1 + 37 * y), AnimationPath::builtin("spellint"), CButton::tooltip(), [this, spell](){ - setEnabled(false); + close(); GH.windows().totalRedraw(); - wasEnabled = true; Settings configID = persistentStorage.write["quickSpell"][std::to_string(spellSlot)]; configID->String() = spell->identifier; parent->create(); @@ -571,6 +527,17 @@ QuickSpellPanelSelect::QuickSpellPanelSelect(QuickSpellPanel * Parent) } } +void QuickSpellPanelSelect::clickReleased(const Point & cursorPosition) +{ + if(!pos.isInside(cursorPosition)) + close(); +} + +bool QuickSpellPanelSelect::receiveEvent(const Point & position, int eventType) const +{ + return true; // capture click also outside of window +} + HeroInfoBasicPanel::HeroInfoBasicPanel(const InfoAboutHero & hero, Point * position, bool initializeBackground) : CIntObject(0) { diff --git a/client/battle/BattleInterfaceClasses.h b/client/battle/BattleInterfaceClasses.h index e65add84d..9560079ef 100644 --- a/client/battle/BattleInterfaceClasses.h +++ b/client/battle/BattleInterfaceClasses.h @@ -149,16 +149,21 @@ public: BattleHero(const BattleInterface & owner, const CGHeroInstance * hero, bool defender); }; -class QuickSpellPanelSelect : public CIntObject +class QuickSpellPanelSelect : public CWindowObject { private: + const int NUM_PER_COLUMN = 19; + std::shared_ptr background; std::shared_ptr rect; std::vector> buttons; QuickSpellPanel * parent; public: QuickSpellPanelSelect(QuickSpellPanel * Parent); - bool wasEnabled; // was the panel opened? -> don't close window because mouse is not in area + + bool receiveEvent(const Point & position, int eventType) const override; + void clickReleased(const Point & cursorPosition) override; + int spellSlot; }; @@ -171,17 +176,9 @@ private: std::vector> buttonsDisabled; std::vector> labels; - std::shared_ptr panelSelect; - - bool receiveEvent(const Point & position, int eventType) const override; - void clickReleased(const Point & cursorPosition) override; - void showPopupWindow(const Point & cursorPosition) override; - void mouseMoved(const Point & cursorPosition, const Point & lastUpdateDistance) override; - - std::shared_ptr initWidget; BattleInterface & owner; public: - QuickSpellPanel(std::shared_ptr initWidget, BattleInterface & owner); + QuickSpellPanel(BattleInterface & owner); void create(); diff --git a/client/battle/BattleWindow.cpp b/client/battle/BattleWindow.cpp index 55472fe96..0046b51b6 100644 --- a/client/battle/BattleWindow.cpp +++ b/client/battle/BattleWindow.cpp @@ -90,6 +90,15 @@ BattleWindow::BattleWindow(BattleInterface & Owner): addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_7, [useSpellIfPossible](){ useSpellIfPossible(7); }); addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_8, [useSpellIfPossible](){ useSpellIfPossible(8); }); addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_9, [useSpellIfPossible](){ useSpellIfPossible(9); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_10, [useSpellIfPossible](){ useSpellIfPossible(10); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_11, [useSpellIfPossible](){ useSpellIfPossible(11); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_12, [useSpellIfPossible](){ useSpellIfPossible(12); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_13, [useSpellIfPossible](){ useSpellIfPossible(13); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_14, [useSpellIfPossible](){ useSpellIfPossible(14); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_15, [useSpellIfPossible](){ useSpellIfPossible(15); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_16, [useSpellIfPossible](){ useSpellIfPossible(16); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_17, [useSpellIfPossible](){ useSpellIfPossible(17); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_18, [useSpellIfPossible](){ useSpellIfPossible(18); }); addShortcut(EShortcut::GLOBAL_OPTIONS, std::bind(&BattleWindow::bOptionsf, this)); addShortcut(EShortcut::BATTLE_SURRENDER, std::bind(&BattleWindow::bSurrenderf, this)); @@ -123,33 +132,9 @@ BattleWindow::BattleWindow(BattleInterface & Owner): createQueue(); createStickyHeroInfoWindows(); + createQuickSpellWindow(); createTimerInfoWindows(); - auto w = widget("cast"); - if(w) - { - auto hero = owner.getBattle()->battleGetMyHero(); - if(hero && settings["general"]["enableUiEnhancements"].Bool()) - { - auto createQuickSpellPanelWindow = [](std::shared_ptr widget, BattleInterface & owner){ - std::shared_ptr window = std::make_shared(widget, owner); - window->moveTo(Point(widget->pos.x - 2, widget->pos.y - 378)); - GH.windows().pushWindow(window); - }; - - w->addHoverCallback([this, createQuickSpellPanelWindow, w](bool on) - { - if(on) - createQuickSpellPanelWindow(w, owner); - }); - w->addPanningCallback([this, createQuickSpellPanelWindow, w](const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) - { - if((currentPosition - initialPosition).y < -20) - createQuickSpellPanelWindow(w, owner); - }); - } - } - if ( owner.tacticsMode ) tacticPhaseStarted(); else @@ -216,6 +201,15 @@ void BattleWindow::createStickyHeroInfoWindows() setPositionInfoWindow(); } +void BattleWindow::createQuickSpellWindow() +{ + OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; + + std::shared_ptr window = std::make_shared(owner); + window->moveTo(Point(pos.x - 68, pos.y - 14)); + GH.windows().pushWindow(window); +} + void BattleWindow::createTimerInfoWindows() { OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; diff --git a/client/battle/BattleWindow.h b/client/battle/BattleWindow.h index 27897df45..15dfaf235 100644 --- a/client/battle/BattleWindow.h +++ b/client/battle/BattleWindow.h @@ -41,6 +41,8 @@ class BattleWindow : public InterfaceObjectConfigurable std::shared_ptr attackerStackWindow; std::shared_ptr defenderStackWindow; + std::shared_ptr quickSpellWindow; + std::shared_ptr attackerTimerWidget; std::shared_ptr defenderTimerWidget; @@ -75,6 +77,7 @@ class BattleWindow : public InterfaceObjectConfigurable void toggleStickyHeroWindowsVisibility(); void createStickyHeroInfoWindows(); + void createQuickSpellWindow(); void createTimerInfoWindows(); std::shared_ptr buildBattleConsole(const JsonNode &) const; diff --git a/client/gui/Shortcut.h b/client/gui/Shortcut.h index 42b96ac1c..1fb40020f 100644 --- a/client/gui/Shortcut.h +++ b/client/gui/Shortcut.h @@ -196,6 +196,15 @@ enum class EShortcut BATTLE_SPELL_SHORTCUT_7, BATTLE_SPELL_SHORTCUT_8, BATTLE_SPELL_SHORTCUT_9, + BATTLE_SPELL_SHORTCUT_10, + BATTLE_SPELL_SHORTCUT_11, + BATTLE_SPELL_SHORTCUT_12, + BATTLE_SPELL_SHORTCUT_13, + BATTLE_SPELL_SHORTCUT_14, + BATTLE_SPELL_SHORTCUT_15, + BATTLE_SPELL_SHORTCUT_16, + BATTLE_SPELL_SHORTCUT_17, + BATTLE_SPELL_SHORTCUT_18, MARKET_DEAL, MARKET_MAX_AMOUNT, diff --git a/client/gui/ShortcutHandler.cpp b/client/gui/ShortcutHandler.cpp index 11bafc8ae..18b4065b8 100644 --- a/client/gui/ShortcutHandler.cpp +++ b/client/gui/ShortcutHandler.cpp @@ -232,6 +232,15 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const {"battleSpellShortcut7", EShortcut::BATTLE_SPELL_SHORTCUT_7 }, {"battleSpellShortcut8", EShortcut::BATTLE_SPELL_SHORTCUT_8 }, {"battleSpellShortcut9", EShortcut::BATTLE_SPELL_SHORTCUT_9 }, + {"battleSpellShortcut10", EShortcut::BATTLE_SPELL_SHORTCUT_10 }, + {"battleSpellShortcut11", EShortcut::BATTLE_SPELL_SHORTCUT_11 }, + {"battleSpellShortcut12", EShortcut::BATTLE_SPELL_SHORTCUT_12 }, + {"battleSpellShortcut13", EShortcut::BATTLE_SPELL_SHORTCUT_13 }, + {"battleSpellShortcut14", EShortcut::BATTLE_SPELL_SHORTCUT_14 }, + {"battleSpellShortcut15", EShortcut::BATTLE_SPELL_SHORTCUT_15 }, + {"battleSpellShortcut16", EShortcut::BATTLE_SPELL_SHORTCUT_16 }, + {"battleSpellShortcut17", EShortcut::BATTLE_SPELL_SHORTCUT_17 }, + {"battleSpellShortcut18", EShortcut::BATTLE_SPELL_SHORTCUT_18 }, {"spectateTrackHero", EShortcut::SPECTATE_TRACK_HERO }, {"spectateSkipBattle", EShortcut::SPECTATE_SKIP_BATTLE }, {"spectateSkipBattleResult", EShortcut::SPECTATE_SKIP_BATTLE_RESULT }, diff --git a/config/shortcutsConfig.json b/config/shortcutsConfig.json index 3502b89d7..d6ba7bf7d 100644 --- a/config/shortcutsConfig.json +++ b/config/shortcutsConfig.json @@ -74,6 +74,15 @@ "battleSpellShortcut7": "7", "battleSpellShortcut8": "8", "battleSpellShortcut9": "9", + "battleSpellShortcut10": "Shift+0", + "battleSpellShortcut11": "Shift+1", + "battleSpellShortcut12": "Shift+2", + "battleSpellShortcut13": "Shift+3", + "battleSpellShortcut14": "Shift+4", + "battleSpellShortcut15": "Shift+5", + "battleSpellShortcut16": "Shift+6", + "battleSpellShortcut17": "Shift+7", + "battleSpellShortcut18": "Shift+8", "battleSurrender": "S", "battleTacticsEnd": [ "Return", "Keypad Enter"], "battleTacticsNext": "Space", From 1daf6dfad1ecdfda2497999cea561fe9783e51ac Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Tue, 16 Jul 2024 23:36:23 +0200 Subject: [PATCH 048/100] make hideable --- client/battle/BattleInterfaceClasses.cpp | 2 +- client/battle/BattleInterfaceClasses.h | 2 +- client/battle/BattleWindow.cpp | 45 ++++++++++++++++++++++-- client/battle/BattleWindow.h | 5 +++ client/gui/Shortcut.h | 1 + client/gui/ShortcutHandler.cpp | 1 + config/schemas/settings.json | 6 +++- config/shortcutsConfig.json | 1 + 8 files changed, 57 insertions(+), 6 deletions(-) diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index cf98b47e7..0bde51b3d 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -420,7 +420,7 @@ BattleHero::BattleHero(const BattleInterface & owner, const CGHeroInstance * her } QuickSpellPanel::QuickSpellPanel(BattleInterface & owner) - : CWindowObject(NEEDS_ANIMATED_BACKGROUND), owner(owner) + : CIntObject(0), owner(owner) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); diff --git a/client/battle/BattleInterfaceClasses.h b/client/battle/BattleInterfaceClasses.h index 9560079ef..149b24390 100644 --- a/client/battle/BattleInterfaceClasses.h +++ b/client/battle/BattleInterfaceClasses.h @@ -167,7 +167,7 @@ public: int spellSlot; }; -class QuickSpellPanel : public CWindowObject +class QuickSpellPanel : public CIntObject { private: std::shared_ptr background; diff --git a/client/battle/BattleWindow.cpp b/client/battle/BattleWindow.cpp index 0046b51b6..28b30af42 100644 --- a/client/battle/BattleWindow.cpp +++ b/client/battle/BattleWindow.cpp @@ -80,6 +80,7 @@ BattleWindow::BattleWindow(BattleInterface & Owner): owner.castThisSpell(id); } }; + addShortcut(EShortcut::BATTLE_TOGGLE_QUICKSPELL, [this](){ this->toggleStickyQuickSpellVisibility();}); addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_0, [useSpellIfPossible](){ useSpellIfPossible(0); }); addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_1, [useSpellIfPossible](){ useSpellIfPossible(1); }); addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_2, [useSpellIfPossible](){ useSpellIfPossible(2); }); @@ -205,9 +206,47 @@ void BattleWindow::createQuickSpellWindow() { OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; - std::shared_ptr window = std::make_shared(owner); - window->moveTo(Point(pos.x - 68, pos.y - 14)); - GH.windows().pushWindow(window); + quickSpellWindow = std::make_shared(owner); + quickSpellWindow->moveTo(Point(pos.x - 68, pos.y - 14)); + + if(settings["battle"]["enableQuickSpellPanel"].Bool()) + showStickyQuickSpellWindow(); + else + hideStickyQuickSpellWindow(); +} + +void BattleWindow::toggleStickyQuickSpellVisibility() +{ + if(settings["battle"]["enableQuickSpellPanel"].Bool()) + hideStickyQuickSpellWindow(); + else + showStickyQuickSpellWindow(); +} + +void BattleWindow::hideStickyQuickSpellWindow() +{ + if(settings["battle"]["enableQuickSpellPanel"].Bool() == false) + return; + + Settings showStickyQuickSpellWindow = settings.write["battle"]["enableQuickSpellPanel"]; + showStickyQuickSpellWindow->Bool() = false; + + quickSpellWindow->disable(); + + GH.windows().totalRedraw(); +} + +void BattleWindow::showStickyQuickSpellWindow() +{ + if(settings["battle"]["enableQuickSpellPanel"].Bool() == true) + return; + + Settings showStickyQuickSpellWindow = settings.write["battle"]["enableQuickSpellPanel"]; + showStickyQuickSpellWindow->Bool() = true; + + quickSpellWindow->enable(); + + GH.windows().totalRedraw(); } void BattleWindow::createTimerInfoWindows() diff --git a/client/battle/BattleWindow.h b/client/battle/BattleWindow.h index 15dfaf235..d5b21657a 100644 --- a/client/battle/BattleWindow.h +++ b/client/battle/BattleWindow.h @@ -76,6 +76,7 @@ class BattleWindow : public InterfaceObjectConfigurable void createQueue(); void toggleStickyHeroWindowsVisibility(); + void toggleStickyQuickSpellVisibility(); void createStickyHeroInfoWindows(); void createQuickSpellWindow(); void createTimerInfoWindows(); @@ -98,6 +99,10 @@ public: void hideStickyHeroWindows(); void showStickyHeroWindows(); + /// Toggle permanent quickspell windows visibility + void hideStickyQuickSpellWindow(); + void showStickyQuickSpellWindow(); + /// Event handler for netpack changing hero mana points void heroManaPointsChanged(const CGHeroInstance * hero); diff --git a/client/gui/Shortcut.h b/client/gui/Shortcut.h index 1fb40020f..5193cd91e 100644 --- a/client/gui/Shortcut.h +++ b/client/gui/Shortcut.h @@ -186,6 +186,7 @@ enum class EShortcut BATTLE_TOGGLE_HEROES_STATS, BATTLE_OPEN_ACTIVE_UNIT, BATTLE_OPEN_HOVERED_UNIT, + BATTLE_TOGGLE_QUICKSPELL, BATTLE_SPELL_SHORTCUT_0, BATTLE_SPELL_SHORTCUT_1, BATTLE_SPELL_SHORTCUT_2, diff --git a/client/gui/ShortcutHandler.cpp b/client/gui/ShortcutHandler.cpp index 18b4065b8..aaf480a6a 100644 --- a/client/gui/ShortcutHandler.cpp +++ b/client/gui/ShortcutHandler.cpp @@ -222,6 +222,7 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const {"battleTacticsNext", EShortcut::BATTLE_TACTICS_NEXT }, {"battleTacticsEnd", EShortcut::BATTLE_TACTICS_END }, {"battleSelectAction", EShortcut::BATTLE_SELECT_ACTION }, + {"battleToggleQuickSpell", EShortcut::BATTLE_TOGGLE_QUICKSPELL }, {"battleSpellShortcut0", EShortcut::BATTLE_SPELL_SHORTCUT_0 }, {"battleSpellShortcut1", EShortcut::BATTLE_SPELL_SHORTCUT_1 }, {"battleSpellShortcut2", EShortcut::BATTLE_SPELL_SHORTCUT_2 }, diff --git a/config/schemas/settings.json b/config/schemas/settings.json index 002500054..56c97240b 100644 --- a/config/schemas/settings.json +++ b/config/schemas/settings.json @@ -372,7 +372,7 @@ "type" : "object", "additionalProperties" : false, "default" : {}, - "required" : [ "speedFactor", "mouseShadow", "cellBorders", "stackRange", "movementHighlightOnHover", "rangeLimitHighlightOnHover", "showQueue", "swipeAttackDistance", "queueSize", "stickyHeroInfoWindows", "enableAutocombatSpells", "endWithAutocombat", "queueSmallSlots", "queueSmallOutside" ], + "required" : [ "speedFactor", "mouseShadow", "cellBorders", "stackRange", "movementHighlightOnHover", "rangeLimitHighlightOnHover", "showQueue", "swipeAttackDistance", "queueSize", "stickyHeroInfoWindows", "enableAutocombatSpells", "endWithAutocombat", "queueSmallSlots", "queueSmallOutside", "enableQuickSpellPanel" ], "properties" : { "speedFactor" : { "type" : "number", @@ -430,6 +430,10 @@ "queueSmallOutside" : { "type": "boolean", "default": false + }, + "enableQuickSpellPanel" : { + "type": "boolean", + "default": true } } }, diff --git a/config/shortcutsConfig.json b/config/shortcutsConfig.json index d6ba7bf7d..744b110ac 100644 --- a/config/shortcutsConfig.json +++ b/config/shortcutsConfig.json @@ -64,6 +64,7 @@ "battleOpenHoveredUnit": "V", "battleRetreat": "R", "battleSelectAction": "S", + "battleToggleQuickSpell": "T", "battleSpellShortcut0": "0", "battleSpellShortcut1": "1", "battleSpellShortcut2": "2", From a3874297f5efaa1ed0ce841b9ddd5f18e2504a90 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Wed, 17 Jul 2024 00:48:22 +0200 Subject: [PATCH 049/100] toggle window --- client/battle/BattleWindow.cpp | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/client/battle/BattleWindow.cpp b/client/battle/BattleWindow.cpp index 28b30af42..24e596402 100644 --- a/client/battle/BattleWindow.cpp +++ b/client/battle/BattleWindow.cpp @@ -132,8 +132,8 @@ BattleWindow::BattleWindow(BattleInterface & Owner): owner.fieldController->createHeroes(); createQueue(); - createStickyHeroInfoWindows(); createQuickSpellWindow(); + createStickyHeroInfoWindows(); createTimerInfoWindows(); if ( owner.tacticsMode ) @@ -225,27 +225,28 @@ void BattleWindow::toggleStickyQuickSpellVisibility() void BattleWindow::hideStickyQuickSpellWindow() { - if(settings["battle"]["enableQuickSpellPanel"].Bool() == false) - return; - Settings showStickyQuickSpellWindow = settings.write["battle"]["enableQuickSpellPanel"]; showStickyQuickSpellWindow->Bool() = false; quickSpellWindow->disable(); + setPositionInfoWindow(); + createTimerInfoWindows(); GH.windows().totalRedraw(); } void BattleWindow::showStickyQuickSpellWindow() { - if(settings["battle"]["enableQuickSpellPanel"].Bool() == true) - return; - Settings showStickyQuickSpellWindow = settings.write["battle"]["enableQuickSpellPanel"]; showStickyQuickSpellWindow->Bool() = true; - quickSpellWindow->enable(); + if(GH.screenDimensions().x >= 1050) + quickSpellWindow->enable(); + else + quickSpellWindow->disable(); + setPositionInfoWindow(); + createTimerInfoWindows(); GH.windows().totalRedraw(); } @@ -253,6 +254,8 @@ void BattleWindow::createTimerInfoWindows() { OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; + int xOffsetAttacker = quickSpellWindow->isActive() ? -53 : 0; + if(LOCPLINT->cb->getStartInfo()->turnTimerInfo.battleTimer != 0 || LOCPLINT->cb->getStartInfo()->turnTimerInfo.unitTimer != 0) { PlayerColor attacker = owner.getBattle()->sideToPlayer(BattleSide::ATTACKER); @@ -261,7 +264,7 @@ void BattleWindow::createTimerInfoWindows() if (attacker.isValidPlayer()) { if (GH.screenDimensions().x >= 1000) - attackerTimerWidget = std::make_shared(Point(-92, 1), attacker); + attackerTimerWidget = std::make_shared(Point(-92 + xOffsetAttacker, 1), attacker); else attackerTimerWidget = std::make_shared(Point(1, 135), attacker); } @@ -372,6 +375,7 @@ void BattleWindow::updateQueue() void BattleWindow::setPositionInfoWindow() { + int xOffsetAttacker = quickSpellWindow->isActive() ? -53 : 0; if(defenderHeroWindow) { Point position = (GH.screenDimensions().x >= 1000) @@ -382,7 +386,7 @@ void BattleWindow::setPositionInfoWindow() if(attackerHeroWindow) { Point position = (GH.screenDimensions().x >= 1000) - ? Point(pos.x - 93, pos.y + 60) + ? Point(pos.x - 93 + xOffsetAttacker, pos.y + 60) : Point(pos.x + 1, pos.y + 195); attackerHeroWindow->moveTo(position); } @@ -396,7 +400,7 @@ void BattleWindow::setPositionInfoWindow() if(attackerStackWindow) { Point position = (GH.screenDimensions().x >= 1000) - ? Point(pos.x - 93, attackerHeroWindow ? attackerHeroWindow->pos.y + 210 : pos.y + 60) + ? Point(pos.x - 93 + xOffsetAttacker, attackerHeroWindow ? attackerHeroWindow->pos.y + 210 : pos.y + 60) : Point(pos.x + 1, attackerHeroWindow ? attackerHeroWindow->pos.y : pos.y + 195); attackerStackWindow->moveTo(position); } @@ -431,6 +435,7 @@ void BattleWindow::updateStackInfoWindow(const CStack * stack) attackerStackWindow = nullptr; setPositionInfoWindow(); + createTimerInfoWindows(); } void BattleWindow::heroManaPointsChanged(const CGHeroInstance * hero) @@ -851,6 +856,8 @@ void BattleWindow::blockUI(bool on) setShortcutBlocked(EShortcut::BATTLE_TACTICS_NEXT, on || !owner.tacticsMode); setShortcutBlocked(EShortcut::BATTLE_CONSOLE_DOWN, on && !owner.tacticsMode); setShortcutBlocked(EShortcut::BATTLE_CONSOLE_UP, on && !owner.tacticsMode); + + quickSpellWindow->setInputEnabled(!on); } void BattleWindow::bOpenActiveUnit() From 0a98169bcfe86e083d427237b8e04057a8208c45 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Wed, 17 Jul 2024 00:55:01 +0200 Subject: [PATCH 050/100] update quick spell panel --- client/battle/BattleWindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/client/battle/BattleWindow.cpp b/client/battle/BattleWindow.cpp index 24e596402..e9fe93486 100644 --- a/client/battle/BattleWindow.cpp +++ b/client/battle/BattleWindow.cpp @@ -371,6 +371,7 @@ void BattleWindow::showStickyHeroWindows() void BattleWindow::updateQueue() { queue->update(); + createQuickSpellWindow(); } void BattleWindow::setPositionInfoWindow() From d23d4b32c7537276858ba0c438d2ddb66b2b5872 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Wed, 17 Jul 2024 01:02:11 +0200 Subject: [PATCH 051/100] cleanup --- client/widgets/Buttons.cpp | 19 +------------------ client/widgets/Buttons.h | 5 ----- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/client/widgets/Buttons.cpp b/client/widgets/Buttons.cpp index 8491afe3e..4ab499cd7 100644 --- a/client/widgets/Buttons.cpp +++ b/client/widgets/Buttons.cpp @@ -72,16 +72,6 @@ void CButton::addPopupCallback(const std::function & callback) this->callbackPopup += callback; } -void CButton::addHoverCallback(const std::function & callback) -{ - this->hoverCallback += callback; -} - -void CButton::addPanningCallback(const std::function & callback) -{ - this->panningCallback += callback; -} - void ButtonBase::setTextOverlay(const std::string & Text, EFonts font, ColorRGBA color) { OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); @@ -312,8 +302,6 @@ void CButton::showPopupWindow(const Point & cursorPosition) void CButton::hover (bool on) { - hoverCallback(on); - if(hoverable && !isBlocked()) { if(on) @@ -338,11 +326,6 @@ void CButton::hover (bool on) } } -void CButton::gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) -{ - panningCallback(initialPosition, currentPosition, lastUpdateDistance); -} - ButtonBase::ButtonBase(Point position, const AnimationPath & defName, EShortcut key, bool playerColoredButton) : CKeyShortcut(key) , stateToIndex({0, 1, 2, 3}) @@ -375,7 +358,7 @@ CButton::CButton(Point position, const AnimationPath &defName, const std::pair callback; CFunctionList callbackPopup; - CFunctionList hoverCallback; - CFunctionList panningCallback; std::array hoverTexts; //texts for statusbar, if empty - first entry will be used std::optional borderColor; // mapping of button state to border color @@ -94,8 +92,6 @@ public: /// adds one more callback to on-click actions void addCallback(const std::function & callback); void addPopupCallback(const std::function & callback); - void addHoverCallback(const std::function & callback); - void addPanningCallback(const std::function & callback); void addHoverText(EButtonState state, const std::string & text); @@ -120,7 +116,6 @@ public: void clickReleased(const Point & cursorPosition) override; void clickCancel(const Point & cursorPosition) override; void hover (bool on) override; - void gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) override; void showAll(Canvas & to) override; /// generates tooltip that can be passed into constructor From b8d872aa6049d3e887bd7251b6de43b9d13059c9 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Wed, 17 Jul 2024 04:24:27 +0200 Subject: [PATCH 052/100] mobile adjustments --- client/battle/BattleInterfaceClasses.cpp | 14 ++++++------- client/battle/BattleInterfaceClasses.h | 2 +- client/battle/BattleWindow.cpp | 25 +++++++++++------------- client/gui/Shortcut.h | 3 --- client/gui/ShortcutHandler.cpp | 3 --- config/shortcutsConfig.json | 3 --- 6 files changed, 19 insertions(+), 31 deletions(-) diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index 0bde51b3d..1ae1acd44 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -426,7 +426,7 @@ QuickSpellPanel::QuickSpellPanel(BattleInterface & owner) addUsedEvents(LCLICK | SHOW_POPUP | MOVE); - pos = Rect(0, 0, 52, 713); + pos = Rect(0, 0, 52, 600); background = std::make_shared(ImagePath::builtin("DIBOXBCK"), pos); rect = std::make_shared(Rect(0, 0, pos.w + 1, pos.h + 1), ColorRGBA(0, 0, 0, 0), ColorRGBA(241, 216, 120, 255)); @@ -445,7 +445,7 @@ void QuickSpellPanel::create() if(!hero) return; - for(int i = 0; i < 19; i++) { + for(int i = 0; i < 16; i++) { std::string spellIdentifier = persistentStorage["quickSpell"][std::to_string(i)].String(); SpellID id; @@ -458,7 +458,7 @@ void QuickSpellPanel::create() id = SpellID::NONE; } - auto button = std::make_shared(Point(2, 6 + 37 * i), AnimationPath::builtin("spellint"), CButton::tooltip(), [this, id, hero](){ + auto button = std::make_shared(Point(2, 5 + 37 * i), AnimationPath::builtin("spellint"), CButton::tooltip(), [this, id, hero](){ if(id.hasValue() && id.toSpell()->canBeCast(owner.getBattle().get(), spells::Mode::HERO, hero)) { owner.castThisSpell(id); @@ -468,15 +468,15 @@ void QuickSpellPanel::create() button->addPopupCallback([this, i](){ auto panelSelect = std::make_shared(this); panelSelect->spellSlot = i; - panelSelect->moveTo(Point(pos.x + 54, pos.y + 5)); + panelSelect->moveTo(Point(pos.x + 54, pos.y + 4)); GH.windows().pushWindow(panelSelect); }); if(!id.hasValue() || !id.toSpell()->canBeCast(owner.getBattle().get(), spells::Mode::HERO, hero)) { - buttonsDisabled.push_back(std::make_shared(Rect(2, 6 + 37 * i, 48, 36), ColorRGBA(0, 0, 0, 172))); + buttonsDisabled.push_back(std::make_shared(Rect(2, 5 + 37 * i, 48, 36), ColorRGBA(0, 0, 0, 172))); } - labels.push_back(std::make_shared(7, 9 + 37 * i, EFonts::FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, std::to_string(i))); + labels.push_back(std::make_shared(7, 8 + 37 * i, EFonts::FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, std::to_string(i))); buttons.push_back(button); } @@ -529,7 +529,7 @@ QuickSpellPanelSelect::QuickSpellPanelSelect(QuickSpellPanel * Parent) void QuickSpellPanelSelect::clickReleased(const Point & cursorPosition) { - if(!pos.isInside(cursorPosition)) + if(!pos.isInside(cursorPosition) && !parent->pos.isInside(cursorPosition)) close(); } diff --git a/client/battle/BattleInterfaceClasses.h b/client/battle/BattleInterfaceClasses.h index 149b24390..5fe9049eb 100644 --- a/client/battle/BattleInterfaceClasses.h +++ b/client/battle/BattleInterfaceClasses.h @@ -152,7 +152,7 @@ public: class QuickSpellPanelSelect : public CWindowObject { private: - const int NUM_PER_COLUMN = 19; + const int NUM_PER_COLUMN = 16; std::shared_ptr background; std::shared_ptr rect; diff --git a/client/battle/BattleWindow.cpp b/client/battle/BattleWindow.cpp index e9fe93486..f641a516a 100644 --- a/client/battle/BattleWindow.cpp +++ b/client/battle/BattleWindow.cpp @@ -81,25 +81,22 @@ BattleWindow::BattleWindow(BattleInterface & Owner): } }; addShortcut(EShortcut::BATTLE_TOGGLE_QUICKSPELL, [this](){ this->toggleStickyQuickSpellVisibility();}); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_0, [useSpellIfPossible](){ useSpellIfPossible(0); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_1, [useSpellIfPossible](){ useSpellIfPossible(1); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_2, [useSpellIfPossible](){ useSpellIfPossible(2); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_3, [useSpellIfPossible](){ useSpellIfPossible(3); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_4, [useSpellIfPossible](){ useSpellIfPossible(4); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_5, [useSpellIfPossible](){ useSpellIfPossible(5); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_6, [useSpellIfPossible](){ useSpellIfPossible(6); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_7, [useSpellIfPossible](){ useSpellIfPossible(7); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_8, [useSpellIfPossible](){ useSpellIfPossible(8); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_9, [useSpellIfPossible](){ useSpellIfPossible(9); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_0, [useSpellIfPossible](){ useSpellIfPossible(0); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_1, [useSpellIfPossible](){ useSpellIfPossible(1); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_2, [useSpellIfPossible](){ useSpellIfPossible(2); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_3, [useSpellIfPossible](){ useSpellIfPossible(3); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_4, [useSpellIfPossible](){ useSpellIfPossible(4); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_5, [useSpellIfPossible](){ useSpellIfPossible(5); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_6, [useSpellIfPossible](){ useSpellIfPossible(6); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_7, [useSpellIfPossible](){ useSpellIfPossible(7); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_8, [useSpellIfPossible](){ useSpellIfPossible(8); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_9, [useSpellIfPossible](){ useSpellIfPossible(9); }); addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_10, [useSpellIfPossible](){ useSpellIfPossible(10); }); addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_11, [useSpellIfPossible](){ useSpellIfPossible(11); }); addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_12, [useSpellIfPossible](){ useSpellIfPossible(12); }); addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_13, [useSpellIfPossible](){ useSpellIfPossible(13); }); addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_14, [useSpellIfPossible](){ useSpellIfPossible(14); }); addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_15, [useSpellIfPossible](){ useSpellIfPossible(15); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_16, [useSpellIfPossible](){ useSpellIfPossible(16); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_17, [useSpellIfPossible](){ useSpellIfPossible(17); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_18, [useSpellIfPossible](){ useSpellIfPossible(18); }); addShortcut(EShortcut::GLOBAL_OPTIONS, std::bind(&BattleWindow::bOptionsf, this)); addShortcut(EShortcut::BATTLE_SURRENDER, std::bind(&BattleWindow::bSurrenderf, this)); @@ -207,7 +204,7 @@ void BattleWindow::createQuickSpellWindow() OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; quickSpellWindow = std::make_shared(owner); - quickSpellWindow->moveTo(Point(pos.x - 68, pos.y - 14)); + quickSpellWindow->moveTo(Point(pos.x - 67, pos.y - 1)); if(settings["battle"]["enableQuickSpellPanel"].Bool()) showStickyQuickSpellWindow(); diff --git a/client/gui/Shortcut.h b/client/gui/Shortcut.h index 5193cd91e..57027fa44 100644 --- a/client/gui/Shortcut.h +++ b/client/gui/Shortcut.h @@ -203,9 +203,6 @@ enum class EShortcut BATTLE_SPELL_SHORTCUT_13, BATTLE_SPELL_SHORTCUT_14, BATTLE_SPELL_SHORTCUT_15, - BATTLE_SPELL_SHORTCUT_16, - BATTLE_SPELL_SHORTCUT_17, - BATTLE_SPELL_SHORTCUT_18, MARKET_DEAL, MARKET_MAX_AMOUNT, diff --git a/client/gui/ShortcutHandler.cpp b/client/gui/ShortcutHandler.cpp index aaf480a6a..9e6ce0e96 100644 --- a/client/gui/ShortcutHandler.cpp +++ b/client/gui/ShortcutHandler.cpp @@ -239,9 +239,6 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const {"battleSpellShortcut13", EShortcut::BATTLE_SPELL_SHORTCUT_13 }, {"battleSpellShortcut14", EShortcut::BATTLE_SPELL_SHORTCUT_14 }, {"battleSpellShortcut15", EShortcut::BATTLE_SPELL_SHORTCUT_15 }, - {"battleSpellShortcut16", EShortcut::BATTLE_SPELL_SHORTCUT_16 }, - {"battleSpellShortcut17", EShortcut::BATTLE_SPELL_SHORTCUT_17 }, - {"battleSpellShortcut18", EShortcut::BATTLE_SPELL_SHORTCUT_18 }, {"spectateTrackHero", EShortcut::SPECTATE_TRACK_HERO }, {"spectateSkipBattle", EShortcut::SPECTATE_SKIP_BATTLE }, {"spectateSkipBattleResult", EShortcut::SPECTATE_SKIP_BATTLE_RESULT }, diff --git a/config/shortcutsConfig.json b/config/shortcutsConfig.json index 744b110ac..f637e6b52 100644 --- a/config/shortcutsConfig.json +++ b/config/shortcutsConfig.json @@ -81,9 +81,6 @@ "battleSpellShortcut13": "Shift+3", "battleSpellShortcut14": "Shift+4", "battleSpellShortcut15": "Shift+5", - "battleSpellShortcut16": "Shift+6", - "battleSpellShortcut17": "Shift+7", - "battleSpellShortcut18": "Shift+8", "battleSurrender": "S", "battleTacticsEnd": [ "Return", "Keypad Enter"], "battleTacticsNext": "Space", From f2a1b47229b20aaa88aad61961ca72155c9ccc1f Mon Sep 17 00:00:00 2001 From: Alexander Wilms Date: Tue, 16 Jul 2024 16:13:58 +0200 Subject: [PATCH 053/100] CModListView::CModListView(): Set background color of QSplitter to window color for seamless appearance --- launcher/modManager/cmodlistview_moc.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/launcher/modManager/cmodlistview_moc.cpp b/launcher/modManager/cmodlistview_moc.cpp index 1a4aa2348..96615ad5d 100644 --- a/launcher/modManager/cmodlistview_moc.cpp +++ b/launcher/modManager/cmodlistview_moc.cpp @@ -134,6 +134,8 @@ CModListView::CModListView(QWidget * parent) ui->updateButton->setIcon(QIcon{":/icons/mod-update.png"}); ui->installButton->setIcon(QIcon{":/icons/mod-download.png"}); + ui->splitter->setStyleSheet("QSplitter::handle {background: palette('window');}"); + setupModModel(); setupFilterModel(); setupModsView(); From 55624026468f2ad7132ebe28e9a47a0953230128 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Wed, 17 Jul 2024 12:37:23 +0200 Subject: [PATCH 054/100] hotkey number from config --- client/battle/BattleInterfaceClasses.cpp | 6 ++++- client/battle/BattleWindow.cpp | 2 +- config/shortcutsConfig.json | 32 ++++++++++++------------ 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index 1ae1acd44..62d37c989 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -57,6 +57,8 @@ #include "../../lib/mapObjects/CGTownInstance.h" #include "../../lib/networkPacks/PacksForClientBattle.h" #include "../../lib/TextOperations.h" +#include "../../lib/json/JsonUtils.h" + void BattleConsole::showAll(Canvas & to) { @@ -437,6 +439,8 @@ void QuickSpellPanel::create() { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); + const JsonNode config = JsonUtils::assembleFromFiles("config/shortcutsConfig"); + labels.clear(); buttons.clear(); buttonsDisabled.clear(); @@ -476,7 +480,7 @@ void QuickSpellPanel::create() { buttonsDisabled.push_back(std::make_shared(Rect(2, 5 + 37 * i, 48, 36), ColorRGBA(0, 0, 0, 172))); } - labels.push_back(std::make_shared(7, 8 + 37 * i, EFonts::FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, std::to_string(i))); + labels.push_back(std::make_shared(7, 8 + 37 * i, EFonts::FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, config["keyboard"]["battleSpellShortcut" + std::to_string(i)].String())); buttons.push_back(button); } diff --git a/client/battle/BattleWindow.cpp b/client/battle/BattleWindow.cpp index f641a516a..67e416502 100644 --- a/client/battle/BattleWindow.cpp +++ b/client/battle/BattleWindow.cpp @@ -204,7 +204,7 @@ void BattleWindow::createQuickSpellWindow() OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; quickSpellWindow = std::make_shared(owner); - quickSpellWindow->moveTo(Point(pos.x - 67, pos.y - 1)); + quickSpellWindow->moveTo(Point(pos.x - 67, pos.y)); if(settings["battle"]["enableQuickSpellPanel"].Bool()) showStickyQuickSpellWindow(); diff --git a/config/shortcutsConfig.json b/config/shortcutsConfig.json index f637e6b52..caf62161d 100644 --- a/config/shortcutsConfig.json +++ b/config/shortcutsConfig.json @@ -65,22 +65,22 @@ "battleRetreat": "R", "battleSelectAction": "S", "battleToggleQuickSpell": "T", - "battleSpellShortcut0": "0", - "battleSpellShortcut1": "1", - "battleSpellShortcut2": "2", - "battleSpellShortcut3": "3", - "battleSpellShortcut4": "4", - "battleSpellShortcut5": "5", - "battleSpellShortcut6": "6", - "battleSpellShortcut7": "7", - "battleSpellShortcut8": "8", - "battleSpellShortcut9": "9", - "battleSpellShortcut10": "Shift+0", - "battleSpellShortcut11": "Shift+1", - "battleSpellShortcut12": "Shift+2", - "battleSpellShortcut13": "Shift+3", - "battleSpellShortcut14": "Shift+4", - "battleSpellShortcut15": "Shift+5", + "battleSpellShortcut0": "1", + "battleSpellShortcut1": "2", + "battleSpellShortcut2": "3", + "battleSpellShortcut3": "4", + "battleSpellShortcut4": "5", + "battleSpellShortcut5": "6", + "battleSpellShortcut6": "7", + "battleSpellShortcut7": "8", + "battleSpellShortcut8": "Shift+1", + "battleSpellShortcut9": "Shift+2", + "battleSpellShortcut10": "Shift+3", + "battleSpellShortcut11": "Shift+4", + "battleSpellShortcut12": "Shift+5", + "battleSpellShortcut13": "Shift+6", + "battleSpellShortcut14": "Shift+7", + "battleSpellShortcut15": "Shift+8", "battleSurrender": "S", "battleTacticsEnd": [ "Return", "Keypad Enter"], "battleTacticsNext": "Space", From 1ca8e9b3ae63144adec8db53b07b395525dad5ae Mon Sep 17 00:00:00 2001 From: Alexander Wilms Date: Sun, 14 Jul 2024 20:41:22 +0200 Subject: [PATCH 055/100] JsonParser::parse(): Print JSON if there were errors while parsing In cases where the file name was not specified, the warning messages were not very useful. Example: ```json File is not a valid JSON file! At line 33, position 1 warning: Comma expected! { "name" : "New Old Heroes", "description" : "New heroes based on old 3DO artwork and other game appearances. Requires Horn of the Abyss.", "modType" : "Heroes", "version" : "1.2.0", "author" : "Aphra", "contact" : "", "heroes" : [ "config/gwenneth.json", "config/balindar.json", "config/nicolas.json", "config/kastore.json", "config/kydoimos.json", "config/athe.json", "config/miseria.json", "config/areshrak.json", "config/pactal.json", "config/zog.json" ], "changelog" : { "1.0.0" : ["Initial release"], "1.1.0" : ["Added Nicolas Gryphonheart and Kastore"], "1.1.1" : ["Bug fixes"], "1.2.0" : ["Added some HotA portrait-only campaign heroes"] }, "depends" : [ "hota.neutralCreatures" ] "keepDisabled" : true } ``` --- lib/json/JsonParser.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/json/JsonParser.cpp b/lib/json/JsonParser.cpp index 320005b25..8f8fa0eef 100644 --- a/lib/json/JsonParser.cpp +++ b/lib/json/JsonParser.cpp @@ -57,6 +57,7 @@ JsonNode JsonParser::parse(const std::string & fileName) { logMod->warn("File %s is not a valid JSON file!", fileName); logMod->warn(errors); + logMod->warn("%s", input); } return root; } From f54b84ead94a286a3dca9949ed66407a1f9871a4 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Wed, 17 Jul 2024 13:20:17 +0200 Subject: [PATCH 056/100] use spellbook to select --- client/battle/BattleInterfaceClasses.cpp | 61 +++--------------------- client/battle/BattleInterfaceClasses.h | 18 ------- client/windows/CSpellWindow.cpp | 21 +++++++- client/windows/CSpellWindow.h | 5 +- 4 files changed, 29 insertions(+), 76 deletions(-) diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index 62d37c989..516ddceed 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -469,11 +469,12 @@ void QuickSpellPanel::create() } }); button->setOverlay(std::make_shared(AnimationPath::builtin("spellint"), !spellIdentifier.empty() ? id.num + 1 : 0)); - button->addPopupCallback([this, i](){ - auto panelSelect = std::make_shared(this); - panelSelect->spellSlot = i; - panelSelect->moveTo(Point(pos.x + 54, pos.y + 4)); - GH.windows().pushWindow(panelSelect); + button->addPopupCallback([this, i, hero](){ + GH.windows().createAndPushWindow(hero, owner.curInt.get(), true, [this, i](SpellID spell){ + Settings configID = persistentStorage.write["quickSpell"][std::to_string(i)]; + configID->String() = spell.toSpell()->identifier; + create(); + }); }); if(!id.hasValue() || !id.toSpell()->canBeCast(owner.getBattle().get(), spells::Mode::HERO, hero)) @@ -492,56 +493,6 @@ void QuickSpellPanel::show(Canvas & to) CIntObject::show(to); } -QuickSpellPanelSelect::QuickSpellPanelSelect(QuickSpellPanel * Parent) - : parent(Parent) -{ - OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); - - addUsedEvents(LCLICK); - - std::vector> spellList; - for (auto const & s : VLC->spellh->objects) - if (s->isCombat() && !s->isSpecial() && !s->isCreatureAbility()) - spellList.push_back(s); - - auto ceil = [](int x, int y){ return(x + y - 1) / y; }; - int columnsNeeded = ceil(spellList.size(), NUM_PER_COLUMN); - - pos = Rect(pos.x, pos.y, columnsNeeded * 50 + 2, 2 + 37 * NUM_PER_COLUMN); - background = std::make_shared(ImagePath::builtin("DIBOXBCK"), Rect(0, 0, pos.w, pos.h)); - rect = std::make_shared(Rect(0, 0, pos.w + 1, pos.h + 1), ColorRGBA(0, 0, 0, 0), ColorRGBA(241, 216, 120, 255)); - - for(int i = 0; i < spellList.size(); i++) - { - int y = i % NUM_PER_COLUMN; - int x = i / NUM_PER_COLUMN; - std::shared_ptr spell = spellList[i]; - auto button = std::make_shared(Point(2 + 50 * x, 1 + 37 * y), AnimationPath::builtin("spellint"), CButton::tooltip(), [this, spell](){ - close(); - GH.windows().totalRedraw(); - Settings configID = persistentStorage.write["quickSpell"][std::to_string(spellSlot)]; - configID->String() = spell->identifier; - parent->create(); - }); - button->addPopupCallback([spell](){ - CRClickPopup::createAndPush(spell->getDescriptionTranslated(0), std::make_shared(ComponentType::SPELL, spell->id)); - }); - button->setOverlay(std::make_shared(AnimationPath::builtin("spellint"), spellList[i]->getId().num + 1)); - buttons.push_back(button); - } -} - -void QuickSpellPanelSelect::clickReleased(const Point & cursorPosition) -{ - if(!pos.isInside(cursorPosition) && !parent->pos.isInside(cursorPosition)) - close(); -} - -bool QuickSpellPanelSelect::receiveEvent(const Point & position, int eventType) const -{ - return true; // capture click also outside of window -} - HeroInfoBasicPanel::HeroInfoBasicPanel(const InfoAboutHero & hero, Point * position, bool initializeBackground) : CIntObject(0) { diff --git a/client/battle/BattleInterfaceClasses.h b/client/battle/BattleInterfaceClasses.h index 5fe9049eb..9cce7143e 100644 --- a/client/battle/BattleInterfaceClasses.h +++ b/client/battle/BattleInterfaceClasses.h @@ -149,24 +149,6 @@ public: BattleHero(const BattleInterface & owner, const CGHeroInstance * hero, bool defender); }; -class QuickSpellPanelSelect : public CWindowObject -{ -private: - const int NUM_PER_COLUMN = 16; - - std::shared_ptr background; - std::shared_ptr rect; - std::vector> buttons; - QuickSpellPanel * parent; -public: - QuickSpellPanelSelect(QuickSpellPanel * Parent); - - bool receiveEvent(const Point & position, int eventType) const override; - void clickReleased(const Point & cursorPosition) override; - - int spellSlot; -}; - class QuickSpellPanel : public CIntObject { private: diff --git a/client/windows/CSpellWindow.cpp b/client/windows/CSpellWindow.cpp index 5c89602c3..e91cbbada 100644 --- a/client/windows/CSpellWindow.cpp +++ b/client/windows/CSpellWindow.cpp @@ -98,13 +98,15 @@ public: } }; -CSpellWindow::CSpellWindow(const CGHeroInstance * _myHero, CPlayerInterface * _myInt, bool openOnBattleSpells): +CSpellWindow::CSpellWindow(const CGHeroInstance * _myHero, CPlayerInterface * _myInt, bool openOnBattleSpells, std::function onSpellSelect): CWindowObject(PLAYER_COLORED | (settings["gameTweaks"]["enableLargeSpellbook"].Bool() ? BORDERED : 0)), battleSpellsOnly(openOnBattleSpells), selectedTab(4), currentPage(0), myHero(_myHero), myInt(_myInt), + openOnBattleSpells(openOnBattleSpells), + onSpellSelect(onSpellSelect), isBigSpellbook(settings["gameTweaks"]["enableLargeSpellbook"].Bool()), spellsPerPage(24), offL(-11), @@ -293,6 +295,14 @@ void CSpellWindow::processSpells() for(auto const & spell : CGI->spellh->objects) { bool searchTextFound = !searchBox || boost::algorithm::contains(boost::algorithm::to_lower_copy(spell->getNameTranslated()), boost::algorithm::to_lower_copy(searchBox->getText())); + + if(onSpellSelect) + { + if(spell->isCombat() == openOnBattleSpells && !spell->isSpecial() && !spell->isCreatureAbility()) + mySpells.push_back(spell.get()); + continue; + } + if(!spell->isCreatureAbility() && myHero->canCastThisSpell(spell.get()) && searchTextFound) mySpells.push_back(spell.get()); } @@ -602,6 +612,13 @@ void CSpellWindow::SpellArea::clickPressed(const Point & cursorPosition) { if(mySpell) { + if(owner->onSpellSelect) + { + owner->onSpellSelect(mySpell->id); + owner->fexitb(); + return; + } + auto spellCost = owner->myInt->cb->getSpellCost(mySpell, owner->myHero); if(spellCost > owner->myHero->mana) //insufficient mana { @@ -738,7 +755,7 @@ void CSpellWindow::SpellArea::setSpell(const CSpell * spell) } ColorRGBA firstLineColor, secondLineColor; - if(spellCost > owner->myHero->mana) //hero cannot cast this spell + if(spellCost > owner->myHero->mana || !owner->onSpellSelect) //hero cannot cast this spell { firstLineColor = Colors::WHITE; secondLineColor = Colors::ORANGE; diff --git a/client/windows/CSpellWindow.h b/client/windows/CSpellWindow.h index 16401a752..f9a761547 100644 --- a/client/windows/CSpellWindow.h +++ b/client/windows/CSpellWindow.h @@ -115,8 +115,11 @@ class CSpellWindow : public CWindowObject std::shared_ptr createBigSpellBook(); + bool openOnBattleSpells; + std::function onSpellSelect; //external processing of selected spell + public: - CSpellWindow(const CGHeroInstance * _myHero, CPlayerInterface * _myInt, bool openOnBattleSpells = true); + CSpellWindow(const CGHeroInstance * _myHero, CPlayerInterface * _myInt, bool openOnBattleSpells = true, std::function onSpellSelect = nullptr); ~CSpellWindow(); void fexitb(); From f0435d8b8e2db7d6e1bb544ae3946857d36b86a6 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Wed, 17 Jul 2024 13:48:00 +0200 Subject: [PATCH 057/100] small fix --- client/battle/BattleInterfaceClasses.cpp | 4 +++- client/battle/BattleInterfaceClasses.h | 2 ++ client/battle/BattleWindow.cpp | 11 +++++++++-- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index 516ddceed..1fa9518b7 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -44,6 +44,7 @@ #include "../render/CAnimation.h" #include "../render/IRenderHandler.h" #include "../adventureMap/CInGameConsole.h" +#include "../eventsSDL/InputHandler.h" #include "../../CCallback.h" #include "../../lib/CStack.h" @@ -470,6 +471,7 @@ void QuickSpellPanel::create() }); button->setOverlay(std::make_shared(AnimationPath::builtin("spellint"), !spellIdentifier.empty() ? id.num + 1 : 0)); button->addPopupCallback([this, i, hero](){ + GH.input().hapticFeedback(); GH.windows().createAndPushWindow(hero, owner.curInt.get(), true, [this, i](SpellID spell){ Settings configID = persistentStorage.write["quickSpell"][std::to_string(i)]; configID->String() = spell.toSpell()->identifier; @@ -479,7 +481,7 @@ void QuickSpellPanel::create() if(!id.hasValue() || !id.toSpell()->canBeCast(owner.getBattle().get(), spells::Mode::HERO, hero)) { - buttonsDisabled.push_back(std::make_shared(Rect(2, 5 + 37 * i, 48, 36), ColorRGBA(0, 0, 0, 172))); + buttonsDisabled.push_back(std::make_shared(Rect(2, 5 + 37 * i, 48, 36), ColorRGBA(0, 0, 0, 128))); } labels.push_back(std::make_shared(7, 8 + 37 * i, EFonts::FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, config["keyboard"]["battleSpellShortcut" + std::to_string(i)].String())); diff --git a/client/battle/BattleInterfaceClasses.h b/client/battle/BattleInterfaceClasses.h index 9cce7143e..d40f04ea4 100644 --- a/client/battle/BattleInterfaceClasses.h +++ b/client/battle/BattleInterfaceClasses.h @@ -160,6 +160,8 @@ private: BattleInterface & owner; public: + bool isEnabled; // isActive() is not working on multiple conditions, because of this we need a seperate flag + QuickSpellPanel(BattleInterface & owner); void create(); diff --git a/client/battle/BattleWindow.cpp b/client/battle/BattleWindow.cpp index 67e416502..b60135d51 100644 --- a/client/battle/BattleWindow.cpp +++ b/client/battle/BattleWindow.cpp @@ -226,6 +226,7 @@ void BattleWindow::hideStickyQuickSpellWindow() showStickyQuickSpellWindow->Bool() = false; quickSpellWindow->disable(); + quickSpellWindow->isEnabled = false; setPositionInfoWindow(); createTimerInfoWindows(); @@ -238,9 +239,15 @@ void BattleWindow::showStickyQuickSpellWindow() showStickyQuickSpellWindow->Bool() = true; if(GH.screenDimensions().x >= 1050) + { quickSpellWindow->enable(); + quickSpellWindow->isEnabled = true; + } else + { quickSpellWindow->disable(); + quickSpellWindow->isEnabled = false; + } setPositionInfoWindow(); createTimerInfoWindows(); @@ -251,7 +258,7 @@ void BattleWindow::createTimerInfoWindows() { OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; - int xOffsetAttacker = quickSpellWindow->isActive() ? -53 : 0; + int xOffsetAttacker = quickSpellWindow->isEnabled ? -53 : 0; if(LOCPLINT->cb->getStartInfo()->turnTimerInfo.battleTimer != 0 || LOCPLINT->cb->getStartInfo()->turnTimerInfo.unitTimer != 0) { @@ -373,7 +380,7 @@ void BattleWindow::updateQueue() void BattleWindow::setPositionInfoWindow() { - int xOffsetAttacker = quickSpellWindow->isActive() ? -53 : 0; + int xOffsetAttacker = quickSpellWindow->isEnabled ? -53 : 0; if(defenderHeroWindow) { Point position = (GH.screenDimensions().x >= 1000) From 434371195d949f068bdc73fa472780023c2db5fe Mon Sep 17 00:00:00 2001 From: Alexander Wilms Date: Wed, 17 Jul 2024 13:07:57 +0200 Subject: [PATCH 058/100] JsonNode constructors: Take fileName as argument * Don't print JSON in JsonParser::parse() in case of errors --- client/globalLobby/GlobalLobbyClient.cpp | 2 +- client/render/CAnimation.cpp | 2 +- launcher/jsonutils.cpp | 2 +- launcher/updatedialog_moc.cpp | 2 +- lib/campaign/CampaignHandler.cpp | 2 +- lib/filesystem/Filesystem.cpp | 4 ++-- lib/json/JsonNode.cpp | 8 ++++---- lib/json/JsonNode.h | 4 ++-- lib/json/JsonParser.cpp | 3 +-- lib/json/JsonUtils.cpp | 2 +- lib/mapping/MapFormatJson.cpp | 2 +- lobby/LobbyServer.cpp | 4 ++-- mapeditor/Animation.cpp | 2 +- mapeditor/jsonutils.cpp | 2 +- server/GlobalLobbyProcessor.cpp | 2 +- 15 files changed, 21 insertions(+), 22 deletions(-) diff --git a/client/globalLobby/GlobalLobbyClient.cpp b/client/globalLobby/GlobalLobbyClient.cpp index 9b58d782f..018af2334 100644 --- a/client/globalLobby/GlobalLobbyClient.cpp +++ b/client/globalLobby/GlobalLobbyClient.cpp @@ -43,7 +43,7 @@ void GlobalLobbyClient::onPacketReceived(const std::shared_ptr"); if(json["type"].String() == "accountCreated") return receiveAccountCreated(json); diff --git a/client/render/CAnimation.cpp b/client/render/CAnimation.cpp index 93549f333..9feaf51a4 100644 --- a/client/render/CAnimation.cpp +++ b/client/render/CAnimation.cpp @@ -191,7 +191,7 @@ void CAnimation::init() std::unique_ptr textData(new ui8[stream->getSize()]); stream->read(textData.get(), stream->getSize()); - const JsonNode config(reinterpret_cast(textData.get()), stream->getSize()); + const JsonNode config(reinterpret_cast(textData.get()), stream->getSize(), jsonResource.getName()); initFromJson(config); } diff --git a/launcher/jsonutils.cpp b/launcher/jsonutils.cpp index 4635e9f50..e4eb884d6 100644 --- a/launcher/jsonutils.cpp +++ b/launcher/jsonutils.cpp @@ -89,7 +89,7 @@ QVariant JsonFromFile(QString filename) } const auto data = file.readAll(); - JsonNode node(reinterpret_cast(data.data()), data.size()); + JsonNode node(reinterpret_cast(data.data()), data.size(), filename.toStdString()); return toVariant(node); } diff --git a/launcher/updatedialog_moc.cpp b/launcher/updatedialog_moc.cpp index 6c9825868..2de3aba7d 100644 --- a/launcher/updatedialog_moc.cpp +++ b/launcher/updatedialog_moc.cpp @@ -67,7 +67,7 @@ UpdateDialog::UpdateDialog(bool calledManually, QWidget *parent): } auto byteArray = response->readAll(); - JsonNode node(reinterpret_cast(byteArray.constData()), byteArray.size()); + JsonNode node(reinterpret_cast(byteArray.constData()), byteArray.size(), ""); loadFromJson(node); }); } diff --git a/lib/campaign/CampaignHandler.cpp b/lib/campaign/CampaignHandler.cpp index f4099a827..5de4f9c7b 100644 --- a/lib/campaign/CampaignHandler.cpp +++ b/lib/campaign/CampaignHandler.cpp @@ -46,7 +46,7 @@ void CampaignHandler::readCampaign(Campaign * ret, const std::vector & inpu } else // text format (json) { - JsonNode jsonCampaign(reinterpret_cast(input.data()), input.size()); + JsonNode jsonCampaign(reinterpret_cast(input.data()), input.size(), filename); readHeaderFromJson(*ret, jsonCampaign, filename, modName, encoding); for(auto & scenario : jsonCampaign["scenarios"].Vector()) diff --git a/lib/filesystem/Filesystem.cpp b/lib/filesystem/Filesystem.cpp index 7b4821c5c..b873e0718 100644 --- a/lib/filesystem/Filesystem.cpp +++ b/lib/filesystem/Filesystem.cpp @@ -117,7 +117,7 @@ void CFilesystemGenerator::loadJsonMap(const std::string &mountPoint, const Json if (filename) { auto configData = CResourceHandler::get("initial")->load(JsonPath::builtin(URI))->readAll(); - const JsonNode configInitial(reinterpret_cast(configData.first.get()), configData.second); + const JsonNode configInitial(reinterpret_cast(configData.first.get()), configData.second, URI); filesystem->addLoader(new CMappedFileLoader(mountPoint, configInitial), false); } } @@ -212,7 +212,7 @@ void CResourceHandler::load(const std::string &fsConfigURI, bool extractArchives { auto fsConfigData = get("initial")->load(JsonPath::builtin(fsConfigURI))->readAll(); - const JsonNode fsConfig(reinterpret_cast(fsConfigData.first.get()), fsConfigData.second); + const JsonNode fsConfig(reinterpret_cast(fsConfigData.first.get()), fsConfigData.second, fsConfigURI); addFilesystem("data", ModScope::scopeBuiltin(), createFileSystem("", fsConfig["filesystem"], extractArchives)); } diff --git a/lib/json/JsonNode.cpp b/lib/json/JsonNode.cpp index 32712c85a..65e5bf0f8 100644 --- a/lib/json/JsonNode.cpp +++ b/lib/json/JsonNode.cpp @@ -86,15 +86,15 @@ JsonNode::JsonNode(const std::string & string) { } -JsonNode::JsonNode(const std::byte * data, size_t datasize) - : JsonNode(data, datasize, JsonParsingSettings()) +JsonNode::JsonNode(const std::byte * data, size_t datasize, const std::string fileName) + : JsonNode(data, datasize, JsonParsingSettings(), fileName) { } -JsonNode::JsonNode(const std::byte * data, size_t datasize, const JsonParsingSettings & parserSettings) +JsonNode::JsonNode(const std::byte * data, size_t datasize, const JsonParsingSettings & parserSettings, const std::string fileName) { JsonParser parser(data, datasize, parserSettings); - *this = parser.parse(""); + *this = parser.parse(fileName); } JsonNode::JsonNode(const JsonPath & fileURI) diff --git a/lib/json/JsonNode.h b/lib/json/JsonNode.h index c4afbcae1..a4a81c8c2 100644 --- a/lib/json/JsonNode.h +++ b/lib/json/JsonNode.h @@ -72,8 +72,8 @@ public: explicit JsonNode(const std::string & string); /// Create tree from Json-formatted input - explicit JsonNode(const std::byte * data, size_t datasize); - explicit JsonNode(const std::byte * data, size_t datasize, const JsonParsingSettings & parserSettings); + explicit JsonNode(const std::byte * data, size_t datasize, const std::string fileName); + explicit JsonNode(const std::byte * data, size_t datasize, const JsonParsingSettings & parserSettings, const std::string fileName); /// Create tree from JSON file explicit JsonNode(const JsonPath & fileURI); diff --git a/lib/json/JsonParser.cpp b/lib/json/JsonParser.cpp index 8f8fa0eef..f3ad1ab9b 100644 --- a/lib/json/JsonParser.cpp +++ b/lib/json/JsonParser.cpp @@ -55,9 +55,8 @@ JsonNode JsonParser::parse(const std::string & fileName) if(!errors.empty()) { - logMod->warn("File %s is not a valid JSON file!", fileName); + logMod->warn("%s is not valid JSON!", fileName); logMod->warn(errors); - logMod->warn("%s", input); } return root; } diff --git a/lib/json/JsonUtils.cpp b/lib/json/JsonUtils.cpp index f2ea7a78e..f1680e11f 100644 --- a/lib/json/JsonUtils.cpp +++ b/lib/json/JsonUtils.cpp @@ -269,7 +269,7 @@ JsonNode JsonUtils::assembleFromFiles(const std::string & filename) for(auto & loader : CResourceHandler::get()->getResourcesWithName(resID)) { auto textData = loader->load(resID)->readAll(); - JsonNode section(reinterpret_cast(textData.first.get()), textData.second); + JsonNode section(reinterpret_cast(textData.first.get()), textData.second, resID.getName()); merge(result, section); } return result; diff --git a/lib/mapping/MapFormatJson.cpp b/lib/mapping/MapFormatJson.cpp index eaa966959..cc6ba42f5 100644 --- a/lib/mapping/MapFormatJson.cpp +++ b/lib/mapping/MapFormatJson.cpp @@ -813,7 +813,7 @@ JsonNode CMapLoaderJson::getFromArchive(const std::string & archiveFilename) auto data = loader.load(resource)->readAll(); - JsonNode res(reinterpret_cast(data.first.get()), data.second); + JsonNode res(reinterpret_cast(data.first.get()), data.second, archiveFilename); return res; } diff --git a/lobby/LobbyServer.cpp b/lobby/LobbyServer.cpp index 6ee3afba1..7f1a08635 100644 --- a/lobby/LobbyServer.cpp +++ b/lobby/LobbyServer.cpp @@ -213,7 +213,7 @@ static JsonNode loadLobbyGameRoomToJson(const LobbyGameRoom & gameRoom) jsonEntry["playerLimit"].Integer() = gameRoom.playerLimit; jsonEntry["ageSeconds"].Integer() = gameRoom.age.count(); if (!gameRoom.modsJson.empty()) // not present in match history - jsonEntry["mods"] = JsonNode(reinterpret_cast(gameRoom.modsJson.data()), gameRoom.modsJson.size()); + jsonEntry["mods"] = JsonNode(reinterpret_cast(gameRoom.modsJson.data()), gameRoom.modsJson.size(), ""); for(const auto & account : gameRoom.participants) jsonEntry["participants"].Vector().push_back(loadLobbyAccountToJson(account)); @@ -348,7 +348,7 @@ JsonNode LobbyServer::parseAndValidateMessage(const std::vector & mes JsonNode json; try { - JsonNode jsonTemp(message.data(), message.size()); + JsonNode jsonTemp(message.data(), message.size(), ""); json = std::move(jsonTemp); } catch (const JsonFormatException & e) diff --git a/mapeditor/Animation.cpp b/mapeditor/Animation.cpp index 16019a65a..94e217610 100644 --- a/mapeditor/Animation.cpp +++ b/mapeditor/Animation.cpp @@ -599,7 +599,7 @@ void Animation::init() std::unique_ptr textData(new ui8[stream->getSize()]); stream->read(textData.get(), stream->getSize()); - const JsonNode config(reinterpret_cast(textData.get()), stream->getSize()); + const JsonNode config(reinterpret_cast(textData.get()), stream->getSize(), resID.getName()); initFromJson(config); } diff --git a/mapeditor/jsonutils.cpp b/mapeditor/jsonutils.cpp index f7ce677bb..5ab2335af 100644 --- a/mapeditor/jsonutils.cpp +++ b/mapeditor/jsonutils.cpp @@ -97,7 +97,7 @@ QVariant JsonFromFile(QString filename) } else { - JsonNode node(reinterpret_cast(data.data()), data.size()); + JsonNode node(reinterpret_cast(data.data()), data.size(), filename.toStdString()); return toVariant(node); } } diff --git a/server/GlobalLobbyProcessor.cpp b/server/GlobalLobbyProcessor.cpp index 7effca617..15fd376f7 100644 --- a/server/GlobalLobbyProcessor.cpp +++ b/server/GlobalLobbyProcessor.cpp @@ -67,7 +67,7 @@ void GlobalLobbyProcessor::onPacketReceived(const std::shared_ptr"); if(json["type"].String() == "operationFailed") return receiveOperationFailed(json); From becd13e03d76429e858d88a8f99f02fe271c090b Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 17 Jul 2024 11:57:03 +0000 Subject: [PATCH 059/100] Fix page flip animation playback --- client/media/CVideoHandler.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/client/media/CVideoHandler.cpp b/client/media/CVideoHandler.cpp index 82777e996..d32da36bd 100644 --- a/client/media/CVideoHandler.cpp +++ b/client/media/CVideoHandler.cpp @@ -586,7 +586,7 @@ bool CVideoPlayer::openAndPlayVideoImpl(const VideoPath & name, const Point & po return true; instance.openVideo(); - instance.prepareOutput(scale, useOverlay); + instance.prepareOutput(scale, true); auto lastTimePoint = boost::chrono::steady_clock::now(); @@ -608,10 +608,7 @@ bool CVideoPlayer::openAndPlayVideoImpl(const VideoPath & name, const Point & po rect.w = instance.dimensions.x; rect.h = instance.dimensions.y; - if(useOverlay) - SDL_RenderFillRect(mainRenderer, &rect); - else - SDL_RenderClear(mainRenderer); + SDL_RenderFillRect(mainRenderer, &rect); if(instance.textureYUV) SDL_RenderCopy(mainRenderer, instance.textureYUV, nullptr, &rect); From c484244ba03fc30e032daab19bf9fece448f4385 Mon Sep 17 00:00:00 2001 From: Alexander Wilms Date: Wed, 17 Jul 2024 14:01:13 +0200 Subject: [PATCH 060/100] JsonNode::JsonNode(): Use reference for fileName argument Co-authored-by: Ivan Savenko --- lib/json/JsonNode.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/json/JsonNode.h b/lib/json/JsonNode.h index a4a81c8c2..7ed8ae9ae 100644 --- a/lib/json/JsonNode.h +++ b/lib/json/JsonNode.h @@ -72,8 +72,8 @@ public: explicit JsonNode(const std::string & string); /// Create tree from Json-formatted input - explicit JsonNode(const std::byte * data, size_t datasize, const std::string fileName); - explicit JsonNode(const std::byte * data, size_t datasize, const JsonParsingSettings & parserSettings, const std::string fileName); + explicit JsonNode(const std::byte * data, size_t datasize, const std::string & fileName); + explicit JsonNode(const std::byte * data, size_t datasize, const JsonParsingSettings & parserSettings, const std::string & fileName); /// Create tree from JSON file explicit JsonNode(const JsonPath & fileURI); From 2dd22c383b60397d302482743f7f097d5f1c8fd5 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Wed, 17 Jul 2024 14:05:14 +0200 Subject: [PATCH 061/100] make inactive buttons darker --- client/battle/BattleInterfaceClasses.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index 1fa9518b7..995146f1c 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -481,7 +481,7 @@ void QuickSpellPanel::create() if(!id.hasValue() || !id.toSpell()->canBeCast(owner.getBattle().get(), spells::Mode::HERO, hero)) { - buttonsDisabled.push_back(std::make_shared(Rect(2, 5 + 37 * i, 48, 36), ColorRGBA(0, 0, 0, 128))); + buttonsDisabled.push_back(std::make_shared(Rect(2, 5 + 37 * i, 48, 36), ColorRGBA(0, 0, 0, 172))); } labels.push_back(std::make_shared(7, 8 + 37 * i, EFonts::FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, config["keyboard"]["battleSpellShortcut" + std::to_string(i)].String())); From b605dea6db3d1a8f15112db8209deb916b638e2f Mon Sep 17 00:00:00 2001 From: Alexander Wilms Date: Wed, 17 Jul 2024 14:37:58 +0200 Subject: [PATCH 062/100] JsonNode::JsonNode(): Also use reference for fileName argument in method definition --- lib/json/JsonNode.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/json/JsonNode.cpp b/lib/json/JsonNode.cpp index 65e5bf0f8..4d0e7ddd5 100644 --- a/lib/json/JsonNode.cpp +++ b/lib/json/JsonNode.cpp @@ -86,12 +86,12 @@ JsonNode::JsonNode(const std::string & string) { } -JsonNode::JsonNode(const std::byte * data, size_t datasize, const std::string fileName) +JsonNode::JsonNode(const std::byte * data, size_t datasize, const std::string & fileName) : JsonNode(data, datasize, JsonParsingSettings(), fileName) { } -JsonNode::JsonNode(const std::byte * data, size_t datasize, const JsonParsingSettings & parserSettings, const std::string fileName) +JsonNode::JsonNode(const std::byte * data, size_t datasize, const JsonParsingSettings & parserSettings, const std::string & fileName) { JsonParser parser(data, datasize, parserSettings); *this = parser.parse(fileName); From 5712c3f9f42b36b2511de484832500469e673655 Mon Sep 17 00:00:00 2001 From: Alexander Wilms Date: Wed, 17 Jul 2024 15:20:18 +0200 Subject: [PATCH 063/100] JsonNode change: Fix test --- test/map/CMapFormatTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/map/CMapFormatTest.cpp b/test/map/CMapFormatTest.cpp index 176d5d916..21dec61b0 100644 --- a/test/map/CMapFormatTest.cpp +++ b/test/map/CMapFormatTest.cpp @@ -95,7 +95,7 @@ static JsonNode getFromArchive(CZipLoader & archive, const std::string & archive auto data = archive.load(resource)->readAll(); - JsonNode res(reinterpret_cast(data.first.get()), data.second); + JsonNode res(reinterpret_cast(data.first.get()), data.second, resource.getName()); return res; } From 7571fb704676a03fca0ab8f47a14ad595c427fcd Mon Sep 17 00:00:00 2001 From: Andrii Danylchenko Date: Wed, 17 Jul 2024 17:09:04 +0300 Subject: [PATCH 064/100] NKAI: fix gosolo when human is lead by gosolo AI + multiplayer --- AI/Nullkiller/AIGateway.cpp | 12 ++++++++++++ client/ClientCommandManager.cpp | 29 ++++++++++++++++++----------- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/AI/Nullkiller/AIGateway.cpp b/AI/Nullkiller/AIGateway.cpp index f3feeda6b..ec1135ee0 100644 --- a/AI/Nullkiller/AIGateway.cpp +++ b/AI/Nullkiller/AIGateway.cpp @@ -28,6 +28,7 @@ #include "../../lib/networkPacks/StackLocation.h" #include "../../lib/battle/BattleStateInfoForRetreat.h" #include "../../lib/battle/BattleInfo.h" +#include "../../lib/CPlayerState.h" #include "AIGateway.h" #include "Goals/Goals.h" @@ -1166,6 +1167,17 @@ void AIGateway::battleEnd(const BattleID & battleID, const BattleResult * br, Qu battlename.clear(); CAdventureAI::battleEnd(battleID, br, queryID); + + // gosolo + if(queryID != QueryID::NONE && myCb->getPlayerState(playerID)->isHuman()) + { + status.addQuery(queryID, "Confirm battle query"); + + requestActionASAP([=]() + { + answerQuery(queryID, 0); + }); + } } void AIGateway::waitTillFree() diff --git a/client/ClientCommandManager.cpp b/client/ClientCommandManager.cpp index 5bba85706..3119c7f68 100644 --- a/client/ClientCommandManager.cpp +++ b/client/ClientCommandManager.cpp @@ -39,6 +39,7 @@ #include "../lib/VCMIDirs.h" #include "../lib/logging/VisualLogger.h" #include "CMT.h" +#include "../lib/serializer/Connection.h" #ifdef SCRIPTING_ENABLED #include "../lib/ScriptHandler.h" @@ -82,31 +83,37 @@ void ClientCommandManager::handleGoSoloCommand() printCommandMessage("Game is not in playing state"); return; } - PlayerColor color; + if(session["aiSolo"].Bool()) { - for(auto & elem : CSH->client->gameState()->players) + // unlikely it will work but just in case to be consistent + for(auto & color : CSH->getAllClientPlayers(CSH->logicConnection->connectionID)) { - if(elem.second.human) - CSH->client->installNewPlayerInterface(std::make_shared(elem.first), elem.first); + if(CSH->client->getStartInfo()->playerInfos.at(color).isControlledByHuman()) + { + CSH->client->installNewPlayerInterface(std::make_shared(color), color); + } } } else { - color = LOCPLINT->playerID; + PlayerColor currentColor = LOCPLINT->playerID; CSH->client->removeGUI(); - for(auto & elem : CSH->client->gameState()->players) + + for(auto & color : CSH->getAllClientPlayers(CSH->logicConnection->connectionID)) { - if(elem.second.human) + if(CSH->client->getStartInfo()->playerInfos.at(color).isControlledByHuman()) { - auto AiToGive = CSH->client->aiNameForPlayer(*CSH->client->getPlayerSettings(elem.first), false, false); - printCommandMessage("Player " + elem.first.toString() + " will be lead by " + AiToGive, ELogLevel::INFO); - CSH->client->installNewPlayerInterface(CDynLibHandler::getNewAI(AiToGive), elem.first); + auto AiToGive = CSH->client->aiNameForPlayer(*CSH->client->getPlayerSettings(color), false, false); + printCommandMessage("Player " + color.toString() + " will be lead by " + AiToGive, ELogLevel::INFO); + CSH->client->installNewPlayerInterface(CDynLibHandler::getNewAI(AiToGive), color); } } + GH.windows().totalRedraw(); - giveTurn(color); + giveTurn(currentColor); } + session["aiSolo"].Bool() = !session["aiSolo"].Bool(); } From 4415f645a719db514bca357585863631206b41fb Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Wed, 17 Jul 2024 18:07:45 +0200 Subject: [PATCH 065/100] lambda -> function; fix color --- client/battle/BattleWindow.cpp | 66 +++++++++++++++++---------------- client/battle/BattleWindow.h | 2 + client/windows/CSpellWindow.cpp | 2 +- 3 files changed, 37 insertions(+), 33 deletions(-) diff --git a/client/battle/BattleWindow.cpp b/client/battle/BattleWindow.cpp index b60135d51..ecfe285ce 100644 --- a/client/battle/BattleWindow.cpp +++ b/client/battle/BattleWindow.cpp @@ -64,39 +64,23 @@ BattleWindow::BattleWindow(BattleInterface & Owner): const JsonNode config(JsonPath::builtin("config/widgets/BattleWindow2.json")); - auto useSpellIfPossible = [this](int slot){ - std::string spellIdentifier = persistentStorage["quickSpell"][std::to_string(slot)].String(); - SpellID id; - try - { - id = SpellID::decode(spellIdentifier); - } - catch(const IdentifierResolutionException& e) - { - return; - } - if(id.hasValue() && owner.getBattle()->battleGetMyHero() && id.toSpell()->canBeCast(owner.getBattle().get(), spells::Mode::HERO, owner.getBattle()->battleGetMyHero())) - { - owner.castThisSpell(id); - } - }; addShortcut(EShortcut::BATTLE_TOGGLE_QUICKSPELL, [this](){ this->toggleStickyQuickSpellVisibility();}); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_0, [useSpellIfPossible](){ useSpellIfPossible(0); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_1, [useSpellIfPossible](){ useSpellIfPossible(1); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_2, [useSpellIfPossible](){ useSpellIfPossible(2); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_3, [useSpellIfPossible](){ useSpellIfPossible(3); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_4, [useSpellIfPossible](){ useSpellIfPossible(4); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_5, [useSpellIfPossible](){ useSpellIfPossible(5); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_6, [useSpellIfPossible](){ useSpellIfPossible(6); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_7, [useSpellIfPossible](){ useSpellIfPossible(7); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_8, [useSpellIfPossible](){ useSpellIfPossible(8); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_9, [useSpellIfPossible](){ useSpellIfPossible(9); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_10, [useSpellIfPossible](){ useSpellIfPossible(10); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_11, [useSpellIfPossible](){ useSpellIfPossible(11); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_12, [useSpellIfPossible](){ useSpellIfPossible(12); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_13, [useSpellIfPossible](){ useSpellIfPossible(13); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_14, [useSpellIfPossible](){ useSpellIfPossible(14); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_15, [useSpellIfPossible](){ useSpellIfPossible(15); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_0, [this](){ useSpellIfPossible(0); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_1, [this](){ useSpellIfPossible(1); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_2, [this](){ useSpellIfPossible(2); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_3, [this](){ useSpellIfPossible(3); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_4, [this](){ useSpellIfPossible(4); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_5, [this](){ useSpellIfPossible(5); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_6, [this](){ useSpellIfPossible(6); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_7, [this](){ useSpellIfPossible(7); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_8, [this](){ useSpellIfPossible(8); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_9, [this](){ useSpellIfPossible(9); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_10, [this](){ useSpellIfPossible(10); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_11, [this](){ useSpellIfPossible(11); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_12, [this](){ useSpellIfPossible(12); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_13, [this](){ useSpellIfPossible(13); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_14, [this](){ useSpellIfPossible(14); }); + addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_15, [this](){ useSpellIfPossible(15); }); addShortcut(EShortcut::GLOBAL_OPTIONS, std::bind(&BattleWindow::bOptionsf, this)); addShortcut(EShortcut::BATTLE_SURRENDER, std::bind(&BattleWindow::bSurrenderf, this)); @@ -291,6 +275,24 @@ std::shared_ptr BattleWindow::buildBattleConsole(const JsonNode & return std::make_shared(owner, background, rect.topLeft(), offset, rect.dimensions() ); } +void BattleWindow::useSpellIfPossible(int slot) +{ + std::string spellIdentifier = persistentStorage["quickSpell"][std::to_string(slot)].String(); + SpellID id; + try + { + id = SpellID::decode(spellIdentifier); + } + catch(const IdentifierResolutionException& e) + { + return; + } + if(id.hasValue() && owner.getBattle()->battleGetMyHero() && id.toSpell()->canBeCast(owner.getBattle().get(), spells::Mode::HERO, owner.getBattle()->battleGetMyHero())) + { + owner.castThisSpell(id); + } +}; + void BattleWindow::toggleQueueVisibility() { if(settings["battle"]["showQueue"].Bool()) diff --git a/client/battle/BattleWindow.h b/client/battle/BattleWindow.h index d5b21657a..8292a6301 100644 --- a/client/battle/BattleWindow.h +++ b/client/battle/BattleWindow.h @@ -71,6 +71,8 @@ class BattleWindow : public InterfaceObjectConfigurable PossiblePlayerBattleAction defaultAction; void showAlternativeActionIcon(PossiblePlayerBattleAction); + void useSpellIfPossible(int slot); + /// flip battle queue visibility to opposite void toggleQueueVisibility(); void createQueue(); diff --git a/client/windows/CSpellWindow.cpp b/client/windows/CSpellWindow.cpp index e91cbbada..f20c0e306 100644 --- a/client/windows/CSpellWindow.cpp +++ b/client/windows/CSpellWindow.cpp @@ -755,7 +755,7 @@ void CSpellWindow::SpellArea::setSpell(const CSpell * spell) } ColorRGBA firstLineColor, secondLineColor; - if(spellCost > owner->myHero->mana || !owner->onSpellSelect) //hero cannot cast this spell + if(spellCost > owner->myHero->mana && !owner->onSpellSelect) //hero cannot cast this spell { firstLineColor = Colors::WHITE; secondLineColor = Colors::ORANGE; From 1b3fcbcd8d59cd4306d66b4c3e43ee9d9d249ab4 Mon Sep 17 00:00:00 2001 From: Alexander Wilms Date: Wed, 17 Jul 2024 18:17:55 +0200 Subject: [PATCH 066/100] CMake.md: Use markdown list instead of HTML table --- docs/developers/CMake.md | 56 +++++++++++++++------------------------- 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/docs/developers/CMake.md b/docs/developers/CMake.md index 720491e4b..140d7ef89 100644 --- a/docs/developers/CMake.md +++ b/docs/developers/CMake.md @@ -1,37 +1,23 @@ # CMake options - - - - - - - - - - - - - - - - - - - - - - - - - - -
OptionEffect
-D CMAKE_BUILD_TYPE=DebugEnables debug info and disables optimizations
-D CMAKE_EXPORT_COMPILE_COMMANDS=ONCreates compile_commands.json for clangd language server -

- For clangd to find the JSON, create a file named .clangd -
.
-├── vcmi -> contains sources and is under git control
-├── build -> contains build output, makefiles, object files,...
-└── .clangd
- with the following content -
CompileFlags:
CompilationDatabase: build
-D ENABLE_CCACHE:BOOL=ONSpeeds up recompilation
-G NinjaUse Ninja build system instead of make, which speeds up the build and doesn't require a -j flag
+* `-D CMAKE_BUILD_TYPE=Debug` + * Enables debug info and disables optimizations +* `-D CMAKE_EXPORT_COMPILE_COMMANDS=ON` + * Creates `compile_commands.json` for [clangd](https://clangd.llvm.org/) language server. + + For clangd to find the JSON, create a file named `.clangd` with this content + ``` + CompileFlags: + CompilationDatabase: build + ``` + and place it here: + ``` + . + ├── vcmi -> contains sources and is under git control + ├── build -> contains build output, makefiles, object files,... + └── .clangd + ``` +* `-D ENABLE_CCACHE:BOOL=ON` + * Speeds up recompilation +* `-G Ninja` + * Use Ninja build system instead of Make, which speeds up the build and doesn't require a `-j` flag \ No newline at end of file From e629b7a371c33b6a68b6b5828e9f79f6c880eb5c Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Wed, 17 Jul 2024 19:26:18 +0200 Subject: [PATCH 067/100] reduce to 12 with gap --- client/battle/BattleInterfaceClasses.cpp | 8 ++++---- client/battle/BattleWindow.cpp | 4 ---- client/gui/Shortcut.h | 4 ---- client/gui/ShortcutHandler.cpp | 4 ---- config/shortcutsConfig.json | 12 ++++-------- 5 files changed, 8 insertions(+), 24 deletions(-) diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index 995146f1c..9837a071a 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -450,7 +450,7 @@ void QuickSpellPanel::create() if(!hero) return; - for(int i = 0; i < 16; i++) { + for(int i = 0; i < 12; i++) { std::string spellIdentifier = persistentStorage["quickSpell"][std::to_string(i)].String(); SpellID id; @@ -463,7 +463,7 @@ void QuickSpellPanel::create() id = SpellID::NONE; } - auto button = std::make_shared(Point(2, 5 + 37 * i), AnimationPath::builtin("spellint"), CButton::tooltip(), [this, id, hero](){ + auto button = std::make_shared(Point(2, 7 + 50 * i), AnimationPath::builtin("spellint"), CButton::tooltip(), [this, id, hero](){ if(id.hasValue() && id.toSpell()->canBeCast(owner.getBattle().get(), spells::Mode::HERO, hero)) { owner.castThisSpell(id); @@ -481,9 +481,9 @@ void QuickSpellPanel::create() if(!id.hasValue() || !id.toSpell()->canBeCast(owner.getBattle().get(), spells::Mode::HERO, hero)) { - buttonsDisabled.push_back(std::make_shared(Rect(2, 5 + 37 * i, 48, 36), ColorRGBA(0, 0, 0, 172))); + buttonsDisabled.push_back(std::make_shared(Rect(2, 7 + 50 * i, 48, 36), ColorRGBA(0, 0, 0, 172))); } - labels.push_back(std::make_shared(7, 8 + 37 * i, EFonts::FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, config["keyboard"]["battleSpellShortcut" + std::to_string(i)].String())); + labels.push_back(std::make_shared(7, 10 + 50 * i, EFonts::FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, config["keyboard"]["battleSpellShortcut" + std::to_string(i)].String())); buttons.push_back(button); } diff --git a/client/battle/BattleWindow.cpp b/client/battle/BattleWindow.cpp index ecfe285ce..fdc6b7fb7 100644 --- a/client/battle/BattleWindow.cpp +++ b/client/battle/BattleWindow.cpp @@ -77,10 +77,6 @@ BattleWindow::BattleWindow(BattleInterface & Owner): addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_9, [this](){ useSpellIfPossible(9); }); addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_10, [this](){ useSpellIfPossible(10); }); addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_11, [this](){ useSpellIfPossible(11); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_12, [this](){ useSpellIfPossible(12); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_13, [this](){ useSpellIfPossible(13); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_14, [this](){ useSpellIfPossible(14); }); - addShortcut(EShortcut::BATTLE_SPELL_SHORTCUT_15, [this](){ useSpellIfPossible(15); }); addShortcut(EShortcut::GLOBAL_OPTIONS, std::bind(&BattleWindow::bOptionsf, this)); addShortcut(EShortcut::BATTLE_SURRENDER, std::bind(&BattleWindow::bSurrenderf, this)); diff --git a/client/gui/Shortcut.h b/client/gui/Shortcut.h index 57027fa44..aedf695d9 100644 --- a/client/gui/Shortcut.h +++ b/client/gui/Shortcut.h @@ -199,10 +199,6 @@ enum class EShortcut BATTLE_SPELL_SHORTCUT_9, BATTLE_SPELL_SHORTCUT_10, BATTLE_SPELL_SHORTCUT_11, - BATTLE_SPELL_SHORTCUT_12, - BATTLE_SPELL_SHORTCUT_13, - BATTLE_SPELL_SHORTCUT_14, - BATTLE_SPELL_SHORTCUT_15, MARKET_DEAL, MARKET_MAX_AMOUNT, diff --git a/client/gui/ShortcutHandler.cpp b/client/gui/ShortcutHandler.cpp index 9e6ce0e96..259a1c6cb 100644 --- a/client/gui/ShortcutHandler.cpp +++ b/client/gui/ShortcutHandler.cpp @@ -235,10 +235,6 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const {"battleSpellShortcut9", EShortcut::BATTLE_SPELL_SHORTCUT_9 }, {"battleSpellShortcut10", EShortcut::BATTLE_SPELL_SHORTCUT_10 }, {"battleSpellShortcut11", EShortcut::BATTLE_SPELL_SHORTCUT_11 }, - {"battleSpellShortcut12", EShortcut::BATTLE_SPELL_SHORTCUT_12 }, - {"battleSpellShortcut13", EShortcut::BATTLE_SPELL_SHORTCUT_13 }, - {"battleSpellShortcut14", EShortcut::BATTLE_SPELL_SHORTCUT_14 }, - {"battleSpellShortcut15", EShortcut::BATTLE_SPELL_SHORTCUT_15 }, {"spectateTrackHero", EShortcut::SPECTATE_TRACK_HERO }, {"spectateSkipBattle", EShortcut::SPECTATE_SKIP_BATTLE }, {"spectateSkipBattleResult", EShortcut::SPECTATE_SKIP_BATTLE_RESULT }, diff --git a/config/shortcutsConfig.json b/config/shortcutsConfig.json index caf62161d..fddef65fe 100644 --- a/config/shortcutsConfig.json +++ b/config/shortcutsConfig.json @@ -73,14 +73,10 @@ "battleSpellShortcut5": "6", "battleSpellShortcut6": "7", "battleSpellShortcut7": "8", - "battleSpellShortcut8": "Shift+1", - "battleSpellShortcut9": "Shift+2", - "battleSpellShortcut10": "Shift+3", - "battleSpellShortcut11": "Shift+4", - "battleSpellShortcut12": "Shift+5", - "battleSpellShortcut13": "Shift+6", - "battleSpellShortcut14": "Shift+7", - "battleSpellShortcut15": "Shift+8", + "battleSpellShortcut8": "9", + "battleSpellShortcut9": "0", + "battleSpellShortcut10": "N", + "battleSpellShortcut11": "M", "battleSurrender": "S", "battleTacticsEnd": [ "Return", "Keypad Enter"], "battleTacticsNext": "Space", From 37dc2a38e8df63887d7a5128f7e744f0eb9fdd40 Mon Sep 17 00:00:00 2001 From: Andrii Danylchenko Date: Thu, 18 Jul 2024 13:37:18 +0300 Subject: [PATCH 068/100] NKAI: reduce double army loss cases --- AI/Nullkiller/Goals/ExecuteHeroChain.cpp | 9 ++++ AI/Nullkiller/Pathfinding/AINodeStorage.cpp | 22 +++++++++ AI/Nullkiller/Pathfinding/GraphPaths.cpp | 52 +++++++++++++++------ AI/Nullkiller/Pathfinding/GraphPaths.h | 2 +- client/ClientCommandManager.cpp | 4 +- 5 files changed, 73 insertions(+), 16 deletions(-) diff --git a/AI/Nullkiller/Goals/ExecuteHeroChain.cpp b/AI/Nullkiller/Goals/ExecuteHeroChain.cpp index d69b529c7..95b5ca725 100644 --- a/AI/Nullkiller/Goals/ExecuteHeroChain.cpp +++ b/AI/Nullkiller/Goals/ExecuteHeroChain.cpp @@ -26,7 +26,12 @@ ExecuteHeroChain::ExecuteHeroChain(const AIPath & path, const CGObjectInstance * if(obj) { objid = obj->id.getNum(); + +#if NKAI_TRACE_LEVEL >= 1 + targetName = obj->getObjectName() + tile.toString(); +#else targetName = obj->typeName + tile.toString(); +#endif } else { @@ -260,7 +265,11 @@ void ExecuteHeroChain::accept(AIGateway * ai) std::string ExecuteHeroChain::toString() const { +#if NKAI_TRACE_LEVEL >= 1 + return "ExecuteHeroChain " + targetName + " by " + chainPath.toString(); +#else return "ExecuteHeroChain " + targetName + " by " + chainPath.targetHero->getNameTranslated(); +#endif } bool ExecuteHeroChain::moveHeroToTile(AIGateway * ai, const CGHeroInstance * hero, const int3 & tile) diff --git a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp index da7abceba..b4fb42cf2 100644 --- a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp +++ b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp @@ -1385,6 +1385,28 @@ void AINodeStorage::calculateChainInfo(std::vector & paths, const int3 & path.armyLoss = node.armyLoss; path.targetObjectDanger = evaluateDanger(pos, path.targetHero, !node.actor->allowBattle); + if(path.targetObjectDanger > 0) + { + if(node.theNodeBefore) + { + auto prevNode = getAINode(node.theNodeBefore); + + if(node.coord == prevNode->coord && node.actor->hero == prevNode->actor->hero) + { + paths.pop_back(); + continue; + } + else + { + path.armyLoss = prevNode->armyLoss; + } + } + else + { + path.armyLoss = 0; + } + } + path.targetObjectArmyLoss = evaluateArmyLoss( path.targetHero, getHeroArmyStrengthWithCommander(path.targetHero, path.heroArmy), diff --git a/AI/Nullkiller/Pathfinding/GraphPaths.cpp b/AI/Nullkiller/Pathfinding/GraphPaths.cpp index 150d37c1a..a3dba0696 100644 --- a/AI/Nullkiller/Pathfinding/GraphPaths.cpp +++ b/AI/Nullkiller/Pathfinding/GraphPaths.cpp @@ -160,7 +160,7 @@ void GraphPaths::dumpToLog() const node.previous.coord.toString(), tile.first.toString(), node.cost, - node.danger); + node.linkDanger); } logBuilder.addLine(node.previous.coord, tile.first); @@ -169,14 +169,17 @@ void GraphPaths::dumpToLog() const }); } -bool GraphPathNode::tryUpdate(const GraphPathNodePointer & pos, const GraphPathNode & prev, const ObjectLink & link) +bool GraphPathNode::tryUpdate( + const GraphPathNodePointer & pos, + const GraphPathNode & prev, + const ObjectLink & link) { auto newCost = prev.cost + link.cost; if(newCost < cost) { previous = pos; - danger = prev.danger + link.danger; + linkDanger = link.danger; cost = newCost; return true; @@ -199,7 +202,7 @@ void GraphPaths::addChainInfo(std::vector & paths, int3 tile, const CGHe std::vector tilesToPass; - uint64_t danger = node.danger; + uint64_t danger = node.linkDanger; float cost = node.cost; bool allowBattle = false; @@ -212,13 +215,13 @@ void GraphPaths::addChainInfo(std::vector & paths, int3 tile, const CGHe if(currentTile == pathNodes.end()) break; - auto currentNode = currentTile->second[current.nodeType]; + auto & currentNode = currentTile->second[current.nodeType]; if(!currentNode.previous.valid()) break; allowBattle = allowBattle || currentNode.nodeType == GrapthPathNodeType::BATTLE; - vstd::amax(danger, currentNode.danger); + vstd::amax(danger, currentNode.linkDanger); vstd::amax(cost, currentNode.cost); tilesToPass.push_back(current); @@ -239,9 +242,13 @@ void GraphPaths::addChainInfo(std::vector & paths, int3 tile, const CGHe if(path.targetHero != hero) continue; - for(auto graphTile = tilesToPass.rbegin(); graphTile != tilesToPass.rend(); graphTile++) + uint64_t loss = 0; + uint64_t strength = getHeroArmyStrengthWithCommander(path.targetHero, path.heroArmy); + + for(auto graphTile = ++tilesToPass.rbegin(); graphTile != tilesToPass.rend(); graphTile++) { AIPathNodeInfo n; + auto & node = getNode(*graphTile); n.coord = graphTile->coord; n.cost = cost; @@ -249,7 +256,21 @@ void GraphPaths::addChainInfo(std::vector & paths, int3 tile, const CGHe n.danger = danger; n.targetHero = hero; n.parentIndex = -1; - n.specialAction = getNode(*graphTile).specialAction; + n.specialAction = node.specialAction; + + if(node.linkDanger > 0) + { + auto additionalLoss = ai->pathfinder->getStorage()->evaluateArmyLoss(path.targetHero, strength, node.linkDanger); + loss += additionalLoss; + + if(strength > additionalLoss) + strength -= additionalLoss; + else + { + strength = 0; + break; + } + } if(n.specialAction) { @@ -264,7 +285,12 @@ void GraphPaths::addChainInfo(std::vector & paths, int3 tile, const CGHe path.nodes.insert(path.nodes.begin(), n); } - path.armyLoss += ai->pathfinder->getStorage()->evaluateArmyLoss(path.targetHero, path.heroArmy->getArmyStrength(), danger); + if(strength == 0) + { + continue; + } + + path.armyLoss += loss; path.targetObjectDanger = ai->pathfinder->getStorage()->evaluateDanger(tile, path.targetHero, !allowBattle); path.targetObjectArmyLoss = ai->pathfinder->getStorage()->evaluateArmyLoss(path.targetHero, path.heroArmy->getArmyStrength(), path.targetObjectDanger); @@ -287,7 +313,7 @@ void GraphPaths::quickAddChainInfoWithBlocker(std::vector & paths, int3 std::vector tilesToPass; - uint64_t danger = targetNode.danger; + uint64_t danger = targetNode.linkDanger; float cost = targetNode.cost; bool allowBattle = false; @@ -303,7 +329,7 @@ void GraphPaths::quickAddChainInfoWithBlocker(std::vector & paths, int3 auto currentNode = currentTile->second[current.nodeType]; allowBattle = allowBattle || currentNode.nodeType == GrapthPathNodeType::BATTLE; - vstd::amax(danger, currentNode.danger); + vstd::amax(danger, currentNode.linkDanger); vstd::amax(cost, currentNode.cost); tilesToPass.push_back(current); @@ -341,7 +367,7 @@ void GraphPaths::quickAddChainInfoWithBlocker(std::vector & paths, int3 // final node n.coord = tile; n.cost = targetNode.cost; - n.danger = targetNode.danger; + n.danger = danger; n.parentIndex = path.nodes.size(); path.nodes.push_back(n); @@ -368,7 +394,7 @@ void GraphPaths::quickAddChainInfoWithBlocker(std::vector & paths, int3 n.coord = graphTile->coord; n.cost = node.cost; n.turns = static_cast(node.cost); - n.danger = node.danger; + n.danger = danger; n.specialAction = node.specialAction; n.parentIndex = path.nodes.size(); diff --git a/AI/Nullkiller/Pathfinding/GraphPaths.h b/AI/Nullkiller/Pathfinding/GraphPaths.h index 83167eabe..a41781f68 100644 --- a/AI/Nullkiller/Pathfinding/GraphPaths.h +++ b/AI/Nullkiller/Pathfinding/GraphPaths.h @@ -67,7 +67,7 @@ struct GraphPathNode GrapthPathNodeType nodeType = GrapthPathNodeType::NORMAL; GraphPathNodePointer previous; float cost = BAD_COST; - uint64_t danger = 0; + uint64_t linkDanger = 0; const CGObjectInstance * obj = nullptr; std::shared_ptr specialAction; diff --git a/client/ClientCommandManager.cpp b/client/ClientCommandManager.cpp index 3119c7f68..b260ff4fb 100644 --- a/client/ClientCommandManager.cpp +++ b/client/ClientCommandManager.cpp @@ -89,7 +89,7 @@ void ClientCommandManager::handleGoSoloCommand() // unlikely it will work but just in case to be consistent for(auto & color : CSH->getAllClientPlayers(CSH->logicConnection->connectionID)) { - if(CSH->client->getStartInfo()->playerInfos.at(color).isControlledByHuman()) + if(color.isValidPlayer() && CSH->client->getStartInfo()->playerInfos.at(color).isControlledByHuman()) { CSH->client->installNewPlayerInterface(std::make_shared(color), color); } @@ -102,7 +102,7 @@ void ClientCommandManager::handleGoSoloCommand() for(auto & color : CSH->getAllClientPlayers(CSH->logicConnection->connectionID)) { - if(CSH->client->getStartInfo()->playerInfos.at(color).isControlledByHuman()) + if(color.isValidPlayer() && CSH->client->getStartInfo()->playerInfos.at(color).isControlledByHuman()) { auto AiToGive = CSH->client->aiNameForPlayer(*CSH->client->getPlayerSettings(color), false, false); printCommandMessage("Player " + color.toString() + " will be lead by " + AiToGive, ELogLevel::INFO); From 6599db44611ab28104b3a9637c33ce90ad3d1321 Mon Sep 17 00:00:00 2001 From: Alexander Wilms Date: Thu, 18 Jul 2024 17:29:46 +0200 Subject: [PATCH 069/100] Less noise when switching languages in launcher * Check if .qm file exists * Don't call installTranslator() for English Fixes #4277 --- launcher/mainwindow_moc.cpp | 26 +++++++++++++++++++++----- mapeditor/mainwindow.cpp | 26 +++++++++++++++++++++----- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/launcher/mainwindow_moc.cpp b/launcher/mainwindow_moc.cpp index 2c6292c5b..574355853 100644 --- a/launcher/mainwindow_moc.cpp +++ b/launcher/mainwindow_moc.cpp @@ -236,16 +236,32 @@ void MainWindow::on_aboutButton_clicked() void MainWindow::updateTranslation() { #ifdef ENABLE_QT_TRANSLATIONS - const std::string translationFile = settings["general"]["language"].String() + ".qm"; - logGlobal->info("Loading translation '%s'", translationFile); + const std::string translationFile = settings["general"]["language"].String()+ ".qm"; + QString translationFileResourcePath = QString{":/translation/%1"}.arg(translationFile.c_str()); - if (!translator.load(QString{":/translation/%1"}.arg(translationFile.c_str()))) + logGlobal->info("Loading translation %s", translationFile); + + if(!QFile::exists(translationFileResourcePath)) { - logGlobal->error("Failed to load translation"); + logGlobal->warn("Translation file %s does not exist", translationFileResourcePath.toStdString()); + return; + } + + if (!translator.load(translationFileResourcePath)) + { + logGlobal->error("Failed to load translation file %s", translationFileResourcePath.toStdString()); + return; + } + + if(translationFile == "english.qm") + { + // translator doesn't need to be installed for English return; } if (!qApp->installTranslator(&translator)) - logGlobal->error("Failed to install translator"); + { + logGlobal->error("Failed to install translator for translation file %s", translationFileResourcePath.toStdString()); + } #endif } diff --git a/mapeditor/mainwindow.cpp b/mapeditor/mainwindow.cpp index a0084b1ef..739a2068e 100644 --- a/mapeditor/mainwindow.cpp +++ b/mapeditor/mainwindow.cpp @@ -133,17 +133,33 @@ void MainWindow::parseCommandLine(ExtractionOptions & extractionOptions) void MainWindow::loadTranslation() { #ifdef ENABLE_QT_TRANSLATIONS - const std::string translationFile = settings["general"]["language"].String() + ".qm"; - logGlobal->info("Loading translation '%s'", translationFile); + const std::string translationFile = settings["general"]["language"].String()+ ".qm"; + QString translationFileResourcePath = QString{":/translation/%1"}.arg(translationFile.c_str()); - if (!translator.load(QString{":/translation/%1"}.arg(translationFile.c_str()))) + logGlobal->info("Loading translation %s", translationFile); + + if(!QFile::exists(translationFileResourcePath)) { - logGlobal->error("Failed to load translation"); + logGlobal->warn("Translation file %s does not exist", translationFileResourcePath.toStdString()); + return; + } + + if (!translator.load(translationFileResourcePath)) + { + logGlobal->error("Failed to load translation file %s", translationFileResourcePath.toStdString()); + return; + } + + if(translationFile == "english.qm") + { + // translator doesn't need to be installed for English return; } if (!qApp->installTranslator(&translator)) - logGlobal->error("Failed to install translator"); + { + logGlobal->error("Failed to install translator for translation file %s", translationFileResourcePath.toStdString()); + } #endif } From 187b7b05177fca6c5d45170b35cbcef4a33d2e5a Mon Sep 17 00:00:00 2001 From: Alexander Wilms Date: Thu, 18 Jul 2024 20:32:17 +0200 Subject: [PATCH 070/100] Show debug message instead of warning if no .qm file for selected language exists --- launcher/mainwindow_moc.cpp | 2 +- mapeditor/mainwindow.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/mainwindow_moc.cpp b/launcher/mainwindow_moc.cpp index 574355853..88b86be46 100644 --- a/launcher/mainwindow_moc.cpp +++ b/launcher/mainwindow_moc.cpp @@ -243,7 +243,7 @@ void MainWindow::updateTranslation() if(!QFile::exists(translationFileResourcePath)) { - logGlobal->warn("Translation file %s does not exist", translationFileResourcePath.toStdString()); + logGlobal->debug("Translation file %s does not exist", translationFileResourcePath.toStdString()); return; } diff --git a/mapeditor/mainwindow.cpp b/mapeditor/mainwindow.cpp index 739a2068e..98ef59470 100644 --- a/mapeditor/mainwindow.cpp +++ b/mapeditor/mainwindow.cpp @@ -140,7 +140,7 @@ void MainWindow::loadTranslation() if(!QFile::exists(translationFileResourcePath)) { - logGlobal->warn("Translation file %s does not exist", translationFileResourcePath.toStdString()); + logGlobal->debug("Translation file %s does not exist", translationFileResourcePath.toStdString()); return; } From 4f06aed631f191bfba5d4787b5c4cbcad01d85fc Mon Sep 17 00:00:00 2001 From: Alexander Wilms Date: Thu, 18 Jul 2024 22:28:47 +0200 Subject: [PATCH 071/100] CModListView::loadScreenshots(): Ensure a mod is selected in allModsView Fixes #989 Fixes #3811 --- launcher/modManager/cmodlistview_moc.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/launcher/modManager/cmodlistview_moc.cpp b/launcher/modManager/cmodlistview_moc.cpp index 96615ad5d..8f6aef347 100644 --- a/launcher/modManager/cmodlistview_moc.cpp +++ b/launcher/modManager/cmodlistview_moc.cpp @@ -962,6 +962,12 @@ void CModListView::loadScreenshots() { if(ui->tabWidget->currentIndex() == 2) { + if(ui->allModsView->currentIndex().row() == -1 || ui->allModsView->currentIndex().column() == -1) + { + // select the first mod, so we can access its data + ui->allModsView->setCurrentIndex(filterModel->index(0, 0)); + } + ui->screenshotsList->clear(); QString modName = ui->allModsView->currentIndex().data(ModRoles::ModNameRole).toString(); assert(modModel->hasMod(modName)); //should be filtered out by check above From 56f17252349a28a01334af44f34759a2fecff774 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 22 May 2024 11:28:13 +0000 Subject: [PATCH 072/100] All images are now loaded via RenderHandler class --- client/mapView/MapRenderer.cpp | 11 ++- client/render/CAnimation.cpp | 5 +- client/render/CDefFile.cpp | 48 +------------ client/render/Graphics.cpp | 49 ------------- client/render/Graphics.h | 10 --- client/render/IRenderHandler.h | 2 + client/renderSDL/RenderHandler.cpp | 108 ++++++++++++++++++++++++++++- client/renderSDL/RenderHandler.h | 27 ++++++++ client/widgets/CGarrisonInt.cpp | 5 +- client/widgets/Images.cpp | 3 +- client/widgets/MiscWidgets.cpp | 3 +- client/windows/CHeroOverview.cpp | 1 - 12 files changed, 147 insertions(+), 125 deletions(-) diff --git a/client/mapView/MapRenderer.cpp b/client/mapView/MapRenderer.cpp index b541b30e3..8c7509f80 100644 --- a/client/mapView/MapRenderer.cpp +++ b/client/mapView/MapRenderer.cpp @@ -115,14 +115,11 @@ void MapTileStorage::load(size_t index, const AnimationPath & filename, EImageBl entry->getImage(i)->setBlitMode(blitMode); } - for(size_t i = 0; i < terrainAnimations[0]->size(); ++i) - { - terrainAnimations[1]->getImage(i)->verticalFlip(); - terrainAnimations[3]->getImage(i)->verticalFlip(); + terrainAnimations[1]->verticalFlip(); + terrainAnimations[3]->verticalFlip(); - terrainAnimations[2]->getImage(i)->horizontalFlip(); - terrainAnimations[3]->getImage(i)->horizontalFlip(); - } + terrainAnimations[2]->horizontalFlip(); + terrainAnimations[3]->horizontalFlip(); } std::shared_ptr MapTileStorage::find(size_t fileIndex, size_t rotationIndex, size_t imageIndex) diff --git a/client/render/CAnimation.cpp b/client/render/CAnimation.cpp index 9feaf51a4..39e373eb9 100644 --- a/client/render/CAnimation.cpp +++ b/client/render/CAnimation.cpp @@ -12,7 +12,6 @@ #include "CDefFile.h" -#include "Graphics.h" #include "../../lib/filesystem/Filesystem.h" #include "../../lib/json/JsonUtils.h" #include "../renderSDL/SDLImage.h" @@ -179,8 +178,8 @@ void CAnimation::init() source[defEntry.first].resize(defEntry.second); } - if (vstd::contains(graphics->imageLists, name.getName())) - initFromJson(graphics->imageLists[name.getName()]); +// if (vstd::contains(graphics->imageLists, name.getName())) +// initFromJson(graphics->imageLists[name.getName()]); auto jsonResource = name.toType(); auto configList = CResourceHandler::get()->getResourcesWithName(jsonResource); diff --git a/client/render/CDefFile.cpp b/client/render/CDefFile.cpp index 23377c4e5..1c8670a09 100644 --- a/client/render/CDefFile.cpp +++ b/client/render/CDefFile.cpp @@ -18,50 +18,6 @@ #include -// Extremely simple file cache. TODO: smarter, more general solution -class CFileCache -{ - static const int cacheSize = 50; //Max number of cached files - struct FileData - { - AnimationPath name; - size_t size; - std::unique_ptr data; - - std::unique_ptr getCopy() - { - auto ret = std::unique_ptr(new ui8[size]); - std::copy(data.get(), data.get() + size, ret.get()); - return ret; - } - FileData(AnimationPath name_, size_t size_, std::unique_ptr data_): - name{std::move(name_)}, - size{size_}, - data{std::move(data_)} - {} - }; - - std::deque cache; -public: - std::unique_ptr getCachedFile(AnimationPath rid) - { - for(auto & file : cache) - { - if (file.name == rid) - return file.getCopy(); - } - // Still here? Cache miss - if (cache.size() > cacheSize) - cache.pop_front(); - - auto data = CResourceHandler::get()->load(rid)->readAll(); - - cache.emplace_back(std::move(rid), data.second, std::move(data.first)); - - return cache.back().getCopy(); - } -}; - enum class DefType : uint32_t { SPELL = 0x40, @@ -76,8 +32,6 @@ enum class DefType : uint32_t BATTLE_HERO = 0x49 }; -static CFileCache animationCache; - /************************************************************************* * DefFile, class used for def loading * *************************************************************************/ @@ -124,7 +78,7 @@ CDefFile::CDefFile(const AnimationPath & Name): {0, 0, 0, 64 } // shadow border below selection ( used in battle def's ) }; - data = animationCache.getCachedFile(Name); + data = CResourceHandler::get()->load(Name)->readAll().first; palette = std::unique_ptr(new SDL_Color[256]); int it = 0; diff --git a/client/render/Graphics.cpp b/client/render/Graphics.cpp index ad8f90d92..8399ff698 100644 --- a/client/render/Graphics.cpp +++ b/client/render/Graphics.cpp @@ -131,7 +131,6 @@ Graphics::Graphics() loadPaletteAndColors(); initializeBattleGraphics(); loadErmuToPicture(); - initializeImageLists(); //(!) do not load any CAnimation here } @@ -244,51 +243,3 @@ void Graphics::loadErmuToPicture() } assert (etp_idx == 44); } - -void Graphics::addImageListEntry(size_t index, size_t group, const std::string & listName, const std::string & imageName) -{ - if (!imageName.empty()) - { - JsonNode entry; - if (group != 0) - entry["group"].Integer() = group; - entry["frame"].Integer() = index; - entry["file"].String() = imageName; - - imageLists["SPRITES/" + listName]["images"].Vector().push_back(entry); - } -} - -void Graphics::addImageListEntries(const EntityService * service) -{ - auto cb = std::bind(&Graphics::addImageListEntry, this, _1, _2, _3, _4); - - auto loopCb = [&](const Entity * entity, bool & stop) - { - entity->registerIcons(cb); - }; - - service->forEachBase(loopCb); -} - -void Graphics::initializeImageLists() -{ - addImageListEntries(CGI->creatures()); - addImageListEntries(CGI->heroTypes()); - addImageListEntries(CGI->artifacts()); - addImageListEntries(CGI->factions()); - addImageListEntries(CGI->spells()); - addImageListEntries(CGI->skills()); -} - -std::shared_ptr Graphics::getAnimation(const AnimationPath & path) -{ - if (cachedAnimations.count(path) != 0) - return cachedAnimations.at(path); - - auto newAnimation = GH.renderHandler().loadAnimation(path); - - newAnimation->preload(); - cachedAnimations[path] = newAnimation; - return newAnimation; -} diff --git a/client/render/Graphics.h b/client/render/Graphics.h index 4170f8e1a..845b14e3b 100644 --- a/client/render/Graphics.h +++ b/client/render/Graphics.h @@ -34,20 +34,12 @@ class IFont; /// Handles fonts, hero images, town images, various graphics class Graphics { - void addImageListEntry(size_t index, size_t group, const std::string & listName, const std::string & imageName); - void addImageListEntries(const EntityService * service); - void initializeBattleGraphics(); void loadPaletteAndColors(); void loadErmuToPicture(); void loadFonts(); - void initializeImageLists(); - - std::map> cachedAnimations; public: - std::shared_ptr getAnimation(const AnimationPath & path); - //Fonts static const int FONTS_NUMBER = 9; std::array< std::shared_ptr, FONTS_NUMBER> fonts; @@ -61,8 +53,6 @@ public: PlayerPalette neutralColorPalette; ColorRGBA neutralColor; - std::map imageLists; - //towns std::map ERMUtoPicture[GameConstants::F_NUMBER]; //maps building ID to it's picture's name for each town type //for battles diff --git a/client/render/IRenderHandler.h b/client/render/IRenderHandler.h index 880295170..551c61ea2 100644 --- a/client/render/IRenderHandler.h +++ b/client/render/IRenderHandler.h @@ -26,6 +26,8 @@ public: virtual std::shared_ptr loadImage(const ImagePath & path) = 0; virtual std::shared_ptr loadImage(const ImagePath & path, EImageBlitMode mode) = 0; + virtual std::shared_ptr loadImage(const AnimationPath & path, int frame, int group) = 0; + /// temporary compatibility method. Creates IImage from existing SDL_Surface /// Surface will be shared, caller must still free it with SDL_FreeSurface virtual std::shared_ptr createImage(SDL_Surface * source) = 0; diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index 87eb99091..98a3e680a 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -10,9 +10,66 @@ #include "StdInc.h" #include "RenderHandler.h" -#include "../render/CAnimation.h" #include "SDLImage.h" +#include "../render/CAnimation.h" +#include "../render/CDefFile.h" + +#include "../../lib/json/JsonNode.h" + +std::shared_ptr RenderHandler::getAnimationFile(const AnimationPath & path) +{ + auto it = animationFiles.find(path); + + if (it != animationFiles.end()) + return it->second; + + auto result = std::make_shared(path); + + animationFiles[path] = result; + return result; +} + +std::shared_ptr RenderHandler::getJsonFile(const JsonPath & path) +{ + auto it = jsonFiles.find(path); + + if (it != jsonFiles.end()) + return it->second; + + auto result = std::make_shared(path); + + jsonFiles[path] = result; + return result; +} + +std::shared_ptr RenderHandler::loadImage(const AnimationPath & path, int frame, int group) +{ + AnimationLocator locator{path, frame, group}; + + auto it = animationFrames.find(locator); + if (it != animationFrames.end()) + return it->second; + + auto defFile = getAnimationFile(path); + auto result = std::make_shared(defFile.get(), frame, group); + + animationFrames[locator] = result; + return result; +} + +//std::vector> RenderHandler::loadImageGroup(const AnimationPath & path, int group) +//{ +// const auto defFile = getAnimationFile(path); +// +// size_t groupSize = defFile->getEntries().at(group); +// +// std::vector> result; +// for (size_t i = 0; i < groupSize; ++i) +// loadImage(path, i, group); +// +// return result; +//} std::shared_ptr RenderHandler::loadImage(const ImagePath & path) { @@ -38,3 +95,52 @@ std::shared_ptr RenderHandler::createAnimation() { return std::make_shared(); } + +// +//void Graphics::addImageListEntry(size_t index, size_t group, const std::string & listName, const std::string & imageName) +//{ +// if (!imageName.empty()) +// { +// JsonNode entry; +// if (group != 0) +// entry["group"].Integer() = group; +// entry["frame"].Integer() = index; +// entry["file"].String() = imageName; +// +// imageLists["SPRITES/" + listName]["images"].Vector().push_back(entry); +// } +//} +// +//void Graphics::addImageListEntries(const EntityService * service) +//{ +// auto cb = std::bind(&Graphics::addImageListEntry, this, _1, _2, _3, _4); +// +// auto loopCb = [&](const Entity * entity, bool & stop) +// { +// entity->registerIcons(cb); +// }; +// +// service->forEachBase(loopCb); +//} +// +//void Graphics::initializeImageLists() +//{ +// addImageListEntries(CGI->creatures()); +// addImageListEntries(CGI->heroTypes()); +// addImageListEntries(CGI->artifacts()); +// addImageListEntries(CGI->factions()); +// addImageListEntries(CGI->spells()); +// addImageListEntries(CGI->skills()); +//} +// +//std::shared_ptr Graphics::getAnimation(const AnimationPath & path) +//{ +// if (cachedAnimations.count(path) != 0) +// return cachedAnimations.at(path); +// +// auto newAnimation = GH.renderHandler().loadAnimation(path); +// +// newAnimation->preload(); +// cachedAnimations[path] = newAnimation; +// return newAnimation; +//} diff --git a/client/renderSDL/RenderHandler.h b/client/renderSDL/RenderHandler.h index 76e30ee9c..1e6e7cc95 100644 --- a/client/renderSDL/RenderHandler.h +++ b/client/renderSDL/RenderHandler.h @@ -11,12 +11,39 @@ #include "../render/IRenderHandler.h" +class CDefFile; + class RenderHandler : public IRenderHandler { + struct AnimationLocator + { + AnimationPath animation; + int frame = -1; + int group = -1; + + bool operator < (const AnimationLocator & other) const + { + if (animation != other.animation) + return animation < other.animation; + if (group != other.group) + return group < other.group; + return frame < other.frame; + } + }; + + std::map> animationFiles; + std::map> jsonFiles; + std::map> imageFiles; + std::map> animationFrames; + + std::shared_ptr getAnimationFile(const AnimationPath & path); + std::shared_ptr getJsonFile(const JsonPath & path); public: std::shared_ptr loadImage(const ImagePath & path) override; std::shared_ptr loadImage(const ImagePath & path, EImageBlitMode mode) override; + std::shared_ptr loadImage(const AnimationPath & path, int frame, int group) override; + std::shared_ptr createImage(SDL_Surface * source) override; std::shared_ptr loadAnimation(const AnimationPath & path) override; diff --git a/client/widgets/CGarrisonInt.cpp b/client/widgets/CGarrisonInt.cpp index 6acf43b61..667cfdc22 100644 --- a/client/widgets/CGarrisonInt.cpp +++ b/client/widgets/CGarrisonInt.cpp @@ -17,7 +17,6 @@ #include "../gui/CGuiHandler.h" #include "../gui/WindowHandler.h" #include "../render/IImage.h" -#include "../render/Graphics.h" #include "../windows/CCreatureWindow.h" #include "../windows/CWindowWithArtifacts.h" #include "../windows/GUIClasses.h" @@ -437,10 +436,10 @@ CGarrisonSlot::CGarrisonSlot(CGarrisonInt * Owner, int x, int y, SlotID IID, EGa AnimationPath imgName = AnimationPath::builtin(owner->smallIcons ? "cprsmall" : "TWCRPORT"); - creatureImage = std::make_shared(graphics->getAnimation(imgName), 0); + creatureImage = std::make_shared(imgName, 0); creatureImage->disable(); - selectionImage = std::make_shared(graphics->getAnimation(imgName), 1); + selectionImage = std::make_shared(imgName, 1); selectionImage->disable(); selectionImage->center(creatureImage->pos.center()); diff --git a/client/widgets/Images.cpp b/client/widgets/Images.cpp index 58f50dcda..66aa98c3c 100644 --- a/client/widgets/Images.cpp +++ b/client/widgets/Images.cpp @@ -19,7 +19,6 @@ #include "../render/CAnimation.h" #include "../render/Canvas.h" #include "../render/ColorFilter.h" -#include "../render/Graphics.h" #include "../battle/BattleInterface.h" #include "../battle/BattleInterfaceClasses.h" @@ -177,7 +176,7 @@ CAnimImage::CAnimImage(const AnimationPath & name, size_t Frame, size_t Group, i { pos.x += x; pos.y += y; - anim = graphics->getAnimation(name); + anim = GH.renderHandler().loadAnimation(name); init(); } diff --git a/client/widgets/MiscWidgets.cpp b/client/widgets/MiscWidgets.cpp index 1bdba2a75..d6d53038b 100644 --- a/client/widgets/MiscWidgets.cpp +++ b/client/widgets/MiscWidgets.cpp @@ -28,7 +28,6 @@ #include "../windows/CCastleInterface.h" #include "../windows/InfoWindows.h" #include "../render/Canvas.h" -#include "../render/Graphics.h" #include "../../CCallback.h" @@ -535,7 +534,7 @@ CreatureTooltip::CreatureTooltip(Point pos, const CGCreature * creature) auto creatureID = creature->getCreature(); int32_t creatureIconIndex = CGI->creatures()->getById(creatureID)->getIconIndex(); - creatureImage = std::make_shared(graphics->getAnimation(AnimationPath::builtin("TWCRPORT")), creatureIconIndex); + creatureImage = std::make_shared(AnimationPath::builtin("TWCRPORT"), creatureIconIndex); creatureImage->center(Point(parent->pos.x + parent->pos.w / 2, parent->pos.y + creatureImage->pos.h / 2 + 11)); bool isHeroSelected = LOCPLINT->localState->getCurrentHero() != nullptr; diff --git a/client/windows/CHeroOverview.cpp b/client/windows/CHeroOverview.cpp index 390d94674..81071598f 100644 --- a/client/windows/CHeroOverview.cpp +++ b/client/windows/CHeroOverview.cpp @@ -14,7 +14,6 @@ #include "../gui/CGuiHandler.h" #include "../render/Canvas.h" #include "../render/Colors.h" -#include "../render/Graphics.h" #include "../render/IImage.h" #include "../renderSDL/RenderHandler.h" #include "../widgets/CComponent.h" From a1fb3b8b01cb0446b60bad9aa492d6219c9e272c Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sun, 2 Jun 2024 15:43:56 +0000 Subject: [PATCH 073/100] Reduce direct access to CAnimation, removed various caching schemes --- client/adventureMap/AdventureMapInterface.h | 1 - client/adventureMap/AdventureMapWidget.cpp | 33 ++--------------- client/adventureMap/AdventureMapWidget.h | 10 +----- client/battle/BattleAnimationClasses.cpp | 1 + client/battle/BattleInterface.h | 1 - client/battle/BattleObstacleController.cpp | 1 + client/battle/BattleProjectileController.cpp | 1 + client/battle/CreatureAnimation.cpp | 1 + client/battle/CreatureAnimation.h | 1 - client/lobby/CSelectionBase.cpp | 11 +++--- client/lobby/CSelectionBase.h | 3 +- client/lobby/SelectionTab.h | 1 + client/mainmenu/CMainMenu.h | 1 - client/render/Graphics.h | 1 - client/renderSDL/CursorHardware.h | 1 - client/renderSDL/CursorSoftware.h | 1 - client/widgets/Buttons.cpp | 1 - client/widgets/Images.cpp | 14 ++++---- client/widgets/Images.h | 4 +-- client/widgets/ObjectLists.h | 1 - client/windows/CExchangeWindow.cpp | 16 +++------ client/windows/CHeroOverview.cpp | 4 +-- client/windows/CHeroWindow.cpp | 15 ++++---- client/windows/CMapOverview.cpp | 1 - client/windows/CSpellWindow.cpp | 24 ++++++------- client/windows/CSpellWindow.h | 4 --- client/windows/GUIClasses.cpp | 37 ++++++-------------- client/windows/GUIClasses.h | 7 ++-- 28 files changed, 59 insertions(+), 138 deletions(-) diff --git a/client/adventureMap/AdventureMapInterface.h b/client/adventureMap/AdventureMapInterface.h index b0509a77c..32ff1df89 100644 --- a/client/adventureMap/AdventureMapInterface.h +++ b/client/adventureMap/AdventureMapInterface.h @@ -31,7 +31,6 @@ class CAnimImage; class CGStatusBar; class AdventureMapWidget; class AdventureMapShortcuts; -class CAnimation; class MapView; class CResDataBar; class CHeroList; diff --git a/client/adventureMap/AdventureMapWidget.cpp b/client/adventureMap/AdventureMapWidget.cpp index 7333f1d37..32372c514 100644 --- a/client/adventureMap/AdventureMapWidget.cpp +++ b/client/adventureMap/AdventureMapWidget.cpp @@ -20,7 +20,6 @@ #include "../gui/CGuiHandler.h" #include "../gui/Shortcut.h" #include "../mapView/MapView.h" -#include "../render/CAnimation.h" #include "../render/IImage.h" #include "../render/IRenderHandler.h" #include "../widgets/Buttons.h" @@ -125,26 +124,6 @@ Rect AdventureMapWidget::readArea(const JsonNode & source, const Rect & bounding return Rect(topLeft + boundingBox.topLeft(), dimensions); } -std::shared_ptr AdventureMapWidget::loadImage(const JsonNode & name) -{ - ImagePath resource = ImagePath::fromJson(name); - - if(images.count(resource) == 0) - images[resource] = GH.renderHandler().loadImage(resource); - - return images[resource]; -} - -std::shared_ptr AdventureMapWidget::loadAnimation(const JsonNode & name) -{ - AnimationPath resource = AnimationPath::fromJson(name); - - if(animations.count(resource) == 0) - animations[resource] = GH.renderHandler().loadAnimation(resource); - - return animations[resource]; -} - std::shared_ptr AdventureMapWidget::buildInfobox(const JsonNode & input) { Rect area = readTargetArea(input["area"]); @@ -157,7 +136,7 @@ std::shared_ptr AdventureMapWidget::buildMapImage(const JsonNode & i Rect targetArea = readTargetArea(input["area"]); Rect sourceArea = readSourceArea(input["sourceArea"], input["area"]); - return std::make_shared(loadImage(input["image"]), targetArea, sourceArea); + return std::make_shared(ImagePath::fromJson(input["image"]), targetArea, sourceArea); } std::shared_ptr AdventureMapWidget::buildMapButton(const JsonNode & input) @@ -257,7 +236,7 @@ std::shared_ptr AdventureMapWidget::buildMapIcon(const JsonNode & in size_t index = input["index"].Integer(); size_t perPlayer = input["perPlayer"].Integer(); - return std::make_shared(area.topLeft(), loadAnimation(input["image"]), index, perPlayer); + return std::make_shared(area.topLeft(), AnimationPath::fromJson(input["image"]), index, perPlayer); } std::shared_ptr AdventureMapWidget::buildMapTownList(const JsonNode & input) @@ -387,16 +366,10 @@ void AdventureMapWidget::setPlayerChildren(CIntObject * widget, const PlayerColo texture->playerColored(player); } - for(const auto & entry : playerColorerImages) - { - if(images.count(entry)) - images[entry]->playerColored(player); - } - redraw(); } -CAdventureMapIcon::CAdventureMapIcon(const Point & position, std::shared_ptr animation, size_t index, size_t iconsPerPlayer) +CAdventureMapIcon::CAdventureMapIcon(const Point & position, const AnimationPath & animation, size_t index, size_t iconsPerPlayer) : index(index) , iconsPerPlayer(iconsPerPlayer) { diff --git a/client/adventureMap/AdventureMapWidget.h b/client/adventureMap/AdventureMapWidget.h index 93a19eeb6..5ec2c34e1 100644 --- a/client/adventureMap/AdventureMapWidget.h +++ b/client/adventureMap/AdventureMapWidget.h @@ -11,7 +11,6 @@ #include "../gui/InterfaceObjectConfigurable.h" -class CAnimation; class CHeroList; class CTownList; class CMinimap; @@ -31,10 +30,6 @@ class AdventureMapWidget : public InterfaceObjectConfigurable /// list of images on which player-colored palette will be applied std::vector playerColorerImages; - /// list of named images shared between widgets - std::map> images; - std::map> animations; - /// Widgets that require access from adventure map std::shared_ptr heroList; std::shared_ptr townList; @@ -48,9 +43,6 @@ class AdventureMapWidget : public InterfaceObjectConfigurable Rect readSourceArea(const JsonNode & source, const JsonNode & sourceCommon); Rect readArea(const JsonNode & source, const Rect & boundingBox); - std::shared_ptr loadImage(const JsonNode & name); - std::shared_ptr loadAnimation(const JsonNode & name); - std::shared_ptr buildInfobox(const JsonNode & input); std::shared_ptr buildMapImage(const JsonNode & input); std::shared_ptr buildMapButton(const JsonNode & input); @@ -104,7 +96,7 @@ class CAdventureMapIcon : public CIntObject size_t index; size_t iconsPerPlayer; public: - CAdventureMapIcon(const Point & position, std::shared_ptr image, size_t index, size_t iconsPerPlayer); + CAdventureMapIcon(const Point & position, const AnimationPath & image, size_t index, size_t iconsPerPlayer); void setPlayer(const PlayerColor & player); }; diff --git a/client/battle/BattleAnimationClasses.cpp b/client/battle/BattleAnimationClasses.cpp index c54a1d202..19271b07c 100644 --- a/client/battle/BattleAnimationClasses.cpp +++ b/client/battle/BattleAnimationClasses.cpp @@ -24,6 +24,7 @@ #include "../gui/CursorHandler.h" #include "../gui/CGuiHandler.h" #include "../media/ISoundPlayer.h" +#include "../render/CAnimation.h" #include "../render/IRenderHandler.h" #include "../../CCallback.h" diff --git a/client/battle/BattleInterface.h b/client/battle/BattleInterface.h index 49e63ebbb..6055cdf5e 100644 --- a/client/battle/BattleInterface.h +++ b/client/battle/BattleInterface.h @@ -39,7 +39,6 @@ class Canvas; class BattleResultWindow; class StackQueue; class CPlayerInterface; -class CAnimation; struct BattleEffect; class IImage; class StackQueue; diff --git a/client/battle/BattleObstacleController.cpp b/client/battle/BattleObstacleController.cpp index 2d7fbdbb7..796e5c7d9 100644 --- a/client/battle/BattleObstacleController.cpp +++ b/client/battle/BattleObstacleController.cpp @@ -21,6 +21,7 @@ #include "../CPlayerInterface.h" #include "../gui/CGuiHandler.h" #include "../media/ISoundPlayer.h" +#include "../render/CAnimation.h" #include "../render/Canvas.h" #include "../render/IRenderHandler.h" diff --git a/client/battle/BattleProjectileController.cpp b/client/battle/BattleProjectileController.cpp index 1a8117baa..6c5854fc0 100644 --- a/client/battle/BattleProjectileController.cpp +++ b/client/battle/BattleProjectileController.cpp @@ -15,6 +15,7 @@ #include "BattleStacksController.h" #include "CreatureAnimation.h" +#include "../render/CAnimation.h" #include "../render/Canvas.h" #include "../render/IRenderHandler.h" #include "../gui/CGuiHandler.h" diff --git a/client/battle/CreatureAnimation.cpp b/client/battle/CreatureAnimation.cpp index 53bca89a4..ae00aa515 100644 --- a/client/battle/CreatureAnimation.cpp +++ b/client/battle/CreatureAnimation.cpp @@ -14,6 +14,7 @@ #include "../../lib/CCreatureHandler.h" #include "../gui/CGuiHandler.h" +#include "../render/CAnimation.h" #include "../render/Canvas.h" #include "../render/ColorFilter.h" #include "../render/IRenderHandler.h" diff --git a/client/battle/CreatureAnimation.h b/client/battle/CreatureAnimation.h index 686e941b5..46d4d87da 100644 --- a/client/battle/CreatureAnimation.h +++ b/client/battle/CreatureAnimation.h @@ -12,7 +12,6 @@ #include "../../lib/FunctionList.h" #include "../../lib/Color.h" #include "../widgets/Images.h" -#include "../render/CAnimation.h" #include "../render/IImage.h" class CIntObject; diff --git a/client/lobby/CSelectionBase.cpp b/client/lobby/CSelectionBase.cpp index b7ea0a076..a330116d7 100644 --- a/client/lobby/CSelectionBase.cpp +++ b/client/lobby/CSelectionBase.cpp @@ -515,9 +515,6 @@ CFlagBox::CFlagBox(const Rect & rect) labelAllies = std::make_shared(0, 0, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[390] + ":"); labelEnemies = std::make_shared(133, 0, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->allTexts[391] + ":"); - - iconsTeamFlags = GH.renderHandler().loadAnimation(AnimationPath::builtin("ITGFLAGS.DEF")); - iconsTeamFlags->preload(); } void CFlagBox::recreate() @@ -529,7 +526,7 @@ void CFlagBox::recreate() const int enemiesX = 5 + 133 + (int)labelEnemies->getWidth(); for(auto i = CSH->si->playerInfos.cbegin(); i != CSH->si->playerInfos.cend(); i++) { - auto flag = std::make_shared(iconsTeamFlags, i->first.getNum(), 0); + auto flag = std::make_shared(AnimationPath::builtin("ITGFLAGS.DEF"), i->first.getNum(), 0); if(i->first == CSH->myFirstColor() || CSH->getPlayerTeamId(i->first) == CSH->getPlayerTeamId(CSH->myFirstColor())) { flag->moveTo(Point(pos.x + alliesX + (int)flagsAllies.size()*flag->pos.w, pos.y)); @@ -546,10 +543,10 @@ void CFlagBox::recreate() void CFlagBox::showPopupWindow(const Point & cursorPosition) { if(SEL->getMapInfo()) - GH.windows().createAndPushWindow(iconsTeamFlags); + GH.windows().createAndPushWindow(); } -CFlagBox::CFlagBoxTooltipBox::CFlagBoxTooltipBox(std::shared_ptr icons) +CFlagBox::CFlagBoxTooltipBox::CFlagBoxTooltipBox() : CWindowObject(BORDERED | RCLICK_POPUP | SHADOW_DISABLED, ImagePath::builtin("DIBOXBCK")) { OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; @@ -577,7 +574,7 @@ CFlagBox::CFlagBoxTooltipBox::CFlagBoxTooltipBox(std::shared_ptr ico int curx = 128 - 9 * team.size(); for(const auto & player : team) { - iconsFlags.push_back(std::make_shared(icons, player, 0, curx, 75 + 50 * curIdx)); + iconsFlags.push_back(std::make_shared(AnimationPath::builtin("ITGFLAGS.DEF"), player, 0, curx, 75 + 50 * curIdx)); curx += 18; } ++curIdx; diff --git a/client/lobby/CSelectionBase.h b/client/lobby/CSelectionBase.h index 3f545892f..2e0e870e2 100644 --- a/client/lobby/CSelectionBase.h +++ b/client/lobby/CSelectionBase.h @@ -174,7 +174,6 @@ public: class CFlagBox : public CIntObject { - std::shared_ptr iconsTeamFlags; std::shared_ptr labelAllies; std::shared_ptr labelEnemies; std::vector> flagsAllies; @@ -192,7 +191,7 @@ public: std::shared_ptr labelGroupTeams; std::vector> iconsFlags; public: - CFlagBoxTooltipBox(std::shared_ptr icons); + CFlagBoxTooltipBox(); }; }; diff --git a/client/lobby/SelectionTab.h b/client/lobby/SelectionTab.h index e19fa055f..4d9278ca0 100644 --- a/client/lobby/SelectionTab.h +++ b/client/lobby/SelectionTab.h @@ -20,6 +20,7 @@ class CSlider; class CLabel; class CPicture; class IImage; +class CAnimation; enum ESortBy { diff --git a/client/mainmenu/CMainMenu.h b/client/mainmenu/CMainMenu.h index 442f92afb..32e421330 100644 --- a/client/mainmenu/CMainMenu.h +++ b/client/mainmenu/CMainMenu.h @@ -24,7 +24,6 @@ class CGStatusBar; class CTextBox; class CTabbedInt; class CAnimImage; -class CAnimation; class CButton; class CFilledTexture; class CLabel; diff --git a/client/render/Graphics.h b/client/render/Graphics.h index 845b14e3b..e6927324d 100644 --- a/client/render/Graphics.h +++ b/client/render/Graphics.h @@ -28,7 +28,6 @@ class JsonNode; VCMI_LIB_NAMESPACE_END struct SDL_Surface; -class CAnimation; class IFont; /// Handles fonts, hero images, town images, various graphics diff --git a/client/renderSDL/CursorHardware.h b/client/renderSDL/CursorHardware.h index c4e311778..02b75fca5 100644 --- a/client/renderSDL/CursorHardware.h +++ b/client/renderSDL/CursorHardware.h @@ -9,7 +9,6 @@ */ #pragma once -class CAnimation; class IImage; struct SDL_Surface; struct SDL_Texture; diff --git a/client/renderSDL/CursorSoftware.h b/client/renderSDL/CursorSoftware.h index 44080e3e2..cb43bb1e5 100644 --- a/client/renderSDL/CursorSoftware.h +++ b/client/renderSDL/CursorSoftware.h @@ -9,7 +9,6 @@ */ #pragma once -class CAnimation; class IImage; struct SDL_Surface; struct SDL_Texture; diff --git a/client/widgets/Buttons.cpp b/client/widgets/Buttons.cpp index 4ab499cd7..a6225668c 100644 --- a/client/widgets/Buttons.cpp +++ b/client/widgets/Buttons.cpp @@ -24,7 +24,6 @@ #include "../gui/InterfaceObjectConfigurable.h" #include "../media/ISoundPlayer.h" #include "../windows/InfoWindows.h" -#include "../render/CAnimation.h" #include "../render/Canvas.h" #include "../render/IRenderHandler.h" diff --git a/client/widgets/Images.cpp b/client/widgets/Images.cpp index 66aa98c3c..bd179eb1f 100644 --- a/client/widgets/Images.cpp +++ b/client/widgets/Images.cpp @@ -111,18 +111,18 @@ void CPicture::colorize(PlayerColor player) bg->playerColored(player); } -CFilledTexture::CFilledTexture(const ImagePath & imageName, Rect position): - CIntObject(0, position.topLeft()), - texture(GH.renderHandler().loadImage(imageName)) +CFilledTexture::CFilledTexture(const ImagePath & imageName, Rect position) + : CIntObject(0, position.topLeft()) + , texture(GH.renderHandler().loadImage(imageName)) { pos.w = position.w; pos.h = position.h; imageArea = Rect(Point(), texture->dimensions()); } -CFilledTexture::CFilledTexture(std::shared_ptr image, Rect position, Rect imageArea) +CFilledTexture::CFilledTexture(const ImagePath & imageName, Rect position, Rect imageArea) : CIntObject(0, position.topLeft()) - , texture(image) + , texture(GH.renderHandler().loadImage(imageName)) , imageArea(imageArea) { pos.w = position.w; @@ -191,8 +191,8 @@ CAnimImage::CAnimImage(std::shared_ptr Anim, size_t Frame, size_t Gr init(); } -CAnimImage::CAnimImage(std::shared_ptr Anim, size_t Frame, Rect targetPos, size_t Group, ui8 Flags): - anim(Anim), +CAnimImage::CAnimImage(const AnimationPath & name, size_t Frame, Rect targetPos, size_t Group, ui8 Flags): + anim(GH.renderHandler().loadAnimation(name)), frame(Frame), group(Group), flags(Flags), diff --git a/client/widgets/Images.h b/client/widgets/Images.h index 004f7916a..d059db52d 100644 --- a/client/widgets/Images.h +++ b/client/widgets/Images.h @@ -68,8 +68,8 @@ protected: Rect imageArea; public: + CFilledTexture(const ImagePath & imageName, Rect position, Rect imageArea); CFilledTexture(const ImagePath & imageName, Rect position); - CFilledTexture(std::shared_ptr image, Rect position, Rect imageArea); void showAll(Canvas & to) override; }; @@ -104,7 +104,7 @@ public: CAnimImage(const AnimationPath & name, size_t Frame, size_t Group=0, int x=0, int y=0, ui8 Flags=0); CAnimImage(std::shared_ptr Anim, size_t Frame, size_t Group=0, int x=0, int y=0, ui8 Flags=0); - CAnimImage(std::shared_ptr Anim, size_t Frame, Rect targetPos, size_t Group=0, ui8 Flags=0); + CAnimImage(const AnimationPath & name, size_t Frame, Rect targetPos, size_t Group=0, ui8 Flags=0); ~CAnimImage(); /// size of animation diff --git a/client/widgets/ObjectLists.h b/client/widgets/ObjectLists.h index d4e07de08..11703130c 100644 --- a/client/widgets/ObjectLists.h +++ b/client/widgets/ObjectLists.h @@ -18,7 +18,6 @@ VCMI_LIB_NAMESPACE_END class CAnimImage; class CSlider; class CLabel; -class CAnimation; /// Used as base for Tabs and List classes class CObjectList : public CIntObject diff --git a/client/windows/CExchangeWindow.cpp b/client/windows/CExchangeWindow.cpp index e447deb70..6d0fca66e 100644 --- a/client/windows/CExchangeWindow.cpp +++ b/client/windows/CExchangeWindow.cpp @@ -26,7 +26,6 @@ #include "../widgets/TextControls.h" #include "../render/IRenderHandler.h" -#include "../render/CAnimation.h" #include "../../CCallback.h" @@ -66,17 +65,12 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2, titles[0] = std::make_shared(147, 25, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, genTitle(heroInst[0])); titles[1] = std::make_shared(653, 25, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, genTitle(heroInst[1])); - auto PSKIL32 = GH.renderHandler().loadAnimation(AnimationPath::builtin("PSKIL32")); - PSKIL32->preload(); - - auto SECSK32 = GH.renderHandler().loadAnimation(AnimationPath::builtin("SECSK32")); - for(int g = 0; g < 4; ++g) { if (qeLayout) - primSkillImages.push_back(std::make_shared(PSKIL32, g, Rect(389, 12 + 26 * g, 22, 22))); + primSkillImages.push_back(std::make_shared(AnimationPath::builtin("PSKIL32"), g, Rect(389, 12 + 26 * g, 22, 22))); else - primSkillImages.push_back(std::make_shared(PSKIL32, g, 0, 385, 19 + 36 * g)); + primSkillImages.push_back(std::make_shared(AnimationPath::builtin("PSKIL32"), g, 0, 385, 19 + 36 * g)); } for(int leftRight : {0, 1}) @@ -88,14 +82,14 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2, for(int m=0; m < hero->secSkills.size(); ++m) - secSkillIcons[leftRight].push_back(std::make_shared(SECSK32, 0, 0, 32 + 36 * m + 454 * leftRight, qeLayout ? 83 : 88)); + secSkillIcons[leftRight].push_back(std::make_shared(AnimationPath::builtin("SECSK32"), 0, 0, 32 + 36 * m + 454 * leftRight, qeLayout ? 83 : 88)); specImages[leftRight] = std::make_shared(AnimationPath::builtin("UN32"), hero->type->imageIndex, 0, 67 + 490 * leftRight, qeLayout ? 41 : 45); - expImages[leftRight] = std::make_shared(PSKIL32, 4, 0, 103 + 490 * leftRight, qeLayout ? 41 : 45); + expImages[leftRight] = std::make_shared(AnimationPath::builtin("PSKIL32"), 4, 0, 103 + 490 * leftRight, qeLayout ? 41 : 45); expValues[leftRight] = std::make_shared(119 + 490 * leftRight, qeLayout ? 66 : 71, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE); - manaImages[leftRight] = std::make_shared(PSKIL32, 5, 0, 139 + 490 * leftRight, qeLayout ? 41 : 45); + manaImages[leftRight] = std::make_shared(AnimationPath::builtin("PSKIL32"), 5, 0, 139 + 490 * leftRight, qeLayout ? 41 : 45); manaValues[leftRight] = std::make_shared(155 + 490 * leftRight, qeLayout ? 66 : 71, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE); } diff --git a/client/windows/CHeroOverview.cpp b/client/windows/CHeroOverview.cpp index 81071598f..563bf9644 100644 --- a/client/windows/CHeroOverview.cpp +++ b/client/windows/CHeroOverview.cpp @@ -224,12 +224,12 @@ void CHeroOverview::genControls() { if((*CGI->heroh)[heroIdx]->haveSpellBook) { - imageSpells.push_back(std::make_shared(GH.renderHandler().loadAnimation(AnimationPath::builtin("ARTIFACT")), 0, Rect(302 + (292 / 2) + 2 * borderOffset, 7 * borderOffset + yOffset + 186 + i * (32 + borderOffset), 32, 32), 0)); + imageSpells.push_back(std::make_shared(AnimationPath::builtin("ARTIFACT"), 0, Rect(302 + (292 / 2) + 2 * borderOffset, 7 * borderOffset + yOffset + 186 + i * (32 + borderOffset), 32, 32), 0)); } i++; } - imageSpells.push_back(std::make_shared(GH.renderHandler().loadAnimation(AnimationPath::builtin("SPELLBON")), (*CGI->spellh)[spell]->getIconIndex(), Rect(302 + (292 / 2) + 2 * borderOffset, 7 * borderOffset + yOffset + 186 + i * (32 + borderOffset), 32, 32), 0)); + imageSpells.push_back(std::make_shared(AnimationPath::builtin("SPELLBON"), (*CGI->spellh)[spell]->getIconIndex(), Rect(302 + (292 / 2) + 2 * borderOffset, 7 * borderOffset + yOffset + 186 + i * (32 + borderOffset), 32, 32), 0)); labelSpellsNames.push_back(std::make_shared(302 + (292 / 2) + 3 * borderOffset + 32 + borderOffset, 8 * borderOffset + yOffset + 186 + i * (32 + borderOffset) + 3, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, (*CGI->spellh)[spell]->getNameTranslated())); i++; } diff --git a/client/windows/CHeroWindow.cpp b/client/windows/CHeroWindow.cpp index 469c3add4..2c9133ac5 100644 --- a/client/windows/CHeroWindow.cpp +++ b/client/windows/CHeroWindow.cpp @@ -28,7 +28,6 @@ #include "../widgets/CGarrisonInt.h" #include "../widgets/TextControls.h" #include "../widgets/Buttons.h" -#include "../render/CAnimation.h" #include "../render/IRenderHandler.h" #include "../../CCallback.h" @@ -131,14 +130,12 @@ CHeroWindow::CHeroWindow(const CGHeroInstance * hero) primSkillValues.push_back(value); } - auto primSkills = GH.renderHandler().loadAnimation(AnimationPath::builtin("PSKIL42")); - primSkills->preload(); - primSkillImages.push_back(std::make_shared(primSkills, 0, 0, 32, 111)); - primSkillImages.push_back(std::make_shared(primSkills, 1, 0, 102, 111)); - primSkillImages.push_back(std::make_shared(primSkills, 2, 0, 172, 111)); - primSkillImages.push_back(std::make_shared(primSkills, 3, 0, 162, 230)); - primSkillImages.push_back(std::make_shared(primSkills, 4, 0, 20, 230)); - primSkillImages.push_back(std::make_shared(primSkills, 5, 0, 242, 111)); + primSkillImages.push_back(std::make_shared(AnimationPath::builtin("PSKIL42"), 0, 0, 32, 111)); + primSkillImages.push_back(std::make_shared(AnimationPath::builtin("PSKIL42"), 1, 0, 102, 111)); + primSkillImages.push_back(std::make_shared(AnimationPath::builtin("PSKIL42"), 2, 0, 172, 111)); + primSkillImages.push_back(std::make_shared(AnimationPath::builtin("PSKIL42"), 3, 0, 162, 230)); + primSkillImages.push_back(std::make_shared(AnimationPath::builtin("PSKIL42"), 4, 0, 20, 230)); + primSkillImages.push_back(std::make_shared(AnimationPath::builtin("PSKIL42"), 5, 0, 242, 111)); specImage = std::make_shared(AnimationPath::builtin("UN44"), 0, 0, 18, 180); specArea = std::make_shared(Rect(18, 180, 136, 42), CGI->generaltexth->heroscrn[27]); diff --git a/client/windows/CMapOverview.cpp b/client/windows/CMapOverview.cpp index d2fae75df..eb9a2c6f5 100644 --- a/client/windows/CMapOverview.cpp +++ b/client/windows/CMapOverview.cpp @@ -20,7 +20,6 @@ #include "../widgets/TextControls.h" #include "../windows/GUIClasses.h" #include "../windows/InfoWindows.h" -#include "../render/CAnimation.h" #include "../render/Canvas.h" #include "../render/IImage.h" #include "../render/IRenderHandler.h" diff --git a/client/windows/CSpellWindow.cpp b/client/windows/CSpellWindow.cpp index f20c0e306..bcb223ed6 100644 --- a/client/windows/CSpellWindow.cpp +++ b/client/windows/CSpellWindow.cpp @@ -30,7 +30,6 @@ #include "../widgets/CTextInput.h" #include "../widgets/TextControls.h" #include "../adventureMap/AdventureMapInterface.h" -#include "../render/CAnimation.h" #include "../render/IRenderHandler.h" #include "../render/IImage.h" #include "../render/IImageLoader.h" @@ -151,18 +150,9 @@ CSpellWindow::CSpellWindow(const CGHeroInstance * _myHero, CPlayerInterface * _m leftCorner = std::make_shared(ImagePath::builtin("SpelTrnL.bmp"), 97 + offL, 77 + offT); rightCorner = std::make_shared(ImagePath::builtin("SpelTrnR.bmp"), 487 + offR, 72 + offT); - spellIcons = GH.renderHandler().loadAnimation(AnimationPath::builtin("Spells")); - schoolTab = std::make_shared(AnimationPath::builtin("SpelTab"), selectedTab, 0, 524 + offR, 88); schoolPicture = std::make_shared(AnimationPath::builtin("Schools"), 0, 0, 117 + offL, 74 + offT); - schoolBorders[0] = GH.renderHandler().loadAnimation(AnimationPath::builtin("SplevA.def")); - schoolBorders[1] = GH.renderHandler().loadAnimation(AnimationPath::builtin("SplevF.def")); - schoolBorders[2] = GH.renderHandler().loadAnimation(AnimationPath::builtin("SplevW.def")); - schoolBorders[3] = GH.renderHandler().loadAnimation(AnimationPath::builtin("SplevE.def")); - - for(auto item : schoolBorders) - item->preload(); mana = std::make_shared(435 + (isBigSpellbook ? 159 : 0), 426 + offB, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW, std::to_string(myHero->mana)); if(isBigSpellbook) @@ -595,7 +585,7 @@ CSpellWindow::SpellArea::SpellArea(Rect pos, CSpellWindow * owner) OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); - image = std::make_shared(owner->spellIcons, 0, 0); + image = std::make_shared(AnimationPath::builtin("Spells"), 0, 0); image->visible = false; name = std::make_shared(39, 70, FONT_TINY, ETextAlignment::CENTER); @@ -744,14 +734,22 @@ void CSpellWindow::SpellArea::setSpell(const CSpell * spell) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); + + static const std::array schoolBorders = { + AnimationPath::builtin("SplevA.def"), + AnimationPath::builtin("SplevF.def"), + AnimationPath::builtin("SplevW.def"), + AnimationPath::builtin("SplevE.def") + }; + schoolBorder.reset(); if (owner->selectedTab >= 4) { if (whichSchool.getNum() != SpellSchool()) - schoolBorder = std::make_shared(owner->schoolBorders.at(whichSchool.getNum()), schoolLevel); + schoolBorder = std::make_shared(schoolBorders.at(whichSchool.getNum()), schoolLevel); } else - schoolBorder = std::make_shared(owner->schoolBorders.at(owner->selectedTab), schoolLevel); + schoolBorder = std::make_shared(schoolBorders.at(owner->selectedTab), schoolLevel); } ColorRGBA firstLineColor, secondLineColor; diff --git a/client/windows/CSpellWindow.h b/client/windows/CSpellWindow.h index f9a761547..73dee4e4c 100644 --- a/client/windows/CSpellWindow.h +++ b/client/windows/CSpellWindow.h @@ -19,7 +19,6 @@ class CSpell; VCMI_LIB_NAMESPACE_END class IImage; -class CAnimation; class CAnimImage; class CPicture; class CLabel; @@ -67,9 +66,6 @@ class CSpellWindow : public CWindowObject InteractiveArea(const Rect &myRect, std::function funcL, int helpTextId, CSpellWindow * _owner); }; - std::shared_ptr spellIcons; - std::array, 4> schoolBorders; //[0]: air, [1]: fire, [2]: water, [3]: earth - std::shared_ptr leftCorner; std::shared_ptr rightCorner; diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index 0fd5b7169..d9fd5683a 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -36,7 +36,6 @@ #include "../widgets/VideoWidget.h" #include "../render/Canvas.h" -#include "../render/CAnimation.h" #include "../render/IRenderHandler.h" #include "../render/IImage.h" @@ -892,8 +891,13 @@ CUniversityWindow::CItem::CItem(CUniversityWindow * _parent, int _ID, int X, int pos.x += X; pos.y += Y; - topBar = std::make_shared(parent->bars, 0, 0, -28, -22); - bottomBar = std::make_shared(parent->bars, 0, 0, -28, 48); + // TODO: restore + //bars->setCustom("UNIVRED", 0, 0); + //bars->setCustom("UNIVGOLD", 1, 0); + //bars->setCustom("UNIVGREN", 2, 0); + + topBar = std::make_shared(ImagePath::builtin("UNIVRED"), Point(-28, -22)); + bottomBar = std::make_shared(ImagePath::builtin("UNIVRED"), Point(-28, 48)); icon = std::make_shared(AnimationPath::builtin("SECSKILL"), _ID * 3 + 3, 0); @@ -932,16 +936,6 @@ int CUniversityWindow::CItem::state() return 2; } -void CUniversityWindow::CItem::showAll(Canvas & to) -{ - //TODO: update when state actually changes - auto stateIndex = state(); - topBar->setFrame(stateIndex); - bottomBar->setFrame(stateIndex); - - CIntObject::showAll(to); -} - CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, const IMarket * _market, const std::function & onWindowClosed) : CWindowObject(PLAYER_COLORED, ImagePath::builtin("UNIVERS1")), hero(_hero), @@ -950,12 +944,7 @@ CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, const IMarket { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); - bars = GH.renderHandler().createAnimation(); - bars->setCustom("UNIVRED", 0, 0); - bars->setCustom("UNIVGOLD", 1, 0); - bars->setCustom("UNIVGREN", 2, 0); - bars->preload(); - + std::string titleStr = CGI->generaltexth->allTexts[602]; std::string speechStr = CGI->generaltexth->allTexts[603]; @@ -1331,18 +1320,12 @@ CThievesGuildWindow::CThievesGuildWindow(const CGObjectInstance * _owner): rowHeaders.push_back(std::make_shared(135, y, FONT_MEDIUM, ETextAlignment::CENTER, Colors::YELLOW, text)); } - auto PRSTRIPS = GH.renderHandler().loadAnimation(AnimationPath::builtin("PRSTRIPS")); - PRSTRIPS->preload(); - for(int g=1; g(PRSTRIPS, g-1, 0, 250 + 66*g, 7)); + columnBackgrounds.push_back(std::make_shared(AnimationPath::builtin("PRSTRIPS"), g-1, 0, 250 + 66*g, 7)); for(int g=0; g(283 + 66*g, 24, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->jktexts[16+g])); - auto itgflags = GH.renderHandler().loadAnimation(AnimationPath::builtin("itgflags")); - itgflags->preload(); - //printing flags for(int g = 0; g < std::size(fields); ++g) //by lines { @@ -1366,7 +1349,7 @@ CThievesGuildWindow::CThievesGuildWindow(const CGObjectInstance * _owner): int rowStartY = ypos + (j ? 4 : 0); for(size_t i=0; i < rowLength[j]; i++) - cells.push_back(std::make_shared(itgflags, players[i + j*4].getNum(), 0, rowStartX + (int)i*12, rowStartY)); + cells.push_back(std::make_shared(AnimationPath::builtin("itgflags"), players[i + j*4].getNum(), 0, rowStartX + (int)i*12, rowStartY)); } } } diff --git a/client/windows/GUIClasses.h b/client/windows/GUIClasses.h index 550ff7a0c..05c65c412 100644 --- a/client/windows/GUIClasses.h +++ b/client/windows/GUIClasses.h @@ -363,15 +363,14 @@ class CUniversityWindow : public CStatusbarWindow class CItem : public CIntObject { std::shared_ptr icon; - std::shared_ptr topBar; - std::shared_ptr bottomBar; + std::shared_ptr topBar; + std::shared_ptr bottomBar; std::shared_ptr name; std::shared_ptr level; public: SecondarySkill ID;//id of selected skill CUniversityWindow * parent; - void showAll(Canvas & to) override; void clickPressed(const Point & cursorPosition) override; void showPopupWindow(const Point & cursorPosition) override; void hover(bool on) override; @@ -382,8 +381,6 @@ class CUniversityWindow : public CStatusbarWindow const CGHeroInstance * hero; const IMarket * market; - std::shared_ptr bars; - std::vector> items; std::shared_ptr cancel; From 600b06b74d538ef5c4bec66e17bb06e450ad80ab Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sun, 2 Jun 2024 18:33:51 +0000 Subject: [PATCH 074/100] Moved all handling of image loading to render handler --- client/battle/BattleFieldController.cpp | 9 +- client/render/CAnimation.cpp | 157 +++++------------------- client/render/CAnimation.h | 12 +- client/render/IImage.h | 1 - client/render/IRenderHandler.h | 3 +- client/renderSDL/RenderHandler.cpp | 115 +++++++++++++++-- client/renderSDL/RenderHandler.h | 13 +- client/renderSDL/SDLImage.cpp | 6 - client/renderSDL/SDLImage.h | 1 - 9 files changed, 151 insertions(+), 166 deletions(-) diff --git a/client/battle/BattleFieldController.cpp b/client/battle/BattleFieldController.cpp index aeb4e2817..9df03805b 100644 --- a/client/battle/BattleFieldController.cpp +++ b/client/battle/BattleFieldController.cpp @@ -560,19 +560,22 @@ void BattleFieldController::flipRangeLimitImagesIntoPositions(std::shared_ptrgetImage(hexEdgeMaskToFrameIndex[HexMasks::topRight])->verticalFlip(); images->getImage(hexEdgeMaskToFrameIndex[HexMasks::right])->verticalFlip(); - images->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomRight])->doubleFlip(); + images->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomRight])->verticalFlip(); + images->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomRight])->horizontalFlip(); images->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomLeft])->horizontalFlip(); images->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottom])->horizontalFlip(); images->getImage(hexEdgeMaskToFrameIndex[HexMasks::topRightHalfCorner])->verticalFlip(); - images->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomRightHalfCorner])->doubleFlip(); + images->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomRightHalfCorner])->verticalFlip(); + images->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomRightHalfCorner])->horizontalFlip(); images->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomLeftHalfCorner])->horizontalFlip(); images->getImage(hexEdgeMaskToFrameIndex[HexMasks::rightHalf])->verticalFlip(); images->getImage(hexEdgeMaskToFrameIndex[HexMasks::topRightCorner])->verticalFlip(); - images->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomRightCorner])->doubleFlip(); + images->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomRightCorner])->verticalFlip(); + images->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomRightCorner])->horizontalFlip(); images->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomLeftCorner])->horizontalFlip(); } diff --git a/client/render/CAnimation.cpp b/client/render/CAnimation.cpp index 39e373eb9..7ab38a01f 100644 --- a/client/render/CAnimation.cpp +++ b/client/render/CAnimation.cpp @@ -10,33 +10,12 @@ #include "StdInc.h" #include "CAnimation.h" -#include "CDefFile.h" +#include "../gui/CGuiHandler.h" +#include "../render/IImage.h" +#include "../render/IRenderHandler.h" #include "../../lib/filesystem/Filesystem.h" #include "../../lib/json/JsonUtils.h" -#include "../renderSDL/SDLImage.h" - -std::shared_ptr CAnimation::getFromExtraDef(std::string filename) -{ - size_t pos = filename.find(':'); - if (pos == -1) - return nullptr; - CAnimation anim(AnimationPath::builtinTODO(filename.substr(0, pos))); - pos++; - size_t frame = atoi(filename.c_str()+pos); - size_t group = 0; - pos = filename.find(':', pos); - if (pos != -1) - { - pos++; - group = frame; - frame = atoi(filename.c_str()+pos); - } - anim.load(frame ,group); - auto ret = anim.images[group][frame]; - anim.images.clear(); - return ret; -} bool CAnimation::loadFrame(size_t frame, size_t group) { @@ -46,8 +25,8 @@ bool CAnimation::loadFrame(size_t frame, size_t group) return false; } - auto image = getImage(frame, group, false); - if(image) + + if(auto image = getImage(frame, group, false)) { return true; } @@ -55,30 +34,38 @@ bool CAnimation::loadFrame(size_t frame, size_t group) //try to get image from def if(source[group][frame].getType() == JsonNode::JsonType::DATA_NULL) { - if(defFile) - { - auto frameList = defFile->getEntries(); + auto image = GH.renderHandler().loadImage(name, frame, group); - if(vstd::contains(frameList, group) && frameList.at(group) > frame) // frame is present - { - images[group][frame] = std::make_shared(defFile.get(), frame, group); - return true; - } + if(image) + { + images[group][frame] = image; + return true; } // still here? image is missing printError(frame, group, "LoadFrame"); - images[group][frame] = std::make_shared(ImagePath::builtin("DEFAULT"), EImageBlitMode::ALPHA); + images[group][frame] = GH.renderHandler().loadImage(ImagePath::builtin("DEFAULT"), EImageBlitMode::OPAQUE); + return false; } - else //load from separate file - { - auto img = getFromExtraDef(source[group][frame]["file"].String()); - if(!img) - img = std::make_shared(source[group][frame], EImageBlitMode::ALPHA); + if (!source[group][frame]["file"].isNull()) + { + auto img = GH.renderHandler().loadImage(ImagePath::fromJson(source[group][frame]["file"]), EImageBlitMode::ALPHA); images[group][frame] = img; return true; } + + if (!source[group][frame]["animation"].isNull()) + { + AnimationPath animationFile = AnimationPath::fromJson(source[group][frame]["animation"]); + int32_t animationGroup = source[group][frame]["sourceGroup"].Integer(); + int32_t animationFrame = source[group][frame]["sourceFrame"].Integer(); + + auto img = GH.renderHandler().loadImage(animationFile, animationFrame, animationGroup); + images[group][frame] = img; + return true; + } + return false; } @@ -96,45 +83,6 @@ bool CAnimation::unloadFrame(size_t frame, size_t group) return false; } -void CAnimation::initFromJson(const JsonNode & config) -{ - std::string basepath; - basepath = config["basepath"].String(); - - JsonNode base; - base["margins"] = config["margins"]; - base["width"] = config["width"]; - base["height"] = config["height"]; - - for(const JsonNode & group : config["sequences"].Vector()) - { - size_t groupID = group["group"].Integer();//TODO: string-to-value conversion("moving" -> MOVING) - source[groupID].clear(); - - for(const JsonNode & frame : group["frames"].Vector()) - { - JsonNode toAdd; - JsonUtils::inherit(toAdd, base); - toAdd["file"].String() = basepath + frame.String(); - source[groupID].push_back(toAdd); - } - } - - for(const JsonNode & node : config["images"].Vector()) - { - size_t group = node["group"].Integer(); - size_t frame = node["frame"].Integer(); - - if (source[group].size() <= frame) - source[group].resize(frame+1); - - JsonNode toAdd; - JsonUtils::inherit(toAdd, base); - toAdd["file"].String() = basepath + node["file"].String(); - source[group][frame] = toAdd; - } -} - void CAnimation::exportBitmaps(const boost::filesystem::path& path) const { if(images.empty()) @@ -168,57 +116,16 @@ void CAnimation::exportBitmaps(const boost::filesystem::path& path) const logGlobal->info("Exported %d frames to %s", counter, actualPath.string()); } -void CAnimation::init() -{ - if(defFile) - { - const std::map defEntries = defFile->getEntries(); - - for (auto & defEntry : defEntries) - source[defEntry.first].resize(defEntry.second); - } - -// if (vstd::contains(graphics->imageLists, name.getName())) -// initFromJson(graphics->imageLists[name.getName()]); - - auto jsonResource = name.toType(); - auto configList = CResourceHandler::get()->getResourcesWithName(jsonResource); - - for(auto & loader : configList) - { - auto stream = loader->load(jsonResource); - std::unique_ptr textData(new ui8[stream->getSize()]); - stream->read(textData.get(), stream->getSize()); - - const JsonNode config(reinterpret_cast(textData.get()), stream->getSize(), jsonResource.getName()); - - initFromJson(config); - } -} - void CAnimation::printError(size_t frame, size_t group, std::string type) const { logGlobal->error("%s error: Request for frame not present in CAnimation! File name: %s, Group: %d, Frame: %d", type, name.getOriginalName(), group, frame); } -CAnimation::CAnimation(const AnimationPath & Name): +CAnimation::CAnimation(const AnimationPath & Name, std::map > layout): name(boost::starts_with(Name.getName(), "SPRITES") ? Name : Name.addPrefix("SPRITES/")), + source(layout), preloaded(false) { - if(CResourceHandler::get()->existsResource(name)) - { - try - { - defFile = std::make_shared(name); - } - catch ( const std::runtime_error & e) - { - logAnim->error("Def file %s failed to load! Reason: %s", Name.getOriginalName(), e.what()); - } - } - - init(); - if(source.empty()) logAnim->error("Animation %s failed to load", Name.getOriginalName()); } @@ -226,7 +133,6 @@ CAnimation::CAnimation(const AnimationPath & Name): CAnimation::CAnimation(): preloaded(false) { - init(); } CAnimation::~CAnimation() = default; @@ -250,8 +156,9 @@ void CAnimation::duplicateImage(const size_t sourceGroup, const size_t sourceFra if(clone.getType() == JsonNode::JsonType::DATA_NULL) { - std::string temp = name.getName()+":"+std::to_string(sourceGroup)+":"+std::to_string(sourceFrame); - clone["file"].String() = temp; + clone["animation"].String() = name.getName(); + clone["sourceGroup"].Integer() = sourceGroup; + clone["sourceFrame"].Integer() = sourceFrame; } source[targetGroup].push_back(clone); diff --git a/client/render/CAnimation.h b/client/render/CAnimation.h index efb66f602..2b2abfe86 100644 --- a/client/render/CAnimation.h +++ b/client/render/CAnimation.h @@ -35,27 +35,17 @@ private: bool preloaded; - std::shared_ptr defFile; - //loader, will be called by load(), require opened def file for loading from it. Returns true if image is loaded bool loadFrame(size_t frame, size_t group); //unloadFrame, returns true if image has been unloaded ( either deleted or decreased refCount) bool unloadFrame(size_t frame, size_t group); - //initialize animation from file - void initFromJson(const JsonNode & input); - void init(); - //to get rid of copy-pasting error message :] void printError(size_t frame, size_t group, std::string type) const; - //not a very nice method to get image from another def file - //TODO: remove after implementing resource manager - std::shared_ptr getFromExtraDef(std::string filename); - public: - CAnimation(const AnimationPath & Name); + CAnimation(const AnimationPath & Name, std::map > layout); CAnimation(); ~CAnimation(); diff --git a/client/render/IImage.h b/client/render/IImage.h index 2b8bf1d8d..abbb71583 100644 --- a/client/render/IImage.h +++ b/client/render/IImage.h @@ -80,7 +80,6 @@ public: virtual void horizontalFlip() = 0; virtual void verticalFlip() = 0; - virtual void doubleFlip() = 0; IImage() = default; virtual ~IImage() = default; diff --git a/client/render/IRenderHandler.h b/client/render/IRenderHandler.h index 551c61ea2..805d3ef67 100644 --- a/client/render/IRenderHandler.h +++ b/client/render/IRenderHandler.h @@ -25,7 +25,6 @@ public: /// Loads image using given path virtual std::shared_ptr loadImage(const ImagePath & path) = 0; virtual std::shared_ptr loadImage(const ImagePath & path, EImageBlitMode mode) = 0; - virtual std::shared_ptr loadImage(const AnimationPath & path, int frame, int group) = 0; /// temporary compatibility method. Creates IImage from existing SDL_Surface @@ -35,6 +34,6 @@ public: /// Loads animation using given path virtual std::shared_ptr loadAnimation(const AnimationPath & path) = 0; - /// Creates empty CAnimation + /// Creates empty CAnimation. Temporary compatibility method virtual std::shared_ptr createAnimation() = 0; }; diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index 98a3e680a..dc7126497 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -15,34 +15,119 @@ #include "../render/CAnimation.h" #include "../render/CDefFile.h" -#include "../../lib/json/JsonNode.h" +#include "../../lib/json/JsonUtils.h" +#include "../../lib/filesystem/Filesystem.h" std::shared_ptr RenderHandler::getAnimationFile(const AnimationPath & path) { - auto it = animationFiles.find(path); + AnimationPath actualPath = boost::starts_with(path.getName(), "SPRITES") ? path : path.addPrefix("SPRITES/"); + + auto it = animationFiles.find(actualPath); if (it != animationFiles.end()) return it->second; - auto result = std::make_shared(path); + if (!CResourceHandler::get()->existsResource(actualPath)) + { + animationFiles[actualPath] = nullptr; + return nullptr; + } - animationFiles[path] = result; + auto result = std::make_shared(actualPath); + + animationFiles[actualPath] = result; return result; } -std::shared_ptr RenderHandler::getJsonFile(const JsonPath & path) +void RenderHandler::initFromJson(AnimationLayoutMap & source, const JsonNode & config) { - auto it = jsonFiles.find(path); + std::string basepath; + basepath = config["basepath"].String(); - if (it != jsonFiles.end()) + JsonNode base; + base["margins"] = config["margins"]; + base["width"] = config["width"]; + base["height"] = config["height"]; + + for(const JsonNode & group : config["sequences"].Vector()) + { + size_t groupID = group["group"].Integer();//TODO: string-to-value conversion("moving" -> MOVING) + source[groupID].clear(); + + for(const JsonNode & frame : group["frames"].Vector()) + { + JsonNode toAdd; + JsonUtils::inherit(toAdd, base); + toAdd["file"].String() = basepath + frame.String(); + source[groupID].push_back(toAdd); + } + } + + for(const JsonNode & node : config["images"].Vector()) + { + size_t group = node["group"].Integer(); + size_t frame = node["frame"].Integer(); + + if (source[group].size() <= frame) + source[group].resize(frame+1); + + JsonNode toAdd; + JsonUtils::inherit(toAdd, base); + toAdd["file"].String() = basepath + node["file"].String(); + source[group][frame] = toAdd; + } +} + +const RenderHandler::AnimationLayoutMap & RenderHandler::getAnimationLayout(const AnimationPath & path) +{ + auto it = animationLayouts.find(path); + + if (it != animationLayouts.end()) return it->second; - auto result = std::make_shared(path); + AnimationLayoutMap result; - jsonFiles[path] = result; - return result; + auto defFile = getAnimationFile(path); + if(defFile) + { + const std::map defEntries = defFile->getEntries(); + + for (auto & defEntry : defEntries) + result[defEntry.first].resize(defEntry.second); + } + + auto jsonResource = path.toType(); + JsonPath actualJsonPath = boost::starts_with(jsonResource.getName(), "SPRITES") ? jsonResource : jsonResource.addPrefix("SPRITES/");; + auto configList = CResourceHandler::get()->getResourcesWithName(actualJsonPath); + + for(auto & loader : configList) + { + auto stream = loader->load(actualJsonPath); + std::unique_ptr textData(new ui8[stream->getSize()]); + stream->read(textData.get(), stream->getSize()); + + const JsonNode config(reinterpret_cast(textData.get()), stream->getSize(), path.getOriginalName()); + + initFromJson(result, config); + } + + animationLayouts[path] = result; + return animationLayouts[path]; } +//std::shared_ptr RenderHandler::getJsonFile(const JsonPath & path) +//{ +// auto it = jsonFiles.find(path); +// +// if (it != jsonFiles.end()) +// return it->second; +// +// auto result = std::make_shared(path); +// +// jsonFiles[path] = result; +// return result; +//} + std::shared_ptr RenderHandler::loadImage(const AnimationPath & path, int frame, int group) { AnimationLocator locator{path, frame, group}; @@ -78,7 +163,13 @@ std::shared_ptr RenderHandler::loadImage(const ImagePath & path) std::shared_ptr RenderHandler::loadImage(const ImagePath & path, EImageBlitMode mode) { - return std::make_shared(path, mode); + auto it = imageFiles.find(path); + if (it != imageFiles.end()) + return it->second; + + auto result = std::make_shared(path, mode); + imageFiles[path] = result; + return result; } std::shared_ptr RenderHandler::createImage(SDL_Surface * source) @@ -88,7 +179,7 @@ std::shared_ptr RenderHandler::createImage(SDL_Surface * source) std::shared_ptr RenderHandler::loadAnimation(const AnimationPath & path) { - return std::make_shared(path); + return std::make_shared(path, getAnimationLayout(path)); } std::shared_ptr RenderHandler::createAnimation() diff --git a/client/renderSDL/RenderHandler.h b/client/renderSDL/RenderHandler.h index 1e6e7cc95..9ac90b926 100644 --- a/client/renderSDL/RenderHandler.h +++ b/client/renderSDL/RenderHandler.h @@ -15,6 +15,8 @@ class CDefFile; class RenderHandler : public IRenderHandler { + using AnimationLayoutMap = std::map>; + struct AnimationLocator { AnimationPath animation; @@ -32,21 +34,22 @@ class RenderHandler : public IRenderHandler }; std::map> animationFiles; - std::map> jsonFiles; + std::map animationLayouts; std::map> imageFiles; std::map> animationFrames; std::shared_ptr getAnimationFile(const AnimationPath & path); - std::shared_ptr getJsonFile(const JsonPath & path); + const AnimationLayoutMap & getAnimationLayout(const AnimationPath & path); + void initFromJson(AnimationLayoutMap & layout, const JsonNode & config); public: + // IRenderHandler implementation + std::shared_ptr loadImage(const ImagePath & path) override; std::shared_ptr loadImage(const ImagePath & path, EImageBlitMode mode) override; - std::shared_ptr loadImage(const AnimationPath & path, int frame, int group) override; - std::shared_ptr createImage(SDL_Surface * source) override; - std::shared_ptr loadAnimation(const AnimationPath & path) override; + std::shared_ptr createImage(SDL_Surface * source) override; std::shared_ptr createAnimation() override; }; diff --git a/client/renderSDL/SDLImage.cpp b/client/renderSDL/SDLImage.cpp index 2a1291c67..dddff02c3 100644 --- a/client/renderSDL/SDLImage.cpp +++ b/client/renderSDL/SDLImage.cpp @@ -266,12 +266,6 @@ void SDLImage::verticalFlip() surf = flipped; } -void SDLImage::doubleFlip() -{ - horizontalFlip(); - verticalFlip(); -} - // Keep the original palette, in order to do color switching operation void SDLImage::savePalette() { diff --git a/client/renderSDL/SDLImage.h b/client/renderSDL/SDLImage.h index 59487ff35..584667b03 100644 --- a/client/renderSDL/SDLImage.h +++ b/client/renderSDL/SDLImage.h @@ -64,7 +64,6 @@ public: void horizontalFlip() override; void verticalFlip() override; - void doubleFlip() override; void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override; void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override; From 2b3e4e01ca0bb9f5bb3a425372d9d90e1fb149d1 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sun, 2 Jun 2024 19:31:56 +0000 Subject: [PATCH 075/100] Remove unused code --- client/render/IImage.h | 4 --- client/renderSDL/SDLImage.cpp | 61 +---------------------------------- client/renderSDL/SDLImage.h | 10 ++---- 3 files changed, 3 insertions(+), 72 deletions(-) diff --git a/client/render/IImage.h b/client/render/IImage.h index abbb71583..40b722f49 100644 --- a/client/render/IImage.h +++ b/client/render/IImage.h @@ -69,8 +69,6 @@ public: //only indexed bitmaps, 16 colors maximum virtual void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) = 0; virtual void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) = 0; - virtual void resetPalette(int colorID) = 0; - virtual void resetPalette() = 0; virtual void setAlpha(uint8_t value) = 0; virtual void setBlitMode(EImageBlitMode mode) = 0; @@ -81,7 +79,5 @@ public: virtual void horizontalFlip() = 0; virtual void verticalFlip() = 0; - IImage() = default; virtual ~IImage() = default; }; - diff --git a/client/renderSDL/SDLImage.cpp b/client/renderSDL/SDLImage.cpp index dddff02c3..7f2f11f07 100644 --- a/client/renderSDL/SDLImage.cpp +++ b/client/renderSDL/SDLImage.cpp @@ -18,8 +18,6 @@ #include "../render/CDefFile.h" #include "../render/Graphics.h" -#include "../../lib/json/JsonNode.h" - #include class SDLImageLoader; @@ -65,39 +63,6 @@ SDLImage::SDLImage(SDL_Surface * from, EImageBlitMode mode) fullSize.y = surf->h; } -SDLImage::SDLImage(const JsonNode & conf, EImageBlitMode mode) - : surf(nullptr), - margins(0, 0), - fullSize(0, 0), - originalPalette(nullptr) -{ - surf = BitmapHandler::loadBitmap(ImagePath::fromJson(conf["file"])); - - if(surf == nullptr) - return; - - savePalette(); - setBlitMode(mode); - - const JsonNode & jsonMargins = conf["margins"]; - - margins.x = static_cast(jsonMargins["left"].Integer()); - margins.y = static_cast(jsonMargins["top"].Integer()); - - fullSize.x = static_cast(conf["width"].Integer()); - fullSize.y = static_cast(conf["height"].Integer()); - - if(fullSize.x == 0) - { - fullSize.x = margins.x + surf->w + (int)jsonMargins["right"].Integer(); - } - - if(fullSize.y == 0) - { - fullSize.y = margins.y + surf->h + (int)jsonMargins["bottom"].Integer(); - } -} - SDLImage::SDLImage(const ImagePath & filename, EImageBlitMode mode) : surf(nullptr), margins(0, 0), @@ -310,24 +275,6 @@ void SDLImage::adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipM } } -void SDLImage::resetPalette() -{ - if(originalPalette == nullptr) - return; - - // Always keep the original palette not changed, copy a new palette to assign to surface - SDL_SetPaletteColors(surf->format->palette, originalPalette->colors, 0, originalPalette->ncolors); -} - -void SDLImage::resetPalette( int colorID ) -{ - if(originalPalette == nullptr) - return; - - // Always keep the original palette not changed, copy a new palette to assign to surface - SDL_SetPaletteColors(surf->format->palette, originalPalette->colors + colorID, colorID, 1); -} - void SDLImage::setSpecialPalette(const IImage::SpecialPalette & specialPalette, uint32_t colorsToSkipMask) { if(surf->format->palette) @@ -345,11 +292,5 @@ void SDLImage::setSpecialPalette(const IImage::SpecialPalette & specialPalette, SDLImage::~SDLImage() { SDL_FreeSurface(surf); - - if(originalPalette != nullptr) - { - SDL_FreePalette(originalPalette); - originalPalette = nullptr; - } + SDL_FreePalette(originalPalette); } - diff --git a/client/renderSDL/SDLImage.h b/client/renderSDL/SDLImage.h index 584667b03..4243606ae 100644 --- a/client/renderSDL/SDLImage.h +++ b/client/renderSDL/SDLImage.h @@ -26,12 +26,12 @@ struct SDL_Palette; */ class SDLImage : public IImage { -public: - const static int DEFAULT_PALETTE_COLORS = 256; //Surface without empty borders SDL_Surface * surf; + + SDL_Palette * originalPalette; //size of left and top borders Point margins; //total size including borders @@ -45,7 +45,6 @@ public: //Load from bitmap file SDLImage(const ImagePath & filename, EImageBlitMode blitMode); - SDLImage(const JsonNode & conf, EImageBlitMode blitMode); //Create using existing surface, extraRef will increase refcount on SDL_Surface SDLImage(SDL_Surface * from, EImageBlitMode blitMode); ~SDLImage(); @@ -67,8 +66,6 @@ public: void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override; void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override; - void resetPalette(int colorID) override; - void resetPalette() override; void setAlpha(uint8_t value) override; void setBlitMode(EImageBlitMode mode) override; @@ -76,7 +73,4 @@ public: void setSpecialPalette(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask) override; friend class SDLImageLoader; - -private: - SDL_Palette * originalPalette; }; From 47de9a62dc58d7760b5f4a0805a56e97d5e51c96 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 4 Jun 2024 11:46:45 +0000 Subject: [PATCH 076/100] Created separate classes for shared, unchangeable image and image manipulators owned by UI elements --- client/CPlayerInterface.cpp | 6 +- client/render/Canvas.cpp | 6 +- client/render/Graphics.cpp | 83 +++----- client/render/Graphics.h | 5 +- client/render/IImage.h | 18 +- client/render/IImageLoader.h | 2 - client/renderSDL/CursorHardware.cpp | 2 +- client/renderSDL/CursorSoftware.cpp | 2 +- client/renderSDL/RenderHandler.cpp | 14 +- client/renderSDL/RenderHandler.h | 5 +- client/renderSDL/SDLImage.cpp | 260 +++++++++++++++--------- client/renderSDL/SDLImage.h | 98 ++++++--- client/renderSDL/SDLImageLoader.cpp | 6 +- client/renderSDL/SDLImageLoader.h | 6 +- client/renderSDL/SDL_Extensions.cpp | 17 -- client/renderSDL/SDL_Extensions.h | 1 - client/widgets/Images.cpp | 11 +- client/windows/CCastleInterface.cpp | 6 +- client/windows/CWindowWithArtifacts.cpp | 7 +- 19 files changed, 312 insertions(+), 243 deletions(-) diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 8d19f2373..e48e4e58d 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1143,9 +1143,9 @@ void CPlayerInterface::showMapObjectSelectDialog(QueryID askID, const Component const CGTownInstance * t = dynamic_cast(cb->getObj(obj)); if(t) { - std::shared_ptr a = GH.renderHandler().loadAnimation(AnimationPath::builtin("ITPA")); - a->preload(); - images.push_back(a->getImage(t->town->clientInfo.icons[t->hasFort()][false] + 2)->scaleFast(Point(35, 23))); + auto image = GH.renderHandler().loadImage(AnimationPath::builtin("ITPA"), t->town->clientInfo.icons[t->hasFort()][false] + 2, 0); + image->scaleFast(Point(35, 23)); + images.push_back(image); } } diff --git a/client/render/Canvas.cpp b/client/render/Canvas.cpp index 1e6bd290d..dbb46b9f0 100644 --- a/client/render/Canvas.cpp +++ b/client/render/Canvas.cpp @@ -81,14 +81,14 @@ void Canvas::draw(const std::shared_ptr& image, const Point & pos) { assert(image); if (image) - image->draw(surface, renderArea.x + pos.x, renderArea.y + pos.y); + image->draw(surface, pos + renderArea.topLeft()); } void Canvas::draw(const std::shared_ptr& image, const Point & pos, const Rect & sourceRect) { assert(image); if (image) - image->draw(surface, renderArea.x + pos.x, renderArea.y + pos.y, &sourceRect); + image->draw(surface, pos + renderArea.topLeft(), &sourceRect); } void Canvas::draw(const Canvas & image, const Point & pos) @@ -192,7 +192,7 @@ void Canvas::fillTexture(const std::shared_ptr& image) for (int y=0; y < surface->h; y+= imageArea.h) { for (int x=0; x < surface->w; x+= imageArea.w) - image->draw(surface, renderArea.x + x, renderArea.y + y); + image->draw(surface, Point(renderArea.x + x, renderArea.y + y)); } } diff --git a/client/render/Graphics.cpp b/client/render/Graphics.cpp index 8399ff698..a42a90c5b 100644 --- a/client/render/Graphics.cpp +++ b/client/render/Graphics.cpp @@ -135,71 +135,34 @@ Graphics::Graphics() //(!) do not load any CAnimation here } -void Graphics::blueToPlayersAdv(SDL_Surface * sur, PlayerColor player) +void Graphics::setPlayerPalette(SDL_Palette * targetPalette, PlayerColor player) { - if(sur->format->palette) + SDL_Color palette[32]; + if(player.isValidPlayer()) { - SDL_Color palette[32]; - if(player.isValidPlayer()) - { - for(int i=0; i<32; ++i) - palette[i] = CSDL_Ext::toSDL(playerColorPalette[player][i]); - } - else if(player == PlayerColor::NEUTRAL) - { - for(int i=0; i<32; ++i) - palette[i] = CSDL_Ext::toSDL(neutralColorPalette[i]); - } - else - { - logGlobal->error("Wrong player id in blueToPlayersAdv (%s)!", player.toString()); - return; - } -//FIXME: not all player colored images have player palette at last 32 indexes -//NOTE: following code is much more correct but still not perfect (bugged with status bar) - CSDL_Ext::setColors(sur, palette, 224, 32); - - -#if 0 - - SDL_Color * bluePalette = playerColorPalette + 32; - - SDL_Palette * oldPalette = sur->format->palette; - - SDL_Palette * newPalette = SDL_AllocPalette(256); - - for(size_t destIndex = 0; destIndex < 256; destIndex++) - { - SDL_Color old = oldPalette->colors[destIndex]; - - bool found = false; - - for(size_t srcIndex = 0; srcIndex < 32; srcIndex++) - { - if(old.b == bluePalette[srcIndex].b && old.g == bluePalette[srcIndex].g && old.r == bluePalette[srcIndex].r) - { - found = true; - newPalette->colors[destIndex] = palette[srcIndex]; - break; - } - } - if(!found) - newPalette->colors[destIndex] = old; - } - - SDL_SetSurfacePalette(sur, newPalette); - - SDL_FreePalette(newPalette); - -#endif // 0 + for(int i=0; i<32; ++i) + palette[i] = CSDL_Ext::toSDL(playerColorPalette[player][i]); } else { - //TODO: implement. H3 method works only for images with palettes. - // Add some kind of player-colored overlay? - // Or keep palette approach here and replace only colors of specific value(s) - // Or just wait for OpenGL support? - logGlobal->warn("Image must have palette to be player-colored!"); + for(int i=0; i<32; ++i) + palette[i] = CSDL_Ext::toSDL(neutralColorPalette[i]); + } + + SDL_SetPaletteColors(targetPalette, palette, 224, 32); +} + +void Graphics::setPlayerFlagColor(SDL_Palette * targetPalette, PlayerColor player) +{ + if(player.isValidPlayer()) + { + SDL_Color color = CSDL_Ext::toSDL(playerColors[player.getNum()]); + SDL_SetPaletteColors(targetPalette, &color, 5, 1); + } + else + { + SDL_Color color = CSDL_Ext::toSDL(neutralColor); + SDL_SetPaletteColors(targetPalette, &color, 5, 1); } } diff --git a/client/render/Graphics.h b/client/render/Graphics.h index e6927324d..8b4941b40 100644 --- a/client/render/Graphics.h +++ b/client/render/Graphics.h @@ -27,7 +27,7 @@ class JsonNode; VCMI_LIB_NAMESPACE_END -struct SDL_Surface; +struct SDL_Palette; class IFont; /// Handles fonts, hero images, town images, various graphics @@ -60,7 +60,8 @@ public: //functions Graphics(); - void blueToPlayersAdv(SDL_Surface * sur, PlayerColor player); //replaces blue interface colour with a color of player + void setPlayerPalette(SDL_Palette * sur, PlayerColor player); + void setPlayerFlagColor(SDL_Palette * sur, PlayerColor player); }; extern Graphics * graphics; diff --git a/client/render/IImage.h b/client/render/IImage.h index 40b722f49..bed254188 100644 --- a/client/render/IImage.h +++ b/client/render/IImage.h @@ -46,10 +46,10 @@ public: static constexpr int32_t SPECIAL_PALETTE_MASK_CREATURES = 0b11110011; //draws image on surface "where" at position - virtual void draw(SDL_Surface * where, int posX = 0, int posY = 0, const Rect * src = nullptr) const = 0; - virtual void draw(SDL_Surface * where, const Rect * dest, const Rect * src) const = 0; + virtual void draw(SDL_Surface * where, const Point & pos, const Rect * src = nullptr) const = 0; + //virtual void draw(SDL_Surface * where, const Rect * dest, const Rect * src) const = 0; - virtual std::shared_ptr scaleFast(const Point & size) const = 0; + virtual void scaleFast(const Point & size) = 0; virtual void exportBitmap(const boost::filesystem::path & path) const = 0; @@ -81,3 +81,15 @@ public: virtual ~IImage() = default; }; + +class IConstImage +{ +public: + virtual Point dimensions() const = 0; + virtual void exportBitmap(const boost::filesystem::path & path) const = 0; + virtual bool isTransparent(const Point & coords) const = 0; + + virtual std::shared_ptr createImageReference() = 0; + + virtual ~IConstImage() = default; +}; diff --git a/client/render/IImageLoader.h b/client/render/IImageLoader.h index 7cd950091..b4368e5f5 100644 --- a/client/render/IImageLoader.h +++ b/client/render/IImageLoader.h @@ -14,8 +14,6 @@ VCMI_LIB_NAMESPACE_BEGIN class Point; VCMI_LIB_NAMESPACE_END -class SDLImage; - struct SDL_Color; class IImageLoader diff --git a/client/renderSDL/CursorHardware.cpp b/client/renderSDL/CursorHardware.cpp index a4a10f2f8..dc42f9e38 100644 --- a/client/renderSDL/CursorHardware.cpp +++ b/client/renderSDL/CursorHardware.cpp @@ -49,7 +49,7 @@ void CursorHardware::setImage(std::shared_ptr image, const Point & pivot CSDL_Ext::fillSurface(cursorSurface, CSDL_Ext::toSDL(Colors::TRANSPARENCY)); - image->draw(cursorSurface); + image->draw(cursorSurface, Point(0,0)); auto oldCursor = cursor; cursor = SDL_CreateColorCursor(cursorSurface, pivotOffset.x, pivotOffset.y); diff --git a/client/renderSDL/CursorSoftware.cpp b/client/renderSDL/CursorSoftware.cpp index 78b9e1250..29b606b96 100644 --- a/client/renderSDL/CursorSoftware.cpp +++ b/client/renderSDL/CursorSoftware.cpp @@ -58,7 +58,7 @@ void CursorSoftware::updateTexture() CSDL_Ext::fillSurface(cursorSurface, CSDL_Ext::toSDL(Colors::TRANSPARENCY)); - cursorImage->draw(cursorSurface); + cursorImage->draw(cursorSurface, Point(0,0)); SDL_UpdateTexture(cursorTexture, nullptr, cursorSurface->pixels, cursorSurface->pitch); needUpdate = false; } diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index dc7126497..e0a5549ef 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -134,13 +134,13 @@ std::shared_ptr RenderHandler::loadImage(const AnimationPath & path, int auto it = animationFrames.find(locator); if (it != animationFrames.end()) - return it->second; + return it->second->createImageReference(); auto defFile = getAnimationFile(path); - auto result = std::make_shared(defFile.get(), frame, group); + auto result = std::make_shared(defFile.get(), frame, group); animationFrames[locator] = result; - return result; + return result->createImageReference(); } //std::vector> RenderHandler::loadImageGroup(const AnimationPath & path, int group) @@ -165,16 +165,16 @@ std::shared_ptr RenderHandler::loadImage(const ImagePath & path, EImageB { auto it = imageFiles.find(path); if (it != imageFiles.end()) - return it->second; + return it->second->createImageReference(); - auto result = std::make_shared(path, mode); + auto result = std::make_shared(path, mode); imageFiles[path] = result; - return result; + return result->createImageReference(); } std::shared_ptr RenderHandler::createImage(SDL_Surface * source) { - return std::make_shared(source, EImageBlitMode::ALPHA); + return std::make_shared(source, EImageBlitMode::ALPHA)->createImageReference(); } std::shared_ptr RenderHandler::loadAnimation(const AnimationPath & path) diff --git a/client/renderSDL/RenderHandler.h b/client/renderSDL/RenderHandler.h index 9ac90b926..455a4722e 100644 --- a/client/renderSDL/RenderHandler.h +++ b/client/renderSDL/RenderHandler.h @@ -12,6 +12,7 @@ #include "../render/IRenderHandler.h" class CDefFile; +class IConstImage; class RenderHandler : public IRenderHandler { @@ -35,8 +36,8 @@ class RenderHandler : public IRenderHandler std::map> animationFiles; std::map animationLayouts; - std::map> imageFiles; - std::map> animationFrames; + std::map> imageFiles; + std::map> animationFrames; std::shared_ptr getAnimationFile(const AnimationPath & path); const AnimationLayoutMap & getAnimationLayout(const AnimationPath & path); diff --git a/client/renderSDL/SDLImage.cpp b/client/renderSDL/SDLImage.cpp index 7f2f11f07..871337310 100644 --- a/client/renderSDL/SDLImage.cpp +++ b/client/renderSDL/SDLImage.cpp @@ -1,4 +1,4 @@ -/* +/* * SDLImage.cpp, part of VCMI engine * * Authors: listed in file AUTHORS in main folder @@ -32,7 +32,7 @@ int IImage::height() const return dimensions().y; } -SDLImage::SDLImage(CDefFile * data, size_t frame, size_t group) +SDLImageConst::SDLImageConst(CDefFile * data, size_t frame, size_t group) : surf(nullptr), margins(0, 0), fullSize(0, 0), @@ -42,10 +42,9 @@ SDLImage::SDLImage(CDefFile * data, size_t frame, size_t group) data->loadFrame(frame, group, loader); savePalette(); - setBlitMode(EImageBlitMode::ALPHA); } -SDLImage::SDLImage(SDL_Surface * from, EImageBlitMode mode) +SDLImageConst::SDLImageConst(SDL_Surface * from, EImageBlitMode mode) : surf(nullptr), margins(0, 0), fullSize(0, 0), @@ -56,14 +55,13 @@ SDLImage::SDLImage(SDL_Surface * from, EImageBlitMode mode) return; savePalette(); - setBlitMode(mode); surf->refcount++; fullSize.x = surf->w; fullSize.y = surf->h; } -SDLImage::SDLImage(const ImagePath & filename, EImageBlitMode mode) +SDLImageConst::SDLImageConst(const ImagePath & filename, EImageBlitMode mode) : surf(nullptr), margins(0, 0), fullSize(0, 0), @@ -79,22 +77,13 @@ SDLImage::SDLImage(const ImagePath & filename, EImageBlitMode mode) else { savePalette(); - setBlitMode(mode); fullSize.x = surf->w; fullSize.y = surf->h; } } -void SDLImage::draw(SDL_Surface *where, int posX, int posY, const Rect *src) const -{ - if(!surf) - return; - Rect destRect(posX, posY, surf->w, surf->h); - draw(where, &destRect, src); -} - -void SDLImage::draw(SDL_Surface* where, const Rect * dest, const Rect* src) const +void SDLImageConst::draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, uint8_t alpha, EImageBlitMode mode) const { if (!surf) return; @@ -118,14 +107,19 @@ void SDLImage::draw(SDL_Surface* where, const Rect * dest, const Rect* src) cons else destShift = margins; - if(dest) - destShift += dest->topLeft(); + destShift += dest; - uint8_t perSurfaceAlpha; - if (SDL_GetSurfaceAlphaMod(surf, &perSurfaceAlpha) != 0) - logGlobal->error("SDL_GetSurfaceAlphaMod faied! %s", SDL_GetError()); + SDL_SetSurfaceAlphaMod(surf, alpha); - if(surf->format->BitsPerPixel == 8 && perSurfaceAlpha == SDL_ALPHA_OPAQUE && blitMode == EImageBlitMode::ALPHA) + if (mode == EImageBlitMode::OPAQUE) + SDL_SetSurfaceBlendMode(surf, SDL_BLENDMODE_NONE); + else + SDL_SetSurfaceBlendMode(surf, SDL_BLENDMODE_BLEND); + + if (palette && surf->format->palette) + SDL_SetSurfacePalette(surf, palette); + + if(surf->format->BitsPerPixel == 8 && alpha == SDL_ALPHA_OPAQUE && mode == EImageBlitMode::ALPHA) { CSDL_Ext::blit8bppAlphaTo24bpp(surf, sourceRect, where, destShift); } @@ -135,10 +129,18 @@ void SDLImage::draw(SDL_Surface* where, const Rect * dest, const Rect* src) cons } } -std::shared_ptr SDLImage::scaleFast(const Point & size) const +const SDL_Palette * SDLImageConst::getPalette() const { - float scaleX = float(size.x) / width(); - float scaleY = float(size.y) / height(); + if (originalPalette == nullptr) + throw std::runtime_error("Palette not found!"); + + return originalPalette; +} + +std::shared_ptr SDLImageConst::scaleFast(const Point & size) const +{ + float scaleX = float(size.x) / dimensions().x; + float scaleY = float(size.y) / dimensions().y; auto scaled = CSDL_Ext::scaleSurfaceFast(surf, (int)(surf->w * scaleX), (int)(surf->h * scaleY)); @@ -149,7 +151,7 @@ std::shared_ptr SDLImage::scaleFast(const Point & size) const else CSDL_Ext::setDefaultColorKey(scaled);//just in case - auto * ret = new SDLImage(scaled, EImageBlitMode::ALPHA); + auto ret = std::make_shared(scaled, EImageBlitMode::ALPHA); ret->fullSize.x = (int) round((float)fullSize.x * scaleX); ret->fullSize.y = (int) round((float)fullSize.y * scaleY); @@ -160,45 +162,26 @@ std::shared_ptr SDLImage::scaleFast(const Point & size) const // erase our own reference SDL_FreeSurface(scaled); - return std::shared_ptr(ret); + return ret; } -void SDLImage::exportBitmap(const boost::filesystem::path& path) const +void SDLImageConst::exportBitmap(const boost::filesystem::path& path) const { SDL_SaveBMP(surf, path.string().c_str()); } -void SDLImage::playerColored(PlayerColor player) +void SDLImageIndexed::playerColored(PlayerColor player) { - if (!surf) - return; - graphics->blueToPlayersAdv(surf, player); + graphics->setPlayerPalette(currentPalette, player); } -void SDLImage::setAlpha(uint8_t value) -{ - CSDL_Ext::setAlpha (surf, value); - if (value != 255) - SDL_SetSurfaceBlendMode(surf, SDL_BLENDMODE_BLEND); -} - -void SDLImage::setBlitMode(EImageBlitMode mode) -{ - blitMode = mode; - - if (blitMode != EImageBlitMode::OPAQUE && surf->format->Amask != 0) - SDL_SetSurfaceBlendMode(surf, SDL_BLENDMODE_BLEND); - else - SDL_SetSurfaceBlendMode(surf, SDL_BLENDMODE_NONE); -} - -void SDLImage::setFlagColor(PlayerColor player) +void SDLImageIndexed::setFlagColor(PlayerColor player) { if(player.isValidPlayer() || player==PlayerColor::NEUTRAL) - CSDL_Ext::setPlayerColor(surf, player); + graphics->setPlayerFlagColor(currentPalette, player); } -bool SDLImage::isTransparent(const Point & coords) const +bool SDLImageConst::isTransparent(const Point & coords) const { if (surf) return CSDL_Ext::isTransparent(surf, coords.x, coords.y); @@ -206,33 +189,45 @@ bool SDLImage::isTransparent(const Point & coords) const return true; } -Point SDLImage::dimensions() const +Point SDLImageConst::dimensions() const { return fullSize; } -void SDLImage::horizontalFlip() +std::shared_ptr SDLImageConst::createImageReference() { - margins.y = fullSize.y - surf->h - margins.y; - - //todo: modify in-place - SDL_Surface * flipped = CSDL_Ext::horizontalFlip(surf); - SDL_FreeSurface(surf); - surf = flipped; + if (surf->format->palette) + return std::make_shared(shared_from_this()); + else + return std::make_shared(shared_from_this()); } -void SDLImage::verticalFlip() +std::shared_ptr SDLImageConst::horizontalFlip() const { - margins.x = fullSize.x - surf->w - margins.x; + SDL_Surface * flipped = CSDL_Ext::horizontalFlip(surf); + auto ret = std::make_shared(flipped, EImageBlitMode::ALPHA); + ret->fullSize = fullSize; + ret->margins.x = margins.x; + ret->margins.y = fullSize.y - surf->h - margins.y; + ret->fullSize = fullSize; - //todo: modify in-place + return ret; +} + +std::shared_ptr SDLImageConst::verticalFlip() const +{ SDL_Surface * flipped = CSDL_Ext::verticalFlip(surf); - SDL_FreeSurface(surf); - surf = flipped; + auto ret = std::make_shared(flipped, EImageBlitMode::ALPHA); + ret->fullSize = fullSize; + ret->margins.x = fullSize.x - surf->w - margins.x; + ret->margins.y = margins.y; + ret->fullSize = fullSize; + + return ret; } // Keep the original palette, in order to do color switching operation -void SDLImage::savePalette() +void SDLImageConst::savePalette() { // For some images that don't have palette, skip this if(surf->format->palette == nullptr) @@ -244,53 +239,132 @@ void SDLImage::savePalette() SDL_SetPaletteColors(originalPalette, surf->format->palette->colors, 0, DEFAULT_PALETTE_COLORS); } -void SDLImage::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) +void SDLImageIndexed::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) { - if(surf->format->palette) - { - std::vector shifterColors(colorsToMove); + const SDL_Palette * originalPalette = image->getPalette(); - for(uint32_t i=0; icolors[firstColorID + i]; - } - CSDL_Ext::setColors(surf, shifterColors.data(), firstColorID, colorsToMove); - } + std::vector shifterColors(colorsToMove); + + for(uint32_t i=0; icolors[firstColorID + i]; + + SDL_SetPaletteColors(currentPalette, shifterColors.data(), firstColorID, colorsToMove); } -void SDLImage::adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) +void SDLImageIndexed::adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) { - if(originalPalette == nullptr) - return; - - SDL_Palette* palette = surf->format->palette; + const SDL_Palette * originalPalette = image->getPalette(); // Note: here we skip first colors in the palette that are predefined in H3 images - for(int i = 0; i < palette->ncolors; i++) + for(int i = 0; i < currentPalette->ncolors; i++) { if(i < std::numeric_limits::digits && ((colorsToSkipMask >> i) & 1) == 1) continue; - palette->colors[i] = CSDL_Ext::toSDL(shifter.shiftColor(CSDL_Ext::fromSDL(originalPalette->colors[i]))); + currentPalette->colors[i] = CSDL_Ext::toSDL(shifter.shiftColor(CSDL_Ext::fromSDL(originalPalette->colors[i]))); } } -void SDLImage::setSpecialPalette(const IImage::SpecialPalette & specialPalette, uint32_t colorsToSkipMask) +SDLImageIndexed::SDLImageIndexed(const std::shared_ptr & image) + :SDLImageBase::SDLImageBase(image) { - if(surf->format->palette) - { - size_t last = std::min(specialPalette.size(), surf->format->palette->ncolors); + auto originalPalette = image->getPalette(); - for (size_t i = 0; i < last; ++i) - { - if(i < std::numeric_limits::digits && ((colorsToSkipMask >> i) & 1) == 1) - surf->format->palette->colors[i] = CSDL_Ext::toSDL(specialPalette[i]); - } + currentPalette = SDL_AllocPalette(originalPalette->ncolors); + SDL_SetPaletteColors(currentPalette, originalPalette->colors, 0, originalPalette->ncolors); +} + +SDLImageIndexed::~SDLImageIndexed() +{ + SDL_FreePalette(currentPalette); +} + +void SDLImageIndexed::setSpecialPallete(const IImage::SpecialPalette & specialPalette, uint32_t colorsToSkipMask) +{ + size_t last = std::min(specialPalette.size(), currentPalette->ncolors); + + for (size_t i = 0; i < last; ++i) + { + if(i < std::numeric_limits::digits && ((colorsToSkipMask >> i) & 1) == 1) + currentPalette->colors[i] = CSDL_Ext::toSDL(specialPalette[i]); } } -SDLImage::~SDLImage() +SDLImageConst::~SDLImageConst() { SDL_FreeSurface(surf); SDL_FreePalette(originalPalette); } + +SDLImageBase::SDLImageBase(const std::shared_ptr & image) + :image(image) + , alphaValue(SDL_ALPHA_OPAQUE) + , blitMode(EImageBlitMode::ALPHA) +{} + +void SDLImageRGB::draw(SDL_Surface * where, const Point & pos, const Rect * src) const +{ + image->draw(where, nullptr, pos, src, alphaValue, blitMode); +} + +void SDLImageIndexed::draw(SDL_Surface * where, const Point & pos, const Rect * src) const +{ + image->draw(where, currentPalette, pos, src, alphaValue, blitMode); +} + +void SDLImageBase::scaleFast(const Point & size) +{ + image = image->scaleFast(size); +} + +void SDLImageBase::exportBitmap(const boost::filesystem::path & path) const +{ + image->exportBitmap(path); +} + +bool SDLImageBase::isTransparent(const Point & coords) const +{ + return image->isTransparent(coords); +} + +Point SDLImageBase::dimensions() const +{ + return image->dimensions(); +} + +void SDLImageBase::setAlpha(uint8_t value) +{ + alphaValue = value; +} + +void SDLImageBase::setBlitMode(EImageBlitMode mode) +{ + blitMode = mode; +} + +void SDLImageBase::horizontalFlip() +{ + image = image->horizontalFlip(); +} + +void SDLImageBase::verticalFlip() +{ + image = image->verticalFlip(); +} + +void SDLImageRGB::setSpecialPallete(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask) +{} + +void SDLImageRGB::playerColored(PlayerColor player) +{} + +void SDLImageRGB::setFlagColor(PlayerColor player) +{} + +void SDLImageRGB::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) +{} + +void SDLImageRGB::adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) +{} + + diff --git a/client/renderSDL/SDLImage.h b/client/renderSDL/SDLImage.h index 4243606ae..020d550a7 100644 --- a/client/renderSDL/SDLImage.h +++ b/client/renderSDL/SDLImage.h @@ -24,9 +24,9 @@ struct SDL_Palette; /* * Wrapper around SDL_Surface */ -class SDLImage : public IImage +class SDLImageConst final : public IConstImage, public std::enable_shared_from_this, boost::noncopyable { - const static int DEFAULT_PALETTE_COLORS = 256; + static constexpr int DEFAULT_PALETTE_COLORS = 256; //Surface without empty borders SDL_Surface * surf; @@ -37,40 +37,78 @@ class SDLImage : public IImage //total size including borders Point fullSize; - EImageBlitMode blitMode; - -public: - //Load image from def file - SDLImage(CDefFile *data, size_t frame, size_t group=0); - //Load from bitmap file - SDLImage(const ImagePath & filename, EImageBlitMode blitMode); - - //Create using existing surface, extraRef will increase refcount on SDL_Surface - SDLImage(SDL_Surface * from, EImageBlitMode blitMode); - ~SDLImage(); - // Keep the original palette, in order to do color switching operation void savePalette(); - void draw(SDL_Surface * where, int posX=0, int posY=0, const Rect *src=nullptr) const override; - void draw(SDL_Surface * where, const Rect * dest, const Rect * src) const override; - std::shared_ptr scaleFast(const Point & size) const override; +public: + //Load image from def file + SDLImageConst(CDefFile *data, size_t frame, size_t group=0); + //Load from bitmap file + SDLImageConst(const ImagePath & filename, EImageBlitMode blitMode); + //Create using existing surface, extraRef will increase refcount on SDL_Surface + SDLImageConst(SDL_Surface * from, EImageBlitMode blitMode); + ~SDLImageConst(); + + void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, uint8_t alpha, EImageBlitMode mode) const; + void exportBitmap(const boost::filesystem::path & path) const override; - void playerColored(PlayerColor player) override; - void setFlagColor(PlayerColor player) override; - bool isTransparent(const Point & coords) const override; Point dimensions() const override; + bool isTransparent(const Point & coords) const override; + std::shared_ptr createImageReference() override; + std::shared_ptr horizontalFlip() const; + std::shared_ptr verticalFlip() const; + std::shared_ptr scaleFast(const Point & size) const; - void horizontalFlip() override; - void verticalFlip() override; - - void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override; - void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override; - - void setAlpha(uint8_t value) override; - void setBlitMode(EImageBlitMode mode) override; - - void setSpecialPalette(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask) override; + const SDL_Palette * getPalette() const; friend class SDLImageLoader; }; + +class SDLImageBase : public IImage, boost::noncopyable +{ +protected: + std::shared_ptr image; + + uint8_t alphaValue; + EImageBlitMode blitMode; + +public: + SDLImageBase(const std::shared_ptr & image); + + void scaleFast(const Point & size) override; + void exportBitmap(const boost::filesystem::path & path) const override; + bool isTransparent(const Point & coords) const override; + Point dimensions() const override; + void setAlpha(uint8_t value) override; + void setBlitMode(EImageBlitMode mode) override; + void horizontalFlip() override; + void verticalFlip() override; +}; + +class SDLImageIndexed final : public SDLImageBase +{ + SDL_Palette * currentPalette = nullptr; + +public: + SDLImageIndexed(const std::shared_ptr & image); + ~SDLImageIndexed(); + + void draw(SDL_Surface * where, const Point & pos, const Rect * src) const override; + void setSpecialPallete(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask) override; + void playerColored(PlayerColor player) override; + void setFlagColor(PlayerColor player) override; + void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override; + void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override; +}; + +class SDLImageRGB final : public SDLImageBase +{ +public: + using SDLImageBase::SDLImageBase; + + void draw(SDL_Surface * where, const Point & pos, const Rect * src) const override; + void playerColored(PlayerColor player) override; + void setFlagColor(PlayerColor player) override; + void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override; + void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override; +}; diff --git a/client/renderSDL/SDLImageLoader.cpp b/client/renderSDL/SDLImageLoader.cpp index 30e288f38..bb2a2c354 100644 --- a/client/renderSDL/SDLImageLoader.cpp +++ b/client/renderSDL/SDLImageLoader.cpp @@ -17,7 +17,7 @@ #include -SDLImageLoader::SDLImageLoader(SDLImage * Img): +SDLImageLoader::SDLImageLoader(SDLImageConst * Img): image(Img), lineStart(nullptr), position(nullptr) @@ -32,8 +32,8 @@ void SDLImageLoader::init(Point SpriteSize, Point Margins, Point FullSize, SDL_C image->fullSize = FullSize; //Prepare surface - SDL_Palette * p = SDL_AllocPalette(SDLImage::DEFAULT_PALETTE_COLORS); - SDL_SetPaletteColors(p, pal, 0, SDLImage::DEFAULT_PALETTE_COLORS); + SDL_Palette * p = SDL_AllocPalette(SDLImageConst::DEFAULT_PALETTE_COLORS); + SDL_SetPaletteColors(p, pal, 0, SDLImageConst::DEFAULT_PALETTE_COLORS); SDL_SetSurfacePalette(image->surf, p); SDL_FreePalette(p); diff --git a/client/renderSDL/SDLImageLoader.h b/client/renderSDL/SDLImageLoader.h index 6e4cca11b..ea9f58414 100644 --- a/client/renderSDL/SDLImageLoader.h +++ b/client/renderSDL/SDLImageLoader.h @@ -11,9 +11,11 @@ #include "../render/IImageLoader.h" +class SDLImageConst; + class SDLImageLoader : public IImageLoader { - SDLImage * image; + SDLImageConst * image; ui8 * lineStart; ui8 * position; public: @@ -25,7 +27,7 @@ public: //init image with these sizes and palette void init(Point SpriteSize, Point Margins, Point FullSize, SDL_Color *pal); - SDLImageLoader(SDLImage * Img); + SDLImageLoader(SDLImageConst * Img); ~SDLImageLoader(); }; diff --git a/client/renderSDL/SDL_Extensions.cpp b/client/renderSDL/SDL_Extensions.cpp index 9000473c4..d5426089a 100644 --- a/client/renderSDL/SDL_Extensions.cpp +++ b/client/renderSDL/SDL_Extensions.cpp @@ -524,23 +524,6 @@ void CSDL_Ext::drawBorder( SDL_Surface * sur, const Rect &r, const SDL_Color &co drawBorder(sur, r.x, r.y, r.w, r.h, color, depth); } -void CSDL_Ext::setPlayerColor(SDL_Surface * sur, const PlayerColor & player) -{ - if(player==PlayerColor::UNFLAGGABLE) - return; - if(sur->format->BitsPerPixel==8) - { - ColorRGBA color = (player == PlayerColor::NEUTRAL - ? graphics->neutralColor - : graphics->playerColors[player.getNum()]); - - SDL_Color colorSDL = toSDL(color); - CSDL_Ext::setColors(sur, &colorSDL, 5, 1); - } - else - logGlobal->warn("Warning, setPlayerColor called on not 8bpp surface!"); -} - CSDL_Ext::TColorPutter CSDL_Ext::getPutterFor(SDL_Surface * const &dest, int incrementing) { #define CASE_BPP(BytesPerPixel) \ diff --git a/client/renderSDL/SDL_Extensions.h b/client/renderSDL/SDL_Extensions.h index c85458c09..8e39ee8ac 100644 --- a/client/renderSDL/SDL_Extensions.h +++ b/client/renderSDL/SDL_Extensions.h @@ -86,7 +86,6 @@ using TColorPutterAlpha = void (*)(uint8_t *&, const uint8_t &, const uint8_t &, void drawBorder(SDL_Surface * sur, int x, int y, int w, int h, const SDL_Color & color, int depth = 1); void drawBorder(SDL_Surface * sur, const Rect & r, const SDL_Color & color, int depth = 1); - void setPlayerColor(SDL_Surface * sur, const PlayerColor & player); //sets correct color of flags; -1 for neutral SDL_Surface * newSurface(int w, int h, SDL_Surface * mod); //creates new surface, with flags/format same as in surface given SDL_Surface * newSurface(int w, int h); //creates new surface, with flags/format same as in screen surface diff --git a/client/widgets/Images.cpp b/client/widgets/Images.cpp index bd179eb1f..35f110685 100644 --- a/client/widgets/Images.cpp +++ b/client/widgets/Images.cpp @@ -100,7 +100,7 @@ void CPicture::setAlpha(uint8_t value) void CPicture::scaleTo(Point size) { - bg = bg->scaleFast(size); + bg->scaleFast(size); pos.w = bg->width(); pos.h = bg->height(); @@ -262,12 +262,9 @@ void CAnimImage::showAll(Canvas & to) if(auto img = anim->getImage(targetFrame, group)) { if(isScaled()) - { - auto scaled = img->scaleFast(scaledSize); - to.draw(scaled, pos.topLeft()); - } - else - to.draw(img, pos.topLeft()); + img->scaleFast(scaledSize); + + to.draw(img, pos.topLeft()); } } } diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 4420a065c..7aaba6b9a 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -885,9 +885,9 @@ void CCastleBuildings::enterCastleGate() availableTowns.push_back(t->id.getNum());//add to the list if(settings["general"]["enableUiEnhancements"].Bool()) { - std::shared_ptr a = GH.renderHandler().loadAnimation(AnimationPath::builtin("ITPA")); - a->preload(); - images.push_back(a->getImage(t->town->clientInfo.icons[t->hasFort()][false] + 2)->scaleFast(Point(35, 23))); + auto image = GH.renderHandler().loadImage(AnimationPath::builtin("ITPA"), t->town->clientInfo.icons[t->hasFort()][false] + 2, 0); + image->scaleFast(Point(35, 23)); + images.push_back(image); } } } diff --git a/client/windows/CWindowWithArtifacts.cpp b/client/windows/CWindowWithArtifacts.cpp index 86ca47e11..3b3da1ba4 100644 --- a/client/windows/CWindowWithArtifacts.cpp +++ b/client/windows/CWindowWithArtifacts.cpp @@ -237,9 +237,10 @@ void CWindowWithArtifacts::setCursorAnimation(const CArtifactInstance & artInst) if(artInst.isScroll() && settings["general"]["enableUiEnhancements"].Bool()) { assert(artInst.getScrollSpellID().num >= 0); - const auto animation = GH.renderHandler().loadAnimation(AnimationPath::builtin("spellscr")); - animation->load(artInst.getScrollSpellID().num); - CCS->curh->dragAndDropCursor(animation->getImage(artInst.getScrollSpellID().num)->scaleFast(Point(44, 34))); + auto image = GH.renderHandler().loadImage(AnimationPath::builtin("spellscr"), artInst.getScrollSpellID().num, 0); + image->scaleFast(Point(44,34)); + + CCS->curh->dragAndDropCursor(image); } else { From a8a330f39f63ff66b6fe90f85f0307155698c6cd Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 4 Jun 2024 16:09:25 +0000 Subject: [PATCH 077/100] Remove unused code and no longer needed caching --- client/battle/BattleInterfaceClasses.cpp | 15 +++----------- client/battle/BattleInterfaceClasses.h | 3 --- client/lobby/SelectionTab.cpp | 13 +++++------- client/lobby/SelectionTab.h | 2 +- client/render/CAnimation.cpp | 1 - client/render/IImage.h | 5 ++--- client/renderSDL/RenderHandler.cpp | 26 ------------------------ client/renderSDL/SDLImage.cpp | 4 ++-- client/renderSDL/SDLImage.h | 2 -- client/renderSDL/SDLImageLoader.cpp | 4 ++-- client/renderSDL/SDLImageLoader.h | 2 ++ client/widgets/Images.cpp | 20 +++++++++--------- client/widgets/Images.h | 2 +- client/windows/CHeroWindow.cpp | 3 +-- 14 files changed, 29 insertions(+), 73 deletions(-) diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index dcbf427a8..2fc460542 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -934,9 +934,6 @@ StackQueue::StackQueue(bool Embedded, BattleInterface & owner) pos.h = 49; pos.x += parent->pos.w/2 - pos.w/2; pos.y += queueSmallOutside ? -queueSmallOutsideYOffset : 10; - - icons = GH.renderHandler().loadAnimation(AnimationPath::builtin("CPRSMALL")); - stateIcons = GH.renderHandler().loadAnimation(AnimationPath::builtin("VCMI/BATTLEQUEUE/STATESSMALL")); } else { @@ -946,13 +943,7 @@ StackQueue::StackQueue(bool Embedded, BattleInterface & owner) pos.y -= pos.h; background = std::make_shared(ImagePath::builtin("DIBOXBCK"), Rect(0, 0, pos.w, pos.h)); - - icons = GH.renderHandler().loadAnimation(AnimationPath::builtin("TWCRPORT")); - stateIcons = GH.renderHandler().loadAnimation(AnimationPath::builtin("VCMI/BATTLEQUEUE/STATESSMALL")); - //TODO: where use big icons? - //stateIcons = GH.renderHandler().loadAnimation("VCMI/BATTLEQUEUE/STATESBIG"); } - stateIcons->preload(); stackBoxes.resize(queueSize); for (int i = 0; i < stackBoxes.size(); i++) @@ -1021,13 +1012,13 @@ StackQueue::StackBox::StackBox(StackQueue * owner): if(owner->embedded) { - icon = std::make_shared(owner->icons, 0, 0, 5, 2); + icon = std::make_shared(AnimationPath::builtin("CPRSMALL"), 0, 0, 5, 2); amount = std::make_shared(pos.w/2, pos.h - 7, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE); roundRect = std::make_shared(Rect(0, 0, 2, 48), ColorRGBA(0, 0, 0, 255), ColorRGBA(0, 255, 0, 255)); } else { - icon = std::make_shared(owner->icons, 0, 0, 9, 1); + icon = std::make_shared(AnimationPath::builtin("TWCRPORT"), 0, 0, 9, 1); amount = std::make_shared(pos.w/2, pos.h - 8, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE); roundRect = std::make_shared(Rect(0, 0, 15, 18), ColorRGBA(0, 0, 0, 255), ColorRGBA(241, 216, 120, 255)); round = std::make_shared(4, 2, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE); @@ -1035,7 +1026,7 @@ StackQueue::StackBox::StackBox(StackQueue * owner): int icon_x = pos.w - 17; int icon_y = pos.h - 18; - stateIcon = std::make_shared(owner->stateIcons, 0, 0, icon_x, icon_y); + stateIcon = std::make_shared(AnimationPath::builtin("VCMI/BATTLEQUEUE/STATESSMALL"), 0, 0, icon_x, icon_y); stateIcon->visible = false; } roundRect->disable(); diff --git a/client/battle/BattleInterfaceClasses.h b/client/battle/BattleInterfaceClasses.h index d40f04ea4..634c66567 100644 --- a/client/battle/BattleInterfaceClasses.h +++ b/client/battle/BattleInterfaceClasses.h @@ -274,9 +274,6 @@ class StackQueue : public CIntObject std::vector> stackBoxes; BattleInterface & owner; - std::shared_ptr icons; - std::shared_ptr stateIcons; - int32_t getSiegeShooterIconID(); public: const bool embedded; diff --git a/client/lobby/SelectionTab.cpp b/client/lobby/SelectionTab.cpp index fdf0807f9..490b0c633 100644 --- a/client/lobby/SelectionTab.cpp +++ b/client/lobby/SelectionTab.cpp @@ -227,11 +227,8 @@ SelectionTab::SelectionTab(ESelectionScreen Type) buttonsSortBy.push_back(sortByDate); } - iconsMapFormats = GH.renderHandler().loadAnimation(AnimationPath::builtin("SCSELC.DEF")); - iconsVictoryCondition = GH.renderHandler().loadAnimation(AnimationPath::builtin("SCNRVICT.DEF")); - iconsLossCondition = GH.renderHandler().loadAnimation(AnimationPath::builtin("SCNRLOSS.DEF")); for(int i = 0; i < positionsToShow; i++) - listItems.push_back(std::make_shared(Point(30, 129 + i * 25), iconsMapFormats, iconsVictoryCondition, iconsLossCondition)); + listItems.push_back(std::make_shared(Point(30, 129 + i * 25))); labelTabTitle = std::make_shared(205, 28, FONT_MEDIUM, ETextAlignment::CENTER, Colors::YELLOW, tabTitle); slider = std::make_shared(Point(372, 86 + (enableUiEnhancements ? 30 : 0)), (tabType != ESelectionScreen::saveGame ? 480 : 430) - (enableUiEnhancements ? 30 : 0), std::bind(&SelectionTab::sliderMove, this, _1), positionsToShow, (int)curItems.size(), 0, Orientation::VERTICAL, CSlider::BLUE); @@ -883,7 +880,7 @@ std::unordered_set SelectionTab::getFiles(std::string dirURI, ERes return ret; } -SelectionTab::ListItem::ListItem(Point position, std::shared_ptr iconsFormats, std::shared_ptr iconsVictory, std::shared_ptr iconsLoss) +SelectionTab::ListItem::ListItem(Point position) : CIntObject(LCLICK, position) { OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; @@ -898,9 +895,9 @@ SelectionTab::ListItem::ListItem(Point position, std::shared_ptr ico labelMapSizeLetter->setAutoRedraw(false); // FIXME: This -12 should not be needed, but for some reason CAnimImage displaced otherwise iconFolder = std::make_shared(ImagePath::builtin("lobby/iconFolder.png"), -8, -12); - iconFormat = std::make_shared(iconsFormats, 0, 0, 59, -12); - iconVictoryCondition = std::make_shared(iconsVictory, 0, 0, 277, -12); - iconLossCondition = std::make_shared(iconsLoss, 0, 0, 310, -12); + iconFormat = std::make_shared(AnimationPath::builtin("SCSELC.DEF"), 0, 0, 59, -12); + iconVictoryCondition = std::make_shared(AnimationPath::builtin("SCNRVICT.DEF"), 0, 0, 277, -12); + iconLossCondition = std::make_shared(AnimationPath::builtin("SCNRLOSS.DEF"), 0, 0, 310, -12); } void SelectionTab::ListItem::updateItem(std::shared_ptr info, bool selected) diff --git a/client/lobby/SelectionTab.h b/client/lobby/SelectionTab.h index 4d9278ca0..67e39047c 100644 --- a/client/lobby/SelectionTab.h +++ b/client/lobby/SelectionTab.h @@ -59,7 +59,7 @@ class SelectionTab : public CIntObject std::shared_ptr pictureEmptyLine; std::shared_ptr labelName; - ListItem(Point position, std::shared_ptr iconsFormats, std::shared_ptr iconsVictory, std::shared_ptr iconsLoss); + ListItem(Point position); void updateItem(std::shared_ptr info = {}, bool selected = false); }; std::vector> listItems; diff --git a/client/render/CAnimation.cpp b/client/render/CAnimation.cpp index 7ab38a01f..f18ea99e5 100644 --- a/client/render/CAnimation.cpp +++ b/client/render/CAnimation.cpp @@ -25,7 +25,6 @@ bool CAnimation::loadFrame(size_t frame, size_t group) return false; } - if(auto image = getImage(frame, group, false)) { return true; diff --git a/client/render/IImage.h b/client/render/IImage.h index bed254188..076380695 100644 --- a/client/render/IImage.h +++ b/client/render/IImage.h @@ -47,17 +47,16 @@ public: //draws image on surface "where" at position virtual void draw(SDL_Surface * where, const Point & pos, const Rect * src = nullptr) const = 0; - //virtual void draw(SDL_Surface * where, const Rect * dest, const Rect * src) const = 0; virtual void scaleFast(const Point & size) = 0; virtual void exportBitmap(const boost::filesystem::path & path) const = 0; //Change palette to specific player - virtual void playerColored(PlayerColor player)=0; + virtual void playerColored(PlayerColor player) = 0; //set special color for flag - virtual void setFlagColor(PlayerColor player)=0; + virtual void setFlagColor(PlayerColor player) = 0; //test transparency of specific pixel virtual bool isTransparent(const Point & coords) const = 0; diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index e0a5549ef..26e0fc407 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -115,19 +115,6 @@ const RenderHandler::AnimationLayoutMap & RenderHandler::getAnimationLayout(cons return animationLayouts[path]; } -//std::shared_ptr RenderHandler::getJsonFile(const JsonPath & path) -//{ -// auto it = jsonFiles.find(path); -// -// if (it != jsonFiles.end()) -// return it->second; -// -// auto result = std::make_shared(path); -// -// jsonFiles[path] = result; -// return result; -//} - std::shared_ptr RenderHandler::loadImage(const AnimationPath & path, int frame, int group) { AnimationLocator locator{path, frame, group}; @@ -143,19 +130,6 @@ std::shared_ptr RenderHandler::loadImage(const AnimationPath & path, int return result->createImageReference(); } -//std::vector> RenderHandler::loadImageGroup(const AnimationPath & path, int group) -//{ -// const auto defFile = getAnimationFile(path); -// -// size_t groupSize = defFile->getEntries().at(group); -// -// std::vector> result; -// for (size_t i = 0; i < groupSize; ++i) -// loadImage(path, i, group); -// -// return result; -//} - std::shared_ptr RenderHandler::loadImage(const ImagePath & path) { return loadImage(path, EImageBlitMode::ALPHA); diff --git a/client/renderSDL/SDLImage.cpp b/client/renderSDL/SDLImage.cpp index 871337310..13bce6bc3 100644 --- a/client/renderSDL/SDLImage.cpp +++ b/client/renderSDL/SDLImage.cpp @@ -234,9 +234,9 @@ void SDLImageConst::savePalette() return; if(originalPalette == nullptr) - originalPalette = SDL_AllocPalette(DEFAULT_PALETTE_COLORS); + originalPalette = SDL_AllocPalette(surf->format->palette->ncolors); - SDL_SetPaletteColors(originalPalette, surf->format->palette->colors, 0, DEFAULT_PALETTE_COLORS); + SDL_SetPaletteColors(originalPalette, surf->format->palette->colors, 0, surf->format->palette->ncolors); } void SDLImageIndexed::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) diff --git a/client/renderSDL/SDLImage.h b/client/renderSDL/SDLImage.h index 020d550a7..fae1d9fe3 100644 --- a/client/renderSDL/SDLImage.h +++ b/client/renderSDL/SDLImage.h @@ -26,8 +26,6 @@ struct SDL_Palette; */ class SDLImageConst final : public IConstImage, public std::enable_shared_from_this, boost::noncopyable { - static constexpr int DEFAULT_PALETTE_COLORS = 256; - //Surface without empty borders SDL_Surface * surf; diff --git a/client/renderSDL/SDLImageLoader.cpp b/client/renderSDL/SDLImageLoader.cpp index bb2a2c354..87b87c6ec 100644 --- a/client/renderSDL/SDLImageLoader.cpp +++ b/client/renderSDL/SDLImageLoader.cpp @@ -32,8 +32,8 @@ void SDLImageLoader::init(Point SpriteSize, Point Margins, Point FullSize, SDL_C image->fullSize = FullSize; //Prepare surface - SDL_Palette * p = SDL_AllocPalette(SDLImageConst::DEFAULT_PALETTE_COLORS); - SDL_SetPaletteColors(p, pal, 0, SDLImageConst::DEFAULT_PALETTE_COLORS); + SDL_Palette * p = SDL_AllocPalette(DEFAULT_PALETTE_COLORS); + SDL_SetPaletteColors(p, pal, 0, DEFAULT_PALETTE_COLORS); SDL_SetSurfacePalette(image->surf, p); SDL_FreePalette(p); diff --git a/client/renderSDL/SDLImageLoader.h b/client/renderSDL/SDLImageLoader.h index ea9f58414..ada6c0230 100644 --- a/client/renderSDL/SDLImageLoader.h +++ b/client/renderSDL/SDLImageLoader.h @@ -15,6 +15,8 @@ class SDLImageConst; class SDLImageLoader : public IImageLoader { + static constexpr int DEFAULT_PALETTE_COLORS = 256; + SDLImageConst * image; ui8 * lineStart; ui8 * position; diff --git a/client/widgets/Images.cpp b/client/widgets/Images.cpp index 35f110685..4ddc4b7c2 100644 --- a/client/widgets/Images.cpp +++ b/client/widgets/Images.cpp @@ -180,16 +180,16 @@ CAnimImage::CAnimImage(const AnimationPath & name, size_t Frame, size_t Group, i init(); } -CAnimImage::CAnimImage(std::shared_ptr Anim, size_t Frame, size_t Group, int x, int y, ui8 Flags): - anim(Anim), - frame(Frame), - group(Group), - flags(Flags) -{ - pos.x += x; - pos.y += y; - init(); -} +//CAnimImage::CAnimImage(std::shared_ptr Anim, size_t Frame, size_t Group, int x, int y, ui8 Flags): +// anim(Anim), +// frame(Frame), +// group(Group), +// flags(Flags) +//{ +// pos.x += x; +// pos.y += y; +// init(); +//} CAnimImage::CAnimImage(const AnimationPath & name, size_t Frame, Rect targetPos, size_t Group, ui8 Flags): anim(GH.renderHandler().loadAnimation(name)), diff --git a/client/widgets/Images.h b/client/widgets/Images.h index d059db52d..7926362ab 100644 --- a/client/widgets/Images.h +++ b/client/widgets/Images.h @@ -103,7 +103,7 @@ public: bool visible; CAnimImage(const AnimationPath & name, size_t Frame, size_t Group=0, int x=0, int y=0, ui8 Flags=0); - CAnimImage(std::shared_ptr Anim, size_t Frame, size_t Group=0, int x=0, int y=0, ui8 Flags=0); +// CAnimImage(std::shared_ptr Anim, size_t Frame, size_t Group=0, int x=0, int y=0, ui8 Flags=0); CAnimImage(const AnimationPath & name, size_t Frame, Rect targetPos, size_t Group=0, ui8 Flags=0); ~CAnimImage(); diff --git a/client/windows/CHeroWindow.cpp b/client/windows/CHeroWindow.cpp index 2c9133ac5..36701a63a 100644 --- a/client/windows/CHeroWindow.cpp +++ b/client/windows/CHeroWindow.cpp @@ -149,12 +149,11 @@ CHeroWindow::CHeroWindow(const CGHeroInstance * hero) expValue = std::make_shared(68, 252); manaValue = std::make_shared(211, 252); - auto secSkills = GH.renderHandler().loadAnimation(AnimationPath::builtin("SECSKILL")); for(int i = 0; i < std::min(hero->secSkills.size(), 8u); ++i) { Rect r = Rect(i%2 == 0 ? 18 : 162, 276 + 48 * (i/2), 136, 42); secSkillAreas.push_back(std::make_shared(r, ComponentType::SEC_SKILL)); - secSkillImages.push_back(std::make_shared(secSkills, 0, 0, r.x, r.y)); + secSkillImages.push_back(std::make_shared(AnimationPath::builtin("SECSKILL"), 0, 0, r.x, r.y)); int x = (i % 2) ? 212 : 68; int y = 280 + 48 * (i/2); From c1ebb6b0e3b5d119f901787809caf48d19bf914f Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 4 Jun 2024 16:24:23 +0000 Subject: [PATCH 078/100] Try to use lazy loading for animations --- client/render/CAnimation.cpp | 31 ++++++++++++------------------- client/render/CAnimation.h | 5 ++--- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/client/render/CAnimation.cpp b/client/render/CAnimation.cpp index f18ea99e5..b9b49d0c9 100644 --- a/client/render/CAnimation.cpp +++ b/client/render/CAnimation.cpp @@ -25,7 +25,7 @@ bool CAnimation::loadFrame(size_t frame, size_t group) return false; } - if(auto image = getImage(frame, group, false)) + if(auto image = getImageImpl(frame, group, false)) { return true; } @@ -122,18 +122,13 @@ void CAnimation::printError(size_t frame, size_t group, std::string type) const CAnimation::CAnimation(const AnimationPath & Name, std::map > layout): name(boost::starts_with(Name.getName(), "SPRITES") ? Name : Name.addPrefix("SPRITES/")), - source(layout), - preloaded(false) + source(layout) { if(source.empty()) logAnim->error("Animation %s failed to load", Name.getOriginalName()); } -CAnimation::CAnimation(): - preloaded(false) -{ -} - +CAnimation::CAnimation() = default; CAnimation::~CAnimation() = default; void CAnimation::duplicateImage(const size_t sourceGroup, const size_t sourceFrame, const size_t targetGroup) @@ -161,11 +156,6 @@ void CAnimation::duplicateImage(const size_t sourceGroup, const size_t sourceFra } source[targetGroup].push_back(clone); - - size_t index = source[targetGroup].size() - 1; - - if(preloaded) - load(index, targetGroup); } void CAnimation::setCustom(std::string filename, size_t frame, size_t group) @@ -176,7 +166,14 @@ void CAnimation::setCustom(std::string filename, size_t frame, size_t group) //FIXME: update image if already loaded } -std::shared_ptr CAnimation::getImage(size_t frame, size_t group, bool verbose) const +std::shared_ptr CAnimation::getImage(size_t frame, size_t group, bool verbose) +{ + if (!loadFrame(frame, group)) + return nullptr; + return getImageImpl(frame, group, verbose); +} + +std::shared_ptr CAnimation::getImageImpl(size_t frame, size_t group, bool verbose) { auto groupIter = images.find(group); if (groupIter != images.end()) @@ -207,11 +204,7 @@ void CAnimation::unload() void CAnimation::preload() { - if(!preloaded) - { - preloaded = true; - load(); - } + // TODO: remove } void CAnimation::loadGroup(size_t group) diff --git a/client/render/CAnimation.h b/client/render/CAnimation.h index 2b2abfe86..9b31c6624 100644 --- a/client/render/CAnimation.h +++ b/client/render/CAnimation.h @@ -33,8 +33,6 @@ private: //animation file name AnimationPath name; - bool preloaded; - //loader, will be called by load(), require opened def file for loading from it. Returns true if image is loaded bool loadFrame(size_t frame, size_t group); @@ -44,6 +42,7 @@ private: //to get rid of copy-pasting error message :] void printError(size_t frame, size_t group, std::string type) const; + std::shared_ptr getImageImpl(size_t frame, size_t group=0, bool verbose=true); public: CAnimation(const AnimationPath & Name, std::map > layout); CAnimation(); @@ -56,7 +55,7 @@ public: //add custom surface to the selected position. void setCustom(std::string filename, size_t frame, size_t group=0); - std::shared_ptr getImage(size_t frame, size_t group=0, bool verbose=true) const; + std::shared_ptr getImage(size_t frame, size_t group=0, bool verbose=true); void exportBitmaps(const boost::filesystem::path & path) const; From fcafe53da91fea0c9d7085db5f422e5ca269c94c Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 5 Jun 2024 13:24:36 +0000 Subject: [PATCH 079/100] Restored previously disabled University code, add interface to notify market-like objects --- client/CPlayerInterface.cpp | 16 +++++---- client/gui/CIntObject.h | 9 +++++ client/windows/CMarketWindow.cpp | 8 ++--- client/windows/CMarketWindow.h | 8 ++--- client/windows/GUIClasses.cpp | 59 +++++++++++++++++++------------- client/windows/GUIClasses.h | 13 ++++--- 6 files changed, 70 insertions(+), 43 deletions(-) diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index e48e4e58d..06c5aff05 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -442,18 +442,20 @@ void CPlayerInterface::heroPrimarySkillChanged(const CGHeroInstance * hero, Prim EVENT_HANDLER_CALLED_BY_CLIENT; if (which == PrimarySkill::EXPERIENCE) { - for(auto ctw : GH.windows().findWindows()) - ctw->updateHero(); + for(auto ctw : GH.windows().findWindows()) + ctw->updateExperience(); } else + { adventureInt->onHeroChanged(hero); + } } void CPlayerInterface::heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val) { EVENT_HANDLER_CALLED_BY_CLIENT; - for (auto cuw : GH.windows().findWindows()) - cuw->redraw(); + for (auto cuw : GH.windows().findWindows()) + cuw->updateSecondarySkills(); } void CPlayerInterface::heroManaPointsChanged(const CGHeroInstance * hero) @@ -472,8 +474,8 @@ void CPlayerInterface::heroMovePointsChanged(const CGHeroInstance * hero) void CPlayerInterface::receivedResource() { EVENT_HANDLER_CALLED_BY_CLIENT; - for (auto mw : GH.windows().findWindows()) - mw->updateResource(); + for (auto mw : GH.windows().findWindows()) + mw->updateResources(); GH.windows().totalRedraw(); } @@ -1675,7 +1677,7 @@ void CPlayerInterface::showHillFortWindow(const CGObjectInstance *object, const void CPlayerInterface::availableArtifactsChanged(const CGBlackMarket * bm) { EVENT_HANDLER_CALLED_BY_CLIENT; - for (auto cmw : GH.windows().findWindows()) + for (auto cmw : GH.windows().findWindows()) cmw->updateArtifacts(); } diff --git a/client/gui/CIntObject.h b/client/gui/CIntObject.h index 06b484376..a2afaea54 100644 --- a/client/gui/CIntObject.h +++ b/client/gui/CIntObject.h @@ -165,6 +165,15 @@ public: virtual void updateGarrisons() = 0; }; +class IMarketHolder +{ +public: + virtual void updateResources() {}; + virtual void updateExperience() {}; + virtual void updateSecondarySkills() {}; + virtual void updateArtifacts() {}; +}; + class ITownHolder { public: diff --git a/client/windows/CMarketWindow.cpp b/client/windows/CMarketWindow.cpp index de09e748b..2789f3ca8 100644 --- a/client/windows/CMarketWindow.cpp +++ b/client/windows/CMarketWindow.cpp @@ -70,12 +70,12 @@ void CMarketWindow::updateGarrisons() update(); } -void CMarketWindow::updateResource() +void CMarketWindow::updateResources() { update(); } -void CMarketWindow::updateHero() +void CMarketWindow::updateExperience() { update(); } @@ -251,7 +251,7 @@ void CMarketWindow::createAltarArtifacts(const IMarket * market, const CGHeroIns }; addSet(heroArts); initWidgetInternals(EMarketMode::ARTIFACT_EXP, CGI->generaltexth->zelp[568]); - updateHero(); + updateExperience(); quitButton->addCallback([altarArtifacts](){altarArtifacts->putBackArtifacts();}); } @@ -262,5 +262,5 @@ void CMarketWindow::createAltarCreatures(const IMarket * market, const CGHeroIns background = createBg(ImagePath::builtin("ALTARMON.bmp"), PLAYER_COLORED); marketWidget = std::make_shared(market, hero); initWidgetInternals(EMarketMode::CREATURE_EXP, CGI->generaltexth->zelp[568]); - updateHero(); + updateExperience(); } diff --git a/client/windows/CMarketWindow.h b/client/windows/CMarketWindow.h index 59403b84d..1f7d2179c 100644 --- a/client/windows/CMarketWindow.h +++ b/client/windows/CMarketWindow.h @@ -12,14 +12,14 @@ #include "../widgets/markets/CMarketBase.h" #include "CWindowWithArtifacts.h" -class CMarketWindow : public CStatusbarWindow, public CWindowWithArtifacts, public IGarrisonHolder +class CMarketWindow final : public CStatusbarWindow, public CWindowWithArtifacts, public IGarrisonHolder, public IMarketHolder { public: CMarketWindow(const IMarket * market, const CGHeroInstance * hero, const std::function & onWindowClosed, EMarketMode mode); - void updateResource(); - void updateArtifacts(); + void updateResources() override; + void updateArtifacts() override; void updateGarrisons() override; - void updateHero(); + void updateExperience() override; void update() override; void close() override; bool holdsGarrison(const CArmedInstance * army) override; diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index d9fd5683a..17f73ef91 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -891,26 +891,20 @@ CUniversityWindow::CItem::CItem(CUniversityWindow * _parent, int _ID, int X, int pos.x += X; pos.y += Y; - // TODO: restore - //bars->setCustom("UNIVRED", 0, 0); - //bars->setCustom("UNIVGOLD", 1, 0); - //bars->setCustom("UNIVGREN", 2, 0); - - topBar = std::make_shared(ImagePath::builtin("UNIVRED"), Point(-28, -22)); - bottomBar = std::make_shared(ImagePath::builtin("UNIVRED"), Point(-28, 48)); - icon = std::make_shared(AnimationPath::builtin("SECSKILL"), _ID * 3 + 3, 0); - name = std::make_shared(22, -13, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, CGI->skillh->getByIndex(ID)->getNameTranslated()); - level = std::make_shared(22, 57, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->levels[0]); - pos.h = icon->pos.h; pos.w = icon->pos.w; + + update(); } void CUniversityWindow::CItem::clickPressed(const Point & cursorPosition) { - if(state() == 2) + bool skillKnown = parent->hero->getSecSkillLevel(ID); + bool canLearn = parent->hero->canLearnSkill(ID); + + if (!skillKnown && canLearn) GH.windows().createAndPushWindow(parent, ID, LOCPLINT->cb->getResourceAmount(EGameResID::GOLD) >= 2000); } @@ -919,23 +913,37 @@ void CUniversityWindow::CItem::showPopupWindow(const Point & cursorPosition) CRClickPopup::createAndPush(CGI->skillh->getByIndex(ID)->getDescriptionTranslated(1), std::make_shared(ComponentType::SEC_SKILL, ID, 1)); } +void CUniversityWindow::CItem::update() +{ + bool skillKnown = parent->hero->getSecSkillLevel(ID); + bool canLearn = parent->hero->canLearnSkill(ID); + + ImagePath image; + + if (skillKnown) + image = ImagePath::builtin("UNIVGOLD"); + else if (canLearn) + image = ImagePath::builtin("UNIVGREN"); + else + image = ImagePath::builtin("UNIVRED"); + + OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); + topBar = std::make_shared(image, Point(-28, -22)); + bottomBar = std::make_shared(image, Point(-28, 48)); + + // needs to be on top of background bars + name = std::make_shared(22, -13, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, ID.toEntity(VLC)->getNameTranslated()); + level = std::make_shared(22, 57, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->levels[0]); +} + void CUniversityWindow::CItem::hover(bool on) { if(on) - GH.statusbar()->write(CGI->skillh->getByIndex(ID)->getNameTranslated()); + GH.statusbar()->write(ID.toEntity(VLC)->getNameTranslated()); else GH.statusbar()->clear(); } -int CUniversityWindow::CItem::state() -{ - if(parent->hero->getSecSkillLevel(SecondarySkill(ID)))//hero know this skill - return 1; - if(!parent->hero->canLearnSkill(SecondarySkill(ID)))//can't learn more skills - return 0; - return 2; -} - CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, const IMarket * _market, const std::function & onWindowClosed) : CWindowObject(PLAYER_COLORED, ImagePath::builtin("UNIVERS1")), hero(_hero), @@ -987,12 +995,17 @@ void CUniversityWindow::close() CStatusbarWindow::close(); } +void CUniversityWindow::updateSecondarySkills() +{ + for (auto const & item : items) + item->update(); +} + void CUniversityWindow::makeDeal(SecondarySkill skill) { LOCPLINT->cb->trade(market, EMarketMode::RESOURCE_SKILL, GameResID(GameResID::GOLD), skill, 1, hero); } - CUnivConfirmWindow::CUnivConfirmWindow(CUniversityWindow * owner_, SecondarySkill SKILL, bool available) : CWindowObject(PLAYER_COLORED, ImagePath::builtin("UNIVERS2.PCX")), owner(owner_) diff --git a/client/windows/GUIClasses.h b/client/windows/GUIClasses.h index 05c65c412..00bf4c8fa 100644 --- a/client/windows/GUIClasses.h +++ b/client/windows/GUIClasses.h @@ -358,9 +358,9 @@ public: CTransformerWindow(const IMarket * _market, const CGHeroInstance * _hero, const std::function & onWindowClosed); }; -class CUniversityWindow : public CStatusbarWindow +class CUniversityWindow final : public CStatusbarWindow, public IMarketHolder { - class CItem : public CIntObject + class CItem final : public CIntObject { std::shared_ptr icon; std::shared_ptr topBar; @@ -374,7 +374,7 @@ class CUniversityWindow : public CStatusbarWindow void clickPressed(const Point & cursorPosition) override; void showPopupWindow(const Point & cursorPosition) override; void hover(bool on) override; - int state();//0=can't learn, 1=learned, 2=can learn + void update(); CItem(CUniversityWindow * _parent, int _ID, int X, int Y); }; @@ -394,11 +394,14 @@ public: CUniversityWindow(const CGHeroInstance * _hero, const IMarket * _market, const std::function & onWindowClosed); void makeDeal(SecondarySkill skill); - void close(); + void close() override; + + // IMarketHolder impl + void updateSecondarySkills() override; }; /// Confirmation window for University -class CUnivConfirmWindow : public CStatusbarWindow +class CUnivConfirmWindow final : public CStatusbarWindow { std::shared_ptr clerkSpeech; std::shared_ptr name; From 9f31ec5326017432f0742eef78eef6f6650563bf Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 5 Jun 2024 14:31:37 +0000 Subject: [PATCH 080/100] Restore previously disabled icons registry --- client/CMT.cpp | 2 + client/render/IRenderHandler.h | 7 ++ client/renderSDL/RenderHandler.cpp | 128 +++++++++++++++-------------- client/renderSDL/RenderHandler.h | 11 ++- 4 files changed, 86 insertions(+), 62 deletions(-) diff --git a/client/CMT.cpp b/client/CMT.cpp index b9862ef91..8109081fa 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -28,6 +28,7 @@ #include "windows/CMessage.h" #include "windows/InfoWindows.h" #include "render/IScreenHandler.h" +#include "render/IRenderHandler.h" #include "render/Graphics.h" #include "../lib/CConfigHandler.h" @@ -347,6 +348,7 @@ int main(int argc, char * argv[]) { pomtime.getDiff(); graphics = new Graphics(); // should be before curh + GH.renderHandler().onLibraryLoadingFinished(CGI); CCS->curh = new CursorHandler(); logGlobal->info("Screen handler: %d ms", pomtime.getDiff()); diff --git a/client/render/IRenderHandler.h b/client/render/IRenderHandler.h index 805d3ef67..f6a8fa5a5 100644 --- a/client/render/IRenderHandler.h +++ b/client/render/IRenderHandler.h @@ -11,6 +11,10 @@ #include "../../lib/filesystem/ResourcePath.h" +VCMI_LIB_NAMESPACE_BEGIN +class Services; +VCMI_LIB_NAMESPACE_END + struct SDL_Surface; class IImage; @@ -22,6 +26,9 @@ class IRenderHandler : public boost::noncopyable public: virtual ~IRenderHandler() = default; + /// Must be called once VLC loading is over to initialize icons + virtual void onLibraryLoadingFinished(const Services * services) = 0; + /// Loads image using given path virtual std::shared_ptr loadImage(const ImagePath & path) = 0; virtual std::shared_ptr loadImage(const ImagePath & path, EImageBlitMode mode) = 0; diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index 26e0fc407..2b0db2da8 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -18,6 +18,15 @@ #include "../../lib/json/JsonUtils.h" #include "../../lib/filesystem/Filesystem.h" +#include +#include +#include +#include +#include +#include +#include +#include + std::shared_ptr RenderHandler::getAnimationFile(const AnimationPath & path) { AnimationPath actualPath = boost::starts_with(path.getName(), "SPRITES") ? path : path.addPrefix("SPRITES/"); @@ -78,16 +87,18 @@ void RenderHandler::initFromJson(AnimationLayoutMap & source, const JsonNode & c } } -const RenderHandler::AnimationLayoutMap & RenderHandler::getAnimationLayout(const AnimationPath & path) +RenderHandler::AnimationLayoutMap & RenderHandler::getAnimationLayout(const AnimationPath & path) { - auto it = animationLayouts.find(path); + AnimationPath actualPath = boost::starts_with(path.getName(), "SPRITES") ? path : path.addPrefix("SPRITES/"); + + auto it = animationLayouts.find(actualPath); if (it != animationLayouts.end()) return it->second; AnimationLayoutMap result; - auto defFile = getAnimationFile(path); + auto defFile = getAnimationFile(actualPath); if(defFile) { const std::map defEntries = defFile->getEntries(); @@ -96,13 +107,12 @@ const RenderHandler::AnimationLayoutMap & RenderHandler::getAnimationLayout(cons result[defEntry.first].resize(defEntry.second); } - auto jsonResource = path.toType(); - JsonPath actualJsonPath = boost::starts_with(jsonResource.getName(), "SPRITES") ? jsonResource : jsonResource.addPrefix("SPRITES/");; - auto configList = CResourceHandler::get()->getResourcesWithName(actualJsonPath); + auto jsonResource = actualPath.toType(); + auto configList = CResourceHandler::get()->getResourcesWithName(jsonResource); for(auto & loader : configList) { - auto stream = loader->load(actualJsonPath); + auto stream = loader->load(jsonResource); std::unique_ptr textData(new ui8[stream->getSize()]); stream->read(textData.get(), stream->getSize()); @@ -111,8 +121,8 @@ const RenderHandler::AnimationLayoutMap & RenderHandler::getAnimationLayout(cons initFromJson(result, config); } - animationLayouts[path] = result; - return animationLayouts[path]; + animationLayouts[actualPath] = result; + return animationLayouts[actualPath]; } std::shared_ptr RenderHandler::loadImage(const AnimationPath & path, int frame, int group) @@ -123,11 +133,24 @@ std::shared_ptr RenderHandler::loadImage(const AnimationPath & path, int if (it != animationFrames.end()) return it->second->createImageReference(); - auto defFile = getAnimationFile(path); - auto result = std::make_shared(defFile.get(), frame, group); + const auto & layout = getAnimationLayout(path); + if (!layout.count(group)) + return loadImage(ImagePath::builtin("DEFAULT")); - animationFrames[locator] = result; - return result->createImageReference(); + if (frame >= layout.at(group).size()) + return loadImage(ImagePath::builtin("DEFAULT")); + + const auto & config = layout.at(group).at(frame); + + if (config.isNull()) + { + auto defFile = getAnimationFile(path); + auto result = std::make_shared(defFile.get(), frame, group); + animationFrames[locator] = result; + return result->createImageReference(); + } + + return loadImage(ImagePath::builtin("DEFAULT")); } std::shared_ptr RenderHandler::loadImage(const ImagePath & path) @@ -161,51 +184,34 @@ std::shared_ptr RenderHandler::createAnimation() return std::make_shared(); } -// -//void Graphics::addImageListEntry(size_t index, size_t group, const std::string & listName, const std::string & imageName) -//{ -// if (!imageName.empty()) -// { -// JsonNode entry; -// if (group != 0) -// entry["group"].Integer() = group; -// entry["frame"].Integer() = index; -// entry["file"].String() = imageName; -// -// imageLists["SPRITES/" + listName]["images"].Vector().push_back(entry); -// } -//} -// -//void Graphics::addImageListEntries(const EntityService * service) -//{ -// auto cb = std::bind(&Graphics::addImageListEntry, this, _1, _2, _3, _4); -// -// auto loopCb = [&](const Entity * entity, bool & stop) -// { -// entity->registerIcons(cb); -// }; -// -// service->forEachBase(loopCb); -//} -// -//void Graphics::initializeImageLists() -//{ -// addImageListEntries(CGI->creatures()); -// addImageListEntries(CGI->heroTypes()); -// addImageListEntries(CGI->artifacts()); -// addImageListEntries(CGI->factions()); -// addImageListEntries(CGI->spells()); -// addImageListEntries(CGI->skills()); -//} -// -//std::shared_ptr Graphics::getAnimation(const AnimationPath & path) -//{ -// if (cachedAnimations.count(path) != 0) -// return cachedAnimations.at(path); -// -// auto newAnimation = GH.renderHandler().loadAnimation(path); -// -// newAnimation->preload(); -// cachedAnimations[path] = newAnimation; -// return newAnimation; -//} +void RenderHandler::addImageListEntries(const EntityService * service) +{ + service->forEachBase([this](const Entity * entity, bool & stop) + { + entity->registerIcons([this](size_t index, size_t group, const std::string & listName, const std::string & imageName) + { + if (imageName.empty()) + return; + + auto & layout = getAnimationLayout(AnimationPath::builtin("SPRITES/" + listName)); + + JsonNode entry; + entry["file"].String() = imageName; + + if (index >= layout[group].size()) + layout[group].resize(index + 1); + + layout[group][index] = entry; + }); + }); +} + +void RenderHandler::onLibraryLoadingFinished(const Services * services) +{ + addImageListEntries(services->creatures()); + addImageListEntries(services->heroTypes()); + addImageListEntries(services->artifacts()); + addImageListEntries(services->factions()); + addImageListEntries(services->spells()); + addImageListEntries(services->skills()); +} diff --git a/client/renderSDL/RenderHandler.h b/client/renderSDL/RenderHandler.h index 455a4722e..f7f4fe990 100644 --- a/client/renderSDL/RenderHandler.h +++ b/client/renderSDL/RenderHandler.h @@ -11,6 +11,10 @@ #include "../render/IRenderHandler.h" +VCMI_LIB_NAMESPACE_BEGIN +class EntityService; +VCMI_LIB_NAMESPACE_END + class CDefFile; class IConstImage; @@ -40,10 +44,15 @@ class RenderHandler : public IRenderHandler std::map> animationFrames; std::shared_ptr getAnimationFile(const AnimationPath & path); - const AnimationLayoutMap & getAnimationLayout(const AnimationPath & path); + AnimationLayoutMap & getAnimationLayout(const AnimationPath & path); void initFromJson(AnimationLayoutMap & layout, const JsonNode & config); + + void addImageListEntry(size_t index, size_t group, const std::string & listName, const std::string & imageName); + void addImageListEntries(const EntityService * service); public: + // IRenderHandler implementation + void onLibraryLoadingFinished(const Services * services) override; std::shared_ptr loadImage(const ImagePath & path) override; std::shared_ptr loadImage(const ImagePath & path, EImageBlitMode mode) override; From 920a39844bf3d68578353641cae297de18141f1d Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 5 Jun 2024 15:50:50 +0000 Subject: [PATCH 081/100] Unified naming of method to recolor UI element to current player scheme --- client/adventureMap/AdventureMapInterface.cpp | 4 ++-- client/adventureMap/AdventureMapWidget.cpp | 10 +++++----- client/adventureMap/AdventureMapWidget.h | 5 ++--- client/adventureMap/CResDataBar.cpp | 6 +++--- client/adventureMap/CResDataBar.h | 2 +- client/battle/BattleInterfaceClasses.cpp | 14 +++++++------- client/globalLobby/GlobalLobbyInviteWindow.cpp | 2 +- client/globalLobby/GlobalLobbyLoginWindow.cpp | 2 +- client/globalLobby/GlobalLobbyRoomWindow.cpp | 2 +- client/globalLobby/GlobalLobbyServerSetup.cpp | 2 +- client/gui/InterfaceObjectConfigurable.cpp | 4 ++-- client/lobby/CSelectionBase.cpp | 2 +- client/lobby/OptionsTab.cpp | 3 +-- client/widgets/Buttons.cpp | 6 +++--- client/widgets/Images.cpp | 6 +++--- client/widgets/Images.h | 6 +++--- client/widgets/MiscWidgets.cpp | 2 +- client/windows/CCastleInterface.cpp | 4 ++-- client/windows/CKingdomInterface.cpp | 4 ++-- client/windows/CWindowObject.cpp | 2 +- 20 files changed, 43 insertions(+), 45 deletions(-) diff --git a/client/adventureMap/AdventureMapInterface.cpp b/client/adventureMap/AdventureMapInterface.cpp index 2a4dbe0be..67e914831 100644 --- a/client/adventureMap/AdventureMapInterface.cpp +++ b/client/adventureMap/AdventureMapInterface.cpp @@ -399,7 +399,7 @@ void AdventureMapInterface::onCurrentPlayerChanged(PlayerColor playerID) return; currentPlayerID = playerID; - widget->setPlayer(playerID); + widget->setPlayerColor(playerID); } void AdventureMapInterface::onPlayerTurnStarted(PlayerColor playerID) @@ -914,7 +914,7 @@ void AdventureMapInterface::onScreenResize() widget = std::make_shared(shortcuts); widget->getMapView()->onViewMapActivated(); - widget->setPlayer(currentPlayerID); + widget->setPlayerColor(currentPlayerID); widget->updateActiveState(); widget->getMinimap()->update(); widget->getInfoBar()->showSelection(); diff --git a/client/adventureMap/AdventureMapWidget.cpp b/client/adventureMap/AdventureMapWidget.cpp index 32372c514..3b439a52e 100644 --- a/client/adventureMap/AdventureMapWidget.cpp +++ b/client/adventureMap/AdventureMapWidget.cpp @@ -335,7 +335,7 @@ std::shared_ptr AdventureMapWidget::getInfoBar() return infoBar; } -void AdventureMapWidget::setPlayer(const PlayerColor & player) +void AdventureMapWidget::setPlayerColor(const PlayerColor & player) { setPlayerChildren(this, player); } @@ -354,16 +354,16 @@ void AdventureMapWidget::setPlayerChildren(CIntObject * widget, const PlayerColo button->setPlayerColor(player); if(resDataBar) - resDataBar->colorize(player); + resDataBar->setPlayerColor(player); if(icon) - icon->setPlayer(player); + icon->setPlayerColor(player); if(container) setPlayerChildren(container, player); if (texture) - texture->playerColored(player); + texture->setPlayerColor(player); } redraw(); @@ -378,7 +378,7 @@ CAdventureMapIcon::CAdventureMapIcon(const Point & position, const AnimationPath image = std::make_shared(animation, index); } -void CAdventureMapIcon::setPlayer(const PlayerColor & player) +void CAdventureMapIcon::setPlayerColor(const PlayerColor & player) { image->setFrame(index + player.getNum() * iconsPerPlayer); } diff --git a/client/adventureMap/AdventureMapWidget.h b/client/adventureMap/AdventureMapWidget.h index 5ec2c34e1..494b25622 100644 --- a/client/adventureMap/AdventureMapWidget.h +++ b/client/adventureMap/AdventureMapWidget.h @@ -56,7 +56,6 @@ class AdventureMapWidget : public InterfaceObjectConfigurable std::shared_ptr buildStatusBar(const JsonNode & input); std::shared_ptr buildTexturePlayerColored(const JsonNode &); - void setPlayerChildren(CIntObject * widget, const PlayerColor & player); void updateActiveStateChildden(CIntObject * widget); public: @@ -68,7 +67,7 @@ public: std::shared_ptr getMapView(); std::shared_ptr getInfoBar(); - void setPlayer(const PlayerColor & player); + void setPlayerColor(const PlayerColor & player); void onMapViewMoved(const Rect & visibleArea, int mapLevel); void updateActiveState(); @@ -98,5 +97,5 @@ class CAdventureMapIcon : public CIntObject public: CAdventureMapIcon(const Point & position, const AnimationPath & image, size_t index, size_t iconsPerPlayer); - void setPlayer(const PlayerColor & player); + void setPlayerColor(const PlayerColor & player); }; diff --git a/client/adventureMap/CResDataBar.cpp b/client/adventureMap/CResDataBar.cpp index 7096b47bc..d15fe230f 100644 --- a/client/adventureMap/CResDataBar.cpp +++ b/client/adventureMap/CResDataBar.cpp @@ -31,7 +31,7 @@ CResDataBar::CResDataBar(const ImagePath & imageName, const Point & position) OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); background = std::make_shared(imageName, 0, 0); - background->colorize(LOCPLINT->playerID); + background->setPlayerColor(LOCPLINT->playerID); pos.w = background->pos.w; pos.h = background->pos.h; @@ -84,7 +84,7 @@ void CResDataBar::showAll(Canvas & to) to.drawText(pos.topLeft() + *datePosition, FONT_SMALL, Colors::WHITE, ETextAlignment::TOPLEFT, buildDateString()); } -void CResDataBar::colorize(PlayerColor player) +void CResDataBar::setPlayerColor(PlayerColor player) { - background->colorize(player); + background->setPlayerColor(player); } diff --git a/client/adventureMap/CResDataBar.h b/client/adventureMap/CResDataBar.h index 3bf294ad4..3363802c4 100644 --- a/client/adventureMap/CResDataBar.h +++ b/client/adventureMap/CResDataBar.h @@ -34,7 +34,7 @@ public: void setDatePosition(const Point & position); void setResourcePosition(const GameResID & resource, const Point & position); - void colorize(PlayerColor player); + void setPlayerColor(PlayerColor player); void showAll(Canvas & to) override; }; diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index 2fc460542..69df2c11e 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -506,7 +506,7 @@ HeroInfoBasicPanel::HeroInfoBasicPanel(const InfoAboutHero & hero, Point * posit { background = std::make_shared(ImagePath::builtin("CHRPOP")); background->getSurface()->setBlitMode(EImageBlitMode::OPAQUE); - background->colorize(hero.owner); + background->setPlayerColor(hero.owner); } initializeData(hero); @@ -574,10 +574,10 @@ StackInfoBasicPanel::StackInfoBasicPanel(const CStack * stack, bool initializeBa background = std::make_shared(ImagePath::builtin("CCRPOP")); background->pos.y += 37; background->getSurface()->setBlitMode(EImageBlitMode::OPAQUE); - background->colorize(stack->getOwner()); + background->setPlayerColor(stack->getOwner()); background2 = std::make_shared(ImagePath::builtin("CHRPOP")); background2->getSurface()->setBlitMode(EImageBlitMode::OPAQUE); - background2->colorize(stack->getOwner()); + background2->setPlayerColor(stack->getOwner()); } initializeData(stack); @@ -681,7 +681,7 @@ HeroInfoWindow::HeroInfoWindow(const InfoAboutHero & hero, Point * position) if (position != nullptr) moveTo(*position); - background->colorize(hero.owner); //maybe add this functionality to base class? + background->setPlayerColor(hero.owner); //maybe add this functionality to base class? content = std::make_shared(hero, nullptr, false); } @@ -692,7 +692,7 @@ BattleResultWindow::BattleResultWindow(const BattleResult & br, CPlayerInterface OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); background = std::make_shared(ImagePath::builtin("CPRESULT")); - background->colorize(owner.playerID); + background->setPlayerColor(owner.playerID); pos = center(background->pos); exit = std::make_shared(Point(384, 505), AnimationPath::builtin("iok6432.def"), std::make_pair("", ""), [&](){ bExitf();}, EShortcut::GLOBAL_ACCEPT); @@ -1037,7 +1037,7 @@ void StackQueue::StackBox::setUnit(const battle::Unit * unit, size_t turn, std:: if(unit) { boundUnitID = unit->unitId(); - background->colorize(unit->unitOwner()); + background->setPlayerColor(unit->unitOwner()); icon->visible = true; // temporary code for mod compatibility: @@ -1083,7 +1083,7 @@ void StackQueue::StackBox::setUnit(const battle::Unit * unit, size_t turn, std:: else { boundUnitID = std::nullopt; - background->colorize(PlayerColor::NEUTRAL); + background->setPlayerColor(PlayerColor::NEUTRAL); icon->visible = false; icon->setFrame(0); amount->setText(""); diff --git a/client/globalLobby/GlobalLobbyInviteWindow.cpp b/client/globalLobby/GlobalLobbyInviteWindow.cpp index ec436fea7..fb63d5b5f 100644 --- a/client/globalLobby/GlobalLobbyInviteWindow.cpp +++ b/client/globalLobby/GlobalLobbyInviteWindow.cpp @@ -79,7 +79,7 @@ GlobalLobbyInviteWindow::GlobalLobbyInviteWindow() pos.h = 420; filledBackground = std::make_shared(ImagePath::builtin("DiBoxBck"), Rect(0, 0, pos.w, pos.h)); - filledBackground->playerColored(PlayerColor(1)); + filledBackground->setPlayerColor(PlayerColor(1)); labelTitle = std::make_shared( pos.w / 2, 20, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, MetaString::createFromTextID("vcmi.lobby.invite.header").toString() ); diff --git a/client/globalLobby/GlobalLobbyLoginWindow.cpp b/client/globalLobby/GlobalLobbyLoginWindow.cpp index 48748548e..06718961e 100644 --- a/client/globalLobby/GlobalLobbyLoginWindow.cpp +++ b/client/globalLobby/GlobalLobbyLoginWindow.cpp @@ -70,7 +70,7 @@ GlobalLobbyLoginWindow::GlobalLobbyLoginWindow() else toggleMode->setSelected(1); - filledBackground->playerColored(PlayerColor(1)); + filledBackground->setPlayerColor(PlayerColor(1)); inputUsername->setCallback([this](const std::string & text) { this->buttonLogin->block(text.empty()); diff --git a/client/globalLobby/GlobalLobbyRoomWindow.cpp b/client/globalLobby/GlobalLobbyRoomWindow.cpp index 06b98d5bb..b1803f0a4 100644 --- a/client/globalLobby/GlobalLobbyRoomWindow.cpp +++ b/client/globalLobby/GlobalLobbyRoomWindow.cpp @@ -188,7 +188,7 @@ GlobalLobbyRoomWindow::GlobalLobbyRoomWindow(GlobalLobbyWindow * window, const s modListTitle = std::make_shared( 182, 59, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::YELLOW, MetaString::createFromTextID("vcmi.lobby.preview.mods").toString()); buttonJoin->block(!errorMessage.empty()); - filledBackground->playerColored(PlayerColor(1)); + filledBackground->setPlayerColor(PlayerColor(1)); center(); } diff --git a/client/globalLobby/GlobalLobbyServerSetup.cpp b/client/globalLobby/GlobalLobbyServerSetup.cpp index b23ff2a9a..ad55222d5 100644 --- a/client/globalLobby/GlobalLobbyServerSetup.cpp +++ b/client/globalLobby/GlobalLobbyServerSetup.cpp @@ -78,7 +78,7 @@ GlobalLobbyServerSetup::GlobalLobbyServerSetup() buttonCreate = std::make_shared(Point(10, 300), AnimationPath::builtin("MuBchck"), CButton::tooltip(), [this](){ onCreate(); }, EShortcut::GLOBAL_ACCEPT); buttonClose = std::make_shared(Point(210, 300), AnimationPath::builtin("MuBcanc"), CButton::tooltip(), [this](){ onClose(); }, EShortcut::GLOBAL_CANCEL); - filledBackground->playerColored(PlayerColor(1)); + filledBackground->setPlayerColor(PlayerColor(1)); updateDescription(); center(); diff --git a/client/gui/InterfaceObjectConfigurable.cpp b/client/gui/InterfaceObjectConfigurable.cpp index 5bcf45e17..bf9736cde 100644 --- a/client/gui/InterfaceObjectConfigurable.cpp +++ b/client/gui/InterfaceObjectConfigurable.cpp @@ -333,7 +333,7 @@ std::shared_ptr InterfaceObjectConfigurable::buildPicture(const JsonNo auto pic = std::make_shared(image, position.x, position.y); if ( config["playerColored"].Bool() && LOCPLINT) - pic->colorize(LOCPLINT->playerID); + pic->setPlayerColor(LOCPLINT->playerID); return pic; } @@ -571,7 +571,7 @@ std::shared_ptr InterfaceObjectConfigurable::buildTexture(const if(playerColor.isValidPlayer()) { auto result = std::make_shared(image, rect); - result->playerColored(playerColor); + result->setPlayerColor(playerColor); return result; } return std::make_shared(image, rect); diff --git a/client/lobby/CSelectionBase.cpp b/client/lobby/CSelectionBase.cpp index a330116d7..04a77fc7c 100644 --- a/client/lobby/CSelectionBase.cpp +++ b/client/lobby/CSelectionBase.cpp @@ -401,7 +401,7 @@ PvPBox::PvPBox(const Rect & rect) setRedrawParent(true); backgroundTexture = std::make_shared(ImagePath::builtin("DiBoxBck"), Rect(0, 0, rect.w, rect.h)); - backgroundTexture->playerColored(PlayerColor(1)); + backgroundTexture->setPlayerColor(PlayerColor(1)); backgroundBorder = std::make_shared(Rect(0, 0, rect.w, rect.h), ColorRGBA(0, 0, 0, 64), ColorRGBA(96, 96, 96, 255), 1); townSelector = std::make_shared(Point(5, 3)); diff --git a/client/lobby/OptionsTab.cpp b/client/lobby/OptionsTab.cpp index 49633bdd1..d8cd1f273 100644 --- a/client/lobby/OptionsTab.cpp +++ b/client/lobby/OptionsTab.cpp @@ -518,9 +518,8 @@ void OptionsTab::SelectionWindow::recreate(int sliderPos) int sliderWidth = ((amountLines > MAX_LINES) ? 16 : 0); pos = Rect(pos.x, pos.y, x + sliderWidth, y); - backgroundTexture = std::make_shared(ImagePath::builtin("DiBoxBck"), Rect(0, 0, pos.w - sliderWidth, pos.h)); - backgroundTexture->playerColored(PlayerColor(1)); + backgroundTexture->setPlayerColor(PlayerColor(1)); updateShadow(); if(type == SelType::TOWN) diff --git a/client/widgets/Buttons.cpp b/client/widgets/Buttons.cpp index a6225668c..2d786ac0a 100644 --- a/client/widgets/Buttons.cpp +++ b/client/widgets/Buttons.cpp @@ -99,7 +99,7 @@ void ButtonBase::setImage(const AnimationPath & defName, bool playerColoredButto pos = image->pos; if (playerColoredButton) - image->playerColored(LOCPLINT->playerID); + image->setPlayerColor(LOCPLINT->playerID); } const JsonNode & ButtonBase::getCurrentConfig() const @@ -134,7 +134,7 @@ void ButtonBase::setConfigurable(const JsonPath & jsonName, bool playerColoredBu pos = configurable->pos; if (playerColoredButton) - image->playerColored(LOCPLINT->playerID); + image->setPlayerColor(LOCPLINT->playerID); } void CButton::addHoverText(EButtonState state, const std::string & text) @@ -364,7 +364,7 @@ CButton::CButton(Point position, const AnimationPath &defName, const std::pairisPlayerColored()) - image->playerColored(player); + image->setPlayerColor(player); } void CButton::showAll(Canvas & to) diff --git a/client/widgets/Images.cpp b/client/widgets/Images.cpp index 4ddc4b7c2..24793b225 100644 --- a/client/widgets/Images.cpp +++ b/client/widgets/Images.cpp @@ -106,7 +106,7 @@ void CPicture::scaleTo(Point size) pos.h = bg->height(); } -void CPicture::colorize(PlayerColor player) +void CPicture::setPlayerColor(PlayerColor player) { bg->playerColored(player); } @@ -145,7 +145,7 @@ FilledTexturePlayerColored::FilledTexturePlayerColored(const ImagePath & imageNa { } -void FilledTexturePlayerColored::playerColored(PlayerColor player) +void FilledTexturePlayerColored::setPlayerColor(PlayerColor player) { // Color transform to make color of brown DIBOX.PCX texture match color of specified player std::array filters = { @@ -301,7 +301,7 @@ void CAnimImage::setFrame(size_t Frame, size_t Group) logGlobal->error("Error: accessing unavailable frame %d:%d in CAnimation!", Group, Frame); } -void CAnimImage::playerColored(PlayerColor currPlayer) +void CAnimImage::setPlayerColor(PlayerColor currPlayer) { player = currPlayer; anim->getImage(frame, group)->playerColored(*player); diff --git a/client/widgets/Images.h b/client/widgets/Images.h index 7926362ab..0f5e965e7 100644 --- a/client/widgets/Images.h +++ b/client/widgets/Images.h @@ -54,7 +54,7 @@ public: /// 0=transparent, 255=opaque void setAlpha(uint8_t value); void scaleTo(Point size); - void colorize(PlayerColor player); + void setPlayerColor(PlayerColor player); void show(Canvas & to) override; void showAll(Canvas & to) override; @@ -79,7 +79,7 @@ class FilledTexturePlayerColored : public CFilledTexture public: FilledTexturePlayerColored(const ImagePath & imageName, Rect position); - void playerColored(PlayerColor player); + void setPlayerColor(PlayerColor player); }; /// Class for displaying one image from animation @@ -114,7 +114,7 @@ public: void setFrame(size_t Frame, size_t Group=0); /// makes image player-colored to specific player - void playerColored(PlayerColor player); + void setPlayerColor(PlayerColor player); /// returns true if image has player-colored effect applied bool isPlayerColored() const; diff --git a/client/widgets/MiscWidgets.cpp b/client/widgets/MiscWidgets.cpp index d6d53038b..81f1da476 100644 --- a/client/widgets/MiscWidgets.cpp +++ b/client/widgets/MiscWidgets.cpp @@ -248,7 +248,7 @@ CMinorResDataBar::CMinorResDataBar() pos.y = 575; background = std::make_shared(ImagePath::builtin("KRESBAR.bmp")); - background->colorize(LOCPLINT->playerID); + background->setPlayerColor(LOCPLINT->playerID); pos.w = background->pos.w; pos.h = background->pos.h; diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 7aaba6b9a..1bc347278 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -272,7 +272,7 @@ CDwellingInfoBox::CDwellingInfoBox(int centerX, int centerY, const CGTownInstanc : CWindowObject(RCLICK_POPUP, ImagePath::builtin("CRTOINFO"), Point(centerX, centerY)) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); - background->colorize(Town->tempOwner); + background->setPlayerColor(Town->tempOwner); const CCreature * creature = Town->creatures.at(level).second.back().toCreature(); @@ -1245,7 +1245,7 @@ CCastleInterface::CCastleInterface(const CGTownInstance * Town, const CGTownInst builds = std::make_shared(town); panel = std::make_shared(ImagePath::builtin("TOWNSCRN"), 0, builds->pos.h); - panel->colorize(LOCPLINT->playerID); + panel->setPlayerColor(LOCPLINT->playerID); pos.w = panel->pos.w; pos.h = builds->pos.h + panel->pos.h; center(); diff --git a/client/windows/CKingdomInterface.cpp b/client/windows/CKingdomInterface.cpp index 7b57bd51e..46feda7ef 100644 --- a/client/windows/CKingdomInterface.cpp +++ b/client/windows/CKingdomInterface.cpp @@ -692,7 +692,7 @@ CKingdHeroList::CKingdHeroList(size_t maxSize, const CreateHeroItemFunctor & onC { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); title = std::make_shared(ImagePath::builtin("OVTITLE"),16,0); - title->colorize(LOCPLINT->playerID); + title->setPlayerColor(LOCPLINT->playerID); heroLabel = std::make_shared(150, 10, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->overview[0]); skillsLabel = std::make_shared(500, 10, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->overview[1]); @@ -736,7 +736,7 @@ CKingdTownList::CKingdTownList(size_t maxSize) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); title = std::make_shared(ImagePath::builtin("OVTITLE"), 16, 0); - title->colorize(LOCPLINT->playerID); + title->setPlayerColor(LOCPLINT->playerID); townLabel = std::make_shared(146, 10,FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->overview[3]); garrHeroLabel = std::make_shared(375, 10, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->overview[4]); visitHeroLabel = std::make_shared(608, 10, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->overview[5]); diff --git a/client/windows/CWindowObject.cpp b/client/windows/CWindowObject.cpp index 30f70432b..0fb45f589 100644 --- a/client/windows/CWindowObject.cpp +++ b/client/windows/CWindowObject.cpp @@ -93,7 +93,7 @@ std::shared_ptr CWindowObject::createBg(const ImagePath & imageName, b auto image = std::make_shared(imageName); image->getSurface()->setBlitMode(EImageBlitMode::OPAQUE); if(playerColored) - image->colorize(LOCPLINT->playerID); + image->setPlayerColor(LOCPLINT->playerID); return image; } From 0e407540ec97c9abb972fa06b75f237ee98351db Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 5 Jun 2024 16:30:53 +0000 Subject: [PATCH 082/100] Fixed player-coloring on adventure map --- client/adventureMap/AdventureMapWidget.cpp | 18 +++++++++++++----- client/adventureMap/AdventureMapWidget.h | 2 +- client/widgets/Images.cpp | 4 ++-- client/widgets/Images.h | 12 +++++++++++- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/client/adventureMap/AdventureMapWidget.cpp b/client/adventureMap/AdventureMapWidget.cpp index 3b439a52e..80b6399fc 100644 --- a/client/adventureMap/AdventureMapWidget.cpp +++ b/client/adventureMap/AdventureMapWidget.cpp @@ -59,7 +59,7 @@ AdventureMapWidget::AdventureMapWidget( std::shared_ptr s const JsonNode config(JsonPath::builtin("config/widgets/adventureMap.json")); for(const auto & entry : config["options"]["imagesPlayerColored"].Vector()) - playerColorerImages.push_back(ImagePath::fromJson(entry)); + playerColoredImages.push_back(ImagePath::fromJson(entry)); build(config); addUsedEvents(KEYBOARD); @@ -135,8 +135,12 @@ std::shared_ptr AdventureMapWidget::buildMapImage(const JsonNode & i { Rect targetArea = readTargetArea(input["area"]); Rect sourceArea = readSourceArea(input["sourceArea"], input["area"]); + ImagePath path = ImagePath::fromJson(input["image"]); - return std::make_shared(ImagePath::fromJson(input["image"]), targetArea, sourceArea); + if (vstd::contains(playerColoredImages, path)) + return std::make_shared(path, targetArea, sourceArea); + else + return std::make_shared(path, targetArea, sourceArea); } std::shared_ptr AdventureMapWidget::buildMapButton(const JsonNode & input) @@ -348,7 +352,8 @@ void AdventureMapWidget::setPlayerChildren(CIntObject * widget, const PlayerColo auto icon = dynamic_cast(entry); auto button = dynamic_cast(entry); auto resDataBar = dynamic_cast(entry); - auto texture = dynamic_cast(entry); + auto textureColored = dynamic_cast(entry); + auto textureIndexed = dynamic_cast(entry); if(button) button->setPlayerColor(player); @@ -362,8 +367,11 @@ void AdventureMapWidget::setPlayerChildren(CIntObject * widget, const PlayerColo if(container) setPlayerChildren(container, player); - if (texture) - texture->setPlayerColor(player); + if (textureColored) + textureColored->setPlayerColor(player); + + if (textureIndexed) + textureIndexed->setPlayerColor(player); } redraw(); diff --git a/client/adventureMap/AdventureMapWidget.h b/client/adventureMap/AdventureMapWidget.h index 494b25622..742e7d683 100644 --- a/client/adventureMap/AdventureMapWidget.h +++ b/client/adventureMap/AdventureMapWidget.h @@ -28,7 +28,7 @@ class AdventureMapWidget : public InterfaceObjectConfigurable std::vector subwidgetSizes; /// list of images on which player-colored palette will be applied - std::vector playerColorerImages; + std::vector playerColoredImages; /// Widgets that require access from adventure map std::shared_ptr heroList; diff --git a/client/widgets/Images.cpp b/client/widgets/Images.cpp index 24793b225..0f67251d7 100644 --- a/client/widgets/Images.cpp +++ b/client/widgets/Images.cpp @@ -140,9 +140,9 @@ void CFilledTexture::showAll(Canvas & to) } } -FilledTexturePlayerColored::FilledTexturePlayerColored(const ImagePath & imageName, Rect position) - : CFilledTexture(imageName, position) +void FilledTexturePlayerIndexed::setPlayerColor(PlayerColor player) { + texture->playerColored(player); } void FilledTexturePlayerColored::setPlayerColor(PlayerColor player) diff --git a/client/widgets/Images.h b/client/widgets/Images.h index 0f5e965e7..f7a53e718 100644 --- a/client/widgets/Images.h +++ b/client/widgets/Images.h @@ -74,10 +74,20 @@ public: void showAll(Canvas & to) override; }; +/// area filled with specific texture, colorized to player color if image is indexed +class FilledTexturePlayerIndexed : public CFilledTexture +{ +public: + using CFilledTexture::CFilledTexture; + + void setPlayerColor(PlayerColor player); +}; + +/// area filled with specific texture, with applied color filter to colorize it to specific player class FilledTexturePlayerColored : public CFilledTexture { public: - FilledTexturePlayerColored(const ImagePath & imageName, Rect position); + using CFilledTexture::CFilledTexture; void setPlayerColor(PlayerColor player); }; From 230add02e476086d0e7a7306d9c07782d9102d77 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 5 Jun 2024 17:04:39 +0000 Subject: [PATCH 083/100] Remove no longer used code --- client/ClientCommandManager.cpp | 1 - client/battle/BattleAnimationClasses.cpp | 2 - client/battle/BattleFieldController.cpp | 6 --- client/battle/BattleInterfaceClasses.cpp | 2 - client/battle/BattleObstacleController.cpp | 6 --- client/battle/BattleProjectileController.cpp | 1 - client/battle/CreatureAnimation.cpp | 4 -- client/gui/CursorHandler.cpp | 4 -- client/mapView/MapRenderer.cpp | 8 ---- client/mapView/MapViewCache.cpp | 1 - client/render/CAnimation.cpp | 45 -------------------- client/render/CAnimation.h | 13 ------ client/widgets/Images.cpp | 17 -------- client/widgets/Images.h | 1 - client/windows/CMessage.cpp | 1 - 15 files changed, 112 deletions(-) diff --git a/client/ClientCommandManager.cpp b/client/ClientCommandManager.cpp index b260ff4fb..dc0984e43 100644 --- a/client/ClientCommandManager.cpp +++ b/client/ClientCommandManager.cpp @@ -389,7 +389,6 @@ void ClientCommandManager::handleDef2bmpCommand(std::istringstream& singleWordBu std::string URI; singleWordBuffer >> URI; auto anim = GH.renderHandler().loadAnimation(AnimationPath::builtin(URI)); - anim->preload(); anim->exportBitmaps(VCMIDirs::get().userExtractedPath()); } diff --git a/client/battle/BattleAnimationClasses.cpp b/client/battle/BattleAnimationClasses.cpp index 19271b07c..8a98605f6 100644 --- a/client/battle/BattleAnimationClasses.cpp +++ b/client/battle/BattleAnimationClasses.cpp @@ -926,8 +926,6 @@ EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath & bool EffectAnimation::init() { - animation->preload(); - auto first = animation->getImage(0, 0, true); if(!first) { diff --git a/client/battle/BattleFieldController.cpp b/client/battle/BattleFieldController.cpp index 9df03805b..5767f1385 100644 --- a/client/battle/BattleFieldController.cpp +++ b/client/battle/BattleFieldController.cpp @@ -128,18 +128,12 @@ BattleFieldController::BattleFieldController(BattleInterface & owner): cellUnitMaxMovementHighlight = GH.renderHandler().loadImage(ImagePath::builtin("UnitMaxMovementHighlight.PNG"), EImageBlitMode::COLORKEY); 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")); - rangedFullDamageLimitImages->preload(); - shootingRangeLimitImages = GH.renderHandler().loadAnimation(AnimationPath::builtin("battle/rangeHighlights/rangeHighlightsRed.json")); - shootingRangeLimitImages->preload(); flipRangeLimitImagesIntoPositions(rangedFullDamageLimitImages); flipRangeLimitImagesIntoPositions(shootingRangeLimitImages); diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index 69df2c11e..e8af74746 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -398,7 +398,6 @@ BattleHero::BattleHero(const BattleInterface & owner, const CGHeroInstance * her animationPath = hero->type->heroClass->imageBattleMale; animation = GH.renderHandler().loadAnimation(animationPath); - animation->preload(); pos.w = 64; pos.h = 136; @@ -413,7 +412,6 @@ BattleHero::BattleHero(const BattleInterface & owner, const CGHeroInstance * her else flagAnimation = GH.renderHandler().loadAnimation(AnimationPath::builtin("CMFLAGL")); - flagAnimation->preload(); flagAnimation->playerColored(hero->tempOwner); switchToNextPhase(); diff --git a/client/battle/BattleObstacleController.cpp b/client/battle/BattleObstacleController.cpp index 796e5c7d9..80ecacdf6 100644 --- a/client/battle/BattleObstacleController.cpp +++ b/client/battle/BattleObstacleController.cpp @@ -55,13 +55,11 @@ void BattleObstacleController::loadObstacleImage(const CObstacleInstance & oi) auto animation = GH.renderHandler().createAnimation(); animation->setCustom(animationName.getName(), 0, 0); animationsCache[animationName] = animation; - animation->preload(); } else { auto animation = GH.renderHandler().loadAnimation(animationName); animationsCache[animationName] = animation; - animation->preload(); } } obstacleAnimations[oi.uniqueID] = animationsCache[animationName]; @@ -87,8 +85,6 @@ void BattleObstacleController::obstacleRemoved(const std::vectorpreload(); - auto first = animation->getImage(0, 0); if(!first) continue; @@ -115,8 +111,6 @@ void BattleObstacleController::obstaclePlaced(const std::vectorgetAppearAnimation()); - animation->preload(); - auto first = animation->getImage(0, 0); if(!first) continue; diff --git a/client/battle/BattleProjectileController.cpp b/client/battle/BattleProjectileController.cpp index 6c5854fc0..c81852446 100644 --- a/client/battle/BattleProjectileController.cpp +++ b/client/battle/BattleProjectileController.cpp @@ -193,7 +193,6 @@ void BattleProjectileController::initStackProjectile(const CStack * stack) std::shared_ptr BattleProjectileController::createProjectileImage(const AnimationPath & path ) { std::shared_ptr projectile = GH.renderHandler().loadAnimation(path); - projectile->preload(); if(projectile->size(1) != 0) logAnim->error("Expected empty group 1 in stack projectile"); diff --git a/client/battle/CreatureAnimation.cpp b/client/battle/CreatureAnimation.cpp index ae00aa515..8627ebfc7 100644 --- a/client/battle/CreatureAnimation.cpp +++ b/client/battle/CreatureAnimation.cpp @@ -202,10 +202,6 @@ CreatureAnimation::CreatureAnimation(const AnimationPath & name_, TSpeedControll forward = GH.renderHandler().loadAnimation(name_); reverse = GH.renderHandler().loadAnimation(name_); - //todo: optimize - forward->preload(); - reverse->preload(); - // if necessary, add one frame into vcmi-only group DEAD if(forward->size(size_t(ECreatureAnimType::DEAD)) == 0) { diff --git a/client/gui/CursorHandler.cpp b/client/gui/CursorHandler.cpp index 2052ea2c9..b09517344 100644 --- a/client/gui/CursorHandler.cpp +++ b/client/gui/CursorHandler.cpp @@ -53,9 +53,6 @@ CursorHandler::CursorHandler() GH.renderHandler().loadAnimation(AnimationPath::builtin("CRSPELL")) }; - for (auto & cursor : cursors) - cursor->preload(); - set(Cursor::Map::POINTER); showType = dynamic_cast(cursor.get()) ? Cursor::ShowType::SOFTWARE : Cursor::ShowType::HARDWARE; } @@ -105,7 +102,6 @@ void CursorHandler::dragAndDropCursor(std::shared_ptr image) void CursorHandler::dragAndDropCursor (const AnimationPath & path, size_t index) { auto anim = GH.renderHandler().loadAnimation(path); - anim->load(index); dragAndDropCursor(anim->getImage(index)); } diff --git a/client/mapView/MapRenderer.cpp b/client/mapView/MapRenderer.cpp index 8c7509f80..7b97ec7fd 100644 --- a/client/mapView/MapRenderer.cpp +++ b/client/mapView/MapRenderer.cpp @@ -104,10 +104,7 @@ void MapTileStorage::load(size_t index, const AnimationPath & filename, EImageBl for(auto & entry : terrainAnimations) { if (!filename.empty()) - { entry = GH.renderHandler().loadAnimation(filename); - entry->preload(); - } else entry = GH.renderHandler().createAnimation(); @@ -253,7 +250,6 @@ uint8_t MapRendererRoad::checksum(IMapRendererContext & context, const int3 & co MapRendererBorder::MapRendererBorder() { animation = GH.renderHandler().loadAnimation(AnimationPath::builtin("EDG")); - animation->preload(); } size_t MapRendererBorder::getIndexForTile(IMapRendererContext & context, const int3 & tile) @@ -315,9 +311,7 @@ uint8_t MapRendererBorder::checksum(IMapRendererContext & context, const int3 & MapRendererFow::MapRendererFow() { fogOfWarFullHide = GH.renderHandler().loadAnimation(AnimationPath::builtin("TSHRC")); - fogOfWarFullHide->preload(); fogOfWarPartialHide = GH.renderHandler().loadAnimation(AnimationPath::builtin("TSHRE")); - fogOfWarPartialHide->preload(); for(size_t i = 0; i < fogOfWarFullHide->size(); ++i) fogOfWarFullHide->getImage(i)->setBlitMode(EImageBlitMode::OPAQUE); @@ -407,7 +401,6 @@ std::shared_ptr MapRendererObjects::getAnimation(const AnimationPath auto ret = GH.renderHandler().loadAnimation(filename); animations[filename] = ret; - ret->preload(); if(generateMovementGroups) { @@ -629,7 +622,6 @@ uint8_t MapRendererOverlay::checksum(IMapRendererContext & context, const int3 & MapRendererPath::MapRendererPath() : pathNodes(GH.renderHandler().loadAnimation(AnimationPath::builtin("ADAG"))) { - pathNodes->preload(); } size_t MapRendererPath::selectImageReachability(bool reachableToday, size_t imageIndex) diff --git a/client/mapView/MapViewCache.cpp b/client/mapView/MapViewCache.cpp index 2bd21ce8d..8534e8a6c 100644 --- a/client/mapView/MapViewCache.cpp +++ b/client/mapView/MapViewCache.cpp @@ -36,7 +36,6 @@ MapViewCache::MapViewCache(const std::shared_ptr & model) , terrain(new Canvas(model->getCacheDimensionsPixels())) , terrainTransition(new Canvas(model->getPixelsVisibleDimensions())) { - iconsStorage->preload(); for(size_t i = 0; i < iconsStorage->size(); ++i) iconsStorage->getImage(i)->setBlitMode(EImageBlitMode::COLORKEY); diff --git a/client/render/CAnimation.cpp b/client/render/CAnimation.cpp index b9b49d0c9..70f486886 100644 --- a/client/render/CAnimation.cpp +++ b/client/render/CAnimation.cpp @@ -187,50 +187,6 @@ std::shared_ptr CAnimation::getImageImpl(size_t frame, size_t group, boo return nullptr; } -void CAnimation::load() -{ - for (auto & elem : source) - for (size_t image=0; image < elem.second.size(); image++) - loadFrame(image, elem.first); -} - -void CAnimation::unload() -{ - for (auto & elem : source) - for (size_t image=0; image < elem.second.size(); image++) - unloadFrame(image, elem.first); - -} - -void CAnimation::preload() -{ - // TODO: remove -} - -void CAnimation::loadGroup(size_t group) -{ - if (vstd::contains(source, group)) - for (size_t image=0; image < source[group].size(); image++) - loadFrame(image, group); -} - -void CAnimation::unloadGroup(size_t group) -{ - if (vstd::contains(source, group)) - for (size_t image=0; image < source[group].size(); image++) - unloadFrame(image, group); -} - -void CAnimation::load(size_t frame, size_t group) -{ - loadFrame(frame, group); -} - -void CAnimation::unload(size_t frame, size_t group) -{ - unloadFrame(frame, group); -} - size_t CAnimation::size(size_t group) const { auto iter = source.find(group); @@ -270,4 +226,3 @@ void CAnimation::createFlippedGroup(const size_t sourceGroup, const size_t targe image->verticalFlip(); } } - diff --git a/client/render/CAnimation.h b/client/render/CAnimation.h index 9b31c6624..ce78b0e4d 100644 --- a/client/render/CAnimation.h +++ b/client/render/CAnimation.h @@ -59,19 +59,6 @@ public: void exportBitmaps(const boost::filesystem::path & path) const; - //all available frames - void load (); - void unload(); - void preload(); - - //all frames from group - void loadGroup (size_t group); - void unloadGroup(size_t group); - - //single image - void load (size_t frame, size_t group=0); - void unload(size_t frame, size_t group=0); - //total count of frames in group (including not loaded) size_t size(size_t group=0) const; diff --git a/client/widgets/Images.cpp b/client/widgets/Images.cpp index 0f67251d7..14151c5fe 100644 --- a/client/widgets/Images.cpp +++ b/client/widgets/Images.cpp @@ -232,10 +232,6 @@ void CAnimImage::setSizeFromImage(const IImage &img) void CAnimImage::init() { visible = true; - anim->load(frame, group); - if (flags & CShowableAnim::BASE) - anim->load(0,group); - auto img = anim->getImage(frame, group); if (img) setSizeFromImage(*img); @@ -287,7 +283,6 @@ void CAnimImage::setFrame(size_t Frame, size_t Group) return; if (anim->size(Group) > Frame) { - anim->load(Frame, Group); frame = Frame; group = Group; if(auto img = anim->getImage(frame, group)) @@ -326,7 +321,6 @@ CShowableAnim::CShowableAnim(int x, int y, const AnimationPath & name, ui8 Flags yOffset(0), alpha(alpha) { - anim->loadGroup(group); last = anim->size(group); auto image = anim->getImage(0, group); @@ -341,11 +335,6 @@ CShowableAnim::CShowableAnim(int x, int y, const AnimationPath & name, ui8 Flags addUsedEvents(TIME); } -CShowableAnim::~CShowableAnim() -{ - anim->unloadGroup(group); -} - void CShowableAnim::setAlpha(ui32 alphaValue) { alpha = std::min(alphaValue, 255); @@ -361,9 +350,6 @@ bool CShowableAnim::set(size_t Group, size_t from, size_t to) if (max < from || max == 0) return false; - anim->unloadGroup(group); - anim->loadGroup(Group); - group = Group; frame = first = from; last = max; @@ -377,9 +363,6 @@ bool CShowableAnim::set(size_t Group) return false; if (group != Group) { - anim->unloadGroup(group); - anim->loadGroup(Group); - first = 0; group = Group; last = anim->size(Group); diff --git a/client/widgets/Images.h b/client/widgets/Images.h index f7a53e718..49c6d40fe 100644 --- a/client/widgets/Images.h +++ b/client/widgets/Images.h @@ -178,7 +178,6 @@ public: void setAlpha(ui32 alphaValue); CShowableAnim(int x, int y, const AnimationPath & name, ui8 flags, ui32 frameTime, size_t Group=0, uint8_t alpha = UINT8_MAX); - ~CShowableAnim(); //set animation to group or part of group bool set(size_t Group); diff --git a/client/windows/CMessage.cpp b/client/windows/CMessage.cpp index 68351ba08..71d8a056e 100644 --- a/client/windows/CMessage.cpp +++ b/client/windows/CMessage.cpp @@ -42,7 +42,6 @@ void CMessage::init() for(int i = 0; i < PlayerColor::PLAYER_LIMIT_I; i++) { dialogBorders[i] = GH.renderHandler().loadAnimation(AnimationPath::builtin("DIALGBOX")); - dialogBorders[i]->preload(); for(int j = 0; j < dialogBorders[i]->size(0); j++) { From b850b6339fc6c8c9439fcdc80da5ff4a6ab02fc6 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 5 Jun 2024 20:16:12 +0000 Subject: [PATCH 084/100] Flipped images are now tracked by RenderHandler --- client/battle/BattleFieldController.cpp | 34 +++---- client/mapView/MapRenderer.cpp | 3 +- client/render/CAnimation.cpp | 109 +++++++++++++--------- client/render/CAnimation.h | 2 + client/render/IImage.h | 7 +- client/render/IRenderHandler.h | 1 + client/renderSDL/RenderHandler.cpp | 119 +++++++++++++++++++----- client/renderSDL/RenderHandler.h | 27 +++--- client/renderSDL/SDLImage.cpp | 14 +-- client/renderSDL/SDLImage.h | 6 +- 10 files changed, 208 insertions(+), 114 deletions(-) diff --git a/client/battle/BattleFieldController.cpp b/client/battle/BattleFieldController.cpp index 5767f1385..aa197002a 100644 --- a/client/battle/BattleFieldController.cpp +++ b/client/battle/BattleFieldController.cpp @@ -552,25 +552,21 @@ void BattleFieldController::calculateRangeLimitAndHighlightImages(uint8_t distan void BattleFieldController::flipRangeLimitImagesIntoPositions(std::shared_ptr images) { - images->getImage(hexEdgeMaskToFrameIndex[HexMasks::topRight])->verticalFlip(); - images->getImage(hexEdgeMaskToFrameIndex[HexMasks::right])->verticalFlip(); - images->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomRight])->verticalFlip(); - images->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomRight])->horizontalFlip(); - images->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomLeft])->horizontalFlip(); - - images->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottom])->horizontalFlip(); - - images->getImage(hexEdgeMaskToFrameIndex[HexMasks::topRightHalfCorner])->verticalFlip(); - images->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomRightHalfCorner])->verticalFlip(); - images->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomRightHalfCorner])->horizontalFlip(); - images->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomLeftHalfCorner])->horizontalFlip(); - - images->getImage(hexEdgeMaskToFrameIndex[HexMasks::rightHalf])->verticalFlip(); - - images->getImage(hexEdgeMaskToFrameIndex[HexMasks::topRightCorner])->verticalFlip(); - images->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomRightCorner])->verticalFlip(); - images->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomRightCorner])->horizontalFlip(); - images->getImage(hexEdgeMaskToFrameIndex[HexMasks::bottomLeftCorner])->horizontalFlip(); + images->verticalFlip(hexEdgeMaskToFrameIndex[HexMasks::topRight]); + images->verticalFlip(hexEdgeMaskToFrameIndex[HexMasks::right]); + images->verticalFlip(hexEdgeMaskToFrameIndex[HexMasks::bottomRight]); + images->horizontalFlip(hexEdgeMaskToFrameIndex[HexMasks::bottomRight]); + images->horizontalFlip(hexEdgeMaskToFrameIndex[HexMasks::bottomLeft]); + images->horizontalFlip(hexEdgeMaskToFrameIndex[HexMasks::bottom]); + images->verticalFlip(hexEdgeMaskToFrameIndex[HexMasks::topRightHalfCorner]); + images->verticalFlip(hexEdgeMaskToFrameIndex[HexMasks::bottomRightHalfCorner]); + images->horizontalFlip(hexEdgeMaskToFrameIndex[HexMasks::bottomRightHalfCorner]); + images->horizontalFlip(hexEdgeMaskToFrameIndex[HexMasks::bottomLeftHalfCorner]); + images->verticalFlip(hexEdgeMaskToFrameIndex[HexMasks::rightHalf]); + images->verticalFlip(hexEdgeMaskToFrameIndex[HexMasks::topRightCorner]); + images->verticalFlip(hexEdgeMaskToFrameIndex[HexMasks::bottomRightCorner]); + images->horizontalFlip(hexEdgeMaskToFrameIndex[HexMasks::bottomRightCorner]); + images->horizontalFlip(hexEdgeMaskToFrameIndex[HexMasks::bottomLeftCorner]); } void BattleFieldController::showHighlightedHexes(Canvas & canvas) diff --git a/client/mapView/MapRenderer.cpp b/client/mapView/MapRenderer.cpp index 7b97ec7fd..095842410 100644 --- a/client/mapView/MapRenderer.cpp +++ b/client/mapView/MapRenderer.cpp @@ -323,8 +323,7 @@ MapRendererFow::MapRendererFow() for(const int rotation : rotations) { fogOfWarPartialHide->duplicateImage(0, rotation, 0); - auto image = fogOfWarPartialHide->getImage(size, 0); - image->verticalFlip(); + fogOfWarPartialHide->verticalFlip(size, 0); size++; } } diff --git a/client/render/CAnimation.cpp b/client/render/CAnimation.cpp index 70f486886..6ceabc0cf 100644 --- a/client/render/CAnimation.cpp +++ b/client/render/CAnimation.cpp @@ -30,42 +30,26 @@ bool CAnimation::loadFrame(size_t frame, size_t group) return true; } + std::shared_ptr image; + //try to get image from def - if(source[group][frame].getType() == JsonNode::JsonType::DATA_NULL) + if(source[group][frame].isNull()) + image = GH.renderHandler().loadImage(name, frame, group); + else + image = GH.renderHandler().loadImage(source[group][frame]); + + if(image) { - auto image = GH.renderHandler().loadImage(name, frame, group); - - if(image) - { - images[group][frame] = image; - return true; - } - // still here? image is missing - + images[group][frame] = image; + return true; + } + else + { + // image is missing printError(frame, group, "LoadFrame"); images[group][frame] = GH.renderHandler().loadImage(ImagePath::builtin("DEFAULT"), EImageBlitMode::OPAQUE); return false; } - - if (!source[group][frame]["file"].isNull()) - { - auto img = GH.renderHandler().loadImage(ImagePath::fromJson(source[group][frame]["file"]), EImageBlitMode::ALPHA); - images[group][frame] = img; - return true; - } - - if (!source[group][frame]["animation"].isNull()) - { - AnimationPath animationFile = AnimationPath::fromJson(source[group][frame]["animation"]); - int32_t animationGroup = source[group][frame]["sourceGroup"].Integer(); - int32_t animationFrame = source[group][frame]["sourceFrame"].Integer(); - - auto img = GH.renderHandler().loadImage(animationFile, animationFrame, animationGroup); - images[group][frame] = img; - return true; - } - - return false; } bool CAnimation::unloadFrame(size_t frame, size_t group) @@ -145,14 +129,13 @@ void CAnimation::duplicateImage(const size_t sourceGroup, const size_t sourceFra return; } - //todo: clone actual loaded Image object JsonNode clone(source[sourceGroup][sourceFrame]); if(clone.getType() == JsonNode::JsonType::DATA_NULL) { clone["animation"].String() = name.getName(); - clone["sourceGroup"].Integer() = sourceGroup; - clone["sourceFrame"].Integer() = sourceFrame; + clone["group"].Integer() = sourceGroup; + clone["frame"].Integer() = sourceFrame; } source[targetGroup].push_back(clone); @@ -197,16 +180,60 @@ size_t CAnimation::size(size_t group) const void CAnimation::horizontalFlip() { - for(auto & group : images) - for(auto & image : group.second) - image.second->horizontalFlip(); + for(auto & group : source) + for(size_t i = 0; i < group.second.size(); ++i) + horizontalFlip(i, group.first); } void CAnimation::verticalFlip() { - for(auto & group : images) - for(auto & image : group.second) - image.second->verticalFlip(); + for(auto & group : source) + for(size_t i = 0; i < group.second.size(); ++i) + verticalFlip(i, group.first); +} + +void CAnimation::horizontalFlip(size_t frame, size_t group) +{ + try + { + images.at(group).at(frame) = nullptr; + } + catch (const std::out_of_range &) + { + // ignore - image not loaded + } + + JsonNode & config = source.at(group).at(frame); + if (config.isNull()) + { + config["animation"].String() = name.getName(); + config["frame"].Integer() = frame; + config["group"].Integer() = group; + } + + config["horizontalFlip"].Bool() = !config["horizontalFlip"].Bool(); +} + +void CAnimation::verticalFlip(size_t frame, size_t group) +{ + try + { + images.at(group).at(frame) = nullptr; + } + catch (const std::out_of_range &) + { + // ignore - image not loaded + } + + JsonNode & config = source.at(group).at(frame); + if (config.isNull()) + { + config["animation"].String() = name.getName(); + config["frame"].Integer() = frame; + config["group"].Integer() = group; + } + + config["verticalFlip"].Bool() = !config["verticalFlip"].Bool(); } void CAnimation::playerColored(PlayerColor player) @@ -221,8 +248,6 @@ void CAnimation::createFlippedGroup(const size_t sourceGroup, const size_t targe for(size_t frame = 0; frame < size(sourceGroup); ++frame) { duplicateImage(sourceGroup, frame, targetGroup); - - auto image = getImage(frame, targetGroup); - image->verticalFlip(); + verticalFlip(frame, targetGroup); } } diff --git a/client/render/CAnimation.h b/client/render/CAnimation.h index ce78b0e4d..7cc974af0 100644 --- a/client/render/CAnimation.h +++ b/client/render/CAnimation.h @@ -62,6 +62,8 @@ public: //total count of frames in group (including not loaded) size_t size(size_t group=0) const; + void horizontalFlip(size_t frame, size_t group=0); + void verticalFlip(size_t frame, size_t group=0); void horizontalFlip(); void verticalFlip(); void playerColored(PlayerColor player); diff --git a/client/render/IImage.h b/client/render/IImage.h index 076380695..63c6ea186 100644 --- a/client/render/IImage.h +++ b/client/render/IImage.h @@ -75,9 +75,6 @@ public: //only indexed bitmaps with 7 special colors virtual void setSpecialPalette(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask) = 0; - virtual void horizontalFlip() = 0; - virtual void verticalFlip() = 0; - virtual ~IImage() = default; }; @@ -90,5 +87,9 @@ public: virtual std::shared_ptr createImageReference() = 0; + virtual std::shared_ptr horizontalFlip() const = 0; + virtual std::shared_ptr verticalFlip() const = 0; + + virtual ~IConstImage() = default; }; diff --git a/client/render/IRenderHandler.h b/client/render/IRenderHandler.h index f6a8fa5a5..b8103ab92 100644 --- a/client/render/IRenderHandler.h +++ b/client/render/IRenderHandler.h @@ -30,6 +30,7 @@ public: virtual void onLibraryLoadingFinished(const Services * services) = 0; /// Loads image using given path + virtual std::shared_ptr loadImage(const JsonNode & config) = 0; virtual std::shared_ptr loadImage(const ImagePath & path) = 0; virtual std::shared_ptr loadImage(const ImagePath & path, EImageBlitMode mode) = 0; virtual std::shared_ptr loadImage(const AnimationPath & path, int frame, int group) = 0; diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index 2b0db2da8..ac62d473a 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -27,6 +27,43 @@ #include #include +RenderHandler::ImageLocator::ImageLocator(const JsonNode & config) + : image(ImagePath::fromJson(config["file"])) + , animation(AnimationPath::fromJson(config["animation"])) + , frame(config["frame"].Integer()) + , group(config["group"].Integer()) + , verticalFlip(config["verticalFlip"].Bool()) + , horizontalFlip(config["horizontalFlip"].Bool()) +{ +} + +RenderHandler::ImageLocator::ImageLocator(const ImagePath & path) + : image(path) +{ +} + +RenderHandler::ImageLocator::ImageLocator(const AnimationPath & path, int frame, int group) + : animation(path) + , frame(frame) + , group(group) +{ +} + +bool RenderHandler::ImageLocator::operator<(const ImageLocator & other) const +{ + if(image != other.image) + return image < other.image; + if(animation != other.animation) + return animation < other.animation; + if(group != other.group) + return group < other.group; + if(frame != other.frame) + return frame < other.frame; + if(verticalFlip != other.verticalFlip) + return verticalFlip < other.verticalFlip; + return horizontalFlip < other.horizontalFlip; +} + std::shared_ptr RenderHandler::getAnimationFile(const AnimationPath & path) { AnimationPath actualPath = boost::starts_with(path.getName(), "SPRITES") ? path : path.addPrefix("SPRITES/"); @@ -103,7 +140,7 @@ RenderHandler::AnimationLayoutMap & RenderHandler::getAnimationLayout(const Anim { const std::map defEntries = defFile->getEntries(); - for (auto & defEntry : defEntries) + for (const auto & defEntry : defEntries) result[defEntry.first].resize(defEntry.second); } @@ -125,32 +162,78 @@ RenderHandler::AnimationLayoutMap & RenderHandler::getAnimationLayout(const Anim return animationLayouts[actualPath]; } -std::shared_ptr RenderHandler::loadImage(const AnimationPath & path, int frame, int group) +std::shared_ptr RenderHandler::loadImageFromSingleFile(const ImagePath & path) { - AnimationLocator locator{path, frame, group}; - - auto it = animationFrames.find(locator); - if (it != animationFrames.end()) - return it->second->createImageReference(); + auto result = std::make_shared(path, EImageBlitMode::COLORKEY); + imageFiles[ImageLocator(path)] = result; + return result; +} +std::shared_ptr RenderHandler::loadImageFromAnimationFileUncached(const AnimationPath & path, int frame, int group) +{ const auto & layout = getAnimationLayout(path); if (!layout.count(group)) - return loadImage(ImagePath::builtin("DEFAULT")); + return loadImageFromSingleFile(ImagePath::builtin("DEFAULT")); if (frame >= layout.at(group).size()) - return loadImage(ImagePath::builtin("DEFAULT")); + return loadImageFromSingleFile(ImagePath::builtin("DEFAULT")); const auto & config = layout.at(group).at(frame); - if (config.isNull()) { auto defFile = getAnimationFile(path); - auto result = std::make_shared(defFile.get(), frame, group); - animationFrames[locator] = result; - return result->createImageReference(); + return std::make_shared(defFile.get(), frame, group); } + else + { + return loadImageImpl(ImageLocator(config)); + } +} - return loadImage(ImagePath::builtin("DEFAULT")); +std::shared_ptr RenderHandler::loadImageFromAnimationFile(const AnimationPath & path, int frame, int group) +{ + auto result = loadImageFromAnimationFileUncached(path, frame, group); + imageFiles[ImageLocator(path, frame, group)] = result; + return result; +} + +std::shared_ptr RenderHandler::loadImageImpl(const ImageLocator & locator) +{ + auto it = imageFiles.find(locator); + if (it != imageFiles.end()) + return it->second; + + std::shared_ptr result; + + if (!locator.image.empty()) + result = loadImageFromSingleFile(locator.image); + else if (!locator.animation.empty()) + result = loadImageFromAnimationFile(locator.animation, locator.frame, locator.group); + + if (!result) + result = loadImageFromSingleFile(ImagePath::builtin("DEFAULT")); + + if (locator.verticalFlip) + result = result->verticalFlip(); + + if (locator.horizontalFlip) + result = result->horizontalFlip(); + + imageFiles[locator] = result; + return result; +} + +std::shared_ptr RenderHandler::loadImage(const JsonNode & config) +{ + if (config.isString()) + return loadImageImpl(ImageLocator(ImagePath::fromJson(config)))->createImageReference(); + else + return loadImageImpl(ImageLocator(config))->createImageReference(); +} + +std::shared_ptr RenderHandler::loadImage(const AnimationPath & path, int frame, int group) +{ + return loadImageImpl(ImageLocator(path, frame, group))->createImageReference(); } std::shared_ptr RenderHandler::loadImage(const ImagePath & path) @@ -160,13 +243,7 @@ std::shared_ptr RenderHandler::loadImage(const ImagePath & path) std::shared_ptr RenderHandler::loadImage(const ImagePath & path, EImageBlitMode mode) { - auto it = imageFiles.find(path); - if (it != imageFiles.end()) - return it->second->createImageReference(); - - auto result = std::make_shared(path, mode); - imageFiles[path] = result; - return result->createImageReference(); + return loadImageImpl(ImageLocator(path))->createImageReference(); } std::shared_ptr RenderHandler::createImage(SDL_Surface * source) diff --git a/client/renderSDL/RenderHandler.h b/client/renderSDL/RenderHandler.h index f7f4fe990..e2a8b8d2a 100644 --- a/client/renderSDL/RenderHandler.h +++ b/client/renderSDL/RenderHandler.h @@ -22,26 +22,25 @@ class RenderHandler : public IRenderHandler { using AnimationLayoutMap = std::map>; - struct AnimationLocator + struct ImageLocator { + ImagePath image; AnimationPath animation; int frame = -1; int group = -1; - bool operator < (const AnimationLocator & other) const - { - if (animation != other.animation) - return animation < other.animation; - if (group != other.group) - return group < other.group; - return frame < other.frame; - } + bool verticalFlip = false; + bool horizontalFlip = false; + + ImageLocator(const JsonNode & config); + ImageLocator(const ImagePath & path); + ImageLocator(const AnimationPath & path, int frame, int group); + bool operator < (const ImageLocator & other) const; }; std::map> animationFiles; std::map animationLayouts; - std::map> imageFiles; - std::map> animationFrames; + std::map> imageFiles; std::shared_ptr getAnimationFile(const AnimationPath & path); AnimationLayoutMap & getAnimationLayout(const AnimationPath & path); @@ -49,11 +48,17 @@ class RenderHandler : public IRenderHandler void addImageListEntry(size_t index, size_t group, const std::string & listName, const std::string & imageName); void addImageListEntries(const EntityService * service); + + std::shared_ptr loadImageFromSingleFile(const ImagePath & path); + std::shared_ptr loadImageFromAnimationFileUncached(const AnimationPath & path, int frame, int group); + std::shared_ptr loadImageFromAnimationFile(const AnimationPath & path, int frame, int group); + std::shared_ptr loadImageImpl(const ImageLocator & config); public: // IRenderHandler implementation void onLibraryLoadingFinished(const Services * services) override; + std::shared_ptr loadImage(const JsonNode & config) override; std::shared_ptr loadImage(const ImagePath & path) override; std::shared_ptr loadImage(const ImagePath & path, EImageBlitMode mode) override; std::shared_ptr loadImage(const AnimationPath & path, int frame, int group) override; diff --git a/client/renderSDL/SDLImage.cpp b/client/renderSDL/SDLImage.cpp index 13bce6bc3..88e52622c 100644 --- a/client/renderSDL/SDLImage.cpp +++ b/client/renderSDL/SDLImage.cpp @@ -202,7 +202,7 @@ std::shared_ptr SDLImageConst::createImageReference() return std::make_shared(shared_from_this()); } -std::shared_ptr SDLImageConst::horizontalFlip() const +std::shared_ptr SDLImageConst::horizontalFlip() const { SDL_Surface * flipped = CSDL_Ext::horizontalFlip(surf); auto ret = std::make_shared(flipped, EImageBlitMode::ALPHA); @@ -214,7 +214,7 @@ std::shared_ptr SDLImageConst::horizontalFlip() const return ret; } -std::shared_ptr SDLImageConst::verticalFlip() const +std::shared_ptr SDLImageConst::verticalFlip() const { SDL_Surface * flipped = CSDL_Ext::verticalFlip(surf); auto ret = std::make_shared(flipped, EImageBlitMode::ALPHA); @@ -342,16 +342,6 @@ void SDLImageBase::setBlitMode(EImageBlitMode mode) blitMode = mode; } -void SDLImageBase::horizontalFlip() -{ - image = image->horizontalFlip(); -} - -void SDLImageBase::verticalFlip() -{ - image = image->verticalFlip(); -} - void SDLImageRGB::setSpecialPallete(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask) {} diff --git a/client/renderSDL/SDLImage.h b/client/renderSDL/SDLImage.h index fae1d9fe3..4cc8d6cc9 100644 --- a/client/renderSDL/SDLImage.h +++ b/client/renderSDL/SDLImage.h @@ -53,8 +53,8 @@ public: Point dimensions() const override; bool isTransparent(const Point & coords) const override; std::shared_ptr createImageReference() override; - std::shared_ptr horizontalFlip() const; - std::shared_ptr verticalFlip() const; + std::shared_ptr horizontalFlip() const override; + std::shared_ptr verticalFlip() const override; std::shared_ptr scaleFast(const Point & size) const; const SDL_Palette * getPalette() const; @@ -79,8 +79,6 @@ public: Point dimensions() const override; void setAlpha(uint8_t value) override; void setBlitMode(EImageBlitMode mode) override; - void horizontalFlip() override; - void verticalFlip() override; }; class SDLImageIndexed final : public SDLImageBase From f11c5f62e12bcdc0070b80a38f04ace5e534f261 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 7 Jun 2024 20:41:54 +0000 Subject: [PATCH 085/100] Remove no longer needed code --- client/renderSDL/CBitmapFont.cpp | 2 +- client/renderSDL/CBitmapHanFont.cpp | 2 +- client/renderSDL/SDLImage.cpp | 2 +- client/renderSDL/SDL_Extensions.cpp | 166 ++++------------------------ client/renderSDL/SDL_Extensions.h | 8 +- client/renderSDL/SDL_PixelAccess.h | 152 ++----------------------- client/windows/CWindowObject.cpp | 4 +- 7 files changed, 35 insertions(+), 301 deletions(-) diff --git a/client/renderSDL/CBitmapFont.cpp b/client/renderSDL/CBitmapFont.cpp index 36a29d174..9b755680d 100644 --- a/client/renderSDL/CBitmapFont.cpp +++ b/client/renderSDL/CBitmapFont.cpp @@ -125,7 +125,7 @@ void CBitmapFont::renderCharacter(SDL_Surface * surface, const BitmapChar & char posX += character.leftOffset; - CSDL_Ext::TColorPutter colorPutter = CSDL_Ext::getPutterFor(surface, 0); + CSDL_Ext::TColorPutter colorPutter = CSDL_Ext::getPutterFor(surface); uint8_t bpp = surface->format->BytesPerPixel; diff --git a/client/renderSDL/CBitmapHanFont.cpp b/client/renderSDL/CBitmapHanFont.cpp index ac1684185..5476d823f 100644 --- a/client/renderSDL/CBitmapHanFont.cpp +++ b/client/renderSDL/CBitmapHanFont.cpp @@ -41,7 +41,7 @@ void CBitmapHanFont::renderCharacter(SDL_Surface * surface, int characterIndex, Rect clipRect; CSDL_Ext::getClipRect(surface, clipRect); - CSDL_Ext::TColorPutter colorPutter = CSDL_Ext::getPutterFor(surface, 0); + CSDL_Ext::TColorPutter colorPutter = CSDL_Ext::getPutterFor(surface); uint8_t bpp = surface->format->BytesPerPixel; // start of line, may differ from 0 due to end of surface or clipped surface diff --git a/client/renderSDL/SDLImage.cpp b/client/renderSDL/SDLImage.cpp index 88e52622c..6428da2c8 100644 --- a/client/renderSDL/SDLImage.cpp +++ b/client/renderSDL/SDLImage.cpp @@ -142,7 +142,7 @@ std::shared_ptr SDLImageConst::scaleFast(const Point & size) cons float scaleX = float(size.x) / dimensions().x; float scaleY = float(size.y) / dimensions().y; - auto scaled = CSDL_Ext::scaleSurfaceFast(surf, (int)(surf->w * scaleX), (int)(surf->h * scaleY)); + auto scaled = CSDL_Ext::scaleSurface(surf, (int)(surf->w * scaleX), (int)(surf->h * scaleY)); if (scaled->format && scaled->format->palette) // fix color keying, because SDL loses it at this point CSDL_Ext::setColorKey(scaled, scaled->format->palette->colors[0]); diff --git a/client/renderSDL/SDL_Extensions.cpp b/client/renderSDL/SDL_Extensions.cpp index d5426089a..9a233517e 100644 --- a/client/renderSDL/SDL_Extensions.cpp +++ b/client/renderSDL/SDL_Extensions.cpp @@ -337,15 +337,16 @@ int CSDL_Ext::blit8bppAlphaTo24bppT(const SDL_Surface * src, const Rect & srcRec uint8_t *colory = (uint8_t*)src->pixels + srcy*src->pitch + srcx; uint8_t *py = (uint8_t*)dst->pixels + dstRect->y*dst->pitch + dstRect->x*bpp; - for(int y=h; y; y--, colory+=src->pitch, py+=dst->pitch) + for(int y=0; ypitch, py+=dst->pitch) { uint8_t *color = colory; uint8_t *p = py; - for(int x = w; x; x--) + for(int x = 0; x < w; ++x) { const SDL_Color &tbc = colors[*color++]; //color to blit - ColorPutter::PutColorAlphaSwitch(p, tbc.r, tbc.g, tbc.b, tbc.a); + ColorPutter::PutColorAlphaSwitch(p, tbc.r, tbc.g, tbc.b, tbc.a); + p += bpp; } } SDL_UnlockSurface(dst); @@ -422,7 +423,7 @@ static void drawLineX(SDL_Surface * sur, int x1, int y1, int x2, int y2, const S uint8_t a = vstd::lerp(color1.a, color2.a, f); uint8_t *p = CSDL_Ext::getPxPtr(sur, x, y); - ColorPutter<4, 0>::PutColor(p, r,g,b,a); + ColorPutter<4>::PutColor(p, r,g,b,a); } } @@ -440,7 +441,7 @@ static void drawLineY(SDL_Surface * sur, int x1, int y1, int x2, int y2, const S uint8_t a = vstd::lerp(color1.a, color2.a, f); uint8_t *p = CSDL_Ext::getPxPtr(sur, x, y); - ColorPutter<4, 0>::PutColor(p, r,g,b,a); + ColorPutter<4>::PutColor(p, r,g,b,a); } } @@ -453,7 +454,7 @@ void CSDL_Ext::drawLine(SDL_Surface * sur, const Point & from, const Point & des if ( width == 0 && height == 0) { uint8_t *p = CSDL_Ext::getPxPtr(sur, from.x, from.y); - ColorPutter<4, 0>::PutColorAlpha(p, color1); + ColorPutter<4>::PutColorAlpha(p, color1); return; } @@ -524,42 +525,18 @@ void CSDL_Ext::drawBorder( SDL_Surface * sur, const Rect &r, const SDL_Color &co drawBorder(sur, r.x, r.y, r.w, r.h, color, depth); } -CSDL_Ext::TColorPutter CSDL_Ext::getPutterFor(SDL_Surface * const &dest, int incrementing) +CSDL_Ext::TColorPutter CSDL_Ext::getPutterFor(SDL_Surface * const &dest) { -#define CASE_BPP(BytesPerPixel) \ -case BytesPerPixel: \ - if(incrementing > 0) \ - return ColorPutter::PutColor; \ - else if(incrementing == 0) \ - return ColorPutter::PutColor; \ - else \ - return ColorPutter::PutColor;\ - break; - switch(dest->format->BytesPerPixel) { - CASE_BPP(2) - CASE_BPP(3) - CASE_BPP(4) + case 3: + return ColorPutter<3>::PutColor; + case 4: + return ColorPutter<4>::PutColor; default: logGlobal->error("%d bpp is not supported!", (int)dest->format->BitsPerPixel); return nullptr; } - -} - -CSDL_Ext::TColorPutterAlpha CSDL_Ext::getPutterAlphaFor(SDL_Surface * const &dest, int incrementing) -{ - switch(dest->format->BytesPerPixel) - { - CASE_BPP(2) - CASE_BPP(3) - CASE_BPP(4) - default: - logGlobal->error("%d bpp is not supported!", (int)dest->format->BitsPerPixel); - return nullptr; - } -#undef CASE_BPP } uint8_t * CSDL_Ext::getPxPtr(const SDL_Surface * const &srf, const int x, const int y) @@ -590,11 +567,10 @@ bool CSDL_Ext::isTransparent( SDL_Surface * srf, int x, int y ) void CSDL_Ext::putPixelWithoutRefresh(SDL_Surface *ekran, const int & x, const int & y, const uint8_t & R, const uint8_t & G, const uint8_t & B, uint8_t A) { uint8_t *p = getPxPtr(ekran, x, y); - getPutterFor(ekran, false)(p, R, G, B); + getPutterFor(ekran)(p, R, G, B); switch(ekran->format->BytesPerPixel) { - case 2: Channels::px<2>::a.set(p, A); break; case 3: Channels::px<3>::a.set(p, A); break; case 4: Channels::px<4>::a.set(p, A); break; } @@ -638,126 +614,24 @@ void CSDL_Ext::convertToGrayscale( SDL_Surface * surf, const Rect & rect ) { switch(surf->format->BytesPerPixel) { - case 2: convertToGrayscaleBpp<2>(surf, rect); break; case 3: convertToGrayscaleBpp<3>(surf, rect); break; case 4: convertToGrayscaleBpp<4>(surf, rect); break; } } -template -void scaleSurfaceFastInternal(SDL_Surface *surf, SDL_Surface *ret) -{ - const float factorX = static_cast(surf->w) / static_cast(ret->w); - const float factorY = static_cast(surf->h) / static_cast(ret->h); - - for(int y = 0; y < ret->h; y++) - { - for(int x = 0; x < ret->w; x++) - { - //coordinates we want to calculate - auto origX = static_cast(floor(factorX * x)); - auto origY = static_cast(floor(factorY * y)); - - // Get pointers to source pixels - uint8_t *srcPtr = (uint8_t*)surf->pixels + origY * surf->pitch + origX * bpp; - uint8_t *destPtr = (uint8_t*)ret->pixels + y * ret->pitch + x * bpp; - - memcpy(destPtr, srcPtr, bpp); - } - } -} - -SDL_Surface * CSDL_Ext::scaleSurfaceFast(SDL_Surface *surf, int width, int height) -{ - if (!surf || !width || !height) - return nullptr; - - //Same size? return copy - this should more be faster - if (width == surf->w && height == surf->h) - return copySurface(surf); - - SDL_Surface *ret = newSurface(width, height, surf); - - switch(surf->format->BytesPerPixel) - { - case 1: scaleSurfaceFastInternal<1>(surf, ret); break; - case 2: scaleSurfaceFastInternal<2>(surf, ret); break; - case 3: scaleSurfaceFastInternal<3>(surf, ret); break; - case 4: scaleSurfaceFastInternal<4>(surf, ret); break; - } - return ret; -} - -template -void scaleSurfaceInternal(SDL_Surface *surf, SDL_Surface *ret) -{ - const float factorX = float(surf->w - 1) / float(ret->w), - factorY = float(surf->h - 1) / float(ret->h); - - for(int y = 0; y < ret->h; y++) - { - for(int x = 0; x < ret->w; x++) - { - //coordinates we want to interpolate - float origX = factorX * x, - origY = factorY * y; - - float x1 = floor(origX), x2 = floor(origX+1), - y1 = floor(origY), y2 = floor(origY+1); - //assert( x1 >= 0 && y1 >= 0 && x2 < surf->w && y2 < surf->h);//All pixels are in range - - // Calculate weights of each source pixel - float w11 = ((origX - x1) * (origY - y1)); - float w12 = ((origX - x1) * (y2 - origY)); - float w21 = ((x2 - origX) * (origY - y1)); - float w22 = ((x2 - origX) * (y2 - origY)); - //assert( w11 + w12 + w21 + w22 > 0.99 && w11 + w12 + w21 + w22 < 1.01);//total weight is ~1.0 - - // Get pointers to source pixels - uint8_t *p11 = (uint8_t*)surf->pixels + int(y1) * surf->pitch + int(x1) * bpp; - uint8_t *p12 = p11 + bpp; - uint8_t *p21 = p11 + surf->pitch; - uint8_t *p22 = p21 + bpp; - // Calculate resulting channels -#define PX(X, PTR) Channels::px::X.get(PTR) - int resR = static_cast(PX(r, p11) * w11 + PX(r, p12) * w12 + PX(r, p21) * w21 + PX(r, p22) * w22); - int resG = static_cast(PX(g, p11) * w11 + PX(g, p12) * w12 + PX(g, p21) * w21 + PX(g, p22) * w22); - int resB = static_cast(PX(b, p11) * w11 + PX(b, p12) * w12 + PX(b, p21) * w21 + PX(b, p22) * w22); - int resA = static_cast(PX(a, p11) * w11 + PX(a, p12) * w12 + PX(a, p21) * w21 + PX(a, p22) * w22); - //assert(resR < 256 && resG < 256 && resB < 256 && resA < 256); -#undef PX - uint8_t *dest = (uint8_t*)ret->pixels + y * ret->pitch + x * bpp; - Channels::px::r.set(dest, resR); - Channels::px::g.set(dest, resG); - Channels::px::b.set(dest, resB); - Channels::px::a.set(dest, resA); - } - } -} - // scaling via bilinear interpolation algorithm. // NOTE: best results are for scaling in range 50%...200%. // And upscaling looks awful right now - should be fixed somehow -SDL_Surface * CSDL_Ext::scaleSurface(SDL_Surface *surf, int width, int height) +SDL_Surface * CSDL_Ext::scaleSurface(SDL_Surface * surf, int width, int height) { - if (!surf || !width || !height) + if(!surf || !width || !height) return nullptr; - if (surf->format->palette) - return scaleSurfaceFast(surf, width, height); + SDL_Surface * intermediate = SDL_ConvertSurface(surf, screen->format, 0); + SDL_Surface * ret = newSurface(width, height, intermediate); - //Same size? return copy - this should more be faster - if (width == surf->w && height == surf->h) - return copySurface(surf); - - SDL_Surface *ret = newSurface(width, height, surf); - - switch(surf->format->BytesPerPixel) - { - case 2: scaleSurfaceInternal<2>(surf, ret); break; - case 3: scaleSurfaceInternal<3>(surf, ret); break; - case 4: scaleSurfaceInternal<4>(surf, ret); break; - } + SDL_SoftStretchLinear(intermediate, nullptr, ret, nullptr); + SDL_FreeSurface(intermediate); return ret; } @@ -851,7 +725,5 @@ void CSDL_Ext::getClipRect(SDL_Surface * src, Rect & other) other = CSDL_Ext::fromSDL(rect); } -template SDL_Surface * CSDL_Ext::createSurfaceWithBpp<2>(int, int); template SDL_Surface * CSDL_Ext::createSurfaceWithBpp<3>(int, int); template SDL_Surface * CSDL_Ext::createSurfaceWithBpp<4>(int, int); - diff --git a/client/renderSDL/SDL_Extensions.h b/client/renderSDL/SDL_Extensions.h index 8e39ee8ac..ec3deeecc 100644 --- a/client/renderSDL/SDL_Extensions.h +++ b/client/renderSDL/SDL_Extensions.h @@ -73,8 +73,7 @@ using TColorPutterAlpha = void (*)(uint8_t *&, const uint8_t &, const uint8_t &, bool isTransparent(SDL_Surface * srf, const Point & position); //checks if surface is transparent at given position uint8_t * getPxPtr(const SDL_Surface * const & srf, const int x, const int y); - TColorPutter getPutterFor(SDL_Surface * const & dest, int incrementing); //incrementing: -1, 0, 1 - TColorPutterAlpha getPutterAlphaFor(SDL_Surface * const & dest, int incrementing); //incrementing: -1, 0, 1 + TColorPutter getPutterFor(SDL_Surface * const & dest); template int blit8bppAlphaTo24bppT(const SDL_Surface * src, const Rect & srcRect, SDL_Surface * dst, const Point & dstPoint); //blits 8 bpp surface with alpha channel to 24 bpp surface @@ -93,10 +92,7 @@ using TColorPutterAlpha = void (*)(uint8_t *&, const uint8_t &, const uint8_t &, template SDL_Surface * createSurfaceWithBpp(int width, int height); //create surface with give bits per pixels value - //scale surface to required size. - //nearest neighbour algorithm - SDL_Surface * scaleSurfaceFast(SDL_Surface * surf, int width, int height); - // bilinear filtering. Uses fallback to scaleSurfaceFast in case of indexed surfaces + // bilinear filtering. Always returns rgba surface SDL_Surface * scaleSurface(SDL_Surface * surf, int width, int height); template diff --git a/client/renderSDL/SDL_PixelAccess.h b/client/renderSDL/SDL_PixelAccess.h index ddd91a663..2cf41ff28 100644 --- a/client/renderSDL/SDL_PixelAccess.h +++ b/client/renderSDL/SDL_PixelAccess.h @@ -109,58 +109,29 @@ namespace Channels }; #endif - - template<> - struct px<2> - { - static channel_subpx<5, 0xF800, 11> r; - static channel_subpx<6, 0x07E0, 5 > g; - static channel_subpx<5, 0x001F, 0 > b; - static channel_empty a; - }; } -template +template struct ColorPutter { static STRONG_INLINE void PutColor(uint8_t *&ptr, const uint8_t & R, const uint8_t & G, const uint8_t & B); static STRONG_INLINE void PutColor(uint8_t *&ptr, const uint8_t & R, const uint8_t & G, const uint8_t & B, const uint8_t & A); static STRONG_INLINE void PutColorAlphaSwitch(uint8_t *&ptr, const uint8_t & R, const uint8_t & G, const uint8_t & B, const uint8_t & A); - static STRONG_INLINE void PutColor(uint8_t *&ptr, const SDL_Color & Color); static STRONG_INLINE void PutColorAlpha(uint8_t *&ptr, const SDL_Color & Color); - static STRONG_INLINE void PutColorRow(uint8_t *&ptr, const SDL_Color & Color, size_t count); }; -template -struct ColorPutter<2, incrementPtr> -{ - static STRONG_INLINE void PutColor(uint8_t *&ptr, const uint8_t & R, const uint8_t & G, const uint8_t & B); - static STRONG_INLINE void PutColor(uint8_t *&ptr, const uint8_t & R, const uint8_t & G, const uint8_t & B, const uint8_t & A); - static STRONG_INLINE void PutColorAlphaSwitch(uint8_t *&ptr, const uint8_t & R, const uint8_t & G, const uint8_t & B, const uint8_t & A); - static STRONG_INLINE void PutColor(uint8_t *&ptr, const SDL_Color & Color); - static STRONG_INLINE void PutColorAlpha(uint8_t *&ptr, const SDL_Color & Color); - static STRONG_INLINE void PutColorRow(uint8_t *&ptr, const SDL_Color & Color, size_t count); -}; - -template -STRONG_INLINE void ColorPutter::PutColorAlpha(uint8_t *&ptr, const SDL_Color & Color) +template +STRONG_INLINE void ColorPutter::PutColorAlpha(uint8_t *&ptr, const SDL_Color & Color) { PutColor(ptr, Color.r, Color.g, Color.b, Color.a); } -template -STRONG_INLINE void ColorPutter::PutColor(uint8_t *&ptr, const SDL_Color & Color) -{ - PutColor(ptr, Color.r, Color.g, Color.b); -} - -template -STRONG_INLINE void ColorPutter::PutColorAlphaSwitch(uint8_t *&ptr, const uint8_t & R, const uint8_t & G, const uint8_t & B, const uint8_t & A) +template +STRONG_INLINE void ColorPutter::PutColorAlphaSwitch(uint8_t *&ptr, const uint8_t & R, const uint8_t & G, const uint8_t & B, const uint8_t & A) { switch (A) { case 0: - ptr += bpp * incrementPtr; return; case 255: PutColor(ptr, R, G, B); @@ -177,124 +148,19 @@ STRONG_INLINE void ColorPutter::PutColorAlphaSwitch(uint8_t * } } -template -STRONG_INLINE void ColorPutter::PutColor(uint8_t *&ptr, const uint8_t & R, const uint8_t & G, const uint8_t & B, const uint8_t & A) +template +STRONG_INLINE void ColorPutter::PutColor(uint8_t *&ptr, const uint8_t & R, const uint8_t & G, const uint8_t & B, const uint8_t & A) { PutColor(ptr, ((((uint32_t)R - (uint32_t)Channels::px::r.get(ptr))*(uint32_t)A) >> 8 ) + (uint32_t)Channels::px::r.get(ptr), ((((uint32_t)G - (uint32_t)Channels::px::g.get(ptr))*(uint32_t)A) >> 8 ) + (uint32_t)Channels::px::g.get(ptr), ((((uint32_t)B - (uint32_t)Channels::px::b.get(ptr))*(uint32_t)A) >> 8 ) + (uint32_t)Channels::px::b.get(ptr)); } - -template -STRONG_INLINE void ColorPutter::PutColor(uint8_t *&ptr, const uint8_t & R, const uint8_t & G, const uint8_t & B) +template +STRONG_INLINE void ColorPutter::PutColor(uint8_t *&ptr, const uint8_t & R, const uint8_t & G, const uint8_t & B) { - static_assert(incrementPtr >= -1 && incrementPtr <= +1, "Invalid incrementPtr value!"); - - if (incrementPtr < 0) - ptr -= bpp; - Channels::px::r.set(ptr, R); Channels::px::g.set(ptr, G); Channels::px::b.set(ptr, B); Channels::px::a.set(ptr, 255); - - if (incrementPtr > 0) - ptr += bpp; - -} - -template -STRONG_INLINE void ColorPutter::PutColorRow(uint8_t *&ptr, const SDL_Color & Color, size_t count) -{ - if (count) - { - uint8_t *pixel = ptr; - PutColor(ptr, Color.r, Color.g, Color.b); - - for (size_t i=0; i -STRONG_INLINE void ColorPutter<2, incrementPtr>::PutColor(uint8_t *&ptr, const uint8_t & R, const uint8_t & G, const uint8_t & B) -{ - if(incrementPtr == -1) - ptr -= 2; - - auto * const px = (uint16_t *)ptr; - *px = (B>>3) + ((G>>2) << 5) + ((R>>3) << 11); //drop least significant bits of 24 bpp encoded color - - if(incrementPtr == 1) - ptr += 2; //bpp -} - -template -STRONG_INLINE void ColorPutter<2, incrementPtr>::PutColorAlphaSwitch(uint8_t *&ptr, const uint8_t & R, const uint8_t & G, const uint8_t & B, const uint8_t & A) -{ - switch (A) - { - case 0: - ptr += 2 * incrementPtr; - return; - case 255: - PutColor(ptr, R, G, B); - return; - default: - PutColor(ptr, R, G, B, A); - return; - } -} - -template -STRONG_INLINE void ColorPutter<2, incrementPtr>::PutColor(uint8_t *&ptr, const uint8_t & R, const uint8_t & G, const uint8_t & B, const uint8_t & A) -{ - const int rbit = 5, gbit = 6, bbit = 5; //bits per color - const int rmask = 0xF800, gmask = 0x7E0, bmask = 0x1F; - const int rshift = 11, gshift = 5, bshift = 0; - - const uint8_t r5 = (*((uint16_t *)ptr) & rmask) >> rshift, - b5 = (*((uint16_t *)ptr) & bmask) >> bshift, - g5 = (*((uint16_t *)ptr) & gmask) >> gshift; - - const uint32_t r8 = (r5 << (8 - rbit)) | (r5 >> (2*rbit - 8)), - g8 = (g5 << (8 - gbit)) | (g5 >> (2*gbit - 8)), - b8 = (b5 << (8 - bbit)) | (b5 >> (2*bbit - 8)); - - PutColor(ptr, - (((R-r8)*A) >> 8) + r8, - (((G-g8)*A) >> 8) + g8, - (((B-b8)*A) >> 8) + b8); -} - -template -STRONG_INLINE void ColorPutter<2, incrementPtr>::PutColorAlpha(uint8_t *&ptr, const SDL_Color & Color) -{ - PutColor(ptr, Color.r, Color.g, Color.b, Color.a); -} - -template -STRONG_INLINE void ColorPutter<2, incrementPtr>::PutColor(uint8_t *&ptr, const SDL_Color & Color) -{ - PutColor(ptr, Color.r, Color.g, Color.b); -} - -template -STRONG_INLINE void ColorPutter<2, incrementPtr>::PutColorRow(uint8_t *&ptr, const SDL_Color & Color, size_t count) -{ - //drop least significant bits of 24 bpp encoded color - uint16_t pixel = (Color.b>>3) + ((Color.g>>2) << 5) + ((Color.r>>3) << 11); - - for (size_t i=0; i Date: Sat, 8 Jun 2024 07:35:13 +0000 Subject: [PATCH 086/100] Reviewed usage of EBlitMode, now specified clearly in all cases --- client/CPlayerInterface.cpp | 2 +- client/ClientCommandManager.cpp | 2 +- client/adventureMap/AdventureMapInterface.cpp | 3 +- client/battle/BattleAnimationClasses.cpp | 2 +- client/battle/BattleFieldController.cpp | 10 +++---- client/battle/BattleInterfaceClasses.cpp | 9 ++---- client/battle/BattleObstacleController.cpp | 6 ++-- client/battle/BattleProjectileController.cpp | 2 +- client/battle/BattleSiegeController.cpp | 6 ++-- client/battle/CreatureAnimation.cpp | 4 +-- client/gui/CursorHandler.cpp | 10 +++---- client/lobby/SelectionTab.cpp | 2 +- client/mapView/MapRenderer.cpp | 18 ++++------- client/mapView/MapViewCache.cpp | 5 +--- client/render/CAnimation.cpp | 9 +++--- client/render/CAnimation.h | 7 +++-- client/render/IImage.h | 12 +++++--- client/render/IRenderHandler.h | 7 ++--- client/renderSDL/RenderHandler.cpp | 25 +++++++--------- client/renderSDL/RenderHandler.h | 7 ++--- client/renderSDL/SDLImage.cpp | 30 +++++++++---------- client/renderSDL/SDLImage.h | 10 +++---- client/widgets/Images.cpp | 22 +++++++++----- client/widgets/Images.h | 1 + client/windows/CCastleInterface.cpp | 2 +- client/windows/CMessage.cpp | 2 +- client/windows/CSpellWindow.cpp | 2 +- client/windows/CWindowWithArtifacts.cpp | 2 +- 28 files changed, 109 insertions(+), 110 deletions(-) diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 06c5aff05..dab02d6dd 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1145,7 +1145,7 @@ void CPlayerInterface::showMapObjectSelectDialog(QueryID askID, const Component const CGTownInstance * t = dynamic_cast(cb->getObj(obj)); if(t) { - auto image = GH.renderHandler().loadImage(AnimationPath::builtin("ITPA"), t->town->clientInfo.icons[t->hasFort()][false] + 2, 0); + auto image = GH.renderHandler().loadImage(AnimationPath::builtin("ITPA"), t->town->clientInfo.icons[t->hasFort()][false] + 2, 0, EImageBlitMode::OPAQUE); image->scaleFast(Point(35, 23)); images.push_back(image); } diff --git a/client/ClientCommandManager.cpp b/client/ClientCommandManager.cpp index dc0984e43..96e150713 100644 --- a/client/ClientCommandManager.cpp +++ b/client/ClientCommandManager.cpp @@ -388,7 +388,7 @@ void ClientCommandManager::handleDef2bmpCommand(std::istringstream& singleWordBu { std::string URI; singleWordBuffer >> URI; - auto anim = GH.renderHandler().loadAnimation(AnimationPath::builtin(URI)); + auto anim = GH.renderHandler().loadAnimation(AnimationPath::builtin(URI), EImageBlitMode::ALPHA); anim->exportBitmaps(VCMIDirs::get().userExtractedPath()); } diff --git a/client/adventureMap/AdventureMapInterface.cpp b/client/adventureMap/AdventureMapInterface.cpp index 67e914831..255dc2884 100644 --- a/client/adventureMap/AdventureMapInterface.cpp +++ b/client/adventureMap/AdventureMapInterface.cpp @@ -31,6 +31,7 @@ #include "../gui/Shortcut.h" #include "../gui/WindowHandler.h" #include "../render/Canvas.h" +#include "../render/IImage.h" #include "../render/IRenderHandler.h" #include "../CMT.h" #include "../PlayerLocalState.h" @@ -178,7 +179,7 @@ void AdventureMapInterface::dim(Canvas & to) { if(!std::dynamic_pointer_cast(window) && std::dynamic_pointer_cast(window) && isBigWindow(window)) { - to.fillTexture(GH.renderHandler().loadImage(ImagePath::builtin("DiBoxBck"))); + to.fillTexture(GH.renderHandler().loadImage(ImagePath::builtin("DiBoxBck"), EImageBlitMode::OPAQUE)); return; } } diff --git a/client/battle/BattleAnimationClasses.cpp b/client/battle/BattleAnimationClasses.cpp index 8a98605f6..434f6c4d7 100644 --- a/client/battle/BattleAnimationClasses.cpp +++ b/client/battle/BattleAnimationClasses.cpp @@ -883,7 +883,7 @@ uint32_t CastAnimation::getAttackClimaxFrame() const EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, int effects, bool reversed): BattleAnimation(owner), - animation(GH.renderHandler().loadAnimation(animationName)), + animation(GH.renderHandler().loadAnimation(animationName, EImageBlitMode::ALPHA)), effectFlags(effects), effectFinished(false), reversed(reversed) diff --git a/client/battle/BattleFieldController.cpp b/client/battle/BattleFieldController.cpp index aa197002a..5ad5759dc 100644 --- a/client/battle/BattleFieldController.cpp +++ b/client/battle/BattleFieldController.cpp @@ -123,17 +123,17 @@ BattleFieldController::BattleFieldController(BattleInterface & owner): //preparing cells and hexes cellBorder = GH.renderHandler().loadImage(ImagePath::builtin("CCELLGRD.BMP"), EImageBlitMode::COLORKEY); - cellShade = GH.renderHandler().loadImage(ImagePath::builtin("CCELLSHD.BMP")); + cellShade = GH.renderHandler().loadImage(ImagePath::builtin("CCELLSHD.BMP"), EImageBlitMode::ALPHA); cellUnitMovementHighlight = GH.renderHandler().loadImage(ImagePath::builtin("UnitMovementHighlight.PNG"), EImageBlitMode::COLORKEY); cellUnitMaxMovementHighlight = GH.renderHandler().loadImage(ImagePath::builtin("UnitMaxMovementHighlight.PNG"), EImageBlitMode::COLORKEY); - attackCursors = GH.renderHandler().loadAnimation(AnimationPath::builtin("CRCOMBAT")); - spellCursors = GH.renderHandler().loadAnimation(AnimationPath::builtin("CRSPELL")); + attackCursors = GH.renderHandler().loadAnimation(AnimationPath::builtin("CRCOMBAT"), EImageBlitMode::COLORKEY); + spellCursors = GH.renderHandler().loadAnimation(AnimationPath::builtin("CRSPELL"), EImageBlitMode::COLORKEY); initializeHexEdgeMaskToFrameIndex(); - rangedFullDamageLimitImages = GH.renderHandler().loadAnimation(AnimationPath::builtin("battle/rangeHighlights/rangeHighlightsGreen.json")); - shootingRangeLimitImages = GH.renderHandler().loadAnimation(AnimationPath::builtin("battle/rangeHighlights/rangeHighlightsRed.json")); + rangedFullDamageLimitImages = GH.renderHandler().loadAnimation(AnimationPath::builtin("battle/rangeHighlights/rangeHighlightsGreen.json"), EImageBlitMode::COLORKEY); + shootingRangeLimitImages = GH.renderHandler().loadAnimation(AnimationPath::builtin("battle/rangeHighlights/rangeHighlightsRed.json"), EImageBlitMode::COLORKEY); flipRangeLimitImagesIntoPositions(rangedFullDamageLimitImages); flipRangeLimitImagesIntoPositions(shootingRangeLimitImages); diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index e8af74746..3f4ab4838 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -397,7 +397,7 @@ BattleHero::BattleHero(const BattleInterface & owner, const CGHeroInstance * her else animationPath = hero->type->heroClass->imageBattleMale; - animation = GH.renderHandler().loadAnimation(animationPath); + animation = GH.renderHandler().loadAnimation(animationPath, EImageBlitMode::ALPHA); pos.w = 64; pos.h = 136; @@ -408,9 +408,9 @@ BattleHero::BattleHero(const BattleInterface & owner, const CGHeroInstance * her animation->verticalFlip(); if(defender) - flagAnimation = GH.renderHandler().loadAnimation(AnimationPath::builtin("CMFLAGR")); + flagAnimation = GH.renderHandler().loadAnimation(AnimationPath::builtin("CMFLAGR"), EImageBlitMode::COLORKEY); else - flagAnimation = GH.renderHandler().loadAnimation(AnimationPath::builtin("CMFLAGL")); + flagAnimation = GH.renderHandler().loadAnimation(AnimationPath::builtin("CMFLAGL"), EImageBlitMode::COLORKEY); flagAnimation->playerColored(hero->tempOwner); @@ -503,7 +503,6 @@ HeroInfoBasicPanel::HeroInfoBasicPanel(const InfoAboutHero & hero, Point * posit if(initializeBackground) { background = std::make_shared(ImagePath::builtin("CHRPOP")); - background->getSurface()->setBlitMode(EImageBlitMode::OPAQUE); background->setPlayerColor(hero.owner); } @@ -571,10 +570,8 @@ StackInfoBasicPanel::StackInfoBasicPanel(const CStack * stack, bool initializeBa { background = std::make_shared(ImagePath::builtin("CCRPOP")); background->pos.y += 37; - background->getSurface()->setBlitMode(EImageBlitMode::OPAQUE); background->setPlayerColor(stack->getOwner()); background2 = std::make_shared(ImagePath::builtin("CHRPOP")); - background2->getSurface()->setBlitMode(EImageBlitMode::OPAQUE); background2->setPlayerColor(stack->getOwner()); } diff --git a/client/battle/BattleObstacleController.cpp b/client/battle/BattleObstacleController.cpp index 80ecacdf6..21bf9caad 100644 --- a/client/battle/BattleObstacleController.cpp +++ b/client/battle/BattleObstacleController.cpp @@ -58,7 +58,7 @@ void BattleObstacleController::loadObstacleImage(const CObstacleInstance & oi) } else { - auto animation = GH.renderHandler().loadAnimation(animationName); + auto animation = GH.renderHandler().loadAnimation(animationName, EImageBlitMode::COLORKEY); animationsCache[animationName] = animation; } } @@ -84,7 +84,7 @@ void BattleObstacleController::obstacleRemoved(const std::vectorgetImage(0, 0); if(!first) continue; @@ -110,7 +110,7 @@ void BattleObstacleController::obstaclePlaced(const std::vectorvisibleForSide(side.value(), owner.getBattle()->battleHasNativeStack(side.value()))) continue; - auto animation = GH.renderHandler().loadAnimation(oi->getAppearAnimation()); + auto animation = GH.renderHandler().loadAnimation(oi->getAppearAnimation(), EImageBlitMode::ALPHA); auto first = animation->getImage(0, 0); if(!first) continue; diff --git a/client/battle/BattleProjectileController.cpp b/client/battle/BattleProjectileController.cpp index c81852446..694ab4e0f 100644 --- a/client/battle/BattleProjectileController.cpp +++ b/client/battle/BattleProjectileController.cpp @@ -192,7 +192,7 @@ void BattleProjectileController::initStackProjectile(const CStack * stack) std::shared_ptr BattleProjectileController::createProjectileImage(const AnimationPath & path ) { - std::shared_ptr projectile = GH.renderHandler().loadAnimation(path); + std::shared_ptr projectile = GH.renderHandler().loadAnimation(path, EImageBlitMode::COLORKEY); if(projectile->size(1) != 0) logAnim->error("Expected empty group 1 in stack projectile"); diff --git a/client/battle/BattleSiegeController.cpp b/client/battle/BattleSiegeController.cpp index 0d1321b0d..05923b2e9 100644 --- a/client/battle/BattleSiegeController.cpp +++ b/client/battle/BattleSiegeController.cpp @@ -182,7 +182,7 @@ BattleSiegeController::BattleSiegeController(BattleInterface & owner, const CGTo if ( !getWallPieceExistence(EWallVisual::EWallVisual(g)) ) continue; - wallPieceImages[g] = GH.renderHandler().loadImage(getWallPieceImageName(EWallVisual::EWallVisual(g), EWallState::REINFORCED)); + wallPieceImages[g] = GH.renderHandler().loadImage(getWallPieceImageName(EWallVisual::EWallVisual(g), EWallState::REINFORCED), EImageBlitMode::COLORKEY); } } @@ -248,7 +248,7 @@ void BattleSiegeController::gateStateChanged(const EGateState state) wallPieceImages[EWallVisual::GATE] = nullptr; if (stateId != EWallState::NONE) - wallPieceImages[EWallVisual::GATE] = GH.renderHandler().loadImage(getWallPieceImageName(EWallVisual::GATE, stateId)); + wallPieceImages[EWallVisual::GATE] = GH.renderHandler().loadImage(getWallPieceImageName(EWallVisual::GATE, stateId), EImageBlitMode::COLORKEY); if (playSound) CCS->soundh->playSound(soundBase::DRAWBRG); @@ -357,7 +357,7 @@ void BattleSiegeController::stackIsCatapulting(const CatapultAttack & ca) auto wallState = EWallState(owner.getBattle()->battleGetWallState(attackInfo.attackedPart)); - wallPieceImages[wallId] = GH.renderHandler().loadImage(getWallPieceImageName(EWallVisual::EWallVisual(wallId), wallState)); + wallPieceImages[wallId] = GH.renderHandler().loadImage(getWallPieceImageName(EWallVisual::EWallVisual(wallId), wallState), EImageBlitMode::COLORKEY); } } diff --git a/client/battle/CreatureAnimation.cpp b/client/battle/CreatureAnimation.cpp index 8627ebfc7..8fcfd1aeb 100644 --- a/client/battle/CreatureAnimation.cpp +++ b/client/battle/CreatureAnimation.cpp @@ -199,8 +199,8 @@ CreatureAnimation::CreatureAnimation(const AnimationPath & name_, TSpeedControll speedController(controller), once(false) { - forward = GH.renderHandler().loadAnimation(name_); - reverse = GH.renderHandler().loadAnimation(name_); + forward = GH.renderHandler().loadAnimation(name_, EImageBlitMode::ALPHA); + reverse = GH.renderHandler().loadAnimation(name_, EImageBlitMode::ALPHA); // if necessary, add one frame into vcmi-only group DEAD if(forward->size(size_t(ECreatureAnimType::DEAD)) == 0) diff --git a/client/gui/CursorHandler.cpp b/client/gui/CursorHandler.cpp index b09517344..146f27b9c 100644 --- a/client/gui/CursorHandler.cpp +++ b/client/gui/CursorHandler.cpp @@ -47,10 +47,10 @@ CursorHandler::CursorHandler() cursors = { - GH.renderHandler().loadAnimation(AnimationPath::builtin("CRADVNTR")), - GH.renderHandler().loadAnimation(AnimationPath::builtin("CRCOMBAT")), - GH.renderHandler().loadAnimation(AnimationPath::builtin("CRDEFLT")), - GH.renderHandler().loadAnimation(AnimationPath::builtin("CRSPELL")) + GH.renderHandler().loadAnimation(AnimationPath::builtin("CRADVNTR"), EImageBlitMode::COLORKEY), + GH.renderHandler().loadAnimation(AnimationPath::builtin("CRCOMBAT"), EImageBlitMode::COLORKEY), + GH.renderHandler().loadAnimation(AnimationPath::builtin("CRDEFLT"), EImageBlitMode::COLORKEY), + GH.renderHandler().loadAnimation(AnimationPath::builtin("CRSPELL"), EImageBlitMode::COLORKEY) }; set(Cursor::Map::POINTER); @@ -101,7 +101,7 @@ void CursorHandler::dragAndDropCursor(std::shared_ptr image) void CursorHandler::dragAndDropCursor (const AnimationPath & path, size_t index) { - auto anim = GH.renderHandler().loadAnimation(path); + auto anim = GH.renderHandler().loadAnimation(path, EImageBlitMode::COLORKEY); dragAndDropCursor(anim->getImage(index)); } diff --git a/client/lobby/SelectionTab.cpp b/client/lobby/SelectionTab.cpp index 490b0c633..c65944237 100644 --- a/client/lobby/SelectionTab.cpp +++ b/client/lobby/SelectionTab.cpp @@ -884,7 +884,7 @@ SelectionTab::ListItem::ListItem(Point position) : CIntObject(LCLICK, position) { OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; - pictureEmptyLine = std::make_shared(GH.renderHandler().loadImage(ImagePath::builtin("camcust")), Rect(25, 121, 349, 26), -8, -14); + pictureEmptyLine = std::make_shared(ImagePath::builtin("camcust"), Rect(25, 121, 349, 26), -8, -14); labelName = std::make_shared(184, 0, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, "", 185); labelName->setAutoRedraw(false); labelAmountOfPlayers = std::make_shared(8, 0, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE); diff --git a/client/mapView/MapRenderer.cpp b/client/mapView/MapRenderer.cpp index 095842410..fc8ce7479 100644 --- a/client/mapView/MapRenderer.cpp +++ b/client/mapView/MapRenderer.cpp @@ -104,12 +104,9 @@ void MapTileStorage::load(size_t index, const AnimationPath & filename, EImageBl for(auto & entry : terrainAnimations) { if (!filename.empty()) - entry = GH.renderHandler().loadAnimation(filename); + entry = GH.renderHandler().loadAnimation(filename, blitMode); else entry = GH.renderHandler().createAnimation(); - - for(size_t i = 0; i < entry->size(); ++i) - entry->getImage(i)->setBlitMode(blitMode); } terrainAnimations[1]->verticalFlip(); @@ -249,7 +246,7 @@ uint8_t MapRendererRoad::checksum(IMapRendererContext & context, const int3 & co MapRendererBorder::MapRendererBorder() { - animation = GH.renderHandler().loadAnimation(AnimationPath::builtin("EDG")); + animation = GH.renderHandler().loadAnimation(AnimationPath::builtin("EDG"), EImageBlitMode::OPAQUE); } size_t MapRendererBorder::getIndexForTile(IMapRendererContext & context, const int3 & tile) @@ -310,11 +307,8 @@ uint8_t MapRendererBorder::checksum(IMapRendererContext & context, const int3 & MapRendererFow::MapRendererFow() { - fogOfWarFullHide = GH.renderHandler().loadAnimation(AnimationPath::builtin("TSHRC")); - fogOfWarPartialHide = GH.renderHandler().loadAnimation(AnimationPath::builtin("TSHRE")); - - for(size_t i = 0; i < fogOfWarFullHide->size(); ++i) - fogOfWarFullHide->getImage(i)->setBlitMode(EImageBlitMode::OPAQUE); + fogOfWarFullHide = GH.renderHandler().loadAnimation(AnimationPath::builtin("TSHRC"), EImageBlitMode::OPAQUE); + fogOfWarPartialHide = GH.renderHandler().loadAnimation(AnimationPath::builtin("TSHRE"), EImageBlitMode::ALPHA); static const std::vector rotations = {22, 15, 2, 13, 12, 16, 28, 17, 20, 19, 7, 24, 26, 25, 30, 32, 27}; @@ -398,7 +392,7 @@ std::shared_ptr MapRendererObjects::getAnimation(const AnimationPath if(it != animations.end()) return it->second; - auto ret = GH.renderHandler().loadAnimation(filename); + auto ret = GH.renderHandler().loadAnimation(filename, EImageBlitMode::ALPHA); animations[filename] = ret; if(generateMovementGroups) @@ -619,7 +613,7 @@ uint8_t MapRendererOverlay::checksum(IMapRendererContext & context, const int3 & } MapRendererPath::MapRendererPath() - : pathNodes(GH.renderHandler().loadAnimation(AnimationPath::builtin("ADAG"))) + : pathNodes(GH.renderHandler().loadAnimation(AnimationPath::builtin("ADAG"), EImageBlitMode::ALPHA)) { } diff --git a/client/mapView/MapViewCache.cpp b/client/mapView/MapViewCache.cpp index 8534e8a6c..cb9ff8d81 100644 --- a/client/mapView/MapViewCache.cpp +++ b/client/mapView/MapViewCache.cpp @@ -31,14 +31,11 @@ MapViewCache::MapViewCache(const std::shared_ptr & model) : model(model) , cachedLevel(0) , mapRenderer(new MapRenderer()) - , iconsStorage(GH.renderHandler().loadAnimation(AnimationPath::builtin("VwSymbol"))) + , iconsStorage(GH.renderHandler().loadAnimation(AnimationPath::builtin("VwSymbol"), EImageBlitMode::COLORKEY)) , intermediate(new Canvas(Point(32, 32))) , terrain(new Canvas(model->getCacheDimensionsPixels())) , terrainTransition(new Canvas(model->getPixelsVisibleDimensions())) { - for(size_t i = 0; i < iconsStorage->size(); ++i) - iconsStorage->getImage(i)->setBlitMode(EImageBlitMode::COLORKEY); - Point visibleSize = model->getTilesVisibleDimensions(); terrainChecksum.resize(boost::extents[visibleSize.x][visibleSize.y]); tilesUpToDate.resize(boost::extents[visibleSize.x][visibleSize.y]); diff --git a/client/render/CAnimation.cpp b/client/render/CAnimation.cpp index 6ceabc0cf..a90007bb2 100644 --- a/client/render/CAnimation.cpp +++ b/client/render/CAnimation.cpp @@ -34,9 +34,9 @@ bool CAnimation::loadFrame(size_t frame, size_t group) //try to get image from def if(source[group][frame].isNull()) - image = GH.renderHandler().loadImage(name, frame, group); + image = GH.renderHandler().loadImage(name, frame, group, mode); else - image = GH.renderHandler().loadImage(source[group][frame]); + image = GH.renderHandler().loadImage(source[group][frame], mode); if(image) { @@ -104,9 +104,10 @@ void CAnimation::printError(size_t frame, size_t group, std::string type) const logGlobal->error("%s error: Request for frame not present in CAnimation! File name: %s, Group: %d, Frame: %d", type, name.getOriginalName(), group, frame); } -CAnimation::CAnimation(const AnimationPath & Name, std::map > layout): +CAnimation::CAnimation(const AnimationPath & Name, std::map > layout, EImageBlitMode mode): name(boost::starts_with(Name.getName(), "SPRITES") ? Name : Name.addPrefix("SPRITES/")), - source(layout) + source(layout), + mode(mode) { if(source.empty()) logAnim->error("Animation %s failed to load", Name.getOriginalName()); diff --git a/client/render/CAnimation.h b/client/render/CAnimation.h index 7cc974af0..28d1753fc 100644 --- a/client/render/CAnimation.h +++ b/client/render/CAnimation.h @@ -9,6 +9,8 @@ */ #pragma once +#include "IImage.h" + #include "../../lib/GameConstants.h" #include "../../lib/filesystem/ResourcePath.h" @@ -17,7 +19,6 @@ class JsonNode; VCMI_LIB_NAMESPACE_END class CDefFile; -class IImage; class RenderHandler; /// Class for handling animation @@ -33,6 +34,8 @@ private: //animation file name AnimationPath name; + EImageBlitMode mode; + //loader, will be called by load(), require opened def file for loading from it. Returns true if image is loaded bool loadFrame(size_t frame, size_t group); @@ -44,7 +47,7 @@ private: std::shared_ptr getImageImpl(size_t frame, size_t group=0, bool verbose=true); public: - CAnimation(const AnimationPath & Name, std::map > layout); + CAnimation(const AnimationPath & Name, std::map > layout, EImageBlitMode mode); CAnimation(); ~CAnimation(); diff --git a/client/render/IImage.h b/client/render/IImage.h index 63c6ea186..6247c1269 100644 --- a/client/render/IImage.h +++ b/client/render/IImage.h @@ -26,13 +26,17 @@ class ColorFilter; /// Defines which blit method will be selected when image is used for rendering enum class EImageBlitMode { - /// Image can have no transparency and can be only used as background + /// Preferred for images that don't need any background + /// Indexed or RGBA: Image can have no transparency and can be only used as background OPAQUE, - /// Image can have only a single color as transparency and has no semi-transparent areas + /// Preferred for images that may need transparency + /// Indexed: Image can have only a single color as transparency and has no semi-transparent areas + /// RGBA: full alpha transparency range, e.g. shadows COLORKEY, - /// Image might have full alpha transparency range, e.g. shadows + /// Should be avoided if possible, use only for images that use def's with semi-transparency + /// Indexed or RGBA: Image might have full alpha transparency range, e.g. shadows ALPHA }; @@ -85,7 +89,7 @@ public: virtual void exportBitmap(const boost::filesystem::path & path) const = 0; virtual bool isTransparent(const Point & coords) const = 0; - virtual std::shared_ptr createImageReference() = 0; + virtual std::shared_ptr createImageReference(EImageBlitMode mode) = 0; virtual std::shared_ptr horizontalFlip() const = 0; virtual std::shared_ptr verticalFlip() const = 0; diff --git a/client/render/IRenderHandler.h b/client/render/IRenderHandler.h index b8103ab92..ed54b9d0d 100644 --- a/client/render/IRenderHandler.h +++ b/client/render/IRenderHandler.h @@ -30,17 +30,16 @@ public: virtual void onLibraryLoadingFinished(const Services * services) = 0; /// Loads image using given path - virtual std::shared_ptr loadImage(const JsonNode & config) = 0; - virtual std::shared_ptr loadImage(const ImagePath & path) = 0; + virtual std::shared_ptr loadImage(const JsonNode & config, EImageBlitMode mode) = 0; virtual std::shared_ptr loadImage(const ImagePath & path, EImageBlitMode mode) = 0; - virtual std::shared_ptr loadImage(const AnimationPath & path, int frame, int group) = 0; + virtual std::shared_ptr loadImage(const AnimationPath & path, int frame, int group, EImageBlitMode mode) = 0; /// temporary compatibility method. Creates IImage from existing SDL_Surface /// Surface will be shared, caller must still free it with SDL_FreeSurface virtual std::shared_ptr createImage(SDL_Surface * source) = 0; /// Loads animation using given path - virtual std::shared_ptr loadAnimation(const AnimationPath & path) = 0; + virtual std::shared_ptr loadAnimation(const AnimationPath & path, EImageBlitMode mode) = 0; /// Creates empty CAnimation. Temporary compatibility method virtual std::shared_ptr createAnimation() = 0; diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index ac62d473a..6168e9358 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -164,7 +164,7 @@ RenderHandler::AnimationLayoutMap & RenderHandler::getAnimationLayout(const Anim std::shared_ptr RenderHandler::loadImageFromSingleFile(const ImagePath & path) { - auto result = std::make_shared(path, EImageBlitMode::COLORKEY); + auto result = std::make_shared(path); imageFiles[ImageLocator(path)] = result; return result; } @@ -223,37 +223,32 @@ std::shared_ptr RenderHandler::loadImageImpl(const ImageLocator & l return result; } -std::shared_ptr RenderHandler::loadImage(const JsonNode & config) +std::shared_ptr RenderHandler::loadImage(const JsonNode & config, EImageBlitMode mode) { if (config.isString()) - return loadImageImpl(ImageLocator(ImagePath::fromJson(config)))->createImageReference(); + return loadImageImpl(ImageLocator(ImagePath::fromJson(config)))->createImageReference(mode); else - return loadImageImpl(ImageLocator(config))->createImageReference(); + return loadImageImpl(ImageLocator(config))->createImageReference(mode); } -std::shared_ptr RenderHandler::loadImage(const AnimationPath & path, int frame, int group) +std::shared_ptr RenderHandler::loadImage(const AnimationPath & path, int frame, int group, EImageBlitMode mode) { - return loadImageImpl(ImageLocator(path, frame, group))->createImageReference(); -} - -std::shared_ptr RenderHandler::loadImage(const ImagePath & path) -{ - return loadImage(path, EImageBlitMode::ALPHA); + return loadImageImpl(ImageLocator(path, frame, group))->createImageReference(mode); } std::shared_ptr RenderHandler::loadImage(const ImagePath & path, EImageBlitMode mode) { - return loadImageImpl(ImageLocator(path))->createImageReference(); + return loadImageImpl(ImageLocator(path))->createImageReference(mode); } std::shared_ptr RenderHandler::createImage(SDL_Surface * source) { - return std::make_shared(source, EImageBlitMode::ALPHA)->createImageReference(); + return std::make_shared(source)->createImageReference(EImageBlitMode::ALPHA); } -std::shared_ptr RenderHandler::loadAnimation(const AnimationPath & path) +std::shared_ptr RenderHandler::loadAnimation(const AnimationPath & path, EImageBlitMode mode) { - return std::make_shared(path, getAnimationLayout(path)); + return std::make_shared(path, getAnimationLayout(path), mode); } std::shared_ptr RenderHandler::createAnimation() diff --git a/client/renderSDL/RenderHandler.h b/client/renderSDL/RenderHandler.h index e2a8b8d2a..d8dd8ba43 100644 --- a/client/renderSDL/RenderHandler.h +++ b/client/renderSDL/RenderHandler.h @@ -58,12 +58,11 @@ public: // IRenderHandler implementation void onLibraryLoadingFinished(const Services * services) override; - std::shared_ptr loadImage(const JsonNode & config) override; - std::shared_ptr loadImage(const ImagePath & path) override; + std::shared_ptr loadImage(const JsonNode & config, EImageBlitMode mode) override; std::shared_ptr loadImage(const ImagePath & path, EImageBlitMode mode) override; - std::shared_ptr loadImage(const AnimationPath & path, int frame, int group) override; + std::shared_ptr loadImage(const AnimationPath & path, int frame, int group, EImageBlitMode mode) override; - std::shared_ptr loadAnimation(const AnimationPath & path) override; + std::shared_ptr loadAnimation(const AnimationPath & path, EImageBlitMode mode) override; std::shared_ptr createImage(SDL_Surface * source) override; std::shared_ptr createAnimation() override; diff --git a/client/renderSDL/SDLImage.cpp b/client/renderSDL/SDLImage.cpp index 6428da2c8..b1bfc703e 100644 --- a/client/renderSDL/SDLImage.cpp +++ b/client/renderSDL/SDLImage.cpp @@ -44,7 +44,7 @@ SDLImageConst::SDLImageConst(CDefFile * data, size_t frame, size_t group) savePalette(); } -SDLImageConst::SDLImageConst(SDL_Surface * from, EImageBlitMode mode) +SDLImageConst::SDLImageConst(SDL_Surface * from) : surf(nullptr), margins(0, 0), fullSize(0, 0), @@ -61,7 +61,7 @@ SDLImageConst::SDLImageConst(SDL_Surface * from, EImageBlitMode mode) fullSize.y = surf->h; } -SDLImageConst::SDLImageConst(const ImagePath & filename, EImageBlitMode mode) +SDLImageConst::SDLImageConst(const ImagePath & filename) : surf(nullptr), margins(0, 0), fullSize(0, 0), @@ -111,10 +111,10 @@ void SDLImageConst::draw(SDL_Surface * where, SDL_Palette * palette, const Point SDL_SetSurfaceAlphaMod(surf, alpha); - if (mode == EImageBlitMode::OPAQUE) - SDL_SetSurfaceBlendMode(surf, SDL_BLENDMODE_NONE); - else + if (mode != EImageBlitMode::OPAQUE && surf->format->Amask != 0) SDL_SetSurfaceBlendMode(surf, SDL_BLENDMODE_BLEND); + else + SDL_SetSurfaceBlendMode(surf, SDL_BLENDMODE_NONE); if (palette && surf->format->palette) SDL_SetSurfacePalette(surf, palette); @@ -151,7 +151,7 @@ std::shared_ptr SDLImageConst::scaleFast(const Point & size) cons else CSDL_Ext::setDefaultColorKey(scaled);//just in case - auto ret = std::make_shared(scaled, EImageBlitMode::ALPHA); + auto ret = std::make_shared(scaled); ret->fullSize.x = (int) round((float)fullSize.x * scaleX); ret->fullSize.y = (int) round((float)fullSize.y * scaleY); @@ -194,18 +194,18 @@ Point SDLImageConst::dimensions() const return fullSize; } -std::shared_ptr SDLImageConst::createImageReference() +std::shared_ptr SDLImageConst::createImageReference(EImageBlitMode mode) { if (surf->format->palette) - return std::make_shared(shared_from_this()); + return std::make_shared(shared_from_this(), mode); else - return std::make_shared(shared_from_this()); + return std::make_shared(shared_from_this(), mode); } std::shared_ptr SDLImageConst::horizontalFlip() const { SDL_Surface * flipped = CSDL_Ext::horizontalFlip(surf); - auto ret = std::make_shared(flipped, EImageBlitMode::ALPHA); + auto ret = std::make_shared(flipped); ret->fullSize = fullSize; ret->margins.x = margins.x; ret->margins.y = fullSize.y - surf->h - margins.y; @@ -217,7 +217,7 @@ std::shared_ptr SDLImageConst::horizontalFlip() const std::shared_ptr SDLImageConst::verticalFlip() const { SDL_Surface * flipped = CSDL_Ext::verticalFlip(surf); - auto ret = std::make_shared(flipped, EImageBlitMode::ALPHA); + auto ret = std::make_shared(flipped); ret->fullSize = fullSize; ret->margins.x = fullSize.x - surf->w - margins.x; ret->margins.y = margins.y; @@ -265,8 +265,8 @@ void SDLImageIndexed::adjustPalette(const ColorFilter & shifter, uint32_t colors } } -SDLImageIndexed::SDLImageIndexed(const std::shared_ptr & image) - :SDLImageBase::SDLImageBase(image) +SDLImageIndexed::SDLImageIndexed(const std::shared_ptr & image, EImageBlitMode mode) + :SDLImageBase::SDLImageBase(image, mode) { auto originalPalette = image->getPalette(); @@ -296,10 +296,10 @@ SDLImageConst::~SDLImageConst() SDL_FreePalette(originalPalette); } -SDLImageBase::SDLImageBase(const std::shared_ptr & image) +SDLImageBase::SDLImageBase(const std::shared_ptr & image, EImageBlitMode mode) :image(image) , alphaValue(SDL_ALPHA_OPAQUE) - , blitMode(EImageBlitMode::ALPHA) + , blitMode(mode) {} void SDLImageRGB::draw(SDL_Surface * where, const Point & pos, const Rect * src) const diff --git a/client/renderSDL/SDLImage.h b/client/renderSDL/SDLImage.h index 4cc8d6cc9..a1631f3bd 100644 --- a/client/renderSDL/SDLImage.h +++ b/client/renderSDL/SDLImage.h @@ -42,9 +42,9 @@ public: //Load image from def file SDLImageConst(CDefFile *data, size_t frame, size_t group=0); //Load from bitmap file - SDLImageConst(const ImagePath & filename, EImageBlitMode blitMode); + SDLImageConst(const ImagePath & filename); //Create using existing surface, extraRef will increase refcount on SDL_Surface - SDLImageConst(SDL_Surface * from, EImageBlitMode blitMode); + SDLImageConst(SDL_Surface * from); ~SDLImageConst(); void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, uint8_t alpha, EImageBlitMode mode) const; @@ -52,7 +52,7 @@ public: void exportBitmap(const boost::filesystem::path & path) const override; Point dimensions() const override; bool isTransparent(const Point & coords) const override; - std::shared_ptr createImageReference() override; + std::shared_ptr createImageReference(EImageBlitMode mode) override; std::shared_ptr horizontalFlip() const override; std::shared_ptr verticalFlip() const override; std::shared_ptr scaleFast(const Point & size) const; @@ -71,7 +71,7 @@ protected: EImageBlitMode blitMode; public: - SDLImageBase(const std::shared_ptr & image); + SDLImageBase(const std::shared_ptr & image, EImageBlitMode mode); void scaleFast(const Point & size) override; void exportBitmap(const boost::filesystem::path & path) const override; @@ -86,7 +86,7 @@ class SDLImageIndexed final : public SDLImageBase SDL_Palette * currentPalette = nullptr; public: - SDLImageIndexed(const std::shared_ptr & image); + SDLImageIndexed(const std::shared_ptr & image, EImageBlitMode mode); ~SDLImageIndexed(); void draw(SDL_Surface * where, const Point & pos, const Rect * src) const override; diff --git a/client/widgets/Images.cpp b/client/widgets/Images.cpp index 14151c5fe..40e3fe646 100644 --- a/client/widgets/Images.cpp +++ b/client/widgets/Images.cpp @@ -50,7 +50,7 @@ CPicture::CPicture( const ImagePath & bmpname ) {} CPicture::CPicture( const ImagePath & bmpname, const Point & position ) - : bg(GH.renderHandler().loadImage(bmpname)) + : bg(GH.renderHandler().loadImage(bmpname, EImageBlitMode::COLORKEY)) , needRefresh(false) { pos.x += position.x; @@ -68,6 +68,14 @@ CPicture::CPicture( const ImagePath & bmpname, const Point & position ) } } +CPicture::CPicture(const ImagePath & bmpname, const Rect &SrcRect, int x, int y) + : CPicture(bmpname, Point(x,y)) +{ + srcRect = SrcRect; + pos.w = srcRect->w; + pos.h = srcRect->h; +} + CPicture::CPicture(std::shared_ptr image, const Rect &SrcRect, int x, int y) : CPicture(image, Point(x,y)) { @@ -113,7 +121,7 @@ void CPicture::setPlayerColor(PlayerColor player) CFilledTexture::CFilledTexture(const ImagePath & imageName, Rect position) : CIntObject(0, position.topLeft()) - , texture(GH.renderHandler().loadImage(imageName)) + , texture(GH.renderHandler().loadImage(imageName, EImageBlitMode::COLORKEY)) { pos.w = position.w; pos.h = position.h; @@ -122,7 +130,7 @@ CFilledTexture::CFilledTexture(const ImagePath & imageName, Rect position) CFilledTexture::CFilledTexture(const ImagePath & imageName, Rect position, Rect imageArea) : CIntObject(0, position.topLeft()) - , texture(GH.renderHandler().loadImage(imageName)) + , texture(GH.renderHandler().loadImage(imageName, EImageBlitMode::COLORKEY)) , imageArea(imageArea) { pos.w = position.w; @@ -176,7 +184,7 @@ CAnimImage::CAnimImage(const AnimationPath & name, size_t Frame, size_t Group, i { pos.x += x; pos.y += y; - anim = GH.renderHandler().loadAnimation(name); + anim = GH.renderHandler().loadAnimation(name, EImageBlitMode::COLORKEY); init(); } @@ -192,7 +200,7 @@ CAnimImage::CAnimImage(const AnimationPath & name, size_t Frame, size_t Group, i //} CAnimImage::CAnimImage(const AnimationPath & name, size_t Frame, Rect targetPos, size_t Group, ui8 Flags): - anim(GH.renderHandler().loadAnimation(name)), + anim(GH.renderHandler().loadAnimation(name, EImageBlitMode::COLORKEY)), frame(Frame), group(Group), flags(Flags), @@ -268,7 +276,7 @@ void CAnimImage::showAll(Canvas & to) void CAnimImage::setAnimationPath(const AnimationPath & name, size_t frame) { this->frame = frame; - anim = GH.renderHandler().loadAnimation(name); + anim = GH.renderHandler().loadAnimation(name, EImageBlitMode::COLORKEY); init(); } @@ -310,7 +318,7 @@ bool CAnimImage::isPlayerColored() const } CShowableAnim::CShowableAnim(int x, int y, const AnimationPath & name, ui8 Flags, ui32 frameTime, size_t Group, uint8_t alpha): - anim(GH.renderHandler().loadAnimation(name)), + anim(GH.renderHandler().loadAnimation(name, EImageBlitMode::ALPHA)), group(Group), frame(0), first(0), diff --git a/client/widgets/Images.h b/client/widgets/Images.h index 49c6d40fe..af459963f 100644 --- a/client/widgets/Images.h +++ b/client/widgets/Images.h @@ -44,6 +44,7 @@ public: /// wrap section of an existing Image CPicture(std::shared_ptr image, const Rect &SrcRext, int x = 0, int y = 0); //wrap subrect of given surface + CPicture(const ImagePath & bmpname, const Rect &SrcRext, int x = 0, int y = 0); //wrap subrect of given surface /// Loads image from specified file name CPicture(const ImagePath & bmpname); diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 1bc347278..3fe1f65cd 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -885,7 +885,7 @@ void CCastleBuildings::enterCastleGate() availableTowns.push_back(t->id.getNum());//add to the list if(settings["general"]["enableUiEnhancements"].Bool()) { - auto image = GH.renderHandler().loadImage(AnimationPath::builtin("ITPA"), t->town->clientInfo.icons[t->hasFort()][false] + 2, 0); + auto image = GH.renderHandler().loadImage(AnimationPath::builtin("ITPA"), t->town->clientInfo.icons[t->hasFort()][false] + 2, 0, EImageBlitMode::OPAQUE); image->scaleFast(Point(35, 23)); images.push_back(image); } diff --git a/client/windows/CMessage.cpp b/client/windows/CMessage.cpp index 71d8a056e..5cf0dd3a7 100644 --- a/client/windows/CMessage.cpp +++ b/client/windows/CMessage.cpp @@ -41,7 +41,7 @@ void CMessage::init() { for(int i = 0; i < PlayerColor::PLAYER_LIMIT_I; i++) { - dialogBorders[i] = GH.renderHandler().loadAnimation(AnimationPath::builtin("DIALGBOX")); + dialogBorders[i] = GH.renderHandler().loadAnimation(AnimationPath::builtin("DIALGBOX"), EImageBlitMode::OPAQUE); for(int j = 0; j < dialogBorders[i]->size(0); j++) { diff --git a/client/windows/CSpellWindow.cpp b/client/windows/CSpellWindow.cpp index bcb223ed6..d3274c304 100644 --- a/client/windows/CSpellWindow.cpp +++ b/client/windows/CSpellWindow.cpp @@ -215,7 +215,7 @@ CSpellWindow::~CSpellWindow() std::shared_ptr CSpellWindow::createBigSpellBook() { - std::shared_ptr img = GH.renderHandler().loadImage(ImagePath::builtin("SpelBack")); + std::shared_ptr img = GH.renderHandler().loadImage(ImagePath::builtin("SpelBack"), EImageBlitMode::OPAQUE); Canvas canvas = Canvas(Point(800, 600)); // edges canvas.draw(img, Point(0, 0), Rect(15, 38, 90, 45)); diff --git a/client/windows/CWindowWithArtifacts.cpp b/client/windows/CWindowWithArtifacts.cpp index 3b3da1ba4..ec9949a5c 100644 --- a/client/windows/CWindowWithArtifacts.cpp +++ b/client/windows/CWindowWithArtifacts.cpp @@ -237,7 +237,7 @@ void CWindowWithArtifacts::setCursorAnimation(const CArtifactInstance & artInst) if(artInst.isScroll() && settings["general"]["enableUiEnhancements"].Bool()) { assert(artInst.getScrollSpellID().num >= 0); - auto image = GH.renderHandler().loadImage(AnimationPath::builtin("spellscr"), artInst.getScrollSpellID().num, 0); + auto image = GH.renderHandler().loadImage(AnimationPath::builtin("spellscr"), artInst.getScrollSpellID().num, 0, EImageBlitMode::COLORKEY); image->scaleFast(Point(44,34)); CCS->curh->dragAndDropCursor(image); From 54099365090678bbc02a5665f9ef2046782657f8 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 13 Jun 2024 10:42:59 +0000 Subject: [PATCH 087/100] Fix build with older SDL --- client/renderSDL/SDL_Extensions.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/client/renderSDL/SDL_Extensions.cpp b/client/renderSDL/SDL_Extensions.cpp index 9a233517e..f846e2de2 100644 --- a/client/renderSDL/SDL_Extensions.cpp +++ b/client/renderSDL/SDL_Extensions.cpp @@ -19,6 +19,8 @@ #include "../../lib/GameConstants.h" #include +#include +#include Rect CSDL_Ext::fromSDL(const SDL_Rect & rect) { @@ -630,7 +632,11 @@ SDL_Surface * CSDL_Ext::scaleSurface(SDL_Surface * surf, int width, int height) SDL_Surface * intermediate = SDL_ConvertSurface(surf, screen->format, 0); SDL_Surface * ret = newSurface(width, height, intermediate); +#if SDL_VERSION_ATLEAST(2,0,16) SDL_SoftStretchLinear(intermediate, nullptr, ret, nullptr); +#else + SDL_SoftStretch(intermediate, nullptr, ret, nullptr); +#endif SDL_FreeSurface(intermediate); return ret; From fd726523a221becfd44707f6dd773c1da52af30e Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 13 Jun 2024 10:44:56 +0000 Subject: [PATCH 088/100] Remove creations of custom animations in runtime --- client/battle/BattleObstacleController.cpp | 45 +++++++++------------- client/battle/BattleObstacleController.h | 6 +-- client/mapView/MapRenderer.cpp | 21 ++++++---- client/render/CAnimation.cpp | 9 ----- client/render/CAnimation.h | 4 -- client/render/IRenderHandler.h | 3 -- client/renderSDL/RenderHandler.cpp | 5 --- client/renderSDL/RenderHandler.h | 1 - mapeditor/Animation.cpp | 8 ---- mapeditor/Animation.h | 3 -- 10 files changed, 35 insertions(+), 70 deletions(-) diff --git a/client/battle/BattleObstacleController.cpp b/client/battle/BattleObstacleController.cpp index 21bf9caad..031d40ee9 100644 --- a/client/battle/BattleObstacleController.cpp +++ b/client/battle/BattleObstacleController.cpp @@ -47,22 +47,16 @@ void BattleObstacleController::loadObstacleImage(const CObstacleInstance & oi) { AnimationPath animationName = oi.getAnimation(); - if (animationsCache.count(animationName) == 0) + if (oi.obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE) { - if (oi.obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE) - { - // obstacle uses single bitmap image for animations - auto animation = GH.renderHandler().createAnimation(); - animation->setCustom(animationName.getName(), 0, 0); - animationsCache[animationName] = animation; - } - else - { - auto animation = GH.renderHandler().loadAnimation(animationName, EImageBlitMode::COLORKEY); - animationsCache[animationName] = animation; - } + // obstacle uses single bitmap image for animations + obstacleImages[oi.uniqueID] = GH.renderHandler().loadImage(animationName.toType(), EImageBlitMode::COLORKEY); + } + else + { + obstacleAnimations[oi.uniqueID] = GH.renderHandler().loadAnimation(animationName, EImageBlitMode::COLORKEY); + obstacleImages[oi.uniqueID] = obstacleAnimations[oi.uniqueID]->getImage(0); } - obstacleAnimations[oi.uniqueID] = animationsCache[animationName]; } void BattleObstacleController::obstacleRemoved(const std::vector & obstacles) @@ -96,6 +90,7 @@ void BattleObstacleController::obstacleRemoved(const std::vectoraddNewAnim(new EffectAnimation(owner, animationPath, whereTo, obstacle["position"].Integer(), 0, true)); obstacleAnimations.erase(oi.id); + obstacleImages.erase(oi.id); //so when multiple obstacles are removed, they show up one after another owner.waitForAnimations(); } @@ -182,26 +177,22 @@ void BattleObstacleController::collectRenderableObjects(BattleRenderer & rendere void BattleObstacleController::tick(uint32_t msPassed) { timePassed += msPassed / 1000.f; + int framesCount = timePassed * AnimationControls::getObstaclesSpeed(); + + for(auto & animation : obstacleAnimations) + { + int frameIndex = framesCount % animation.second->size(0); + obstacleImages[animation.first] = animation.second->getImage(frameIndex, 0); + } } std::shared_ptr BattleObstacleController::getObstacleImage(const CObstacleInstance & oi) { - int framesCount = timePassed * AnimationControls::getObstaclesSpeed(); - std::shared_ptr animation; - // obstacle is not loaded yet, don't show anything - if (obstacleAnimations.count(oi.uniqueID) == 0) + if (obstacleImages.count(oi.uniqueID) == 0) return nullptr; - animation = obstacleAnimations[oi.uniqueID]; - assert(animation); - - if(animation) - { - int frameIndex = framesCount % animation->size(0); - return animation->getImage(frameIndex, 0); - } - return nullptr; + return obstacleImages[oi.uniqueID]; } Point BattleObstacleController::getObstaclePosition(std::shared_ptr image, const CObstacleInstance & obstacle) diff --git a/client/battle/BattleObstacleController.h b/client/battle/BattleObstacleController.h index c4a7467a4..39119cf32 100644 --- a/client/battle/BattleObstacleController.h +++ b/client/battle/BattleObstacleController.h @@ -36,12 +36,12 @@ class BattleObstacleController /// total time, in seconds, since start of battle. Used for animating obstacles float timePassed; - /// cached animations of all obstacles in current battle - std::map> animationsCache; - /// list of all obstacles that are currently being rendered std::map> obstacleAnimations; + /// Current images for all present obstacles + std::map> obstacleImages; + void loadObstacleImage(const CObstacleInstance & oi); std::shared_ptr getObstacleImage(const CObstacleInstance & oi); diff --git a/client/mapView/MapRenderer.cpp b/client/mapView/MapRenderer.cpp index fc8ce7479..bbd3b6001 100644 --- a/client/mapView/MapRenderer.cpp +++ b/client/mapView/MapRenderer.cpp @@ -105,21 +105,28 @@ void MapTileStorage::load(size_t index, const AnimationPath & filename, EImageBl { if (!filename.empty()) entry = GH.renderHandler().loadAnimation(filename, blitMode); - else - entry = GH.renderHandler().createAnimation(); } - terrainAnimations[1]->verticalFlip(); - terrainAnimations[3]->verticalFlip(); + if (terrainAnimations[1]) + terrainAnimations[1]->verticalFlip(); - terrainAnimations[2]->horizontalFlip(); - terrainAnimations[3]->horizontalFlip(); + if (terrainAnimations[3]) + terrainAnimations[3]->verticalFlip(); + + if (terrainAnimations[2]) + terrainAnimations[2]->horizontalFlip(); + + if (terrainAnimations[3]) + terrainAnimations[3]->horizontalFlip(); } std::shared_ptr MapTileStorage::find(size_t fileIndex, size_t rotationIndex, size_t imageIndex) { const auto & animation = animations[fileIndex][rotationIndex]; - return animation->getImage(imageIndex); + if (animation) + return animation->getImage(imageIndex); + else + return nullptr; } MapRendererTerrain::MapRendererTerrain() diff --git a/client/render/CAnimation.cpp b/client/render/CAnimation.cpp index a90007bb2..4d5b9cf86 100644 --- a/client/render/CAnimation.cpp +++ b/client/render/CAnimation.cpp @@ -113,7 +113,6 @@ CAnimation::CAnimation(const AnimationPath & Name, std::maperror("Animation %s failed to load", Name.getOriginalName()); } -CAnimation::CAnimation() = default; CAnimation::~CAnimation() = default; void CAnimation::duplicateImage(const size_t sourceGroup, const size_t sourceFrame, const size_t targetGroup) @@ -142,14 +141,6 @@ void CAnimation::duplicateImage(const size_t sourceGroup, const size_t sourceFra source[targetGroup].push_back(clone); } -void CAnimation::setCustom(std::string filename, size_t frame, size_t group) -{ - if (source[group].size() <= frame) - source[group].resize(frame+1); - source[group][frame]["file"].String() = filename; - //FIXME: update image if already loaded -} - std::shared_ptr CAnimation::getImage(size_t frame, size_t group, bool verbose) { if (!loadFrame(frame, group)) diff --git a/client/render/CAnimation.h b/client/render/CAnimation.h index 28d1753fc..5a6f5c1ae 100644 --- a/client/render/CAnimation.h +++ b/client/render/CAnimation.h @@ -48,16 +48,12 @@ private: std::shared_ptr getImageImpl(size_t frame, size_t group=0, bool verbose=true); public: CAnimation(const AnimationPath & Name, std::map > layout, EImageBlitMode mode); - CAnimation(); ~CAnimation(); //duplicates frame at [sourceGroup, sourceFrame] as last frame in targetGroup //and loads it if animation is preloaded void duplicateImage(const size_t sourceGroup, const size_t sourceFrame, const size_t targetGroup); - //add custom surface to the selected position. - void setCustom(std::string filename, size_t frame, size_t group=0); - std::shared_ptr getImage(size_t frame, size_t group=0, bool verbose=true); void exportBitmaps(const boost::filesystem::path & path) const; diff --git a/client/render/IRenderHandler.h b/client/render/IRenderHandler.h index ed54b9d0d..8647546e8 100644 --- a/client/render/IRenderHandler.h +++ b/client/render/IRenderHandler.h @@ -40,7 +40,4 @@ public: /// Loads animation using given path virtual std::shared_ptr loadAnimation(const AnimationPath & path, EImageBlitMode mode) = 0; - - /// Creates empty CAnimation. Temporary compatibility method - virtual std::shared_ptr createAnimation() = 0; }; diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index 6168e9358..764b2ddf2 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -251,11 +251,6 @@ std::shared_ptr RenderHandler::loadAnimation(const AnimationPath & p return std::make_shared(path, getAnimationLayout(path), mode); } -std::shared_ptr RenderHandler::createAnimation() -{ - return std::make_shared(); -} - void RenderHandler::addImageListEntries(const EntityService * service) { service->forEachBase([this](const Entity * entity, bool & stop) diff --git a/client/renderSDL/RenderHandler.h b/client/renderSDL/RenderHandler.h index d8dd8ba43..90ef898ab 100644 --- a/client/renderSDL/RenderHandler.h +++ b/client/renderSDL/RenderHandler.h @@ -65,5 +65,4 @@ public: std::shared_ptr loadAnimation(const AnimationPath & path, EImageBlitMode mode) override; std::shared_ptr createImage(SDL_Surface * source) override; - std::shared_ptr createAnimation() override; }; diff --git a/mapeditor/Animation.cpp b/mapeditor/Animation.cpp index 94e217610..0b1c89161 100644 --- a/mapeditor/Animation.cpp +++ b/mapeditor/Animation.cpp @@ -711,14 +711,6 @@ void Animation::duplicateImage(const size_t sourceGroup, const size_t sourceFram load(index, targetGroup); } -void Animation::setCustom(std::string filename, size_t frame, size_t group) -{ - if(source[group].size() <= frame) - source[group].resize(frame+1); - source[group][frame]["file"].String() = filename; - //FIXME: update image if already loaded -} - std::shared_ptr Animation::getImage(size_t frame, size_t group, bool verbose) const { auto groupIter = images.find(group); diff --git a/mapeditor/Animation.h b/mapeditor/Animation.h index 2e4556413..253e68981 100644 --- a/mapeditor/Animation.h +++ b/mapeditor/Animation.h @@ -68,9 +68,6 @@ public: // adjust the color of the animation, used in battle spell effects, e.g. Cloned objects - //add custom surface to the selected position. - void setCustom(std::string filename, size_t frame, size_t group = 0); - std::shared_ptr getImage(size_t frame, size_t group = 0, bool verbose = true) const; //all available frames From 2f68beead128d3adf4a1590e41c7b6261665b67a Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 13 Jun 2024 10:52:32 +0000 Subject: [PATCH 089/100] Remove old code --- client/renderSDL/SDL_Extensions.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/client/renderSDL/SDL_Extensions.cpp b/client/renderSDL/SDL_Extensions.cpp index f846e2de2..5e76a59c3 100644 --- a/client/renderSDL/SDL_Extensions.cpp +++ b/client/renderSDL/SDL_Extensions.cpp @@ -361,7 +361,6 @@ int CSDL_Ext::blit8bppAlphaTo24bpp(const SDL_Surface * src, const Rect & srcRect { switch(dst->format->BytesPerPixel) { - case 2: return blit8bppAlphaTo24bppT<2>(src, srcRect, dst, dstPoint); case 3: return blit8bppAlphaTo24bppT<3>(src, srcRect, dst, dstPoint); case 4: return blit8bppAlphaTo24bppT<4>(src, srcRect, dst, dstPoint); default: From e4bed98674d859f60c21112541df1f7385a3c944 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 17 Jun 2024 09:30:16 +0000 Subject: [PATCH 090/100] Replaced raw json nodes with ImageLocator class --- client/CMakeLists.txt | 2 + client/render/CAnimation.cpp | 44 ++++----------------- client/render/CAnimation.h | 10 +++-- client/render/IRenderHandler.h | 4 +- client/render/ImageLocator.cpp | 51 ++++++++++++++++++++++++ client/render/ImageLocator.h | 29 ++++++++++++++ client/renderSDL/RenderHandler.cpp | 62 ++++++------------------------ client/renderSDL/RenderHandler.h | 20 +--------- 8 files changed, 111 insertions(+), 111 deletions(-) create mode 100644 client/render/ImageLocator.cpp create mode 100644 client/render/ImageLocator.h diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 65f156973..9630a478c 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -88,6 +88,7 @@ set(client_SRCS render/Colors.cpp render/Graphics.cpp render/IFont.cpp + render/ImageLocator.cpp renderSDL/CBitmapFont.cpp renderSDL/CBitmapHanFont.cpp @@ -287,6 +288,7 @@ set(client_HEADERS render/IFont.h render/IImage.h render/IImageLoader.h + render/ImageLocator.h render/IRenderHandler.h render/IScreenHandler.h diff --git a/client/render/CAnimation.cpp b/client/render/CAnimation.cpp index 4d5b9cf86..d411ae81b 100644 --- a/client/render/CAnimation.cpp +++ b/client/render/CAnimation.cpp @@ -26,17 +26,9 @@ bool CAnimation::loadFrame(size_t frame, size_t group) } if(auto image = getImageImpl(frame, group, false)) - { return true; - } - std::shared_ptr image; - - //try to get image from def - if(source[group][frame].isNull()) - image = GH.renderHandler().loadImage(name, frame, group, mode); - else - image = GH.renderHandler().loadImage(source[group][frame], mode); + std::shared_ptr image = GH.renderHandler().loadImage(source[group][frame], mode); if(image) { @@ -104,7 +96,7 @@ void CAnimation::printError(size_t frame, size_t group, std::string type) const logGlobal->error("%s error: Request for frame not present in CAnimation! File name: %s, Group: %d, Frame: %d", type, name.getOriginalName(), group, frame); } -CAnimation::CAnimation(const AnimationPath & Name, std::map > layout, EImageBlitMode mode): +CAnimation::CAnimation(const AnimationPath & Name, std::map > layout, EImageBlitMode mode): name(boost::starts_with(Name.getName(), "SPRITES") ? Name : Name.addPrefix("SPRITES/")), source(layout), mode(mode) @@ -129,15 +121,7 @@ void CAnimation::duplicateImage(const size_t sourceGroup, const size_t sourceFra return; } - JsonNode clone(source[sourceGroup][sourceFrame]); - - if(clone.getType() == JsonNode::JsonType::DATA_NULL) - { - clone["animation"].String() = name.getName(); - clone["group"].Integer() = sourceGroup; - clone["frame"].Integer() = sourceFrame; - } - + ImageLocator clone(source[sourceGroup][sourceFrame]); source[targetGroup].push_back(clone); } @@ -195,15 +179,8 @@ void CAnimation::horizontalFlip(size_t frame, size_t group) // ignore - image not loaded } - JsonNode & config = source.at(group).at(frame); - if (config.isNull()) - { - config["animation"].String() = name.getName(); - config["frame"].Integer() = frame; - config["group"].Integer() = group; - } - - config["horizontalFlip"].Bool() = !config["horizontalFlip"].Bool(); + ImageLocator & locator = source.at(group).at(frame); + locator.horizontalFlip = !locator.horizontalFlip; } void CAnimation::verticalFlip(size_t frame, size_t group) @@ -217,15 +194,8 @@ void CAnimation::verticalFlip(size_t frame, size_t group) // ignore - image not loaded } - JsonNode & config = source.at(group).at(frame); - if (config.isNull()) - { - config["animation"].String() = name.getName(); - config["frame"].Integer() = frame; - config["group"].Integer() = group; - } - - config["verticalFlip"].Bool() = !config["verticalFlip"].Bool(); + ImageLocator & locator = source.at(group).at(frame); + locator.verticalFlip = !locator.verticalFlip; } void CAnimation::playerColored(PlayerColor player) diff --git a/client/render/CAnimation.h b/client/render/CAnimation.h index 5a6f5c1ae..13b96ed25 100644 --- a/client/render/CAnimation.h +++ b/client/render/CAnimation.h @@ -10,6 +10,7 @@ #pragma once #include "IImage.h" +#include "ImageLocator.h" #include "../../lib/GameConstants.h" #include "../../lib/filesystem/ResourcePath.h" @@ -25,8 +26,8 @@ class RenderHandler; class CAnimation { private: - //source[group][position] - file with this frame, if string is empty - image located in def file - std::map > source; + //source[group][position] - location of this frame + std::map > source; //bitmap[group][position], store objects with loaded bitmaps std::map > > images; @@ -36,6 +37,9 @@ private: EImageBlitMode mode; + // current player color, if any + PlayerColor player = PlayerColor::CANNOT_DETERMINE; + //loader, will be called by load(), require opened def file for loading from it. Returns true if image is loaded bool loadFrame(size_t frame, size_t group); @@ -47,7 +51,7 @@ private: std::shared_ptr getImageImpl(size_t frame, size_t group=0, bool verbose=true); public: - CAnimation(const AnimationPath & Name, std::map > layout, EImageBlitMode mode); + CAnimation(const AnimationPath & Name, std::map > layout, EImageBlitMode mode); ~CAnimation(); //duplicates frame at [sourceGroup, sourceFrame] as last frame in targetGroup diff --git a/client/render/IRenderHandler.h b/client/render/IRenderHandler.h index 8647546e8..aa16e4acf 100644 --- a/client/render/IRenderHandler.h +++ b/client/render/IRenderHandler.h @@ -9,7 +9,7 @@ */ #pragma once -#include "../../lib/filesystem/ResourcePath.h" +#include "ImageLocator.h" VCMI_LIB_NAMESPACE_BEGIN class Services; @@ -30,7 +30,7 @@ public: virtual void onLibraryLoadingFinished(const Services * services) = 0; /// Loads image using given path - virtual std::shared_ptr loadImage(const JsonNode & config, EImageBlitMode mode) = 0; + virtual std::shared_ptr loadImage(const ImageLocator & locator, EImageBlitMode mode) = 0; virtual std::shared_ptr loadImage(const ImagePath & path, EImageBlitMode mode) = 0; virtual std::shared_ptr loadImage(const AnimationPath & path, int frame, int group, EImageBlitMode mode) = 0; diff --git a/client/render/ImageLocator.cpp b/client/render/ImageLocator.cpp new file mode 100644 index 000000000..f1c94462e --- /dev/null +++ b/client/render/ImageLocator.cpp @@ -0,0 +1,51 @@ +/* + * ImageLocator.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#include "StdInc.h" +#include "ImageLocator.h" + +#include "../../lib/json/JsonNode.h" + + +ImageLocator::ImageLocator(const JsonNode & config) + : image(ImagePath::fromJson(config["file"])) + , defFile(AnimationPath::fromJson(config["defFile"])) + , defFrame(config["defFrame"].Integer()) + , defGroup(config["defGroup"].Integer()) + , verticalFlip(config["verticalFlip"].Bool()) + , horizontalFlip(config["horizontalFlip"].Bool()) +{ +} + +ImageLocator::ImageLocator(const ImagePath & path) + : image(path) +{ +} + +ImageLocator::ImageLocator(const AnimationPath & path, int frame, int group) + : defFile(path) + , defFrame(frame) + , defGroup(group) +{ +} + +bool ImageLocator::operator<(const ImageLocator & other) const +{ + if(image != other.image) + return image < other.image; + if(defFile != other.defFile) + return defFile < other.defFile; + if(defGroup != other.defGroup) + return defGroup < other.defGroup; + if(defFrame != other.defFrame) + return defFrame < other.defFrame; + if(verticalFlip != other.verticalFlip) + return verticalFlip < other.verticalFlip; + return horizontalFlip < other.horizontalFlip; +} diff --git a/client/render/ImageLocator.h b/client/render/ImageLocator.h new file mode 100644 index 000000000..13b6f1625 --- /dev/null +++ b/client/render/ImageLocator.h @@ -0,0 +1,29 @@ +/* + * ImageLocator.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#pragma once + +#include "../../lib/filesystem/ResourcePath.h" + +struct ImageLocator +{ + std::optional image; + std::optional defFile; + int defFrame = -1; + int defGroup = -1; + + bool verticalFlip = false; + bool horizontalFlip = false; + + ImageLocator() = default; + ImageLocator(const JsonNode & config); + ImageLocator(const ImagePath & path); + ImageLocator(const AnimationPath & path, int frame, int group); + bool operator < (const ImageLocator & other) const; +}; diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index 764b2ddf2..bfaf83ec1 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -27,43 +27,6 @@ #include #include -RenderHandler::ImageLocator::ImageLocator(const JsonNode & config) - : image(ImagePath::fromJson(config["file"])) - , animation(AnimationPath::fromJson(config["animation"])) - , frame(config["frame"].Integer()) - , group(config["group"].Integer()) - , verticalFlip(config["verticalFlip"].Bool()) - , horizontalFlip(config["horizontalFlip"].Bool()) -{ -} - -RenderHandler::ImageLocator::ImageLocator(const ImagePath & path) - : image(path) -{ -} - -RenderHandler::ImageLocator::ImageLocator(const AnimationPath & path, int frame, int group) - : animation(path) - , frame(frame) - , group(group) -{ -} - -bool RenderHandler::ImageLocator::operator<(const ImageLocator & other) const -{ - if(image != other.image) - return image < other.image; - if(animation != other.animation) - return animation < other.animation; - if(group != other.group) - return group < other.group; - if(frame != other.frame) - return frame < other.frame; - if(verticalFlip != other.verticalFlip) - return verticalFlip < other.verticalFlip; - return horizontalFlip < other.horizontalFlip; -} - std::shared_ptr RenderHandler::getAnimationFile(const AnimationPath & path) { AnimationPath actualPath = boost::starts_with(path.getName(), "SPRITES") ? path : path.addPrefix("SPRITES/"); @@ -179,14 +142,14 @@ std::shared_ptr RenderHandler::loadImageFromAnimationFileUncached(c return loadImageFromSingleFile(ImagePath::builtin("DEFAULT")); const auto & config = layout.at(group).at(frame); - if (config.isNull()) + if (config.image) { - auto defFile = getAnimationFile(path); - return std::make_shared(defFile.get(), frame, group); + return loadImageImpl(ImageLocator(config)); } else { - return loadImageImpl(ImageLocator(config)); + auto defFile = getAnimationFile(path); + return std::make_shared(defFile.get(), frame, group); } } @@ -205,10 +168,10 @@ std::shared_ptr RenderHandler::loadImageImpl(const ImageLocator & l std::shared_ptr result; - if (!locator.image.empty()) - result = loadImageFromSingleFile(locator.image); - else if (!locator.animation.empty()) - result = loadImageFromAnimationFile(locator.animation, locator.frame, locator.group); + if (!locator.image) + result = loadImageFromSingleFile(*locator.image); + else if (locator.defFile) + result = loadImageFromAnimationFile(*locator.defFile, locator.defFrame, locator.defGroup); if (!result) result = loadImageFromSingleFile(ImagePath::builtin("DEFAULT")); @@ -223,17 +186,14 @@ std::shared_ptr RenderHandler::loadImageImpl(const ImageLocator & l return result; } -std::shared_ptr RenderHandler::loadImage(const JsonNode & config, EImageBlitMode mode) +std::shared_ptr RenderHandler::loadImage(const ImageLocator & locator, EImageBlitMode mode) { - if (config.isString()) - return loadImageImpl(ImageLocator(ImagePath::fromJson(config)))->createImageReference(mode); - else - return loadImageImpl(ImageLocator(config))->createImageReference(mode); + return loadImageImpl(locator)->createImageReference(mode); } std::shared_ptr RenderHandler::loadImage(const AnimationPath & path, int frame, int group, EImageBlitMode mode) { - return loadImageImpl(ImageLocator(path, frame, group))->createImageReference(mode); + return loadImageFromAnimationFile(path, frame, group)->createImageReference(mode); } std::shared_ptr RenderHandler::loadImage(const ImagePath & path, EImageBlitMode mode) diff --git a/client/renderSDL/RenderHandler.h b/client/renderSDL/RenderHandler.h index 90ef898ab..1d1d72755 100644 --- a/client/renderSDL/RenderHandler.h +++ b/client/renderSDL/RenderHandler.h @@ -20,23 +20,7 @@ class IConstImage; class RenderHandler : public IRenderHandler { - using AnimationLayoutMap = std::map>; - - struct ImageLocator - { - ImagePath image; - AnimationPath animation; - int frame = -1; - int group = -1; - - bool verticalFlip = false; - bool horizontalFlip = false; - - ImageLocator(const JsonNode & config); - ImageLocator(const ImagePath & path); - ImageLocator(const AnimationPath & path, int frame, int group); - bool operator < (const ImageLocator & other) const; - }; + using AnimationLayoutMap = std::map>; std::map> animationFiles; std::map animationLayouts; @@ -58,7 +42,7 @@ public: // IRenderHandler implementation void onLibraryLoadingFinished(const Services * services) override; - std::shared_ptr loadImage(const JsonNode & config, EImageBlitMode mode) override; + std::shared_ptr loadImage(const ImageLocator & locator, EImageBlitMode mode) override; std::shared_ptr loadImage(const ImagePath & path, EImageBlitMode mode) override; std::shared_ptr loadImage(const AnimationPath & path, int frame, int group, EImageBlitMode mode) override; From 73e052b1d019bbc74870c1cc0d051674912d720e Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 12 Jul 2024 12:05:16 +0000 Subject: [PATCH 091/100] Fix merge --- client/renderSDL/SDLImage.cpp | 4 ++-- client/renderSDL/SDLImage.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/client/renderSDL/SDLImage.cpp b/client/renderSDL/SDLImage.cpp index b1bfc703e..a147af10e 100644 --- a/client/renderSDL/SDLImage.cpp +++ b/client/renderSDL/SDLImage.cpp @@ -279,7 +279,7 @@ SDLImageIndexed::~SDLImageIndexed() SDL_FreePalette(currentPalette); } -void SDLImageIndexed::setSpecialPallete(const IImage::SpecialPalette & specialPalette, uint32_t colorsToSkipMask) +void SDLImageIndexed::setSpecialPalette(const IImage::SpecialPalette & specialPalette, uint32_t colorsToSkipMask) { size_t last = std::min(specialPalette.size(), currentPalette->ncolors); @@ -342,7 +342,7 @@ void SDLImageBase::setBlitMode(EImageBlitMode mode) blitMode = mode; } -void SDLImageRGB::setSpecialPallete(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask) +void SDLImageRGB::setSpecialPalette(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask) {} void SDLImageRGB::playerColored(PlayerColor player) diff --git a/client/renderSDL/SDLImage.h b/client/renderSDL/SDLImage.h index a1631f3bd..155eb173b 100644 --- a/client/renderSDL/SDLImage.h +++ b/client/renderSDL/SDLImage.h @@ -90,7 +90,7 @@ public: ~SDLImageIndexed(); void draw(SDL_Surface * where, const Point & pos, const Rect * src) const override; - void setSpecialPallete(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask) override; + void setSpecialPalette(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask) override; void playerColored(PlayerColor player) override; void setFlagColor(PlayerColor player) override; void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override; @@ -103,6 +103,7 @@ public: using SDLImageBase::SDLImageBase; void draw(SDL_Surface * where, const Point & pos, const Rect * src) const override; + void setSpecialPalette(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask) override; void playerColored(PlayerColor player) override; void setFlagColor(PlayerColor player) override; void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override; From 81c7c0ce2425963a6b420bf391035fcdc686df5c Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 16 Jul 2024 19:51:00 +0000 Subject: [PATCH 092/100] Fixed loading of flipped and duplicate images in animations --- client/render/CAnimation.cpp | 32 +++++++++++++++--------------- client/render/CAnimation.h | 2 ++ client/render/ImageLocator.cpp | 5 +++++ client/render/ImageLocator.h | 6 ++++-- client/renderSDL/RenderHandler.cpp | 14 ++++++------- 5 files changed, 34 insertions(+), 25 deletions(-) diff --git a/client/render/CAnimation.cpp b/client/render/CAnimation.cpp index d411ae81b..514540d79 100644 --- a/client/render/CAnimation.cpp +++ b/client/render/CAnimation.cpp @@ -28,7 +28,7 @@ bool CAnimation::loadFrame(size_t frame, size_t group) if(auto image = getImageImpl(frame, group, false)) return true; - std::shared_ptr image = GH.renderHandler().loadImage(source[group][frame], mode); + std::shared_ptr image = GH.renderHandler().loadImage(getImageLocator(frame, group), mode); if(image) { @@ -109,19 +109,7 @@ CAnimation::~CAnimation() = default; void CAnimation::duplicateImage(const size_t sourceGroup, const size_t sourceFrame, const size_t targetGroup) { - if(!source.count(sourceGroup)) - { - logAnim->error("Group %d missing in %s", sourceGroup, name.getName()); - return; - } - - if(source[sourceGroup].size() <= sourceFrame) - { - logAnim->error("Frame [%d %d] missing in %s", sourceGroup, sourceFrame, name.getName()); - return; - } - - ImageLocator clone(source[sourceGroup][sourceFrame]); + ImageLocator clone(getImageLocator(sourceFrame, sourceGroup)); source[targetGroup].push_back(clone); } @@ -179,8 +167,9 @@ void CAnimation::horizontalFlip(size_t frame, size_t group) // ignore - image not loaded } - ImageLocator & locator = source.at(group).at(frame); + auto locator = getImageLocator(frame, group); locator.horizontalFlip = !locator.horizontalFlip; + source[group][frame] = locator; } void CAnimation::verticalFlip(size_t frame, size_t group) @@ -194,8 +183,9 @@ void CAnimation::verticalFlip(size_t frame, size_t group) // ignore - image not loaded } - ImageLocator & locator = source.at(group).at(frame); + auto locator = getImageLocator(frame, group); locator.verticalFlip = !locator.verticalFlip; + source[group][frame] = locator; } void CAnimation::playerColored(PlayerColor player) @@ -213,3 +203,13 @@ void CAnimation::createFlippedGroup(const size_t sourceGroup, const size_t targe verticalFlip(frame, targetGroup); } } + +ImageLocator CAnimation::getImageLocator(size_t frame, size_t group) const +{ + const ImageLocator & locator = source.at(group).at(frame); + + if (!locator.empty()) + return locator; + + return ImageLocator(name, frame, group); +} diff --git a/client/render/CAnimation.h b/client/render/CAnimation.h index 13b96ed25..75c8d1566 100644 --- a/client/render/CAnimation.h +++ b/client/render/CAnimation.h @@ -50,6 +50,8 @@ private: void printError(size_t frame, size_t group, std::string type) const; std::shared_ptr getImageImpl(size_t frame, size_t group=0, bool verbose=true); + + ImageLocator getImageLocator(size_t frame, size_t group) const; public: CAnimation(const AnimationPath & Name, std::map > layout, EImageBlitMode mode); ~CAnimation(); diff --git a/client/render/ImageLocator.cpp b/client/render/ImageLocator.cpp index f1c94462e..250fd2f47 100644 --- a/client/render/ImageLocator.cpp +++ b/client/render/ImageLocator.cpp @@ -49,3 +49,8 @@ bool ImageLocator::operator<(const ImageLocator & other) const return verticalFlip < other.verticalFlip; return horizontalFlip < other.horizontalFlip; } + +bool ImageLocator::empty() const +{ + return !image.has_value() && !defFile.has_value(); +} diff --git a/client/render/ImageLocator.h b/client/render/ImageLocator.h index 13b6f1625..875b537c1 100644 --- a/client/render/ImageLocator.h +++ b/client/render/ImageLocator.h @@ -22,8 +22,10 @@ struct ImageLocator bool horizontalFlip = false; ImageLocator() = default; - ImageLocator(const JsonNode & config); - ImageLocator(const ImagePath & path); ImageLocator(const AnimationPath & path, int frame, int group); + explicit ImageLocator(const JsonNode & config); + explicit ImageLocator(const ImagePath & path); + bool operator < (const ImageLocator & other) const; + bool empty() const; }; diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index bfaf83ec1..ae932a16a 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -68,7 +68,7 @@ void RenderHandler::initFromJson(AnimationLayoutMap & source, const JsonNode & c JsonNode toAdd; JsonUtils::inherit(toAdd, base); toAdd["file"].String() = basepath + frame.String(); - source[groupID].push_back(toAdd); + source[groupID].push_back(ImageLocator(toAdd)); } } @@ -83,7 +83,7 @@ void RenderHandler::initFromJson(AnimationLayoutMap & source, const JsonNode & c JsonNode toAdd; JsonUtils::inherit(toAdd, base); toAdd["file"].String() = basepath + node["file"].String(); - source[group][frame] = toAdd; + source[group][frame] = ImageLocator(toAdd); } } @@ -141,10 +141,10 @@ std::shared_ptr RenderHandler::loadImageFromAnimationFileUncached(c if (frame >= layout.at(group).size()) return loadImageFromSingleFile(ImagePath::builtin("DEFAULT")); - const auto & config = layout.at(group).at(frame); - if (config.image) + const auto & locator = layout.at(group).at(frame); + if (locator.image) { - return loadImageImpl(ImageLocator(config)); + return loadImageImpl(locator); } else { @@ -168,7 +168,7 @@ std::shared_ptr RenderHandler::loadImageImpl(const ImageLocator & l std::shared_ptr result; - if (!locator.image) + if (locator.image) result = loadImageFromSingleFile(*locator.image); else if (locator.defFile) result = loadImageFromAnimationFile(*locator.defFile, locator.defFrame, locator.defGroup); @@ -228,7 +228,7 @@ void RenderHandler::addImageListEntries(const EntityService * service) if (index >= layout[group].size()) layout[group].resize(index + 1); - layout[group][index] = entry; + layout[group][index] = ImageLocator(entry); }); }); } From 4f8f217b496f5144f36f9fbe7018972cbeea8614 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 16 Jul 2024 20:14:49 +0000 Subject: [PATCH 093/100] Moved range highlight transforms to config file --- .../rangeHighlights/rangeHighlightsGreen.json | 38 ++++----- .../rangeHighlights/rangeHighlightsRed.json | 38 ++++----- client/battle/BattleFieldController.cpp | 81 ++++++------------- client/battle/BattleFieldController.h | 3 - client/renderSDL/RenderHandler.cpp | 6 +- 5 files changed, 65 insertions(+), 101 deletions(-) diff --git a/Mods/vcmi/Sprites/battle/rangeHighlights/rangeHighlightsGreen.json b/Mods/vcmi/Sprites/battle/rangeHighlights/rangeHighlightsGreen.json index 1c6b60d36..fff27bb58 100644 --- a/Mods/vcmi/Sprites/battle/rangeHighlights/rangeHighlightsGreen.json +++ b/Mods/vcmi/Sprites/battle/rangeHighlights/rangeHighlightsGreen.json @@ -2,33 +2,33 @@ "basepath" : "battle/rangeHighlights/green/", "images" : [ - { "frame" : 0, "file" : "empty.png"}, // 000001 -> 00 empty frame + { "frame" : 0, "file" : "empty.png"}, // 000001 -> 00 empty frame // load single edges - { "frame" : 1, "file" : "topLeft.png"}, //000001 -> 01 topLeft - { "frame" : 2, "file" : "topLeft.png"}, //000010 -> 02 topRight - { "frame" : 3, "file" : "left.png"}, //000100 -> 04 right - { "frame" : 4, "file" : "topLeft.png"}, //001000 -> 08 bottomRight - { "frame" : 5, "file" : "topLeft.png"}, //010000 -> 16 bottomLeft - { "frame" : 6, "file" : "left.png"}, //100000 -> 32 left + { "frame" : 1, "file" : "topLeft.png"}, //000001 -> 01 topLeft + { "frame" : 2, "file" : "topLeft.png", "verticalFlip" : true }, //000010 -> 02 topRight + { "frame" : 3, "file" : "left.png", "verticalFlip" : true }, //000100 -> 04 right + { "frame" : 4, "file" : "topLeft.png", "verticalFlip" : true, "horizontalFlip" : true }, //001000 -> 08 bottomRight + { "frame" : 5, "file" : "topLeft.png", "horizontalFlip" : true }, //010000 -> 16 bottomLeft + { "frame" : 6, "file" : "left.png"}, //100000 -> 32 left // load double edges - { "frame" : 7, "file" : "top.png"}, //000011 -> 03 top - { "frame" : 8, "file" : "top.png"}, //011000 -> 24 bottom - { "frame" : 9, "file" : "topLeftHalfCorner.png"}, //000110 -> 06 topRightHalfCorner - { "frame" : 10, "file" : "topLeftHalfCorner.png"}, //001100 -> 12 bottomRightHalfCorner - { "frame" : 11, "file" : "topLeftHalfCorner.png"}, //110000 -> 48 bottomLeftHalfCorner - { "frame" : 12, "file" : "topLeftHalfCorner.png"}, //100001 -> 33 topLeftHalfCorner + { "frame" : 7, "file" : "top.png"}, //000011 -> 03 top + { "frame" : 8, "file" : "top.png", "horizontalFlip" : true }, //011000 -> 24 bottom + { "frame" : 9, "file" : "topLeftHalfCorner.png", "verticalFlip" : true }, //000110 -> 06 topRightHalfCorner + { "frame" : 10, "file" : "topLeftHalfCorner.png", "verticalFlip" : true, "horizontalFlip" : true }, //001100 -> 12 bottomRightHalfCorner + { "frame" : 11, "file" : "topLeftHalfCorner.png", "horizontalFlip" : true }, //110000 -> 48 bottomLeftHalfCorner + { "frame" : 12, "file" : "topLeftHalfCorner.png"}, //100001 -> 33 topLeftHalfCorner // load halves - { "frame" : 13, "file" : "leftHalf.png"}, //001110 -> 14 rightHalf - { "frame" : 14, "file" : "leftHalf.png"}, //110001 -> 49 leftHalf + { "frame" : 13, "file" : "leftHalf.png", "verticalFlip" : true}, //001110 -> 14 rightHalf + { "frame" : 14, "file" : "leftHalf.png"}, //110001 -> 49 leftHalf // load corners - { "frame" : 15, "file" : "topLeftCorner.png"}, //000111 -> 07 topRightCorner - { "frame" : 16, "file" : "topLeftCorner.png"}, //011100 -> 28 bottomRightCorner - { "frame" : 17, "file" : "topLeftCorner.png"}, //111000 -> 56 bottomLeftCorner - { "frame" : 18, "file" : "topLeftCorner.png"} //100011 -> 35 topLeftCorner + { "frame" : 15, "file" : "topLeftCorner.png", "verticalFlip" : true }, //000111 -> 07 topRightCorner + { "frame" : 16, "file" : "topLeftCorner.png", "verticalFlip" : true, "horizontalFlip" : true }, //011100 -> 28 bottomRightCorner + { "frame" : 17, "file" : "topLeftCorner.png", "horizontalFlip" : true }, //111000 -> 56 bottomLeftCorner + { "frame" : 18, "file" : "topLeftCorner.png"} //100011 -> 35 topLeftCorner ] } diff --git a/Mods/vcmi/Sprites/battle/rangeHighlights/rangeHighlightsRed.json b/Mods/vcmi/Sprites/battle/rangeHighlights/rangeHighlightsRed.json index 42350c073..68908f0ce 100644 --- a/Mods/vcmi/Sprites/battle/rangeHighlights/rangeHighlightsRed.json +++ b/Mods/vcmi/Sprites/battle/rangeHighlights/rangeHighlightsRed.json @@ -2,33 +2,33 @@ "basepath" : "battle/rangeHighlights/red/", "images" : [ - { "frame" : 0, "file" : "empty.png"}, // 000001 -> 00 empty frame + { "frame" : 0, "file" : "empty.png"}, // 000001 -> 00 empty frame // load single edges - { "frame" : 1, "file" : "topLeft.png"}, //000001 -> 01 topLeft - { "frame" : 2, "file" : "topLeft.png"}, //000010 -> 02 topRight - { "frame" : 3, "file" : "left.png"}, //000100 -> 04 right - { "frame" : 4, "file" : "topLeft.png"}, //001000 -> 08 bottomRight - { "frame" : 5, "file" : "topLeft.png"}, //010000 -> 16 bottomLeft - { "frame" : 6, "file" : "left.png"}, //100000 -> 32 left + { "frame" : 1, "file" : "topLeft.png"}, //000001 -> 01 topLeft + { "frame" : 2, "file" : "topLeft.png", "verticalFlip" : true }, //000010 -> 02 topRight + { "frame" : 3, "file" : "left.png", "verticalFlip" : true }, //000100 -> 04 right + { "frame" : 4, "file" : "topLeft.png", "verticalFlip" : true, "horizontalFlip" : true }, //001000 -> 08 bottomRight + { "frame" : 5, "file" : "topLeft.png", "horizontalFlip" : true }, //010000 -> 16 bottomLeft + { "frame" : 6, "file" : "left.png"}, //100000 -> 32 left // load double edges - { "frame" : 7, "file" : "top.png"}, //000011 -> 03 top - { "frame" : 8, "file" : "top.png"}, //011000 -> 24 bottom - { "frame" : 9, "file" : "topLeftHalfCorner.png"}, //000110 -> 06 topRightHalfCorner - { "frame" : 10, "file" : "topLeftHalfCorner.png"}, //001100 -> 12 bottomRightHalfCorner - { "frame" : 11, "file" : "topLeftHalfCorner.png"}, //110000 -> 48 bottomLeftHalfCorner - { "frame" : 12, "file" : "topLeftHalfCorner.png"}, //100001 -> 33 topLeftHalfCorner + { "frame" : 7, "file" : "top.png"}, //000011 -> 03 top + { "frame" : 8, "file" : "top.png", "horizontalFlip" : true }, //011000 -> 24 bottom + { "frame" : 9, "file" : "topLeftHalfCorner.png", "verticalFlip" : true }, //000110 -> 06 topRightHalfCorner + { "frame" : 10, "file" : "topLeftHalfCorner.png", "verticalFlip" : true, "horizontalFlip" : true }, //001100 -> 12 bottomRightHalfCorner + { "frame" : 11, "file" : "topLeftHalfCorner.png", "horizontalFlip" : true }, //110000 -> 48 bottomLeftHalfCorner + { "frame" : 12, "file" : "topLeftHalfCorner.png"}, //100001 -> 33 topLeftHalfCorner // load halves - { "frame" : 13, "file" : "leftHalf.png"}, //001110 -> 14 rightHalf - { "frame" : 14, "file" : "leftHalf.png"}, //110001 -> 49 leftHalf + { "frame" : 13, "file" : "leftHalf.png", "verticalFlip" : true}, //001110 -> 14 rightHalf + { "frame" : 14, "file" : "leftHalf.png"}, //110001 -> 49 leftHalf // load corners - { "frame" : 15, "file" : "topLeftCorner.png"}, //000111 -> 07 topRightCorner - { "frame" : 16, "file" : "topLeftCorner.png"}, //011100 -> 28 bottomRightCorner - { "frame" : 17, "file" : "topLeftCorner.png"}, //111000 -> 56 bottomLeftCorner - { "frame" : 18, "file" : "topLeftCorner.png"} //100011 -> 35 topLeftCorner + { "frame" : 15, "file" : "topLeftCorner.png", "verticalFlip" : true }, //000111 -> 07 topRightCorner + { "frame" : 16, "file" : "topLeftCorner.png", "verticalFlip" : true, "horizontalFlip" : true }, //011100 -> 28 bottomRightCorner + { "frame" : 17, "file" : "topLeftCorner.png", "horizontalFlip" : true }, //111000 -> 56 bottomLeftCorner + { "frame" : 18, "file" : "topLeftCorner.png"} //100011 -> 35 topLeftCorner ] } diff --git a/client/battle/BattleFieldController.cpp b/client/battle/BattleFieldController.cpp index 5ad5759dc..439e5ab71 100644 --- a/client/battle/BattleFieldController.cpp +++ b/client/battle/BattleFieldController.cpp @@ -82,39 +82,30 @@ namespace HexMasks }; } -std::map hexEdgeMaskToFrameIndex; - -// Maps HexEdgesMask to "Frame" indexes for range highlight images -void initializeHexEdgeMaskToFrameIndex() +static const std::map hexEdgeMaskToFrameIndex = { - hexEdgeMaskToFrameIndex[HexMasks::empty] = 0; - - hexEdgeMaskToFrameIndex[HexMasks::topLeft] = 1; - hexEdgeMaskToFrameIndex[HexMasks::topRight] = 2; - hexEdgeMaskToFrameIndex[HexMasks::right] = 3; - hexEdgeMaskToFrameIndex[HexMasks::bottomRight] = 4; - hexEdgeMaskToFrameIndex[HexMasks::bottomLeft] = 5; - hexEdgeMaskToFrameIndex[HexMasks::left] = 6; - - hexEdgeMaskToFrameIndex[HexMasks::top] = 7; - hexEdgeMaskToFrameIndex[HexMasks::bottom] = 8; - - hexEdgeMaskToFrameIndex[HexMasks::topRightHalfCorner] = 9; - hexEdgeMaskToFrameIndex[HexMasks::bottomRightHalfCorner] = 10; - hexEdgeMaskToFrameIndex[HexMasks::bottomLeftHalfCorner] = 11; - hexEdgeMaskToFrameIndex[HexMasks::topLeftHalfCorner] = 12; - - hexEdgeMaskToFrameIndex[HexMasks::rightTopAndBottom] = 13; - hexEdgeMaskToFrameIndex[HexMasks::leftTopAndBottom] = 14; - - hexEdgeMaskToFrameIndex[HexMasks::rightHalf] = 13; - hexEdgeMaskToFrameIndex[HexMasks::leftHalf] = 14; - - hexEdgeMaskToFrameIndex[HexMasks::topRightCorner] = 15; - hexEdgeMaskToFrameIndex[HexMasks::bottomRightCorner] = 16; - hexEdgeMaskToFrameIndex[HexMasks::bottomLeftCorner] = 17; - hexEdgeMaskToFrameIndex[HexMasks::topLeftCorner] = 18; -} + { HexMasks::empty, 0 }, + { HexMasks::topLeft, 1 }, + { HexMasks::topRight, 2 }, + { HexMasks::right, 3 }, + { HexMasks::bottomRight, 4 }, + { HexMasks::bottomLeft, 5 }, + { HexMasks::left, 6 }, + { HexMasks::top, 7 }, + { HexMasks::bottom, 8 }, + { HexMasks::topRightHalfCorner, 9 }, + { HexMasks::bottomRightHalfCorner, 10 }, + { HexMasks::bottomLeftHalfCorner, 11 }, + { HexMasks::topLeftHalfCorner, 12 }, + { HexMasks::rightTopAndBottom, 13 }, + { HexMasks::leftTopAndBottom, 14 }, + { HexMasks::rightHalf, 13 }, + { HexMasks::leftHalf, 14 }, + { HexMasks::topRightCorner, 15 }, + { HexMasks::bottomRightCorner, 16 }, + { HexMasks::bottomLeftCorner, 17 }, + { HexMasks::topLeftCorner, 18 } +}; BattleFieldController::BattleFieldController(BattleInterface & owner): owner(owner) @@ -130,14 +121,9 @@ BattleFieldController::BattleFieldController(BattleInterface & owner): attackCursors = GH.renderHandler().loadAnimation(AnimationPath::builtin("CRCOMBAT"), EImageBlitMode::COLORKEY); spellCursors = GH.renderHandler().loadAnimation(AnimationPath::builtin("CRSPELL"), EImageBlitMode::COLORKEY); - initializeHexEdgeMaskToFrameIndex(); - rangedFullDamageLimitImages = GH.renderHandler().loadAnimation(AnimationPath::builtin("battle/rangeHighlights/rangeHighlightsGreen.json"), EImageBlitMode::COLORKEY); shootingRangeLimitImages = GH.renderHandler().loadAnimation(AnimationPath::builtin("battle/rangeHighlights/rangeHighlightsRed.json"), EImageBlitMode::COLORKEY); - flipRangeLimitImagesIntoPositions(rangedFullDamageLimitImages); - flipRangeLimitImagesIntoPositions(shootingRangeLimitImages); - if(!owner.siegeController) { auto bfieldType = owner.getBattle()->battleGetBattlefieldType(); @@ -536,7 +522,7 @@ std::vector> BattleFieldController::calculateRangeLimitH mask.set(direction); uint8_t imageKey = static_cast(mask.to_ulong()); - output.push_back(limitImages->getImage(hexEdgeMaskToFrameIndex[imageKey])); + output.push_back(limitImages->getImage(hexEdgeMaskToFrameIndex.at(imageKey))); } return output; @@ -550,25 +536,6 @@ void BattleFieldController::calculateRangeLimitAndHighlightImages(uint8_t distan rangeLimitHexesHighlights = calculateRangeLimitHighlightImages(rangeLimitNeighbourDirections, rangeLimitImages); } -void BattleFieldController::flipRangeLimitImagesIntoPositions(std::shared_ptr images) -{ - images->verticalFlip(hexEdgeMaskToFrameIndex[HexMasks::topRight]); - images->verticalFlip(hexEdgeMaskToFrameIndex[HexMasks::right]); - images->verticalFlip(hexEdgeMaskToFrameIndex[HexMasks::bottomRight]); - images->horizontalFlip(hexEdgeMaskToFrameIndex[HexMasks::bottomRight]); - images->horizontalFlip(hexEdgeMaskToFrameIndex[HexMasks::bottomLeft]); - images->horizontalFlip(hexEdgeMaskToFrameIndex[HexMasks::bottom]); - images->verticalFlip(hexEdgeMaskToFrameIndex[HexMasks::topRightHalfCorner]); - images->verticalFlip(hexEdgeMaskToFrameIndex[HexMasks::bottomRightHalfCorner]); - images->horizontalFlip(hexEdgeMaskToFrameIndex[HexMasks::bottomRightHalfCorner]); - images->horizontalFlip(hexEdgeMaskToFrameIndex[HexMasks::bottomLeftHalfCorner]); - images->verticalFlip(hexEdgeMaskToFrameIndex[HexMasks::rightHalf]); - images->verticalFlip(hexEdgeMaskToFrameIndex[HexMasks::topRightCorner]); - images->verticalFlip(hexEdgeMaskToFrameIndex[HexMasks::bottomRightCorner]); - images->horizontalFlip(hexEdgeMaskToFrameIndex[HexMasks::bottomRightCorner]); - images->horizontalFlip(hexEdgeMaskToFrameIndex[HexMasks::bottomLeftCorner]); -} - void BattleFieldController::showHighlightedHexes(Canvas & canvas) { std::vector rangedFullDamageLimitHexes; diff --git a/client/battle/BattleFieldController.h b/client/battle/BattleFieldController.h index 359c82ad8..4500e95ab 100644 --- a/client/battle/BattleFieldController.h +++ b/client/battle/BattleFieldController.h @@ -84,9 +84,6 @@ class BattleFieldController : public CIntObject /// calculates all hexes for a range limit and what images to be shown as highlight for each of the hexes void calculateRangeLimitAndHighlightImages(uint8_t distance, std::shared_ptr rangeLimitImages, std::vector & rangeLimitHexes, std::vector> & rangeLimitHexesHighlights); - /// to reduce the number of source images used, some images will be used as flipped versions of preloaded ones - void flipRangeLimitImagesIntoPositions(std::shared_ptr images); - void showBackground(Canvas & canvas); void showBackgroundImage(Canvas & canvas); void showBackgroundImageWithHexes(Canvas & canvas); diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index ae932a16a..a080b7796 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -65,10 +65,10 @@ void RenderHandler::initFromJson(AnimationLayoutMap & source, const JsonNode & c for(const JsonNode & frame : group["frames"].Vector()) { - JsonNode toAdd; + JsonNode toAdd = frame; JsonUtils::inherit(toAdd, base); toAdd["file"].String() = basepath + frame.String(); - source[groupID].push_back(ImageLocator(toAdd)); + source[groupID].emplace_back(toAdd); } } @@ -80,7 +80,7 @@ void RenderHandler::initFromJson(AnimationLayoutMap & source, const JsonNode & c if (source[group].size() <= frame) source[group].resize(frame+1); - JsonNode toAdd; + JsonNode toAdd = node; JsonUtils::inherit(toAdd, base); toAdd["file"].String() = basepath + node["file"].String(); source[group][frame] = ImageLocator(toAdd); From 9f3663b7d416cc5eeaca3928f9973f582efc9004 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 16 Jul 2024 20:46:59 +0000 Subject: [PATCH 094/100] Remove unused method --- client/renderSDL/RenderHandler.cpp | 2 +- client/renderSDL/SDL_Extensions.cpp | 13 ------------- client/renderSDL/SDL_Extensions.h | 2 -- 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index a080b7796..ec4a0bdc3 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -188,7 +188,7 @@ std::shared_ptr RenderHandler::loadImageImpl(const ImageLocator & l std::shared_ptr RenderHandler::loadImage(const ImageLocator & locator, EImageBlitMode mode) { - return loadImageImpl(locator)->createImageReference(mode); + return loadImageImpl(locator)->createImageReference(mode); } std::shared_ptr RenderHandler::loadImage(const AnimationPath & path, int frame, int group, EImageBlitMode mode) diff --git a/client/renderSDL/SDL_Extensions.cpp b/client/renderSDL/SDL_Extensions.cpp index 5e76a59c3..f97e86a33 100644 --- a/client/renderSDL/SDL_Extensions.cpp +++ b/client/renderSDL/SDL_Extensions.cpp @@ -63,19 +63,6 @@ void CSDL_Ext::setAlpha(SDL_Surface * bg, int value) SDL_SetSurfaceAlphaMod(bg, value); } -void CSDL_Ext::updateRect(SDL_Surface *surface, const Rect & rect ) -{ - SDL_Rect rectSDL = CSDL_Ext::toSDL(rect); - if(0 !=SDL_UpdateTexture(screenTexture, &rectSDL, surface->pixels, surface->pitch)) - logGlobal->error("%sSDL_UpdateTexture %s", __FUNCTION__, SDL_GetError()); - - SDL_RenderClear(mainRenderer); - if(0 != SDL_RenderCopy(mainRenderer, screenTexture, nullptr, nullptr)) - logGlobal->error("%sSDL_RenderCopy %s", __FUNCTION__, SDL_GetError()); - SDL_RenderPresent(mainRenderer); - -} - SDL_Surface * CSDL_Ext::newSurface(int w, int h) { return newSurface(w, h, screen); diff --git a/client/renderSDL/SDL_Extensions.h b/client/renderSDL/SDL_Extensions.h index ec3deeecc..b5118c71f 100644 --- a/client/renderSDL/SDL_Extensions.h +++ b/client/renderSDL/SDL_Extensions.h @@ -61,8 +61,6 @@ using TColorPutterAlpha = void (*)(uint8_t *&, const uint8_t &, const uint8_t &, void fillRect(SDL_Surface * dst, const Rect & dstrect, const SDL_Color & color); void fillRectBlended(SDL_Surface * dst, const Rect & dstrect, const SDL_Color & color); - void updateRect(SDL_Surface * surface, const Rect & rect); - void putPixelWithoutRefresh(SDL_Surface * ekran, const int & x, const int & y, const uint8_t & R, const uint8_t & G, const uint8_t & B, uint8_t A = 255); void putPixelWithoutRefreshIfInSurf(SDL_Surface *ekran, const int & x, const int & y, const uint8_t & R, const uint8_t & G, const uint8_t & B, uint8_t A = 255); From 15c5cd6f6c4c33b2d36b47a1f4f3d7f6ee8b64ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Trung=20L=C3=AA?= <8@tle.id.au> Date: Fri, 19 Jul 2024 20:13:46 +1000 Subject: [PATCH 095/100] Add notes for Fedora --- docs/players/Installation_Linux.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/players/Installation_Linux.md b/docs/players/Installation_Linux.md index e68dadc2c..9440246b3 100644 --- a/docs/players/Installation_Linux.md +++ b/docs/players/Installation_Linux.md @@ -43,6 +43,15 @@ To install VCMI from repository: sudo apt-get update sudo apt-get install vcmi ``` + +### Fedora (40 or newer) + +Stable VCMI version is available in RPM Fusion repository. Learn how to enable it in [wiki](https://docs.fedoraproject.org/en-US/quick-docs/rpmfusion-setup/). To install VCMI from repository: + +``` + sudo dnf update + sudo dnf install vcmi +``` ### Flatpak (distribution-agnostic) Latest public release build can be installed via Flatpak. From 70e309c4f90db2987c10aa890dbdd999c7c558b7 Mon Sep 17 00:00:00 2001 From: Alexander Wilms Date: Fri, 19 Jul 2024 12:38:42 +0200 Subject: [PATCH 096/100] CModListView::loadScreenshots(): Check isValid() instead of row() and column() Co-authored-by: Andrey Filipenkov --- launcher/modManager/cmodlistview_moc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/modManager/cmodlistview_moc.cpp b/launcher/modManager/cmodlistview_moc.cpp index 8f6aef347..0fa415c5e 100644 --- a/launcher/modManager/cmodlistview_moc.cpp +++ b/launcher/modManager/cmodlistview_moc.cpp @@ -962,7 +962,7 @@ void CModListView::loadScreenshots() { if(ui->tabWidget->currentIndex() == 2) { - if(ui->allModsView->currentIndex().row() == -1 || ui->allModsView->currentIndex().column() == -1) + if(!ui->allModsView->currentIndex().isValid()) { // select the first mod, so we can access its data ui->allModsView->setCurrentIndex(filterModel->index(0, 0)); From c40eea6e1023621bd949c8573e6d2855b878ed38 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 19 Jul 2024 10:46:20 +0000 Subject: [PATCH 097/100] Fix fade-in animation - per-surface alpha was not set correctly --- client/renderSDL/SDLImage.cpp | 4 ++-- client/widgets/Images.cpp | 4 ++-- client/widgets/Images.h | 1 + client/windows/CCastleInterface.cpp | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/client/renderSDL/SDLImage.cpp b/client/renderSDL/SDLImage.cpp index a147af10e..ae12c1089 100644 --- a/client/renderSDL/SDLImage.cpp +++ b/client/renderSDL/SDLImage.cpp @@ -111,7 +111,7 @@ void SDLImageConst::draw(SDL_Surface * where, SDL_Palette * palette, const Point SDL_SetSurfaceAlphaMod(surf, alpha); - if (mode != EImageBlitMode::OPAQUE && surf->format->Amask != 0) + if (alpha != SDL_ALPHA_OPAQUE || (mode != EImageBlitMode::OPAQUE && surf->format->Amask != 0)) SDL_SetSurfaceBlendMode(surf, SDL_BLENDMODE_BLEND); else SDL_SetSurfaceBlendMode(surf, SDL_BLENDMODE_NONE); @@ -119,7 +119,7 @@ void SDLImageConst::draw(SDL_Surface * where, SDL_Palette * palette, const Point if (palette && surf->format->palette) SDL_SetSurfacePalette(surf, palette); - if(surf->format->BitsPerPixel == 8 && alpha == SDL_ALPHA_OPAQUE && mode == EImageBlitMode::ALPHA) + if(surf->format->palette && alpha == SDL_ALPHA_OPAQUE && mode == EImageBlitMode::ALPHA) { CSDL_Ext::blit8bppAlphaTo24bpp(surf, sourceRect, where, destShift); } diff --git a/client/widgets/Images.cpp b/client/widgets/Images.cpp index 40e3fe646..35218477e 100644 --- a/client/widgets/Images.cpp +++ b/client/widgets/Images.cpp @@ -318,7 +318,7 @@ bool CAnimImage::isPlayerColored() const } CShowableAnim::CShowableAnim(int x, int y, const AnimationPath & name, ui8 Flags, ui32 frameTime, size_t Group, uint8_t alpha): - anim(GH.renderHandler().loadAnimation(name, EImageBlitMode::ALPHA)), + anim(GH.renderHandler().loadAnimation(name, (Flags & PALETTE_ALPHA) ? EImageBlitMode::ALPHA : EImageBlitMode::COLORKEY)), group(Group), frame(0), first(0), @@ -451,7 +451,7 @@ void CShowableAnim::setDuration(int durationMs) } CCreatureAnim::CCreatureAnim(int x, int y, const AnimationPath & name, ui8 flags, ECreatureAnimType type): - CShowableAnim(x, y, name, flags, 100, size_t(type)) // H3 uses 100 ms per frame, irregardless of battle speed settings + CShowableAnim(x, y, name, flags | PALETTE_ALPHA, 100, size_t(type)) // H3 uses 100 ms per frame, irregardless of battle speed settings { xOffset = 0; yOffset = 0; diff --git a/client/widgets/Images.h b/client/widgets/Images.h index af459963f..5e66276d1 100644 --- a/client/widgets/Images.h +++ b/client/widgets/Images.h @@ -146,6 +146,7 @@ public: BASE=1, //base frame will be blitted before current one HORIZONTAL_FLIP=2, //TODO: will be displayed rotated VERTICAL_FLIP=4, //TODO: will be displayed rotated + PALETTE_ALPHA=8, // use alpha channel for images with palette. Required for creatures in battle and map objects PLAY_ONCE=32 //play animation only once and stop at last frame }; protected: diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 3fe1f65cd..bb2fe18c0 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -95,7 +95,7 @@ CBuildingRect::CBuildingRect(CCastleBuildings * Par, const CGTownInstance * Town } if(!str->borderName.empty()) - border = GH.renderHandler().loadImage(str->borderName, EImageBlitMode::ALPHA); + border = GH.renderHandler().loadImage(str->borderName, EImageBlitMode::COLORKEY); if(!str->areaName.empty()) area = GH.renderHandler().loadImage(str->areaName, EImageBlitMode::ALPHA); From b256919dab7ef186a2430ed4da4fa96fa376bb46 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 19 Jul 2024 12:03:29 +0000 Subject: [PATCH 098/100] Implemented per-surface alpha for images that have palettes with alpha --- client/renderSDL/SDLImage.cpp | 4 ++-- client/renderSDL/SDL_Extensions.cpp | 35 ++++++++++++++++++++--------- client/renderSDL/SDL_Extensions.h | 6 ++--- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/client/renderSDL/SDLImage.cpp b/client/renderSDL/SDLImage.cpp index ae12c1089..c7e47cd97 100644 --- a/client/renderSDL/SDLImage.cpp +++ b/client/renderSDL/SDLImage.cpp @@ -119,9 +119,9 @@ void SDLImageConst::draw(SDL_Surface * where, SDL_Palette * palette, const Point if (palette && surf->format->palette) SDL_SetSurfacePalette(surf, palette); - if(surf->format->palette && alpha == SDL_ALPHA_OPAQUE && mode == EImageBlitMode::ALPHA) + if(surf->format->palette && mode == EImageBlitMode::ALPHA) { - CSDL_Ext::blit8bppAlphaTo24bpp(surf, sourceRect, where, destShift); + CSDL_Ext::blit8bppAlphaTo24bpp(surf, sourceRect, where, destShift, alpha); } else { diff --git a/client/renderSDL/SDL_Extensions.cpp b/client/renderSDL/SDL_Extensions.cpp index f97e86a33..63f8e2528 100644 --- a/client/renderSDL/SDL_Extensions.cpp +++ b/client/renderSDL/SDL_Extensions.cpp @@ -210,8 +210,8 @@ uint32_t CSDL_Ext::getPixel(SDL_Surface *surface, const int & x, const int & y, } } -template -int CSDL_Ext::blit8bppAlphaTo24bppT(const SDL_Surface * src, const Rect & srcRectInput, SDL_Surface * dst, const Point & dstPointInput) +template +int CSDL_Ext::blit8bppAlphaTo24bppT(const SDL_Surface * src, const Rect & srcRectInput, SDL_Surface * dst, const Point & dstPointInput, uint8_t alpha) { SDL_Rect srcRectInstance = CSDL_Ext::toSDL(srcRectInput); SDL_Rect dstRectInstance = CSDL_Ext::toSDL(Rect(dstPointInput, srcRectInput.dimensions())); @@ -334,7 +334,11 @@ int CSDL_Ext::blit8bppAlphaTo24bppT(const SDL_Surface * src, const Rect & srcRec for(int x = 0; x < w; ++x) { const SDL_Color &tbc = colors[*color++]; //color to blit - ColorPutter::PutColorAlphaSwitch(p, tbc.r, tbc.g, tbc.b, tbc.a); + if constexpr (useAlpha) + ColorPutter::PutColorAlphaSwitch(p, tbc.r, tbc.g, tbc.b, int(alpha) * tbc.a / 255 ); + else + ColorPutter::PutColorAlphaSwitch(p, tbc.r, tbc.g, tbc.b, tbc.a); + p += bpp; } } @@ -344,16 +348,27 @@ int CSDL_Ext::blit8bppAlphaTo24bppT(const SDL_Surface * src, const Rect & srcRec return 0; } -int CSDL_Ext::blit8bppAlphaTo24bpp(const SDL_Surface * src, const Rect & srcRect, SDL_Surface * dst, const Point & dstPoint) +int CSDL_Ext::blit8bppAlphaTo24bpp(const SDL_Surface * src, const Rect & srcRect, SDL_Surface * dst, const Point & dstPoint, uint8_t alpha) { - switch(dst->format->BytesPerPixel) + if (alpha == SDL_ALPHA_OPAQUE) { - case 3: return blit8bppAlphaTo24bppT<3>(src, srcRect, dst, dstPoint); - case 4: return blit8bppAlphaTo24bppT<4>(src, srcRect, dst, dstPoint); - default: - logGlobal->error("%d bpp is not supported!", (int)dst->format->BitsPerPixel); - return -1; + switch(dst->format->BytesPerPixel) + { + case 3: return blit8bppAlphaTo24bppT<3, false>(src, srcRect, dst, dstPoint, alpha); + case 4: return blit8bppAlphaTo24bppT<4, false>(src, srcRect, dst, dstPoint, alpha); + } } + else + { + switch(dst->format->BytesPerPixel) + { + case 3: return blit8bppAlphaTo24bppT<3, true>(src, srcRect, dst, dstPoint, alpha); + case 4: return blit8bppAlphaTo24bppT<4, true>(src, srcRect, dst, dstPoint, alpha); + } + } + + logGlobal->error("%d bpp is not supported!", (int)dst->format->BitsPerPixel); + return -1; } uint32_t CSDL_Ext::colorTouint32_t(const SDL_Color * color) diff --git a/client/renderSDL/SDL_Extensions.h b/client/renderSDL/SDL_Extensions.h index b5118c71f..9b1dec1aa 100644 --- a/client/renderSDL/SDL_Extensions.h +++ b/client/renderSDL/SDL_Extensions.h @@ -73,9 +73,9 @@ using TColorPutterAlpha = void (*)(uint8_t *&, const uint8_t &, const uint8_t &, uint8_t * getPxPtr(const SDL_Surface * const & srf, const int x, const int y); TColorPutter getPutterFor(SDL_Surface * const & dest); - template - int blit8bppAlphaTo24bppT(const SDL_Surface * src, const Rect & srcRect, SDL_Surface * dst, const Point & dstPoint); //blits 8 bpp surface with alpha channel to 24 bpp surface - int blit8bppAlphaTo24bpp(const SDL_Surface * src, const Rect & srcRect, SDL_Surface * dst, const Point & dstPoint); //blits 8 bpp surface with alpha channel to 24 bpp surface + template + int blit8bppAlphaTo24bppT(const SDL_Surface * src, const Rect & srcRect, SDL_Surface * dst, const Point & dstPoint, uint8_t alpha); //blits 8 bpp surface with alpha channel to 24 bpp surface + int blit8bppAlphaTo24bpp(const SDL_Surface * src, const Rect & srcRect, SDL_Surface * dst, const Point & dstPoint, uint8_t alpha); //blits 8 bpp surface with alpha channel to 24 bpp surface uint32_t colorTouint32_t(const SDL_Color * color); //little endian only void drawLine(SDL_Surface * sur, const Point & from, const Point & dest, const SDL_Color & color1, const SDL_Color & color2); From 2d3734656d82a75adf788c0f65b4a88961207a30 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 19 Jul 2024 12:17:06 +0000 Subject: [PATCH 099/100] Fix test --- lib/mapping/CMapEditManager.h | 8 ++++---- test/map/CMapEditManagerTest.cpp | 21 +++++++++++---------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/lib/mapping/CMapEditManager.h b/lib/mapping/CMapEditManager.h index 1d952598b..10f68a8ae 100644 --- a/lib/mapping/CMapEditManager.h +++ b/lib/mapping/CMapEditManager.h @@ -72,16 +72,16 @@ public: CMap * getMap(); /// Clears the terrain. The free level is filled with water and the underground level with rock. - void clearTerrain(vstd::RNG * gen = nullptr); + void clearTerrain(vstd::RNG * gen); /// Draws terrain at the current terrain selection. The selection will be cleared automatically. - void drawTerrain(TerrainId terType, int decorationsPercentage, vstd::RNG * gen = nullptr); + void drawTerrain(TerrainId terType, int decorationsPercentage, vstd::RNG * gen); /// Draws roads at the current terrain selection. The selection will be cleared automatically. - void drawRoad(RoadId roadType, vstd::RNG * gen = nullptr); + void drawRoad(RoadId roadType, vstd::RNG * gen); /// Draws rivers at the current terrain selection. The selection will be cleared automatically. - void drawRiver(RiverId riverType, vstd::RNG * gen = nullptr); + void drawRiver(RiverId riverType, vstd::RNG * gen); void insertObject(CGObjectInstance * obj); void insertObjects(std::set & objects); diff --git a/test/map/CMapEditManagerTest.cpp b/test/map/CMapEditManagerTest.cpp index 0020a6e93..b1e7f56e6 100644 --- a/test/map/CMapEditManagerTest.cpp +++ b/test/map/CMapEditManagerTest.cpp @@ -25,15 +25,16 @@ TEST(MapManager, DrawTerrain_Type) try { auto map = std::make_unique(nullptr); + CRandomGenerator rand; map->width = 52; map->height = 52; map->initTerrain(); auto editManager = map->getEditManager(); - editManager->clearTerrain(); + editManager->clearTerrain(&rand); // 1x1 Blow up editManager->getTerrainSelection().select(int3(5, 5, 0)); - editManager->drawTerrain(ETerrainId::GRASS, 10); + editManager->drawTerrain(ETerrainId::GRASS, 10, &rand); static const int3 squareCheck[] = { int3(5,5,0), int3(5,4,0), int3(4,4,0), int3(4,5,0) }; for(const auto & tile : squareCheck) { @@ -42,20 +43,20 @@ TEST(MapManager, DrawTerrain_Type) // Concat to square editManager->getTerrainSelection().select(int3(6, 5, 0)); - editManager->drawTerrain(ETerrainId::GRASS, 10); + editManager->drawTerrain(ETerrainId::GRASS, 10, &rand); EXPECT_EQ(map->getTile(int3(6, 4, 0)).terType->getId(), ETerrainId::GRASS); editManager->getTerrainSelection().select(int3(6, 5, 0)); - editManager->drawTerrain(ETerrainId::LAVA, 10); + editManager->drawTerrain(ETerrainId::LAVA, 10, &rand); EXPECT_EQ(map->getTile(int3(4, 4, 0)).terType->getId(), ETerrainId::GRASS); EXPECT_EQ(map->getTile(int3(7, 4, 0)).terType->getId(), ETerrainId::LAVA); // Special case water,rock editManager->getTerrainSelection().selectRange(MapRect(int3(10, 10, 0), 10, 5)); - editManager->drawTerrain(ETerrainId::GRASS, 10); + editManager->drawTerrain(ETerrainId::GRASS, 10, &rand); editManager->getTerrainSelection().selectRange(MapRect(int3(15, 17, 0), 10, 5)); - editManager->drawTerrain(ETerrainId::GRASS, 10); + editManager->drawTerrain(ETerrainId::GRASS, 10, &rand); editManager->getTerrainSelection().select(int3(21, 16, 0)); - editManager->drawTerrain(ETerrainId::GRASS, 10); + editManager->drawTerrain(ETerrainId::GRASS, 10, &rand); EXPECT_EQ(map->getTile(int3(20, 15, 0)).terType->getId(), ETerrainId::GRASS); // Special case non water,rock @@ -66,16 +67,16 @@ TEST(MapManager, DrawTerrain_Type) { editManager->getTerrainSelection().select(tile); } - editManager->drawTerrain(ETerrainId::GRASS, 10); + editManager->drawTerrain(ETerrainId::GRASS, 10, &rand); EXPECT_EQ(map->getTile(int3(35, 44, 0)).terType->getId(), ETerrainId::WATER); // Rock case editManager->getTerrainSelection().selectRange(MapRect(int3(1, 1, 1), 15, 15)); - editManager->drawTerrain(ETerrainId::SUBTERRANEAN, 10); + editManager->drawTerrain(ETerrainId::SUBTERRANEAN, 10, &rand); std::vector vec({ int3(6, 6, 1), int3(7, 6, 1), int3(8, 6, 1), int3(5, 7, 1), int3(6, 7, 1), int3(7, 7, 1), int3(8, 7, 1), int3(4, 8, 1), int3(5, 8, 1), int3(6, 8, 1)}); editManager->getTerrainSelection().setSelection(vec); - editManager->drawTerrain(ETerrainId::ROCK, 10); + editManager->drawTerrain(ETerrainId::ROCK, 10, &rand); EXPECT_TRUE(!map->getTile(int3(5, 6, 1)).terType->isPassable() || !map->getTile(int3(7, 8, 1)).terType->isPassable()); //todo: add checks here and enable, also use smaller size From 0d44bf1506a4b490cc6dcafd98199ebd86b1dedb Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 19 Jul 2024 12:30:10 +0000 Subject: [PATCH 100/100] Fix build --- client/renderSDL/SDL_Extensions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/renderSDL/SDL_Extensions.cpp b/client/renderSDL/SDL_Extensions.cpp index 63f8e2528..5987e7d6c 100644 --- a/client/renderSDL/SDL_Extensions.cpp +++ b/client/renderSDL/SDL_Extensions.cpp @@ -211,7 +211,7 @@ uint32_t CSDL_Ext::getPixel(SDL_Surface *surface, const int & x, const int & y, } template -int CSDL_Ext::blit8bppAlphaTo24bppT(const SDL_Surface * src, const Rect & srcRectInput, SDL_Surface * dst, const Point & dstPointInput, uint8_t alpha) +int CSDL_Ext::blit8bppAlphaTo24bppT(const SDL_Surface * src, const Rect & srcRectInput, SDL_Surface * dst, const Point & dstPointInput, [[maybe_unused]] uint8_t alpha) { SDL_Rect srcRectInstance = CSDL_Ext::toSDL(srcRectInput); SDL_Rect dstRectInstance = CSDL_Ext::toSDL(Rect(dstPointInput, srcRectInput.dimensions()));