mirror of
https://github.com/vcmi/vcmi.git
synced 2025-11-23 22:37:55 +02:00
Merge branch 'develop' into feature/nullkiller2
This commit is contained in:
@@ -52,6 +52,7 @@ set(vcmiclientcommon_SRCS
|
||||
gui/ShortcutHandler.cpp
|
||||
gui/WindowHandler.cpp
|
||||
|
||||
lobby/BattleOnlyMode.cpp
|
||||
lobby/CBonusSelection.cpp
|
||||
lobby/CCampaignInfoScreen.cpp
|
||||
lobby/CLobbyScreen.cpp
|
||||
@@ -262,6 +263,7 @@ set(vcmiclientcommon_HEADERS
|
||||
gui/TextAlignment.h
|
||||
gui/WindowHandler.h
|
||||
|
||||
lobby/BattleOnlyMode.h
|
||||
lobby/CBonusSelection.h
|
||||
lobby/CCampaignInfoScreen.h
|
||||
lobby/CLobbyScreen.h
|
||||
|
||||
@@ -97,6 +97,7 @@
|
||||
#include "../lib/mapObjects/MiscObjects.h"
|
||||
#include "../lib/mapObjects/ObjectTemplate.h"
|
||||
|
||||
#include "../lib/mapping/CMap.h"
|
||||
#include "../lib/mapping/CMapHeader.h"
|
||||
|
||||
#include "../lib/networkPacks/PacksForClient.h"
|
||||
@@ -658,7 +659,7 @@ void CPlayerInterface::battleStart(const BattleID & battleID, const CCreatureSet
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
|
||||
bool useQuickCombat = settings["adventure"]["quickCombat"].Bool();
|
||||
bool useQuickCombat = settings["adventure"]["quickCombat"].Bool() || GAME->map().getMap()->battleOnly;
|
||||
bool forceQuickCombat = settings["adventure"]["forceQuickCombat"].Bool();
|
||||
|
||||
if ((replayAllowed && useQuickCombat) || forceQuickCombat)
|
||||
@@ -1482,7 +1483,7 @@ void CPlayerInterface::playerBlocked(int reason, bool start)
|
||||
{
|
||||
if(reason == PlayerBlocked::EReason::UPCOMING_BATTLE)
|
||||
{
|
||||
if(GAME->server().howManyPlayerInterfaces() > 1 && GAME->interface() != this && GAME->interface()->makingTurn == false)
|
||||
if(GAME->server().howManyPlayerInterfaces() > 1 && GAME->interface() != this && GAME->interface()->makingTurn == false && !GAME->map().getMap()->battleOnly)
|
||||
{
|
||||
//one of our players who isn't last in order got attacked not by our another player (happens for example in hotseat mode)
|
||||
GAME->setInterfaceInstance(this);
|
||||
|
||||
@@ -432,6 +432,13 @@ void CServerHandler::setCampaignBonus(int bonusId) const
|
||||
sendLobbyPack(lscb);
|
||||
}
|
||||
|
||||
void CServerHandler::setBattleOnlyModeStartInfo(std::shared_ptr<BattleOnlyModeStartInfo> startInfo) const
|
||||
{
|
||||
LobbySetBattleOnlyModeStartInfo lsbomsui;
|
||||
lsbomsui.startInfo = startInfo;
|
||||
sendLobbyPack(lsbomsui);
|
||||
}
|
||||
|
||||
void CServerHandler::setMapInfo(std::shared_ptr<CMapInfo> to, std::shared_ptr<CMapGenOptions> mapGenOpts) const
|
||||
{
|
||||
LobbySetMap lsm;
|
||||
|
||||
@@ -77,6 +77,7 @@ public:
|
||||
virtual void setCampaignState(std::shared_ptr<CampaignState> newCampaign) = 0;
|
||||
virtual void setCampaignMap(CampaignScenarioID mapId) const = 0;
|
||||
virtual void setCampaignBonus(int bonusId) const = 0;
|
||||
virtual void setBattleOnlyModeStartInfo(std::shared_ptr<BattleOnlyModeStartInfo> startInfo) const = 0;
|
||||
virtual void setMapInfo(std::shared_ptr<CMapInfo> to, std::shared_ptr<CMapGenOptions> mapGenOpts = {}) const = 0;
|
||||
virtual void setPlayer(PlayerColor color) const = 0;
|
||||
virtual void setPlayerName(PlayerColor color, const std::string & name) const = 0;
|
||||
@@ -186,6 +187,7 @@ public:
|
||||
void setCampaignState(std::shared_ptr<CampaignState> newCampaign) override;
|
||||
void setCampaignMap(CampaignScenarioID mapId) const override;
|
||||
void setCampaignBonus(int bonusId) const override;
|
||||
void setBattleOnlyModeStartInfo(std::shared_ptr<BattleOnlyModeStartInfo> startInfo) const override;
|
||||
void setMapInfo(std::shared_ptr<CMapInfo> to, std::shared_ptr<CMapGenOptions> mapGenOpts = {}) const override;
|
||||
void setPlayer(PlayerColor color) const override;
|
||||
void setPlayerName(PlayerColor color, const std::string & name) const override;
|
||||
|
||||
@@ -59,4 +59,5 @@ public:
|
||||
void visitLobbyLoadProgress(LobbyLoadProgress & pack) override;
|
||||
void visitLobbyUpdateState(LobbyUpdateState & pack) override;
|
||||
void visitLobbyShowMessage(LobbyShowMessage & pack) override;
|
||||
void visitLobbySetBattleOnlyModeStartInfo(LobbySetBattleOnlyModeStartInfo & pack) override;
|
||||
};
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "windows/GUIClasses.h"
|
||||
#include "windows/CCastleInterface.h"
|
||||
#include "mapView/mapHandler.h"
|
||||
#include "mainmenu/CMainMenu.h"
|
||||
#include "adventureMap/AdventureMapInterface.h"
|
||||
#include "adventureMap/CInGameConsole.h"
|
||||
#include "battle/BattleInterface.h"
|
||||
@@ -400,7 +401,7 @@ void ApplyClientNetPackVisitor::visitPlayerEndsGame(PlayerEndsGame & pack)
|
||||
bool localHumanWinsGame = vstd::contains(cl.playerint, pack.player) && cl.gameInfo().getPlayerState(pack.player)->human && pack.victoryLossCheckResult.victory();
|
||||
bool lastHumanEndsGame = GAME->server().howManyPlayerInterfaces() == 1 && vstd::contains(cl.playerint, pack.player) && cl.gameInfo().getPlayerState(pack.player)->human && !settings["session"]["spectate"].Bool();
|
||||
|
||||
if(lastHumanEndsGame || localHumanWinsGame)
|
||||
if(lastHumanEndsGame || localHumanWinsGame || pack.silentEnd)
|
||||
{
|
||||
assert(adventureInt);
|
||||
if(adventureInt)
|
||||
@@ -409,7 +410,13 @@ void ApplyClientNetPackVisitor::visitPlayerEndsGame(PlayerEndsGame & pack)
|
||||
adventureInt.reset();
|
||||
}
|
||||
|
||||
GAME->server().showHighScoresAndEndGameplay(pack.player, pack.victoryLossCheckResult.victory(), pack.statistic);
|
||||
if(!pack.silentEnd)
|
||||
GAME->server().showHighScoresAndEndGameplay(pack.player, pack.victoryLossCheckResult.victory(), pack.statistic);
|
||||
else
|
||||
{
|
||||
GAME->server().endGameplay();
|
||||
GAME->mainmenu()->menu->switchToTab("main");
|
||||
}
|
||||
}
|
||||
|
||||
// In auto testing pack.mode we always close client if red pack.player won or lose
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "lobby/ExtraOptionsTab.h"
|
||||
#include "lobby/SelectionTab.h"
|
||||
#include "lobby/CBonusSelection.h"
|
||||
#include "lobby/BattleOnlyMode.h"
|
||||
#include "globalLobby/GlobalLobbyWindow.h"
|
||||
#include "globalLobby/GlobalLobbyServerSetup.h"
|
||||
#include "globalLobby/GlobalLobbyClient.h"
|
||||
@@ -113,6 +114,9 @@ void ApplyOnLobbyScreenNetPackVisitor::visitLobbyGuiAction(LobbyGuiAction & pack
|
||||
if(!lobby || !handler.isGuest())
|
||||
return;
|
||||
|
||||
if(auto topWindow = ENGINE->windows().topWindow<BattleOnlyModeWindow>())
|
||||
topWindow->close();
|
||||
|
||||
switch(pack.action)
|
||||
{
|
||||
case LobbyGuiAction::NO_TAB:
|
||||
@@ -133,6 +137,9 @@ void ApplyOnLobbyScreenNetPackVisitor::visitLobbyGuiAction(LobbyGuiAction & pack
|
||||
case LobbyGuiAction::OPEN_EXTRA_OPTIONS:
|
||||
lobby->toggleTab(lobby->tabExtraOptions);
|
||||
break;
|
||||
case LobbyGuiAction::BATTLE_MODE:
|
||||
BattleOnlyMode::openBattleWindow();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,3 +239,9 @@ void ApplyOnLobbyScreenNetPackVisitor::visitLobbyShowMessage(LobbyShowMessage &
|
||||
lobby->buttonStart->block(false);
|
||||
handler.showServerError(pack.message.toString());
|
||||
}
|
||||
|
||||
void ApplyOnLobbyScreenNetPackVisitor::visitLobbySetBattleOnlyModeStartInfo(LobbySetBattleOnlyModeStartInfo & pack)
|
||||
{
|
||||
if(auto topWindow = ENGINE->windows().topWindow<BattleOnlyModeWindow>())
|
||||
topWindow->applyStartInfo(pack.startInfo);
|
||||
}
|
||||
|
||||
@@ -264,6 +264,7 @@ void BattleActionsController::reorderPossibleActionsPriority(const CStack * stac
|
||||
case PossiblePlayerBattleAction::NO_LOCATION:
|
||||
case PossiblePlayerBattleAction::FREE_LOCATION:
|
||||
case PossiblePlayerBattleAction::OBSTACLE:
|
||||
case PossiblePlayerBattleAction::SACRIFICE:
|
||||
if(!stack->hasBonusOfType(BonusType::NO_SPELLCAST_BY_DEFAULT) && targetStack != nullptr)
|
||||
{
|
||||
PlayerColor stackOwner = owner.getBattle()->battleGetOwner(targetStack);
|
||||
@@ -652,10 +653,11 @@ bool BattleActionsController::actionIsLegal(PossiblePlayerBattleAction action, c
|
||||
case PossiblePlayerBattleAction::WALK_AND_ATTACK:
|
||||
case PossiblePlayerBattleAction::ATTACK_AND_RETURN:
|
||||
{
|
||||
if (owner.fieldController->isTileAttackable(targetHex)) // move isTileAttackable to be part of battleCanAttack?
|
||||
auto activeStack = owner.stacksController->getActiveStack();
|
||||
if (targetStack && targetStack != activeStack && owner.fieldController->isTileAttackable(targetHex)) // move isTileAttackable to be part of battleCanAttack?
|
||||
{
|
||||
BattleHex attackFromHex = owner.fieldController->fromWhichHexAttack(targetHex);
|
||||
if(owner.getBattle()->battleCanAttack(owner.stacksController->getActiveStack(), targetStack, attackFromHex))
|
||||
if(owner.getBattle()->battleCanAttack(activeStack, targetStack, attackFromHex))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -698,7 +700,14 @@ bool BattleActionsController::actionIsLegal(PossiblePlayerBattleAction action, c
|
||||
return selectedStack && isCastingPossibleHere(action.spell().toSpell(), selectedStack, targetHex);
|
||||
|
||||
case PossiblePlayerBattleAction::SACRIFICE: //choose our living stack to sacrifice
|
||||
return targetStack && targetStack != selectedStack && targetStackOwned && targetStack->alive();
|
||||
{
|
||||
if(!targetStack)
|
||||
return false;
|
||||
|
||||
auto unit = targetStack->acquire();
|
||||
return targetStack != selectedStack && targetStackOwned && targetStack->alive()
|
||||
&& unit->isLiving() && !unit->hasBonusOfType(BonusType::MECHANICAL);
|
||||
}
|
||||
|
||||
case PossiblePlayerBattleAction::OBSTACLE:
|
||||
case PossiblePlayerBattleAction::FREE_LOCATION:
|
||||
|
||||
@@ -819,7 +819,7 @@ bool BattleFieldController::isTileAttackable(const BattleHex & number) const
|
||||
|
||||
for (auto & elem : occupiableHexes)
|
||||
{
|
||||
if (BattleHex::mutualPosition(elem, number) != BattleHex::EDir::NONE || elem == number)
|
||||
if (BattleHex::mutualPosition(elem, number) != BattleHex::EDir::NONE)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -161,7 +161,7 @@ const CCreature & BattleProjectileController::getShooter(const CStack * stack) c
|
||||
if(creature->getId() == CreatureID::ARROW_TOWERS)
|
||||
creature = owner.siegeController->getTurretCreature(stack->initialPosition);
|
||||
|
||||
if(creature->animation.missileFrameAngles.empty())
|
||||
if(creature->animation.missileFrameAngles.empty() && creature->animation.projectileRay.empty())
|
||||
{
|
||||
logAnim->error("Mod error: Creature '%s' on the Archer's tower is not a shooter. Mod should be fixed. Trying to use archer's data instead...", creature->getNameSingularTranslated());
|
||||
creature = CreatureID(CreatureID::ARCHER).toCreature();
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
|
||||
#include "BattleWindow.h"
|
||||
|
||||
#include "../GameInstance.h"
|
||||
#include "../Client.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../GameEngine.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
@@ -23,6 +26,7 @@
|
||||
#include "../widgets/VideoWidget.h"
|
||||
|
||||
#include "../../lib/CStack.h"
|
||||
#include "../../lib/CPlayerState.h"
|
||||
#include "../../lib/ConditionalWait.h"
|
||||
#include "../../lib/GameLibrary.h"
|
||||
#include "../../lib/StartInfo.h"
|
||||
@@ -45,7 +49,14 @@ BattleResultWindow::BattleResultWindow(const BattleResult & br, CPlayerInterface
|
||||
exit = std::make_shared<CButton>(Point(384, 505), AnimationPath::builtin("iok6432.def"), std::make_pair("", ""), [this](){ bExitf();}, EShortcut::GLOBAL_ACCEPT);
|
||||
exit->setBorderColor(Colors::METALLIC_GOLD);
|
||||
|
||||
if(allowReplay || owner.cb->getStartInfo()->extraOptionsInfo.unlimitedReplay)
|
||||
auto battle = owner.cb->getBattle(br.battleID);
|
||||
const auto * attackerPlayer = GAME->server().client->gameInfo().getPlayerState(battle->sideToPlayer(BattleSide::ATTACKER));
|
||||
const auto * defenderPlayer = GAME->server().client->gameInfo().getPlayerState(battle->sideToPlayer(BattleSide::DEFENDER));
|
||||
bool isAttackerHuman = attackerPlayer && attackerPlayer->isHuman();
|
||||
bool isDefenderHuman = defenderPlayer && defenderPlayer->isHuman();
|
||||
bool onlyOnePlayerHuman = isAttackerHuman != isDefenderHuman;
|
||||
|
||||
if((allowReplay || owner.cb->getStartInfo()->extraOptionsInfo.unlimitedReplay) && onlyOnePlayerHuman)
|
||||
{
|
||||
repeat = std::make_shared<CButton>(Point(24, 505), AnimationPath::builtin("icn6432.def"), std::make_pair("", ""), [this](){ bRepeatf();}, EShortcut::GLOBAL_CANCEL);
|
||||
repeat->setBorderColor(Colors::METALLIC_GOLD);
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
#include "../../lib/entities/artifact/CArtHandler.h"
|
||||
#include "../../lib/filesystem/ResourcePath.h"
|
||||
#include "../../lib/gameState/InfoAboutArmy.h"
|
||||
#include "../../lib/mapping/CMapHeader.h"
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../../lib/texts/CGeneralTextHandler.h"
|
||||
|
||||
@@ -851,6 +852,8 @@ void BattleWindow::endWithAutocombat()
|
||||
|
||||
void BattleWindow::showAll(Canvas & to)
|
||||
{
|
||||
if(owner.curInt->cb->getMapHeader()->battleOnly)
|
||||
to.fillTexture(ENGINE->renderHandler().loadImage(ImagePath::builtin("DiBoxBck"), EImageBlitMode::OPAQUE));
|
||||
CIntObject::showAll(to);
|
||||
|
||||
if (ENGINE->screenDimensions().x != 800 || ENGINE->screenDimensions().y !=600)
|
||||
|
||||
@@ -132,7 +132,7 @@ class CursorHandler final
|
||||
Point pos;
|
||||
float frameTime;
|
||||
int32_t currentCursorIndex;
|
||||
int32_t currentFrame;
|
||||
int32_t currentFrame {};
|
||||
Cursor::ShowType showType;
|
||||
bool showing;
|
||||
|
||||
|
||||
517
client/lobby/BattleOnlyMode.cpp
Normal file
517
client/lobby/BattleOnlyMode.cpp
Normal file
@@ -0,0 +1,517 @@
|
||||
/*
|
||||
* BattleOnlyMode.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 "BattleOnlyMode.h"
|
||||
|
||||
#include "../CServerHandler.h"
|
||||
#include "../GameEngine.h"
|
||||
#include "../GameInstance.h"
|
||||
|
||||
#include "../render/IRenderHandler.h"
|
||||
#include "../render/CAnimation.h"
|
||||
#include "../render/Canvas.h"
|
||||
#include "../render/CanvasImage.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/GraphicalPrimitiveCanvas.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
#include "../widgets/CTextInput.h"
|
||||
#include "../widgets/Images.h"
|
||||
#include "../windows/GUIClasses.h"
|
||||
#include "../windows/CHeroOverview.h"
|
||||
#include "../windows/CCreatureWindow.h"
|
||||
|
||||
#include "../../lib/GameLibrary.h"
|
||||
#include "../../lib/gameState/CGameState.h"
|
||||
#include "../../lib/networkPacks/PacksForLobby.h"
|
||||
#include "../../lib/StartInfo.h"
|
||||
#include "../../lib/VCMIDirs.h"
|
||||
#include "../../lib/CRandomGenerator.h"
|
||||
#include "../../lib/callback/EditorCallback.h"
|
||||
#include "../../lib/entities/hero/CHero.h"
|
||||
#include "../../lib/entities/hero/CHeroClass.h"
|
||||
#include "../../lib/entities/hero/CHeroHandler.h"
|
||||
#include "../../lib/entities/faction/CTown.h"
|
||||
#include "../../lib/entities/faction/CTownHandler.h"
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../../lib/mapObjects/CGTownInstance.h"
|
||||
#include "../../lib/mapObjectConstructors/AObjectTypeHandler.h"
|
||||
#include "../../lib/mapObjectConstructors/CObjectClassesHandler.h"
|
||||
#include "../../lib/mapping/CMap.h"
|
||||
#include "../../lib/mapping/CMapInfo.h"
|
||||
#include "../../lib/mapping/CMapEditManager.h"
|
||||
#include "../../lib/mapping/CMapService.h"
|
||||
#include "../../lib/mapping/MapFormat.h"
|
||||
#include "../../lib/texts/CGeneralTextHandler.h"
|
||||
#include "../../lib/texts/MetaString.h"
|
||||
#include "../../lib/texts/TextOperations.h"
|
||||
#include "../../lib/filesystem/Filesystem.h"
|
||||
|
||||
void BattleOnlyMode::openBattleWindow()
|
||||
{
|
||||
GAME->server().sendGuiAction(LobbyGuiAction::BATTLE_MODE);
|
||||
ENGINE->windows().createAndPushWindow<BattleOnlyModeWindow>();
|
||||
}
|
||||
|
||||
BattleOnlyModeWindow::BattleOnlyModeWindow()
|
||||
: CWindowObject(BORDERED)
|
||||
, startInfo(std::make_shared<BattleOnlyModeStartInfo>())
|
||||
, disabledColor(GAME->server().isHost() ? Colors::WHITE : Colors::ORANGE)
|
||||
{
|
||||
OBJECT_CONSTRUCTION;
|
||||
|
||||
pos.w = 519;
|
||||
pos.h = 238;
|
||||
|
||||
updateShadow();
|
||||
center();
|
||||
|
||||
init();
|
||||
|
||||
backgroundTexture = std::make_shared<FilledTexturePlayerColored>(Rect(0, 0, pos.w, pos.h));
|
||||
backgroundTexture->setPlayerColor(PlayerColor(1));
|
||||
buttonOk = std::make_shared<CButton>(Point(191, 203), AnimationPath::builtin("MuBchck"), CButton::tooltip(), [this](){ startBattle(); }, EShortcut::GLOBAL_ACCEPT);
|
||||
buttonOk->block(true);
|
||||
buttonAbort = std::make_shared<CButton>(Point(265, 203), AnimationPath::builtin("MuBcanc"), CButton::tooltip(), [this](){
|
||||
GAME->server().sendGuiAction(LobbyGuiAction::NO_TAB);
|
||||
close();
|
||||
}, EShortcut::GLOBAL_CANCEL);
|
||||
buttonAbort->block(true);
|
||||
title = std::make_shared<CLabel>(260, 20, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyMode"));
|
||||
|
||||
battlefieldSelector = std::make_shared<CButton>(Point(29, 174), AnimationPath::builtin("GSPButtonClear"), CButton::tooltip(), [this](){
|
||||
std::vector<std::string> texts;
|
||||
std::vector<std::shared_ptr<IImage>> images;
|
||||
|
||||
auto & terrains = LIBRARY->terrainTypeHandler->objects;
|
||||
for (const auto & terrain : terrains)
|
||||
{
|
||||
if(!terrain->isPassable())
|
||||
continue;
|
||||
|
||||
texts.push_back(terrain->getNameTranslated());
|
||||
|
||||
const auto & patterns = LIBRARY->terviewh->getTerrainViewPatterns(terrain->getId());
|
||||
TerrainViewPattern pattern;
|
||||
for(auto & p : patterns)
|
||||
if(p[0].id == "n1")
|
||||
pattern = p[0];
|
||||
auto image = ENGINE->renderHandler().loadImage(terrain->tilesFilename, pattern.mapping[0].first, 0, EImageBlitMode::OPAQUE);
|
||||
image->scaleTo(Point(23, 23), EScalingAlgorithm::NEAREST);
|
||||
images.push_back(image);
|
||||
}
|
||||
|
||||
auto factions = LIBRARY->townh->getDefaultAllowed();
|
||||
for (const auto & faction : factions)
|
||||
{
|
||||
texts.push_back(faction.toFaction()->getNameTranslated());
|
||||
|
||||
auto image = ENGINE->renderHandler().loadImage(AnimationPath::builtin("ITPA"), faction.toFaction()->town->clientInfo.icons[true][false] + 2, 0, EImageBlitMode::OPAQUE);
|
||||
image->scaleTo(Point(35, 23), EScalingAlgorithm::NEAREST);
|
||||
images.push_back(image);
|
||||
}
|
||||
|
||||
ENGINE->windows().createAndPushWindow<CObjectListWindow>(texts, nullptr, LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyModeBattlefield"), LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyModeBattlefieldSelect"), [this, terrains, factions](int index){
|
||||
if(terrains.size() > index)
|
||||
{
|
||||
startInfo->selectedTerrain = terrains[index]->getId();
|
||||
startInfo->selectedTown = std::nullopt;
|
||||
}
|
||||
else
|
||||
{
|
||||
startInfo->selectedTerrain = std::nullopt;
|
||||
auto it = std::next(factions.begin(), index - terrains.size());
|
||||
if (it != factions.end())
|
||||
startInfo->selectedTown = *it;
|
||||
}
|
||||
onChange();
|
||||
}, (startInfo->selectedTerrain ? static_cast<int>(*startInfo->selectedTerrain) : static_cast<int>(*startInfo->selectedTown + terrains.size())), images, true, true);
|
||||
});
|
||||
battlefieldSelector->block(GAME->server().isGuest());
|
||||
buttonReset = std::make_shared<CButton>(Point(289, 174), AnimationPath::builtin("GSPButtonClear"), CButton::tooltip(), [this](){
|
||||
if(GAME->server().isHost())
|
||||
{
|
||||
startInfo->selectedTerrain = TerrainId::DIRT;
|
||||
startInfo->selectedTown = std::nullopt;
|
||||
startInfo->selectedHero[0] = std::nullopt;
|
||||
startInfo->selectedArmy[0].fill(CStackBasicDescriptor(CreatureID::NONE, 1));
|
||||
for(size_t i=0; i<GameConstants::ARMY_SIZE; i++)
|
||||
heroSelector1->selectedArmyInput.at(i)->disable();
|
||||
}
|
||||
startInfo->selectedHero[1] = std::nullopt;
|
||||
startInfo->selectedArmy[1].fill(CStackBasicDescriptor(CreatureID::NONE, 1));
|
||||
for(size_t i=0; i<GameConstants::ARMY_SIZE; i++)
|
||||
heroSelector2->selectedArmyInput.at(i)->disable();
|
||||
onChange();
|
||||
});
|
||||
buttonReset->setTextOverlay(LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyModeReset"), EFonts::FONT_SMALL, Colors::WHITE);
|
||||
|
||||
heroSelector1 = std::make_shared<BattleOnlyModeHeroSelector>(0, *this, Point(0, 40));
|
||||
heroSelector2 = std::make_shared<BattleOnlyModeHeroSelector>(1, *this, Point(260, 40));
|
||||
|
||||
heroSelector1->setInputEnabled(GAME->server().isHost());
|
||||
|
||||
onChange();
|
||||
}
|
||||
|
||||
void BattleOnlyModeWindow::init()
|
||||
{
|
||||
map = std::make_unique<CMap>(nullptr);
|
||||
map->version = EMapFormat::VCMI;
|
||||
map->creationDateTime = std::time(nullptr);
|
||||
map->width = 10;
|
||||
map->height = 10;
|
||||
map->mapLevels = 1;
|
||||
map->battleOnly = true;
|
||||
map->name = MetaString::createFromTextID("vcmi.lobby.battleOnlyMode");
|
||||
|
||||
cb = std::make_unique<EditorCallback>(map.get());
|
||||
}
|
||||
|
||||
void BattleOnlyModeWindow::onChange()
|
||||
{
|
||||
GAME->server().setBattleOnlyModeStartInfo(startInfo);
|
||||
}
|
||||
|
||||
void BattleOnlyModeWindow::update()
|
||||
{
|
||||
setTerrainButtonText();
|
||||
setOkButtonEnabled();
|
||||
|
||||
heroSelector1->setHeroIcon();
|
||||
heroSelector1->setCreatureIcons();
|
||||
heroSelector2->setHeroIcon();
|
||||
heroSelector2->setCreatureIcons();
|
||||
redraw();
|
||||
}
|
||||
|
||||
void BattleOnlyModeWindow::applyStartInfo(std::shared_ptr<BattleOnlyModeStartInfo> si)
|
||||
{
|
||||
startInfo = si;
|
||||
update();
|
||||
}
|
||||
|
||||
void BattleOnlyModeWindow::setTerrainButtonText()
|
||||
{
|
||||
battlefieldSelector->setTextOverlay(LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyModeBattlefield") + ": " + (startInfo->selectedTerrain ? (*startInfo->selectedTerrain).toEntity(LIBRARY)->getNameTranslated() : (*startInfo->selectedTown).toEntity(LIBRARY)->getNameTranslated()), EFonts::FONT_SMALL, disabledColor);
|
||||
}
|
||||
|
||||
void BattleOnlyModeWindow::setOkButtonEnabled()
|
||||
{
|
||||
bool army2Empty = std::all_of(startInfo->selectedArmy[1].begin(), startInfo->selectedArmy[1].end(), [](const auto x) { return x.getId() == CreatureID::NONE; });
|
||||
|
||||
bool canStart = (startInfo->selectedTerrain || startInfo->selectedTown);
|
||||
canStart &= (startInfo->selectedHero[0] && ((startInfo->selectedHero[1]) || (startInfo->selectedTown && !army2Empty)));
|
||||
buttonOk->block(!canStart || GAME->server().isGuest());
|
||||
buttonAbort->block(GAME->server().isGuest());
|
||||
}
|
||||
|
||||
std::shared_ptr<IImage> drawBlackBox(Point size, std::string text, ColorRGBA color)
|
||||
{
|
||||
auto image = ENGINE->renderHandler().createImage(size, CanvasScalingPolicy::AUTO);
|
||||
Canvas canvas = image->getCanvas();
|
||||
canvas.drawColor(Rect(0, 0, size.x, size.y), Colors::BLACK);
|
||||
canvas.drawText(Point(size.x / 2, size.y / 2), FONT_TINY, color, ETextAlignment::CENTER, text);
|
||||
return image;
|
||||
}
|
||||
|
||||
BattleOnlyModeHeroSelector::BattleOnlyModeHeroSelector(int id, BattleOnlyModeWindow& p, Point position)
|
||||
: parent(p)
|
||||
, id(id)
|
||||
{
|
||||
OBJECT_CONSTRUCTION;
|
||||
|
||||
pos.x += position.x;
|
||||
pos.y += position.y;
|
||||
|
||||
backgroundImage = std::make_shared<CPicture>(ImagePath::builtin("heroSlotsBlue"), Point(3, 4));
|
||||
|
||||
for(size_t i=0; i<GameConstants::PRIMARY_SKILLS; i++)
|
||||
{
|
||||
auto image = std::make_shared<CAnimImage>(AnimationPath::builtin("PSKIL32"), i, 0, 78 + i * 36, 26);
|
||||
primSkills.push_back(image);
|
||||
primSkillsBorder.push_back(std::make_shared<GraphicalPrimitiveCanvas>(Rect(78 + i * 36, 26, 32, 32)));
|
||||
primSkillsBorder.back()->addRectangle(Point(0, 0), Point(32, 32), ColorRGBA(44, 108, 255));
|
||||
primSkillsInput.push_back(std::make_shared<CTextInput>(Rect(78 + i * 36, 58, 32, 16), EFonts::FONT_SMALL, ETextAlignment::CENTER, false));
|
||||
primSkillsInput.back()->setColor(id == 1 ? Colors::WHITE : parent.disabledColor);
|
||||
primSkillsInput.back()->setFilterNumber(0, 100);
|
||||
primSkillsInput.back()->setText("0");
|
||||
primSkillsInput.back()->setCallback([this, i, id](const std::string & text){
|
||||
parent.startInfo->primSkillLevel[id][i] = std::stoi(primSkillsInput[i]->getText());
|
||||
parent.onChange();
|
||||
});
|
||||
}
|
||||
|
||||
creatureImage.resize(GameConstants::ARMY_SIZE);
|
||||
for(size_t i=0; i<GameConstants::ARMY_SIZE; i++)
|
||||
{
|
||||
selectedArmyInput.push_back(std::make_shared<CTextInput>(Rect(5 + i * 36, 113, 32, 16), EFonts::FONT_SMALL, ETextAlignment::CENTER, false));
|
||||
selectedArmyInput.back()->setColor(id == 1 ? Colors::WHITE : parent.disabledColor);
|
||||
selectedArmyInput.back()->setFilterNumber(1, 10000000, 3);
|
||||
selectedArmyInput.back()->setText("1");
|
||||
selectedArmyInput.back()->setCallback([this, i, id](const std::string & text){
|
||||
if(parent.startInfo->selectedArmy[id][i].getId() != CreatureID::NONE)
|
||||
{
|
||||
parent.startInfo->selectedArmy[id][i].setCount(TextOperations::parseMetric<int>(text));
|
||||
parent.onChange();
|
||||
selectedArmyInput[i]->enable();
|
||||
}
|
||||
else
|
||||
selectedArmyInput[i]->disable();
|
||||
});
|
||||
}
|
||||
|
||||
setHeroIcon();
|
||||
setCreatureIcons();
|
||||
}
|
||||
|
||||
void BattleOnlyModeHeroSelector::setHeroIcon()
|
||||
{
|
||||
OBJECT_CONSTRUCTION;
|
||||
|
||||
if(!parent.startInfo->selectedHero[id])
|
||||
{
|
||||
heroImage = std::make_shared<CPicture>(drawBlackBox(Point(58, 64), LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyModeSelect"), id == 1 ? Colors::WHITE : parent.disabledColor), Point(6, 7));
|
||||
heroLabel = std::make_shared<CLabel>(160, 16, FONT_SMALL, ETextAlignment::CENTER, id == 1 ? Colors::WHITE : parent.disabledColor, LIBRARY->generaltexth->translate("core.genrltxt.507"));
|
||||
for(size_t i=0; i<GameConstants::PRIMARY_SKILLS; i++)
|
||||
primSkillsInput[i]->setText("0");
|
||||
}
|
||||
else
|
||||
{
|
||||
heroImage = std::make_shared<CPicture>(ENGINE->renderHandler().loadAnimation(AnimationPath::builtin("PortraitsLarge"), EImageBlitMode::COLORKEY)->getImage((*parent.startInfo->selectedHero[id]).toHeroType()->imageIndex), Point(6, 7));
|
||||
heroLabel = std::make_shared<CLabel>(160, 16, FONT_SMALL, ETextAlignment::CENTER, id == 1 ? Colors::WHITE : parent.disabledColor, (*parent.startInfo->selectedHero[id]).toHeroType()->getNameTranslated());
|
||||
for(size_t i=0; i<GameConstants::PRIMARY_SKILLS; i++)
|
||||
primSkillsInput[i]->setText(std::to_string(parent.startInfo->primSkillLevel[id][i]));
|
||||
}
|
||||
|
||||
heroImage->addLClickCallback([this](){
|
||||
auto allowedSet = LIBRARY->heroh->getDefaultAllowed();
|
||||
std::vector<HeroTypeID> heroes(allowedSet.begin(), allowedSet.end());
|
||||
std::sort(heroes.begin(), heroes.end(), [](auto a, auto b) {
|
||||
auto heroA = a.toHeroType();
|
||||
auto heroB = b.toHeroType();
|
||||
if(heroA->heroClass->faction != heroB->heroClass->faction)
|
||||
return heroA->heroClass->faction < heroB->heroClass->faction;
|
||||
if(heroA->heroClass->getId() != heroB->heroClass->getId())
|
||||
return heroA->heroClass->getId() < heroB->heroClass->getId();
|
||||
return heroA->getNameTranslated() < heroB->getNameTranslated();
|
||||
});
|
||||
|
||||
int selectedIndex = !parent.startInfo->selectedHero[id] ? 0 : (1 + std::distance(heroes.begin(), std::find_if(heroes.begin(), heroes.end(), [this](auto heroID) {
|
||||
return heroID == (*parent.startInfo->selectedHero[id]);
|
||||
})));
|
||||
|
||||
std::vector<std::string> texts;
|
||||
std::vector<std::shared_ptr<IImage>> images;
|
||||
// Add "no hero" option
|
||||
texts.push_back(LIBRARY->generaltexth->translate("core.genrltxt.507"));
|
||||
images.push_back(nullptr);
|
||||
for (const auto & h : heroes)
|
||||
{
|
||||
texts.push_back(h.toHeroType()->getNameTranslated());
|
||||
|
||||
auto image = ENGINE->renderHandler().loadImage(AnimationPath::builtin("PortraitsSmall"), h.toHeroType()->imageIndex, 0, EImageBlitMode::OPAQUE);
|
||||
image->scaleTo(Point(35, 23), EScalingAlgorithm::NEAREST);
|
||||
images.push_back(image);
|
||||
}
|
||||
auto window = std::make_shared<CObjectListWindow>(texts, nullptr, LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyModeHeroSelect"), LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyModeHeroSelect"), [this, heroes](int index){
|
||||
if(index == 0)
|
||||
{
|
||||
parent.startInfo->selectedHero[id] = std::nullopt;
|
||||
parent.onChange();
|
||||
return;
|
||||
}
|
||||
index--;
|
||||
|
||||
parent.startInfo->selectedHero[id] = heroes[index];
|
||||
|
||||
for(size_t i=0; i<GameConstants::PRIMARY_SKILLS; i++)
|
||||
parent.startInfo->primSkillLevel[id][i] = 0;
|
||||
parent.onChange();
|
||||
}, selectedIndex, images, true, true);
|
||||
window->onPopup = [heroes](int index) {
|
||||
if(index == 0)
|
||||
return;
|
||||
index--;
|
||||
|
||||
ENGINE->windows().createAndPushWindow<CHeroOverview>(heroes.at(index));
|
||||
};
|
||||
ENGINE->windows().pushWindow(window);
|
||||
});
|
||||
|
||||
heroImage->addRClickCallback([this](){
|
||||
if(!parent.startInfo->selectedHero[id])
|
||||
return;
|
||||
|
||||
ENGINE->windows().createAndPushWindow<CHeroOverview>(parent.startInfo->selectedHero[id]->toHeroType()->getId());
|
||||
});
|
||||
}
|
||||
|
||||
void BattleOnlyModeHeroSelector::setCreatureIcons()
|
||||
{
|
||||
OBJECT_CONSTRUCTION;
|
||||
|
||||
for(int i = 0; i < creatureImage.size(); i++)
|
||||
{
|
||||
if(parent.startInfo->selectedArmy[id][i].getId() == CreatureID::NONE)
|
||||
{
|
||||
creatureImage[i] = std::make_shared<CPicture>(drawBlackBox(Point(32, 32), LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyModeSelect"), id == 1 ? Colors::WHITE : parent.disabledColor), Point(6 + i * 36, 78));
|
||||
selectedArmyInput[i]->disable();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto unit = parent.startInfo->selectedArmy[id][i];
|
||||
auto creatureID = unit.getId();
|
||||
creatureImage[i] = std::make_shared<CPicture>(ENGINE->renderHandler().loadAnimation(AnimationPath::builtin("CPRSMALL"), EImageBlitMode::COLORKEY)->getImage(LIBRARY->creh->objects.at(creatureID)->getIconIndex()), Point(6 + i * 36, 78));
|
||||
selectedArmyInput[i]->setText(TextOperations::formatMetric(unit.getCount(), 3));
|
||||
selectedArmyInput[i]->enable();
|
||||
}
|
||||
|
||||
creatureImage[i]->addLClickCallback([this, i](){
|
||||
auto allowedSet = LIBRARY->creh->getDefaultAllowed();
|
||||
std::vector<CreatureID> creatures(allowedSet.begin(), allowedSet.end());
|
||||
std::sort(creatures.begin(), creatures.end(), [](auto a, auto b) {
|
||||
auto creatureA = a.toCreature();
|
||||
auto creatureB = b.toCreature();
|
||||
if(creatureA->getFactionID() != creatureB->getFactionID())
|
||||
return creatureA->getFactionID() < creatureB->getFactionID();
|
||||
if(creatureA->getLevel() != creatureB->getLevel())
|
||||
return creatureA->getLevel() < creatureB->getLevel();
|
||||
if(creatureA->upgrades.size() != creatureB->upgrades.size())
|
||||
return creatureA->upgrades.size() > creatureB->upgrades.size();
|
||||
return creatureA->getNameSingularTranslated() < creatureB->getNameSingularTranslated();
|
||||
});
|
||||
|
||||
int selectedIndex = parent.startInfo->selectedArmy[id][i].getId() == CreatureID::NONE ? 0 : (1 + std::distance(creatures.begin(), std::find_if(creatures.begin(), creatures.end(), [this, i](auto creatureID) {
|
||||
return creatureID == parent.startInfo->selectedArmy[id][i].getId();
|
||||
})));
|
||||
|
||||
std::vector<std::string> texts;
|
||||
std::vector<std::shared_ptr<IImage>> images;
|
||||
// Add "no creature" option
|
||||
texts.push_back(LIBRARY->generaltexth->translate("core.genrltxt.507"));
|
||||
images.push_back(nullptr);
|
||||
for (const auto & c : creatures)
|
||||
{
|
||||
texts.push_back(c.toCreature()->getNameSingularTranslated());
|
||||
|
||||
auto image = ENGINE->renderHandler().loadImage(AnimationPath::builtin("CPRSMALL"), c.toCreature()->getIconIndex(), 0, EImageBlitMode::OPAQUE);
|
||||
image->scaleTo(Point(23, 23), EScalingAlgorithm::NEAREST);
|
||||
images.push_back(image);
|
||||
}
|
||||
auto window = std::make_shared<CObjectListWindow>(texts, nullptr, LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyModeCreatureSelect"), LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyModeCreatureSelect"), [this, creatures, i](int index){
|
||||
if(index == 0)
|
||||
{
|
||||
parent.startInfo->selectedArmy[id][i] = CStackBasicDescriptor(CreatureID::NONE, 1);
|
||||
parent.onChange();
|
||||
return;
|
||||
}
|
||||
index--;
|
||||
|
||||
auto creature = creatures.at(index).toCreature();
|
||||
parent.startInfo->selectedArmy[id][i] = CStackBasicDescriptor(creature->getId(), 100);
|
||||
parent.onChange();
|
||||
}, selectedIndex, images, true, true);
|
||||
window->onPopup = [creatures](int index) {
|
||||
if(index == 0)
|
||||
return;
|
||||
index--;
|
||||
|
||||
ENGINE->windows().createAndPushWindow<CStackWindow>(creatures.at(index).toCreature(), true);
|
||||
};
|
||||
ENGINE->windows().pushWindow(window);
|
||||
});
|
||||
|
||||
creatureImage[i]->addRClickCallback([this, i](){
|
||||
if(parent.startInfo->selectedArmy[id][i].getId() == CreatureID::NONE)
|
||||
return;
|
||||
|
||||
ENGINE->windows().createAndPushWindow<CStackWindow>(LIBRARY->creh->objects.at(parent.startInfo->selectedArmy[id][i].getId()).get(), true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void BattleOnlyModeWindow::startBattle()
|
||||
{
|
||||
auto rng = &CRandomGenerator::getDefault();
|
||||
|
||||
map->initTerrain();
|
||||
map->getEditManager()->clearTerrain(rng);
|
||||
|
||||
map->getEditManager()->getTerrainSelection().selectAll();
|
||||
map->getEditManager()->drawTerrain(!startInfo->selectedTerrain ? TerrainId::DIRT : *startInfo->selectedTerrain, 0, rng);
|
||||
|
||||
map->players[0].canComputerPlay = true;
|
||||
map->players[0].canHumanPlay = true;
|
||||
map->players[1] = map->players[0];
|
||||
|
||||
auto knownHeroes = LIBRARY->objtypeh->knownSubObjects(Obj::HERO);
|
||||
|
||||
auto addHero = [&, this](int sel, PlayerColor color, const int3 & position)
|
||||
{
|
||||
auto factory = LIBRARY->objtypeh->getHandlerFor(Obj::HERO, (*startInfo->selectedHero[sel]).toHeroType()->heroClass->getId());
|
||||
auto templates = factory->getTemplates();
|
||||
auto obj = std::dynamic_pointer_cast<CGHeroInstance>(factory->create(cb.get(), templates.front()));
|
||||
obj->setHeroType(*startInfo->selectedHero[sel]);
|
||||
|
||||
obj->setOwner(color);
|
||||
obj->pos = position;
|
||||
for(size_t i=0; i<GameConstants::PRIMARY_SKILLS; i++)
|
||||
obj->pushPrimSkill(PrimarySkill(i), startInfo->primSkillLevel[sel][i]);
|
||||
obj->clearSlots();
|
||||
for(int slot = 0; slot < GameConstants::ARMY_SIZE; slot++)
|
||||
if(startInfo->selectedArmy[sel][slot].getId() != CreatureID::NONE)
|
||||
obj->setCreature(SlotID(slot), startInfo->selectedArmy[sel][slot].getId(), startInfo->selectedArmy[sel][slot].getCount());
|
||||
map->getEditManager()->insertObject(obj);
|
||||
};
|
||||
|
||||
addHero(0, PlayerColor(0), int3(5, 6, 0));
|
||||
if(!startInfo->selectedTown)
|
||||
addHero(1, PlayerColor(1), int3(5, 5, 0));
|
||||
else
|
||||
{
|
||||
auto factory = LIBRARY->objtypeh->getHandlerFor(Obj::TOWN, *startInfo->selectedTown);
|
||||
auto templates = factory->getTemplates();
|
||||
auto obj = factory->create(cb.get(), templates.front());
|
||||
auto townObj = std::dynamic_pointer_cast<CGTownInstance>(obj);
|
||||
obj->setOwner(PlayerColor(1));
|
||||
obj->pos = int3(5, 5, 0);
|
||||
for (const auto & building : townObj->getTown()->getAllBuildings())
|
||||
townObj->addBuilding(building);
|
||||
if(!startInfo->selectedHero[1])
|
||||
{
|
||||
for(int slot = 0; slot < GameConstants::ARMY_SIZE; slot++)
|
||||
if(startInfo->selectedArmy[1][slot].getId() != CreatureID::NONE)
|
||||
townObj->getArmy()->setCreature(SlotID(slot), startInfo->selectedArmy[1][slot].getId(), startInfo->selectedArmy[1][slot].getCount());
|
||||
}
|
||||
else
|
||||
addHero(1, PlayerColor(1), int3(5, 5, 0));
|
||||
|
||||
map->getEditManager()->insertObject(townObj);
|
||||
}
|
||||
|
||||
auto path = VCMIDirs::get().userDataPath() / "Maps";
|
||||
boost::filesystem::create_directories(path);
|
||||
const std::string fileName = "BattleOnlyMode.vmap";
|
||||
const auto fullPath = path / fileName;
|
||||
CMapService mapService;
|
||||
mapService.saveMap(map, fullPath);
|
||||
CResourceHandler::get()->updateFilteredFiles([&](const std::string & mount) { return true; });
|
||||
|
||||
auto mapInfo = std::make_shared<CMapInfo>();
|
||||
mapInfo->mapInit("Maps/BattleOnlyMode");
|
||||
GAME->server().setMapInfo(mapInfo);
|
||||
ExtraOptionsInfo extraOptions;
|
||||
extraOptions.unlimitedReplay = true;
|
||||
GAME->server().setExtraOptionsInfo(extraOptions);
|
||||
GAME->server().sendStartGame();
|
||||
}
|
||||
92
client/lobby/BattleOnlyMode.h
Normal file
92
client/lobby/BattleOnlyMode.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* BattleOnlyMode.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 "../windows/CWindowObject.h"
|
||||
#include "../../lib/constants/EntityIdentifiers.h"
|
||||
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
class CGHeroInstance;
|
||||
class CCreatureSet;
|
||||
class CMap;
|
||||
class EditorCallback;
|
||||
class BattleOnlyModeStartInfo;
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
class FilledTexturePlayerColored;
|
||||
class CButton;
|
||||
class CPicture;
|
||||
class CLabel;
|
||||
class BattleOnlyModeWindow;
|
||||
class CAnimImage;
|
||||
class GraphicalPrimitiveCanvas;
|
||||
class CTextInput;
|
||||
class TransparentFilledRectangle;
|
||||
|
||||
class BattleOnlyMode
|
||||
{
|
||||
public:
|
||||
static void openBattleWindow();
|
||||
};
|
||||
|
||||
class BattleOnlyModeHeroSelector : public CIntObject
|
||||
{
|
||||
private:
|
||||
BattleOnlyModeWindow& parent;
|
||||
|
||||
std::shared_ptr<CPicture> backgroundImage;
|
||||
std::shared_ptr<CPicture> heroImage;
|
||||
std::shared_ptr<CLabel> heroLabel;
|
||||
std::vector<std::shared_ptr<CPicture>> creatureImage;
|
||||
|
||||
int id;
|
||||
public:
|
||||
std::vector<std::shared_ptr<CAnimImage>> primSkills;
|
||||
std::vector<std::shared_ptr<GraphicalPrimitiveCanvas>> primSkillsBorder;
|
||||
std::vector<std::shared_ptr<CTextInput>> primSkillsInput;
|
||||
|
||||
std::vector<std::shared_ptr<CTextInput>> selectedArmyInput;
|
||||
|
||||
void setHeroIcon();
|
||||
void setCreatureIcons();
|
||||
BattleOnlyModeHeroSelector(int id, BattleOnlyModeWindow& parent, Point position);
|
||||
};
|
||||
|
||||
class BattleOnlyModeWindow : public CWindowObject
|
||||
{
|
||||
friend class BattleOnlyModeHeroSelector;
|
||||
private:
|
||||
std::shared_ptr<BattleOnlyModeStartInfo> startInfo;
|
||||
std::unique_ptr<CMap> map;
|
||||
std::shared_ptr<EditorCallback> cb;
|
||||
|
||||
std::shared_ptr<FilledTexturePlayerColored> backgroundTexture;
|
||||
std::shared_ptr<CButton> buttonOk;
|
||||
std::shared_ptr<CButton> buttonAbort;
|
||||
std::shared_ptr<CLabel> title;
|
||||
|
||||
std::shared_ptr<CButton> battlefieldSelector;
|
||||
std::shared_ptr<CButton> buttonReset;
|
||||
std::shared_ptr<BattleOnlyModeHeroSelector> heroSelector1;
|
||||
std::shared_ptr<BattleOnlyModeHeroSelector> heroSelector2;
|
||||
|
||||
ColorRGBA disabledColor;
|
||||
|
||||
void init();
|
||||
void onChange();
|
||||
void update();
|
||||
void setTerrainButtonText();
|
||||
void setOkButtonEnabled();
|
||||
void startBattle();
|
||||
public:
|
||||
BattleOnlyModeWindow();
|
||||
void applyStartInfo(std::shared_ptr<BattleOnlyModeStartInfo> si);
|
||||
};
|
||||
@@ -172,14 +172,26 @@ RandomMapTab::RandomMapTab():
|
||||
{
|
||||
std::vector<std::string> texts;
|
||||
texts.push_back(readText(variables["randomTemplate"]));
|
||||
for(auto & t : getTemplates())
|
||||
texts.push_back(t->getName());
|
||||
|
||||
auto selectedTemplate = mapGenOptions->getMapTemplate();
|
||||
const auto& templates = getTemplates();
|
||||
for(int i = 0; i < templates.size(); i++)
|
||||
{
|
||||
if(selectedTemplate)
|
||||
{
|
||||
if(templates[i]->getId() == selectedTemplate->getId())
|
||||
templateIndex = i + 1;
|
||||
}
|
||||
else
|
||||
templateIndex = 0;
|
||||
|
||||
texts.push_back(templates[i]->getName());
|
||||
}
|
||||
|
||||
ENGINE->windows().popWindows(1);
|
||||
ENGINE->windows().createAndPushWindow<CObjectListWindow>(texts, nullptr, LIBRARY->generaltexth->translate("vcmi.lobby.templatesSelect.hover"), LIBRARY->generaltexth->translate("vcmi.lobby.templatesSelect.help"), [this](int index){
|
||||
widget<ComboBox>("templateList")->setItem(index);
|
||||
templateIndex = index;
|
||||
}, templateIndex, std::vector<std::shared_ptr<IImage>>(), true);
|
||||
}, templateIndex, std::vector<std::shared_ptr<IImage>>(), true, true);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "SelectionTab.h"
|
||||
#include "CSelectionBase.h"
|
||||
#include "CLobbyScreen.h"
|
||||
#include "BattleOnlyMode.h"
|
||||
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../CServerHandler.h"
|
||||
@@ -241,6 +242,13 @@ SelectionTab::SelectionTab(ESelectionScreen Type)
|
||||
sortByDate->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("lobby/selectionTabSortDate")));
|
||||
buttonsSortBy.push_back(sortByDate);
|
||||
|
||||
if(tabType == ESelectionScreen::newGame)
|
||||
{
|
||||
buttonBattleOnlyMode = std::make_shared<CButton>(Point(23, 18), AnimationPath::builtin("lobby/battleButton"), CButton::tooltip("", LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyMode")), [tabTitle, tabTitleDelete](){
|
||||
BattleOnlyMode::openBattleWindow();
|
||||
});
|
||||
}
|
||||
|
||||
if(tabType == ESelectionScreen::loadGame || tabType == ESelectionScreen::newGame)
|
||||
{
|
||||
buttonDeleteMode = std::make_shared<CButton>(Point(367, 18), AnimationPath::builtin("lobby/deleteButton"), CButton::tooltip("", LIBRARY->generaltexth->translate("vcmi.lobby.deleteMode")), [this, tabTitle, tabTitleDelete](){
|
||||
@@ -315,6 +323,8 @@ void SelectionTab::toggleMode()
|
||||
{
|
||||
if(slider)
|
||||
slider->block(true);
|
||||
if(buttonBattleOnlyMode)
|
||||
buttonBattleOnlyMode->block(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -325,6 +335,7 @@ void SelectionTab::toggleMode()
|
||||
inputName->disable();
|
||||
auto files = getFiles("Maps/", EResType::MAP);
|
||||
files.erase(ResourcePath("Maps/Tutorial.tut", EResType::MAP));
|
||||
files.erase(ResourcePath("Maps/BattleOnlyMode.vmap", EResType::MAP));
|
||||
parseMaps(files);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -128,6 +128,8 @@ private:
|
||||
std::shared_ptr<CButton> buttonDeleteMode;
|
||||
bool deleteMode;
|
||||
|
||||
std::shared_ptr<CButton> buttonBattleOnlyMode;
|
||||
|
||||
bool enableUiEnhancements;
|
||||
std::shared_ptr<CButton> buttonCampaignSet;
|
||||
|
||||
|
||||
@@ -52,9 +52,11 @@ void PackRollbackGeneratorVisitor::visitRebalanceStacks(RebalanceStacks & pack)
|
||||
const auto * srcArmy = dynamic_cast<const CArmedInstance *>(srcObject);
|
||||
const auto * dstArmy = dynamic_cast<const CArmedInstance *>(dstObject);
|
||||
|
||||
const auto * artifact = srcArmy->getStack(pack.srcSlot).getSlot(ArtifactPosition::CREATURE_SLOT);
|
||||
|
||||
if (srcArmy->getStack(pack.srcSlot).getTotalExperience() != 0 ||
|
||||
dstArmy->getStack(pack.srcSlot).getTotalExperience() != 0 ||
|
||||
srcArmy->getStack(pack.srcSlot).getSlot(ArtifactPosition::CREATURE_SLOT)->artifactID.hasValue())
|
||||
(artifact && artifact->artifactID.hasValue()))
|
||||
{
|
||||
// TODO: rollback creature artifacts & stack experience
|
||||
return;
|
||||
|
||||
@@ -93,6 +93,14 @@ void AssetGenerator::initialize()
|
||||
|
||||
animationFiles[AnimationPath::builtin("SPRITES/GSPButtonClear")] = createGSPButtonClear();
|
||||
|
||||
for (PlayerColor color(-1); color < PlayerColor::PLAYER_LIMIT; ++color)
|
||||
{
|
||||
std::string name = "TownPortalBackgroundBlue" + (color == -1 ? "" : "-" + color.toString());
|
||||
imageFiles[ImagePath::builtin(name)] = [this, color](){ return createGateListColored(std::max(PlayerColor(0), color), PlayerColor(1)); };
|
||||
}
|
||||
|
||||
imageFiles[ImagePath::builtin("heroSlotsBlue.png")] = [this](){ return createHeroSlotsColored(PlayerColor(1));};
|
||||
|
||||
createPaletteShiftedSprites();
|
||||
}
|
||||
|
||||
@@ -129,6 +137,22 @@ void AssetGenerator::addAnimationFile(const AnimationPath & path, AnimationLayou
|
||||
animationFiles[path] = anim;
|
||||
}
|
||||
|
||||
auto getColorFilters()
|
||||
{
|
||||
auto filterSettings = LIBRARY->settingsHandler->getFullConfig()["interface"]["playerColoredBackground"];
|
||||
static const std::array<ColorFilter, PlayerColor::PLAYER_LIMIT_I> filters = {
|
||||
ColorFilter::genRangeShifter( filterSettings["red" ].convertTo<std::vector<float>>() ),
|
||||
ColorFilter::genRangeShifter( filterSettings["blue" ].convertTo<std::vector<float>>() ),
|
||||
ColorFilter::genRangeShifter( filterSettings["tan" ].convertTo<std::vector<float>>() ),
|
||||
ColorFilter::genRangeShifter( filterSettings["green" ].convertTo<std::vector<float>>() ),
|
||||
ColorFilter::genRangeShifter( filterSettings["orange"].convertTo<std::vector<float>>() ),
|
||||
ColorFilter::genRangeShifter( filterSettings["purple"].convertTo<std::vector<float>>() ),
|
||||
ColorFilter::genRangeShifter( filterSettings["teal" ].convertTo<std::vector<float>>() ),
|
||||
ColorFilter::genRangeShifter( filterSettings["pink" ].convertTo<std::vector<float>>() )
|
||||
};
|
||||
return filters;
|
||||
}
|
||||
|
||||
AssetGenerator::CanvasPtr AssetGenerator::createAdventureOptionsCleanBackground() const
|
||||
{
|
||||
auto locator = ImageLocator(ImagePath::builtin("ADVOPTBK"), EImageBlitMode::OPAQUE);
|
||||
@@ -208,17 +232,7 @@ AssetGenerator::CanvasPtr AssetGenerator::createPlayerColoredBackground(const Pl
|
||||
std::shared_ptr<IImage> texture = ENGINE->renderHandler().loadImage(locator);
|
||||
|
||||
// transform to make color of brown DIBOX.PCX texture match color of specified player
|
||||
auto filterSettings = LIBRARY->settingsHandler->getFullConfig()["interface"]["playerColoredBackground"];
|
||||
static const std::array<ColorFilter, PlayerColor::PLAYER_LIMIT_I> filters = {
|
||||
ColorFilter::genRangeShifter( filterSettings["red" ].convertTo<std::vector<float>>() ),
|
||||
ColorFilter::genRangeShifter( filterSettings["blue" ].convertTo<std::vector<float>>() ),
|
||||
ColorFilter::genRangeShifter( filterSettings["tan" ].convertTo<std::vector<float>>() ),
|
||||
ColorFilter::genRangeShifter( filterSettings["green" ].convertTo<std::vector<float>>() ),
|
||||
ColorFilter::genRangeShifter( filterSettings["orange"].convertTo<std::vector<float>>() ),
|
||||
ColorFilter::genRangeShifter( filterSettings["purple"].convertTo<std::vector<float>>() ),
|
||||
ColorFilter::genRangeShifter( filterSettings["teal" ].convertTo<std::vector<float>>() ),
|
||||
ColorFilter::genRangeShifter( filterSettings["pink" ].convertTo<std::vector<float>>() )
|
||||
};
|
||||
static const std::array<ColorFilter, PlayerColor::PLAYER_LIMIT_I> filters = getColorFilters();
|
||||
|
||||
assert(player.isValidPlayer());
|
||||
if (!player.isValidPlayer())
|
||||
@@ -884,3 +898,67 @@ AssetGenerator::AnimationLayoutMap AssetGenerator::createGSPButtonClear()
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
AssetGenerator::CanvasPtr AssetGenerator::createGateListColored(PlayerColor color, PlayerColor backColor) const
|
||||
{
|
||||
auto locator = ImageLocator(ImagePath::builtin("TpGate"), EImageBlitMode::COLORKEY);
|
||||
std::shared_ptr<IImage> img = ENGINE->renderHandler().loadImage(locator);
|
||||
img->playerColored(color);
|
||||
std::shared_ptr<IImage> imgColored = ENGINE->renderHandler().loadImage(locator);
|
||||
static const std::array<ColorFilter, PlayerColor::PLAYER_LIMIT_I> filters = getColorFilters();
|
||||
imgColored->adjustPalette(filters[backColor.getNum()], 0);
|
||||
|
||||
auto image = ENGINE->renderHandler().createImage(img->dimensions(), CanvasScalingPolicy::IGNORE);
|
||||
Canvas canvas = image->getCanvas();
|
||||
|
||||
canvas.draw(imgColored, Point(0, 0));
|
||||
|
||||
std::vector<Rect> keepOriginalRects = {
|
||||
Rect(0, 0, 14, 393),
|
||||
Rect(293, 0, 13, 393),
|
||||
Rect(0, 393, 8, 76),
|
||||
Rect(299, 393, 6, 76),
|
||||
Rect(0, 0, 306, 16),
|
||||
Rect(0, 383, 306, 10),
|
||||
Rect(0, 441, 306, 2),
|
||||
Rect(0, 462, 306, 7),
|
||||
// Edges
|
||||
Rect(14, 15, 2, 5),
|
||||
Rect(16, 15, 3, 2),
|
||||
Rect(16, 17, 1, 1),
|
||||
Rect(14, 379, 3, 4),
|
||||
Rect(16, 381, 2, 2),
|
||||
Rect(16, 380, 1, 1),
|
||||
Rect(289, 16, 2, 2),
|
||||
Rect(291, 16, 2, 4),
|
||||
Rect(289, 381, 2, 2),
|
||||
Rect(291, 379, 2, 4)
|
||||
};
|
||||
for(auto & rect : keepOriginalRects)
|
||||
canvas.draw(img, Point(rect.x, rect.y), rect);
|
||||
|
||||
std::vector<Rect> blackRect = {
|
||||
Rect(14, 401, 66, 32),
|
||||
Rect(227, 401, 66, 32)
|
||||
};
|
||||
for(auto & rect : blackRect)
|
||||
canvas.drawBorder(rect, Colors::BLACK);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
AssetGenerator::CanvasPtr AssetGenerator::createHeroSlotsColored(PlayerColor backColor) const
|
||||
{
|
||||
auto locator = ImageLocator(AnimationPath::builtin("OVSLOT"), 4, 0, EImageBlitMode::COLORKEY);
|
||||
std::shared_ptr<IImage> img = ENGINE->renderHandler().loadImage(locator);
|
||||
static const std::array<ColorFilter, PlayerColor::PLAYER_LIMIT_I> filters = getColorFilters();
|
||||
img->adjustPalette(filters[backColor.getNum()], 0);
|
||||
|
||||
auto image = ENGINE->renderHandler().createImage(Point(260, 150), CanvasScalingPolicy::IGNORE);
|
||||
Canvas canvas = image->getCanvas();
|
||||
canvas.draw(img, Point(0, 0), Rect(3, 4, 253, 107));
|
||||
for(int i = 0; i<7; i++)
|
||||
canvas.draw(img, Point(1 + i * 36, 108), Rect(76, 57, 35, 17));
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
@@ -66,6 +66,8 @@ private:
|
||||
CanvasPtr createCreatureInfoPanelElement(CreatureInfoPanelElement element) const;
|
||||
CanvasPtr createQuestWindow() const;
|
||||
AnimationLayoutMap createGSPButtonClear();
|
||||
CanvasPtr createGateListColored(PlayerColor color, PlayerColor backColor) const;
|
||||
CanvasPtr createHeroSlotsColored(PlayerColor backColor) const;
|
||||
|
||||
void createPaletteShiftedSprites();
|
||||
void generatePaletteShiftedAnimation(const AnimationPath & source, const std::vector<PaletteAnimation> & animation);
|
||||
|
||||
@@ -65,6 +65,8 @@ Rect CanvasImage::contentRect() const
|
||||
|
||||
Point CanvasImage::dimensions() const
|
||||
{
|
||||
if (scalingPolicy != CanvasScalingPolicy::IGNORE)
|
||||
return Point(surface->w, surface->h) / ENGINE->screenHandler().getScalingFactor();
|
||||
return {surface->w, surface->h};
|
||||
}
|
||||
|
||||
|
||||
@@ -200,9 +200,9 @@ void CTextInput::setFilterFilename()
|
||||
onTextFiltering = std::bind(&CTextInput::filenameFilter, _1, _2);
|
||||
}
|
||||
|
||||
void CTextInput::setFilterNumber(int minValue, int maxValue)
|
||||
void CTextInput::setFilterNumber(int minValue, int maxValue, int metricDigits)
|
||||
{
|
||||
onTextFiltering = std::bind(&CTextInput::numberFilter, _1, _2, minValue, maxValue);
|
||||
onTextFiltering = std::bind(&CTextInput::numberFilter, _1, _2, minValue, maxValue, metricDigits);
|
||||
}
|
||||
|
||||
std::string CTextInput::getVisibleText() const
|
||||
@@ -256,6 +256,10 @@ void CTextInput::keyPressed(EShortcut key)
|
||||
|
||||
if(redrawNeeded)
|
||||
{
|
||||
std::string oldText = currentText;
|
||||
if(onTextFiltering)
|
||||
onTextFiltering(currentText, oldText);
|
||||
|
||||
updateLabel();
|
||||
if(onTextEdited)
|
||||
onTextEdited(currentText);
|
||||
@@ -295,9 +299,9 @@ void CTextInput::textInputted(const std::string & enteredText)
|
||||
if(onTextFiltering)
|
||||
onTextFiltering(currentText, oldText);
|
||||
|
||||
updateLabel();
|
||||
if(currentText != oldText)
|
||||
{
|
||||
updateLabel();
|
||||
if(onTextEdited)
|
||||
onTextEdited(currentText);
|
||||
}
|
||||
@@ -321,40 +325,63 @@ void CTextInput::filenameFilter(std::string & text, const std::string &oldText)
|
||||
text.erase(pos, 1);
|
||||
}
|
||||
|
||||
void CTextInput::numberFilter(std::string & text, const std::string & oldText, int minValue, int maxValue)
|
||||
std::optional<char> getMetricSuffix(const std::string& text)
|
||||
{
|
||||
const std::string suffixes = "kKmMgGtTpPeE";
|
||||
std::vector<char> found;
|
||||
|
||||
// Collect all suffixes in the string
|
||||
for (char c : text) {
|
||||
if (suffixes.find(c) != std::string::npos) {
|
||||
// Normalize: 'k' lowercase, others uppercase
|
||||
found.push_back((c == 'k' || c == 'K') ? 'k' : static_cast<char>(std::toupper(c)));
|
||||
}
|
||||
}
|
||||
|
||||
if (found.empty()) return std::nullopt; // No suffix
|
||||
if (found.size() == 1) return found[0]; // Single suffix
|
||||
// More than one suffix
|
||||
bool allSame = std::all_of(found.begin(), found.end(), [&](char c){ return c == found[0]; });
|
||||
if (allSame) return std::nullopt; // Multiple but identical → nullopt
|
||||
return found.back(); // Multiple different → last suffix
|
||||
}
|
||||
|
||||
|
||||
void CTextInput::numberFilter(std::string & text, const std::string & oldText, int minValue, int maxValue, int metricDigits)
|
||||
{
|
||||
assert(minValue < maxValue);
|
||||
|
||||
if(text.empty())
|
||||
bool isNegative = std::count_if(text.begin(), text.end(), [](char c){ return c == '-'; }) == 1 && minValue < 0;
|
||||
auto suffix = getMetricSuffix(text);
|
||||
if(metricDigits == 0)
|
||||
suffix = std::nullopt;
|
||||
|
||||
// Remove all non-digit characters
|
||||
text.erase(std::remove_if(text.begin(), text.end(), [](char c){ return !isdigit(c); }), text.end());
|
||||
|
||||
// Remove leading zeros
|
||||
size_t firstNonZero = text.find_first_not_of('0');
|
||||
if (firstNonZero > 0)
|
||||
text.erase(0, firstNonZero);
|
||||
|
||||
if (text.empty())
|
||||
text = "0";
|
||||
|
||||
size_t pos = 0;
|
||||
if(text[0] == '-') //allow '-' sign as first symbol only
|
||||
pos++;
|
||||
// Add negative sign
|
||||
text = (isNegative ? "-" : "") + text;
|
||||
|
||||
while(pos < text.size())
|
||||
{
|
||||
if(text[pos] < '0' || text[pos] > '9')
|
||||
{
|
||||
text = oldText;
|
||||
return; //new text is not number.
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
try
|
||||
{
|
||||
int value = boost::lexical_cast<int>(text);
|
||||
if(value < minValue)
|
||||
text = std::to_string(minValue);
|
||||
else if(value > maxValue)
|
||||
text = std::to_string(maxValue);
|
||||
}
|
||||
catch(boost::bad_lexical_cast &)
|
||||
{
|
||||
//Should never happen. Unless I missed some cases
|
||||
logGlobal->warn("Warning: failed to convert %s to number!", text);
|
||||
text = oldText;
|
||||
}
|
||||
// Restore suffix if it exists
|
||||
if (suffix)
|
||||
text += *suffix;
|
||||
|
||||
// Clamp value
|
||||
int value = TextOperations::parseMetric<int>(text);
|
||||
if (metricDigits)
|
||||
text = (isNegative && value == 0 ? "-" : "") + TextOperations::formatMetric(value, metricDigits);
|
||||
if (value < minValue)
|
||||
text = metricDigits ? TextOperations::formatMetric(minValue, metricDigits) : std::to_string(minValue);
|
||||
else if (value > maxValue)
|
||||
text = metricDigits ? TextOperations::formatMetric(maxValue, metricDigits) : std::to_string(maxValue);
|
||||
}
|
||||
|
||||
void CTextInput::activate()
|
||||
|
||||
@@ -66,7 +66,7 @@ protected:
|
||||
static void filenameFilter(std::string & text, const std::string & oldText);
|
||||
//Filter that will allow only input of numbers in range min-max (min-max are allowed)
|
||||
//min-max should be set via something like std::bind
|
||||
static void numberFilter(std::string & text, const std::string & oldText, int minValue, int maxValue);
|
||||
static void numberFilter(std::string & text, const std::string & oldText, int minValue, int maxValue, int metricDigits);
|
||||
|
||||
std::string getVisibleText() const;
|
||||
void createLabel(bool giveFocusToInput);
|
||||
@@ -99,7 +99,7 @@ public:
|
||||
/// Enables filtering entered text that ensures that text is valid filename (existing or not)
|
||||
void setFilterFilename();
|
||||
/// Enable filtering entered text that ensures that text is valid number in provided range [min, max]
|
||||
void setFilterNumber(int minValue, int maxValue);
|
||||
void setFilterNumber(int minValue, int maxValue, int metricDigits=0);
|
||||
|
||||
void setFont(EFonts Font);
|
||||
void setColor(const ColorRGBA & Color);
|
||||
|
||||
@@ -46,7 +46,7 @@ CPicture::CPicture(std::shared_ptr<IImage> image, const Point & position)
|
||||
pos.w = bg->width();
|
||||
pos.h = bg->height();
|
||||
|
||||
addUsedEvents(SHOW_POPUP);
|
||||
addUsedEvents(LCLICK | SHOW_POPUP);
|
||||
}
|
||||
|
||||
CPicture::CPicture( const ImagePath &bmpname, int x, int y )
|
||||
@@ -75,7 +75,7 @@ CPicture::CPicture( const ImagePath & bmpname, const Point & position, EImageBli
|
||||
pos.w = pos.h = 0;
|
||||
}
|
||||
|
||||
addUsedEvents(SHOW_POPUP);
|
||||
addUsedEvents(LCLICK | SHOW_POPUP);
|
||||
}
|
||||
|
||||
CPicture::CPicture( const ImagePath & bmpname, const Point & position )
|
||||
@@ -89,7 +89,7 @@ CPicture::CPicture(const ImagePath & bmpname, const Rect &SrcRect, int x, int y)
|
||||
pos.w = srcRect->w;
|
||||
pos.h = srcRect->h;
|
||||
|
||||
addUsedEvents(SHOW_POPUP);
|
||||
addUsedEvents(LCLICK | SHOW_POPUP);
|
||||
}
|
||||
|
||||
CPicture::CPicture(std::shared_ptr<IImage> image, const Rect &SrcRect, int x, int y)
|
||||
@@ -99,7 +99,7 @@ CPicture::CPicture(std::shared_ptr<IImage> image, const Rect &SrcRect, int x, in
|
||||
pos.w = srcRect->w;
|
||||
pos.h = srcRect->h;
|
||||
|
||||
addUsedEvents(SHOW_POPUP);
|
||||
addUsedEvents(LCLICK | SHOW_POPUP);
|
||||
}
|
||||
|
||||
void CPicture::show(Canvas & to)
|
||||
@@ -137,11 +137,22 @@ void CPicture::setPlayerColor(PlayerColor player)
|
||||
bg->playerColored(player);
|
||||
}
|
||||
|
||||
void CPicture::addLClickCallback(const std::function<void()> & callback)
|
||||
{
|
||||
lCallback = callback;
|
||||
}
|
||||
|
||||
void CPicture::addRClickCallback(const std::function<void()> & callback)
|
||||
{
|
||||
rCallback = callback;
|
||||
}
|
||||
|
||||
void CPicture::clickPressed(const Point & cursorPosition)
|
||||
{
|
||||
if(lCallback)
|
||||
lCallback();
|
||||
}
|
||||
|
||||
void CPicture::showPopupWindow(const Point & cursorPosition)
|
||||
{
|
||||
if(rCallback)
|
||||
|
||||
@@ -27,6 +27,7 @@ enum class EImageBlitMode : uint8_t;
|
||||
class CPicture : public CIntObject
|
||||
{
|
||||
std::shared_ptr<IImage> bg;
|
||||
std::function<void()> lCallback;
|
||||
std::function<void()> rCallback;
|
||||
|
||||
public:
|
||||
@@ -60,10 +61,12 @@ public:
|
||||
void scaleTo(Point size);
|
||||
void setPlayerColor(PlayerColor player);
|
||||
|
||||
void addLClickCallback(const std::function<void()> & callback);
|
||||
void addRClickCallback(const std::function<void()> & callback);
|
||||
|
||||
void show(Canvas & to) override;
|
||||
void showAll(Canvas & to) override;
|
||||
void clickPressed(const Point & cursorPosition) override;
|
||||
void showPopupWindow(const Point & cursorPosition) override;
|
||||
};
|
||||
|
||||
|
||||
@@ -623,13 +623,15 @@ void MoraleLuckBox::set(const AFactionMember * node)
|
||||
else if(morale && node && node->getBonusBearer()->hasBonusOfType(BonusType::NO_MORALE))
|
||||
{
|
||||
auto noMorale = node->getBonusBearer()->getBonus(Selector::type()(BonusType::NO_MORALE));
|
||||
text += "\n" + noMorale->Description(GAME->interface()->cb.get());
|
||||
if(GAME->interface())
|
||||
text += "\n" + noMorale->Description(GAME->interface()->cb.get());
|
||||
component.value = 0;
|
||||
}
|
||||
else if (!morale && node && node->getBonusBearer()->hasBonusOfType(BonusType::NO_LUCK))
|
||||
{
|
||||
auto noLuck = node->getBonusBearer()->getBonus(Selector::type()(BonusType::NO_LUCK));
|
||||
text += "\n" + noLuck->Description(GAME->interface()->cb.get());
|
||||
if(GAME->interface())
|
||||
text += "\n" + noLuck->Description(GAME->interface()->cb.get());
|
||||
component.value = 0;
|
||||
}
|
||||
else
|
||||
@@ -637,7 +639,7 @@ void MoraleLuckBox::set(const AFactionMember * node)
|
||||
std::string addInfo = "";
|
||||
for(auto & bonus : * modifierList)
|
||||
{
|
||||
if(bonus->val) {
|
||||
if(GAME->interface() && bonus->val) {
|
||||
const std::string& description = bonus->Description(GAME->interface()->cb.get());
|
||||
//arraytxt already contains \n
|
||||
if (description.size() && description[0] != '\n')
|
||||
|
||||
@@ -2184,8 +2184,16 @@ void CMageGuildScreen::updateSpells(ObjectInstanceID tID)
|
||||
uint32_t spellCount = town->spellsAtLevel(i+1,false); //spell at level with -1 hmmm?
|
||||
for(uint32_t j=0; j<spellCount; j++)
|
||||
{
|
||||
if(i<town->mageGuildLevel() && town->spells[i].size()>j)
|
||||
if (town->hasBuilt(BuildingSubID::AURORA_BOREALIS))
|
||||
{
|
||||
std::string auroraBorealisName = town->getTown()->getSpecialBuilding(BuildingSubID::AURORA_BOREALIS)->getNameTranslated();
|
||||
|
||||
auroraBorealisScrolls.push_back(std::make_shared<ScrollAllSpells>(positions[i][j], auroraBorealisName));
|
||||
}
|
||||
else if(i<town->mageGuildLevel() && town->spells[i].size()>j)
|
||||
{
|
||||
spells.push_back(std::make_shared<Scroll>(positions[i][j], town->spells[i][j].toSpell(), townId));
|
||||
}
|
||||
else
|
||||
emptyScrolls.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("TPMAGES.DEF"), 1, 0, positions[i][j].x, positions[i][j].y));
|
||||
}
|
||||
@@ -2194,6 +2202,22 @@ void CMageGuildScreen::updateSpells(ObjectInstanceID tID)
|
||||
redraw();
|
||||
}
|
||||
|
||||
CMageGuildScreen::ScrollAllSpells::ScrollAllSpells(Point position, const std::string & buildingName)
|
||||
{
|
||||
constexpr int auroraBorealisImageIndex = 70;
|
||||
|
||||
OBJECT_CONSTRUCTION;
|
||||
pos += position;
|
||||
image = std::make_shared<CAnimImage>(AnimationPath::builtin("SPELLSCR"), auroraBorealisImageIndex);
|
||||
pos = image->pos;
|
||||
|
||||
MetaString description;
|
||||
description.appendTextID("core.genrltxt.714");
|
||||
description.replaceRawString(buildingName);
|
||||
|
||||
text = std::make_shared<LRClickableAreaWText>(Rect(Point(), pos.dimensions()), description.toString(), description.toString() );
|
||||
}
|
||||
|
||||
CMageGuildScreen::Scroll::Scroll(Point position, const CSpell *Spell, ObjectInstanceID townId)
|
||||
: spell(Spell), townId(townId)
|
||||
{
|
||||
|
||||
@@ -37,6 +37,7 @@ class CGarrisonInt;
|
||||
class CComponent;
|
||||
class CComponentBox;
|
||||
class LRClickableArea;
|
||||
class LRClickableAreaWText;
|
||||
class CTextInputWithConfirm;
|
||||
|
||||
/// Building "button"
|
||||
@@ -394,10 +395,21 @@ class CMageGuildScreen : public CStatusbarWindow
|
||||
void showPopupWindow(const Point & cursorPosition) override;
|
||||
void hover(bool on) override;
|
||||
};
|
||||
|
||||
class ScrollAllSpells : public CIntObject
|
||||
{
|
||||
std::shared_ptr<CAnimImage> image;
|
||||
std::shared_ptr<LRClickableAreaWText> text;
|
||||
|
||||
public:
|
||||
ScrollAllSpells(Point position, const std::string & buildingName);
|
||||
};
|
||||
|
||||
std::shared_ptr<CPicture> window;
|
||||
std::shared_ptr<CButton> exit;
|
||||
std::vector<std::shared_ptr<Scroll>> spells;
|
||||
std::vector<std::shared_ptr<CAnimImage>> emptyScrolls;
|
||||
std::vector<std::shared_ptr<ScrollAllSpells>> auroraBorealisScrolls;
|
||||
|
||||
std::shared_ptr<CMinorResDataBar> resdatabar;
|
||||
|
||||
|
||||
@@ -428,7 +428,7 @@ CStackWindow::ButtonsSection::ButtonsSection(CStackWindow * owner, int yOffset)
|
||||
};
|
||||
|
||||
std::string tooltipText = "vcmi.creatureWindow." + btnIDs[buttonIndex];
|
||||
parent->switchButtons[buttonIndex] = std::make_shared<CButton>(Point(302 + (int)buttonIndex*40, 5), AnimationPath::builtin("stackWindow/upgradeButton"), CButton::tooltipLocalized(tooltipText), onSwitch);
|
||||
parent->switchButtons[buttonIndex] = std::make_shared<CButton>(Point(342, 5), AnimationPath::builtin("stackWindow/upgradeButton"), CButton::tooltipLocalized(tooltipText), onSwitch);
|
||||
parent->switchButtons[buttonIndex]->setOverlay(std::make_shared<CAnimImage>(AnimationPath::builtin("stackWindow/switchModeIcons"), buttonIndex));
|
||||
}
|
||||
parent->switchButtons[parent->activeTab]->disable();
|
||||
@@ -952,8 +952,8 @@ void CStackWindow::initSections()
|
||||
{
|
||||
OBJECT_CONSTRUCTION;
|
||||
|
||||
bool showArt = GAME->interface()->cb->getSettings().getBoolean(EGameSettings::MODULE_STACK_ARTIFACT) && info->commander == nullptr && info->stackNode;
|
||||
bool showExp = (GAME->interface()->cb->getSettings().getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE) || info->commander != nullptr) && info->stackNode;
|
||||
bool showArt = GAME->interface() && GAME->interface()->cb->getSettings().getBoolean(EGameSettings::MODULE_STACK_ARTIFACT) && info->commander == nullptr && info->stackNode;
|
||||
bool showExp = ((GAME->interface() && GAME->interface()->cb->getSettings().getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE)) || info->commander != nullptr) && info->stackNode;
|
||||
|
||||
mainSection = std::make_shared<MainSection>(this, pos.h, showExp, showArt);
|
||||
|
||||
|
||||
@@ -214,8 +214,8 @@ void CHeroOverview::genControls()
|
||||
{
|
||||
secSkills.push_back(std::make_shared<CSecSkillPlace>(Point(302, 7 * borderOffset + yOffset + 186 + i * (32 + borderOffset)),
|
||||
CSecSkillPlace::ImageSize::SMALL, skill.first, skill.second));
|
||||
labelSecSkillsNames.push_back(std::make_shared<CLabel>(334 + 2 * borderOffset, 8 * borderOffset + yOffset + 186 + i * (32 + borderOffset) - 5, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, LIBRARY->generaltexth->levels[skill.second - 1]));
|
||||
labelSecSkillsNames.push_back(std::make_shared<CLabel>(334 + 2 * borderOffset, 8 * borderOffset + yOffset + 186 + i * (32 + borderOffset) + 10, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, (*LIBRARY->skillh)[skill.first]->getNameTranslated()));
|
||||
labelSecSkillsNames.push_back(std::make_shared<CLabel>(334 + 2 * borderOffset, 8 * borderOffset + yOffset + 186 + i * (32 + borderOffset) - 5, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, LIBRARY->generaltexth->levels[skill.second - 1], 90));
|
||||
labelSecSkillsNames.push_back(std::make_shared<CLabel>(334 + 2 * borderOffset, 8 * borderOffset + yOffset + 186 + i * (32 + borderOffset) + 10, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, (*LIBRARY->skillh)[skill.first]->getNameTranslated(), 90));
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -240,7 +240,8 @@ void CHeroOverview::genControls()
|
||||
}
|
||||
|
||||
imageSpells.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("SPELLBON"), (*LIBRARY->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<CLabel>(302 + (292 / 2) + 3 * borderOffset + 32 + borderOffset, 8 * borderOffset + yOffset + 186 + i * (32 + borderOffset) + 3, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, (*LIBRARY->spellh)[spell]->getNameTranslated()));
|
||||
Rect labelPos(302 + (292 / 2) + 2 * borderOffset + 32 + borderOffset + 5, 7 * borderOffset + yOffset + 186 + i * (32 + borderOffset), (292 / 2) - 32 - 3 * borderOffset - 10, 32);
|
||||
labelSpellsNames.push_back(std::make_shared<CMultiLineLabel>(labelPos, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::WHITE, (*LIBRARY->spellh)[spell]->getNameTranslated()));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ class CHeroOverview : public CWindowObject
|
||||
|
||||
std::shared_ptr<CLabel> labelSpellTitle;
|
||||
std::vector<std::shared_ptr<CAnimImage>> imageSpells;
|
||||
std::vector<std::shared_ptr<CLabel>> labelSpellsNames;
|
||||
std::vector<std::shared_ptr<CMultiLineLabel>> labelSpellsNames;
|
||||
|
||||
std::shared_ptr<CLabel> labelSecSkillTitle;
|
||||
std::vector<std::shared_ptr<CSecSkillPlace>> secSkills;
|
||||
@@ -69,4 +69,4 @@ class CHeroOverview : public CWindowObject
|
||||
|
||||
public:
|
||||
CHeroOverview(const HeroTypeID & h);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/VideoWidget.h"
|
||||
#include "../adventureMap/AdventureMapInterface.h"
|
||||
#include "../eventsSDL/InputHandler.h"
|
||||
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/GameConstants.h"
|
||||
@@ -99,6 +100,7 @@ CSpellWindow::InteractiveArea::InteractiveArea(const Rect & myRect, const std::f
|
||||
|
||||
void CSpellWindow::InteractiveArea::clickPressed(const Point & cursorPosition)
|
||||
{
|
||||
ENGINE->input().hapticFeedback();
|
||||
onLeft();
|
||||
}
|
||||
|
||||
@@ -687,6 +689,8 @@ void CSpellWindow::SpellArea::clickPressed(const Point & cursorPosition)
|
||||
{
|
||||
if(mySpell)
|
||||
{
|
||||
ENGINE->input().hapticFeedback();
|
||||
|
||||
if(owner->onSpellSelect)
|
||||
{
|
||||
owner->onSpellSelect(mySpell->id);
|
||||
|
||||
@@ -86,7 +86,9 @@ std::shared_ptr<CPicture> CWindowObject::createBg(const ImagePath & imageName, b
|
||||
return nullptr;
|
||||
|
||||
auto image = std::make_shared<CPicture>(imageName, Point(0,0), EImageBlitMode::OPAQUE);
|
||||
if(playerColored && GAME->interface())
|
||||
if(!GAME->interface())
|
||||
image->setPlayerColor(PlayerColor(1)); // in main menu we use blue
|
||||
else if(playerColored)
|
||||
image->setPlayerColor(GAME->interface()->playerID);
|
||||
return image;
|
||||
}
|
||||
|
||||
@@ -1535,8 +1535,9 @@ CObjectListWindow::CItem::CItem(CObjectListWindow * _parent, size_t _id, std::st
|
||||
index(_id)
|
||||
{
|
||||
OBJECT_CONSTRUCTION;
|
||||
if(parent->images.size() > index)
|
||||
icon = std::make_shared<CPicture>(parent->images[index], Point(1, 1));
|
||||
auto imgIndex = parent->itemsVisible[index].first;
|
||||
if(parent->images.size() > index && parent->images[imgIndex])
|
||||
icon = std::make_shared<CPicture>(parent->images[imgIndex], Point(1, 1));
|
||||
border = std::make_shared<CPicture>(ImagePath::builtin("TPGATES"));
|
||||
pos = border->pos;
|
||||
|
||||
@@ -1577,12 +1578,13 @@ void CObjectListWindow::CItem::clickDouble(const Point & cursorPosition)
|
||||
|
||||
void CObjectListWindow::CItem::showPopupWindow(const Point & cursorPosition)
|
||||
{
|
||||
int where = parent->itemsVisible[index].first;
|
||||
if(parent->onPopup)
|
||||
parent->onPopup(index);
|
||||
parent->onPopup(where);
|
||||
}
|
||||
|
||||
CObjectListWindow::CObjectListWindow(const std::vector<int> & _items, std::shared_ptr<CIntObject> titleWidget_, std::string _title, std::string _descr, std::function<void(int)> Callback, size_t initialSelection, std::vector<std::shared_ptr<IImage>> images, bool searchBoxEnabled)
|
||||
: CWindowObject(PLAYER_COLORED, ImagePath::builtin("TPGATE")),
|
||||
CObjectListWindow::CObjectListWindow(const std::vector<int> & _items, std::shared_ptr<CIntObject> titleWidget_, std::string _title, std::string _descr, std::function<void(int)> Callback, size_t initialSelection, std::vector<std::shared_ptr<IImage>> images, bool searchBoxEnabled, bool blue)
|
||||
: CWindowObject(PLAYER_COLORED, ImagePath::builtin(blue ? "TownPortalBackgroundBlue" : "TPGATE")),
|
||||
onSelect(Callback),
|
||||
selected(initialSelection),
|
||||
images(images)
|
||||
@@ -1601,12 +1603,12 @@ CObjectListWindow::CObjectListWindow(const std::vector<int> & _items, std::share
|
||||
}
|
||||
itemsVisible = items;
|
||||
|
||||
init(titleWidget_, _title, _descr, searchBoxEnabled);
|
||||
init(titleWidget_, _title, _descr, searchBoxEnabled, blue);
|
||||
list->scrollTo(std::min(static_cast<int>(initialSelection + 4), static_cast<int>(items.size() - 1))); // 4 is for centering (list have 9 elements)
|
||||
}
|
||||
|
||||
CObjectListWindow::CObjectListWindow(const std::vector<std::string> & _items, std::shared_ptr<CIntObject> titleWidget_, std::string _title, std::string _descr, std::function<void(int)> Callback, size_t initialSelection, std::vector<std::shared_ptr<IImage>> images, bool searchBoxEnabled)
|
||||
: CWindowObject(PLAYER_COLORED, ImagePath::builtin("TPGATE")),
|
||||
CObjectListWindow::CObjectListWindow(const std::vector<std::string> & _items, std::shared_ptr<CIntObject> titleWidget_, std::string _title, std::string _descr, std::function<void(int)> Callback, size_t initialSelection, std::vector<std::shared_ptr<IImage>> images, bool searchBoxEnabled, bool blue)
|
||||
: CWindowObject(PLAYER_COLORED, ImagePath::builtin(blue ? "TownPortalBackgroundBlue" : "TPGATE")),
|
||||
onSelect(Callback),
|
||||
selected(initialSelection),
|
||||
images(images)
|
||||
@@ -1625,17 +1627,17 @@ CObjectListWindow::CObjectListWindow(const std::vector<std::string> & _items, st
|
||||
}
|
||||
itemsVisible = items;
|
||||
|
||||
init(titleWidget_, _title, _descr, searchBoxEnabled);
|
||||
init(titleWidget_, _title, _descr, searchBoxEnabled, blue);
|
||||
list->scrollTo(std::min(static_cast<int>(initialSelection + 4), static_cast<int>(items.size() - 1))); // 4 is for centering (list have 9 elements)
|
||||
}
|
||||
|
||||
void CObjectListWindow::init(std::shared_ptr<CIntObject> titleWidget_, std::string _title, std::string _descr, bool searchBoxEnabled)
|
||||
void CObjectListWindow::init(std::shared_ptr<CIntObject> titleWidget_, std::string _title, std::string _descr, bool searchBoxEnabled, bool blue)
|
||||
{
|
||||
titleWidget = titleWidget_;
|
||||
|
||||
title = std::make_shared<CLabel>(152, 27, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, _title);
|
||||
descr = std::make_shared<CLabel>(145, 133, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, _descr);
|
||||
exit = std::make_shared<CButton>( Point(228, 402), AnimationPath::builtin("ICANCEL.DEF"), CButton::tooltip(), std::bind(&CObjectListWindow::exitPressed, this), EShortcut::GLOBAL_CANCEL);
|
||||
exit = std::make_shared<CButton>( Point(228, 402), AnimationPath::builtin(blue ? "MuBcanc" : "ICANCEL.DEF"), CButton::tooltip(), std::bind(&CObjectListWindow::exitPressed, this), EShortcut::GLOBAL_CANCEL);
|
||||
|
||||
if(titleWidget)
|
||||
{
|
||||
@@ -1644,10 +1646,10 @@ void CObjectListWindow::init(std::shared_ptr<CIntObject> titleWidget_, std::stri
|
||||
titleWidget->pos.y = 75 + pos.y - titleWidget->pos.h/2;
|
||||
}
|
||||
list = std::make_shared<CListBox>(std::bind(&CObjectListWindow::genItem, this, _1),
|
||||
Point(14, 151), Point(0, 25), 9, itemsVisible.size(), 0, 1, Rect(262, -32, 256, 256) );
|
||||
Point(14, 151), Point(0, 25), 9, itemsVisible.size(), 0, 1 + (blue ? 4 : 0), Rect(262, -32, 256, 256) );
|
||||
list->setRedrawParent(true);
|
||||
|
||||
ok = std::make_shared<CButton>(Point(15, 402), AnimationPath::builtin("IOKAY.DEF"), CButton::tooltip(), std::bind(&CObjectListWindow::elementSelected, this), EShortcut::GLOBAL_ACCEPT);
|
||||
ok = std::make_shared<CButton>(Point(15, 402), AnimationPath::builtin(blue ? "MuBchck" : "IOKAY.DEF"), CButton::tooltip(), std::bind(&CObjectListWindow::elementSelected, this), EShortcut::GLOBAL_ACCEPT);
|
||||
ok->block(!list->size());
|
||||
|
||||
if(!searchBoxEnabled)
|
||||
@@ -1655,8 +1657,8 @@ void CObjectListWindow::init(std::shared_ptr<CIntObject> titleWidget_, std::stri
|
||||
|
||||
Rect r(50, 90, pos.w - 100, 16);
|
||||
const ColorRGBA rectangleColor = ColorRGBA(0, 0, 0, 75);
|
||||
const ColorRGBA borderColor = ColorRGBA(128, 100, 75);
|
||||
const ColorRGBA grayedColor = ColorRGBA(158, 130, 105);
|
||||
const ColorRGBA borderColor = blue ? ColorRGBA(75, 84, 128) : ColorRGBA(128, 100, 75);
|
||||
const ColorRGBA grayedColor = blue ? ColorRGBA(105, 127, 159) : ColorRGBA(158, 130, 105);
|
||||
searchBoxRectangle = std::make_shared<TransparentFilledRectangle>(r.resize(1), rectangleColor, borderColor);
|
||||
searchBoxDescription = std::make_shared<CLabel>(r.center().x, r.center().y, FONT_SMALL, ETextAlignment::CENTER, grayedColor, LIBRARY->generaltexth->translate("vcmi.spellBook.search"));
|
||||
|
||||
@@ -1676,9 +1678,12 @@ void CObjectListWindow::trimTextIfTooWide(std::string & text, bool preserveCount
|
||||
{
|
||||
auto posBrace = text.find_last_of("(");
|
||||
auto posClosing = text.find_last_of(")");
|
||||
std::string objCount = text.substr(posBrace, posClosing - posBrace) + ')';
|
||||
suffix += " ";
|
||||
suffix += objCount;
|
||||
if (posBrace != std::string::npos && posClosing != std::string::npos && posClosing > posBrace)
|
||||
{
|
||||
std::string objCount = text.substr(posBrace, posClosing - posBrace) + ')';
|
||||
suffix += " ";
|
||||
suffix += objCount;
|
||||
}
|
||||
}
|
||||
|
||||
const auto & font = ENGINE->renderHandler().loadFont(FONT_SMALL);
|
||||
|
||||
@@ -205,7 +205,7 @@ class CObjectListWindow : public CWindowObject
|
||||
std::vector< std::pair<int, std::string> > items; //all items present in list
|
||||
std::vector< std::pair<int, std::string> > itemsVisible; //visible items present in list
|
||||
|
||||
void init(std::shared_ptr<CIntObject> titleWidget_, std::string _title, std::string _descr, bool searchBoxEnabled);
|
||||
void init(std::shared_ptr<CIntObject> titleWidget_, std::string _title, std::string _descr, bool searchBoxEnabled, bool blue);
|
||||
void trimTextIfTooWide(std::string & text, bool preserveCountSuffix) const; // trim item's text to fit within window's width
|
||||
void itemsSearchCallback(const std::string & text);
|
||||
void exitPressed();
|
||||
@@ -219,8 +219,8 @@ public:
|
||||
/// Callback will be called when OK button is pressed, returns id of selected item. initState = initially selected item
|
||||
/// Image can be nullptr
|
||||
///item names will be taken from map objects
|
||||
CObjectListWindow(const std::vector<int> &_items, std::shared_ptr<CIntObject> titleWidget_, std::string _title, std::string _descr, std::function<void(int)> Callback, size_t initialSelection = 0, std::vector<std::shared_ptr<IImage>> images = {}, bool searchBoxEnabled = false);
|
||||
CObjectListWindow(const std::vector<std::string> &_items, std::shared_ptr<CIntObject> titleWidget_, std::string _title, std::string _descr, std::function<void(int)> Callback, size_t initialSelection = 0, std::vector<std::shared_ptr<IImage>> images = {}, bool searchBoxEnabled = false);
|
||||
CObjectListWindow(const std::vector<int> &_items, std::shared_ptr<CIntObject> titleWidget_, std::string _title, std::string _descr, std::function<void(int)> Callback, size_t initialSelection = 0, std::vector<std::shared_ptr<IImage>> images = {}, bool searchBoxEnabled = false, bool blue = false);
|
||||
CObjectListWindow(const std::vector<std::string> &_items, std::shared_ptr<CIntObject> titleWidget_, std::string _title, std::string _descr, std::function<void(int)> Callback, size_t initialSelection = 0, std::vector<std::shared_ptr<IImage>> images = {}, bool searchBoxEnabled = false, bool blue = false);
|
||||
|
||||
std::shared_ptr<CIntObject> genItem(size_t index);
|
||||
void elementSelected();//call callback and close this window
|
||||
|
||||
Reference in New Issue
Block a user