mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-20 20:23:03 +02:00
Merge branch 'vcmi/master' into 'vcmi/develop'
This commit is contained in:
commit
b7391f49f6
29
ChangeLog.md
29
ChangeLog.md
@ -31,6 +31,35 @@
|
|||||||
* Added support for multiple music tracks for terrains on adventure map
|
* Added support for multiple music tracks for terrains on adventure map
|
||||||
* Fixed several cases where vcmi will report errors in json without specifying filename of invalid file
|
* Fixed several cases where vcmi will report errors in json without specifying filename of invalid file
|
||||||
|
|
||||||
|
# 1.5.5 -> 1.5.6
|
||||||
|
|
||||||
|
### Stability
|
||||||
|
* Fixed possible crash on transferring hero to next campaign scenario if hero has combined artifact some components of which can be transferred
|
||||||
|
* Fixed possible crash on transferring hero to next campaign scenario that has creature with faction limiter in his army
|
||||||
|
* Fixed possible crash on application shutdown due to incorrect destruction order of UI entities
|
||||||
|
|
||||||
|
### Multiplayer
|
||||||
|
* Mod compatibility issues when joining a lobby room now use color coding to make them less easy to miss.
|
||||||
|
* Incompatible mods are now placed before compatible mods when joining lobby room.
|
||||||
|
* Fixed text overflow in online lobby interface
|
||||||
|
* Fixed jittering simultaneous turns slider after moving it twice over short period
|
||||||
|
* Fixed non-functioning slider in invite to game room dialog
|
||||||
|
|
||||||
|
### Interface
|
||||||
|
* Fixed some shortcuts that were not active during the enemy's turn, such as Thieves' Guild.
|
||||||
|
* Game now correctly uses melee damage calculation when forcing a melee attack with a shooter.
|
||||||
|
* Game will now close all open dialogs on start of our turn, to avoid bugs like locked right-click popups
|
||||||
|
|
||||||
|
### Map Objects
|
||||||
|
* Spells the hero can't learn are no longer hidden when received from a rewardable object, such as the Pandora Box
|
||||||
|
* Spells that cannot be learned are now displayed with gray text in the name of the spell.
|
||||||
|
* Configurable objects with scouted state such as Witch Hut in HotA now correctly show their reward on right click after vising them but refusing to accept reward
|
||||||
|
* Right-click tooltip on map dwelling now always shows produced creatures. Player that owns the dwelling can also see number of creatures available for recruit
|
||||||
|
|
||||||
|
### Modding
|
||||||
|
* Fixed possible crash on invalid SPELL_LIKE_ATTACK bonus
|
||||||
|
* Added compatibility check when loading maps with old names for boats
|
||||||
|
|
||||||
# 1.5.4 -> 1.5.5
|
# 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 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.
|
||||||
|
@ -171,40 +171,39 @@ void CPlayerInterface::initGameInterface(std::shared_ptr<Environment> ENV, std::
|
|||||||
adventureInt.reset(new AdventureMapInterface());
|
adventureInt.reset(new AdventureMapInterface());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CPlayerInterface::closeAllDialogs()
|
||||||
|
{
|
||||||
|
// remove all active dialogs that do not expect query answer
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
auto adventureWindow = GH.windows().topWindow<AdventureMapInterface>();
|
||||||
|
auto infoWindow = GH.windows().topWindow<CInfoWindow>();
|
||||||
|
|
||||||
|
if(adventureWindow != nullptr)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if(infoWindow && infoWindow->ID != QueryID::NONE)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (infoWindow)
|
||||||
|
infoWindow->close();
|
||||||
|
else
|
||||||
|
GH.windows().popWindows(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(castleInt)
|
||||||
|
castleInt->close();
|
||||||
|
|
||||||
|
castleInt = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void CPlayerInterface::playerEndsTurn(PlayerColor player)
|
void CPlayerInterface::playerEndsTurn(PlayerColor player)
|
||||||
{
|
{
|
||||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||||
if (player == playerID)
|
if (player == playerID)
|
||||||
{
|
{
|
||||||
makingTurn = false;
|
makingTurn = false;
|
||||||
|
closeAllDialogs();
|
||||||
// remove all active dialogs that do not expect query answer
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
auto adventureWindow = GH.windows().topWindow<AdventureMapInterface>();
|
|
||||||
auto infoWindow = GH.windows().topWindow<CInfoWindow>();
|
|
||||||
|
|
||||||
if(adventureWindow != nullptr)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if(infoWindow && infoWindow->ID != QueryID::NONE)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (infoWindow)
|
|
||||||
infoWindow->close();
|
|
||||||
else
|
|
||||||
GH.windows().popWindows(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(castleInt)
|
|
||||||
castleInt->close();
|
|
||||||
|
|
||||||
castleInt = nullptr;
|
|
||||||
|
|
||||||
// remove all pending dialogs that do not expect query answer
|
|
||||||
vstd::erase_if(dialogs, [](const std::shared_ptr<CInfoWindow> & window){
|
|
||||||
return window->ID == QueryID::NONE;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,6 +285,7 @@ void CPlayerInterface::gamePause(bool pause)
|
|||||||
|
|
||||||
void CPlayerInterface::yourTurn(QueryID queryID)
|
void CPlayerInterface::yourTurn(QueryID queryID)
|
||||||
{
|
{
|
||||||
|
closeAllDialogs();
|
||||||
CTutorialWindow::openWindowFirstTime(TutorialMode::TOUCH_ADVENTUREMAP);
|
CTutorialWindow::openWindowFirstTime(TutorialMode::TOUCH_ADVENTUREMAP);
|
||||||
|
|
||||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||||
@ -1477,7 +1477,7 @@ void CPlayerInterface::update()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
//if there are any waiting dialogs, show them
|
//if there are any waiting dialogs, show them
|
||||||
if ((CSH->howManyPlayerInterfaces() <= 1 || makingTurn) && !dialogs.empty() && !showingDialog->isBusy())
|
if (makingTurn && !dialogs.empty() && !showingDialog->isBusy())
|
||||||
{
|
{
|
||||||
showingDialog->setBusy();
|
showingDialog->setBusy();
|
||||||
GH.windows().pushWindow(dialogs.front());
|
GH.windows().pushWindow(dialogs.front());
|
||||||
|
@ -197,6 +197,7 @@ public: // public interface for use by client via LOCPLINT access
|
|||||||
void performAutosave();
|
void performAutosave();
|
||||||
void gamePause(bool pause);
|
void gamePause(bool pause);
|
||||||
void endNetwork();
|
void endNetwork();
|
||||||
|
void closeAllDialogs();
|
||||||
|
|
||||||
///returns true if all events are processed internally
|
///returns true if all events are processed internally
|
||||||
bool capturedAllEvents();
|
bool capturedAllEvents();
|
||||||
|
@ -375,7 +375,7 @@ void AdventureMapInterface::onEnemyTurnStarted(PlayerColor playerID, bool isHuma
|
|||||||
mapAudio->onEnemyTurnStarted();
|
mapAudio->onEnemyTurnStarted();
|
||||||
widget->getMinimap()->setAIRadar(!isHuman);
|
widget->getMinimap()->setAIRadar(!isHuman);
|
||||||
widget->getInfoBar()->startEnemyTurn(playerID);
|
widget->getInfoBar()->startEnemyTurn(playerID);
|
||||||
setState(isHuman ? EAdventureState::OTHER_HUMAN_PLAYER_TURN : EAdventureState::AI_PLAYER_TURN);
|
setState(isHuman ? EAdventureState::MAKING_TURN : EAdventureState::AI_PLAYER_TURN);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdventureMapInterface::setState(EAdventureState state)
|
void AdventureMapInterface::setState(EAdventureState state)
|
||||||
|
@ -532,7 +532,7 @@ bool AdventureMapShortcuts::optionCanVisitObject()
|
|||||||
auto * hero = LOCPLINT->localState->getCurrentHero();
|
auto * hero = LOCPLINT->localState->getCurrentHero();
|
||||||
auto objects = LOCPLINT->cb->getVisitableObjs(hero->visitablePos());
|
auto objects = LOCPLINT->cb->getVisitableObjs(hero->visitablePos());
|
||||||
|
|
||||||
assert(vstd::contains(objects,hero));
|
//assert(vstd::contains(objects,hero));
|
||||||
return objects.size() > 1; // there is object other than our hero
|
return objects.size() > 1; // there is object other than our hero
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -577,16 +577,15 @@ bool AdventureMapShortcuts::optionInWorldView()
|
|||||||
|
|
||||||
bool AdventureMapShortcuts::optionSidePanelActive()
|
bool AdventureMapShortcuts::optionSidePanelActive()
|
||||||
{
|
{
|
||||||
return state == EAdventureState::MAKING_TURN || state == EAdventureState::WORLD_VIEW || state == EAdventureState::OTHER_HUMAN_PLAYER_TURN;
|
return state == EAdventureState::MAKING_TURN || state == EAdventureState::WORLD_VIEW;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AdventureMapShortcuts::optionMapScrollingActive()
|
bool AdventureMapShortcuts::optionMapScrollingActive()
|
||||||
{
|
{
|
||||||
return state == EAdventureState::MAKING_TURN || state == EAdventureState::WORLD_VIEW || state == EAdventureState::OTHER_HUMAN_PLAYER_TURN;
|
return state == EAdventureState::MAKING_TURN || state == EAdventureState::WORLD_VIEW;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AdventureMapShortcuts::optionMapViewActive()
|
bool AdventureMapShortcuts::optionMapViewActive()
|
||||||
{
|
{
|
||||||
return state == EAdventureState::MAKING_TURN || state == EAdventureState::WORLD_VIEW || state == EAdventureState::CASTING_SPELL
|
return state == EAdventureState::MAKING_TURN || state == EAdventureState::WORLD_VIEW || state == EAdventureState::CASTING_SPELL;
|
||||||
|| state == EAdventureState::OTHER_HUMAN_PLAYER_TURN;
|
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@ enum class EAdventureState
|
|||||||
HOTSEAT_WAIT,
|
HOTSEAT_WAIT,
|
||||||
MAKING_TURN,
|
MAKING_TURN,
|
||||||
AI_PLAYER_TURN,
|
AI_PLAYER_TURN,
|
||||||
OTHER_HUMAN_PLAYER_TURN,
|
|
||||||
CASTING_SPELL,
|
CASTING_SPELL,
|
||||||
WORLD_VIEW
|
WORLD_VIEW
|
||||||
};
|
};
|
||||||
|
@ -499,9 +499,12 @@ std::string BattleActionsController::actionGetStatusMessage(PossiblePlayerBattle
|
|||||||
case PossiblePlayerBattleAction::WALK_AND_ATTACK:
|
case PossiblePlayerBattleAction::WALK_AND_ATTACK:
|
||||||
case PossiblePlayerBattleAction::ATTACK_AND_RETURN: //TODO: allow to disable return
|
case PossiblePlayerBattleAction::ATTACK_AND_RETURN: //TODO: allow to disable return
|
||||||
{
|
{
|
||||||
|
const auto * attacker = owner.stacksController->getActiveStack();
|
||||||
BattleHex attackFromHex = owner.fieldController->fromWhichHexAttack(targetHex);
|
BattleHex attackFromHex = owner.fieldController->fromWhichHexAttack(targetHex);
|
||||||
|
int distance = attacker->position.isValid() ? owner.getBattle()->battleGetDistances(attacker, attacker->getPosition())[attackFromHex] : 0;
|
||||||
DamageEstimation retaliation;
|
DamageEstimation retaliation;
|
||||||
DamageEstimation estimation = owner.getBattle()->battleEstimateDamage(owner.stacksController->getActiveStack(), targetStack, attackFromHex, &retaliation);
|
BattleAttackInfo attackInfo(attacker, targetStack, distance, false );
|
||||||
|
DamageEstimation estimation = owner.getBattle()->battleEstimateDamage(attackInfo, &retaliation);
|
||||||
estimation.kills.max = std::min<int64_t>(estimation.kills.max, targetStack->getCount());
|
estimation.kills.max = std::min<int64_t>(estimation.kills.max, targetStack->getCount());
|
||||||
estimation.kills.min = std::min<int64_t>(estimation.kills.min, targetStack->getCount());
|
estimation.kills.min = std::min<int64_t>(estimation.kills.min, targetStack->getCount());
|
||||||
bool enemyMayBeKilled = estimation.kills.max == targetStack->getCount();
|
bool enemyMayBeKilled = estimation.kills.max == targetStack->getCount();
|
||||||
@ -514,7 +517,8 @@ std::string BattleActionsController::actionGetStatusMessage(PossiblePlayerBattle
|
|||||||
const auto * shooter = owner.stacksController->getActiveStack();
|
const auto * shooter = owner.stacksController->getActiveStack();
|
||||||
|
|
||||||
DamageEstimation retaliation;
|
DamageEstimation retaliation;
|
||||||
DamageEstimation estimation = owner.getBattle()->battleEstimateDamage(shooter, targetStack, shooter->getPosition(), &retaliation);
|
BattleAttackInfo attackInfo(shooter, targetStack, 0, true );
|
||||||
|
DamageEstimation estimation = owner.getBattle()->battleEstimateDamage(attackInfo, &retaliation);
|
||||||
estimation.kills.max = std::min<int64_t>(estimation.kills.max, targetStack->getCount());
|
estimation.kills.max = std::min<int64_t>(estimation.kills.max, targetStack->getCount());
|
||||||
estimation.kills.min = std::min<int64_t>(estimation.kills.min, targetStack->getCount());
|
estimation.kills.min = std::min<int64_t>(estimation.kills.min, targetStack->getCount());
|
||||||
return formatRangedAttack(estimation, targetStack->getName(), shooter->shots.available());
|
return formatRangedAttack(estimation, targetStack->getName(), shooter->shots.available());
|
||||||
|
@ -94,7 +94,7 @@ GlobalLobbyInviteWindow::GlobalLobbyInviteWindow()
|
|||||||
};
|
};
|
||||||
|
|
||||||
listBackground = std::make_shared<TransparentFilledRectangle>(Rect(8, 48, 220, 324), ColorRGBA(0, 0, 0, 64), ColorRGBA(64, 80, 128, 255), 1);
|
listBackground = std::make_shared<TransparentFilledRectangle>(Rect(8, 48, 220, 324), ColorRGBA(0, 0, 0, 64), ColorRGBA(64, 80, 128, 255), 1);
|
||||||
accountList = std::make_shared<CListBox>(createAccountCardCallback, Point(10, 50), Point(0, 40), 8, 0, 0, 1 | 4, Rect(200, 0, 320, 320));
|
accountList = std::make_shared<CListBox>(createAccountCardCallback, Point(10, 50), Point(0, 40), 8, CSH->getGlobalLobby().getActiveAccounts().size(), 0, 1 | 4, Rect(200, 0, 320, 320));
|
||||||
|
|
||||||
buttonClose = std::make_shared<CButton>(Point(86, 384), AnimationPath::builtin("MuBchck"), CButton::tooltip(), [this]() { close(); }, EShortcut::GLOBAL_RETURN );
|
buttonClose = std::make_shared<CButton>(Point(86, 384), AnimationPath::builtin("MuBchck"), CButton::tooltip(), [this]() { close(); }, EShortcut::GLOBAL_RETURN );
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ GlobalLobbyLoginWindow::GlobalLobbyLoginWindow()
|
|||||||
filledBackground = std::make_shared<FilledTexturePlayerColored>(ImagePath::builtin("DiBoxBck"), Rect(0, 0, pos.w, pos.h));
|
filledBackground = std::make_shared<FilledTexturePlayerColored>(ImagePath::builtin("DiBoxBck"), Rect(0, 0, pos.w, pos.h));
|
||||||
labelTitle = std::make_shared<CLabel>( pos.w / 2, 20, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->translate("vcmi.lobby.login.title"));
|
labelTitle = std::make_shared<CLabel>( pos.w / 2, 20, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->translate("vcmi.lobby.login.title"));
|
||||||
labelUsernameTitle = std::make_shared<CLabel>( 10, 65, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->translate("vcmi.lobby.login.username"));
|
labelUsernameTitle = std::make_shared<CLabel>( 10, 65, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->translate("vcmi.lobby.login.username"));
|
||||||
labelUsername = std::make_shared<CLabel>( 10, 65, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, loginAs.toString());
|
labelUsername = std::make_shared<CLabel>( 10, 65, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, loginAs.toString(), 265);
|
||||||
backgroundUsername = std::make_shared<TransparentFilledRectangle>(Rect(10, 90, 264, 20), ColorRGBA(0,0,0,128), ColorRGBA(64,64,64,64));
|
backgroundUsername = std::make_shared<TransparentFilledRectangle>(Rect(10, 90, 264, 20), ColorRGBA(0,0,0,128), ColorRGBA(64,64,64,64));
|
||||||
inputUsername = std::make_shared<CTextInput>(Rect(15, 93, 260, 16), FONT_SMALL, ETextAlignment::CENTERLEFT, true);
|
inputUsername = std::make_shared<CTextInput>(Rect(15, 93, 260, 16), FONT_SMALL, ETextAlignment::CENTERLEFT, true);
|
||||||
buttonLogin = std::make_shared<CButton>(Point(10, 180), AnimationPath::builtin("MuBchck"), CButton::tooltip(), [this](){ onLogin(); }, EShortcut::GLOBAL_ACCEPT);
|
buttonLogin = std::make_shared<CButton>(Point(10, 180), AnimationPath::builtin("MuBchck"), CButton::tooltip(), [this](){ onLogin(); }, EShortcut::GLOBAL_ACCEPT);
|
||||||
|
@ -37,7 +37,7 @@ GlobalLobbyRoomAccountCard::GlobalLobbyRoomAccountCard(const GlobalLobbyAccount
|
|||||||
pos.w = 130;
|
pos.w = 130;
|
||||||
pos.h = 40;
|
pos.h = 40;
|
||||||
backgroundOverlay = std::make_shared<TransparentFilledRectangle>(Rect(0, 0, pos.w, pos.h), ColorRGBA(0, 0, 0, 128), ColorRGBA(64, 64, 64, 64), 1);
|
backgroundOverlay = std::make_shared<TransparentFilledRectangle>(Rect(0, 0, pos.w, pos.h), ColorRGBA(0, 0, 0, 128), ColorRGBA(64, 64, 64, 64), 1);
|
||||||
labelName = std::make_shared<CLabel>(5, 10, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::WHITE, accountDescription.displayName);
|
labelName = std::make_shared<CLabel>(5, 10, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::WHITE, accountDescription.displayName, 120);
|
||||||
labelStatus = std::make_shared<CLabel>(5, 30, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::YELLOW, accountDescription.status);
|
labelStatus = std::make_shared<CLabel>(5, 30, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::YELLOW, accountDescription.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,9 +56,14 @@ GlobalLobbyRoomModCard::GlobalLobbyRoomModCard(const GlobalLobbyRoomModInfo & mo
|
|||||||
pos.h = 40;
|
pos.h = 40;
|
||||||
backgroundOverlay = std::make_shared<TransparentFilledRectangle>(Rect(0, 0, pos.w, pos.h), ColorRGBA(0, 0, 0, 128), ColorRGBA(64, 64, 64, 64), 1);
|
backgroundOverlay = std::make_shared<TransparentFilledRectangle>(Rect(0, 0, pos.w, pos.h), ColorRGBA(0, 0, 0, 128), ColorRGBA(64, 64, 64, 64), 1);
|
||||||
|
|
||||||
labelName = std::make_shared<CLabel>(5, 10, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::WHITE, modInfo.modName);
|
labelName = std::make_shared<CLabel>(5, 10, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::WHITE, modInfo.modName, 190);
|
||||||
labelVersion = std::make_shared<CLabel>(195, 30, FONT_SMALL, ETextAlignment::CENTERRIGHT, Colors::YELLOW, modInfo.version);
|
labelVersion = std::make_shared<CLabel>(195, 30, FONT_SMALL, ETextAlignment::CENTERRIGHT, Colors::YELLOW, modInfo.version);
|
||||||
labelStatus = std::make_shared<CLabel>(5, 30, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::YELLOW, CGI->generaltexth->translate("vcmi.lobby.mod.state." + statusToString.at(modInfo.status)));
|
auto statusColor = Colors::RED;
|
||||||
|
if(modInfo.status == ModVerificationStatus::FULL_MATCH)
|
||||||
|
statusColor = ColorRGBA(128, 128, 128);
|
||||||
|
else if(modInfo.status == ModVerificationStatus::VERSION_MISMATCH)
|
||||||
|
statusColor = Colors::YELLOW;
|
||||||
|
labelStatus = std::make_shared<CLabel>(5, 30, FONT_SMALL, ETextAlignment::CENTERLEFT, statusColor, CGI->generaltexth->translate("vcmi.lobby.mod.state." + statusToString.at(modInfo.status)));
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string getJoinRoomErrorMessage(const GlobalLobbyRoom & roomDescription, const std::vector<GlobalLobbyRoomModInfo> & modVerificationList)
|
static std::string getJoinRoomErrorMessage(const GlobalLobbyRoom & roomDescription, const std::vector<GlobalLobbyRoomModInfo> & modVerificationList)
|
||||||
@ -134,6 +139,13 @@ GlobalLobbyRoomWindow::GlobalLobbyRoomWindow(GlobalLobbyWindow * window, const s
|
|||||||
|
|
||||||
modVerificationList.push_back(modInfo);
|
modVerificationList.push_back(modInfo);
|
||||||
}
|
}
|
||||||
|
std::sort(modVerificationList.begin(), modVerificationList.end(), [](const GlobalLobbyRoomModInfo &a, const GlobalLobbyRoomModInfo &b)
|
||||||
|
{
|
||||||
|
if(a.status == b.status)
|
||||||
|
return a.modName < b.modName;
|
||||||
|
|
||||||
|
return a.status < b.status;
|
||||||
|
});
|
||||||
|
|
||||||
MetaString subtitleText;
|
MetaString subtitleText;
|
||||||
subtitleText.appendTextID("vcmi.lobby.preview.subtitle");
|
subtitleText.appendTextID("vcmi.lobby.preview.subtitle");
|
||||||
@ -142,7 +154,7 @@ GlobalLobbyRoomWindow::GlobalLobbyRoomWindow(GlobalLobbyWindow * window, const s
|
|||||||
|
|
||||||
filledBackground = std::make_shared<FilledTexturePlayerColored>(ImagePath::builtin("DiBoxBck"), Rect(0, 0, pos.w, pos.h));
|
filledBackground = std::make_shared<FilledTexturePlayerColored>(ImagePath::builtin("DiBoxBck"), Rect(0, 0, pos.w, pos.h));
|
||||||
labelTitle = std::make_shared<CLabel>( pos.w / 2, 20, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, MetaString::createFromTextID("vcmi.lobby.preview.title").toString());
|
labelTitle = std::make_shared<CLabel>( pos.w / 2, 20, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, MetaString::createFromTextID("vcmi.lobby.preview.title").toString());
|
||||||
labelSubtitle = std::make_shared<CLabel>( pos.w / 2, 40, FONT_MEDIUM, ETextAlignment::CENTER, Colors::YELLOW, subtitleText.toString());
|
labelSubtitle = std::make_shared<CLabel>( pos.w / 2, 40, FONT_MEDIUM, ETextAlignment::CENTER, Colors::YELLOW, subtitleText.toString(), 400);
|
||||||
|
|
||||||
labelVersionTitle = std::make_shared<CLabel>( 10, 60, FONT_MEDIUM, ETextAlignment::CENTERLEFT, Colors::YELLOW, MetaString::createFromTextID("vcmi.lobby.preview.version").toString());
|
labelVersionTitle = std::make_shared<CLabel>( 10, 60, FONT_MEDIUM, ETextAlignment::CENTERLEFT, Colors::YELLOW, MetaString::createFromTextID("vcmi.lobby.preview.version").toString());
|
||||||
labelVersionValue = std::make_shared<CLabel>( 10, 80, FONT_MEDIUM, ETextAlignment::CENTERLEFT, Colors::WHITE, roomDescription.gameVersion);
|
labelVersionValue = std::make_shared<CLabel>( 10, 80, FONT_MEDIUM, ETextAlignment::CENTERLEFT, Colors::WHITE, roomDescription.gameVersion);
|
||||||
|
@ -207,7 +207,7 @@ GlobalLobbyAccountCard::GlobalLobbyAccountCard(GlobalLobbyWindow * window, const
|
|||||||
: GlobalLobbyChannelCardBase(window, Point(130, 40), "player", accountDescription.accountID, accountDescription.displayName)
|
: GlobalLobbyChannelCardBase(window, Point(130, 40), "player", accountDescription.accountID, accountDescription.displayName)
|
||||||
{
|
{
|
||||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||||
labelName = std::make_shared<CLabel>(5, 10, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::WHITE, accountDescription.displayName);
|
labelName = std::make_shared<CLabel>(5, 10, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::WHITE, accountDescription.displayName, 120);
|
||||||
labelStatus = std::make_shared<CLabel>(5, 30, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::YELLOW, accountDescription.status);
|
labelStatus = std::make_shared<CLabel>(5, 30, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::YELLOW, accountDescription.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,8 +238,8 @@ GlobalLobbyRoomCard::GlobalLobbyRoomCard(GlobalLobbyWindow * window, const Globa
|
|||||||
else
|
else
|
||||||
backgroundOverlay = std::make_shared<TransparentFilledRectangle>(Rect(0, 0, pos.w, pos.h), ColorRGBA(0, 0, 0, 128), ColorRGBA(64, 64, 64, 64), 1);
|
backgroundOverlay = std::make_shared<TransparentFilledRectangle>(Rect(0, 0, pos.w, pos.h), ColorRGBA(0, 0, 0, 128), ColorRGBA(64, 64, 64, 64), 1);
|
||||||
|
|
||||||
labelName = std::make_shared<CLabel>(5, 10, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::WHITE, roomDescription.hostAccountDisplayName);
|
labelName = std::make_shared<CLabel>(5, 10, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::WHITE, roomDescription.hostAccountDisplayName, 180);
|
||||||
labelDescription = std::make_shared<CLabel>(5, 30, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::YELLOW, roomDescription.description);
|
labelDescription = std::make_shared<CLabel>(5, 30, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::YELLOW, roomDescription.description, 160);
|
||||||
labelRoomSize = std::make_shared<CLabel>(212, 10, FONT_SMALL, ETextAlignment::CENTERRIGHT, Colors::YELLOW, roomSizeText.toString());
|
labelRoomSize = std::make_shared<CLabel>(212, 10, FONT_SMALL, ETextAlignment::CENTERRIGHT, Colors::YELLOW, roomSizeText.toString());
|
||||||
labelRoomStatus = std::make_shared<CLabel>(225, 30, FONT_SMALL, ETextAlignment::CENTERRIGHT, Colors::YELLOW, roomStatusText.toString());
|
labelRoomStatus = std::make_shared<CLabel>(225, 30, FONT_SMALL, ETextAlignment::CENTERRIGHT, Colors::YELLOW, roomStatusText.toString());
|
||||||
iconRoomSize = std::make_shared<CPicture>(ImagePath::builtin("lobby/iconPlayer"), Point(214, 5));
|
iconRoomSize = std::make_shared<CPicture>(ImagePath::builtin("lobby/iconPlayer"), Point(214, 5));
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "../eventsSDL/InputHandler.h"
|
#include "../eventsSDL/InputHandler.h"
|
||||||
|
|
||||||
#include "../CGameInfo.h"
|
#include "../CGameInfo.h"
|
||||||
|
#include "../adventureMap/AdventureMapInterface.h"
|
||||||
#include "../render/Colors.h"
|
#include "../render/Colors.h"
|
||||||
#include "../render/Graphics.h"
|
#include "../render/Graphics.h"
|
||||||
#include "../render/IFont.h"
|
#include "../render/IFont.h"
|
||||||
@ -145,7 +146,13 @@ CGuiHandler::CGuiHandler()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
CGuiHandler::~CGuiHandler() = default;
|
CGuiHandler::~CGuiHandler()
|
||||||
|
{
|
||||||
|
// enforce deletion order on shutdown
|
||||||
|
// all UI elements including adventure map must be destroyed before Gui Handler
|
||||||
|
// proper solution would be removal of adventureInt global
|
||||||
|
adventureInt.reset();
|
||||||
|
}
|
||||||
|
|
||||||
ShortcutHandler & CGuiHandler::shortcuts()
|
ShortcutHandler & CGuiHandler::shortcuts()
|
||||||
{
|
{
|
||||||
|
@ -345,7 +345,8 @@ std::shared_ptr<CLabel> InterfaceObjectConfigurable::buildLabel(const JsonNode &
|
|||||||
auto color = readColor(config["color"]);
|
auto color = readColor(config["color"]);
|
||||||
auto text = readText(config["text"]);
|
auto text = readText(config["text"]);
|
||||||
auto position = readPosition(config["position"]);
|
auto position = readPosition(config["position"]);
|
||||||
return std::make_shared<CLabel>(position.x, position.y, font, alignment, color, text);
|
auto maxWidth = config["maxWidth"].Integer();
|
||||||
|
return std::make_shared<CLabel>(position.x, position.y, font, alignment, color, text, maxWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<CMultiLineLabel> InterfaceObjectConfigurable::buildMultiLineLabel(const JsonNode & config) const
|
std::shared_ptr<CMultiLineLabel> InterfaceObjectConfigurable::buildMultiLineLabel(const JsonNode & config) const
|
||||||
|
@ -340,10 +340,10 @@ void OptionsTabBase::recreate(bool campaign)
|
|||||||
|
|
||||||
//Simultaneous turns
|
//Simultaneous turns
|
||||||
if(auto turnSlider = widget<CSlider>("simturnsDurationMin"))
|
if(auto turnSlider = widget<CSlider>("simturnsDurationMin"))
|
||||||
turnSlider->setValue(SEL->getStartInfo()->simturnsInfo.requiredTurns);
|
turnSlider->setValue(SEL->getStartInfo()->simturnsInfo.requiredTurns, false);
|
||||||
|
|
||||||
if(auto turnSlider = widget<CSlider>("simturnsDurationMax"))
|
if(auto turnSlider = widget<CSlider>("simturnsDurationMax"))
|
||||||
turnSlider->setValue(SEL->getStartInfo()->simturnsInfo.optionalTurns);
|
turnSlider->setValue(SEL->getStartInfo()->simturnsInfo.optionalTurns, false);
|
||||||
|
|
||||||
if(auto w = widget<CLabel>("labelSimturnsDurationValueMin"))
|
if(auto w = widget<CLabel>("labelSimturnsDurationValueMin"))
|
||||||
w->setText(generateSimturnsDurationText(SEL->getStartInfo()->simturnsInfo.requiredTurns));
|
w->setText(generateSimturnsDurationText(SEL->getStartInfo()->simturnsInfo.requiredTurns));
|
||||||
@ -388,7 +388,7 @@ void OptionsTabBase::recreate(bool campaign)
|
|||||||
auto & tpreset = variables["timerPresets"].Vector()[idx];
|
auto & tpreset = variables["timerPresets"].Vector()[idx];
|
||||||
if(tpreset.Vector().at(1).Integer() == turnTimerRemote.turnTimer / 1000)
|
if(tpreset.Vector().at(1).Integer() == turnTimerRemote.turnTimer / 1000)
|
||||||
{
|
{
|
||||||
turnSlider->scrollTo(idx);
|
turnSlider->scrollTo(idx, false);
|
||||||
if(auto w = widget<CLabel>("labelTurnDurationValue"))
|
if(auto w = widget<CLabel>("labelTurnDurationValue"))
|
||||||
w->setText(CGI->generaltexth->turnDurations[idx]);
|
w->setText(CGI->generaltexth->turnDurations[idx]);
|
||||||
}
|
}
|
||||||
|
@ -293,7 +293,10 @@ std::string CComponent::getSubtitle() const
|
|||||||
return CGI->artifacts()->getById(data.subType.as<ArtifactID>())->getNameTranslated();
|
return CGI->artifacts()->getById(data.subType.as<ArtifactID>())->getNameTranslated();
|
||||||
case ComponentType::SPELL_SCROLL:
|
case ComponentType::SPELL_SCROLL:
|
||||||
case ComponentType::SPELL:
|
case ComponentType::SPELL:
|
||||||
return CGI->spells()->getById(data.subType.as<SpellID>())->getNameTranslated();
|
if (data.value < 0)
|
||||||
|
return "{#A9A9A9|" + CGI->spells()->getById(data.subType.as<SpellID>())->getNameTranslated() + "}";
|
||||||
|
else
|
||||||
|
return CGI->spells()->getById(data.subType.as<SpellID>())->getNameTranslated();
|
||||||
case ComponentType::NONE:
|
case ComponentType::NONE:
|
||||||
case ComponentType::MORALE:
|
case ComponentType::MORALE:
|
||||||
case ComponentType::LUCK:
|
case ComponentType::LUCK:
|
||||||
|
@ -70,7 +70,7 @@ int CSlider::getValue() const
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSlider::setValue(int to)
|
void CSlider::setValue(int to, bool callCallbacks)
|
||||||
{
|
{
|
||||||
scrollTo(value);
|
scrollTo(value);
|
||||||
}
|
}
|
||||||
@ -113,7 +113,7 @@ void CSlider::updateSliderPos()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSlider::scrollTo(int to)
|
void CSlider::scrollTo(int to, bool callCallbacks)
|
||||||
{
|
{
|
||||||
vstd::amax(to, 0);
|
vstd::amax(to, 0);
|
||||||
vstd::amin(to, positions);
|
vstd::amin(to, positions);
|
||||||
@ -125,7 +125,8 @@ void CSlider::scrollTo(int to)
|
|||||||
|
|
||||||
updateSliderPos();
|
updateSliderPos();
|
||||||
|
|
||||||
moved(getValue());
|
if (callCallbacks)
|
||||||
|
moved(getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSlider::clickPressed(const Point & cursorPosition)
|
void CSlider::clickPressed(const Point & cursorPosition)
|
||||||
@ -321,7 +322,7 @@ int SliderNonlinear::getValue() const
|
|||||||
return scaledValues.at(CSlider::getValue());
|
return scaledValues.at(CSlider::getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SliderNonlinear::setValue(int to)
|
void SliderNonlinear::setValue(int to, bool callCallbacks)
|
||||||
{
|
{
|
||||||
size_t nearest = 0;
|
size_t nearest = 0;
|
||||||
|
|
||||||
@ -334,5 +335,5 @@ void SliderNonlinear::setValue(int to)
|
|||||||
nearest = i;
|
nearest = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollTo(nearest);
|
scrollTo(nearest, callCallbacks);
|
||||||
}
|
}
|
||||||
|
@ -52,14 +52,14 @@ public:
|
|||||||
void clearScrollBounds();
|
void clearScrollBounds();
|
||||||
|
|
||||||
/// Value modifiers
|
/// Value modifiers
|
||||||
void scrollTo(int value);
|
void scrollTo(int value, bool callCallbacks = true);
|
||||||
void scrollBy(int amount) override;
|
void scrollBy(int amount) override;
|
||||||
void scrollToMin();
|
void scrollToMin();
|
||||||
void scrollToMax();
|
void scrollToMax();
|
||||||
|
|
||||||
/// Amount modifier
|
/// Amount modifier
|
||||||
void setAmount(int to);
|
void setAmount(int to);
|
||||||
virtual void setValue(int to);
|
virtual void setValue(int to, bool callCallbacks = true);
|
||||||
|
|
||||||
/// Accessors
|
/// Accessors
|
||||||
int getAmount() const;
|
int getAmount() const;
|
||||||
@ -95,7 +95,7 @@ class SliderNonlinear : public CSlider
|
|||||||
|
|
||||||
using CSlider::setAmount; // make private
|
using CSlider::setAmount; // make private
|
||||||
public:
|
public:
|
||||||
void setValue(int to) override;
|
void setValue(int to, bool callCallbacks) override;
|
||||||
int getValue() const override;
|
int getValue() const override;
|
||||||
|
|
||||||
SliderNonlinear(Point position, int length, const std::function<void(int)> & Moved, const std::vector<int> & values, int Value, Orientation orientation, EStyle style);
|
SliderNonlinear(Point position, int length, const std::function<void(int)> & Moved, const std::vector<int> & values, int Value, Orientation orientation, EStyle style);
|
||||||
|
@ -155,18 +155,21 @@
|
|||||||
"types" : {
|
"types" : {
|
||||||
"boatNecropolis" : {
|
"boatNecropolis" : {
|
||||||
"index" : 0,
|
"index" : 0,
|
||||||
|
"compatibilityIdentifiers" : [ "evil" ],
|
||||||
"actualAnimation" : "AB01_.def",
|
"actualAnimation" : "AB01_.def",
|
||||||
"overlayAnimation" : "ABM01_.def",
|
"overlayAnimation" : "ABM01_.def",
|
||||||
"flagAnimations" : ["ABF01L", "ABF01G", "ABF01R", "ABF01D", "ABF01B", "ABF01P", "ABF01W", "ABF01K"]
|
"flagAnimations" : ["ABF01L", "ABF01G", "ABF01R", "ABF01D", "ABF01B", "ABF01P", "ABF01W", "ABF01K"]
|
||||||
},
|
},
|
||||||
"boatCastle" : {
|
"boatCastle" : {
|
||||||
"index" : 1,
|
"index" : 1,
|
||||||
|
"compatibilityIdentifiers" : [ "good" ],
|
||||||
"actualAnimation" : "AB02_.def",
|
"actualAnimation" : "AB02_.def",
|
||||||
"overlayAnimation" : "ABM02_.def",
|
"overlayAnimation" : "ABM02_.def",
|
||||||
"flagAnimations" : ["ABF02L", "ABF02G", "ABF02R", "ABF02D", "ABF02B", "ABF02P", "ABF02W", "ABF02K"]
|
"flagAnimations" : ["ABF02L", "ABF02G", "ABF02R", "ABF02D", "ABF02B", "ABF02P", "ABF02W", "ABF02K"]
|
||||||
},
|
},
|
||||||
"boatFortress" : {
|
"boatFortress" : {
|
||||||
"index" : 2,
|
"index" : 2,
|
||||||
|
"compatibilityIdentifiers" : [ "neutral" ],
|
||||||
"actualAnimation" : "AB03_.def",
|
"actualAnimation" : "AB03_.def",
|
||||||
"overlayAnimation" : "ABM03_.def",
|
"overlayAnimation" : "ABM03_.def",
|
||||||
"flagAnimations" : ["ABF03L", "ABF03G", "ABF03R", "ABF03D", "ABF03B", "ABF03P", "ABF03W", "ABF03K"]
|
"flagAnimations" : ["ABF03L", "ABF03G", "ABF03R", "ABF03D", "ABF03B", "ABF03P", "ABF03W", "ABF03K"]
|
||||||
|
@ -43,7 +43,8 @@
|
|||||||
{
|
{
|
||||||
"name" : "accountNameLabel",
|
"name" : "accountNameLabel",
|
||||||
"type": "labelTitleMain",
|
"type": "labelTitleMain",
|
||||||
"position": {"x": 15, "y": 10}
|
"position": {"x": 15, "y": 10},
|
||||||
|
"maxWidth": 230
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
|
6
debian/changelog
vendored
6
debian/changelog
vendored
@ -4,6 +4,12 @@ vcmi (1.6.0) jammy; urgency=medium
|
|||||||
|
|
||||||
-- Ivan Savenko <saven.ivan@gmail.com> Fri, 30 Aug 2024 12:00:00 +0200
|
-- Ivan Savenko <saven.ivan@gmail.com> Fri, 30 Aug 2024 12:00:00 +0200
|
||||||
|
|
||||||
|
vcmi (1.5.6) jammy; urgency=medium
|
||||||
|
|
||||||
|
* New upstream release
|
||||||
|
|
||||||
|
-- Ivan Savenko <saven.ivan@gmail.com> Sun, 4 Aug 2024 12:00:00 +0200
|
||||||
|
|
||||||
vcmi (1.5.5) jammy; urgency=medium
|
vcmi (1.5.5) jammy; urgency=medium
|
||||||
|
|
||||||
* New upstream release
|
* New upstream release
|
||||||
|
@ -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)
|
[![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.0/total)](https://github.com/vcmi/vcmi/releases/tag/1.5.0)
|
||||||
[![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.4/total)](https://github.com/vcmi/vcmi/releases/tag/1.5.5)
|
||||||
[![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/1.5.5/total)](https://github.com/vcmi/vcmi/releases/tag/1.5.6)
|
||||||
[![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/total)](https://github.com/vcmi/vcmi/releases)
|
[![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/total)](https://github.com/vcmi/vcmi/releases)
|
||||||
|
|
||||||
# VCMI Project
|
# VCMI Project
|
||||||
|
@ -463,7 +463,9 @@ Configurable object has following structure:
|
|||||||
|
|
||||||
`"text"`: [text](#text),
|
`"text"`: [text](#text),
|
||||||
|
|
||||||
`"position"`: [position](#position)
|
`"position"`: [position](#position),
|
||||||
|
|
||||||
|
`"maxWidth"`: int` optional, trim longer text
|
||||||
|
|
||||||
#### [VCMI-1.4] Multi-line label
|
#### [VCMI-1.4] Multi-line label
|
||||||
|
|
||||||
|
@ -91,6 +91,7 @@
|
|||||||
<launchable type="desktop-id">vcmilauncher.desktop</launchable>
|
<launchable type="desktop-id">vcmilauncher.desktop</launchable>
|
||||||
<releases>
|
<releases>
|
||||||
<release version="1.6.0" date="2024-08-30" type="development"/>
|
<release version="1.6.0" date="2024-08-30" type="development"/>
|
||||||
|
<release version="1.5.6" date="2024-08-04" type="stable"/>
|
||||||
<release version="1.5.5" date="2024-07-17" type="stable"/>
|
<release version="1.5.5" date="2024-07-17" type="stable"/>
|
||||||
<release version="1.5.4" date="2024-07-12" type="stable"/>
|
<release version="1.5.4" date="2024-07-12" type="stable"/>
|
||||||
<release version="1.5.3" date="2024-06-21" type="stable"/>
|
<release version="1.5.3" date="2024-06-21" type="stable"/>
|
||||||
|
@ -755,15 +755,15 @@ DamageEstimation CBattleInfoCallback::battleEstimateDamage(const battle::Unit *
|
|||||||
{
|
{
|
||||||
RETURN_IF_NOT_BATTLE({});
|
RETURN_IF_NOT_BATTLE({});
|
||||||
auto reachability = battleGetDistances(attacker, attacker->getPosition());
|
auto reachability = battleGetDistances(attacker, attacker->getPosition());
|
||||||
int getMovementRange = attackerPosition.isValid() ? reachability[attackerPosition] : 0;
|
int movementRange = attackerPosition.isValid() ? reachability[attackerPosition] : 0;
|
||||||
return battleEstimateDamage(attacker, defender, getMovementRange, retaliationDmg);
|
return battleEstimateDamage(attacker, defender, movementRange, retaliationDmg);
|
||||||
}
|
}
|
||||||
|
|
||||||
DamageEstimation CBattleInfoCallback::battleEstimateDamage(const battle::Unit * attacker, const battle::Unit * defender, int getMovementRange, DamageEstimation * retaliationDmg) const
|
DamageEstimation CBattleInfoCallback::battleEstimateDamage(const battle::Unit * attacker, const battle::Unit * defender, int movementRange, DamageEstimation * retaliationDmg) const
|
||||||
{
|
{
|
||||||
RETURN_IF_NOT_BATTLE({});
|
RETURN_IF_NOT_BATTLE({});
|
||||||
const bool shooting = battleCanShoot(attacker, defender->getPosition());
|
const bool shooting = battleCanShoot(attacker, defender->getPosition());
|
||||||
const BattleAttackInfo bai(attacker, defender, getMovementRange, shooting);
|
const BattleAttackInfo bai(attacker, defender, movementRange, shooting);
|
||||||
return battleEstimateDamage(bai, retaliationDmg);
|
return battleEstimateDamage(bai, retaliationDmg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,18 +32,14 @@ public:
|
|||||||
int32_t getNum() const
|
int32_t getNum() const
|
||||||
{
|
{
|
||||||
int32_t result;
|
int32_t result;
|
||||||
|
|
||||||
std::visit([&result] (const auto& v) { result = v.getNum(); }, value);
|
std::visit([&result] (const auto& v) { result = v.getNum(); }, value);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string toString() const
|
std::string toString() const
|
||||||
{
|
{
|
||||||
std::string result;
|
std::string result;
|
||||||
|
|
||||||
std::visit([&result] (const auto& v) { result = v.encode(v.getNum()); }, value);
|
std::visit([&result] (const auto& v) { result = v.encode(v.getNum()); }, value);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,6 +54,13 @@ public:
|
|||||||
return IdentifierType();
|
return IdentifierType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool hasValue() const
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
std::visit([&result] (const auto& v) { result = v.hasValue(); }, value);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h)
|
template <typename Handler> void serialize(Handler &h)
|
||||||
{
|
{
|
||||||
h & value;
|
h & value;
|
||||||
|
@ -133,13 +133,15 @@ void CGameStateCampaign::trimCrossoverHeroesParameters(const CampaignTravel & tr
|
|||||||
if(!art)
|
if(!art)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
bool takeable = travelOptions.artifactsKeptByHero.count(art->artType->getId());
|
ArtifactLocation al(hero.hero->id, artifactPosition);
|
||||||
|
|
||||||
if (takeable)
|
bool takeable = travelOptions.artifactsKeptByHero.count(art->artType->getId());
|
||||||
|
bool locked = hero.hero->getSlot(al.slot)->locked;
|
||||||
|
|
||||||
|
if (!locked && takeable)
|
||||||
hero.transferrableArtifacts.push_back(artifactPosition);
|
hero.transferrableArtifacts.push_back(artifactPosition);
|
||||||
|
|
||||||
ArtifactLocation al(hero.hero->id, artifactPosition);
|
if (!locked && !takeable)
|
||||||
if(!takeable && !hero.hero->getSlot(al.slot)->locked) //don't try removing locked artifacts -> it crashes #1719
|
|
||||||
{
|
{
|
||||||
hero.hero->getArt(al.slot)->removeFrom(*hero.hero, al.slot);
|
hero.hero->getArt(al.slot)->removeFrom(*hero.hero, al.slot);
|
||||||
return true;
|
return true;
|
||||||
|
@ -348,15 +348,19 @@ void CGDwelling::newTurn(vstd::RNG & rand) const
|
|||||||
|
|
||||||
std::vector<Component> CGDwelling::getPopupComponents(PlayerColor player) const
|
std::vector<Component> CGDwelling::getPopupComponents(PlayerColor player) const
|
||||||
{
|
{
|
||||||
if (getOwner() != player)
|
bool visitedByOwner = getOwner() == player;
|
||||||
return {};
|
|
||||||
|
|
||||||
std::vector<Component> result;
|
std::vector<Component> result;
|
||||||
|
|
||||||
if (ID == Obj::CREATURE_GENERATOR1 && !creatures.empty())
|
if (ID == Obj::CREATURE_GENERATOR1 && !creatures.empty())
|
||||||
{
|
{
|
||||||
for (auto const & creature : creatures.front().second)
|
for (auto const & creature : creatures.front().second)
|
||||||
result.emplace_back(ComponentType::CREATURE, creature, creatures.front().first);
|
{
|
||||||
|
if (visitedByOwner)
|
||||||
|
result.emplace_back(ComponentType::CREATURE, creature, creatures.front().first);
|
||||||
|
else
|
||||||
|
result.emplace_back(ComponentType::CREATURE, creature);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ID == Obj::CREATURE_GENERATOR4)
|
if (ID == Obj::CREATURE_GENERATOR4)
|
||||||
@ -364,7 +368,12 @@ std::vector<Component> CGDwelling::getPopupComponents(PlayerColor player) const
|
|||||||
for (auto const & creatureLevel : creatures)
|
for (auto const & creatureLevel : creatures)
|
||||||
{
|
{
|
||||||
if (!creatureLevel.second.empty())
|
if (!creatureLevel.second.empty())
|
||||||
result.emplace_back(ComponentType::CREATURE, creatureLevel.second.back(), creatureLevel.first);
|
{
|
||||||
|
if (visitedByOwner)
|
||||||
|
result.emplace_back(ComponentType::CREATURE, creatureLevel.second.back(), creatureLevel.first);
|
||||||
|
else
|
||||||
|
result.emplace_back(ComponentType::CREATURE, creatureLevel.second.back());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@ -426,7 +435,7 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h) const
|
|||||||
if(count) //there are available creatures
|
if(count) //there are available creatures
|
||||||
{
|
{
|
||||||
|
|
||||||
if (VLC->settings()->getBoolean(EGameSettings::DWELLINGS_ACCUMULATE_WHEN_OWNED))
|
if (VLC->settings()->getBoolean(EGameSettings::DWELLINGS_MERGE_ON_RECRUIT))
|
||||||
{
|
{
|
||||||
SlotID testSlot = h->getSlotFor(crid);
|
SlotID testSlot = h->getSlotFor(crid);
|
||||||
if(!testSlot.validSlot()) //no available slot - try merging army of visiting hero
|
if(!testSlot.validSlot()) //no available slot - try merging army of visiting hero
|
||||||
|
@ -1704,6 +1704,16 @@ void CGHeroInstance::serializeJsonOptions(JsonSerializeFormat & handler)
|
|||||||
setHeroTypeName(typeName);
|
setHeroTypeName(typeName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!handler.saving)
|
||||||
|
{
|
||||||
|
if(!appearance)
|
||||||
|
{
|
||||||
|
// crossoverDeserialize
|
||||||
|
type = getHeroType().toHeroType();
|
||||||
|
appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, type->heroClass->getIndex())->getTemplates().front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CArmedInstance::serializeJsonOptions(handler);
|
CArmedInstance::serializeJsonOptions(handler);
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -1719,13 +1729,6 @@ void CGHeroInstance::serializeJsonOptions(JsonSerializeFormat & handler)
|
|||||||
|
|
||||||
if(!handler.saving)
|
if(!handler.saving)
|
||||||
{
|
{
|
||||||
if(!appearance)
|
|
||||||
{
|
|
||||||
// crossoverDeserialize
|
|
||||||
type = getHeroType().toHeroType();
|
|
||||||
appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, type->heroClass->getIndex())->getTemplates().front();
|
|
||||||
}
|
|
||||||
|
|
||||||
patrol.patrolling = (rawPatrolRadius > NO_PATROLING);
|
patrol.patrolling = (rawPatrolRadius > NO_PATROLING);
|
||||||
patrol.initialPos = visitablePos();
|
patrol.initialPos = visitablePos();
|
||||||
patrol.patrolRadius = (rawPatrolRadius > NO_PATROLING) ? rawPatrolRadius : 0;
|
patrol.patrolRadius = (rawPatrolRadius > NO_PATROLING) ? rawPatrolRadius : 0;
|
||||||
|
@ -184,7 +184,26 @@ void CRewardableObject::heroLevelUpDone(const CGHeroInstance *hero) const
|
|||||||
void CRewardableObject::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
|
void CRewardableObject::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
|
||||||
{
|
{
|
||||||
if(answer == 0)
|
if(answer == 0)
|
||||||
|
{
|
||||||
|
switch (configuration.visitMode)
|
||||||
|
{
|
||||||
|
case Rewardable::VISIT_UNLIMITED:
|
||||||
|
case Rewardable::VISIT_BONUS:
|
||||||
|
case Rewardable::VISIT_HERO:
|
||||||
|
case Rewardable::VISIT_LIMITER:
|
||||||
|
{
|
||||||
|
// workaround for object with refusable reward not getting marked as visited
|
||||||
|
// TODO: better solution that would also work for player-visitable objects
|
||||||
|
if (!wasScouted(hero->getOwner()))
|
||||||
|
{
|
||||||
|
ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_TEAM, id, hero->id);
|
||||||
|
cb->sendAndApply(&cov);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return; // player refused
|
return; // player refused
|
||||||
|
}
|
||||||
|
|
||||||
if(answer > 0 && answer-1 < configuration.info.size())
|
if(answer > 0 && answer-1 < configuration.info.size())
|
||||||
{
|
{
|
||||||
|
@ -115,8 +115,10 @@ void Rewardable::Reward::loadComponents(std::vector<Component> & comps, const CG
|
|||||||
comps.emplace_back(ComponentType::ARTIFACT, entry);
|
comps.emplace_back(ComponentType::ARTIFACT, entry);
|
||||||
|
|
||||||
for(const auto & entry : spells)
|
for(const auto & entry : spells)
|
||||||
if (!h || h->canLearnSpell(entry.toEntity(VLC), true))
|
{
|
||||||
comps.emplace_back(ComponentType::SPELL, entry);
|
bool learnable = !h || h->canLearnSpell(entry.toEntity(VLC), true);
|
||||||
|
comps.emplace_back(ComponentType::SPELL, entry, learnable ? 0 : -1);
|
||||||
|
}
|
||||||
|
|
||||||
for(const auto & entry : creatures)
|
for(const auto & entry : creatures)
|
||||||
comps.emplace_back(ComponentType::CREATURE, entry.type->getId(), entry.count);
|
comps.emplace_back(ComponentType::CREATURE, entry.type->getId(), entry.count);
|
||||||
|
@ -694,6 +694,15 @@ void CGameHandler::onNewTurn()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto & player : gs->players)
|
||||||
|
{
|
||||||
|
if (player.second.status != EPlayerStatus::INGAME)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (player.second.heroes.empty() && player.second.towns.empty())
|
||||||
|
throw std::runtime_error("Invalid player in player state! Player " + std::to_string(player.first.getNum()) + ", map name: " + gs->map->name.toString() + ", map description: " + gs->map->description.toString());
|
||||||
|
}
|
||||||
|
|
||||||
if (newWeek && !firstTurn)
|
if (newWeek && !firstTurn)
|
||||||
{
|
{
|
||||||
n.specialWeek = NewTurn::NORMAL;
|
n.specialWeek = NewTurn::NORMAL;
|
||||||
|
@ -496,7 +496,7 @@ bool BattleActionProcessor::doHealAction(const CBattleInfoCallback & battle, con
|
|||||||
else
|
else
|
||||||
destStack = battle.battleGetUnitByPos(target.at(0).hexValue);
|
destStack = battle.battleGetUnitByPos(target.at(0).hexValue);
|
||||||
|
|
||||||
if(stack == nullptr || destStack == nullptr || !healerAbility || healerAbility->subtype == BonusSubtypeID())
|
if(stack == nullptr || destStack == nullptr || !healerAbility || !healerAbility->subtype.hasValue())
|
||||||
{
|
{
|
||||||
gameHandler->complain("There is either no healer, no destination, or healer cannot heal :P");
|
gameHandler->complain("There is either no healer, no destination, or healer cannot heal :P");
|
||||||
}
|
}
|
||||||
@ -973,7 +973,7 @@ void BattleActionProcessor::makeAttack(const CBattleInfoCallback & battle, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<const Bonus> bonus = attacker->getFirstBonus(Selector::type()(BonusType::SPELL_LIKE_ATTACK));
|
std::shared_ptr<const Bonus> bonus = attacker->getFirstBonus(Selector::type()(BonusType::SPELL_LIKE_ATTACK));
|
||||||
if(bonus && ranged) //TODO: make it work in melee?
|
if(bonus && ranged && bonus->subtype.hasValue()) //TODO: make it work in melee?
|
||||||
{
|
{
|
||||||
//this is need for displaying hit animation
|
//this is need for displaying hit animation
|
||||||
bat.flags |= BattleAttack::SPELL_LIKE;
|
bat.flags |= BattleAttack::SPELL_LIKE;
|
||||||
|
Loading…
Reference in New Issue
Block a user