mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-22 22:13:35 +02:00
Merge branch 'master' into 'develop'
This commit is contained in:
commit
2a193effcc
@ -16,8 +16,6 @@
|
|||||||
#include "../../lib/battle/BattleAction.h"
|
#include "../../lib/battle/BattleAction.h"
|
||||||
#include "../../lib/battle/BattleInfo.h"
|
#include "../../lib/battle/BattleInfo.h"
|
||||||
|
|
||||||
static std::shared_ptr<CBattleCallback> cbc;
|
|
||||||
|
|
||||||
CStupidAI::CStupidAI()
|
CStupidAI::CStupidAI()
|
||||||
: side(-1)
|
: side(-1)
|
||||||
, wasWaitingForRealize(false)
|
, wasWaitingForRealize(false)
|
||||||
@ -41,7 +39,7 @@ void CStupidAI::initBattleInterface(std::shared_ptr<Environment> ENV, std::share
|
|||||||
{
|
{
|
||||||
print("init called, saving ptr to IBattleCallback");
|
print("init called, saving ptr to IBattleCallback");
|
||||||
env = ENV;
|
env = ENV;
|
||||||
cbc = cb = CB;
|
cb = CB;
|
||||||
|
|
||||||
wasWaitingForRealize = CB->waitTillRealize;
|
wasWaitingForRealize = CB->waitTillRealize;
|
||||||
wasUnlockingGs = CB->unlockGsWhenWaiting;
|
wasUnlockingGs = CB->unlockGsWhenWaiting;
|
||||||
@ -73,11 +71,11 @@ public:
|
|||||||
std::vector<BattleHex> attackFrom; //for melee fight
|
std::vector<BattleHex> attackFrom; //for melee fight
|
||||||
EnemyInfo(const CStack * _s) : s(_s), adi(0), adr(0)
|
EnemyInfo(const CStack * _s) : s(_s), adi(0), adr(0)
|
||||||
{}
|
{}
|
||||||
void calcDmg(const BattleID & battleID, const CStack * ourStack)
|
void calcDmg(std::shared_ptr<CBattleCallback> cb, const BattleID & battleID, const CStack * ourStack)
|
||||||
{
|
{
|
||||||
// FIXME: provide distance info for Jousting bonus
|
// FIXME: provide distance info for Jousting bonus
|
||||||
DamageEstimation retal;
|
DamageEstimation retal;
|
||||||
DamageEstimation dmg = cbc->getBattle(battleID)->battleEstimateDamage(ourStack, s, 0, &retal);
|
DamageEstimation dmg = cb->getBattle(battleID)->battleEstimateDamage(ourStack, s, 0, &retal);
|
||||||
adi = static_cast<int>((dmg.damage.min + dmg.damage.max) / 2);
|
adi = static_cast<int>((dmg.damage.min + dmg.damage.max) / 2);
|
||||||
adr = static_cast<int>((retal.damage.min + retal.damage.max) / 2);
|
adr = static_cast<int>((retal.damage.min + retal.damage.max) / 2);
|
||||||
}
|
}
|
||||||
@ -93,14 +91,14 @@ bool isMoreProfitable(const EnemyInfo &ei1, const EnemyInfo& ei2)
|
|||||||
return (ei1.adi-ei1.adr) < (ei2.adi - ei2.adr);
|
return (ei1.adi-ei1.adr) < (ei2.adi - ei2.adr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool willSecondHexBlockMoreEnemyShooters(const BattleID & battleID, const BattleHex &h1, const BattleHex &h2)
|
static bool willSecondHexBlockMoreEnemyShooters(std::shared_ptr<CBattleCallback> cb, const BattleID & battleID, const BattleHex &h1, const BattleHex &h2)
|
||||||
{
|
{
|
||||||
int shooters[2] = {0}; //count of shooters on hexes
|
int shooters[2] = {0}; //count of shooters on hexes
|
||||||
|
|
||||||
for(int i = 0; i < 2; i++)
|
for(int i = 0; i < 2; i++)
|
||||||
{
|
{
|
||||||
for (auto & neighbour : (i ? h2 : h1).neighbouringTiles())
|
for (auto & neighbour : (i ? h2 : h1).neighbouringTiles())
|
||||||
if(const auto * s = cbc->getBattle(battleID)->battleGetUnitByPos(neighbour))
|
if(const auto * s = cb->getBattle(battleID)->battleGetUnitByPos(neighbour))
|
||||||
if(s->isShooter())
|
if(s->isShooter())
|
||||||
shooters[i]++;
|
shooters[i]++;
|
||||||
}
|
}
|
||||||
@ -172,10 +170,10 @@ void CStupidAI::activeStack(const BattleID & battleID, const CStack * stack)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for ( auto & enemy : enemiesReachable )
|
for ( auto & enemy : enemiesReachable )
|
||||||
enemy.calcDmg(battleID, stack);
|
enemy.calcDmg(cb, battleID, stack);
|
||||||
|
|
||||||
for ( auto & enemy : enemiesShootable )
|
for ( auto & enemy : enemiesShootable )
|
||||||
enemy.calcDmg(battleID, stack);
|
enemy.calcDmg(cb, battleID, stack);
|
||||||
|
|
||||||
if(enemiesShootable.size())
|
if(enemiesShootable.size())
|
||||||
{
|
{
|
||||||
@ -186,7 +184,7 @@ void CStupidAI::activeStack(const BattleID & battleID, const CStack * stack)
|
|||||||
else if(enemiesReachable.size())
|
else if(enemiesReachable.size())
|
||||||
{
|
{
|
||||||
const EnemyInfo &ei= *std::max_element(enemiesReachable.begin(), enemiesReachable.end(), &isMoreProfitable);
|
const EnemyInfo &ei= *std::max_element(enemiesReachable.begin(), enemiesReachable.end(), &isMoreProfitable);
|
||||||
BattleHex targetHex = *std::max_element(ei.attackFrom.begin(), ei.attackFrom.end(), [&](auto a, auto b) { return willSecondHexBlockMoreEnemyShooters(battleID, a, b);});
|
BattleHex targetHex = *std::max_element(ei.attackFrom.begin(), ei.attackFrom.end(), [&](auto a, auto b) { return willSecondHexBlockMoreEnemyShooters(cb, battleID, a, b);});
|
||||||
|
|
||||||
cb->battleMakeUnitAction(battleID, BattleAction::makeMeleeAttack(stack, ei.s->getPosition(), targetHex));
|
cb->battleMakeUnitAction(battleID, BattleAction::makeMeleeAttack(stack, ei.s->getPosition(), targetHex));
|
||||||
return;
|
return;
|
||||||
|
27
ChangeLog.md
27
ChangeLog.md
@ -1,4 +1,4 @@
|
|||||||
# 1.4.3 -> 1.5.0
|
# 1.4.5 -> 1.5.0
|
||||||
|
|
||||||
### General
|
### General
|
||||||
* Added Chinese translation to map editor
|
* Added Chinese translation to map editor
|
||||||
@ -10,6 +10,31 @@
|
|||||||
* Added status bar to the backpack window
|
* Added status bar to the backpack window
|
||||||
* Quick backpack window is now only available when enabled Interface enhancements
|
* Quick backpack window is now only available when enabled Interface enhancements
|
||||||
|
|
||||||
|
# 1.4.4 -> 1.4.5
|
||||||
|
|
||||||
|
### Stability
|
||||||
|
* Fixed crash on creature spellcasting
|
||||||
|
* Fixed crash on unit entering magical obstacles such as quicksands
|
||||||
|
* Fixed freeze on map loading on some systems
|
||||||
|
* Fixed crash on attempt to start campaign with unsupported map
|
||||||
|
* Fixed crash on opening creature information window with invalid SPELL_IMMUNITY bonus
|
||||||
|
|
||||||
|
### Random Maps Generator
|
||||||
|
* Fixed placement of guards sometimes resulting into open connection into third zone
|
||||||
|
* Fixed rare crash on multithreaded access during placement of artifacts or wandering monsters
|
||||||
|
|
||||||
|
### Map Editor
|
||||||
|
* Fixed inspector using wrong editor for some values
|
||||||
|
|
||||||
|
### AI
|
||||||
|
* Fixed bug leading to AI not attacking wandering monsters in some cases
|
||||||
|
* Fixed crash on using StupidAI for autocombat or for enemy players
|
||||||
|
|
||||||
|
# 1.4.3 -> 1.4.4
|
||||||
|
|
||||||
|
### General
|
||||||
|
* Fixed crash on generation of random maps
|
||||||
|
|
||||||
# 1.4.2 -> 1.4.3
|
# 1.4.2 -> 1.4.3
|
||||||
|
|
||||||
### General
|
### General
|
||||||
|
@ -72,6 +72,7 @@
|
|||||||
"vcmi.lobby.noUnderground" : "no underground",
|
"vcmi.lobby.noUnderground" : "no underground",
|
||||||
"vcmi.lobby.sortDate" : "Sorts maps by change date",
|
"vcmi.lobby.sortDate" : "Sorts maps by change date",
|
||||||
|
|
||||||
|
"vcmi.client.errors.invalidMap" : "{Invalid map or campaign}\n\nFailed to start game! Selected map or campaign might be invalid or corrupted. Reason:\n%s",
|
||||||
"vcmi.client.errors.missingCampaigns" : "{Missing data files}\n\nCampaigns data files were not found! You may be using incomplete or corrupted Heroes 3 data files. Please reinstall game data.",
|
"vcmi.client.errors.missingCampaigns" : "{Missing data files}\n\nCampaigns data files were not found! You may be using incomplete or corrupted Heroes 3 data files. Please reinstall game data.",
|
||||||
"vcmi.server.errors.existingProcess" : "Another VCMI server process is running. Please terminate it before starting a new game.",
|
"vcmi.server.errors.existingProcess" : "Another VCMI server process is running. Please terminate it before starting a new game.",
|
||||||
"vcmi.server.errors.modsToEnable" : "{Following mods are required}",
|
"vcmi.server.errors.modsToEnable" : "{Following mods are required}",
|
||||||
|
@ -750,7 +750,7 @@ void BattleActionsController::actionRealize(PossiblePlayerBattleAction action, B
|
|||||||
|
|
||||||
if (!spellcastingModeActive())
|
if (!spellcastingModeActive())
|
||||||
{
|
{
|
||||||
if (action.spell().toSpell())
|
if (action.spell().hasValue())
|
||||||
{
|
{
|
||||||
owner.giveCommand(EActionType::MONSTER_SPELL, targetHex, action.spell());
|
owner.giveCommand(EActionType::MONSTER_SPELL, targetHex, action.spell());
|
||||||
}
|
}
|
||||||
@ -887,17 +887,17 @@ void BattleActionsController::tryActivateStackSpellcasting(const CStack *casterS
|
|||||||
{
|
{
|
||||||
// faerie dragon can cast only one, randomly selected spell until their next move
|
// faerie dragon can cast only one, randomly selected spell until their next move
|
||||||
//TODO: faerie dragon type spell should be selected by server
|
//TODO: faerie dragon type spell should be selected by server
|
||||||
const auto * spellToCast = owner.getBattle()->getRandomCastedSpell(CRandomGenerator::getDefault(), casterStack).toSpell();
|
const auto spellToCast = owner.getBattle()->getRandomCastedSpell(CRandomGenerator::getDefault(), casterStack);
|
||||||
|
|
||||||
if (spellToCast)
|
if (spellToCast.hasValue())
|
||||||
creatureSpells.push_back(spellToCast);
|
creatureSpells.push_back(spellToCast.toSpell());
|
||||||
}
|
}
|
||||||
|
|
||||||
TConstBonusListPtr bl = casterStack->getBonuses(Selector::type()(BonusType::SPELLCASTER));
|
TConstBonusListPtr bl = casterStack->getBonuses(Selector::type()(BonusType::SPELLCASTER));
|
||||||
|
|
||||||
for(const auto & bonus : *bl)
|
for(const auto & bonus : *bl)
|
||||||
{
|
{
|
||||||
if (bonus->additionalInfo[0] <= 0)
|
if (bonus->additionalInfo[0] <= 0 && bonus->subtype.as<SpellID>().hasValue())
|
||||||
creatureSpells.push_back(bonus->subtype.as<SpellID>().toSpell());
|
creatureSpells.push_back(bonus->subtype.as<SpellID>().toSpell());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -352,13 +352,13 @@ void BattleInterface::spellCast(const BattleSpellCast * sc)
|
|||||||
CCS->curh->set(Cursor::Combat::BLOCKED);
|
CCS->curh->set(Cursor::Combat::BLOCKED);
|
||||||
|
|
||||||
const SpellID spellID = sc->spellID;
|
const SpellID spellID = sc->spellID;
|
||||||
|
|
||||||
|
if(!spellID.hasValue())
|
||||||
|
return;
|
||||||
|
|
||||||
const CSpell * spell = spellID.toSpell();
|
const CSpell * spell = spellID.toSpell();
|
||||||
auto targetedTile = sc->tile;
|
auto targetedTile = sc->tile;
|
||||||
|
|
||||||
assert(spell);
|
|
||||||
if(!spell)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const AudioPath & castSoundPath = spell->getCastSound();
|
const AudioPath & castSoundPath = spell->getCastSound();
|
||||||
|
|
||||||
if (!castSoundPath.empty())
|
if (!castSoundPath.empty())
|
||||||
|
@ -21,10 +21,6 @@
|
|||||||
|
|
||||||
#include <SDL_events.h>
|
#include <SDL_events.h>
|
||||||
|
|
||||||
#ifdef VCMI_APPLE
|
|
||||||
# include <dispatch/dispatch.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void InputSourceText::handleEventTextInput(const SDL_TextInputEvent & text)
|
void InputSourceText::handleEventTextInput(const SDL_TextInputEvent & text)
|
||||||
{
|
{
|
||||||
GH.events().dispatchTextInput(text.text);
|
GH.events().dispatchTextInput(text.text);
|
||||||
@ -37,11 +33,8 @@ void InputSourceText::handleEventTextEditing(const SDL_TextEditingEvent & text)
|
|||||||
|
|
||||||
void InputSourceText::startTextInput(const Rect & whereInput)
|
void InputSourceText::startTextInput(const Rect & whereInput)
|
||||||
{
|
{
|
||||||
|
GH.dispatchMainThread([whereInput]()
|
||||||
#ifdef VCMI_APPLE
|
{
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Rect rectInScreenCoordinates = GH.screenHandler().convertLogicalPointsToWindow(whereInput);
|
Rect rectInScreenCoordinates = GH.screenHandler().convertLogicalPointsToWindow(whereInput);
|
||||||
SDL_Rect textInputRect = CSDL_Ext::toSDL(rectInScreenCoordinates);
|
SDL_Rect textInputRect = CSDL_Ext::toSDL(rectInScreenCoordinates);
|
||||||
|
|
||||||
@ -51,24 +44,16 @@ void InputSourceText::startTextInput(const Rect & whereInput)
|
|||||||
{
|
{
|
||||||
SDL_StartTextInput();
|
SDL_StartTextInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef VCMI_APPLE
|
|
||||||
});
|
});
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputSourceText::stopTextInput()
|
void InputSourceText::stopTextInput()
|
||||||
{
|
{
|
||||||
#ifdef VCMI_APPLE
|
GH.dispatchMainThread([]()
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
{
|
||||||
#endif
|
|
||||||
|
|
||||||
if (SDL_IsTextInputActive() == SDL_TRUE)
|
if (SDL_IsTextInputActive() == SDL_TRUE)
|
||||||
{
|
{
|
||||||
SDL_StopTextInput();
|
SDL_StopTextInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef VCMI_APPLE
|
|
||||||
});
|
});
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
@ -135,11 +135,23 @@ void CLobbyScreen::toggleTab(std::shared_ptr<CIntObject> tab)
|
|||||||
|
|
||||||
void CLobbyScreen::startCampaign()
|
void CLobbyScreen::startCampaign()
|
||||||
{
|
{
|
||||||
if(CSH->mi)
|
if(!CSH->mi)
|
||||||
{
|
return;
|
||||||
|
|
||||||
|
try {
|
||||||
auto ourCampaign = CampaignHandler::getCampaign(CSH->mi->fileURI);
|
auto ourCampaign = CampaignHandler::getCampaign(CSH->mi->fileURI);
|
||||||
CSH->setCampaignState(ourCampaign);
|
CSH->setCampaignState(ourCampaign);
|
||||||
}
|
}
|
||||||
|
catch (const std::runtime_error &e)
|
||||||
|
{
|
||||||
|
// handle possible exception on map loading. For example campaign that contains map in unsupported format
|
||||||
|
// for example, wog campaigns or hota campaigns without hota map support mod
|
||||||
|
MetaString message;
|
||||||
|
message.appendTextID("vcmi.client.errors.invalidMap");
|
||||||
|
message.replaceRawString(e.what());
|
||||||
|
|
||||||
|
CInfoWindow::showInfoDialog(message.toString(), {});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CLobbyScreen::startScenario(bool allowOnlyAI)
|
void CLobbyScreen::startScenario(bool allowOnlyAI)
|
||||||
|
@ -205,6 +205,8 @@ void SDLImage::exportBitmap(const boost::filesystem::path& path) const
|
|||||||
|
|
||||||
void SDLImage::playerColored(PlayerColor player)
|
void SDLImage::playerColored(PlayerColor player)
|
||||||
{
|
{
|
||||||
|
if (!surf)
|
||||||
|
return;
|
||||||
graphics->blueToPlayersAdv(surf, player);
|
graphics->blueToPlayersAdv(surf, player);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -553,8 +553,7 @@ Point CGStatusBar::getBorderSize()
|
|||||||
|
|
||||||
CTextInput::CTextInput(const Rect & Pos, EFonts font, const CFunctionList<void(const std::string &)> & CB, bool giveFocusToInput)
|
CTextInput::CTextInput(const Rect & Pos, EFonts font, const CFunctionList<void(const std::string &)> & CB, bool giveFocusToInput)
|
||||||
: CLabel(Pos.x, Pos.y, font, ETextAlignment::CENTER),
|
: CLabel(Pos.x, Pos.y, font, ETextAlignment::CENTER),
|
||||||
cb(CB),
|
cb(CB)
|
||||||
CFocusable(std::make_shared<CKeyboardFocusListener>(this))
|
|
||||||
{
|
{
|
||||||
setRedrawParent(true);
|
setRedrawParent(true);
|
||||||
pos.h = Pos.h;
|
pos.h = Pos.h;
|
||||||
@ -570,7 +569,7 @@ CTextInput::CTextInput(const Rect & Pos, EFonts font, const CFunctionList<void(c
|
|||||||
}
|
}
|
||||||
|
|
||||||
CTextInput::CTextInput(const Rect & Pos, const Point & bgOffset, const ImagePath & bgName, const CFunctionList<void(const std::string &)> & CB)
|
CTextInput::CTextInput(const Rect & Pos, const Point & bgOffset, const ImagePath & bgName, const CFunctionList<void(const std::string &)> & CB)
|
||||||
:cb(CB), CFocusable(std::make_shared<CKeyboardFocusListener>(this))
|
:cb(CB)
|
||||||
{
|
{
|
||||||
pos += Pos.topLeft();
|
pos += Pos.topLeft();
|
||||||
pos.h = Pos.h;
|
pos.h = Pos.h;
|
||||||
@ -587,7 +586,6 @@ CTextInput::CTextInput(const Rect & Pos, const Point & bgOffset, const ImagePath
|
|||||||
}
|
}
|
||||||
|
|
||||||
CTextInput::CTextInput(const Rect & Pos, std::shared_ptr<IImage> srf)
|
CTextInput::CTextInput(const Rect & Pos, std::shared_ptr<IImage> srf)
|
||||||
:CFocusable(std::make_shared<CKeyboardFocusListener>(this))
|
|
||||||
{
|
{
|
||||||
pos += Pos.topLeft();
|
pos += Pos.topLeft();
|
||||||
OBJ_CONSTRUCTION;
|
OBJ_CONSTRUCTION;
|
||||||
@ -603,20 +601,15 @@ CTextInput::CTextInput(const Rect & Pos, std::shared_ptr<IImage> srf)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::atomic<int> CKeyboardFocusListener::usageIndex(0);
|
std::atomic<int> CFocusable::usageIndex(0);
|
||||||
|
|
||||||
CKeyboardFocusListener::CKeyboardFocusListener(CTextInput * textInput)
|
void CFocusable::focusGot()
|
||||||
:textInput(textInput)
|
|
||||||
{
|
{
|
||||||
}
|
GH.startTextInput(pos);
|
||||||
|
|
||||||
void CKeyboardFocusListener::focusGot()
|
|
||||||
{
|
|
||||||
GH.startTextInput(textInput->pos);
|
|
||||||
usageIndex++;
|
usageIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CKeyboardFocusListener::focusLost()
|
void CFocusable::focusLost()
|
||||||
{
|
{
|
||||||
if(0 == --usageIndex)
|
if(0 == --usageIndex)
|
||||||
{
|
{
|
||||||
@ -769,12 +762,6 @@ void CTextInput::numberFilter(std::string & text, const std::string & oldText, i
|
|||||||
}
|
}
|
||||||
|
|
||||||
CFocusable::CFocusable()
|
CFocusable::CFocusable()
|
||||||
:CFocusable(std::make_shared<IFocusListener>())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CFocusable::CFocusable(std::shared_ptr<IFocusListener> focusListener)
|
|
||||||
: focusListener(focusListener)
|
|
||||||
{
|
{
|
||||||
focus = false;
|
focus = false;
|
||||||
focusables.push_back(this);
|
focusables.push_back(this);
|
||||||
@ -785,7 +772,7 @@ CFocusable::~CFocusable()
|
|||||||
if(hasFocus())
|
if(hasFocus())
|
||||||
{
|
{
|
||||||
inputWithFocus = nullptr;
|
inputWithFocus = nullptr;
|
||||||
focusListener->focusLost();
|
focusLost();
|
||||||
}
|
}
|
||||||
|
|
||||||
focusables -= this;
|
focusables -= this;
|
||||||
@ -799,13 +786,13 @@ bool CFocusable::hasFocus() const
|
|||||||
void CFocusable::giveFocus()
|
void CFocusable::giveFocus()
|
||||||
{
|
{
|
||||||
focus = true;
|
focus = true;
|
||||||
focusListener->focusGot();
|
focusGot();
|
||||||
redraw();
|
redraw();
|
||||||
|
|
||||||
if(inputWithFocus)
|
if(inputWithFocus)
|
||||||
{
|
{
|
||||||
inputWithFocus->focus = false;
|
inputWithFocus->focus = false;
|
||||||
inputWithFocus->focusListener->focusLost();
|
inputWithFocus->focusLost();
|
||||||
inputWithFocus->redraw();
|
inputWithFocus->redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -837,7 +824,7 @@ void CFocusable::removeFocus()
|
|||||||
if(this == inputWithFocus)
|
if(this == inputWithFocus)
|
||||||
{
|
{
|
||||||
focus = false;
|
focus = false;
|
||||||
focusListener->focusLost();
|
focusLost();
|
||||||
redraw();
|
redraw();
|
||||||
|
|
||||||
inputWithFocus = nullptr;
|
inputWithFocus = nullptr;
|
||||||
|
@ -163,25 +163,12 @@ public:
|
|||||||
void clear() override;
|
void clear() override;
|
||||||
void setEnteringMode(bool on) override;
|
void setEnteringMode(bool on) override;
|
||||||
void setEnteredText(const std::string & text) override;
|
void setEnteredText(const std::string & text) override;
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
class CFocusable;
|
|
||||||
|
|
||||||
class IFocusListener
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual void focusGot() {};
|
|
||||||
virtual void focusLost() {};
|
|
||||||
virtual ~IFocusListener() = default;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// UIElement which can get input focus
|
/// UIElement which can get input focus
|
||||||
class CFocusable : public virtual CIntObject
|
class CFocusable : public virtual CIntObject
|
||||||
{
|
{
|
||||||
private:
|
static std::atomic<int> usageIndex;
|
||||||
std::shared_ptr<IFocusListener> focusListener;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool focus; //only one focusable control can have focus at one moment
|
bool focus; //only one focusable control can have focus at one moment
|
||||||
|
|
||||||
@ -190,27 +177,16 @@ public:
|
|||||||
void removeFocus(); //remove focus
|
void removeFocus(); //remove focus
|
||||||
bool hasFocus() const;
|
bool hasFocus() const;
|
||||||
|
|
||||||
|
void focusGot();
|
||||||
|
void focusLost();
|
||||||
|
|
||||||
static std::list<CFocusable *> focusables; //all existing objs
|
static std::list<CFocusable *> focusables; //all existing objs
|
||||||
static CFocusable * inputWithFocus; //who has focus now
|
static CFocusable * inputWithFocus; //who has focus now
|
||||||
|
|
||||||
CFocusable();
|
CFocusable();
|
||||||
CFocusable(std::shared_ptr<IFocusListener> focusListener);
|
|
||||||
~CFocusable();
|
~CFocusable();
|
||||||
};
|
};
|
||||||
|
|
||||||
class CTextInput;
|
|
||||||
class CKeyboardFocusListener : public IFocusListener
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
static std::atomic<int> usageIndex;
|
|
||||||
CTextInput * textInput;
|
|
||||||
|
|
||||||
public:
|
|
||||||
CKeyboardFocusListener(CTextInput * textInput);
|
|
||||||
void focusGot() override;
|
|
||||||
void focusLost() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Text input box where players can enter text
|
/// Text input box where players can enter text
|
||||||
class CTextInput : public CLabel, public CFocusable
|
class CTextInput : public CLabel, public CFocusable
|
||||||
{
|
{
|
||||||
|
6
debian/changelog
vendored
6
debian/changelog
vendored
@ -4,6 +4,12 @@ vcmi (1.5.0) jammy; urgency=medium
|
|||||||
|
|
||||||
-- Ivan Savenko <saven.ivan@gmail.com> Fri, 1 Mar 2024 12:00:00 +0200
|
-- Ivan Savenko <saven.ivan@gmail.com> Fri, 1 Mar 2024 12:00:00 +0200
|
||||||
|
|
||||||
|
vcmi (1.4.5) jammy; urgency=medium
|
||||||
|
|
||||||
|
* New upstream release
|
||||||
|
|
||||||
|
-- Ivan Savenko <saven.ivan@gmail.com> Tue, 23 Jan 2024 12:00:00 +0200
|
||||||
|
|
||||||
vcmi (1.4.4) jammy; urgency=medium
|
vcmi (1.4.4) jammy; urgency=medium
|
||||||
|
|
||||||
* New upstream release
|
* New upstream release
|
||||||
|
@ -77,6 +77,7 @@
|
|||||||
</screenshots>
|
</screenshots>
|
||||||
<releases>
|
<releases>
|
||||||
<release version="1.5.0" date="2024-03-01" type="development"/>
|
<release version="1.5.0" date="2024-03-01" type="development"/>
|
||||||
|
<release version="1.4.5" date="2024-01-23" type="stable"/>
|
||||||
<release version="1.4.4" date="2024-01-20" type="stable"/>
|
<release version="1.4.4" date="2024-01-20" type="stable"/>
|
||||||
<release version="1.4.3" date="2024-01-19" type="stable"/>
|
<release version="1.4.3" date="2024-01-19" type="stable"/>
|
||||||
<release version="1.4.2" date="2023-12-25" type="stable"/>
|
<release version="1.4.2" date="2023-12-25" type="stable"/>
|
||||||
|
@ -38,6 +38,9 @@ bool CModEntry::isEnabled() const
|
|||||||
if(!isInstalled())
|
if(!isInstalled())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (!isVisible())
|
||||||
|
return false;
|
||||||
|
|
||||||
return modSettings["active"].toBool();
|
return modSettings["active"].toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -521,7 +521,7 @@ QStringList CModListView::findDependentMods(QString mod, bool excludeDisabled)
|
|||||||
{
|
{
|
||||||
auto current = modModel->getMod(modName);
|
auto current = modModel->getMod(modName);
|
||||||
|
|
||||||
if(!current.isInstalled())
|
if(!current.isInstalled() || !current.isVisible())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if(current.getDependencies().contains(mod, Qt::CaseInsensitive))
|
if(current.getDependencies().contains(mod, Qt::CaseInsensitive))
|
||||||
|
@ -76,10 +76,10 @@ std::string CBonusTypeHandler::bonusToString(const std::shared_ptr<Bonus> & bonu
|
|||||||
if (text.find("${val}") != std::string::npos)
|
if (text.find("${val}") != std::string::npos)
|
||||||
boost::algorithm::replace_all(text, "${val}", std::to_string(bearer->valOfBonuses(Selector::typeSubtype(bonus->type, bonus->subtype))));
|
boost::algorithm::replace_all(text, "${val}", std::to_string(bearer->valOfBonuses(Selector::typeSubtype(bonus->type, bonus->subtype))));
|
||||||
|
|
||||||
if (text.find("${subtype.creature}") != std::string::npos && bonus->subtype.as<CreatureID>() != CreatureID::NONE)
|
if (text.find("${subtype.creature}") != std::string::npos && bonus->subtype.as<CreatureID>().hasValue())
|
||||||
boost::algorithm::replace_all(text, "${subtype.creature}", bonus->subtype.as<CreatureID>().toCreature()->getNamePluralTranslated());
|
boost::algorithm::replace_all(text, "${subtype.creature}", bonus->subtype.as<CreatureID>().toCreature()->getNamePluralTranslated());
|
||||||
|
|
||||||
if (text.find("${subtype.spell}") != std::string::npos && bonus->subtype.as<SpellID>() != SpellID::NONE)
|
if (text.find("${subtype.spell}") != std::string::npos && bonus->subtype.as<SpellID>().hasValue())
|
||||||
boost::algorithm::replace_all(text, "${subtype.spell}", bonus->subtype.as<SpellID>().toSpell()->getNameTranslated());
|
boost::algorithm::replace_all(text, "${subtype.spell}", bonus->subtype.as<SpellID>().toSpell()->getNameTranslated());
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
@ -95,8 +95,11 @@ ImagePath CBonusTypeHandler::bonusToGraphics(const std::shared_ptr<Bonus> & bonu
|
|||||||
case BonusType::SPELL_IMMUNITY:
|
case BonusType::SPELL_IMMUNITY:
|
||||||
{
|
{
|
||||||
fullPath = true;
|
fullPath = true;
|
||||||
|
if (bonus->subtype.as<SpellID>().hasValue())
|
||||||
|
{
|
||||||
const CSpell * sp = bonus->subtype.as<SpellID>().toSpell();
|
const CSpell * sp = bonus->subtype.as<SpellID>().toSpell();
|
||||||
fileName = sp->getIconImmune();
|
fileName = sp->getIconImmune();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case BonusType::SPELL_DAMAGE_REDUCTION: //Spell damage reduction for all schools
|
case BonusType::SPELL_DAMAGE_REDUCTION: //Spell damage reduction for all schools
|
||||||
|
@ -80,7 +80,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
|||||||
IdentifierType JsonRandom::decodeKey(const std::string & modScope, const std::string & value, const Variables & variables)
|
IdentifierType JsonRandom::decodeKey(const std::string & modScope, const std::string & value, const Variables & variables)
|
||||||
{
|
{
|
||||||
if (value.empty() || value[0] != '@')
|
if (value.empty() || value[0] != '@')
|
||||||
return IdentifierType(*VLC->identifiers()->getIdentifier(modScope, IdentifierType::entityType(), value));
|
return IdentifierType(VLC->identifiers()->getIdentifier(modScope, IdentifierType::entityType(), value).value_or(-1));
|
||||||
else
|
else
|
||||||
return loadVariable(IdentifierType::entityType(), value, variables, IdentifierType::NONE);
|
return loadVariable(IdentifierType::entityType(), value, variables, IdentifierType::NONE);
|
||||||
}
|
}
|
||||||
@ -89,7 +89,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
|||||||
IdentifierType JsonRandom::decodeKey(const JsonNode & value, const Variables & variables)
|
IdentifierType JsonRandom::decodeKey(const JsonNode & value, const Variables & variables)
|
||||||
{
|
{
|
||||||
if (value.String().empty() || value.String()[0] != '@')
|
if (value.String().empty() || value.String()[0] != '@')
|
||||||
return IdentifierType(*VLC->identifiers()->getIdentifier(IdentifierType::entityType(), value));
|
return IdentifierType(VLC->identifiers()->getIdentifier(IdentifierType::entityType(), value).value_or(-1));
|
||||||
else
|
else
|
||||||
return loadVariable(IdentifierType::entityType(), value.String(), variables, IdentifierType::NONE);
|
return loadVariable(IdentifierType::entityType(), value.String(), variables, IdentifierType::NONE);
|
||||||
}
|
}
|
||||||
|
@ -867,9 +867,10 @@ bool CBattleInfoCallback::handleObstacleTriggersForUnit(SpellCastEnvironment & s
|
|||||||
auto shouldReveal = !spellObstacle->hidden || !battleIsObstacleVisibleForSide(*obstacle, (BattlePerspective::BattlePerspective)side);
|
auto shouldReveal = !spellObstacle->hidden || !battleIsObstacleVisibleForSide(*obstacle, (BattlePerspective::BattlePerspective)side);
|
||||||
const auto * hero = battleGetFightingHero(spellObstacle->casterSide);
|
const auto * hero = battleGetFightingHero(spellObstacle->casterSide);
|
||||||
auto caster = spells::ObstacleCasterProxy(getBattle()->getSidePlayer(spellObstacle->casterSide), hero, *spellObstacle);
|
auto caster = spells::ObstacleCasterProxy(getBattle()->getSidePlayer(spellObstacle->casterSide), hero, *spellObstacle);
|
||||||
const auto * sp = obstacle->getTrigger().toSpell();
|
|
||||||
if(obstacle->triggersEffects() && sp)
|
if(obstacle->triggersEffects() && obstacle->getTrigger().hasValue())
|
||||||
{
|
{
|
||||||
|
const auto * sp = obstacle->getTrigger().toSpell();
|
||||||
auto cast = spells::BattleCast(this, &caster, spells::Mode::PASSIVE, sp);
|
auto cast = spells::BattleCast(this, &caster, spells::Mode::PASSIVE, sp);
|
||||||
spells::detail::ProblemImpl ignored;
|
spells::detail::ProblemImpl ignored;
|
||||||
auto target = spells::Target(1, spells::Destination(&unit));
|
auto target = spells::Target(1, spells::Destination(&unit));
|
||||||
|
@ -52,6 +52,11 @@ public:
|
|||||||
num = value;
|
num = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr bool hasValue() const
|
||||||
|
{
|
||||||
|
return num >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
struct hash
|
struct hash
|
||||||
{
|
{
|
||||||
size_t operator()(const IdentifierBase & id) const
|
size_t operator()(const IdentifierBase & id) const
|
||||||
|
@ -21,17 +21,7 @@
|
|||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
AObjectTypeHandler::AObjectTypeHandler() = default;
|
AObjectTypeHandler::AObjectTypeHandler() = default;
|
||||||
|
AObjectTypeHandler::~AObjectTypeHandler() = default;
|
||||||
AObjectTypeHandler::~AObjectTypeHandler()
|
|
||||||
{
|
|
||||||
// FIXME: currently on Android there is a weird crash in destructor of 'base' member
|
|
||||||
// this code attempts to localize and fix this crash
|
|
||||||
if (base)
|
|
||||||
{
|
|
||||||
base->clear();
|
|
||||||
base.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string AObjectTypeHandler::getJsonKey() const
|
std::string AObjectTypeHandler::getJsonKey() const
|
||||||
{
|
{
|
||||||
@ -89,12 +79,12 @@ void AObjectTypeHandler::init(const JsonNode & input)
|
|||||||
if (base)
|
if (base)
|
||||||
JsonUtils::inherit(entry.second, *base);
|
JsonUtils::inherit(entry.second, *base);
|
||||||
|
|
||||||
auto * tmpl = new ObjectTemplate;
|
auto tmpl = std::make_shared<ObjectTemplate>();
|
||||||
tmpl->id = Obj(type);
|
tmpl->id = Obj(type);
|
||||||
tmpl->subid = subtype;
|
tmpl->subid = subtype;
|
||||||
tmpl->stringID = entry.first; // FIXME: create "fullID" - type.object.template?
|
tmpl->stringID = entry.first; // FIXME: create "fullID" - type.object.template?
|
||||||
tmpl->readJson(entry.second);
|
tmpl->readJson(entry.second);
|
||||||
templates.push_back(std::shared_ptr<const ObjectTemplate>(tmpl));
|
templates.push_back(tmpl);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(const JsonNode & node : input["sounds"]["ambient"].Vector())
|
for(const JsonNode & node : input["sounds"]["ambient"].Vector())
|
||||||
@ -188,12 +178,13 @@ void AObjectTypeHandler::addTemplate(JsonNode config)
|
|||||||
config.setType(JsonNode::JsonType::DATA_STRUCT); // ensure that input is not null
|
config.setType(JsonNode::JsonType::DATA_STRUCT); // ensure that input is not null
|
||||||
if (base)
|
if (base)
|
||||||
JsonUtils::inherit(config, *base);
|
JsonUtils::inherit(config, *base);
|
||||||
auto * tmpl = new ObjectTemplate;
|
|
||||||
|
auto tmpl = std::make_shared<ObjectTemplate>();
|
||||||
tmpl->id = Obj(type);
|
tmpl->id = Obj(type);
|
||||||
tmpl->subid = subtype;
|
tmpl->subid = subtype;
|
||||||
tmpl->stringID.clear(); // TODO?
|
tmpl->stringID.clear(); // TODO?
|
||||||
tmpl->readJson(config);
|
tmpl->readJson(config);
|
||||||
templates.emplace_back(tmpl);
|
templates.push_back(tmpl);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::shared_ptr<const ObjectTemplate>> AObjectTypeHandler::getTemplates() const
|
std::vector<std::shared_ptr<const ObjectTemplate>> AObjectTypeHandler::getTemplates() const
|
||||||
|
@ -109,13 +109,13 @@ std::vector<JsonNode> CObjectClassesHandler::loadLegacyData()
|
|||||||
|
|
||||||
for (size_t i = 0; i < totalNumber; i++)
|
for (size_t i = 0; i < totalNumber; i++)
|
||||||
{
|
{
|
||||||
auto * tmpl = new ObjectTemplate;
|
auto tmpl = std::make_shared<ObjectTemplate>();
|
||||||
|
|
||||||
tmpl->readTxt(parser);
|
tmpl->readTxt(parser);
|
||||||
parser.endLine();
|
parser.endLine();
|
||||||
|
|
||||||
std::pair key(tmpl->id, tmpl->subid);
|
std::pair key(tmpl->id, tmpl->subid);
|
||||||
legacyTemplates.insert(std::make_pair(key, std::shared_ptr<const ObjectTemplate>(tmpl)));
|
legacyTemplates.insert(std::make_pair(key, tmpl));
|
||||||
}
|
}
|
||||||
|
|
||||||
objects.resize(256);
|
objects.resize(256);
|
||||||
|
@ -35,17 +35,17 @@ void DwellingInstanceConstructor::initTypeData(const JsonNode & input)
|
|||||||
const auto totalLevels = levels.size();
|
const auto totalLevels = levels.size();
|
||||||
|
|
||||||
availableCreatures.resize(totalLevels);
|
availableCreatures.resize(totalLevels);
|
||||||
for(auto currentLevel = 0; currentLevel < totalLevels; currentLevel++)
|
for(int currentLevel = 0; currentLevel < totalLevels; currentLevel++)
|
||||||
{
|
{
|
||||||
const JsonVector & creaturesOnLevel = levels[currentLevel].Vector();
|
const JsonVector & creaturesOnLevel = levels[currentLevel].Vector();
|
||||||
const auto creaturesNumber = creaturesOnLevel.size();
|
const auto creaturesNumber = creaturesOnLevel.size();
|
||||||
availableCreatures[currentLevel].resize(creaturesNumber);
|
availableCreatures[currentLevel].resize(creaturesNumber);
|
||||||
|
|
||||||
for(auto currentCreature = 0; currentCreature < creaturesNumber; currentCreature++)
|
for(int currentCreature = 0; currentCreature < creaturesNumber; currentCreature++)
|
||||||
{
|
{
|
||||||
VLC->identifiers()->requestIdentifier("creature", creaturesOnLevel[currentCreature], [=] (si32 index)
|
VLC->identifiers()->requestIdentifier("creature", creaturesOnLevel[currentCreature], [this, currentLevel, currentCreature] (si32 index)
|
||||||
{
|
{
|
||||||
availableCreatures[currentLevel][currentCreature] = CreatureID(index).toCreature();
|
availableCreatures.at(currentLevel).at(currentCreature) = CreatureID(index).toCreature();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
assert(!availableCreatures[currentLevel].empty());
|
assert(!availableCreatures[currentLevel].empty());
|
||||||
|
@ -1075,14 +1075,14 @@ void CMapLoaderJson::MapObjectLoader::construct()
|
|||||||
|
|
||||||
auto handler = VLC->objtypeh->getHandlerFor( ModScope::scopeMap(), typeName, subtypeName);
|
auto handler = VLC->objtypeh->getHandlerFor( ModScope::scopeMap(), typeName, subtypeName);
|
||||||
|
|
||||||
auto * appearance = new ObjectTemplate;
|
auto appearance = std::make_shared<ObjectTemplate>();
|
||||||
|
|
||||||
appearance->id = Obj(handler->getIndex());
|
appearance->id = Obj(handler->getIndex());
|
||||||
appearance->subid = handler->getSubIndex();
|
appearance->subid = handler->getSubIndex();
|
||||||
appearance->readJson(configuration["template"], false);
|
appearance->readJson(configuration["template"], false);
|
||||||
|
|
||||||
// Will be destroyed soon and replaced with shared template
|
// Will be destroyed soon and replaced with shared template
|
||||||
instance = handler->create(owner->map->cb, std::shared_ptr<const ObjectTemplate>(appearance));
|
instance = handler->create(owner->map->cb, appearance);
|
||||||
|
|
||||||
instance->id = ObjectInstanceID(static_cast<si32>(owner->map->objects.size()));
|
instance->id = ObjectInstanceID(static_cast<si32>(owner->map->objects.size()));
|
||||||
instance->instanceName = jsonKey;
|
instance->instanceName = jsonKey;
|
||||||
|
@ -3966,14 +3966,14 @@ bool CGameHandler::moveStack(const StackLocation &src, const StackLocation &dst,
|
|||||||
|
|
||||||
void CGameHandler::castSpell(const spells::Caster * caster, SpellID spellID, const int3 &pos)
|
void CGameHandler::castSpell(const spells::Caster * caster, SpellID spellID, const int3 &pos)
|
||||||
{
|
{
|
||||||
const CSpell * s = spellID.toSpell();
|
if (!spellID.hasValue())
|
||||||
if(!s)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
AdventureSpellCastParameters p;
|
AdventureSpellCastParameters p;
|
||||||
p.caster = caster;
|
p.caster = caster;
|
||||||
p.pos = pos;
|
p.pos = pos;
|
||||||
|
|
||||||
|
const CSpell * s = spellID.toSpell();
|
||||||
s->adventureCast(spellEnv, p);
|
s->adventureCast(spellEnv, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,9 +327,9 @@ void ApplyGhNetPackVisitor::visitCastAdvSpell(CastAdvSpell & pack)
|
|||||||
{
|
{
|
||||||
gh.throwIfWrongOwner(&pack, pack.hid);
|
gh.throwIfWrongOwner(&pack, pack.hid);
|
||||||
|
|
||||||
const CSpell * s = pack.sid.toSpell();
|
if (!pack.sid.hasValue())
|
||||||
if(!s)
|
|
||||||
gh.throwNotAllowedAction(&pack);
|
gh.throwNotAllowedAction(&pack);
|
||||||
|
|
||||||
const CGHeroInstance * h = gh.getHero(pack.hid);
|
const CGHeroInstance * h = gh.getHero(pack.hid);
|
||||||
if(!h)
|
if(!h)
|
||||||
gh.throwNotAllowedAction(&pack);
|
gh.throwNotAllowedAction(&pack);
|
||||||
@ -338,6 +338,7 @@ void ApplyGhNetPackVisitor::visitCastAdvSpell(CastAdvSpell & pack)
|
|||||||
p.caster = h;
|
p.caster = h;
|
||||||
p.pos = pack.pos;
|
p.pos = pack.pos;
|
||||||
|
|
||||||
|
const CSpell * s = pack.sid.toSpell();
|
||||||
result = s->adventureCast(gh.spellEnv, p);
|
result = s->adventureCast(gh.spellEnv, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,21 +88,21 @@ void TurnTimerHandler::onPlayerGetTurn(PlayerColor player)
|
|||||||
void TurnTimerHandler::update(int waitTime)
|
void TurnTimerHandler::update(int waitTime)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> guard(mx);
|
std::lock_guard<std::recursive_mutex> guard(mx);
|
||||||
if(const auto * gs = gameHandler.gameState())
|
if(!gameHandler.getStartInfo()->turnTimerInfo.isEnabled())
|
||||||
{
|
return;
|
||||||
|
|
||||||
for(PlayerColor player(0); player < PlayerColor::PLAYER_LIMIT; ++player)
|
for(PlayerColor player(0); player < PlayerColor::PLAYER_LIMIT; ++player)
|
||||||
if(gs->isPlayerMakingTurn(player))
|
if(gameHandler.gameState()->isPlayerMakingTurn(player))
|
||||||
onPlayerMakingTurn(player, waitTime);
|
onPlayerMakingTurn(player, waitTime);
|
||||||
|
|
||||||
// create copy for iterations - battle might end during onBattleLoop call
|
// create copy for iterations - battle might end during onBattleLoop call
|
||||||
std::vector<BattleID> ongoingBattles;
|
std::vector<BattleID> ongoingBattles;
|
||||||
|
|
||||||
for (auto & battle : gs->currentBattles)
|
for (auto & battle : gameHandler.gameState()->currentBattles)
|
||||||
ongoingBattles.push_back(battle->battleID);
|
ongoingBattles.push_back(battle->battleID);
|
||||||
|
|
||||||
for (auto & battleID : ongoingBattles)
|
for (auto & battleID : ongoingBattles)
|
||||||
onBattleLoop(battleID, waitTime);
|
onBattleLoop(battleID, waitTime);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TurnTimerHandler::timerCountDown(int & timer, int initialTimer, PlayerColor player, int waitTime)
|
bool TurnTimerHandler::timerCountDown(int & timer, int initialTimer, PlayerColor player, int waitTime)
|
||||||
|
@ -102,13 +102,13 @@ bool BattleActionProcessor::doHeroSpellAction(const CBattleInfoCallback & battle
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CSpell * s = ba.spell.toSpell();
|
if (!ba.spell.hasValue())
|
||||||
if (!s)
|
|
||||||
{
|
{
|
||||||
logGlobal->error("Wrong spell id (%d)!", ba.spell.getNum());
|
logGlobal->error("Wrong spell id (%d)!", ba.spell.getNum());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CSpell * s = ba.spell.toSpell();
|
||||||
spells::BattleCast parameters(&battle, h, spells::Mode::HERO, s);
|
spells::BattleCast parameters(&battle, h, spells::Mode::HERO, s);
|
||||||
|
|
||||||
spells::detail::ProblemImpl problem;
|
spells::detail::ProblemImpl problem;
|
||||||
|
Loading…
Reference in New Issue
Block a user