mirror of
https://github.com/vcmi/vcmi.git
synced 2025-06-04 23:17:41 +02:00
Merge branch 'vcmi/master' into 'vcmi/develop'
This commit is contained in:
commit
df83fa33a1
10
.github/workflows/github.yml
vendored
10
.github/workflows/github.yml
vendored
@ -20,11 +20,11 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- platform: linux-qt6
|
||||
os: ubuntu-22.04
|
||||
os: ubuntu-24.04
|
||||
test: 0
|
||||
preset: linux-clang-test
|
||||
- platform: linux
|
||||
os: ubuntu-22.04
|
||||
os: ubuntu-24.04
|
||||
test: 1
|
||||
preset: linux-gcc-test
|
||||
- platform: linux
|
||||
@ -124,7 +124,7 @@ jobs:
|
||||
# also, running it on multiple presets is redundant and slightly increases already long CI built times
|
||||
if: ${{ startsWith(matrix.preset, 'linux-clang-test') }}
|
||||
run: |
|
||||
pip3 install jstyleson
|
||||
sudo apt install python3-jstyleson
|
||||
python3 CI/linux-qt6/validate_json.py
|
||||
|
||||
- name: Dependencies
|
||||
@ -201,8 +201,8 @@ jobs:
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
if [[ ${{matrix.preset}} == linux-gcc-test ]]; then GCC12=1; fi
|
||||
cmake -DENABLE_CCACHE:BOOL=ON --preset ${{ matrix.preset }} ${GCC12:+-DCMAKE_C_COMPILER=gcc-12 -DCMAKE_CXX_COMPILER=g++-12}
|
||||
if [[ ${{matrix.preset}} == linux-gcc-test ]]; then GCC14=1; fi
|
||||
cmake -DENABLE_CCACHE:BOOL=ON --preset ${{ matrix.preset }} ${GCC14:+-DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14}
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
|
@ -21,7 +21,6 @@
|
||||
#include "../../lib/CTownHandler.h"
|
||||
#include "../../lib/mapObjects/MiscObjects.h"
|
||||
#include "../../lib/spells/CSpellHandler.h"
|
||||
#include "../../lib/CondSh.h"
|
||||
#include "Pathfinding/AIPathfinder.h"
|
||||
#include "Engine/Nullkiller.h"
|
||||
|
||||
|
@ -15,233 +15,15 @@
|
||||
#include "../Goals/Composition.h"
|
||||
#include "../Goals/ExecuteHeroChain.h"
|
||||
#include "../Markers/ExplorationPoint.h"
|
||||
#include "CaptureObjectsBehavior.h"
|
||||
#include "../Goals/CaptureObject.h"
|
||||
#include "../../../lib/CPlayerState.h"
|
||||
#include "../Goals/ExploreNeighbourTile.h"
|
||||
#include "../Helpers/ExplorationHelper.h"
|
||||
|
||||
namespace NKAI
|
||||
{
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
struct ExplorationHelper
|
||||
{
|
||||
const CGHeroInstance * hero;
|
||||
int sightRadius;
|
||||
float bestValue;
|
||||
TSubgoal bestGoal;
|
||||
int3 bestTile;
|
||||
int bestTilesDiscovered;
|
||||
const Nullkiller * ai;
|
||||
CCallback * cbp;
|
||||
const TeamState * ts;
|
||||
int3 ourPos;
|
||||
bool allowDeadEndCancellation;
|
||||
|
||||
ExplorationHelper(const CGHeroInstance * hero, const Nullkiller * ai)
|
||||
:ai(ai), cbp(ai->cb.get()), hero(hero)
|
||||
{
|
||||
ts = cbp->getPlayerTeam(ai->playerID);
|
||||
sightRadius = hero->getSightRadius();
|
||||
bestGoal = sptr(Goals::Invalid());
|
||||
bestValue = 0;
|
||||
bestTilesDiscovered = 0;
|
||||
ourPos = hero->visitablePos();
|
||||
allowDeadEndCancellation = true;
|
||||
}
|
||||
|
||||
TSubgoal makeComposition() const
|
||||
{
|
||||
Composition c;
|
||||
c.addNext(ExplorationPoint(bestTile, bestTilesDiscovered));
|
||||
c.addNext(bestGoal);
|
||||
return sptr(c);
|
||||
}
|
||||
|
||||
void scanSector(int scanRadius)
|
||||
{
|
||||
int3 tile = int3(0, 0, ourPos.z);
|
||||
|
||||
const auto & slice = (*(ts->fogOfWarMap))[ourPos.z];
|
||||
|
||||
for(tile.x = ourPos.x - scanRadius; tile.x <= ourPos.x + scanRadius; tile.x++)
|
||||
{
|
||||
for(tile.y = ourPos.y - scanRadius; tile.y <= ourPos.y + scanRadius; tile.y++)
|
||||
{
|
||||
|
||||
if(cbp->isInTheMap(tile) && slice[tile.x][tile.y])
|
||||
{
|
||||
scanTile(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void scanMap()
|
||||
{
|
||||
int3 mapSize = cbp->getMapSize();
|
||||
int perimeter = 2 * sightRadius * (mapSize.x + mapSize.y);
|
||||
|
||||
std::vector<int3> from;
|
||||
std::vector<int3> to;
|
||||
|
||||
from.reserve(perimeter);
|
||||
to.reserve(perimeter);
|
||||
|
||||
foreach_tile_pos([&](const int3 & pos)
|
||||
{
|
||||
if((*(ts->fogOfWarMap))[pos.z][pos.x][pos.y])
|
||||
{
|
||||
bool hasInvisibleNeighbor = false;
|
||||
|
||||
foreach_neighbour(cbp, pos, [&](CCallback * cbp, int3 neighbour)
|
||||
{
|
||||
if(!(*(ts->fogOfWarMap))[neighbour.z][neighbour.x][neighbour.y])
|
||||
{
|
||||
hasInvisibleNeighbor = true;
|
||||
}
|
||||
});
|
||||
|
||||
if(hasInvisibleNeighbor)
|
||||
from.push_back(pos);
|
||||
}
|
||||
});
|
||||
|
||||
logAi->debug("Exploration scan visible area perimeter for hero %s", hero->getNameTranslated());
|
||||
|
||||
for(const int3 & tile : from)
|
||||
{
|
||||
scanTile(tile);
|
||||
}
|
||||
|
||||
if(!bestGoal->invalid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
allowDeadEndCancellation = false;
|
||||
|
||||
for(int i = 0; i < sightRadius; i++)
|
||||
{
|
||||
getVisibleNeighbours(from, to);
|
||||
vstd::concatenate(from, to);
|
||||
vstd::removeDuplicates(from);
|
||||
}
|
||||
|
||||
logAi->debug("Exploration scan all possible tiles for hero %s", hero->getNameTranslated());
|
||||
|
||||
for(const int3 & tile : from)
|
||||
{
|
||||
scanTile(tile);
|
||||
}
|
||||
}
|
||||
|
||||
void scanTile(const int3 & tile)
|
||||
{
|
||||
if(tile == ourPos
|
||||
|| !ai->pathfinder->isTileAccessible(hero, tile)) //shouldn't happen, but it does
|
||||
return;
|
||||
|
||||
int tilesDiscovered = howManyTilesWillBeDiscovered(tile);
|
||||
if(!tilesDiscovered)
|
||||
return;
|
||||
|
||||
auto paths = ai->pathfinder->getPathInfo(tile);
|
||||
auto waysToVisit = CaptureObjectsBehavior::getVisitGoals(paths, ai, ai->cb->getTopObj(tile));
|
||||
|
||||
for(int i = 0; i != paths.size(); i++)
|
||||
{
|
||||
auto & path = paths[i];
|
||||
auto goal = waysToVisit[i];
|
||||
|
||||
if(path.exchangeCount > 1 || path.targetHero != hero || path.movementCost() <= 0.0 || goal->invalid())
|
||||
continue;
|
||||
|
||||
float ourValue = (float)tilesDiscovered * tilesDiscovered / path.movementCost();
|
||||
|
||||
if(ourValue > bestValue) //avoid costly checks of tiles that don't reveal much
|
||||
{
|
||||
auto obj = cb->getTopObj(tile);
|
||||
|
||||
// picking up resources does not yield any exploration at all.
|
||||
// if it blocks the way to some explorable tile AIPathfinder will take care of it
|
||||
if(obj && obj->isBlockedVisitable())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(isSafeToVisit(hero, path.heroArmy, path.getTotalDanger()))
|
||||
{
|
||||
bestGoal = goal;
|
||||
bestValue = ourValue;
|
||||
bestTile = tile;
|
||||
bestTilesDiscovered = tilesDiscovered;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void getVisibleNeighbours(const std::vector<int3> & tiles, std::vector<int3> & out) const
|
||||
{
|
||||
for(const int3 & tile : tiles)
|
||||
{
|
||||
foreach_neighbour(cbp, tile, [&](CCallback * cbp, int3 neighbour)
|
||||
{
|
||||
if((*(ts->fogOfWarMap))[neighbour.z][neighbour.x][neighbour.y])
|
||||
{
|
||||
out.push_back(neighbour);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
int howManyTilesWillBeDiscovered(const int3 & pos) const
|
||||
{
|
||||
int ret = 0;
|
||||
int3 npos = int3(0, 0, pos.z);
|
||||
|
||||
const auto & slice = (*(ts->fogOfWarMap))[pos.z];
|
||||
|
||||
for(npos.x = pos.x - sightRadius; npos.x <= pos.x + sightRadius; npos.x++)
|
||||
{
|
||||
for(npos.y = pos.y - sightRadius; npos.y <= pos.y + sightRadius; npos.y++)
|
||||
{
|
||||
if(cbp->isInTheMap(npos)
|
||||
&& pos.dist2d(npos) - 0.5 < sightRadius
|
||||
&& !slice[npos.x][npos.y])
|
||||
{
|
||||
if(allowDeadEndCancellation
|
||||
&& !hasReachableNeighbor(npos))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool hasReachableNeighbor(const int3 & pos) const
|
||||
{
|
||||
for(const int3 & dir : int3::getDirs())
|
||||
{
|
||||
int3 tile = pos + dir;
|
||||
if(cbp->isInTheMap(tile))
|
||||
{
|
||||
auto isAccessible = ai->pathfinder->isTileAccessible(hero, tile);
|
||||
|
||||
if(isAccessible)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
std::string ExplorationBehavior::toString() const
|
||||
{
|
||||
return "Explore";
|
||||
@ -301,17 +83,13 @@ Goals::TGoalVec ExplorationBehavior::decompose(const Nullkiller * ai) const
|
||||
{
|
||||
ExplorationHelper scanResult(hero, ai);
|
||||
|
||||
scanResult.scanSector(1);
|
||||
|
||||
if(!scanResult.bestGoal->invalid())
|
||||
if(scanResult.scanSector(1))
|
||||
{
|
||||
tasks.push_back(scanResult.makeComposition());
|
||||
continue;
|
||||
}
|
||||
|
||||
scanResult.scanSector(15);
|
||||
|
||||
if(!scanResult.bestGoal->invalid())
|
||||
if(scanResult.scanSector(15))
|
||||
{
|
||||
tasks.push_back(scanResult.makeComposition());
|
||||
continue;
|
||||
@ -319,9 +97,7 @@ Goals::TGoalVec ExplorationBehavior::decompose(const Nullkiller * ai) const
|
||||
|
||||
if(ai->getScanDepth() == ScanDepth::ALL_FULL)
|
||||
{
|
||||
scanResult.scanMap();
|
||||
|
||||
if(!scanResult.bestGoal->invalid())
|
||||
if(scanResult.scanMap())
|
||||
{
|
||||
tasks.push_back(scanResult.makeComposition());
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ set(Nullkiller_SRCS
|
||||
Goals/ExchangeSwapTownHeroes.cpp
|
||||
Goals/CompleteQuest.cpp
|
||||
Goals/StayAtTown.cpp
|
||||
Goals/ExploreNeighbourTile.cpp
|
||||
Markers/ArmyUpgrade.cpp
|
||||
Markers/HeroExchange.cpp
|
||||
Markers/UnlockCluster.cpp
|
||||
@ -62,6 +63,7 @@ set(Nullkiller_SRCS
|
||||
Behaviors/StayAtTownBehavior.cpp
|
||||
Behaviors/ExplorationBehavior.cpp
|
||||
Helpers/ArmyFormation.cpp
|
||||
Helpers/ExplorationHelper.cpp
|
||||
AIGateway.cpp
|
||||
)
|
||||
|
||||
@ -113,6 +115,7 @@ set(Nullkiller_HEADERS
|
||||
Goals/CompleteQuest.h
|
||||
Goals/Goals.h
|
||||
Goals/StayAtTown.h
|
||||
Goals/ExploreNeighbourTile.h
|
||||
Markers/ArmyUpgrade.h
|
||||
Markers/HeroExchange.h
|
||||
Markers/UnlockCluster.h
|
||||
@ -135,6 +138,7 @@ set(Nullkiller_HEADERS
|
||||
Behaviors/StayAtTownBehavior.h
|
||||
Behaviors/ExplorationBehavior.h
|
||||
Helpers/ArmyFormation.h
|
||||
Helpers/ExplorationHelper.h
|
||||
AIGateway.h
|
||||
)
|
||||
|
||||
|
@ -75,7 +75,8 @@ namespace Goals
|
||||
STAY_AT_TOWN_BEHAVIOR,
|
||||
STAY_AT_TOWN,
|
||||
EXPLORATION_BEHAVIOR,
|
||||
EXPLORATION_POINT
|
||||
EXPLORATION_POINT,
|
||||
EXPLORE_NEIGHBOUR_TILE
|
||||
};
|
||||
|
||||
class DLL_EXPORT TSubgoal : public std::shared_ptr<AbstractGoal>
|
||||
|
@ -18,10 +18,11 @@ class AIGateway;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
template<typename T> class DLL_EXPORT CGoal : public AbstractGoal
|
||||
template<typename T>
|
||||
class DLL_EXPORT CGoal : public AbstractGoal
|
||||
{
|
||||
public:
|
||||
CGoal<T>(EGoals goal = INVALID) : AbstractGoal(goal)
|
||||
CGoal(EGoals goal = INVALID) : AbstractGoal(goal)
|
||||
{
|
||||
isAbstract = true;
|
||||
value = 0;
|
||||
@ -32,7 +33,7 @@ namespace Goals
|
||||
town = nullptr;
|
||||
}
|
||||
|
||||
CGoal<T> * clone() const override
|
||||
CGoal * clone() const override
|
||||
{
|
||||
return new T(static_cast<T const &>(*this)); //casting enforces template instantiation
|
||||
}
|
||||
@ -64,15 +65,16 @@ namespace Goals
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> class DLL_EXPORT ElementarGoal : public CGoal<T>, public ITask
|
||||
template<typename T>
|
||||
class DLL_EXPORT ElementarGoal : public CGoal<T>, public ITask
|
||||
{
|
||||
public:
|
||||
ElementarGoal<T>(EGoals goal = INVALID) : CGoal<T>(goal), ITask()
|
||||
ElementarGoal(EGoals goal = INVALID) : CGoal<T>(goal), ITask()
|
||||
{
|
||||
AbstractGoal::isAbstract = false;
|
||||
}
|
||||
|
||||
ElementarGoal<T>(const ElementarGoal<T> & other) : CGoal<T>(other), ITask(other)
|
||||
ElementarGoal(const ElementarGoal<T> & other) : CGoal<T>(other), ITask(other)
|
||||
{
|
||||
}
|
||||
|
||||
|
69
AI/Nullkiller/Goals/ExploreNeighbourTile.cpp
Normal file
69
AI/Nullkiller/Goals/ExploreNeighbourTile.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* ExploreNeighbourTile.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 "ExploreNeighbourTile.h"
|
||||
#include "../AIGateway.h"
|
||||
#include "../AIUtility.h"
|
||||
#include "../Helpers/ExplorationHelper.h"
|
||||
|
||||
|
||||
namespace NKAI
|
||||
{
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
bool ExploreNeighbourTile::operator==(const ExploreNeighbourTile & other) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void ExploreNeighbourTile::accept(AIGateway * ai)
|
||||
{
|
||||
ExplorationHelper h(hero, ai->nullkiller.get());
|
||||
|
||||
for(int i = 0; i < tilesToExplore && hero->movementPointsRemaining() > 0; i++)
|
||||
{
|
||||
int3 pos = hero->visitablePos();
|
||||
float value = 0;
|
||||
int3 target = int3(-1);
|
||||
foreach_neighbour(pos, [&](int3 tile)
|
||||
{
|
||||
auto pathInfo = ai->myCb->getPathsInfo(hero)->getPathInfo(tile);
|
||||
|
||||
if(pathInfo->turns > 0)
|
||||
return;
|
||||
|
||||
if(pathInfo->accessible == EPathAccessibility::ACCESSIBLE)
|
||||
{
|
||||
float newValue = h.howManyTilesWillBeDiscovered(tile);
|
||||
|
||||
newValue /= std::min(0.1f, pathInfo->getCost());
|
||||
|
||||
if(newValue > value)
|
||||
{
|
||||
value = newValue;
|
||||
target = tile;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if(!target.valid() || !ai->moveHeroToTile(target, hero))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string ExploreNeighbourTile::toString() const
|
||||
{
|
||||
return "Explore neighbour tiles by " + hero->getNameTranslated();
|
||||
}
|
||||
|
||||
}
|
45
AI/Nullkiller/Goals/ExploreNeighbourTile.h
Normal file
45
AI/Nullkiller/Goals/ExploreNeighbourTile.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* ExploreNeighbourTile.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 "CGoal.h"
|
||||
|
||||
namespace NKAI
|
||||
{
|
||||
|
||||
class AIGateway;
|
||||
class FuzzyHelper;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
class DLL_EXPORT ExploreNeighbourTile : public ElementarGoal<ExploreNeighbourTile>
|
||||
{
|
||||
private:
|
||||
int tilesToExplore;
|
||||
|
||||
public:
|
||||
ExploreNeighbourTile(const CGHeroInstance * hero, int amount)
|
||||
: ElementarGoal(Goals::EXPLORE_NEIGHBOUR_TILE)
|
||||
{
|
||||
tilesToExplore = amount;
|
||||
sethero(hero);
|
||||
}
|
||||
|
||||
bool operator==(const ExploreNeighbourTile & other) const override;
|
||||
|
||||
void accept(AIGateway * ai) override;
|
||||
std::string toString() const override;
|
||||
|
||||
private:
|
||||
//TSubgoal decomposeSingle() const override;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
235
AI/Nullkiller/Helpers/ExplorationHelper.cpp
Normal file
235
AI/Nullkiller/Helpers/ExplorationHelper.cpp
Normal file
@ -0,0 +1,235 @@
|
||||
/*
|
||||
* ExplorationHelper.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 "ExplorationHelper.h"
|
||||
#include "../../../lib/mapObjects/CGTownInstance.h"
|
||||
#include "../Engine/Nullkiller.h"
|
||||
#include "../Goals/Invalid.h"
|
||||
#include "../Goals/Composition.h"
|
||||
#include "../Goals/ExecuteHeroChain.h"
|
||||
#include "../Markers/ExplorationPoint.h"
|
||||
#include "../../../lib/CPlayerState.h"
|
||||
#include "../Behaviors/CaptureObjectsBehavior.h"
|
||||
#include "../Goals/ExploreNeighbourTile.h"
|
||||
|
||||
namespace NKAI
|
||||
{
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
ExplorationHelper::ExplorationHelper(const CGHeroInstance * hero, const Nullkiller * ai)
|
||||
:ai(ai), cbp(ai->cb.get()), hero(hero)
|
||||
{
|
||||
ts = cbp->getPlayerTeam(ai->playerID);
|
||||
sightRadius = hero->getSightRadius();
|
||||
bestGoal = sptr(Goals::Invalid());
|
||||
bestValue = 0;
|
||||
bestTilesDiscovered = 0;
|
||||
ourPos = hero->visitablePos();
|
||||
allowDeadEndCancellation = true;
|
||||
}
|
||||
|
||||
TSubgoal ExplorationHelper::makeComposition() const
|
||||
{
|
||||
Composition c;
|
||||
c.addNext(ExplorationPoint(bestTile, bestTilesDiscovered));
|
||||
c.addNextSequence({bestGoal, sptr(ExploreNeighbourTile(hero, 5))});
|
||||
return sptr(c);
|
||||
}
|
||||
|
||||
|
||||
bool ExplorationHelper::scanSector(int scanRadius)
|
||||
{
|
||||
int3 tile = int3(0, 0, ourPos.z);
|
||||
|
||||
const auto & slice = ts->fogOfWarMap[ourPos.z];
|
||||
|
||||
for(tile.x = ourPos.x - scanRadius; tile.x <= ourPos.x + scanRadius; tile.x++)
|
||||
{
|
||||
for(tile.y = ourPos.y - scanRadius; tile.y <= ourPos.y + scanRadius; tile.y++)
|
||||
{
|
||||
if(cbp->isInTheMap(tile) && slice[tile.x][tile.y])
|
||||
{
|
||||
scanTile(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !bestGoal->invalid();
|
||||
}
|
||||
|
||||
bool ExplorationHelper::scanMap()
|
||||
{
|
||||
int3 mapSize = cbp->getMapSize();
|
||||
int perimeter = 2 * sightRadius * (mapSize.x + mapSize.y);
|
||||
|
||||
std::vector<int3> from;
|
||||
std::vector<int3> to;
|
||||
|
||||
from.reserve(perimeter);
|
||||
to.reserve(perimeter);
|
||||
|
||||
foreach_tile_pos([&](const int3 & pos)
|
||||
{
|
||||
if(ts->fogOfWarMap[pos.z][pos.x][pos.y])
|
||||
{
|
||||
bool hasInvisibleNeighbor = false;
|
||||
|
||||
foreach_neighbour(cbp, pos, [&](CCallback * cbp, int3 neighbour)
|
||||
{
|
||||
if(!ts->fogOfWarMap[neighbour.z][neighbour.x][neighbour.y])
|
||||
{
|
||||
hasInvisibleNeighbor = true;
|
||||
}
|
||||
});
|
||||
|
||||
if(hasInvisibleNeighbor)
|
||||
from.push_back(pos);
|
||||
}
|
||||
});
|
||||
|
||||
logAi->debug("Exploration scan visible area perimeter for hero %s", hero->getNameTranslated());
|
||||
|
||||
for(const int3 & tile : from)
|
||||
{
|
||||
scanTile(tile);
|
||||
}
|
||||
|
||||
if(!bestGoal->invalid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
allowDeadEndCancellation = false;
|
||||
|
||||
for(int i = 0; i < sightRadius; i++)
|
||||
{
|
||||
getVisibleNeighbours(from, to);
|
||||
vstd::concatenate(from, to);
|
||||
vstd::removeDuplicates(from);
|
||||
}
|
||||
|
||||
logAi->debug("Exploration scan all possible tiles for hero %s", hero->getNameTranslated());
|
||||
|
||||
for(const int3 & tile : from)
|
||||
{
|
||||
scanTile(tile);
|
||||
}
|
||||
|
||||
return !bestGoal->invalid();
|
||||
}
|
||||
|
||||
void ExplorationHelper::scanTile(const int3 & tile)
|
||||
{
|
||||
if(tile == ourPos
|
||||
|| !ai->cb->getTile(tile, false)
|
||||
|| !ai->pathfinder->isTileAccessible(hero, tile)) //shouldn't happen, but it does
|
||||
return;
|
||||
|
||||
int tilesDiscovered = howManyTilesWillBeDiscovered(tile);
|
||||
if(!tilesDiscovered)
|
||||
return;
|
||||
|
||||
auto paths = ai->pathfinder->getPathInfo(tile);
|
||||
auto waysToVisit = CaptureObjectsBehavior::getVisitGoals(paths, ai, ai->cb->getTopObj(tile));
|
||||
|
||||
for(int i = 0; i != paths.size(); i++)
|
||||
{
|
||||
auto & path = paths[i];
|
||||
auto goal = waysToVisit[i];
|
||||
|
||||
if(path.exchangeCount > 1 || path.targetHero != hero || path.movementCost() <= 0.0 || goal->invalid())
|
||||
continue;
|
||||
|
||||
float ourValue = (float)tilesDiscovered * tilesDiscovered / path.movementCost();
|
||||
|
||||
if(ourValue > bestValue) //avoid costly checks of tiles that don't reveal much
|
||||
{
|
||||
auto obj = cb->getTopObj(tile);
|
||||
|
||||
// picking up resources does not yield any exploration at all.
|
||||
// if it blocks the way to some explorable tile AIPathfinder will take care of it
|
||||
if(obj && obj->isBlockedVisitable())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(isSafeToVisit(hero, path.heroArmy, path.getTotalDanger()))
|
||||
{
|
||||
bestGoal = goal;
|
||||
bestValue = ourValue;
|
||||
bestTile = tile;
|
||||
bestTilesDiscovered = tilesDiscovered;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExplorationHelper::getVisibleNeighbours(const std::vector<int3> & tiles, std::vector<int3> & out) const
|
||||
{
|
||||
for(const int3 & tile : tiles)
|
||||
{
|
||||
foreach_neighbour(cbp, tile, [&](CCallback * cbp, int3 neighbour)
|
||||
{
|
||||
if(ts->fogOfWarMap[neighbour.z][neighbour.x][neighbour.y])
|
||||
{
|
||||
out.push_back(neighbour);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
int ExplorationHelper::howManyTilesWillBeDiscovered(const int3 & pos) const
|
||||
{
|
||||
int ret = 0;
|
||||
int3 npos = int3(0, 0, pos.z);
|
||||
|
||||
const auto & slice = ts->fogOfWarMap[pos.z];
|
||||
|
||||
for(npos.x = pos.x - sightRadius; npos.x <= pos.x + sightRadius; npos.x++)
|
||||
{
|
||||
for(npos.y = pos.y - sightRadius; npos.y <= pos.y + sightRadius; npos.y++)
|
||||
{
|
||||
if(cbp->isInTheMap(npos)
|
||||
&& pos.dist2d(npos) - 0.5 < sightRadius
|
||||
&& !slice[npos.x][npos.y])
|
||||
{
|
||||
if(allowDeadEndCancellation
|
||||
&& !hasReachableNeighbor(npos))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ExplorationHelper::hasReachableNeighbor(const int3 & pos) const
|
||||
{
|
||||
for(const int3 & dir : int3::getDirs())
|
||||
{
|
||||
int3 tile = pos + dir;
|
||||
if(cbp->isInTheMap(tile))
|
||||
{
|
||||
auto isAccessible = ai->pathfinder->isTileAccessible(hero, tile);
|
||||
|
||||
if(isAccessible)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
51
AI/Nullkiller/Helpers/ExplorationHelper.h
Normal file
51
AI/Nullkiller/Helpers/ExplorationHelper.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* ExplorationHelper.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 "../AIUtility.h"
|
||||
|
||||
#include "../../../lib/GameConstants.h"
|
||||
#include "../../../lib/VCMI_Lib.h"
|
||||
#include "../../../lib/CTownHandler.h"
|
||||
#include "../../../lib/CBuildingHandler.h"
|
||||
#include "../Goals/AbstractGoal.h"
|
||||
|
||||
namespace NKAI
|
||||
{
|
||||
|
||||
class ExplorationHelper
|
||||
{
|
||||
private:
|
||||
const CGHeroInstance * hero;
|
||||
int sightRadius;
|
||||
float bestValue;
|
||||
Goals::TSubgoal bestGoal;
|
||||
int3 bestTile;
|
||||
int bestTilesDiscovered;
|
||||
const Nullkiller * ai;
|
||||
CCallback * cbp;
|
||||
const TeamState * ts;
|
||||
int3 ourPos;
|
||||
bool allowDeadEndCancellation;
|
||||
|
||||
public:
|
||||
ExplorationHelper(const CGHeroInstance * hero, const Nullkiller * ai);
|
||||
Goals::TSubgoal makeComposition() const;
|
||||
bool scanSector(int scanRadius);
|
||||
bool scanMap();
|
||||
int howManyTilesWillBeDiscovered(const int3 & pos) const;
|
||||
|
||||
private:
|
||||
void scanTile(const int3 & tile);
|
||||
bool hasReachableNeighbor(const int3 & pos) const;
|
||||
void getVisibleNeighbours(const std::vector<int3> & tiles, std::vector<int3> & out) const;
|
||||
};
|
||||
|
||||
}
|
@ -18,10 +18,11 @@ class VCAI;
|
||||
|
||||
namespace Goals
|
||||
{
|
||||
template<typename T> class DLL_EXPORT CGoal : public AbstractGoal
|
||||
template<typename T>
|
||||
class DLL_EXPORT CGoal : public AbstractGoal
|
||||
{
|
||||
public:
|
||||
CGoal<T>(EGoals goal = INVALID) : AbstractGoal(goal)
|
||||
CGoal(EGoals goal = INVALID) : AbstractGoal(goal)
|
||||
{
|
||||
priority = 0;
|
||||
isElementar = false;
|
||||
@ -56,7 +57,7 @@ namespace Goals
|
||||
return f->evaluate(static_cast<T &>(*this)); //casting enforces template instantiation
|
||||
}
|
||||
|
||||
CGoal<T> * clone() const override
|
||||
CGoal * clone() const override
|
||||
{
|
||||
return new T(static_cast<T const &>(*this)); //casting enforces template instantiation
|
||||
}
|
||||
|
@ -23,7 +23,6 @@
|
||||
#include "../../lib/CTownHandler.h"
|
||||
#include "../../lib/mapObjects/MiscObjects.h"
|
||||
#include "../../lib/spells/CSpellHandler.h"
|
||||
#include "../../lib/CondSh.h"
|
||||
#include "Pathfinding/AIPathfinder.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
@ -1,5 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
sudo apt remove needrestart
|
||||
sudo apt-get update
|
||||
|
||||
# Dependencies
|
||||
|
@ -1,5 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
sudo apt remove needrestart
|
||||
sudo apt-get update
|
||||
|
||||
# Dependencies
|
||||
|
93
ChangeLog.md
93
ChangeLog.md
@ -1,3 +1,96 @@
|
||||
# 1.5.1 -> 1.5.2
|
||||
|
||||
### Stability
|
||||
* Fixed crash on closing game while combat or map animations are playing
|
||||
* Fixed crash on closing game while network thread is waiting for dialog to be closed
|
||||
* Fixed random crash on starting random map with 'random' number of players
|
||||
* Fixed crash caused by thread races on loading map list
|
||||
* Failure to read data from network connection will show up as 'disconnection' and not as a crash
|
||||
* Fixed a possible crash when replaying a manually played battle with the 'unlimited battle replay' option set
|
||||
* Fixed crash when loading save made on a 64-bit system or connecting to multiplayer game with a 64-bit host on a 32-bit system (and vice versa)
|
||||
* Fixed crash when ending a battle in a draw when a hero has the Necromancy skill
|
||||
* Fixed crash when having SPELL_LIKE_ATTACK bonus with invalid spell ID
|
||||
* Fixed transfer of non-first artefacts in backpack if hero does not transfer as well
|
||||
* Game will now abort loading if a corrupt mod is detected instead of crashing without explanation later
|
||||
|
||||
### Multiplayer
|
||||
* Contact between allied players will no longer break simturns
|
||||
* Having hero in range of object owned by another player will now be registered as contact
|
||||
* Multiplayer saves are now visible when starting a single player game
|
||||
* Added chat command '!vote' to initiate a vote to change the duration of simultaneous turns or to change turn timers
|
||||
* Added chat command '!help' to list all available chat commands
|
||||
* All multiplayer chat commands now use a leading exclamation mark
|
||||
|
||||
### Campaigns
|
||||
* If the hero attacks an enemy player and is defeated, he will be correctly registered as defeated by the defending player.
|
||||
* Allow standard victory condition on 'To kill a hero' campaign mission in line with H3
|
||||
* Fixes Adrienne starting without Inferno spell in campaign
|
||||
|
||||
### Interface
|
||||
* For artefacts that are part of a combined artefact, the game will now show which component of that artefact your hero has.
|
||||
* Fixed broken in 1.5.1 shortcut for artifact sets saving
|
||||
* Fixed full screen toggle (F4) not applying changes immediately
|
||||
* Retaliation preview now accounts for creatures that don't receive retaliations (Sprites, Archdevils, etc)
|
||||
* Fixed not visible retaliation preview if damage estimation string is longer than battle log line due to long creature name
|
||||
* Game will now select last save on loading screen
|
||||
* High Scores screen and Campaign Epilogue screen are now displayed with background on resolutions higher than 800x600
|
||||
* Fixed non-functioning shortcut 'P' to access Puzzle Map from adventure map
|
||||
* Added keyboard shortcuts to markets and altars. 'Space' to confirm deal and 'M' to trade maximum possible amount
|
||||
* Pressing 'Escape' in main menu will now trigger 'Back' and 'Quit' buttons
|
||||
* Added keyboard shortcuts to hero exchange window:
|
||||
* * 'F10' will now swap armies
|
||||
* * 'F11' will now swap artifacts. Additionally, 'Ctrl+F11' will swap equipped artifacts, and 'Shift+F11' will swap backpacks
|
||||
* * Added unassigned shortcuts to move armies or artifacts to left or right side
|
||||
* Added keyboard shortcuts to access buildings from town interface:
|
||||
* * 'F' will now open Fort window
|
||||
* * 'B' will now open Town Hall window
|
||||
* * 'G' will now open Mage Guild window
|
||||
* * 'M' will now open Marketplace
|
||||
* * 'R' will now open recruitment interface
|
||||
* * 'T' will now open Tavern window
|
||||
* * 'G' will now open Thieves Guild
|
||||
* * 'E' will now open hero exchange screen, if both heroes are present in town
|
||||
* * 'H' will now open hero screen. Additionally, 'Shift+H' will open garrisoned hero screen, and 'Ctrl+H' will open visiting hero screen
|
||||
* * 'Space' will now swap visiting and garrisoned heroes
|
||||
* Added keyboard shortcuts to switch between tabs in Scenario Selection window:
|
||||
* * 'E' will open Extra Options tab
|
||||
* * 'T' will open Turn Options tab
|
||||
* * 'I' will open Invite Players window (only for lobby games)
|
||||
* * 'R' will now replay video in campaigns
|
||||
* Added keyboard shortcuts to Adventure map:
|
||||
* * 'Ctrl+L' will now prompt to open Load Game screen
|
||||
* * 'Ctrl+M' will now prompt to go to main menu
|
||||
* * 'Ctrl+N' will now prompt to go to New Game screen
|
||||
* * 'Ctrl+Q' will now prompt to quit game
|
||||
* * Page Up, Page Down, Home and End keys will now move hero on adventure map similar to numpad equivalents
|
||||
* * Fixed non-functioning shortcuts '+' and '-' on numpad to zoom adventure map
|
||||
* Added keyboard shortcuts to Battle interface:
|
||||
* * 'V' now allows to view information of hovered unit
|
||||
* * 'I' now allows to view information of active unit
|
||||
|
||||
### Mechanics
|
||||
* Game will no longer pick creatures exclusive to AB campaigns for random creatures or for Refugee Camp, in line with H3
|
||||
* If original movement rules are on, it is not possible to attack guards from visitable object directly, only from free tile
|
||||
* Fixed bug leading that allowed picking up objects while flying on top of water
|
||||
* Hero can now land when flying from guarded tile to accessible guarded tile irregardless of original movement rules switch
|
||||
* Interface will now use same arrow for U-turns in path as H3
|
||||
|
||||
### AI
|
||||
* Nullkiller AI can now explore the map
|
||||
* Nullkiller AI will no longer use the map reveal cheat when allied with a human or when playing on low difficulty
|
||||
* Nullkiller AI is now used by default for allied players
|
||||
|
||||
### Launcher
|
||||
* When extracting data from gog.com offline installer game will extract files directly into used data directory instead of temporary directory
|
||||
|
||||
### Map Editor
|
||||
* Fixed victory / loss conditions widget initialization
|
||||
|
||||
### Modding
|
||||
* Hero specialties with multiple bonuses that have TIMES_HERO_LEVEL updater now work as expected
|
||||
* Spells that apply multiple bonuses with same type and subtype but different value type now work as expected
|
||||
* Added option to toggle layout of guards in creature banks
|
||||
|
||||
# 1.5.0 -> 1.5.1
|
||||
|
||||
### Stability
|
||||
|
@ -242,16 +242,23 @@
|
||||
"vcmi.adventureMap.revisitObject.help" : "{重新访问}\n\n让当前英雄重新访问地图建筑或城镇。",
|
||||
|
||||
"vcmi.battleWindow.pressKeyToSkipIntro" : "按下任意键立即开始战斗",
|
||||
"vcmi.battleWindow.damageEstimation.melee" : "近战攻击 %CREATURE (%DAMAGE).",
|
||||
"vcmi.battleWindow.damageEstimation.meleeKills" : "近战攻击 %CREATURE (%DAMAGE, %KILLS).",
|
||||
"vcmi.battleWindow.damageEstimation.ranged" : "射击 %CREATURE (%SHOTS, %DAMAGE).",
|
||||
"vcmi.battleWindow.damageEstimation.rangedKills" : "射击 %CREATURE (%SHOTS, %DAMAGE, %KILLS).",
|
||||
"vcmi.battleWindow.damageEstimation.melee" : "近战攻击 %CREATURE (%DAMAGE)。",
|
||||
"vcmi.battleWindow.damageEstimation.meleeKills" : "近战攻击 %CREATURE (%DAMAGE, %KILLS)。",
|
||||
"vcmi.battleWindow.damageEstimation.ranged" : "射击 %CREATURE (%SHOTS, %DAMAGE)。",
|
||||
"vcmi.battleWindow.damageEstimation.rangedKills" : "射击 %CREATURE (%SHOTS, %DAMAGE, %KILLS)。",
|
||||
"vcmi.battleWindow.damageEstimation.shots" : "%d 弹药剩余",
|
||||
"vcmi.battleWindow.damageEstimation.shots.1" : "%d 弹药剩余",
|
||||
"vcmi.battleWindow.damageEstimation.damage" : "%d 伤害",
|
||||
"vcmi.battleWindow.damageEstimation.damage.1" : "%d 伤害",
|
||||
"vcmi.battleWindow.damageEstimation.kills" : "%d 将被消灭",
|
||||
"vcmi.battleWindow.damageEstimation.kills.1" : "%d 将被消灭",
|
||||
|
||||
"vcmi.battleWindow.damageRetaliation.will" : "将会反击",
|
||||
"vcmi.battleWindow.damageRetaliation.may" : "可能反击",
|
||||
"vcmi.battleWindow.damageRetaliation.never" : "不会反击。",
|
||||
"vcmi.battleWindow.damageRetaliation.damage" : "(%DAMAGE)。",
|
||||
"vcmi.battleWindow.damageRetaliation.damageKills" : "(%DAMAGE, %KILLS)。",
|
||||
|
||||
"vcmi.battleWindow.killed" : "已消灭",
|
||||
"vcmi.battleWindow.accurateShot.resultDescription.0" : "%d %s 死于精准射击",
|
||||
"vcmi.battleWindow.accurateShot.resultDescription.1" : "%d %s 死于精准射击",
|
||||
|
@ -252,6 +252,13 @@
|
||||
"vcmi.battleWindow.damageEstimation.damage.1" : "obrażenia: %d",
|
||||
"vcmi.battleWindow.damageEstimation.kills" : "zginie: %d",
|
||||
"vcmi.battleWindow.damageEstimation.kills.1" : "zginie: %d",
|
||||
|
||||
"vcmi.battleWindow.damageRetaliation.will" : "Nastąpi odwet ",
|
||||
"vcmi.battleWindow.damageRetaliation.may" : "Możliwy odwet ",
|
||||
"vcmi.battleWindow.damageRetaliation.never" : "Nie będzie odwetu.",
|
||||
"vcmi.battleWindow.damageRetaliation.damage" : "(%DAMAGE).",
|
||||
"vcmi.battleWindow.damageRetaliation.damageKills" : "(%DAMAGE, %KILLS).",
|
||||
|
||||
"vcmi.battleWindow.killed" : "Zabici",
|
||||
"vcmi.battleWindow.accurateShot.resultDescription.0" : "%d %s zostało zabitych poprzez celne strzały!",
|
||||
"vcmi.battleWindow.accurateShot.resultDescription.1" : "%d %s został zabity poprzez celny strzał!",
|
||||
|
@ -435,6 +435,8 @@ static void mainLoop()
|
||||
|
||||
[[noreturn]] static void quitApplication()
|
||||
{
|
||||
CSH->endNetwork();
|
||||
|
||||
if(!settings["session"]["headless"].Bool())
|
||||
{
|
||||
if(CSH->client)
|
||||
@ -443,6 +445,8 @@ static void mainLoop()
|
||||
GH.windows().clear();
|
||||
}
|
||||
|
||||
vstd::clear_pointer(CSH);
|
||||
|
||||
CMM.reset();
|
||||
|
||||
if(!settings["session"]["headless"].Bool())
|
||||
@ -463,7 +467,6 @@ static void mainLoop()
|
||||
vstd::clear_pointer(graphics);
|
||||
}
|
||||
|
||||
vstd::clear_pointer(CSH);
|
||||
vstd::clear_pointer(VLC);
|
||||
|
||||
// sometimes leads to a hang. TODO: investigate
|
||||
|
@ -144,6 +144,7 @@ set(client_SRCS
|
||||
|
||||
windows/CCastleInterface.cpp
|
||||
windows/CCreatureWindow.cpp
|
||||
windows/CExchangeWindow.cpp
|
||||
windows/CHeroOverview.cpp
|
||||
windows/CHeroWindow.cpp
|
||||
windows/CKingdomInterface.cpp
|
||||
@ -346,6 +347,7 @@ set(client_HEADERS
|
||||
|
||||
windows/CCastleInterface.h
|
||||
windows/CCreatureWindow.h
|
||||
windows/CExchangeWindow.h
|
||||
windows/CHeroOverview.h
|
||||
windows/CHeroWindow.h
|
||||
windows/CKingdomInterface.h
|
||||
@ -377,6 +379,7 @@ set(client_HEADERS
|
||||
Client.h
|
||||
ClientCommandManager.h
|
||||
ClientNetPackVisitors.h
|
||||
ConditionalWait.h
|
||||
HeroMovementController.h
|
||||
GameChatHandler.h
|
||||
LobbyClientNetPackVisitors.h
|
||||
|
@ -53,6 +53,7 @@
|
||||
|
||||
#include "windows/CCastleInterface.h"
|
||||
#include "windows/CCreatureWindow.h"
|
||||
#include "windows/CExchangeWindow.h"
|
||||
#include "windows/CHeroWindow.h"
|
||||
#include "windows/CKingdomInterface.h"
|
||||
#include "windows/CMarketWindow.h"
|
||||
@ -74,7 +75,6 @@
|
||||
#include "../lib/CStopWatch.h"
|
||||
#include "../lib/CThreadHelper.h"
|
||||
#include "../lib/CTownHandler.h"
|
||||
#include "../lib/CondSh.h"
|
||||
#include "../lib/GameConstants.h"
|
||||
#include "../lib/RoadHandler.h"
|
||||
#include "../lib/StartInfo.h"
|
||||
@ -139,10 +139,10 @@ CPlayerInterface::CPlayerInterface(PlayerColor Player):
|
||||
LOCPLINT = this;
|
||||
playerID=Player;
|
||||
human=true;
|
||||
battleInt = nullptr;
|
||||
battleInt.reset();
|
||||
castleInt = nullptr;
|
||||
makingTurn = false;
|
||||
showingDialog = new CondSh<bool>(false);
|
||||
showingDialog = new ConditionalWait();
|
||||
cingconsole = new CInGameConsole();
|
||||
autosaveCount = 0;
|
||||
isAutoFightOn = false;
|
||||
@ -1006,7 +1006,7 @@ void CPlayerInterface::showInfoDialog(const std::string &text, const std::vector
|
||||
if (makingTurn && GH.windows().count() > 0 && LOCPLINT == this)
|
||||
{
|
||||
CCS->soundh->playSound(static_cast<soundBase::soundID>(soundID));
|
||||
showingDialog->set(true);
|
||||
showingDialog->setBusy();
|
||||
movementController->requestMovementAbort(); // interrupt movement to show dialog
|
||||
GH.windows().pushWindow(temp);
|
||||
}
|
||||
@ -1029,7 +1029,7 @@ void CPlayerInterface::showInfoDialogAndWait(std::vector<Component> & components
|
||||
void CPlayerInterface::showYesNoDialog(const std::string &text, CFunctionList<void()> onYes, CFunctionList<void()> onNo, const std::vector<std::shared_ptr<CComponent>> & components)
|
||||
{
|
||||
movementController->requestMovementAbort();
|
||||
LOCPLINT->showingDialog->setn(true);
|
||||
LOCPLINT->showingDialog->setBusy();
|
||||
CInfoWindow::showYesNoDialog(text, components, onYes, onNo, playerID);
|
||||
}
|
||||
|
||||
@ -1205,7 +1205,7 @@ void CPlayerInterface::heroBonusChanged( const CGHeroInstance *hero, const Bonus
|
||||
void CPlayerInterface::moveHero( const CGHeroInstance *h, const CGPath& path )
|
||||
{
|
||||
assert(h);
|
||||
assert(!showingDialog->get());
|
||||
assert(!showingDialog->isBusy());
|
||||
assert(dialogs.empty());
|
||||
|
||||
LOG_TRACE(logGlobal);
|
||||
@ -1215,7 +1215,7 @@ void CPlayerInterface::moveHero( const CGHeroInstance *h, const CGPath& path )
|
||||
return; //can't find hero
|
||||
|
||||
//It shouldn't be possible to move hero with open dialog (or dialog waiting in bg)
|
||||
if (showingDialog->get() || !dialogs.empty())
|
||||
if (showingDialog->isBusy() || !dialogs.empty())
|
||||
return;
|
||||
|
||||
if (localState->isHeroSleeping(h))
|
||||
@ -1383,9 +1383,7 @@ void CPlayerInterface::waitWhileDialog()
|
||||
}
|
||||
|
||||
auto unlockInterface = vstd::makeUnlockGuard(GH.interfaceMutex);
|
||||
boost::unique_lock<boost::mutex> un(showingDialog->mx);
|
||||
while(showingDialog->data)
|
||||
showingDialog->cond.wait(un);
|
||||
showingDialog->waitWhileBusy();
|
||||
}
|
||||
|
||||
void CPlayerInterface::showShipyardDialog(const IShipyard *obj)
|
||||
@ -1490,9 +1488,9 @@ void CPlayerInterface::update()
|
||||
return;
|
||||
|
||||
//if there are any waiting dialogs, show them
|
||||
if ((CSH->howManyPlayerInterfaces() <= 1 || makingTurn) && !dialogs.empty() && !showingDialog->get())
|
||||
if ((CSH->howManyPlayerInterfaces() <= 1 || makingTurn) && !dialogs.empty() && !showingDialog->isBusy())
|
||||
{
|
||||
showingDialog->set(true);
|
||||
showingDialog->setBusy();
|
||||
GH.windows().pushWindow(dialogs.front());
|
||||
dialogs.pop_front();
|
||||
}
|
||||
@ -1504,6 +1502,11 @@ void CPlayerInterface::update()
|
||||
GH.windows().simpleRedraw();
|
||||
}
|
||||
|
||||
void CPlayerInterface::endNetwork()
|
||||
{
|
||||
showingDialog->requestTermination();
|
||||
}
|
||||
|
||||
int CPlayerInterface::getLastIndex( std::string namePrefix)
|
||||
{
|
||||
using namespace boost::filesystem;
|
||||
|
@ -25,7 +25,7 @@ struct CGPath;
|
||||
class CCreatureSet;
|
||||
class CGObjectInstance;
|
||||
struct UpgradeInfo;
|
||||
template <typename T> struct CondSh;
|
||||
class ConditionalWait;
|
||||
struct CPathsInfo;
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
@ -72,7 +72,7 @@ public: // TODO: make private
|
||||
std::unique_ptr<PlayerLocalState> localState;
|
||||
|
||||
//minor interfaces
|
||||
CondSh<bool> *showingDialog; //indicates if dialog box is displayed
|
||||
ConditionalWait * showingDialog; //indicates if dialog box is displayed
|
||||
|
||||
bool makingTurn; //if player is already making his turn
|
||||
|
||||
@ -198,6 +198,7 @@ public: // public interface for use by client via LOCPLINT access
|
||||
void proposeLoadingGame();
|
||||
void performAutosave();
|
||||
void gamePause(bool pause);
|
||||
void endNetwork();
|
||||
|
||||
///returns true if all events are processed internally
|
||||
bool capturedAllEvents();
|
||||
|
@ -29,6 +29,7 @@
|
||||
|
||||
#include "../lib/CConfigHandler.h"
|
||||
#include "../lib/CGeneralTextHandler.h"
|
||||
#include "ConditionalWait.h"
|
||||
#include "../lib/CThreadHelper.h"
|
||||
#include "../lib/StartInfo.h"
|
||||
#include "../lib/TurnTimerInfo.h"
|
||||
@ -131,6 +132,17 @@ CServerHandler::~CServerHandler()
|
||||
}
|
||||
}
|
||||
|
||||
void CServerHandler::endNetwork()
|
||||
{
|
||||
if (client)
|
||||
client->endNetwork();
|
||||
networkHandler->stop();
|
||||
{
|
||||
auto unlockInterface = vstd::makeUnlockGuard(GH.interfaceMutex);
|
||||
threadNetwork.join();
|
||||
}
|
||||
}
|
||||
|
||||
CServerHandler::CServerHandler()
|
||||
: networkHandler(INetworkHandler::createHandler())
|
||||
, lobbyClient(std::make_unique<GlobalLobbyClient>())
|
||||
@ -158,7 +170,14 @@ void CServerHandler::threadRunNetwork()
|
||||
{
|
||||
logGlobal->info("Starting network thread");
|
||||
setThreadName("runNetwork");
|
||||
networkHandler->run();
|
||||
try {
|
||||
networkHandler->run();
|
||||
}
|
||||
catch (const TerminationRequestedException & e)
|
||||
{
|
||||
logGlobal->info("Terminating network thread");
|
||||
return;
|
||||
}
|
||||
logGlobal->info("Ending network thread");
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,6 @@
|
||||
|
||||
#include "../lib/network/NetworkInterface.h"
|
||||
#include "../lib/StartInfo.h"
|
||||
#include "../lib/CondSh.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
@ -208,6 +207,7 @@ public:
|
||||
|
||||
void startGameplay(VCMI_LIB_WRAP_NAMESPACE(CGameState) * gameState = nullptr);
|
||||
void showHighScoresAndEndGameplay(PlayerColor player, bool victory);
|
||||
void endNetwork();
|
||||
void endGameplay();
|
||||
void restartGameplay();
|
||||
void startCampaignScenario(HighScoreParameter param, std::shared_ptr<CampaignState> cs = {});
|
||||
|
@ -218,6 +218,22 @@ void CClient::save(const std::string & fname)
|
||||
sendRequest(&save_game, PlayerColor::NEUTRAL);
|
||||
}
|
||||
|
||||
void CClient::endNetwork()
|
||||
{
|
||||
if (CGI->mh)
|
||||
CGI->mh->endNetwork();
|
||||
|
||||
if (CPlayerInterface::battleInt)
|
||||
CPlayerInterface::battleInt->endNetwork();
|
||||
|
||||
for(auto & i : playerint)
|
||||
{
|
||||
auto interface = std::dynamic_pointer_cast<CPlayerInterface>(i.second);
|
||||
if (interface)
|
||||
interface->endNetwork();
|
||||
}
|
||||
}
|
||||
|
||||
void CClient::endGame()
|
||||
{
|
||||
#if SCRIPTING_ENABLED
|
||||
|
@ -133,6 +133,7 @@ public:
|
||||
void loadGame(CGameState * gameState);
|
||||
|
||||
void save(const std::string & fname);
|
||||
void endNetwork();
|
||||
void endGame();
|
||||
|
||||
void initMapHandler();
|
||||
|
@ -179,11 +179,6 @@ void ClientCommandManager::handleRedrawCommand()
|
||||
GH.windows().totalRedraw();
|
||||
}
|
||||
|
||||
void ClientCommandManager::handleNotDialogCommand()
|
||||
{
|
||||
LOCPLINT->showingDialog->setn(false);
|
||||
}
|
||||
|
||||
void ClientCommandManager::handleTranslateGameCommand()
|
||||
{
|
||||
std::map<std::string, std::map<std::string, std::string>> textsByMod;
|
||||
@ -584,9 +579,6 @@ void ClientCommandManager::processCommand(const std::string & message, bool call
|
||||
else if(commandName == "redraw")
|
||||
handleRedrawCommand();
|
||||
|
||||
else if(commandName == "not dialog")
|
||||
handleNotDialogCommand();
|
||||
|
||||
else if(message=="translate" || message=="translate game")
|
||||
handleTranslateGameCommand();
|
||||
|
||||
|
@ -45,9 +45,6 @@ class ClientCommandManager //take mantis #2292 issue about account if thinking a
|
||||
// Redraw the current screen
|
||||
void handleRedrawCommand();
|
||||
|
||||
// Set the state indicating if dialog box is active to "no"
|
||||
void handleNotDialogCommand();
|
||||
|
||||
// Extracts all translateable game texts into Translation directory, separating files on per-mod basis
|
||||
void handleTranslateGameCommand();
|
||||
|
||||
|
77
client/ConditionalWait.h
Normal file
77
client/ConditionalWait.h
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* ConditionalWait.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 <condition_variable>
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class TerminationRequestedException : public std::exception
|
||||
{
|
||||
public:
|
||||
using exception::exception;
|
||||
|
||||
const char* what() const noexcept override
|
||||
{
|
||||
return "Thread termination requested";
|
||||
}
|
||||
};
|
||||
|
||||
class ConditionalWait
|
||||
{
|
||||
bool isBusyValue = false;
|
||||
bool isTerminating = false;
|
||||
std::condition_variable cond;
|
||||
std::mutex mx;
|
||||
|
||||
void set(bool value)
|
||||
{
|
||||
boost::unique_lock<std::mutex> lock(mx);
|
||||
isBusyValue = value;
|
||||
}
|
||||
|
||||
public:
|
||||
ConditionalWait() = default;
|
||||
|
||||
void setBusy()
|
||||
{
|
||||
set(true);
|
||||
}
|
||||
|
||||
void setFree()
|
||||
{
|
||||
set(false);
|
||||
cond.notify_all();
|
||||
}
|
||||
|
||||
void requestTermination()
|
||||
{
|
||||
isTerminating = true;
|
||||
setFree();
|
||||
}
|
||||
|
||||
bool isBusy()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mx);
|
||||
return isBusyValue;
|
||||
}
|
||||
|
||||
void waitWhileBusy()
|
||||
{
|
||||
std::unique_lock<std::mutex> un(mx);
|
||||
while(isBusyValue)
|
||||
cond.wait(un);
|
||||
|
||||
if (isTerminating)
|
||||
throw TerminationRequestedException();
|
||||
}
|
||||
};
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
@ -22,7 +22,7 @@
|
||||
|
||||
#include "../CCallback.h"
|
||||
|
||||
#include "../lib/CondSh.h"
|
||||
#include "ConditionalWait.h"
|
||||
#include "../lib/CConfigHandler.h"
|
||||
#include "../lib/pathfinder/CGPathNode.h"
|
||||
#include "../lib/mapObjects/CGHeroInstance.h"
|
||||
@ -238,7 +238,7 @@ void HeroMovementController::onMoveHeroApplied()
|
||||
assert(currentlyMovingHero);
|
||||
const auto * hero = currentlyMovingHero;
|
||||
|
||||
bool canMove = LOCPLINT->localState->hasPath(hero) && LOCPLINT->localState->getPath(hero).nextNode().turns == 0 && !LOCPLINT->showingDialog->get();
|
||||
bool canMove = LOCPLINT->localState->hasPath(hero) && LOCPLINT->localState->getPath(hero).nextNode().turns == 0 && !LOCPLINT->showingDialog->isBusy();
|
||||
bool wantStop = stoppingMovement;
|
||||
bool canStop = !canMove || canHeroStopAtNode(LOCPLINT->localState->getPath(hero).currNode());
|
||||
|
||||
|
@ -43,6 +43,7 @@ void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyClientConnected(LobbyClientCon
|
||||
// Check if it's LobbyClientConnected for our client
|
||||
if(pack.uuid == handler.logicConnection->uuid)
|
||||
{
|
||||
handler.logicConnection->setSerializationVersion(pack.version);
|
||||
handler.logicConnection->connectionID = pack.clientId;
|
||||
if(handler.mapToStart)
|
||||
{
|
||||
|
@ -886,9 +886,9 @@ void AdventureMapInterface::hotkeySwitchMapLevel()
|
||||
widget->getMapView()->onMapLevelSwitched();
|
||||
}
|
||||
|
||||
void AdventureMapInterface::hotkeyZoom(int delta)
|
||||
void AdventureMapInterface::hotkeyZoom(int delta, bool useDeadZone)
|
||||
{
|
||||
widget->getMapView()->onMapZoomLevelChanged(delta);
|
||||
widget->getMapView()->onMapZoomLevelChanged(delta, useDeadZone);
|
||||
}
|
||||
|
||||
void AdventureMapInterface::onScreenResize()
|
||||
|
@ -120,7 +120,7 @@ public:
|
||||
void hotkeyEndingTurn();
|
||||
void hotkeyNextTown();
|
||||
void hotkeySwitchMapLevel();
|
||||
void hotkeyZoom(int delta);
|
||||
void hotkeyZoom(int delta, bool useDeadZone);
|
||||
|
||||
/// Called by PlayerInterface when specified player is ready to start his turn
|
||||
void onHotseatWaitStarted(PlayerColor playerID);
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "AdventureMapShortcuts.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CMT.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../PlayerLocalState.h"
|
||||
@ -77,21 +78,26 @@ std::vector<AdventureMapShortcutState> AdventureMapShortcuts::getShortcuts()
|
||||
{ EShortcut::ADVENTURE_CAST_SPELL, optionHeroSelected(), [this]() { this->showSpellbook(); } },
|
||||
{ EShortcut::ADVENTURE_GAME_OPTIONS, optionInMapView(), [this]() { this->adventureOptions(); } },
|
||||
{ EShortcut::GLOBAL_OPTIONS, optionInMapView(), [this]() { this->systemOptions(); } },
|
||||
{ EShortcut::ADVENTURE_FIRST_HERO, optionInMapView(), [this]() { this->firstHero(); } },
|
||||
{ EShortcut::ADVENTURE_NEXT_HERO, optionHasNextHero(), [this]() { this->nextHero(); } },
|
||||
{ EShortcut::GAME_END_TURN, optionCanEndTurn(), [this]() { this->endTurn(); } },
|
||||
{ EShortcut::ADVENTURE_END_TURN, optionCanEndTurn(), [this]() { this->endTurn(); } },
|
||||
{ EShortcut::ADVENTURE_THIEVES_GUILD, optionInMapView(), [this]() { this->showThievesGuild(); } },
|
||||
{ EShortcut::ADVENTURE_VIEW_SCENARIO, optionInMapView(), [this]() { this->showScenarioInfo(); } },
|
||||
{ EShortcut::GAME_SAVE_GAME, optionInMapView(), [this]() { this->saveGame(); } },
|
||||
{ EShortcut::GAME_LOAD_GAME, optionInMapView(), [this]() { this->loadGame(); } },
|
||||
{ EShortcut::ADVENTURE_QUIT_GAME, optionInMapView(), [this]() { this->quitGame(); } },
|
||||
{ EShortcut::ADVENTURE_TO_MAIN_MENU, optionInMapView(), [this]() { this->toMainMenu(); } },
|
||||
{ EShortcut::ADVENTURE_SAVE_GAME, optionInMapView(), [this]() { this->saveGame(); } },
|
||||
{ EShortcut::ADVENTURE_NEW_GAME, optionInMapView(), [this]() { this->newGame(); } },
|
||||
{ EShortcut::ADVENTURE_LOAD_GAME, optionInMapView(), [this]() { this->loadGame(); } },
|
||||
{ EShortcut::ADVENTURE_RESTART_GAME, optionInMapView(), [this]() { this->restartGame(); } },
|
||||
{ EShortcut::ADVENTURE_DIG_GRAIL, optionHeroSelected(), [this]() { this->digGrail(); } },
|
||||
{ EShortcut::ADVENTURE_VIEW_PUZZLE, optionSidePanelActive(),[this]() { this->viewPuzzleMap(); } },
|
||||
{ EShortcut::GAME_RESTART_GAME, optionInMapView(), [this]() { this->restartGame(); } },
|
||||
{ EShortcut::ADVENTURE_VISIT_OBJECT, optionCanVisitObject(), [this]() { this->visitObject(); } },
|
||||
{ EShortcut::ADVENTURE_VIEW_SELECTED, optionInMapView(), [this]() { this->openObject(); } },
|
||||
{ EShortcut::GAME_OPEN_MARKETPLACE, optionInMapView(), [this]() { this->showMarketplace(); } },
|
||||
{ EShortcut::ADVENTURE_ZOOM_IN, optionSidePanelActive(),[this]() { this->zoom(+1); } },
|
||||
{ EShortcut::ADVENTURE_ZOOM_OUT, optionSidePanelActive(),[this]() { this->zoom(-1); } },
|
||||
{ EShortcut::ADVENTURE_MARKETPLACE, optionInMapView(), [this]() { this->showMarketplace(); } },
|
||||
{ EShortcut::ADVENTURE_ZOOM_IN, optionSidePanelActive(),[this]() { this->zoom(+10); } },
|
||||
{ EShortcut::ADVENTURE_ZOOM_OUT, optionSidePanelActive(),[this]() { this->zoom(-10); } },
|
||||
{ EShortcut::ADVENTURE_ZOOM_RESET, optionSidePanelActive(),[this]() { this->zoom( 0); } },
|
||||
{ EShortcut::ADVENTURE_FIRST_TOWN, optionInMapView(), [this]() { this->firstTown(); } },
|
||||
{ EShortcut::ADVENTURE_NEXT_TOWN, optionInMapView(), [this]() { this->nextTown(); } },
|
||||
{ EShortcut::ADVENTURE_NEXT_OBJECT, optionInMapView(), [this]() { this->nextObject(); } },
|
||||
{ EShortcut::ADVENTURE_MOVE_HERO_SW, optionHeroSelected(), [this]() { this->moveHeroDirectional({-1, +1}); } },
|
||||
@ -223,6 +229,16 @@ void AdventureMapShortcuts::systemOptions()
|
||||
GH.windows().createAndPushWindow<SettingsMainWindow>();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::firstHero()
|
||||
{
|
||||
if (!LOCPLINT->localState->getWanderingHeroes().empty())
|
||||
{
|
||||
const auto * hero = LOCPLINT->localState->getWanderingHero(0);
|
||||
LOCPLINT->localState->setSelection(hero);
|
||||
owner.centerOnObject(hero);
|
||||
}
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::nextHero()
|
||||
{
|
||||
const auto * currHero = LOCPLINT->localState->getCurrentHero();
|
||||
@ -288,6 +304,49 @@ void AdventureMapShortcuts::showScenarioInfo()
|
||||
AdventureOptions::showScenarioInfo();
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::toMainMenu()
|
||||
{
|
||||
LOCPLINT->showYesNoDialog(
|
||||
CGI->generaltexth->allTexts[578],
|
||||
[]()
|
||||
{
|
||||
CSH->endGameplay();
|
||||
GH.defActionsDef = 63;
|
||||
CMM->menu->switchToTab("main");
|
||||
},
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::newGame()
|
||||
{
|
||||
LOCPLINT->showYesNoDialog(
|
||||
CGI->generaltexth->allTexts[578],
|
||||
[]()
|
||||
{
|
||||
CSH->endGameplay();
|
||||
GH.defActionsDef = 63;
|
||||
CMM->menu->switchToTab("new");
|
||||
},
|
||||
nullptr
|
||||
);
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::quitGame()
|
||||
{
|
||||
LOCPLINT->showYesNoDialog(
|
||||
CGI->generaltexth->allTexts[578],
|
||||
[]()
|
||||
{
|
||||
GH.dispatchMainThread( []()
|
||||
{
|
||||
handleQuit(false);
|
||||
});
|
||||
},
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::saveGame()
|
||||
{
|
||||
GH.windows().createAndPushWindow<CSavingScreen>();
|
||||
@ -366,6 +425,16 @@ void AdventureMapShortcuts::showMarketplace()
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.noTownWithMarket"));
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::firstTown()
|
||||
{
|
||||
if (!LOCPLINT->localState->getOwnedTowns().empty())
|
||||
{
|
||||
const auto * town = LOCPLINT->localState->getOwnedTown(0);
|
||||
LOCPLINT->localState->setSelection(town);
|
||||
owner.centerOnObject(town);
|
||||
}
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::nextTown()
|
||||
{
|
||||
owner.hotkeyNextTown();
|
||||
@ -373,7 +442,7 @@ void AdventureMapShortcuts::nextTown()
|
||||
|
||||
void AdventureMapShortcuts::zoom( int distance)
|
||||
{
|
||||
owner.hotkeyZoom(distance);
|
||||
owner.hotkeyZoom(distance, false);
|
||||
}
|
||||
|
||||
void AdventureMapShortcuts::nextObject()
|
||||
@ -494,7 +563,7 @@ bool AdventureMapShortcuts::optionInWorldView()
|
||||
|
||||
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 || state == EAdventureState::OTHER_HUMAN_PLAYER_TURN;
|
||||
}
|
||||
|
||||
bool AdventureMapShortcuts::optionMapScrollingActive()
|
||||
|
@ -49,10 +49,14 @@ class AdventureMapShortcuts
|
||||
void showSpellbook();
|
||||
void adventureOptions();
|
||||
void systemOptions();
|
||||
void firstHero();
|
||||
void nextHero();
|
||||
void endTurn();
|
||||
void showThievesGuild();
|
||||
void showScenarioInfo();
|
||||
void toMainMenu();
|
||||
void newGame();
|
||||
void quitGame();
|
||||
void saveGame();
|
||||
void loadGame();
|
||||
void digGrail();
|
||||
@ -61,6 +65,7 @@ class AdventureMapShortcuts
|
||||
void visitObject();
|
||||
void openObject();
|
||||
void showMarketplace();
|
||||
void firstTown();
|
||||
void nextTown();
|
||||
void nextObject();
|
||||
void zoom( int distance);
|
||||
|
@ -45,7 +45,7 @@ AdventureOptions::AdventureOptions()
|
||||
scenInfo = std::make_shared<CButton>(Point(24, 198), AnimationPath::builtin("ADVINFO.DEF"), CButton::tooltip(), [&](){ close(); }, EShortcut::ADVENTURE_VIEW_SCENARIO);
|
||||
scenInfo->addCallback(AdventureOptions::showScenarioInfo);
|
||||
|
||||
replay = std::make_shared<CButton>(Point(24, 257), AnimationPath::builtin("ADVTURN.DEF"), CButton::tooltip(), [&](){ close(); });
|
||||
replay = std::make_shared<CButton>(Point(24, 257), AnimationPath::builtin("ADVTURN.DEF"), CButton::tooltip(), [&](){ close(); }, EShortcut::ADVENTURE_REPLAY_TURN);
|
||||
replay->addCallback([]{ LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.replayOpponentTurnNotImplemented")); });
|
||||
|
||||
exit = std::make_shared<CButton>(Point(203, 313), AnimationPath::builtin("IOK6432.DEF"), CButton::tooltip(), std::bind(&AdventureOptions::close, this), EShortcut::GLOBAL_RETURN);
|
||||
|
@ -158,9 +158,6 @@ void CInGameConsole::keyPressed (EShortcut key)
|
||||
break;
|
||||
|
||||
case EShortcut::GAME_ACTIVATE_CONSOLE:
|
||||
if(GH.isKeyboardAltDown())
|
||||
return; //QoL for alt-tab operating system shortcut
|
||||
|
||||
if(!enteredText.empty())
|
||||
endEnteringText(false);
|
||||
else
|
||||
|
@ -39,7 +39,6 @@
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/CHeroHandler.h"
|
||||
#include "../../lib/CondSh.h"
|
||||
#include "../../lib/gameState/InfoAboutArmy.h"
|
||||
#include "../../lib/mapObjects/CGTownInstance.h"
|
||||
#include "../../lib/networkPacks/PacksForClientBattle.h"
|
||||
@ -96,7 +95,7 @@ BattleInterface::BattleInterface(const BattleID & battleID, const CCreatureSet *
|
||||
obstacleController.reset(new BattleObstacleController(*this));
|
||||
|
||||
adventureInt->onAudioPaused();
|
||||
ongoingAnimationsState.set(true);
|
||||
ongoingAnimationsState.setBusy();
|
||||
|
||||
GH.windows().pushWindow(windowObject);
|
||||
windowObject->blockUI(true);
|
||||
@ -341,7 +340,7 @@ void BattleInterface::battleFinished(const BattleResult& br, QueryID queryID)
|
||||
GH.windows().pushWindow(wnd);
|
||||
|
||||
curInt->waitWhileDialog(); // Avoid freeze when AI end turn after battle. Check bug #1897
|
||||
CPlayerInterface::battleInt = nullptr;
|
||||
CPlayerInterface::battleInt.reset();
|
||||
}
|
||||
|
||||
void BattleInterface::spellCast(const BattleSpellCast * sc)
|
||||
@ -752,6 +751,11 @@ void BattleInterface::castThisSpell(SpellID spellID)
|
||||
actionsController->castThisSpell(spellID);
|
||||
}
|
||||
|
||||
void BattleInterface::endNetwork()
|
||||
{
|
||||
ongoingAnimationsState.requestTermination();
|
||||
}
|
||||
|
||||
void BattleInterface::executeStagedAnimations()
|
||||
{
|
||||
EAnimationEvents earliestStage = EAnimationEvents::COUNT;
|
||||
@ -783,19 +787,19 @@ void BattleInterface::executeAnimationStage(EAnimationEvents event)
|
||||
|
||||
void BattleInterface::onAnimationsStarted()
|
||||
{
|
||||
ongoingAnimationsState.setn(true);
|
||||
ongoingAnimationsState.setBusy();
|
||||
}
|
||||
|
||||
void BattleInterface::onAnimationsFinished()
|
||||
{
|
||||
ongoingAnimationsState.setn(false);
|
||||
ongoingAnimationsState.setFree();
|
||||
}
|
||||
|
||||
void BattleInterface::waitForAnimations()
|
||||
{
|
||||
{
|
||||
auto unlockInterface = vstd::makeUnlockGuard(GH.interfaceMutex);
|
||||
ongoingAnimationsState.waitUntil(false);
|
||||
ongoingAnimationsState.waitWhileBusy();
|
||||
}
|
||||
|
||||
assert(!hasAnimations());
|
||||
@ -810,7 +814,7 @@ void BattleInterface::waitForAnimations()
|
||||
|
||||
bool BattleInterface::hasAnimations()
|
||||
{
|
||||
return ongoingAnimationsState.get();
|
||||
return ongoingAnimationsState.isBusy();
|
||||
}
|
||||
|
||||
void BattleInterface::checkForAnimations()
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "BattleConstants.h"
|
||||
#include "../gui/CIntObject.h"
|
||||
#include "../../lib/spells/CSpellHandler.h" //CSpell::TAnimation
|
||||
#include "../../lib/CondSh.h"
|
||||
#include "../ConditionalWait.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
@ -99,7 +99,7 @@ class BattleInterface
|
||||
};
|
||||
|
||||
/// Conditional variables that are set depending on ongoing animations on the battlefield
|
||||
CondSh<bool> ongoingAnimationsState;
|
||||
ConditionalWait ongoingAnimationsState;
|
||||
|
||||
/// List of events that are waiting to be triggered
|
||||
std::vector<AwaitingAnimationEvents> awaitingEvents;
|
||||
@ -186,6 +186,7 @@ public:
|
||||
void setBattleQueueVisibility(bool visible);
|
||||
void setStickyHeroWindowsVisibility(bool visible);
|
||||
|
||||
void endNetwork();
|
||||
void executeStagedAnimations();
|
||||
void executeAnimationStage( EAnimationEvents event);
|
||||
void onAnimationsStarted();
|
||||
|
@ -52,7 +52,6 @@
|
||||
#include "../../lib/CTownHandler.h"
|
||||
#include "../../lib/CHeroHandler.h"
|
||||
#include "../../lib/StartInfo.h"
|
||||
#include "../../lib/CondSh.h"
|
||||
#include "../../lib/mapObjects/CGTownInstance.h"
|
||||
#include "../../lib/networkPacks/PacksForClientBattle.h"
|
||||
#include "../../lib/TextOperations.h"
|
||||
@ -83,6 +82,13 @@ std::vector<std::string> BattleConsole::getVisibleText()
|
||||
|
||||
auto result = CMessage::breakText(text, pos.w, FONT_SMALL);
|
||||
|
||||
if(result.size() > 2 && text.find('\n') != std::string::npos)
|
||||
{
|
||||
// Text has too many lines to fit into console, but has line breaks. Try ignore them and fit text that way
|
||||
std::string cleanText = boost::algorithm::replace_all_copy(text, "\n", " ");
|
||||
result = CMessage::breakText(cleanText, pos.w, FONT_SMALL);
|
||||
}
|
||||
|
||||
if(result.size() > 2)
|
||||
result.resize(2);
|
||||
return result;
|
||||
@ -801,7 +807,7 @@ BattleResultResources BattleResultWindow::getResources(const BattleResult & br)
|
||||
|
||||
void BattleResultWindow::activate()
|
||||
{
|
||||
owner.showingDialog->set(true);
|
||||
owner.showingDialog->setBusy();
|
||||
CIntObject::activate();
|
||||
}
|
||||
|
||||
@ -819,7 +825,7 @@ void BattleResultWindow::buttonPressed(int button)
|
||||
|
||||
//Result window and battle interface are gone. We requested all dialogs to be closed before opening the battle,
|
||||
//so we can be sure that there is no dialogs left on GUI stack.
|
||||
intTmp.showingDialog->setn(false);
|
||||
intTmp.showingDialog->setFree();
|
||||
}
|
||||
|
||||
void BattleResultWindow::bExitf()
|
||||
|
@ -39,7 +39,6 @@
|
||||
#include "../../lib/battle/BattleHex.h"
|
||||
#include "../../lib/CRandomGenerator.h"
|
||||
#include "../../lib/CStack.h"
|
||||
#include "../../lib/CondSh.h"
|
||||
#include "../../lib/TextOperations.h"
|
||||
|
||||
static void onAnimationFinished(const CStack *stack, std::weak_ptr<CreatureAnimation> anim)
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/Images.h"
|
||||
#include "../windows/CMessage.h"
|
||||
#include "../windows/CCreatureWindow.h"
|
||||
#include "../render/CAnimation.h"
|
||||
#include "../render/Canvas.h"
|
||||
#include "../render/IRenderHandler.h"
|
||||
@ -76,6 +77,8 @@ BattleWindow::BattleWindow(BattleInterface & owner):
|
||||
addShortcut(EShortcut::BATTLE_TACTICS_NEXT, std::bind(&BattleWindow::bTacticNextStack, this));
|
||||
addShortcut(EShortcut::BATTLE_TACTICS_END, std::bind(&BattleWindow::bTacticPhaseEnd, this));
|
||||
addShortcut(EShortcut::BATTLE_SELECT_ACTION, std::bind(&BattleWindow::bSwitchActionf, this));
|
||||
addShortcut(EShortcut::BATTLE_OPEN_ACTIVE_UNIT, std::bind(&BattleWindow::bOpenActiveUnit, this));
|
||||
addShortcut(EShortcut::BATTLE_OPEN_HOVERED_UNIT, std::bind(&BattleWindow::bOpenHoveredUnit, this));
|
||||
|
||||
addShortcut(EShortcut::BATTLE_TOGGLE_QUEUE, [this](){ this->toggleQueueVisibility();});
|
||||
addShortcut(EShortcut::BATTLE_TOGGLE_HEROES_STATS, [this](){ this->toggleStickyHeroWindowsVisibility();});
|
||||
@ -188,11 +191,6 @@ void BattleWindow::createTimerInfoWindows()
|
||||
}
|
||||
}
|
||||
|
||||
BattleWindow::~BattleWindow()
|
||||
{
|
||||
CPlayerInterface::battleInt = nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<BattleConsole> BattleWindow::buildBattleConsole(const JsonNode & config) const
|
||||
{
|
||||
auto rect = readRect(config["rect"]);
|
||||
@ -754,6 +752,8 @@ void BattleWindow::blockUI(bool on)
|
||||
bool canWait = owner.stacksController->getActiveStack() ? !owner.stacksController->getActiveStack()->waitedThisTurn : false;
|
||||
|
||||
setShortcutBlocked(EShortcut::GLOBAL_OPTIONS, on);
|
||||
setShortcutBlocked(EShortcut::BATTLE_OPEN_ACTIVE_UNIT, on);
|
||||
setShortcutBlocked(EShortcut::BATTLE_OPEN_HOVERED_UNIT, on);
|
||||
setShortcutBlocked(EShortcut::BATTLE_RETREAT, on || !owner.getBattle()->battleCanFlee());
|
||||
setShortcutBlocked(EShortcut::BATTLE_SURRENDER, on || owner.getBattle()->battleGetSurrenderCost() < 0);
|
||||
setShortcutBlocked(EShortcut::BATTLE_CAST_SPELL, on || owner.tacticsMode || !canCastSpells);
|
||||
@ -768,6 +768,26 @@ void BattleWindow::blockUI(bool on)
|
||||
setShortcutBlocked(EShortcut::BATTLE_CONSOLE_UP, on && !owner.tacticsMode);
|
||||
}
|
||||
|
||||
void BattleWindow::bOpenActiveUnit()
|
||||
{
|
||||
const auto * unit = owner.stacksController->getActiveStack();
|
||||
|
||||
if (unit)
|
||||
GH.windows().createAndPushWindow<CStackWindow>(unit, false);;
|
||||
}
|
||||
|
||||
void BattleWindow::bOpenHoveredUnit()
|
||||
{
|
||||
const auto units = owner.stacksController->getHoveredStacksUnitIds();
|
||||
|
||||
if (!units.empty())
|
||||
{
|
||||
const auto * unit = owner.getBattle()->battleGetStackByID(units[0]);
|
||||
if (unit)
|
||||
GH.windows().createAndPushWindow<CStackWindow>(unit, false);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<uint32_t> BattleWindow::getQueueHoveredUnitId()
|
||||
{
|
||||
return queue->getHoveredUnitIdIfAny();
|
||||
|
@ -56,6 +56,8 @@ class BattleWindow : public InterfaceObjectConfigurable
|
||||
void bConsoleDownf();
|
||||
void bTacticNextStack();
|
||||
void bTacticPhaseEnd();
|
||||
void bOpenActiveUnit();
|
||||
void bOpenHoveredUnit();
|
||||
|
||||
/// functions for handling actions after they were confirmed by popup window
|
||||
void reallyFlee();
|
||||
@ -80,7 +82,6 @@ class BattleWindow : public InterfaceObjectConfigurable
|
||||
|
||||
public:
|
||||
BattleWindow(BattleInterface & owner );
|
||||
~BattleWindow();
|
||||
|
||||
/// Closes window once battle finished
|
||||
void close();
|
||||
|
@ -165,6 +165,7 @@ void InputHandler::preprocessEvent(const SDL_Event & ev)
|
||||
{
|
||||
if(ev.key.keysym.sym == SDLK_F4 && (ev.key.keysym.mod & KMOD_ALT))
|
||||
{
|
||||
// FIXME: dead code? Looks like intercepted by OS/SDL and delivered as SDL_Quit instead?
|
||||
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
|
||||
handleQuit(true);
|
||||
return;
|
||||
@ -176,16 +177,6 @@ void InputHandler::preprocessEvent(const SDL_Event & ev)
|
||||
handleQuit(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if(ev.type == SDL_KEYDOWN && ev.key.keysym.sym == SDLK_F4)
|
||||
{
|
||||
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
|
||||
Settings full = settings.write["video"]["fullscreen"];
|
||||
full->Bool() = !full->Bool();
|
||||
|
||||
GH.onScreenResize(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if(ev.type == SDL_USEREVENT)
|
||||
{
|
||||
|
@ -75,9 +75,16 @@ void InputSourceKeyboard::handleEventKeyDown(const SDL_KeyboardEvent & key)
|
||||
|
||||
auto shortcutsVector = GH.shortcuts().translateKeycode(keyName);
|
||||
|
||||
if (vstd::contains(shortcutsVector, EShortcut::LOBBY_ACTIVATE_INTERFACE))
|
||||
if (vstd::contains(shortcutsVector, EShortcut::MAIN_MENU_LOBBY))
|
||||
CSH->getGlobalLobby().activateInterface();
|
||||
|
||||
if (vstd::contains(shortcutsVector, EShortcut::GLOBAL_FULLSCREEN))
|
||||
{
|
||||
Settings full = settings.write["video"]["fullscreen"];
|
||||
full->Bool() = !full->Bool();
|
||||
GH.onScreenResize(true);
|
||||
}
|
||||
|
||||
if (vstd::contains(shortcutsVector, EShortcut::SPECTATE_TRACK_HERO))
|
||||
{
|
||||
Settings s = settings.write["session"];
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include "../CServerHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/GraphicalPrimitiveCanvas.h"
|
||||
#include "../widgets/Images.h"
|
||||
@ -95,7 +96,7 @@ GlobalLobbyInviteWindow::GlobalLobbyInviteWindow()
|
||||
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));
|
||||
|
||||
buttonClose = std::make_shared<CButton>(Point(86, 384), AnimationPath::builtin("MuBchck"), CButton::tooltip(), [this]() { close(); } );
|
||||
buttonClose = std::make_shared<CButton>(Point(86, 384), AnimationPath::builtin("MuBchck"), CButton::tooltip(), [this]() { close(); }, EShortcut::GLOBAL_RETURN );
|
||||
|
||||
center();
|
||||
}
|
||||
|
@ -12,12 +12,11 @@
|
||||
#include "GlobalLobbyLoginWindow.h"
|
||||
|
||||
#include "GlobalLobbyClient.h"
|
||||
#include "GlobalLobbyWindow.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/CTextInput.h"
|
||||
#include "../widgets/Images.h"
|
||||
@ -47,8 +46,8 @@ GlobalLobbyLoginWindow::GlobalLobbyLoginWindow()
|
||||
labelUsername = std::make_shared<CLabel>( 10, 65, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, loginAs.toString());
|
||||
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);
|
||||
buttonLogin = std::make_shared<CButton>(Point(10, 180), AnimationPath::builtin("MuBchck"), CButton::tooltip(), [this](){ onLogin(); });
|
||||
buttonClose = std::make_shared<CButton>(Point(210, 180), AnimationPath::builtin("MuBcanc"), CButton::tooltip(), [this](){ onClose(); });
|
||||
buttonLogin = std::make_shared<CButton>(Point(10, 180), AnimationPath::builtin("MuBchck"), CButton::tooltip(), [this](){ onLogin(); }, EShortcut::GLOBAL_ACCEPT);
|
||||
buttonClose = std::make_shared<CButton>(Point(210, 180), AnimationPath::builtin("MuBcanc"), CButton::tooltip(), [this](){ onClose(); }, EShortcut::GLOBAL_CANCEL);
|
||||
labelStatus = std::make_shared<CTextBox>( "", Rect(15, 115, 255, 60), 1, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE);
|
||||
|
||||
auto buttonRegister = std::make_shared<CToggleButton>(Point(10, 40), AnimationPath::builtin("GSPBUT2"), CButton::tooltip(), 0);
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../mainmenu/CMainMenu.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/Images.h"
|
||||
@ -25,10 +26,8 @@
|
||||
#include "../widgets/GraphicalPrimitiveCanvas.h"
|
||||
#include "../widgets/ObjectLists.h"
|
||||
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/MetaString.h"
|
||||
#include "../../lib/VCMI_Lib.h"
|
||||
#include "../../lib/modding/CModHandler.h"
|
||||
#include "../../lib/modding/CModInfo.h"
|
||||
|
||||
@ -148,8 +147,8 @@ GlobalLobbyRoomWindow::GlobalLobbyRoomWindow(GlobalLobbyWindow * window, const s
|
||||
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);
|
||||
|
||||
buttonJoin = std::make_shared<CButton>(Point(10, 360), AnimationPath::builtin("MuBchck"), CButton::tooltip(), [this](){ onJoin(); });
|
||||
buttonClose = std::make_shared<CButton>(Point(100, 360), AnimationPath::builtin("MuBcanc"), CButton::tooltip(), [this](){ onClose(); });
|
||||
buttonJoin = std::make_shared<CButton>(Point(10, 360), AnimationPath::builtin("MuBchck"), CButton::tooltip(), [this](){ onJoin(); }, EShortcut::GLOBAL_ACCEPT);
|
||||
buttonClose = std::make_shared<CButton>(Point(100, 360), AnimationPath::builtin("MuBcanc"), CButton::tooltip(), [this](){ onClose(); }, EShortcut::GLOBAL_CANCEL);
|
||||
|
||||
MetaString joinStatusText;
|
||||
std::string errorMessage = getJoinRoomErrorMessage(roomDescription, modVerificationList);
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../mainmenu/CMainMenu.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/Images.h"
|
||||
@ -74,8 +75,8 @@ GlobalLobbyServerSetup::GlobalLobbyServerSetup()
|
||||
|
||||
labelDescription = std::make_shared<CTextBox>("", Rect(10, 195, pos.w - 20, 80), 1, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE);
|
||||
|
||||
buttonCreate = std::make_shared<CButton>(Point(10, 300), AnimationPath::builtin("MuBchck"), CButton::tooltip(), [this](){ onCreate(); });
|
||||
buttonClose = std::make_shared<CButton>(Point(210, 300), AnimationPath::builtin("MuBcanc"), CButton::tooltip(), [this](){ onClose(); });
|
||||
buttonCreate = std::make_shared<CButton>(Point(10, 300), AnimationPath::builtin("MuBchck"), CButton::tooltip(), [this](){ onCreate(); }, EShortcut::GLOBAL_ACCEPT);
|
||||
buttonClose = std::make_shared<CButton>(Point(210, 300), AnimationPath::builtin("MuBcanc"), CButton::tooltip(), [this](){ onClose(); }, EShortcut::GLOBAL_CANCEL);
|
||||
|
||||
filledBackground->playerColored(PlayerColor(1));
|
||||
|
||||
|
@ -9,7 +9,6 @@
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "CGuiHandler.h"
|
||||
#include "../lib/CondSh.h"
|
||||
|
||||
#include "CIntObject.h"
|
||||
#include "CursorHandler.h"
|
||||
|
@ -10,7 +10,6 @@
|
||||
#pragma once
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
template <typename T> struct CondSh;
|
||||
class Point;
|
||||
class Rect;
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@ -492,7 +492,7 @@ void InterfaceObjectConfigurable::loadButtonHotkey(std::shared_ptr<CButton> butt
|
||||
return;
|
||||
|
||||
button->addCallback(target->second.callback);
|
||||
target->second.assignedToButton = true;
|
||||
target->second.assignedButtons.push_back(button);
|
||||
}
|
||||
|
||||
std::shared_ptr<CLabelGroup> InterfaceObjectConfigurable::buildLabelGroup(const JsonNode & config) const
|
||||
@ -821,8 +821,9 @@ void InterfaceObjectConfigurable::keyPressed(EShortcut key)
|
||||
if (target == shortcuts.end())
|
||||
return;
|
||||
|
||||
if (target->second.assignedToButton)
|
||||
return; // will be handled by button instance
|
||||
for (auto const & button :target->second.assignedButtons)
|
||||
if (button->isActive())
|
||||
return; // will be handled by button instance
|
||||
|
||||
if (target->second.blocked)
|
||||
return;
|
||||
|
@ -118,7 +118,7 @@ private:
|
||||
struct ShortcutState
|
||||
{
|
||||
std::function<void()> callback;
|
||||
mutable bool assignedToButton = false;
|
||||
mutable std::vector<std::shared_ptr<CButton>> assignedButtons;
|
||||
bool blocked = false;
|
||||
};
|
||||
|
||||
|
@ -66,25 +66,55 @@ enum class EShortcut
|
||||
MAIN_MENU_CAMPAIGN_AB,
|
||||
MAIN_MENU_CAMPAIGN_CUSTOM,
|
||||
|
||||
MAIN_MENU_HOTSEAT,
|
||||
MAIN_MENU_LOBBY,
|
||||
MAIN_MENU_HOST_GAME,
|
||||
MAIN_MENU_JOIN_GAME,
|
||||
|
||||
HIGH_SCORES_CAMPAIGNS,
|
||||
HIGH_SCORES_SCENARIOS,
|
||||
HIGH_SCORES_RESET,
|
||||
|
||||
// Game lobby / scenario selection
|
||||
LOBBY_BEGIN_STANDARD_GAME, // b
|
||||
LOBBY_BEGIN_CAMPAIGN, // Return
|
||||
LOBBY_LOAD_GAME, // l, Return
|
||||
LOBBY_SAVE_GAME, // s, Return
|
||||
LOBBY_RANDOM_MAP, // Open random map tab
|
||||
LOBBY_HIDE_CHAT,
|
||||
LOBBY_TOGGLE_CHAT,
|
||||
LOBBY_ADDITIONAL_OPTIONS, // Open additional options tab
|
||||
LOBBY_SELECT_SCENARIO, // Open map list tab
|
||||
LOBBY_REPLAY_VIDEO,
|
||||
LOBBY_EXTRA_OPTIONS,
|
||||
LOBBY_TURN_OPTIONS,
|
||||
LOBBY_INVITE_PLAYERS,
|
||||
LOBBY_FLIP_COIN,
|
||||
LOBBY_RANDOM_TOWN,
|
||||
LOBBY_RANDOM_TOWN_VS,
|
||||
|
||||
MAPS_SIZE_S,
|
||||
MAPS_SIZE_M,
|
||||
MAPS_SIZE_L,
|
||||
MAPS_SIZE_XL,
|
||||
MAPS_SIZE_ALL,
|
||||
|
||||
MAPS_SORT_PLAYERS,
|
||||
MAPS_SORT_SIZE,
|
||||
MAPS_SORT_FORMAT,
|
||||
MAPS_SORT_NAME,
|
||||
MAPS_SORT_VICTORY,
|
||||
MAPS_SORT_DEFEAT,
|
||||
MAPS_SORT_MAPS,
|
||||
MAPS_SORT_CHANGEDATE,
|
||||
|
||||
SETTINGS_LOAD_GAME,
|
||||
SETTINGS_SAVE_GAME,
|
||||
SETTINGS_NEW_GAME,
|
||||
SETTINGS_RESTART_GAME,
|
||||
SETTINGS_TO_MAIN_MENU,
|
||||
SETTINGS_QUIT_GAME,
|
||||
|
||||
// In-game hotkeys, require game state but may be available in windows other than adventure map
|
||||
GAME_END_TURN,
|
||||
GAME_LOAD_GAME,
|
||||
GAME_SAVE_GAME,
|
||||
GAME_RESTART_GAME,
|
||||
GAME_TO_MAIN_MENU,
|
||||
GAME_QUIT_GAME,
|
||||
GAME_OPEN_MARKETPLACE,
|
||||
GAME_OPEN_THIEVES_GUILD,
|
||||
GAME_ACTIVATE_CONSOLE, // Tab, activates in-game console
|
||||
|
||||
// Adventure map screen
|
||||
@ -98,10 +128,11 @@ enum class EShortcut
|
||||
ADVENTURE_VIEW_SELECTED,// Open window with currently selected hero/town
|
||||
ADVENTURE_NEXT_TOWN,
|
||||
ADVENTURE_NEXT_HERO,
|
||||
ADVENTURE_NEXT_OBJECT, // TODO: context-sensitive next object - select next hero/town, depending on current selection
|
||||
ADVENTURE_FIRST_TOWN, // TODO: select first available town in the list
|
||||
ADVENTURE_FIRST_HERO, // TODO: select first available hero in the list
|
||||
ADVENTURE_NEXT_OBJECT, // context-sensitive next object - select next hero/town, depending on current selection
|
||||
ADVENTURE_FIRST_TOWN, // select first available town in the list
|
||||
ADVENTURE_FIRST_HERO, // select first available hero in the list
|
||||
ADVENTURE_VIEW_SCENARIO,// View Scenario Information window
|
||||
ADVENTURE_REPLAY_TURN,
|
||||
ADVENTURE_DIG_GRAIL,
|
||||
ADVENTURE_VIEW_PUZZLE,
|
||||
ADVENTURE_VIEW_WORLD,
|
||||
@ -113,11 +144,19 @@ enum class EShortcut
|
||||
ADVENTURE_KINGDOM_OVERVIEW,
|
||||
ADVENTURE_QUEST_LOG,
|
||||
ADVENTURE_CAST_SPELL,
|
||||
ADVENTURE_MARKETPLACE,
|
||||
ADVENTURE_THIEVES_GUILD,
|
||||
ADVENTURE_EXIT_WORLD_VIEW,
|
||||
ADVENTURE_ZOOM_IN,
|
||||
ADVENTURE_ZOOM_OUT,
|
||||
ADVENTURE_ZOOM_RESET,
|
||||
ADVENTURE_END_TURN,
|
||||
ADVENTURE_LOAD_GAME,
|
||||
ADVENTURE_SAVE_GAME,
|
||||
ADVENTURE_NEW_GAME,
|
||||
ADVENTURE_RESTART_GAME,
|
||||
ADVENTURE_TO_MAIN_MENU,
|
||||
ADVENTURE_QUIT_GAME,
|
||||
|
||||
// Move hero one tile in specified direction. Bound to cursors & numpad buttons
|
||||
ADVENTURE_MOVE_HERO_SW,
|
||||
@ -145,8 +184,20 @@ enum class EShortcut
|
||||
BATTLE_TACTICS_END,
|
||||
BATTLE_SELECT_ACTION, // Alternative actions toggle
|
||||
BATTLE_TOGGLE_HEROES_STATS,
|
||||
BATTLE_OPEN_ACTIVE_UNIT,
|
||||
BATTLE_OPEN_HOVERED_UNIT,
|
||||
|
||||
LOBBY_ACTIVATE_INTERFACE,
|
||||
MARKET_DEAL,
|
||||
MARKET_MAX_AMOUNT,
|
||||
MARKET_SACRIFICE_ALL,
|
||||
MARKET_SACRIFICE_BACKPACK,
|
||||
MARKET_RESOURCE_PLAYER,
|
||||
MARKET_ARTIFACT_RESOURCE,
|
||||
MARKET_RESOURCE_ARTIFACT,
|
||||
MARKET_CREATURE_RESOURCE,
|
||||
MARKET_RESOURCE_RESOURCE,
|
||||
MARKET_CREATURE_EXPERIENCE,
|
||||
MARKET_ARTIFACT_EXPERIENCE,
|
||||
|
||||
SPECTATE_TRACK_HERO,
|
||||
SPECTATE_SKIP_BATTLE,
|
||||
@ -154,11 +205,22 @@ enum class EShortcut
|
||||
|
||||
// Town screen
|
||||
TOWN_OPEN_TAVERN,
|
||||
TOWN_OPEN_HALL,
|
||||
TOWN_OPEN_FORT,
|
||||
TOWN_OPEN_MARKET,
|
||||
TOWN_OPEN_MAGE_GUILD,
|
||||
TOWN_OPEN_THIEVES_GUILD,
|
||||
TOWN_OPEN_RECRUITMENT,
|
||||
TOWN_OPEN_HERO_EXCHANGE,
|
||||
TOWN_OPEN_HERO,
|
||||
TOWN_OPEN_VISITING_HERO,
|
||||
TOWN_OPEN_GARRISONED_HERO,
|
||||
TOWN_SWAP_ARMIES, // Swap garrisoned and visiting armies
|
||||
|
||||
// Creature & creature recruitment screen
|
||||
RECRUITMENT_MAX, // Set number of creatures to recruit to max
|
||||
RECRUITMENT_MIN, // Set number of creatures to recruit to min (1)
|
||||
RECRUITMENT_SWITCH_LEVEL,
|
||||
RECRUITMENT_UPGRADE, // Upgrade current creature
|
||||
RECRUITMENT_UPGRADE_ALL, // Upgrade all creatures (Hill Fort / Skeleton Transformer)
|
||||
|
||||
@ -172,17 +234,44 @@ enum class EShortcut
|
||||
HERO_LOOSE_FORMATION,
|
||||
HERO_TIGHT_FORMATION,
|
||||
HERO_TOGGLE_TACTICS, // b
|
||||
HERO_ARMY_SPLIT,
|
||||
HERO_BACKPACK,
|
||||
HERO_COSTUME_0,
|
||||
HERO_COSTUME_1,
|
||||
HERO_COSTUME_2,
|
||||
HERO_COSTUME_3,
|
||||
HERO_COSTUME_4,
|
||||
HERO_COSTUME_5,
|
||||
HERO_COSTUME_6,
|
||||
HERO_COSTUME_7,
|
||||
HERO_COSTUME_8,
|
||||
HERO_COSTUME_9,
|
||||
HERO_COSTUME_SAVE_0,
|
||||
HERO_COSTUME_SAVE_1,
|
||||
HERO_COSTUME_SAVE_2,
|
||||
HERO_COSTUME_SAVE_3,
|
||||
HERO_COSTUME_SAVE_4,
|
||||
HERO_COSTUME_SAVE_5,
|
||||
HERO_COSTUME_SAVE_6,
|
||||
HERO_COSTUME_SAVE_7,
|
||||
HERO_COSTUME_SAVE_8,
|
||||
HERO_COSTUME_SAVE_9,
|
||||
|
||||
HERO_COSTUME_LOAD_0,
|
||||
HERO_COSTUME_LOAD_1,
|
||||
HERO_COSTUME_LOAD_2,
|
||||
HERO_COSTUME_LOAD_3,
|
||||
HERO_COSTUME_LOAD_4,
|
||||
HERO_COSTUME_LOAD_5,
|
||||
HERO_COSTUME_LOAD_6,
|
||||
HERO_COSTUME_LOAD_7,
|
||||
HERO_COSTUME_LOAD_8,
|
||||
HERO_COSTUME_LOAD_9,
|
||||
|
||||
EXCHANGE_ARMY_TO_LEFT,
|
||||
EXCHANGE_ARMY_TO_RIGHT,
|
||||
EXCHANGE_ARMY_SWAP,
|
||||
EXCHANGE_ARTIFACTS_TO_LEFT,
|
||||
EXCHANGE_ARTIFACTS_TO_RIGHT,
|
||||
EXCHANGE_ARTIFACTS_SWAP,
|
||||
EXCHANGE_EQUIPPED_TO_LEFT,
|
||||
EXCHANGE_EQUIPPED_TO_RIGHT,
|
||||
EXCHANGE_EQUIPPED_SWAP,
|
||||
EXCHANGE_BACKPACK_TO_LEFT,
|
||||
EXCHANGE_BACKPACK_TO_RIGHT,
|
||||
EXCHANGE_BACKPACK_SWAP,
|
||||
EXCHANGE_BACKPACK_LEFT,
|
||||
EXCHANGE_BACKPACK_RIGHT,
|
||||
|
||||
// Spellbook screen
|
||||
SPELLBOOK_TAB_ADVENTURE,
|
||||
|
@ -22,6 +22,25 @@ ShortcutHandler::ShortcutHandler()
|
||||
mappedKeyboardShortcuts = loadShortcuts(config["keyboard"]);
|
||||
mappedJoystickShortcuts = loadShortcuts(config["joystickButtons"]);
|
||||
mappedJoystickAxes = loadShortcuts(config["joystickAxes"]);
|
||||
|
||||
#ifndef ENABLE_GOLDMASTER
|
||||
std::vector<EShortcut> assignedShortcuts;
|
||||
std::vector<EShortcut> missingShortcuts;
|
||||
|
||||
for (auto const & entry : config["keyboard"].Struct())
|
||||
{
|
||||
EShortcut shortcutID = findShortcut(entry.first);
|
||||
assert(!vstd::contains(assignedShortcuts, shortcutID));
|
||||
assignedShortcuts.push_back(shortcutID);
|
||||
}
|
||||
|
||||
for (EShortcut id = vstd::next(EShortcut::NONE, 1); id < EShortcut::AFTER_LAST; id = vstd::next(id, 1))
|
||||
if (!vstd::contains(assignedShortcuts, id))
|
||||
missingShortcuts.push_back(id);
|
||||
|
||||
if (!missingShortcuts.empty())
|
||||
logGlobal->error("Found %d shortcuts without config entry!", missingShortcuts.size());
|
||||
#endif
|
||||
}
|
||||
|
||||
std::multimap<std::string, EShortcut> ShortcutHandler::loadShortcuts(const JsonNode & data) const
|
||||
@ -131,22 +150,24 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const
|
||||
{"mainMenuCampaignRoe", EShortcut::MAIN_MENU_CAMPAIGN_ROE },
|
||||
{"mainMenuCampaignAb", EShortcut::MAIN_MENU_CAMPAIGN_AB },
|
||||
{"mainMenuCampaignCustom", EShortcut::MAIN_MENU_CAMPAIGN_CUSTOM },
|
||||
{"mainMenuLobby", EShortcut::MAIN_MENU_LOBBY },
|
||||
{"lobbyBeginStandardGame", EShortcut::LOBBY_BEGIN_STANDARD_GAME },
|
||||
{"lobbyBeginCampaign", EShortcut::LOBBY_BEGIN_CAMPAIGN },
|
||||
{"lobbyLoadGame", EShortcut::LOBBY_LOAD_GAME },
|
||||
{"lobbySaveGame", EShortcut::LOBBY_SAVE_GAME },
|
||||
{"lobbyRandomMap", EShortcut::LOBBY_RANDOM_MAP },
|
||||
{"lobbyHideChat", EShortcut::LOBBY_HIDE_CHAT },
|
||||
{"lobbyToggleChat", EShortcut::LOBBY_TOGGLE_CHAT },
|
||||
{"lobbyAdditionalOptions", EShortcut::LOBBY_ADDITIONAL_OPTIONS },
|
||||
{"lobbySelectScenario", EShortcut::LOBBY_SELECT_SCENARIO },
|
||||
{"gameEndTurn", EShortcut::GAME_END_TURN },
|
||||
{"gameLoadGame", EShortcut::GAME_LOAD_GAME },
|
||||
{"gameSaveGame", EShortcut::GAME_SAVE_GAME },
|
||||
{"gameRestartGame", EShortcut::GAME_RESTART_GAME },
|
||||
{"gameMainMenu", EShortcut::GAME_TO_MAIN_MENU },
|
||||
{"gameQuitGame", EShortcut::GAME_QUIT_GAME },
|
||||
{"gameOpenMarketplace", EShortcut::GAME_OPEN_MARKETPLACE },
|
||||
{"gameOpenThievesGuild", EShortcut::GAME_OPEN_THIEVES_GUILD },
|
||||
{"gameEndTurn", EShortcut::ADVENTURE_END_TURN }, // compatibility ID - extra's use this string
|
||||
{"adventureEndTurn", EShortcut::ADVENTURE_END_TURN },
|
||||
{"adventureLoadGame", EShortcut::ADVENTURE_LOAD_GAME },
|
||||
{"adventureSaveGame", EShortcut::ADVENTURE_SAVE_GAME },
|
||||
{"adventureRestartGame", EShortcut::ADVENTURE_RESTART_GAME },
|
||||
{"adventureMainMenu", EShortcut::ADVENTURE_TO_MAIN_MENU },
|
||||
{"adventureQuitGame", EShortcut::ADVENTURE_QUIT_GAME },
|
||||
{"adventureMarketplace", EShortcut::ADVENTURE_MARKETPLACE },
|
||||
{"adventureThievesGuild", EShortcut::ADVENTURE_THIEVES_GUILD },
|
||||
{"gameActivateConsole", EShortcut::GAME_ACTIVATE_CONSOLE },
|
||||
{"adventureGameOptions", EShortcut::ADVENTURE_GAME_OPTIONS },
|
||||
{"adventureToggleGrid", EShortcut::ADVENTURE_TOGGLE_GRID },
|
||||
@ -201,7 +222,6 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const
|
||||
{"battleTacticsNext", EShortcut::BATTLE_TACTICS_NEXT },
|
||||
{"battleTacticsEnd", EShortcut::BATTLE_TACTICS_END },
|
||||
{"battleSelectAction", EShortcut::BATTLE_SELECT_ACTION },
|
||||
{"lobbyActivateInterface", EShortcut::LOBBY_ACTIVATE_INTERFACE },
|
||||
{"spectateTrackHero", EShortcut::SPECTATE_TRACK_HERO },
|
||||
{"spectateSkipBattle", EShortcut::SPECTATE_SKIP_BATTLE },
|
||||
{"spectateSkipBattleResult", EShortcut::SPECTATE_SKIP_BATTLE_RESULT },
|
||||
@ -218,20 +238,118 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const
|
||||
{"heroLooseFormation", EShortcut::HERO_LOOSE_FORMATION },
|
||||
{"heroTightFormation", EShortcut::HERO_TIGHT_FORMATION },
|
||||
{"heroToggleTactics", EShortcut::HERO_TOGGLE_TACTICS },
|
||||
{"heroCostume0", EShortcut::HERO_COSTUME_0 },
|
||||
{"heroCostume1", EShortcut::HERO_COSTUME_1 },
|
||||
{"heroCostume2", EShortcut::HERO_COSTUME_2 },
|
||||
{"heroCostume3", EShortcut::HERO_COSTUME_3 },
|
||||
{"heroCostume4", EShortcut::HERO_COSTUME_4 },
|
||||
{"heroCostume5", EShortcut::HERO_COSTUME_5 },
|
||||
{"heroCostume6", EShortcut::HERO_COSTUME_6 },
|
||||
{"heroCostume7", EShortcut::HERO_COSTUME_7 },
|
||||
{"heroCostume8", EShortcut::HERO_COSTUME_8 },
|
||||
{"heroCostume9", EShortcut::HERO_COSTUME_9 },
|
||||
{"heroCostumeSave0", EShortcut::HERO_COSTUME_SAVE_0 },
|
||||
{"heroCostumeSave1", EShortcut::HERO_COSTUME_SAVE_1 },
|
||||
{"heroCostumeSave2", EShortcut::HERO_COSTUME_SAVE_2 },
|
||||
{"heroCostumeSave3", EShortcut::HERO_COSTUME_SAVE_3 },
|
||||
{"heroCostumeSave4", EShortcut::HERO_COSTUME_SAVE_4 },
|
||||
{"heroCostumeSave5", EShortcut::HERO_COSTUME_SAVE_5 },
|
||||
{"heroCostumeSave6", EShortcut::HERO_COSTUME_SAVE_6 },
|
||||
{"heroCostumeSave7", EShortcut::HERO_COSTUME_SAVE_7 },
|
||||
{"heroCostumeSave8", EShortcut::HERO_COSTUME_SAVE_8 },
|
||||
{"heroCostumeSave9", EShortcut::HERO_COSTUME_SAVE_9 },
|
||||
{"heroCostumeLoad0", EShortcut::HERO_COSTUME_LOAD_0 },
|
||||
{"heroCostumeLoad1", EShortcut::HERO_COSTUME_LOAD_1 },
|
||||
{"heroCostumeLoad2", EShortcut::HERO_COSTUME_LOAD_2 },
|
||||
{"heroCostumeLoad3", EShortcut::HERO_COSTUME_LOAD_3 },
|
||||
{"heroCostumeLoad4", EShortcut::HERO_COSTUME_LOAD_4 },
|
||||
{"heroCostumeLoad5", EShortcut::HERO_COSTUME_LOAD_5 },
|
||||
{"heroCostumeLoad6", EShortcut::HERO_COSTUME_LOAD_6 },
|
||||
{"heroCostumeLoad7", EShortcut::HERO_COSTUME_LOAD_7 },
|
||||
{"heroCostumeLoad8", EShortcut::HERO_COSTUME_LOAD_8 },
|
||||
{"heroCostumeLoad9", EShortcut::HERO_COSTUME_LOAD_9 },
|
||||
{"spellbookTabAdventure", EShortcut::SPELLBOOK_TAB_ADVENTURE },
|
||||
{"spellbookTabCombat", EShortcut::SPELLBOOK_TAB_COMBAT }
|
||||
{"spellbookTabCombat", EShortcut::SPELLBOOK_TAB_COMBAT },
|
||||
{"mainMenuHotseat", EShortcut::MAIN_MENU_HOTSEAT },
|
||||
{"mainMenuHostGame", EShortcut::MAIN_MENU_HOST_GAME },
|
||||
{"mainMenuJoinGame", EShortcut::MAIN_MENU_JOIN_GAME },
|
||||
{"highScoresCampaigns", EShortcut::HIGH_SCORES_CAMPAIGNS },
|
||||
{"highScoresScenarios", EShortcut::HIGH_SCORES_SCENARIOS },
|
||||
{"highScoresReset", EShortcut::HIGH_SCORES_RESET },
|
||||
{"lobbyReplayVideo", EShortcut::LOBBY_REPLAY_VIDEO },
|
||||
{"lobbyExtraOptions", EShortcut::LOBBY_EXTRA_OPTIONS },
|
||||
{"lobbyTurnOptions", EShortcut::LOBBY_TURN_OPTIONS },
|
||||
{"lobbyInvitePlayers", EShortcut::LOBBY_INVITE_PLAYERS },
|
||||
{"lobbyFlipCoin", EShortcut::LOBBY_FLIP_COIN },
|
||||
{"lobbyRandomTown", EShortcut::LOBBY_RANDOM_TOWN },
|
||||
{"lobbyRandomTownVs", EShortcut::LOBBY_RANDOM_TOWN_VS },
|
||||
{"mapsSizeS", EShortcut::MAPS_SIZE_S },
|
||||
{"mapsSizeM", EShortcut::MAPS_SIZE_M },
|
||||
{"mapsSizeL", EShortcut::MAPS_SIZE_L },
|
||||
{"mapsSizeXl", EShortcut::MAPS_SIZE_XL },
|
||||
{"mapsSizeAll", EShortcut::MAPS_SIZE_ALL },
|
||||
{"mapsSortPlayers", EShortcut::MAPS_SORT_PLAYERS },
|
||||
{"mapsSortSize", EShortcut::MAPS_SORT_SIZE },
|
||||
{"mapsSortFormat", EShortcut::MAPS_SORT_FORMAT },
|
||||
{"mapsSortName", EShortcut::MAPS_SORT_NAME },
|
||||
{"mapsSortVictory", EShortcut::MAPS_SORT_VICTORY },
|
||||
{"mapsSortDefeat", EShortcut::MAPS_SORT_DEFEAT },
|
||||
{"mapsSortMaps", EShortcut::MAPS_SORT_MAPS },
|
||||
{"mapsSortChangedate", EShortcut::MAPS_SORT_CHANGEDATE },
|
||||
{"settingsLoadGame", EShortcut::SETTINGS_LOAD_GAME },
|
||||
{"settingsSaveGame", EShortcut::SETTINGS_SAVE_GAME },
|
||||
{"settingsNewGame", EShortcut::SETTINGS_NEW_GAME },
|
||||
{"settingsRestartGame", EShortcut::SETTINGS_RESTART_GAME },
|
||||
{"settingsToMainMenu", EShortcut::SETTINGS_TO_MAIN_MENU },
|
||||
{"settingsQuitGame", EShortcut::SETTINGS_QUIT_GAME },
|
||||
{"adventureReplayTurn", EShortcut::ADVENTURE_REPLAY_TURN },
|
||||
{"adventureNewGame", EShortcut::ADVENTURE_NEW_GAME },
|
||||
{"battleOpenActiveUnit", EShortcut::BATTLE_OPEN_ACTIVE_UNIT },
|
||||
{"battleOpenHoveredUnit", EShortcut::BATTLE_OPEN_HOVERED_UNIT },
|
||||
{"marketDeal", EShortcut::MARKET_DEAL },
|
||||
{"marketMaxAmount", EShortcut::MARKET_MAX_AMOUNT },
|
||||
{"marketSacrificeAll", EShortcut::MARKET_SACRIFICE_ALL },
|
||||
{"marketSacrificeBackpack", EShortcut::MARKET_SACRIFICE_BACKPACK },
|
||||
{"marketResourcePlayer", EShortcut::MARKET_RESOURCE_PLAYER },
|
||||
{"marketArtifactResource", EShortcut::MARKET_ARTIFACT_RESOURCE },
|
||||
{"marketResourceArtifact", EShortcut::MARKET_RESOURCE_ARTIFACT },
|
||||
{"marketCreatureResource", EShortcut::MARKET_CREATURE_RESOURCE },
|
||||
{"marketResourceResource", EShortcut::MARKET_RESOURCE_RESOURCE },
|
||||
{"marketCreatureExperience", EShortcut::MARKET_CREATURE_EXPERIENCE },
|
||||
{"marketArtifactExperience", EShortcut::MARKET_ARTIFACT_EXPERIENCE },
|
||||
{"townOpenHall", EShortcut::TOWN_OPEN_HALL },
|
||||
{"townOpenFort", EShortcut::TOWN_OPEN_FORT },
|
||||
{"townOpenMarket", EShortcut::TOWN_OPEN_MARKET },
|
||||
{"townOpenMageGuild", EShortcut::TOWN_OPEN_MAGE_GUILD },
|
||||
{"townOpenThievesGuild", EShortcut::TOWN_OPEN_THIEVES_GUILD },
|
||||
{"townOpenRecruitment", EShortcut::TOWN_OPEN_RECRUITMENT },
|
||||
{"townOpenHeroExchange", EShortcut::TOWN_OPEN_HERO_EXCHANGE },
|
||||
{"townOpenHero", EShortcut::TOWN_OPEN_HERO },
|
||||
{"townOpenVisitingHero", EShortcut::TOWN_OPEN_VISITING_HERO },
|
||||
{"townOpenGarrisonedHero", EShortcut::TOWN_OPEN_GARRISONED_HERO },
|
||||
{"recruitmentSwitchLevel", EShortcut::RECRUITMENT_SWITCH_LEVEL },
|
||||
{"heroArmySplit", EShortcut::HERO_ARMY_SPLIT },
|
||||
{"heroBackpack", EShortcut::HERO_BACKPACK },
|
||||
{"exchangeArmyToLeft", EShortcut::EXCHANGE_ARMY_TO_LEFT },
|
||||
{"exchangeArmyToRight", EShortcut::EXCHANGE_ARMY_TO_RIGHT },
|
||||
{"exchangeArmySwap", EShortcut::EXCHANGE_ARMY_SWAP },
|
||||
{"exchangeArtifactsToLeft", EShortcut::EXCHANGE_ARTIFACTS_TO_LEFT },
|
||||
{"exchangeArtifactsToRight", EShortcut::EXCHANGE_ARTIFACTS_TO_RIGHT },
|
||||
{"exchangeArtifactsSwap", EShortcut::EXCHANGE_ARTIFACTS_SWAP },
|
||||
{"exchangeBackpackLeft", EShortcut::EXCHANGE_BACKPACK_LEFT },
|
||||
{"exchangeBackpackRight", EShortcut::EXCHANGE_BACKPACK_RIGHT },
|
||||
{"exchangeEquippedToLeft", EShortcut::EXCHANGE_EQUIPPED_TO_LEFT },
|
||||
{"exchangeEquippedToRight", EShortcut::EXCHANGE_EQUIPPED_TO_RIGHT},
|
||||
{"exchangeEquippedSwap", EShortcut::EXCHANGE_EQUIPPED_SWAP },
|
||||
{"exchangeBackpackToLeft", EShortcut::EXCHANGE_BACKPACK_TO_LEFT },
|
||||
{"exchangeBackpackToRight", EShortcut::EXCHANGE_BACKPACK_TO_RIGHT},
|
||||
{"exchangeBackpackSwap", EShortcut::EXCHANGE_BACKPACK_SWAP },
|
||||
};
|
||||
|
||||
#ifndef ENABLE_GOLDMASTER
|
||||
std::vector<EShortcut> assignedShortcuts;
|
||||
std::vector<EShortcut> missingShortcuts;
|
||||
for (auto const & entry : shortcutNames)
|
||||
assignedShortcuts.push_back(entry.second);
|
||||
|
||||
for (EShortcut id = vstd::next(EShortcut::NONE, 1); id < EShortcut::AFTER_LAST; id = vstd::next(id, 1))
|
||||
if (!vstd::contains(assignedShortcuts, id))
|
||||
missingShortcuts.push_back(id);
|
||||
|
||||
if (!missingShortcuts.empty())
|
||||
logGlobal->error("Found %d shortcuts without assigned string name!", missingShortcuts.size());
|
||||
#endif
|
||||
|
||||
if (shortcutNames.count(identifier))
|
||||
return shortcutNames.at(identifier);
|
||||
return EShortcut::NONE;
|
||||
|
@ -70,9 +70,18 @@ CBonusSelection::CBonusSelection()
|
||||
|
||||
panelBackground = std::make_shared<CPicture>(ImagePath::builtin("CAMPBRF.BMP"), 456, 6);
|
||||
|
||||
buttonStart = std::make_shared<CButton>(Point(475, 536), AnimationPath::builtin("CBBEGIB.DEF"), CButton::tooltip(), std::bind(&CBonusSelection::startMap, this), EShortcut::GLOBAL_ACCEPT);
|
||||
const auto & playVideo = [this]()
|
||||
{
|
||||
GH.windows().createAndPushWindow<CPrologEpilogVideo>(
|
||||
getCampaign()->scenario(CSH->campaignMap).prolog,
|
||||
[this]() { redraw(); } );
|
||||
};
|
||||
|
||||
buttonStart = std::make_shared<CButton>(
|
||||
Point(475, 536), AnimationPath::builtin("CBBEGIB.DEF"), CButton::tooltip(), std::bind(&CBonusSelection::startMap, this), EShortcut::GLOBAL_ACCEPT
|
||||
);
|
||||
buttonRestart = std::make_shared<CButton>(Point(475, 536), AnimationPath::builtin("CBRESTB.DEF"), CButton::tooltip(), std::bind(&CBonusSelection::restartMap, this), EShortcut::GLOBAL_ACCEPT);
|
||||
buttonVideo = std::make_shared<CButton>(Point(705, 214), AnimationPath::builtin("CBVIDEB.DEF"), CButton::tooltip(), [this](){ GH.windows().createAndPushWindow<CPrologEpilogVideo>(getCampaign()->scenario(CSH->campaignMap).prolog, [this](){ redraw(); }); });
|
||||
buttonVideo = std::make_shared<CButton>(Point(705, 214), AnimationPath::builtin("CBVIDEB.DEF"), CButton::tooltip(), playVideo, EShortcut::LOBBY_REPLAY_VIDEO);
|
||||
buttonBack = std::make_shared<CButton>(Point(624, 536), AnimationPath::builtin("CBCANCB.DEF"), CButton::tooltip(), std::bind(&CBonusSelection::goBack, this), EShortcut::GLOBAL_CANCEL);
|
||||
|
||||
campaignName = std::make_shared<CLabel>(481, 28, FONT_BIG, ETextAlignment::TOPLEFT, Colors::YELLOW, CSH->si->getCampaignName());
|
||||
@ -103,8 +112,11 @@ CBonusSelection::CBonusSelection()
|
||||
|
||||
if(getCampaign()->playerSelectedDifficulty())
|
||||
{
|
||||
buttonDifficultyLeft = std::make_shared<CButton>(settings["general"]["enableUiEnhancements"].Bool() ? Point(693, 495) : Point(694, 508), AnimationPath::builtin("SCNRBLF.DEF"), CButton::tooltip(), std::bind(&CBonusSelection::decreaseDifficulty, this));
|
||||
buttonDifficultyRight = std::make_shared<CButton>(settings["general"]["enableUiEnhancements"].Bool() ? Point(739, 495) : Point(738, 508), AnimationPath::builtin("SCNRBRT.DEF"), CButton::tooltip(), std::bind(&CBonusSelection::increaseDifficulty, this));
|
||||
Point posLeft = settings["general"]["enableUiEnhancements"].Bool() ? Point(693, 495) : Point(694, 508);
|
||||
Point posRight = settings["general"]["enableUiEnhancements"].Bool() ? Point(739, 495) : Point(738, 508);
|
||||
|
||||
buttonDifficultyLeft = std::make_shared<CButton>(posLeft, AnimationPath::builtin("SCNRBLF.DEF"), CButton::tooltip(), std::bind(&CBonusSelection::decreaseDifficulty, this), EShortcut::MOVE_LEFT);
|
||||
buttonDifficultyRight = std::make_shared<CButton>(posRight, AnimationPath::builtin("SCNRBRT.DEF"), CButton::tooltip(), std::bind(&CBonusSelection::increaseDifficulty, this), EShortcut::MOVE_RIGHT);
|
||||
}
|
||||
|
||||
for(auto scenarioID : getCampaign()->allScenarios())
|
||||
@ -124,7 +136,7 @@ CBonusSelection::CBonusSelection()
|
||||
tabExtraOptions->recActions = UPDATE | SHOWALL | LCLICK | RCLICK_POPUP;
|
||||
tabExtraOptions->recreate(true);
|
||||
tabExtraOptions->setEnabled(false);
|
||||
buttonExtraOptions = std::make_shared<CButton>(Point(643, 431), AnimationPath::builtin("GSPBUT2.DEF"), CGI->generaltexth->zelp[46], [this]{ tabExtraOptions->setEnabled(!tabExtraOptions->isActive()); GH.windows().totalRedraw(); }, EShortcut::NONE);
|
||||
buttonExtraOptions = std::make_shared<CButton>(Point(643, 431), AnimationPath::builtin("GSPBUT2.DEF"), CGI->generaltexth->zelp[46], [this]{ tabExtraOptions->setEnabled(!tabExtraOptions->isActive()); GH.windows().totalRedraw(); }, EShortcut::LOBBY_EXTRA_OPTIONS);
|
||||
buttonExtraOptions->setTextOverlay(CGI->generaltexth->translate("vcmi.optionsTab.extraOptions.hover"), FONT_SMALL, Colors::WHITE);
|
||||
}
|
||||
}
|
||||
|
@ -57,12 +57,12 @@ CLobbyScreen::CLobbyScreen(ESelectionScreen screenType)
|
||||
buttonOptions = std::make_shared<CButton>(Point(411, 510), AnimationPath::builtin("GSPBUTT.DEF"), CGI->generaltexth->zelp[46], std::bind(&CLobbyScreen::toggleTab, this, tabOpt), EShortcut::LOBBY_ADDITIONAL_OPTIONS);
|
||||
if(settings["general"]["enableUiEnhancements"].Bool())
|
||||
{
|
||||
buttonTurnOptions = std::make_shared<CButton>(Point(619, 105), AnimationPath::builtin("GSPBUT2.DEF"), CGI->generaltexth->zelp[46], std::bind(&CLobbyScreen::toggleTab, this, tabTurnOptions), EShortcut::NONE);
|
||||
buttonExtraOptions = std::make_shared<CButton>(Point(619, 510), AnimationPath::builtin("GSPBUT2.DEF"), CGI->generaltexth->zelp[46], std::bind(&CLobbyScreen::toggleTab, this, tabExtraOptions), EShortcut::NONE);
|
||||
buttonTurnOptions = std::make_shared<CButton>(Point(619, 105), AnimationPath::builtin("GSPBUT2.DEF"), CGI->generaltexth->zelp[46], std::bind(&CLobbyScreen::toggleTab, this, tabTurnOptions), EShortcut::LOBBY_TURN_OPTIONS);
|
||||
buttonExtraOptions = std::make_shared<CButton>(Point(619, 510), AnimationPath::builtin("GSPBUT2.DEF"), CGI->generaltexth->zelp[46], std::bind(&CLobbyScreen::toggleTab, this, tabExtraOptions), EShortcut::LOBBY_EXTRA_OPTIONS);
|
||||
}
|
||||
};
|
||||
|
||||
buttonChat = std::make_shared<CButton>(Point(619, 80), AnimationPath::builtin("GSPBUT2.DEF"), CGI->generaltexth->zelp[48], std::bind(&CLobbyScreen::toggleChat, this), EShortcut::LOBBY_HIDE_CHAT);
|
||||
buttonChat = std::make_shared<CButton>(Point(619, 80), AnimationPath::builtin("GSPBUT2.DEF"), CGI->generaltexth->zelp[48], std::bind(&CLobbyScreen::toggleChat, this), EShortcut::LOBBY_TOGGLE_CHAT);
|
||||
buttonChat->setTextOverlay(CGI->generaltexth->allTexts[532], FONT_SMALL, Colors::WHITE);
|
||||
|
||||
switch(screenType)
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "../widgets/CTextInput.h"
|
||||
#include "../widgets/GraphicalPrimitiveCanvas.h"
|
||||
#include "../widgets/Images.h"
|
||||
#include "../widgets/MiscWidgets.h"
|
||||
#include "../widgets/ObjectLists.h"
|
||||
#include "../widgets/Slider.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
@ -142,8 +143,8 @@ InfoCard::InfoCard()
|
||||
chat = std::make_shared<CChatBox>(Rect(18, 126, 335, 143));
|
||||
pvpBox = std::make_shared<PvPBox>(Rect(17, 396, 338, 105));
|
||||
|
||||
buttonInvitePlayers = std::make_shared<CButton>(Point(20, 365), AnimationPath::builtin("pregameInvitePlayers"), CGI->generaltexth->zelp[105], [](){ CSH->getGlobalLobby().activateRoomInviteInterface(); } );
|
||||
buttonOpenGlobalLobby = std::make_shared<CButton>(Point(188, 365), AnimationPath::builtin("pregameReturnToLobby"), CGI->generaltexth->zelp[105], [](){ CSH->getGlobalLobby().activateInterface(); });
|
||||
buttonInvitePlayers = std::make_shared<CButton>(Point(20, 365), AnimationPath::builtin("pregameInvitePlayers"), CGI->generaltexth->zelp[105], [](){ CSH->getGlobalLobby().activateRoomInviteInterface(); }, EShortcut::LOBBY_INVITE_PLAYERS );
|
||||
buttonOpenGlobalLobby = std::make_shared<CButton>(Point(188, 365), AnimationPath::builtin("pregameReturnToLobby"), CGI->generaltexth->zelp[105], [](){ CSH->getGlobalLobby().activateInterface(); }, EShortcut::MAIN_MENU_LOBBY );
|
||||
|
||||
buttonInvitePlayers->setTextOverlay (MetaString::createFromTextID("vcmi.lobby.invite.header").toString(), EFonts::FONT_SMALL, Colors::WHITE);
|
||||
buttonOpenGlobalLobby->setTextOverlay(MetaString::createFromTextID("vcmi.lobby.backToLobby").toString(), EFonts::FONT_SMALL, Colors::WHITE);
|
||||
@ -417,7 +418,7 @@ PvPBox::PvPBox(const Rect & rect)
|
||||
LobbyPvPAction lpa;
|
||||
lpa.action = LobbyPvPAction::COIN;
|
||||
CSH->sendLobbyPack(lpa);
|
||||
}, EShortcut::NONE);
|
||||
}, EShortcut::LOBBY_FLIP_COIN);
|
||||
buttonFlipCoin->setTextOverlay(CGI->generaltexth->translate("vcmi.lobby.pvp.coin.hover"), EFonts::FONT_SMALL, Colors::WHITE);
|
||||
|
||||
buttonRandomTown = std::make_shared<CButton>(Point(190, 31), AnimationPath::builtin("GSPBUT2.DEF"), CButton::tooltip("", CGI->generaltexth->translate("vcmi.lobby.pvp.randomTown.help")), [getBannedTowns](){
|
||||
@ -425,7 +426,7 @@ PvPBox::PvPBox(const Rect & rect)
|
||||
lpa.action = LobbyPvPAction::RANDOM_TOWN;
|
||||
lpa.bannedTowns = getBannedTowns();
|
||||
CSH->sendLobbyPack(lpa);
|
||||
}, EShortcut::NONE);
|
||||
}, EShortcut::LOBBY_RANDOM_TOWN);
|
||||
buttonRandomTown->setTextOverlay(CGI->generaltexth->translate("vcmi.lobby.pvp.randomTown.hover"), EFonts::FONT_SMALL, Colors::WHITE);
|
||||
|
||||
buttonRandomTownVs = std::make_shared<CButton>(Point(190, 56), AnimationPath::builtin("GSPBUT2.DEF"), CButton::tooltip("", CGI->generaltexth->translate("vcmi.lobby.pvp.randomTownVs.help")), [getBannedTowns](){
|
||||
@ -433,7 +434,7 @@ PvPBox::PvPBox(const Rect & rect)
|
||||
lpa.action = LobbyPvPAction::RANDOM_TOWN_VS;
|
||||
lpa.bannedTowns = getBannedTowns();
|
||||
CSH->sendLobbyPack(lpa);
|
||||
}, EShortcut::NONE);
|
||||
}, EShortcut::LOBBY_RANDOM_TOWN_VS);
|
||||
buttonRandomTownVs->setTextOverlay(CGI->generaltexth->translate("vcmi.lobby.pvp.randomTownVs.hover"), EFonts::FONT_SMALL, Colors::WHITE);
|
||||
}
|
||||
|
||||
@ -444,7 +445,7 @@ TownSelector::TownSelector(const Point & loc)
|
||||
setRedrawParent(true);
|
||||
|
||||
int count = 0;
|
||||
for(auto const & factionID : VLC->townh->getDefaultAllowed())
|
||||
for(auto const & factionID : CGI->townh->getDefaultAllowed())
|
||||
{
|
||||
townsEnabled[factionID] = true;
|
||||
count++;
|
||||
|
@ -169,24 +169,23 @@ SelectionTab::SelectionTab(ESelectionScreen Type)
|
||||
labelMapSizes = std::make_shared<CLabel>(87, 62, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[510]);
|
||||
|
||||
// TODO: Global constants?
|
||||
int sizes[] = {CMapHeader::MAP_SIZE_SMALL,
|
||||
CMapHeader::MAP_SIZE_MIDDLE,
|
||||
CMapHeader::MAP_SIZE_LARGE,
|
||||
CMapHeader::MAP_SIZE_XLARGE,
|
||||
0};
|
||||
const char * filterIconNmes[] = {"SCSMBUT.DEF", "SCMDBUT.DEF", "SCLGBUT.DEF", "SCXLBUT.DEF", "SCALBUT.DEF"};
|
||||
for(int i = 0; i < 5; i++)
|
||||
buttonsSortBy.push_back(std::make_shared<CButton>(Point(158 + 47 * i, 46), AnimationPath::builtin(filterIconNmes[i]), CGI->generaltexth->zelp[54 + i], std::bind(&SelectionTab::filter, this, sizes[i], true)));
|
||||
constexpr std::array sizes = {CMapHeader::MAP_SIZE_SMALL, CMapHeader::MAP_SIZE_MIDDLE, CMapHeader::MAP_SIZE_LARGE, CMapHeader::MAP_SIZE_XLARGE, 0};
|
||||
constexpr std::array filterIconNmes = {"SCSMBUT.DEF", "SCMDBUT.DEF", "SCLGBUT.DEF", "SCXLBUT.DEF", "SCALBUT.DEF"};
|
||||
constexpr std::array filterShortcuts = { EShortcut::MAPS_SIZE_S, EShortcut::MAPS_SIZE_M, EShortcut::MAPS_SIZE_L, EShortcut::MAPS_SIZE_XL, EShortcut::MAPS_SIZE_ALL };
|
||||
|
||||
int xpos[] = {23, 55, 88, 121, 306, 339};
|
||||
const char * sortIconNames[] = {"SCBUTT1.DEF", "SCBUTT2.DEF", "SCBUTCP.DEF", "SCBUTT3.DEF", "SCBUTT4.DEF", "SCBUTT5.DEF"};
|
||||
for(int i = 0; i < 5; i++)
|
||||
buttonsSortBy.push_back(std::make_shared<CButton>(Point(158 + 47 * i, 46), AnimationPath::builtin(filterIconNmes[i]), CGI->generaltexth->zelp[54 + i], std::bind(&SelectionTab::filter, this, sizes[i], true), filterShortcuts[i]));
|
||||
|
||||
constexpr std::array xpos = {23, 55, 88, 121, 306, 339};
|
||||
constexpr std::array sortIconNames = {"SCBUTT1.DEF", "SCBUTT2.DEF", "SCBUTCP.DEF", "SCBUTT3.DEF", "SCBUTT4.DEF", "SCBUTT5.DEF"};
|
||||
constexpr std::array sortShortcuts = { EShortcut::MAPS_SORT_PLAYERS, EShortcut::MAPS_SORT_SIZE, EShortcut::MAPS_SORT_FORMAT, EShortcut::MAPS_SORT_NAME, EShortcut::MAPS_SORT_VICTORY, EShortcut::MAPS_SORT_DEFEAT };
|
||||
for(int i = 0; i < 6; i++)
|
||||
{
|
||||
ESortBy criteria = (ESortBy)i;
|
||||
if(criteria == _name)
|
||||
criteria = generalSortingBy;
|
||||
|
||||
buttonsSortBy.push_back(std::make_shared<CButton>(Point(xpos[i], 86), AnimationPath::builtin(sortIconNames[i]), CGI->generaltexth->zelp[107 + i], std::bind(&SelectionTab::sortBy, this, criteria)));
|
||||
buttonsSortBy.push_back(std::make_shared<CButton>(Point(xpos[i], 86), AnimationPath::builtin(sortIconNames[i]), CGI->generaltexth->zelp[107 + i], std::bind(&SelectionTab::sortBy, this, criteria), sortShortcuts[i]));
|
||||
}
|
||||
}
|
||||
|
||||
@ -212,8 +211,8 @@ SelectionTab::SelectionTab(ESelectionScreen Type)
|
||||
pos.x += 3;
|
||||
pos.y += 6;
|
||||
|
||||
buttonsSortBy.push_back(std::make_shared<CButton>(Point(23, 86), AnimationPath::builtin("CamCusM.DEF"), CButton::tooltip(), std::bind(&SelectionTab::sortBy, this, _numOfMaps)));
|
||||
buttonsSortBy.push_back(std::make_shared<CButton>(Point(55, 86), AnimationPath::builtin("CamCusL.DEF"), CButton::tooltip(), std::bind(&SelectionTab::sortBy, this, _name)));
|
||||
buttonsSortBy.push_back(std::make_shared<CButton>(Point(23, 86), AnimationPath::builtin("CamCusM.DEF"), CButton::tooltip(), std::bind(&SelectionTab::sortBy, this, _numOfMaps), EShortcut::MAPS_SORT_MAPS));
|
||||
buttonsSortBy.push_back(std::make_shared<CButton>(Point(55, 86), AnimationPath::builtin("CamCusL.DEF"), CButton::tooltip(), std::bind(&SelectionTab::sortBy, this, _name), EShortcut::MAPS_SORT_NAME));
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
@ -222,7 +221,7 @@ SelectionTab::SelectionTab(ESelectionScreen Type)
|
||||
|
||||
if(enableUiEnhancements)
|
||||
{
|
||||
auto sortByDate = std::make_shared<CButton>(Point(371, 85), AnimationPath::builtin("selectionTabSortDate"), CButton::tooltip("", CGI->generaltexth->translate("vcmi.lobby.sortDate")), std::bind(&SelectionTab::sortBy, this, ESortBy::_changeDate));
|
||||
auto sortByDate = std::make_shared<CButton>(Point(371, 85), AnimationPath::builtin("selectionTabSortDate"), CButton::tooltip("", CGI->generaltexth->translate("vcmi.lobby.sortDate")), std::bind(&SelectionTab::sortBy, this, ESortBy::_changeDate), EShortcut::MAPS_SORT_CHANGEDATE);
|
||||
sortByDate->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("lobby/selectionTabSortDate")));
|
||||
buttonsSortBy.push_back(sortByDate);
|
||||
}
|
||||
@ -677,6 +676,8 @@ void SelectionTab::selectFileName(std::string fname)
|
||||
}
|
||||
}
|
||||
|
||||
filter(-1);
|
||||
|
||||
for(int i = (int)curItems.size() - 1; i >= 0; i--)
|
||||
{
|
||||
if(curItems[i]->fileURI == fname)
|
||||
@ -687,13 +688,25 @@ void SelectionTab::selectFileName(std::string fname)
|
||||
}
|
||||
}
|
||||
|
||||
filter(-1);
|
||||
selectAbs(-1);
|
||||
|
||||
if(tabType == ESelectionScreen::saveGame && inputName->getText().empty())
|
||||
inputName->setText("NEWGAME");
|
||||
}
|
||||
|
||||
void SelectionTab::selectNewestFile()
|
||||
{
|
||||
time_t newestTime = 0;
|
||||
std::string newestFile = "";
|
||||
for(int i = (int)allItems.size() - 1; i >= 0; i--)
|
||||
if(allItems[i]->lastWrite > newestTime)
|
||||
{
|
||||
newestTime = allItems[i]->lastWrite;
|
||||
newestFile = allItems[i]->fileURI;
|
||||
}
|
||||
selectFileName(newestFile);
|
||||
}
|
||||
|
||||
std::shared_ptr<ElementInfo> SelectionTab::getSelectedMapInfo() const
|
||||
{
|
||||
return curItems.empty() || curItems[selectionPos]->isFolder ? nullptr : curItems[selectionPos];
|
||||
@ -733,6 +746,8 @@ void SelectionTab::restoreLastSelection()
|
||||
selectFileName(settings["general"]["lastCampaign"].String());
|
||||
break;
|
||||
case ESelectionScreen::loadGame:
|
||||
selectNewestFile();
|
||||
break;
|
||||
case ESelectionScreen::saveGame:
|
||||
selectFileName(settings["general"]["lastSave"].String());
|
||||
}
|
||||
@ -795,7 +810,7 @@ void SelectionTab::parseSaves(const std::unordered_set<ResourcePath> & files)
|
||||
switch(CSH->getLoadMode())
|
||||
{
|
||||
case ELoadMode::SINGLE:
|
||||
if(isMultiplayer || isCampaign || isTutorial)
|
||||
if(isCampaign || isTutorial)
|
||||
mapInfo->mapHeader.reset();
|
||||
break;
|
||||
case ELoadMode::CAMPAIGN:
|
||||
@ -806,10 +821,14 @@ void SelectionTab::parseSaves(const std::unordered_set<ResourcePath> & files)
|
||||
if(!isTutorial)
|
||||
mapInfo->mapHeader.reset();
|
||||
break;
|
||||
default:
|
||||
case ELoadMode::MULTI:
|
||||
if(!isMultiplayer)
|
||||
mapInfo->mapHeader.reset();
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
mapInfo->mapHeader.reset();
|
||||
break;
|
||||
}
|
||||
|
||||
allItems.push_back(mapInfo);
|
||||
|
@ -101,6 +101,7 @@ public:
|
||||
int getLine() const;
|
||||
int getLine(const Point & position) const;
|
||||
void selectFileName(std::string fname);
|
||||
void selectNewestFile();
|
||||
std::shared_ptr<ElementInfo> getSelectedMapInfo() const;
|
||||
void rememberCurrentSelection();
|
||||
void restoreLastSelection();
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "../windows/InfoWindows.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
#include "../render/Canvas.h"
|
||||
#include "../render/IRenderHandler.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
@ -82,7 +83,8 @@ CHighScoreScreen::CHighScoreScreen(HighScorePage highscorepage, int highlighted)
|
||||
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
pos = center(Rect(0, 0, 800, 600));
|
||||
updateShadow();
|
||||
|
||||
backgroundAroundMenu = std::make_shared<CFilledTexture>(ImagePath::builtin("DIBOXBCK"), Rect(-pos.x, -pos.y, GH.screenDimensions().x, GH.screenDimensions().y));
|
||||
|
||||
addHighScores();
|
||||
addButtons();
|
||||
@ -110,10 +112,10 @@ void CHighScoreScreen::addButtons()
|
||||
|
||||
buttons.clear();
|
||||
|
||||
buttons.push_back(std::make_shared<CButton>(Point(31, 113), AnimationPath::builtin("HISCCAM.DEF"), CButton::tooltip(), [&](){ buttonCampaignClick(); }));
|
||||
buttons.push_back(std::make_shared<CButton>(Point(31, 345), AnimationPath::builtin("HISCSTA.DEF"), CButton::tooltip(), [&](){ buttonScenarioClick(); }));
|
||||
buttons.push_back(std::make_shared<CButton>(Point(726, 113), AnimationPath::builtin("HISCRES.DEF"), CButton::tooltip(), [&](){ buttonResetClick(); }));
|
||||
buttons.push_back(std::make_shared<CButton>(Point(726, 345), AnimationPath::builtin("HISCEXT.DEF"), CButton::tooltip(), [&](){ buttonExitClick(); }));
|
||||
buttons.push_back(std::make_shared<CButton>(Point(31, 113), AnimationPath::builtin("HISCCAM.DEF"), CButton::tooltip(), [&](){ buttonCampaignClick(); }, EShortcut::HIGH_SCORES_CAMPAIGNS));
|
||||
buttons.push_back(std::make_shared<CButton>(Point(31, 345), AnimationPath::builtin("HISCSTA.DEF"), CButton::tooltip(), [&](){ buttonScenarioClick(); }, EShortcut::HIGH_SCORES_SCENARIOS));
|
||||
buttons.push_back(std::make_shared<CButton>(Point(726, 113), AnimationPath::builtin("HISCRES.DEF"), CButton::tooltip(), [&](){ buttonResetClick(); }, EShortcut::HIGH_SCORES_RESET));
|
||||
buttons.push_back(std::make_shared<CButton>(Point(726, 345), AnimationPath::builtin("HISCEXT.DEF"), CButton::tooltip(), [&](){ buttonExitClick(); }, EShortcut::GLOBAL_RETURN));
|
||||
}
|
||||
|
||||
void CHighScoreScreen::addHighScores()
|
||||
@ -223,8 +225,8 @@ CHighScoreInputScreen::CHighScoreInputScreen(bool won, HighScoreCalculation calc
|
||||
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
pos = center(Rect(0, 0, 800, 600));
|
||||
updateShadow();
|
||||
|
||||
backgroundAroundMenu = std::make_shared<CFilledTexture>(ImagePath::builtin("DIBOXBCK"), Rect(-pos.x, -pos.y, GH.screenDimensions().x, GH.screenDimensions().y));
|
||||
background = std::make_shared<TransparentFilledRectangle>(Rect(0, 0, pos.w, pos.h), Colors::BLACK);
|
||||
|
||||
if(won)
|
||||
@ -341,7 +343,8 @@ CHighScoreInput::CHighScoreInput(std::string playerName, std::function<void(std:
|
||||
|
||||
buttonOk = std::make_shared<CButton>(Point(26, 142), AnimationPath::builtin("MUBCHCK.DEF"), CGI->generaltexth->zelp[560], std::bind(&CHighScoreInput::okay, this), EShortcut::GLOBAL_ACCEPT);
|
||||
buttonCancel = std::make_shared<CButton>(Point(142, 142), AnimationPath::builtin("MUBCANC.DEF"), CGI->generaltexth->zelp[561], std::bind(&CHighScoreInput::abort, this), EShortcut::GLOBAL_CANCEL);
|
||||
statusBar = CGStatusBar::create(std::make_shared<CPicture>(background->getSurface(), Rect(7, 186, 218, 18), 7, 186));
|
||||
// FIXME: broken. Never activates?
|
||||
// statusBar = CGStatusBar::create(std::make_shared<CPicture>(background->getSurface(), Rect(7, 186, 218, 18), 7, 186));
|
||||
textInput = std::make_shared<CTextInput>(Rect(18, 104, 200, 25), FONT_SMALL, ETextAlignment::CENTER, true);
|
||||
textInput->setText(playerName);
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ class CMultiLineLabel;
|
||||
class CAnimImage;
|
||||
class CTextInput;
|
||||
class VideoWidgetBase;
|
||||
class CFilledTexture;
|
||||
|
||||
class TransparentFilledRectangle;
|
||||
|
||||
@ -62,6 +63,7 @@ private:
|
||||
HighScorePage highscorepage;
|
||||
|
||||
std::shared_ptr<CPicture> background;
|
||||
std::shared_ptr<CFilledTexture> backgroundAroundMenu;
|
||||
std::vector<std::shared_ptr<CButton>> buttons;
|
||||
std::vector<std::shared_ptr<CLabel>> texts;
|
||||
std::vector<std::shared_ptr<CAnimImage>> images;
|
||||
@ -95,6 +97,7 @@ class CHighScoreInputScreen : public CWindowObject
|
||||
std::shared_ptr<CHighScoreInput> input;
|
||||
std::shared_ptr<TransparentFilledRectangle> background;
|
||||
std::shared_ptr<VideoWidgetBase> videoPlayer;
|
||||
std::shared_ptr<CFilledTexture> backgroundAroundMenu;
|
||||
|
||||
bool won;
|
||||
HighScoreCalculation calc;
|
||||
|
@ -58,7 +58,6 @@
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/GameConstants.h"
|
||||
#include "../../lib/CRandomGenerator.h"
|
||||
#include "../../lib/CondSh.h"
|
||||
|
||||
std::shared_ptr<CMainMenu> CMM;
|
||||
ISelectionScreenInfo * SEL;
|
||||
@ -265,7 +264,8 @@ CMainMenuConfig::CMainMenuConfig()
|
||||
: campaignSets(JsonPath::builtin("config/campaignSets.json"))
|
||||
, config(JsonPath::builtin("config/mainmenu.json"))
|
||||
{
|
||||
|
||||
if (config["game-select"].Vector().empty())
|
||||
handleFatalError("Main menu config is invalid or corrupted. Please disable any mods or reinstall VCMI", false);
|
||||
}
|
||||
|
||||
const CMainMenuConfig & CMainMenuConfig::get()
|
||||
@ -447,11 +447,11 @@ CMultiMode::CMultiMode(ESelectionScreen ScreenType)
|
||||
playerName->setText(getPlayerName());
|
||||
playerName->setCallback(std::bind(&CMultiMode::onNameChange, this, _1));
|
||||
|
||||
buttonHotseat = std::make_shared<CButton>(Point(373, 78 + 57 * 0), AnimationPath::builtin("MUBHOT.DEF"), CGI->generaltexth->zelp[266], std::bind(&CMultiMode::hostTCP, this));
|
||||
buttonLobby = std::make_shared<CButton>(Point(373, 78 + 57 * 1), AnimationPath::builtin("MUBONL.DEF"), CGI->generaltexth->zelp[265], std::bind(&CMultiMode::openLobby, this));
|
||||
buttonHotseat = std::make_shared<CButton>(Point(373, 78 + 57 * 0), AnimationPath::builtin("MUBHOT.DEF"), CGI->generaltexth->zelp[266], std::bind(&CMultiMode::hostTCP, this), EShortcut::MAIN_MENU_HOTSEAT);
|
||||
buttonLobby = std::make_shared<CButton>(Point(373, 78 + 57 * 1), AnimationPath::builtin("MUBONL.DEF"), CGI->generaltexth->zelp[265], std::bind(&CMultiMode::openLobby, this), EShortcut::MAIN_MENU_LOBBY);
|
||||
|
||||
buttonHost = std::make_shared<CButton>(Point(373, 78 + 57 * 3), AnimationPath::builtin("MUBHOST.DEF"), CButton::tooltip(CGI->generaltexth->translate("vcmi.mainMenu.hostTCP"), ""), std::bind(&CMultiMode::hostTCP, this));
|
||||
buttonJoin = std::make_shared<CButton>(Point(373, 78 + 57 * 4), AnimationPath::builtin("MUBJOIN.DEF"), CButton::tooltip(CGI->generaltexth->translate("vcmi.mainMenu.joinTCP"), ""), std::bind(&CMultiMode::joinTCP, this));
|
||||
buttonHost = std::make_shared<CButton>(Point(373, 78 + 57 * 3), AnimationPath::builtin("MUBHOST.DEF"), CButton::tooltip(CGI->generaltexth->translate("vcmi.mainMenu.hostTCP"), ""), std::bind(&CMultiMode::hostTCP, this), EShortcut::MAIN_MENU_HOST_GAME);
|
||||
buttonJoin = std::make_shared<CButton>(Point(373, 78 + 57 * 4), AnimationPath::builtin("MUBJOIN.DEF"), CButton::tooltip(CGI->generaltexth->translate("vcmi.mainMenu.joinTCP"), ""), std::bind(&CMultiMode::joinTCP, this), EShortcut::MAIN_MENU_JOIN_GAME);
|
||||
|
||||
buttonCancel = std::make_shared<CButton>(Point(373, 424), AnimationPath::builtin("MUBCANC.DEF"), CGI->generaltexth->zelp[288], [=](){ close();}, EShortcut::GLOBAL_CANCEL);
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
//#include "../gui/FramerateManager.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
#include "../widgets/VideoWidget.h"
|
||||
#include "../widgets/Images.h"
|
||||
#include "../render/Canvas.h"
|
||||
|
||||
CPrologEpilogVideo::CPrologEpilogVideo(CampaignScenarioPrologEpilog _spe, std::function<void()> callback)
|
||||
@ -27,7 +28,8 @@ CPrologEpilogVideo::CPrologEpilogVideo(CampaignScenarioPrologEpilog _spe, std::f
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
addUsedEvents(LCLICK | TIME);
|
||||
pos = center(Rect(0, 0, 800, 600));
|
||||
updateShadow();
|
||||
|
||||
backgroundAroundMenu = std::make_shared<CFilledTexture>(ImagePath::builtin("DIBOXBCK"), Rect(-pos.x, -pos.y, GH.screenDimensions().x, GH.screenDimensions().y));
|
||||
|
||||
//TODO: remove hardcoded paths. Some of campaigns video actually consist from 2 parts
|
||||
// however, currently our campaigns format expects only a single video file
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
class CMultiLineLabel;
|
||||
class VideoWidget;
|
||||
class CFilledTexture;
|
||||
|
||||
class CPrologEpilogVideo : public CWindowObject
|
||||
{
|
||||
@ -27,6 +28,7 @@ class CPrologEpilogVideo : public CWindowObject
|
||||
|
||||
std::shared_ptr<CMultiLineLabel> text;
|
||||
std::shared_ptr<VideoWidget> videoPlayer;
|
||||
std::shared_ptr<CFilledTexture> backgroundAroundMenu;
|
||||
|
||||
bool voiceStopped = false;
|
||||
|
||||
|
@ -25,6 +25,8 @@ public:
|
||||
virtual ~IMapObjectObserver();
|
||||
|
||||
virtual bool hasOngoingAnimations() = 0;
|
||||
virtual void waitForOngoingAnimations(){};
|
||||
virtual void endNetwork(){};
|
||||
|
||||
/// Plays fade-in animation and adds object to map
|
||||
virtual void onObjectFadeIn(const CGObjectInstance * obj, const PlayerColor & initiator) = 0;
|
||||
|
@ -664,15 +664,15 @@ size_t MapRendererPath::selectImageArrow(bool reachableToday, const int3 & curr,
|
||||
// is (directionToArrowIndex[7][5])
|
||||
//
|
||||
const static size_t directionToArrowIndex[9][9] = {
|
||||
{16, 17, 18, 7, 0, 19, 6, 5, 0 },
|
||||
{8, 9, 18, 7, 0, 19, 6, 0, 20},
|
||||
{8, 1, 10, 7, 0, 19, 0, 21, 20},
|
||||
{24, 17, 18, 15, 0, 0, 6, 5, 4 },
|
||||
{16, 17, 18, 7, 0, 19, 6, 5, 12},
|
||||
{8, 9, 18, 7, 0, 19, 6, 13, 20},
|
||||
{8, 1, 10, 7, 0, 19, 14, 21, 20},
|
||||
{24, 17, 18, 15, 0, 11, 6, 5, 4 },
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{8, 1, 2, 0, 0, 11, 22, 21, 20},
|
||||
{24, 17, 0, 23, 0, 3, 14, 5, 4 },
|
||||
{24, 0, 2, 23, 0, 3, 22, 13, 4 },
|
||||
{0, 1, 2, 23, 0, 3, 22, 21, 12}
|
||||
{8, 1, 2, 15, 0, 11, 22, 21, 20},
|
||||
{24, 17, 10, 23, 0, 3, 14, 5, 4 },
|
||||
{24, 9, 2, 23, 0, 3, 22, 13, 4 },
|
||||
{16, 1, 2, 23, 0, 3, 22, 21, 12}
|
||||
};
|
||||
|
||||
size_t enterDirection = (curr.x - next.x + 1) + 3 * (curr.y - next.y + 1);
|
||||
|
@ -239,9 +239,9 @@ void MapView::onViewWorldActivated(uint32_t tileSize)
|
||||
controller->setTileSize(Point(tileSize, tileSize));
|
||||
}
|
||||
|
||||
void MapView::onMapZoomLevelChanged(int stepsChange)
|
||||
void MapView::onMapZoomLevelChanged(int stepsChange, bool useDeadZone)
|
||||
{
|
||||
controller->modifyTileSize(stepsChange);
|
||||
controller->modifyTileSize(stepsChange, useDeadZone);
|
||||
}
|
||||
|
||||
void MapView::onViewMapActivated()
|
||||
|
@ -87,7 +87,7 @@ public:
|
||||
void onViewWorldActivated(uint32_t tileSize);
|
||||
|
||||
/// Changes zoom level / tile size of current view by specified factor
|
||||
void onMapZoomLevelChanged(int stepsChange);
|
||||
void onMapZoomLevelChanged(int stepsChange, bool useDeadZone);
|
||||
|
||||
/// Switches view from View World mode back to standard view
|
||||
void onViewMapActivated();
|
||||
|
@ -87,7 +87,7 @@ void MapViewActions::mouseMoved(const Point & cursorPosition, const Point & last
|
||||
|
||||
void MapViewActions::wheelScrolled(int distance)
|
||||
{
|
||||
adventureInt->hotkeyZoom(distance * 4);
|
||||
adventureInt->hotkeyZoom(distance * 4, true);
|
||||
}
|
||||
|
||||
void MapViewActions::mouseDragged(const Point & cursorPosition, const Point & lastUpdateDistance)
|
||||
@ -114,7 +114,7 @@ void MapViewActions::gesturePinch(const Point & centerPosition, double lastUpdat
|
||||
int oldZoomSteps = std::round(std::log(pinchZoomFactor) / std::log(1.01));
|
||||
|
||||
if (newZoomSteps != oldZoomSteps)
|
||||
adventureInt->hotkeyZoom(newZoomSteps - oldZoomSteps);
|
||||
adventureInt->hotkeyZoom(newZoomSteps - oldZoomSteps, true);
|
||||
|
||||
pinchZoomFactor = newZoom;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/StartInfo.h"
|
||||
#include "../../lib/UnlockGuard.h"
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../../lib/mapObjects/MiscObjects.h"
|
||||
#include "../../lib/pathfinder/CGPathNode.h"
|
||||
@ -88,7 +89,7 @@ void MapViewController::setTileSize(const Point & tileSize)
|
||||
setViewCenter(newViewCenter, model->getLevel());
|
||||
}
|
||||
|
||||
void MapViewController::modifyTileSize(int stepsChange)
|
||||
void MapViewController::modifyTileSize(int stepsChange, bool useDeadZone)
|
||||
{
|
||||
// we want to zoom in/out in fixed 10% steps, to allow player to return back to exactly 100% zoom just by scrolling
|
||||
// so, zooming in for 5 steps will put game at 1.1^5 = 1.61 scale
|
||||
@ -117,10 +118,13 @@ void MapViewController::modifyTileSize(int stepsChange)
|
||||
if (actualZoom != currentZoom)
|
||||
{
|
||||
targetTileSize = actualZoom;
|
||||
if(actualZoom.x >= defaultTileSize - zoomTileDeadArea && actualZoom.x <= defaultTileSize + zoomTileDeadArea)
|
||||
actualZoom.x = defaultTileSize;
|
||||
if(actualZoom.y >= defaultTileSize - zoomTileDeadArea && actualZoom.y <= defaultTileSize + zoomTileDeadArea)
|
||||
actualZoom.y = defaultTileSize;
|
||||
if (useDeadZone)
|
||||
{
|
||||
if(actualZoom.x >= defaultTileSize - zoomTileDeadArea && actualZoom.x <= defaultTileSize + zoomTileDeadArea)
|
||||
actualZoom.x = defaultTileSize;
|
||||
if(actualZoom.y >= defaultTileSize - zoomTileDeadArea && actualZoom.y <= defaultTileSize + zoomTileDeadArea)
|
||||
actualZoom.y = defaultTileSize;
|
||||
}
|
||||
|
||||
bool isInDeadZone = targetTileSize != actualZoom || actualZoom == Point(defaultTileSize, defaultTileSize);
|
||||
|
||||
@ -346,6 +350,7 @@ bool MapViewController::isEventVisible(const CGHeroInstance * obj, const int3 &
|
||||
|
||||
void MapViewController::fadeOutObject(const CGObjectInstance * obj)
|
||||
{
|
||||
animationWait.setBusy();
|
||||
logGlobal->debug("Starting fade out animation");
|
||||
fadingOutContext = std::make_shared<MapRendererAdventureFadingContext>(*state);
|
||||
fadingOutContext->animationTime = adventureContext->animationTime;
|
||||
@ -366,6 +371,7 @@ void MapViewController::fadeOutObject(const CGObjectInstance * obj)
|
||||
|
||||
void MapViewController::fadeInObject(const CGObjectInstance * obj)
|
||||
{
|
||||
animationWait.setBusy();
|
||||
logGlobal->debug("Starting fade in animation");
|
||||
fadingInContext = std::make_shared<MapRendererAdventureFadingContext>(*state);
|
||||
fadingInContext->animationTime = adventureContext->animationTime;
|
||||
@ -505,6 +511,7 @@ void MapViewController::onAfterHeroTeleported(const CGHeroInstance * obj, const
|
||||
|
||||
if(isEventVisible(obj, from, dest))
|
||||
{
|
||||
animationWait.setBusy();
|
||||
logGlobal->debug("Starting teleport animation");
|
||||
teleportContext = std::make_shared<MapRendererAdventureTransitionContext>(*state);
|
||||
teleportContext->animationTime = adventureContext->animationTime;
|
||||
@ -540,6 +547,7 @@ void MapViewController::onHeroMoved(const CGHeroInstance * obj, const int3 & fro
|
||||
|
||||
if(movementTime > 1)
|
||||
{
|
||||
animationWait.setBusy();
|
||||
logGlobal->debug("Starting movement animation");
|
||||
movementContext = std::make_shared<MapRendererAdventureMovingContext>(*state);
|
||||
movementContext->animationTime = adventureContext->animationTime;
|
||||
@ -577,6 +585,17 @@ bool MapViewController::hasOngoingAnimations()
|
||||
return false;
|
||||
}
|
||||
|
||||
void MapViewController::waitForOngoingAnimations()
|
||||
{
|
||||
auto unlockInterface = vstd::makeUnlockGuard(GH.interfaceMutex);
|
||||
animationWait.waitWhileBusy();
|
||||
}
|
||||
|
||||
void MapViewController::endNetwork()
|
||||
{
|
||||
animationWait.requestTermination();
|
||||
}
|
||||
|
||||
void MapViewController::activateAdventureContext(uint32_t animationTime)
|
||||
{
|
||||
resetContext();
|
||||
@ -642,6 +661,7 @@ void MapViewController::resetContext()
|
||||
worldViewContext.reset();
|
||||
spellViewContext.reset();
|
||||
puzzleMapContext.reset();
|
||||
animationWait.setFree();
|
||||
}
|
||||
|
||||
void MapViewController::setTerrainVisibility(bool showAllTerrain)
|
||||
|
@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "IMapRendererObserver.h"
|
||||
#include "../ConditionalWait.h"
|
||||
#include "../../lib/Point.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
@ -34,6 +35,8 @@ class MapRendererPuzzleMapContext;
|
||||
/// such as its position and any animations
|
||||
class MapViewController : public IMapObjectObserver
|
||||
{
|
||||
ConditionalWait animationWait;
|
||||
|
||||
std::shared_ptr<IMapRendererContext> context;
|
||||
std::shared_ptr<MapRendererContextState> state;
|
||||
std::shared_ptr<MapViewModel> model;
|
||||
@ -52,7 +55,7 @@ class MapViewController : public IMapObjectObserver
|
||||
|
||||
private:
|
||||
const int defaultTileSize = 32;
|
||||
const int zoomTileDeadArea = 5;
|
||||
const int zoomTileDeadArea = 4;
|
||||
Point targetTileSize = Point(32, 32);
|
||||
bool wasInDeadZone = true;
|
||||
|
||||
@ -68,6 +71,9 @@ private:
|
||||
|
||||
// IMapObjectObserver impl
|
||||
bool hasOngoingAnimations() override;
|
||||
void waitForOngoingAnimations() override;
|
||||
void endNetwork() override;
|
||||
|
||||
void onObjectFadeIn(const CGObjectInstance * obj, const PlayerColor & initiator) override;
|
||||
void onObjectFadeOut(const CGObjectInstance * obj, const PlayerColor & initiator) override;
|
||||
void onObjectInstantAdd(const CGObjectInstance * obj, const PlayerColor & initiator) override;
|
||||
@ -91,7 +97,7 @@ public:
|
||||
void setViewCenter(const int3 & position);
|
||||
void setViewCenter(const Point & position, int level);
|
||||
void setTileSize(const Point & tileSize);
|
||||
void modifyTileSize(int stepsChange);
|
||||
void modifyTileSize(int stepsChange, bool useDeadZone);
|
||||
void tick(uint32_t timePassed);
|
||||
void afterRender();
|
||||
|
||||
|
@ -19,7 +19,6 @@
|
||||
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/TerrainHandler.h"
|
||||
#include "../../lib/UnlockGuard.h"
|
||||
#include "../../lib/mapObjectConstructors/CObjectClassesHandler.h"
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../../lib/mapObjects/ObjectTemplate.h"
|
||||
@ -36,13 +35,19 @@ bool CMapHandler::hasOngoingAnimations()
|
||||
|
||||
void CMapHandler::waitForOngoingAnimations()
|
||||
{
|
||||
while(CGI->mh->hasOngoingAnimations())
|
||||
for(auto * observer : observers)
|
||||
{
|
||||
auto unlockInterface = vstd::makeUnlockGuard(GH.interfaceMutex);
|
||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(1));
|
||||
if (observer->hasOngoingAnimations())
|
||||
observer->waitForOngoingAnimations();
|
||||
}
|
||||
}
|
||||
|
||||
void CMapHandler::endNetwork()
|
||||
{
|
||||
for(auto * observer : observers)
|
||||
observer->endNetwork();
|
||||
}
|
||||
|
||||
std::string CMapHandler::getTerrainDescr(const int3 & pos, bool rightClick) const
|
||||
{
|
||||
const TerrainTile & t = map->getTile(pos);
|
||||
|
@ -71,6 +71,7 @@ public:
|
||||
|
||||
/// blocking wait until all ongoing animatins are over
|
||||
void waitForOngoingAnimations();
|
||||
void endNetwork();
|
||||
|
||||
static bool compareObjectBlitOrder(const CGObjectInstance * a, const CGObjectInstance * b);
|
||||
};
|
||||
|
@ -140,11 +140,6 @@ void CCommanderArtPlace::showPopupWindow(const Point & cursorPosition)
|
||||
CArtPlace::showPopupWindow(cursorPosition);
|
||||
}
|
||||
|
||||
CHeroArtPlace::CHeroArtPlace(Point position, const CArtifactInstance * art)
|
||||
: CArtPlace(position, art)
|
||||
{
|
||||
}
|
||||
|
||||
void CArtPlace::lockSlot(bool on)
|
||||
{
|
||||
if(locked == on)
|
||||
@ -219,20 +214,35 @@ void CArtPlace::setGestureCallback(const ClickFunctor & callback)
|
||||
gestureCallback = callback;
|
||||
}
|
||||
|
||||
void CHeroArtPlace::addCombinedArtInfo(const std::map<const CArtifact*, int> & arts)
|
||||
void CArtPlace::addCombinedArtInfo(const std::map<const ArtifactID, std::vector<ArtifactID>> & arts)
|
||||
{
|
||||
for(const auto & combinedArt : arts)
|
||||
for(const auto & availableArts : arts)
|
||||
{
|
||||
std::string artList;
|
||||
text += "\n\n";
|
||||
text += "{" + combinedArt.first->getNameTranslated() + "}";
|
||||
if(arts.size() == 1)
|
||||
const auto combinedArt = availableArts.first.toArtifact();
|
||||
MetaString info;
|
||||
info.appendEOL();
|
||||
info.appendEOL();
|
||||
info.appendRawString("{");
|
||||
info.appendName(combinedArt->getId());
|
||||
info.appendRawString("}");
|
||||
info.appendRawString(" (%d/%d)");
|
||||
info.replaceNumber(availableArts.second.size());
|
||||
info.replaceNumber(combinedArt->getConstituents().size());
|
||||
for(const auto part : combinedArt->getConstituents())
|
||||
{
|
||||
for(const auto part : combinedArt.first->getConstituents())
|
||||
artList += "\n" + part->getNameTranslated();
|
||||
info.appendEOL();
|
||||
if(vstd::contains(availableArts.second, part->getId()))
|
||||
{
|
||||
info.appendName(part->getId());
|
||||
}
|
||||
else
|
||||
{
|
||||
info.appendRawString("{#A9A9A9|");
|
||||
info.appendName(part->getId());
|
||||
info.appendRawString("}");
|
||||
}
|
||||
}
|
||||
text += " (" + boost::str(boost::format("%d") % combinedArt.second) + " / " +
|
||||
boost::str(boost::format("%d") % combinedArt.first->getConstituents().size()) + ")" + artList;
|
||||
text += info.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@ public:
|
||||
void clickPressed(const Point & cursorPosition) override;
|
||||
void showPopupWindow(const Point & cursorPosition) override;
|
||||
void gesture(bool on, const Point & initialPosition, const Point & finalPosition) override;
|
||||
void addCombinedArtInfo(const std::map<const ArtifactID, std::vector<ArtifactID>> & arts);
|
||||
|
||||
private:
|
||||
const CArtifactInstance * ourArt;
|
||||
@ -59,13 +60,6 @@ public:
|
||||
void showPopupWindow(const Point & cursorPosition) override;
|
||||
};
|
||||
|
||||
class CHeroArtPlace: public CArtPlace
|
||||
{
|
||||
public:
|
||||
CHeroArtPlace(Point position, const CArtifactInstance * art = nullptr);
|
||||
void addCombinedArtInfo(const std::map<const CArtifact*, int> & arts);
|
||||
};
|
||||
|
||||
namespace ArtifactUtilsClient
|
||||
{
|
||||
bool askToAssemble(const CGHeroInstance * hero, const ArtifactPosition & slot);
|
||||
|
@ -82,7 +82,7 @@ void CArtifactsOfHeroBackpack::initAOHbackpack(size_t slots, bool slider)
|
||||
const auto pos = Point(slotSizeWithMargin * (artPlaceIdx % slotsColumnsMax),
|
||||
slotSizeWithMargin * (artPlaceIdx / slotsColumnsMax));
|
||||
backpackSlotsBackgrounds.emplace_back(std::make_shared<CPicture>(ImagePath::builtin("heroWindow/artifactSlotEmpty"), pos));
|
||||
artPlace = std::make_shared<CHeroArtPlace>(pos);
|
||||
artPlace = std::make_shared<CArtPlace>(pos);
|
||||
artPlace->setArtifact(nullptr);
|
||||
artPlace->setClickPressedCallback(std::bind(&CArtifactsOfHeroBase::clickPrassedArtPlace, this, _1, _2));
|
||||
artPlace->setShowPopupCallback(std::bind(&CArtifactsOfHeroBase::showPopupArtPlace, this, _1, _2));
|
||||
|
@ -55,12 +55,12 @@ void CArtifactsOfHeroBase::init(
|
||||
pos += position;
|
||||
for(int g = 0; g < ArtifactPosition::BACKPACK_START; g++)
|
||||
{
|
||||
artWorn[ArtifactPosition(g)] = std::make_shared<CHeroArtPlace>(slotPos[g]);
|
||||
artWorn[ArtifactPosition(g)] = std::make_shared<CArtPlace>(slotPos[g]);
|
||||
}
|
||||
backpack.clear();
|
||||
for(int s = 0; s < 5; s++)
|
||||
{
|
||||
auto artPlace = std::make_shared<CHeroArtPlace>(Point(403 + 46 * s, 365));
|
||||
auto artPlace = std::make_shared<CArtPlace>(Point(403 + 46 * s, 365));
|
||||
backpack.push_back(artPlace);
|
||||
}
|
||||
for(auto artPlace : artWorn)
|
||||
@ -256,21 +256,21 @@ void CArtifactsOfHeroBase::setSlotData(ArtPlacePtr artPlace, const ArtifactPosit
|
||||
{
|
||||
artPlace->lockSlot(slotInfo->locked);
|
||||
artPlace->setArtifact(slotInfo->artifact);
|
||||
if(!slotInfo->artifact->isCombined())
|
||||
if(slotInfo->locked || slotInfo->artifact->isCombined())
|
||||
return;
|
||||
|
||||
// If the artifact is part of at least one combined artifact, add additional information
|
||||
std::map<const ArtifactID, std::vector<ArtifactID>> arts;
|
||||
for(const auto combinedArt : slotInfo->artifact->artType->getPartOf())
|
||||
{
|
||||
// If the artifact is part of at least one combined artifact, add additional information
|
||||
std::map<const CArtifact*, int> arts;
|
||||
for(const auto combinedArt : slotInfo->artifact->artType->getPartOf())
|
||||
arts.try_emplace(combinedArt->getId(), std::vector<ArtifactID>{});
|
||||
for(const auto part : combinedArt->getConstituents())
|
||||
{
|
||||
arts.insert(std::pair(combinedArt, 0));
|
||||
for(const auto part : combinedArt->getConstituents())
|
||||
{
|
||||
if(curHero->hasArt(part->getId(), false))
|
||||
arts.at(combinedArt)++;
|
||||
}
|
||||
if(curHero->hasArt(part->getId(), false, false, false))
|
||||
arts.at(combinedArt->getId()).emplace_back(part->getId());
|
||||
}
|
||||
artPlace->addCombinedArtInfo(arts);
|
||||
}
|
||||
artPlace->addCombinedArtInfo(arts);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -18,7 +18,7 @@ class CButton;
|
||||
class CArtifactsOfHeroBase : virtual public CIntObject, public CKeyShortcut
|
||||
{
|
||||
protected:
|
||||
using ArtPlacePtr = std::shared_ptr<CHeroArtPlace>;
|
||||
using ArtPlacePtr = std::shared_ptr<CArtPlace>;
|
||||
using BpackScrollFunctor = std::function<void(int)>;
|
||||
|
||||
public:
|
||||
|
@ -33,49 +33,27 @@ void CArtifactsOfHeroMain::keyPressed(EShortcut key)
|
||||
{
|
||||
if(!shortcutPressed)
|
||||
{
|
||||
uint32_t costumeIdx;
|
||||
switch(key)
|
||||
int saveIdx = vstd::find_pos(costumeSaveShortcuts, key);
|
||||
int loadIdx = vstd::find_pos(costumeLoadShortcuts, key);
|
||||
|
||||
if (saveIdx != -1)
|
||||
{
|
||||
case EShortcut::HERO_COSTUME_0:
|
||||
costumeIdx = 0;
|
||||
break;
|
||||
case EShortcut::HERO_COSTUME_1:
|
||||
costumeIdx = 1;
|
||||
break;
|
||||
case EShortcut::HERO_COSTUME_2:
|
||||
costumeIdx = 2;
|
||||
break;
|
||||
case EShortcut::HERO_COSTUME_3:
|
||||
costumeIdx = 3;
|
||||
break;
|
||||
case EShortcut::HERO_COSTUME_4:
|
||||
costumeIdx = 4;
|
||||
break;
|
||||
case EShortcut::HERO_COSTUME_5:
|
||||
costumeIdx = 5;
|
||||
break;
|
||||
case EShortcut::HERO_COSTUME_6:
|
||||
costumeIdx = 6;
|
||||
break;
|
||||
case EShortcut::HERO_COSTUME_7:
|
||||
costumeIdx = 7;
|
||||
break;
|
||||
case EShortcut::HERO_COSTUME_8:
|
||||
costumeIdx = 8;
|
||||
break;
|
||||
case EShortcut::HERO_COSTUME_9:
|
||||
costumeIdx = 9;
|
||||
break;
|
||||
default:
|
||||
shortcutPressed = true;
|
||||
LOCPLINT->cb->manageHeroCostume(getHero()->id, saveIdx, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (loadIdx != -1)
|
||||
{
|
||||
shortcutPressed = true;
|
||||
LOCPLINT->cb->manageHeroCostume(getHero()->id, loadIdx, false);
|
||||
return;
|
||||
}
|
||||
shortcutPressed = true;
|
||||
LOCPLINT->cb->manageHeroCostume(getHero()->id, costumeIdx, GH.isKeyboardCtrlDown());
|
||||
}
|
||||
}
|
||||
|
||||
void CArtifactsOfHeroMain::keyReleased(EShortcut key)
|
||||
{
|
||||
if(vstd::contains(costumesSwitcherHotkeys, key))
|
||||
if(vstd::contains(costumeSaveShortcuts, key) || vstd::contains(costumeLoadShortcuts, key))
|
||||
shortcutPressed = false;
|
||||
}
|
||||
|
@ -20,17 +20,29 @@ public:
|
||||
void keyReleased(EShortcut key) override;
|
||||
|
||||
private:
|
||||
const std::vector<EShortcut> costumesSwitcherHotkeys =
|
||||
{
|
||||
EShortcut::HERO_COSTUME_0,
|
||||
EShortcut::HERO_COSTUME_1,
|
||||
EShortcut::HERO_COSTUME_2,
|
||||
EShortcut::HERO_COSTUME_3,
|
||||
EShortcut::HERO_COSTUME_4,
|
||||
EShortcut::HERO_COSTUME_5,
|
||||
EShortcut::HERO_COSTUME_6,
|
||||
EShortcut::HERO_COSTUME_7,
|
||||
EShortcut::HERO_COSTUME_8,
|
||||
EShortcut::HERO_COSTUME_9
|
||||
static constexpr std::array costumeSaveShortcuts = {
|
||||
EShortcut::HERO_COSTUME_SAVE_0,
|
||||
EShortcut::HERO_COSTUME_SAVE_1,
|
||||
EShortcut::HERO_COSTUME_SAVE_2,
|
||||
EShortcut::HERO_COSTUME_SAVE_3,
|
||||
EShortcut::HERO_COSTUME_SAVE_4,
|
||||
EShortcut::HERO_COSTUME_SAVE_5,
|
||||
EShortcut::HERO_COSTUME_SAVE_6,
|
||||
EShortcut::HERO_COSTUME_SAVE_7,
|
||||
EShortcut::HERO_COSTUME_SAVE_8,
|
||||
EShortcut::HERO_COSTUME_SAVE_9
|
||||
};
|
||||
|
||||
static constexpr std::array costumeLoadShortcuts = {
|
||||
EShortcut::HERO_COSTUME_LOAD_0,
|
||||
EShortcut::HERO_COSTUME_LOAD_1,
|
||||
EShortcut::HERO_COSTUME_LOAD_2,
|
||||
EShortcut::HERO_COSTUME_LOAD_3,
|
||||
EShortcut::HERO_COSTUME_LOAD_4,
|
||||
EShortcut::HERO_COSTUME_LOAD_5,
|
||||
EShortcut::HERO_COSTUME_LOAD_6,
|
||||
EShortcut::HERO_COSTUME_LOAD_7,
|
||||
EShortcut::HERO_COSTUME_LOAD_8,
|
||||
EShortcut::HERO_COSTUME_LOAD_9
|
||||
};
|
||||
};
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "../render/IImage.h"
|
||||
#include "../render/Graphics.h"
|
||||
#include "../windows/CCreatureWindow.h"
|
||||
#include "../windows/CWindowWithArtifacts.h"
|
||||
#include "../windows/GUIClasses.h"
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "CAltarArtifacts.h"
|
||||
|
||||
#include "../../gui/CGuiHandler.h"
|
||||
#include "../../gui/Shortcut.h"
|
||||
#include "../../widgets/Buttons.h"
|
||||
#include "../../widgets/TextControls.h"
|
||||
|
||||
@ -35,16 +36,16 @@ CAltarArtifacts::CAltarArtifacts(const IMarket * market, const CGHeroInstance *
|
||||
altarArtifacts = altarObj;
|
||||
|
||||
deal = std::make_shared<CButton>(Point(269, 520), AnimationPath::builtin("ALTSACR.DEF"),
|
||||
CGI->generaltexth->zelp[585], [this]() {CAltarArtifacts::makeDeal(); });
|
||||
CGI->generaltexth->zelp[585], [this]() {CAltarArtifacts::makeDeal(); }, EShortcut::MARKET_DEAL);
|
||||
labels.emplace_back(std::make_shared<CLabel>(450, 32, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[477]));
|
||||
labels.emplace_back(std::make_shared<CLabel>(302, 424, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[478]));
|
||||
|
||||
sacrificeAllButton = std::make_shared<CButton>(Point(393, 520), AnimationPath::builtin("ALTFILL.DEF"),
|
||||
CGI->generaltexth->zelp[571], std::bind(&CExperienceAltar::sacrificeAll, this));
|
||||
CGI->generaltexth->zelp[571], std::bind(&CExperienceAltar::sacrificeAll, this), EShortcut::MARKET_SACRIFICE_ALL);
|
||||
sacrificeAllButton->block(hero->artifactsInBackpack.empty() && hero->artifactsWorn.empty());
|
||||
|
||||
sacrificeBackpackButton = std::make_shared<CButton>(Point(147, 520), AnimationPath::builtin("ALTEMBK.DEF"),
|
||||
CGI->generaltexth->zelp[570], std::bind(&CAltarArtifacts::sacrificeBackpack, this));
|
||||
CGI->generaltexth->zelp[570], std::bind(&CAltarArtifacts::sacrificeBackpack, this), EShortcut::MARKET_SACRIFICE_BACKPACK);
|
||||
sacrificeBackpackButton->block(hero->artifactsInBackpack.empty());
|
||||
|
||||
// Hero's artifacts
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "CAltarCreatures.h"
|
||||
|
||||
#include "../../gui/CGuiHandler.h"
|
||||
#include "../../gui/Shortcut.h"
|
||||
#include "../../widgets/Buttons.h"
|
||||
#include "../../widgets/TextControls.h"
|
||||
|
||||
@ -33,7 +34,7 @@ CAltarCreatures::CAltarCreatures(const IMarket * market, const CGHeroInstance *
|
||||
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255 - DISPOSE);
|
||||
|
||||
deal = std::make_shared<CButton>(dealButtonPosWithSlider, AnimationPath::builtin("ALTSACR.DEF"),
|
||||
CGI->generaltexth->zelp[584], [this]() {CAltarCreatures::makeDeal();});
|
||||
CGI->generaltexth->zelp[584], [this]() {CAltarCreatures::makeDeal();}, EShortcut::MARKET_DEAL);
|
||||
labels.emplace_back(std::make_shared<CLabel>(155, 30, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW,
|
||||
boost::str(boost::format(CGI->generaltexth->allTexts[272]) % hero->getNameTranslated())));
|
||||
labels.emplace_back(std::make_shared<CLabel>(450, 30, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[479]));
|
||||
@ -44,7 +45,7 @@ CAltarCreatures::CAltarCreatures(const IMarket * market, const CGHeroInstance *
|
||||
unitsOnAltar.resize(GameConstants::ARMY_SIZE, 0);
|
||||
expPerUnit.resize(GameConstants::ARMY_SIZE, 0);
|
||||
sacrificeAllButton = std::make_shared<CButton>(
|
||||
Point(393, 520), AnimationPath::builtin("ALTARMY.DEF"), CGI->generaltexth->zelp[579], std::bind(&CExperienceAltar::sacrificeAll, this));
|
||||
Point(393, 520), AnimationPath::builtin("ALTARMY.DEF"), CGI->generaltexth->zelp[579], std::bind(&CExperienceAltar::sacrificeAll, this), EShortcut::MARKET_SACRIFICE_ALL);
|
||||
|
||||
// Hero creatures panel
|
||||
assert(bidTradePanel);
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "CArtifactsBuying.h"
|
||||
|
||||
#include "../../gui/CGuiHandler.h"
|
||||
#include "../../gui/Shortcut.h"
|
||||
#include "../../widgets/Buttons.h"
|
||||
#include "../../widgets/TextControls.h"
|
||||
|
||||
@ -38,7 +39,7 @@ CArtifactsBuying::CArtifactsBuying(const IMarket * market, const CGHeroInstance
|
||||
title = CGI->generaltexth->allTexts[349];
|
||||
labels.emplace_back(std::make_shared<CLabel>(titlePos.x, titlePos.y, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, title));
|
||||
deal = std::make_shared<CButton>(dealButtonPos, AnimationPath::builtin("TPMRKB.DEF"),
|
||||
CGI->generaltexth->zelp[595], [this](){CArtifactsBuying::makeDeal();});
|
||||
CGI->generaltexth->zelp[595], [this](){CArtifactsBuying::makeDeal();}, EShortcut::MARKET_DEAL);
|
||||
labels.emplace_back(std::make_shared<CLabel>(445, 148, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->allTexts[168]));
|
||||
|
||||
// Player's resources
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "CArtifactsSelling.h"
|
||||
|
||||
#include "../../gui/CGuiHandler.h"
|
||||
#include "../../gui/Shortcut.h"
|
||||
#include "../../widgets/Buttons.h"
|
||||
#include "../../widgets/TextControls.h"
|
||||
|
||||
@ -43,7 +44,7 @@ CArtifactsSelling::CArtifactsSelling(const IMarket * market, const CGHeroInstanc
|
||||
labels.emplace_back(std::make_shared<CLabel>(titlePos.x, titlePos.y, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, title));
|
||||
labels.push_back(std::make_shared<CLabel>(155, 56, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, boost::str(boost::format(CGI->generaltexth->allTexts[271]) % hero->getNameTranslated())));
|
||||
deal = std::make_shared<CButton>(dealButtonPos, AnimationPath::builtin("TPMRKB.DEF"),
|
||||
CGI->generaltexth->zelp[595], [this](){CArtifactsSelling::makeDeal();});
|
||||
CGI->generaltexth->zelp[595], [this](){CArtifactsSelling::makeDeal();}, EShortcut::MARKET_DEAL);
|
||||
bidSelectedSlot = std::make_shared<CTradeableItem>(Rect(Point(123, 470), Point(69, 66)), EType::ARTIFACT_TYPE, 0, 0);
|
||||
|
||||
// Market resources panel
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "CFreelancerGuild.h"
|
||||
|
||||
#include "../../gui/CGuiHandler.h"
|
||||
#include "../../gui/Shortcut.h"
|
||||
#include "../../widgets/Buttons.h"
|
||||
#include "../../widgets/TextControls.h"
|
||||
|
||||
@ -39,7 +40,7 @@ CFreelancerGuild::CFreelancerGuild(const IMarket * market, const CGHeroInstance
|
||||
labels.emplace_back(std::make_shared<CLabel>(155, 103, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE,
|
||||
boost::str(boost::format(CGI->generaltexth->allTexts[272]) % hero->getNameTranslated())));
|
||||
deal = std::make_shared<CButton>(dealButtonPosWithSlider, AnimationPath::builtin("TPMRKB.DEF"),
|
||||
CGI->generaltexth->zelp[595], [this]() {CFreelancerGuild::makeDeal();});
|
||||
CGI->generaltexth->zelp[595], [this]() {CFreelancerGuild::makeDeal();}, EShortcut::MARKET_DEAL);
|
||||
offerSlider->moveTo(pos.topLeft() + Point(232, 489));
|
||||
|
||||
// Hero creatures panel
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include "../Images.h"
|
||||
#include "../../gui/CGuiHandler.h"
|
||||
#include "../../gui/Shortcut.h"
|
||||
#include "../../widgets/Buttons.h"
|
||||
#include "../../widgets/TextControls.h"
|
||||
|
||||
@ -200,7 +201,7 @@ CMarketSlider::CMarketSlider(const CSlider::SliderMovingFunctor & movingCallback
|
||||
[this]()
|
||||
{
|
||||
offerSlider->scrollToMax();
|
||||
});
|
||||
}, EShortcut::MARKET_MAX_AMOUNT);
|
||||
}
|
||||
|
||||
void CMarketSlider::deselect()
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "CMarketResources.h"
|
||||
|
||||
#include "../../gui/CGuiHandler.h"
|
||||
#include "../../gui/Shortcut.h"
|
||||
#include "../../widgets/Buttons.h"
|
||||
#include "../../widgets/TextControls.h"
|
||||
|
||||
@ -36,7 +37,7 @@ CMarketResources::CMarketResources(const IMarket * market, const CGHeroInstance
|
||||
|
||||
labels.emplace_back(std::make_shared<CLabel>(titlePos.x, titlePos.y, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[158]));
|
||||
deal = std::make_shared<CButton>(dealButtonPosWithSlider, AnimationPath::builtin("TPMRKB.DEF"),
|
||||
CGI->generaltexth->zelp[595], [this]() {CMarketResources::makeDeal(); });
|
||||
CGI->generaltexth->zelp[595], [this]() {CMarketResources::makeDeal(); }, EShortcut::MARKET_DEAL);
|
||||
|
||||
// Player's resources
|
||||
assert(bidTradePanel);
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "CTransferResources.h"
|
||||
|
||||
#include "../../gui/CGuiHandler.h"
|
||||
#include "../../gui/Shortcut.h"
|
||||
#include "../../widgets/Buttons.h"
|
||||
#include "../../widgets/TextControls.h"
|
||||
|
||||
@ -34,7 +35,7 @@ CTransferResources::CTransferResources(const IMarket * market, const CGHeroInsta
|
||||
labels.emplace_back(std::make_shared<CLabel>(titlePos.x, titlePos.y, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[158]));
|
||||
labels.emplace_back(std::make_shared<CLabel>(445, 56, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->allTexts[169]));
|
||||
deal = std::make_shared<CButton>(dealButtonPosWithSlider, AnimationPath::builtin("TPMRKB.DEF"),
|
||||
CGI->generaltexth->zelp[595], [this](){CTransferResources::makeDeal();});
|
||||
CGI->generaltexth->zelp[595], [this](){CTransferResources::makeDeal();}, EShortcut::MARKET_DEAL);
|
||||
|
||||
// Player's resources
|
||||
assert(bidTradePanel);
|
||||
|
@ -1036,6 +1036,40 @@ void CCastleBuildings::openTownHall()
|
||||
GH.windows().createAndPushWindow<CHallInterface>(town);
|
||||
}
|
||||
|
||||
void CCastleBuildings::enterAnyThievesGuild()
|
||||
{
|
||||
std::vector<const CGTownInstance*> towns = LOCPLINT->cb->getTownsInfo(true);
|
||||
for(auto & town : towns)
|
||||
{
|
||||
if(town->builtBuildings.count(BuildingID::TAVERN))
|
||||
{
|
||||
LOCPLINT->showThievesGuildWindow(town);
|
||||
return;
|
||||
}
|
||||
}
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.noTownWithTavern"));
|
||||
}
|
||||
|
||||
void CCastleBuildings::enterAnyMarket()
|
||||
{
|
||||
if(town->builtBuildings.count(BuildingID::MARKETPLACE))
|
||||
{
|
||||
GH.windows().createAndPushWindow<CMarketWindow>(town, nullptr, nullptr, EMarketMode::RESOURCE_RESOURCE);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<const CGTownInstance*> towns = LOCPLINT->cb->getTownsInfo(true);
|
||||
for(auto & town : towns)
|
||||
{
|
||||
if(town->builtBuildings.count(BuildingID::MARKETPLACE))
|
||||
{
|
||||
GH.windows().createAndPushWindow<CMarketWindow>(town, nullptr, nullptr, EMarketMode::RESOURCE_RESOURCE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.noTownWithMarket"));
|
||||
}
|
||||
|
||||
CCreaInfo::CCreaInfo(Point position, const CGTownInstance * Town, int Level, bool compact, bool _showAvailable):
|
||||
town(Town),
|
||||
level(Level),
|
||||
@ -1216,7 +1250,7 @@ CCastleInterface::CCastleInterface(const CGTownInstance * Town, const CGTownInst
|
||||
exit = std::make_shared<CButton>(Point(744, 544), AnimationPath::builtin("TSBTNS"), CButton::tooltip(CGI->generaltexth->tcommands[8]), [&](){close();}, EShortcut::GLOBAL_RETURN);
|
||||
exit->setImageOrder(4, 5, 6, 7);
|
||||
|
||||
auto split = std::make_shared<CButton>(Point(744, 382), AnimationPath::builtin("TSBTNS"), CButton::tooltip(CGI->generaltexth->tcommands[3]), [this]() { garr->splitClick(); });
|
||||
auto split = std::make_shared<CButton>(Point(744, 382), AnimationPath::builtin("TSBTNS"), CButton::tooltip(CGI->generaltexth->tcommands[3]), [this]() { garr->splitClick(); }, EShortcut::HERO_ARMY_SPLIT);
|
||||
garr->addSplitBtn(split);
|
||||
|
||||
Rect barRect(9, 182, 732, 18);
|
||||
@ -1225,8 +1259,8 @@ CCastleInterface::CCastleInterface(const CGTownInstance * Town, const CGTownInst
|
||||
resdatabar = std::make_shared<CResDataBar>(ImagePath::builtin("ARESBAR"), 3, 575, 37, 3, 84, 78);
|
||||
|
||||
townlist = std::make_shared<CTownList>(3, Rect(Point(743, 414), Point(48, 128)), Point(1,16), Point(0, 32), LOCPLINT->localState->getOwnedTowns().size() );
|
||||
townlist->setScrollUpButton( std::make_shared<CButton>( Point(744, 414), AnimationPath::builtin("IAM014"), CButton::tooltipLocalized("core.help.306")));
|
||||
townlist->setScrollDownButton( std::make_shared<CButton>( Point(744, 526), AnimationPath::builtin("IAM015"), CButton::tooltipLocalized("core.help.307")));
|
||||
townlist->setScrollUpButton( std::make_shared<CButton>( Point(744, 414), AnimationPath::builtin("IAM014"), CButton::tooltipLocalized("core.help.306"), 0, EShortcut::MOVE_UP));
|
||||
townlist->setScrollDownButton( std::make_shared<CButton>( Point(744, 526), AnimationPath::builtin("IAM015"), CButton::tooltipLocalized("core.help.307"), 0, EShortcut::MOVE_DOWN));
|
||||
|
||||
if(from)
|
||||
townlist->select(from);
|
||||
@ -1327,27 +1361,14 @@ void CCastleInterface::recreateIcons()
|
||||
hall = std::make_shared<CTownInfo>(80, 413, town, true);
|
||||
fort = std::make_shared<CTownInfo>(122, 413, town, false);
|
||||
|
||||
fastTownHall = std::make_shared<CButton>(Point(80, 413), AnimationPath::builtin("castleInterfaceQuickAccess"), CButton::tooltip(), [this](){ builds->enterTownHall(); });
|
||||
fastTownHall = std::make_shared<CButton>(Point(80, 413), AnimationPath::builtin("castleInterfaceQuickAccess"), CButton::tooltip(), [this](){ builds->enterTownHall(); }, EShortcut::TOWN_OPEN_HALL);
|
||||
fastTownHall->setOverlay(std::make_shared<CAnimImage>(AnimationPath::builtin("ITMTL"), town->hallLevel()));
|
||||
|
||||
int imageIndex = town->fortLevel() == CGTownInstance::EFortLevel::NONE ? 3 : town->fortLevel() - 1;
|
||||
fastArmyPurchase = std::make_shared<CButton>(Point(122, 413), AnimationPath::builtin("castleInterfaceQuickAccess"), CButton::tooltip(), [this](){ builds->enterToTheQuickRecruitmentWindow(); });
|
||||
fastArmyPurchase = std::make_shared<CButton>(Point(122, 413), AnimationPath::builtin("castleInterfaceQuickAccess"), CButton::tooltip(), [this](){ builds->enterToTheQuickRecruitmentWindow(); }, EShortcut::TOWN_OPEN_RECRUITMENT);
|
||||
fastArmyPurchase->setOverlay(std::make_shared<CAnimImage>(AnimationPath::builtin("itmcl"), imageIndex));
|
||||
|
||||
fastMarket = std::make_shared<LRClickableArea>(Rect(163, 410, 64, 42), [&]()
|
||||
{
|
||||
std::vector<const CGTownInstance*> towns = LOCPLINT->cb->getTownsInfo(true);
|
||||
for(auto & town : towns)
|
||||
{
|
||||
if(town->builtBuildings.count(BuildingID::MARKETPLACE))
|
||||
{
|
||||
GH.windows().createAndPushWindow<CMarketWindow>(town, nullptr, nullptr, EMarketMode::RESOURCE_RESOURCE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.noTownWithMarket"));
|
||||
});
|
||||
|
||||
fastMarket = std::make_shared<LRClickableArea>(Rect(163, 410, 64, 42), [this]() { builds->enterAnyMarket(); });
|
||||
fastTavern = std::make_shared<LRClickableArea>(Rect(15, 387, 58, 64), [&]()
|
||||
{
|
||||
if(town->builtBuildings.count(BuildingID::TAVERN))
|
||||
@ -1368,18 +1389,41 @@ void CCastleInterface::recreateIcons()
|
||||
|
||||
for(size_t i=0; i<4; i++)
|
||||
creainfo.push_back(std::make_shared<CCreaInfo>(Point(14 + 55 * (int)i, 507), town, (int)i + 4, compactCreatureInfo, useAvailableCreaturesForLabel));
|
||||
|
||||
}
|
||||
|
||||
void CCastleInterface::keyPressed(EShortcut key)
|
||||
{
|
||||
switch(key)
|
||||
{
|
||||
case EShortcut::MOVE_UP:
|
||||
townlist->selectPrev();
|
||||
case EShortcut::TOWN_OPEN_FORT:
|
||||
GH.windows().createAndPushWindow<CFortScreen>(town);
|
||||
break;
|
||||
case EShortcut::MOVE_DOWN:
|
||||
townlist->selectNext();
|
||||
case EShortcut::TOWN_OPEN_MARKET:
|
||||
builds->enterAnyMarket();
|
||||
break;
|
||||
case EShortcut::TOWN_OPEN_MAGE_GUILD:
|
||||
if(town->hasBuilt(BuildingID::MAGES_GUILD_1))
|
||||
builds->enterMagesGuild();
|
||||
break;
|
||||
case EShortcut::TOWN_OPEN_THIEVES_GUILD:
|
||||
break;
|
||||
case EShortcut::TOWN_OPEN_HERO_EXCHANGE:
|
||||
if (town->visitingHero && town->garrisonHero)
|
||||
LOCPLINT->showHeroExchange(town->visitingHero->id, town->garrisonHero->id);
|
||||
break;
|
||||
case EShortcut::TOWN_OPEN_HERO:
|
||||
if (town->visitingHero)
|
||||
LOCPLINT->openHeroWindow(town->visitingHero);
|
||||
else if (town->garrisonHero)
|
||||
LOCPLINT->openHeroWindow(town->garrisonHero);
|
||||
break;
|
||||
case EShortcut::TOWN_OPEN_VISITING_HERO:
|
||||
if (town->visitingHero)
|
||||
LOCPLINT->openHeroWindow(town->visitingHero);
|
||||
break;
|
||||
case EShortcut::TOWN_OPEN_GARRISONED_HERO:
|
||||
if (town->garrisonHero)
|
||||
LOCPLINT->openHeroWindow(town->garrisonHero);
|
||||
break;
|
||||
case EShortcut::TOWN_SWAP_ARMIES:
|
||||
heroes->swapArmies();
|
||||
|
@ -154,7 +154,6 @@ class CCastleBuildings : public CIntObject
|
||||
void enterBuilding(BuildingID building);//for buildings with simple description + pic left-click messages
|
||||
void enterCastleGate();
|
||||
void enterFountain(const BuildingID & building, BuildingSubID::EBuildingSubID subID, BuildingID upgrades);//Rampart's fountains
|
||||
void enterMagesGuild();
|
||||
|
||||
void openMagesGuild();
|
||||
void openTownHall();
|
||||
@ -168,6 +167,9 @@ public:
|
||||
|
||||
void enterDwelling(int level);
|
||||
void enterTownHall();
|
||||
void enterMagesGuild();
|
||||
void enterAnyMarket();
|
||||
void enterAnyThievesGuild();
|
||||
void enterToTheQuickRecruitmentWindow();
|
||||
|
||||
void buildingClicked(BuildingID building, BuildingSubID::EBuildingSubID subID = BuildingSubID::NONE, BuildingID upgrades = BuildingID::NONE);
|
||||
|
383
client/windows/CExchangeWindow.cpp
Normal file
383
client/windows/CExchangeWindow.cpp
Normal file
@ -0,0 +1,383 @@
|
||||
/*
|
||||
* CExchangeWindow.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 "CExchangeWindow.h"
|
||||
|
||||
#include "CHeroBackpackWindow.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
|
||||
#include "../widgets/CGarrisonInt.h"
|
||||
#include "../widgets/Images.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
|
||||
#include "../render/IRenderHandler.h"
|
||||
#include "../render/CAnimation.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
|
||||
#include "../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../lib/CGeneralTextHandler.h"
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/CSkillHandler.h"
|
||||
#include "../lib/TextOperations.h"
|
||||
|
||||
static const std::string QUICK_EXCHANGE_BG = "quick-exchange/TRADEQE";
|
||||
|
||||
static bool isQuickExchangeLayoutAvailable()
|
||||
{
|
||||
return CResourceHandler::get()->existsResource(ImagePath::builtin("SPRITES/" + QUICK_EXCHANGE_BG));
|
||||
}
|
||||
|
||||
CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID queryID)
|
||||
: CWindowObject(PLAYER_COLORED | BORDERED, ImagePath::builtin(isQuickExchangeLayoutAvailable() ? QUICK_EXCHANGE_BG : "TRADE2")),
|
||||
controller(hero1, hero2),
|
||||
moveStackLeftButtons(),
|
||||
moveStackRightButtons()
|
||||
{
|
||||
const bool qeLayout = isQuickExchangeLayoutAvailable();
|
||||
|
||||
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
|
||||
addUsedEvents(KEYBOARD);
|
||||
|
||||
heroInst[0] = LOCPLINT->cb->getHero(hero1);
|
||||
heroInst[1] = LOCPLINT->cb->getHero(hero2);
|
||||
|
||||
auto genTitle = [](const CGHeroInstance * h)
|
||||
{
|
||||
boost::format fmt(CGI->generaltexth->allTexts[138]);
|
||||
fmt % h->getNameTranslated() % h->level % h->getClassNameTranslated();
|
||||
return boost::str(fmt);
|
||||
};
|
||||
|
||||
titles[0] = std::make_shared<CLabel>(147, 25, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, genTitle(heroInst[0]));
|
||||
titles[1] = std::make_shared<CLabel>(653, 25, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, genTitle(heroInst[1]));
|
||||
|
||||
auto PSKIL32 = GH.renderHandler().loadAnimation(AnimationPath::builtin("PSKIL32"));
|
||||
PSKIL32->preload();
|
||||
|
||||
auto SECSK32 = GH.renderHandler().loadAnimation(AnimationPath::builtin("SECSK32"));
|
||||
|
||||
for(int g = 0; g < 4; ++g)
|
||||
{
|
||||
if (qeLayout)
|
||||
primSkillImages.push_back(std::make_shared<CAnimImage>(PSKIL32, g, Rect(389, 12 + 26 * g, 22, 22)));
|
||||
else
|
||||
primSkillImages.push_back(std::make_shared<CAnimImage>(PSKIL32, g, 0, 385, 19 + 36 * g));
|
||||
}
|
||||
|
||||
for(int leftRight : {0, 1})
|
||||
{
|
||||
const CGHeroInstance * hero = heroInst.at(leftRight);
|
||||
|
||||
for(int m=0; m<GameConstants::PRIMARY_SKILLS; ++m)
|
||||
primSkillValues[leftRight].push_back(std::make_shared<CLabel>(352 + (qeLayout ? 96 : 93) * leftRight, (qeLayout ? 22 : 35) + (qeLayout ? 26 : 36) * m, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE));
|
||||
|
||||
|
||||
for(int m=0; m < hero->secSkills.size(); ++m)
|
||||
secSkillIcons[leftRight].push_back(std::make_shared<CAnimImage>(SECSK32, 0, 0, 32 + 36 * m + 454 * leftRight, qeLayout ? 83 : 88));
|
||||
|
||||
specImages[leftRight] = std::make_shared<CAnimImage>(AnimationPath::builtin("UN32"), hero->type->imageIndex, 0, 67 + 490 * leftRight, qeLayout ? 41 : 45);
|
||||
|
||||
expImages[leftRight] = std::make_shared<CAnimImage>(PSKIL32, 4, 0, 103 + 490 * leftRight, qeLayout ? 41 : 45);
|
||||
expValues[leftRight] = std::make_shared<CLabel>(119 + 490 * leftRight, qeLayout ? 66 : 71, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE);
|
||||
|
||||
manaImages[leftRight] = std::make_shared<CAnimImage>(PSKIL32, 5, 0, 139 + 490 * leftRight, qeLayout ? 41 : 45);
|
||||
manaValues[leftRight] = std::make_shared<CLabel>(155 + 490 * leftRight, qeLayout ? 66 : 71, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE);
|
||||
}
|
||||
|
||||
artifs[0] = std::make_shared<CArtifactsOfHeroMain>(Point(-334, 151));
|
||||
artifs[0]->clickPressedCallback = [this, hero = heroInst[0]](const CArtPlace & artPlace, const Point & cursorPosition){clickPressedOnArtPlace(hero, artPlace.slot, true, false, false);};
|
||||
artifs[0]->showPopupCallback = [this, heroArts = artifs[0]](CArtPlace & artPlace, const Point & cursorPosition){showArtifactAssembling(*heroArts, artPlace, cursorPosition);};
|
||||
artifs[0]->gestureCallback = [this, hero = heroInst[0]](const CArtPlace & artPlace, const Point & cursorPosition){showQuickBackpackWindow(hero, artPlace.slot, cursorPosition);};
|
||||
artifs[0]->setHero(heroInst[0]);
|
||||
artifs[1] = std::make_shared<CArtifactsOfHeroMain>(Point(98, 151));
|
||||
artifs[1]->clickPressedCallback = [this, hero = heroInst[1]](const CArtPlace & artPlace, const Point & cursorPosition){clickPressedOnArtPlace(hero, artPlace.slot, true, false, false);};
|
||||
artifs[1]->showPopupCallback = [this, heroArts = artifs[1]](CArtPlace & artPlace, const Point & cursorPosition){showArtifactAssembling(*heroArts, artPlace, cursorPosition);};
|
||||
artifs[1]->gestureCallback = [this, hero = heroInst[1]](const CArtPlace & artPlace, const Point & cursorPosition){showQuickBackpackWindow(hero, artPlace.slot, cursorPosition);};
|
||||
artifs[1]->setHero(heroInst[1]);
|
||||
|
||||
|
||||
addSet(artifs[0]);
|
||||
addSet(artifs[1]);
|
||||
|
||||
for(int g=0; g<4; ++g)
|
||||
{
|
||||
primSkillAreas.push_back(std::make_shared<LRClickableAreaWTextComp>());
|
||||
if (qeLayout)
|
||||
primSkillAreas[g]->pos = Rect(Point(pos.x + 324, pos.y + 12 + 26 * g), Point(152, 22));
|
||||
else
|
||||
primSkillAreas[g]->pos = Rect(Point(pos.x + 329, pos.y + 19 + 36 * g), Point(140, 32));
|
||||
primSkillAreas[g]->text = CGI->generaltexth->arraytxt[2+g];
|
||||
primSkillAreas[g]->component = Component( ComponentType::PRIM_SKILL, PrimarySkill(g));
|
||||
primSkillAreas[g]->hoverText = CGI->generaltexth->heroscrn[1];
|
||||
boost::replace_first(primSkillAreas[g]->hoverText, "%s", CGI->generaltexth->primarySkillNames[g]);
|
||||
}
|
||||
|
||||
//heroes related thing
|
||||
for(int b=0; b < heroInst.size(); b++)
|
||||
{
|
||||
const CGHeroInstance * hero = heroInst.at(b);
|
||||
|
||||
//secondary skill's clickable areas
|
||||
for(int g=0; g<hero->secSkills.size(); ++g)
|
||||
{
|
||||
SecondarySkill skill = hero->secSkills[g].first;
|
||||
int level = hero->secSkills[g].second; // <1, 3>
|
||||
secSkillAreas[b].push_back(std::make_shared<LRClickableAreaWTextComp>());
|
||||
secSkillAreas[b][g]->pos = Rect(Point(pos.x + 32 + g * 36 + b * 454 , pos.y + (qeLayout ? 83 : 88)), Point(32, 32) );
|
||||
secSkillAreas[b][g]->component = Component(ComponentType::SEC_SKILL, skill, level);
|
||||
secSkillAreas[b][g]->text = CGI->skillh->getByIndex(skill)->getDescriptionTranslated(level);
|
||||
|
||||
secSkillAreas[b][g]->hoverText = CGI->generaltexth->heroscrn[21];
|
||||
boost::algorithm::replace_first(secSkillAreas[b][g]->hoverText, "%s", CGI->generaltexth->levels[level - 1]);
|
||||
boost::algorithm::replace_first(secSkillAreas[b][g]->hoverText, "%s", CGI->skillh->getByIndex(skill)->getNameTranslated());
|
||||
}
|
||||
|
||||
heroAreas[b] = std::make_shared<CHeroArea>(257 + 228 * b, 13, hero);
|
||||
heroAreas[b]->addClickCallback([this, hero]() -> void
|
||||
{
|
||||
if(getPickedArtifact() == nullptr)
|
||||
LOCPLINT->openHeroWindow(hero);
|
||||
});
|
||||
|
||||
specialtyAreas[b] = std::make_shared<LRClickableAreaWText>();
|
||||
specialtyAreas[b]->pos = Rect(Point(pos.x + 69 + 490 * b, pos.y + (qeLayout ? 41 : 45)), Point(32, 32));
|
||||
specialtyAreas[b]->hoverText = CGI->generaltexth->heroscrn[27];
|
||||
specialtyAreas[b]->text = hero->type->getSpecialtyDescriptionTranslated();
|
||||
|
||||
experienceAreas[b] = std::make_shared<LRClickableAreaWText>();
|
||||
experienceAreas[b]->pos = Rect(Point(pos.x + 105 + 490 * b, pos.y + (qeLayout ? 41 : 45)), Point(32, 32));
|
||||
experienceAreas[b]->hoverText = CGI->generaltexth->heroscrn[9];
|
||||
experienceAreas[b]->text = CGI->generaltexth->allTexts[2];
|
||||
boost::algorithm::replace_first(experienceAreas[b]->text, "%d", std::to_string(hero->level));
|
||||
boost::algorithm::replace_first(experienceAreas[b]->text, "%d", std::to_string(CGI->heroh->reqExp(hero->level+1)));
|
||||
boost::algorithm::replace_first(experienceAreas[b]->text, "%d", std::to_string(hero->exp));
|
||||
|
||||
spellPointsAreas[b] = std::make_shared<LRClickableAreaWText>();
|
||||
spellPointsAreas[b]->pos = Rect(Point(pos.x + 141 + 490 * b, pos.y + (qeLayout ? 41 : 45)), Point(32, 32));
|
||||
spellPointsAreas[b]->hoverText = CGI->generaltexth->heroscrn[22];
|
||||
spellPointsAreas[b]->text = CGI->generaltexth->allTexts[205];
|
||||
boost::algorithm::replace_first(spellPointsAreas[b]->text, "%s", hero->getNameTranslated());
|
||||
boost::algorithm::replace_first(spellPointsAreas[b]->text, "%d", std::to_string(hero->mana));
|
||||
boost::algorithm::replace_first(spellPointsAreas[b]->text, "%d", std::to_string(hero->manaLimit()));
|
||||
|
||||
morale[b] = std::make_shared<MoraleLuckBox>(true, Rect(Point(176 + 490 * b, 39), Point(32, 32)), true);
|
||||
luck[b] = std::make_shared<MoraleLuckBox>(false, Rect(Point(212 + 490 * b, 39), Point(32, 32)), true);
|
||||
}
|
||||
|
||||
quit = std::make_shared<CButton>(Point(732, 567), AnimationPath::builtin("IOKAY.DEF"), CGI->generaltexth->zelp[600], std::bind(&CExchangeWindow::close, this), EShortcut::GLOBAL_ACCEPT);
|
||||
if(queryID.getNum() > 0)
|
||||
quit->addCallback([=](){ LOCPLINT->cb->selectionMade(0, queryID); });
|
||||
|
||||
questlogButton[0] = std::make_shared<CButton>(Point( 10, qeLayout ? 39 : 44), AnimationPath::builtin("hsbtns4.def"), CButton::tooltip(CGI->generaltexth->heroscrn[0]), std::bind(&CExchangeWindow::questLogShortcut, this), EShortcut::ADVENTURE_QUEST_LOG);
|
||||
questlogButton[1] = std::make_shared<CButton>(Point(740, qeLayout ? 39 : 44), AnimationPath::builtin("hsbtns4.def"), CButton::tooltip(CGI->generaltexth->heroscrn[0]), std::bind(&CExchangeWindow::questLogShortcut, this), EShortcut::ADVENTURE_QUEST_LOG);
|
||||
|
||||
Rect barRect(5, 578, 725, 18);
|
||||
statusbar = CGStatusBar::create(std::make_shared<CPicture>(background->getSurface(), barRect, 5, 578));
|
||||
|
||||
//garrison interface
|
||||
|
||||
garr = std::make_shared<CGarrisonInt>(Point(69, qeLayout ? 122 : 131), 4, Point(418,0), heroInst[0], heroInst[1], true, true);
|
||||
auto splitButtonCallback = [&](){ garr->splitClick(); };
|
||||
garr->addSplitBtn(std::make_shared<CButton>( Point( 10, qeLayout ? 122 : 132), AnimationPath::builtin("TSBTNS.DEF"), CButton::tooltip(CGI->generaltexth->tcommands[3]), splitButtonCallback, EShortcut::HERO_ARMY_SPLIT));
|
||||
garr->addSplitBtn(std::make_shared<CButton>( Point(744, qeLayout ? 122 : 132), AnimationPath::builtin("TSBTNS.DEF"), CButton::tooltip(CGI->generaltexth->tcommands[3]), splitButtonCallback, EShortcut::HERO_ARMY_SPLIT));
|
||||
|
||||
if(qeLayout)
|
||||
{
|
||||
moveAllGarrButtonLeft = std::make_shared<CButton>(Point(325, 118), AnimationPath::builtin("quick-exchange/armRight.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[1]),
|
||||
[this](){ this->moveUnitsShortcut(false); });
|
||||
exchangeGarrButton = std::make_shared<CButton>(Point(377, 118), AnimationPath::builtin("quick-exchange/swapAll.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[2]),
|
||||
[this](){ controller.swapArmy(); });
|
||||
moveAllGarrButtonRight = std::make_shared<CButton>(Point(425, 118), AnimationPath::builtin("quick-exchange/armLeft.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[1]),
|
||||
[this](){ this->moveUnitsShortcut(true); });
|
||||
moveArtifactsButtonLeft = std::make_shared<CButton>(Point(325, 154), AnimationPath::builtin("quick-exchange/artRight.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[3]),
|
||||
[this](){ this->moveArtifactsCallback(false);});
|
||||
exchangeArtifactsButton = std::make_shared<CButton>(Point(377, 154), AnimationPath::builtin("quick-exchange/swapAll.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[4]),
|
||||
[this](){ this->swapArtifactsCallback(); });
|
||||
moveArtifactsButtonRight = std::make_shared<CButton>(Point(425, 154), AnimationPath::builtin("quick-exchange/artLeft.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[3]),
|
||||
[this](){ this->moveArtifactsCallback(true);});
|
||||
|
||||
backpackButtonLeft = std::make_shared<CButton>(Point(325, 518), AnimationPath::builtin("heroBackpack"), CButton::tooltipLocalized("vcmi.heroWindow.openBackpack"),
|
||||
[this](){ this->backpackShortcut(true); });
|
||||
backpackButtonRight = std::make_shared<CButton>(Point(419, 518), AnimationPath::builtin("heroBackpack"), CButton::tooltipLocalized("vcmi.heroWindow.openBackpack"),
|
||||
[this](){ this->backpackShortcut(false); });
|
||||
backpackButtonLeft->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("heroWindow/backpackButtonIcon")));
|
||||
backpackButtonRight->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("heroWindow/backpackButtonIcon")));
|
||||
|
||||
auto leftHeroBlock = heroInst[0]->tempOwner != LOCPLINT->cb->getPlayerID();
|
||||
auto rightHeroBlock = heroInst[1]->tempOwner != LOCPLINT->cb->getPlayerID();
|
||||
moveAllGarrButtonLeft->block(leftHeroBlock);
|
||||
exchangeGarrButton->block(leftHeroBlock || rightHeroBlock);
|
||||
moveAllGarrButtonRight->block(rightHeroBlock);
|
||||
moveArtifactsButtonLeft->block(leftHeroBlock);
|
||||
exchangeArtifactsButton->block(leftHeroBlock || rightHeroBlock);
|
||||
moveArtifactsButtonRight->block(rightHeroBlock);
|
||||
backpackButtonLeft->block(leftHeroBlock);
|
||||
backpackButtonRight->block(rightHeroBlock);
|
||||
|
||||
for(int i = 0; i < GameConstants::ARMY_SIZE; i++)
|
||||
{
|
||||
moveStackLeftButtons.push_back(
|
||||
std::make_shared<CButton>(
|
||||
Point(484 + 35 * i, 154),
|
||||
AnimationPath::builtin("quick-exchange/unitLeft.DEF"),
|
||||
CButton::tooltip(CGI->generaltexth->qeModCommands[1]),
|
||||
std::bind(&CExchangeController::moveStack, &controller, false, SlotID(i))));
|
||||
moveStackLeftButtons.back()->block(leftHeroBlock);
|
||||
|
||||
moveStackRightButtons.push_back(
|
||||
std::make_shared<CButton>(
|
||||
Point(66 + 35 * i, 154),
|
||||
AnimationPath::builtin("quick-exchange/unitRight.DEF"),
|
||||
CButton::tooltip(CGI->generaltexth->qeModCommands[1]),
|
||||
std::bind(&CExchangeController::moveStack, &controller, true, SlotID(i))));
|
||||
moveStackLeftButtons.back()->block(rightHeroBlock);
|
||||
}
|
||||
}
|
||||
|
||||
CWindowWithArtifacts::update();
|
||||
}
|
||||
|
||||
void CExchangeWindow::moveArtifactsCallback(bool leftToRight)
|
||||
{
|
||||
bool moveEquipped = !GH.isKeyboardShiftDown();
|
||||
bool moveBackpack = !GH.isKeyboardCmdDown();
|
||||
controller.moveArtifacts(leftToRight, moveEquipped, moveBackpack);
|
||||
};
|
||||
|
||||
void CExchangeWindow::swapArtifactsCallback()
|
||||
{
|
||||
bool moveEquipped = !GH.isKeyboardShiftDown();
|
||||
bool moveBackpack = !GH.isKeyboardCmdDown();
|
||||
controller.swapArtifacts(moveEquipped, moveBackpack);
|
||||
}
|
||||
|
||||
void CExchangeWindow::moveUnitsShortcut(bool leftToRight)
|
||||
{
|
||||
std::optional<SlotID> slotId = std::nullopt;
|
||||
if(const auto * slot = getSelectedSlotID())
|
||||
slotId = slot->getSlot();
|
||||
controller.moveArmy(leftToRight, slotId);
|
||||
};
|
||||
|
||||
void CExchangeWindow::backpackShortcut(bool leftHero)
|
||||
{
|
||||
GH.windows().createAndPushWindow<CHeroBackpackWindow>(heroInst[leftHero ? 0 : 1], artSets);
|
||||
};
|
||||
|
||||
void CExchangeWindow::keyPressed(EShortcut key)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case EShortcut::EXCHANGE_ARMY_TO_LEFT:
|
||||
moveUnitsShortcut(false);
|
||||
break;
|
||||
case EShortcut::EXCHANGE_ARMY_TO_RIGHT:
|
||||
moveUnitsShortcut(true);
|
||||
break;
|
||||
case EShortcut::EXCHANGE_ARMY_SWAP:
|
||||
controller.swapArmy();
|
||||
break;
|
||||
case EShortcut::EXCHANGE_ARTIFACTS_TO_LEFT:
|
||||
controller.moveArtifacts(false, true, true);
|
||||
break;
|
||||
case EShortcut::EXCHANGE_ARTIFACTS_TO_RIGHT:
|
||||
controller.moveArtifacts(true, true, true);
|
||||
break;
|
||||
case EShortcut::EXCHANGE_ARTIFACTS_SWAP:
|
||||
controller.swapArtifacts(true, true);
|
||||
break;
|
||||
case EShortcut::EXCHANGE_EQUIPPED_TO_LEFT:
|
||||
controller.moveArtifacts(false, true, false);
|
||||
break;
|
||||
case EShortcut::EXCHANGE_EQUIPPED_TO_RIGHT:
|
||||
controller.moveArtifacts(true, true, false);
|
||||
break;
|
||||
case EShortcut::EXCHANGE_EQUIPPED_SWAP:
|
||||
controller.swapArtifacts(true, false);
|
||||
break;
|
||||
case EShortcut::EXCHANGE_BACKPACK_TO_LEFT:
|
||||
controller.moveArtifacts(false, false, true);
|
||||
break;
|
||||
case EShortcut::EXCHANGE_BACKPACK_TO_RIGHT:
|
||||
controller.moveArtifacts(true, false, true);
|
||||
break;
|
||||
case EShortcut::EXCHANGE_BACKPACK_SWAP:
|
||||
controller.swapArtifacts(false, true);
|
||||
break;
|
||||
case EShortcut::EXCHANGE_BACKPACK_LEFT:
|
||||
backpackShortcut(true);
|
||||
break;
|
||||
case EShortcut::EXCHANGE_BACKPACK_RIGHT:
|
||||
backpackShortcut(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const CGarrisonSlot * CExchangeWindow::getSelectedSlotID() const
|
||||
{
|
||||
return garr->getSelection();
|
||||
}
|
||||
|
||||
void CExchangeWindow::updateGarrisons()
|
||||
{
|
||||
garr->recreateSlots();
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
bool CExchangeWindow::holdsGarrison(const CArmedInstance * army)
|
||||
{
|
||||
return garr->upperArmy() == army || garr->lowerArmy() == army;
|
||||
}
|
||||
|
||||
void CExchangeWindow::questLogShortcut()
|
||||
{
|
||||
CCS->curh->dragAndDropCursor(nullptr);
|
||||
LOCPLINT->showQuestLog();
|
||||
}
|
||||
|
||||
void CExchangeWindow::update()
|
||||
{
|
||||
for(size_t leftRight : {0, 1})
|
||||
{
|
||||
const CGHeroInstance * hero = heroInst.at(leftRight);
|
||||
|
||||
for(int m=0; m<GameConstants::PRIMARY_SKILLS; ++m)
|
||||
{
|
||||
auto value = heroInst[leftRight]->getPrimSkillLevel(static_cast<PrimarySkill>(m));
|
||||
primSkillValues[leftRight][m]->setText(std::to_string(value));
|
||||
}
|
||||
|
||||
for(int m=0; m < hero->secSkills.size(); ++m)
|
||||
{
|
||||
int id = hero->secSkills[m].first;
|
||||
int level = hero->secSkills[m].second;
|
||||
|
||||
secSkillIcons[leftRight][m]->setFrame(2 + id * 3 + level);
|
||||
}
|
||||
|
||||
expValues[leftRight]->setText(TextOperations::formatMetric(hero->exp, 3));
|
||||
manaValues[leftRight]->setText(TextOperations::formatMetric(hero->mana, 3));
|
||||
|
||||
morale[leftRight]->set(hero);
|
||||
luck[leftRight]->set(hero);
|
||||
}
|
||||
}
|
||||
|
78
client/windows/CExchangeWindow.h
Normal file
78
client/windows/CExchangeWindow.h
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* CExchangeWindow.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 "CWindowWithArtifacts.h"
|
||||
#include "../widgets/CExchangeController.h"
|
||||
|
||||
class CGarrisonSlot;
|
||||
|
||||
class CExchangeWindow : public CStatusbarWindow, public IGarrisonHolder, public CWindowWithArtifacts
|
||||
{
|
||||
std::array<std::shared_ptr<CLabel>, 2> titles;
|
||||
std::vector<std::shared_ptr<CAnimImage>> primSkillImages;//shared for both heroes
|
||||
std::array<std::vector<std::shared_ptr<CLabel>>, 2> primSkillValues;
|
||||
std::array<std::vector<std::shared_ptr<CAnimImage>>, 2> secSkillIcons;
|
||||
std::array<std::shared_ptr<CAnimImage>, 2> specImages;
|
||||
std::array<std::shared_ptr<CAnimImage>, 2> expImages;
|
||||
std::array<std::shared_ptr<CLabel>, 2> expValues;
|
||||
std::array<std::shared_ptr<CAnimImage>, 2> manaImages;
|
||||
std::array<std::shared_ptr<CLabel>, 2> manaValues;
|
||||
|
||||
std::vector<std::shared_ptr<LRClickableAreaWTextComp>> primSkillAreas;
|
||||
std::array<std::vector<std::shared_ptr<LRClickableAreaWTextComp>>, 2> secSkillAreas;
|
||||
|
||||
std::array<std::shared_ptr<CHeroArea>, 2> heroAreas;
|
||||
std::array<std::shared_ptr<LRClickableAreaWText>, 2> specialtyAreas;
|
||||
std::array<std::shared_ptr<LRClickableAreaWText>, 2> experienceAreas;
|
||||
std::array<std::shared_ptr<LRClickableAreaWText>, 2> spellPointsAreas;
|
||||
|
||||
std::array<std::shared_ptr<MoraleLuckBox>, 2> morale;
|
||||
std::array<std::shared_ptr<MoraleLuckBox>, 2> luck;
|
||||
|
||||
std::shared_ptr<CButton> quit;
|
||||
std::array<std::shared_ptr<CButton>, 2> questlogButton;
|
||||
|
||||
std::shared_ptr<CGarrisonInt> garr;
|
||||
std::shared_ptr<CButton> moveAllGarrButtonLeft;
|
||||
std::shared_ptr<CButton> exchangeGarrButton;
|
||||
std::shared_ptr<CButton> moveAllGarrButtonRight;
|
||||
std::shared_ptr<CButton> moveArtifactsButtonLeft;
|
||||
std::shared_ptr<CButton> exchangeArtifactsButton;
|
||||
std::shared_ptr<CButton> moveArtifactsButtonRight;
|
||||
std::vector<std::shared_ptr<CButton>> moveStackLeftButtons;
|
||||
std::vector<std::shared_ptr<CButton>> moveStackRightButtons;
|
||||
std::shared_ptr<CButton> backpackButtonLeft;
|
||||
std::shared_ptr<CButton> backpackButtonRight;
|
||||
CExchangeController controller;
|
||||
|
||||
void moveArtifactsCallback(bool leftToRight);
|
||||
void swapArtifactsCallback();
|
||||
void moveUnitsShortcut(bool leftToRight);
|
||||
void backpackShortcut(bool leftHero);
|
||||
void questLogShortcut();
|
||||
|
||||
std::array<const CGHeroInstance *, 2> heroInst;
|
||||
std::array<std::shared_ptr<CArtifactsOfHeroMain>, 2> artifs;
|
||||
|
||||
const CGarrisonSlot * getSelectedSlotID() const;
|
||||
|
||||
public:
|
||||
CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID queryID);
|
||||
|
||||
void keyPressed(EShortcut key) override;
|
||||
|
||||
void update() override;
|
||||
|
||||
// IGarrisonHolder impl
|
||||
void updateGarrisons() override;
|
||||
bool holdsGarrison(const CArmedInstance * army) override;
|
||||
|
||||
};
|
@ -13,7 +13,7 @@
|
||||
#include "CCreatureWindow.h"
|
||||
#include "CHeroBackpackWindow.h"
|
||||
#include "CKingdomInterface.h"
|
||||
#include "GUIClasses.h"
|
||||
#include "CExchangeWindow.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
@ -22,6 +22,7 @@
|
||||
#include "../gui/TextAlignment.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
#include "../widgets/Images.h"
|
||||
#include "../widgets/MiscWidgets.h"
|
||||
#include "../widgets/CComponent.h"
|
||||
#include "../widgets/CGarrisonInt.h"
|
||||
@ -207,7 +208,7 @@ void CHeroWindow::update()
|
||||
boost::algorithm::replace_first(helpBox, "%s", CGI->generaltexth->allTexts[43]);
|
||||
|
||||
garr = std::make_shared<CGarrisonInt>(Point(15, 485), 8, Point(), curHero);
|
||||
auto split = std::make_shared<CButton>(Point(539, 519), AnimationPath::builtin("hsbtns9.def"), CButton::tooltip(CGI->generaltexth->allTexts[256], helpBox), [&](){ garr->splitClick(); });
|
||||
auto split = std::make_shared<CButton>(Point(539, 519), AnimationPath::builtin("hsbtns9.def"), CButton::tooltip(CGI->generaltexth->allTexts[256], helpBox), [&](){ garr->splitClick(); }, EShortcut::HERO_ARMY_SPLIT);
|
||||
garr->addSplitBtn(split);
|
||||
}
|
||||
if(!arts)
|
||||
@ -278,9 +279,8 @@ void CHeroWindow::update()
|
||||
|
||||
for(auto cew : GH.windows().findWindows<CExchangeWindow>())
|
||||
{
|
||||
for(int g=0; g < cew->heroInst.size(); ++g)
|
||||
if(cew->heroInst[g] == curHero)
|
||||
noDismiss = true;
|
||||
if (cew->holdsGarrison(curHero))
|
||||
noDismiss = true;
|
||||
}
|
||||
|
||||
//if player only have one hero and no towns
|
||||
|
@ -878,7 +878,7 @@ class ArtSlotsTab : public CIntObject
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<CAnimImage> background;
|
||||
std::vector<std::shared_ptr<CHeroArtPlace>> arts;
|
||||
std::vector<std::shared_ptr<CArtPlace>> arts;
|
||||
|
||||
ArtSlotsTab()
|
||||
{
|
||||
@ -886,7 +886,7 @@ public:
|
||||
background = std::make_shared<CAnimImage>(AnimationPath::builtin("OVSLOT"), 4);
|
||||
pos = background->pos;
|
||||
for(int i=0; i<9; i++)
|
||||
arts.push_back(std::make_shared<CHeroArtPlace>(Point(269+i*48, 66)));
|
||||
arts.push_back(std::make_shared<CArtPlace>(Point(269+i*48, 66)));
|
||||
}
|
||||
};
|
||||
|
||||
@ -894,7 +894,7 @@ class BackpackTab : public CIntObject
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<CAnimImage> background;
|
||||
std::vector<std::shared_ptr<CHeroArtPlace>> arts;
|
||||
std::vector<std::shared_ptr<CArtPlace>> arts;
|
||||
std::shared_ptr<CButton> btnLeft;
|
||||
std::shared_ptr<CButton> btnRight;
|
||||
|
||||
@ -906,7 +906,7 @@ public:
|
||||
btnLeft = std::make_shared<CButton>(Point(269, 66), AnimationPath::builtin("HSBTNS3"), CButton::tooltip(), 0);
|
||||
btnRight = std::make_shared<CButton>(Point(675, 66), AnimationPath::builtin("HSBTNS5"), CButton::tooltip(), 0);
|
||||
for(int i=0; i<8; i++)
|
||||
arts.push_back(std::make_shared<CHeroArtPlace>(Point(294+i*48, 66)));
|
||||
arts.push_back(std::make_shared<CArtPlace>(Point(294+i*48, 66)));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -128,35 +128,35 @@ void CMarketWindow::createChangeModeButtons(EMarketMode currentMode, const IMark
|
||||
auto buttonPos = Point(18, 520);
|
||||
|
||||
auto addButton = [this, &buttonPos](const AnimationPath & picPath, const std::pair<std::string, std::string> & buttonHelpContainer,
|
||||
const std::function<void()> & pressButtonFunctor)
|
||||
const std::function<void()> & pressButtonFunctor, EShortcut shortcut)
|
||||
{
|
||||
changeModeButtons.emplace_back(std::make_shared<CButton>(buttonPos, picPath, buttonHelpContainer, pressButtonFunctor));
|
||||
changeModeButtons.emplace_back(std::make_shared<CButton>(buttonPos, picPath, buttonHelpContainer, pressButtonFunctor, shortcut));
|
||||
buttonPos -= Point(0, buttonHeightWithMargin);
|
||||
};
|
||||
|
||||
if(isButtonVisible(EMarketMode::RESOURCE_PLAYER))
|
||||
addButton(AnimationPath::builtin("TPMRKBU1.DEF"), CGI->generaltexth->zelp[612], std::bind(&CMarketWindow::createTransferResources, this, market, hero));
|
||||
addButton(AnimationPath::builtin("TPMRKBU1.DEF"), CGI->generaltexth->zelp[612], std::bind(&CMarketWindow::createTransferResources, this, market, hero), EShortcut::MARKET_RESOURCE_PLAYER);
|
||||
if(isButtonVisible(EMarketMode::ARTIFACT_RESOURCE))
|
||||
addButton(AnimationPath::builtin("TPMRKBU3.DEF"), CGI->generaltexth->zelp[613], std::bind(&CMarketWindow::createArtifactsSelling, this, market, hero));
|
||||
addButton(AnimationPath::builtin("TPMRKBU3.DEF"), CGI->generaltexth->zelp[613], std::bind(&CMarketWindow::createArtifactsSelling, this, market, hero), EShortcut::MARKET_ARTIFACT_RESOURCE);
|
||||
if(isButtonVisible(EMarketMode::RESOURCE_ARTIFACT))
|
||||
addButton(AnimationPath::builtin("TPMRKBU2.DEF"), CGI->generaltexth->zelp[598], std::bind(&CMarketWindow::createArtifactsBuying, this, market, hero));
|
||||
addButton(AnimationPath::builtin("TPMRKBU2.DEF"), CGI->generaltexth->zelp[598], std::bind(&CMarketWindow::createArtifactsBuying, this, market, hero), EShortcut::MARKET_RESOURCE_ARTIFACT);
|
||||
|
||||
buttonPos = Point(516, 520 - buttonHeightWithMargin);
|
||||
if(isButtonVisible(EMarketMode::CREATURE_RESOURCE))
|
||||
addButton(AnimationPath::builtin("TPMRKBU4.DEF"), CGI->generaltexth->zelp[599], std::bind(&CMarketWindow::createFreelancersGuild, this, market, hero));
|
||||
addButton(AnimationPath::builtin("TPMRKBU4.DEF"), CGI->generaltexth->zelp[599], std::bind(&CMarketWindow::createFreelancersGuild, this, market, hero), EShortcut::MARKET_CREATURE_RESOURCE);
|
||||
if(isButtonVisible(EMarketMode::RESOURCE_RESOURCE))
|
||||
addButton(AnimationPath::builtin("TPMRKBU5.DEF"), CGI->generaltexth->zelp[605], std::bind(&CMarketWindow::createMarketResources, this, market, hero));
|
||||
addButton(AnimationPath::builtin("TPMRKBU5.DEF"), CGI->generaltexth->zelp[605], std::bind(&CMarketWindow::createMarketResources, this, market, hero), EShortcut::MARKET_RESOURCE_RESOURCE);
|
||||
|
||||
buttonPos = Point(516, 421);
|
||||
if(isButtonVisible(EMarketMode::CREATURE_EXP))
|
||||
{
|
||||
addButton(AnimationPath::builtin("ALTSACC.DEF"), CGI->generaltexth->zelp[572], std::bind(&CMarketWindow::createAltarCreatures, this, market, hero));
|
||||
addButton(AnimationPath::builtin("ALTSACC.DEF"), CGI->generaltexth->zelp[572], std::bind(&CMarketWindow::createAltarCreatures, this, market, hero), EShortcut::MARKET_CREATURE_EXPERIENCE);
|
||||
if(marketWidget->hero->getAlignment() == EAlignment::GOOD)
|
||||
changeModeButtons.back()->block(true);
|
||||
}
|
||||
if(isButtonVisible(EMarketMode::ARTIFACT_EXP))
|
||||
{
|
||||
addButton(AnimationPath::builtin("ALTART.DEF"), CGI->generaltexth->zelp[580], std::bind(&CMarketWindow::createAltarArtifacts, this, market, hero));
|
||||
addButton(AnimationPath::builtin("ALTART.DEF"), CGI->generaltexth->zelp[580], std::bind(&CMarketWindow::createAltarArtifacts, this, market, hero), EShortcut::MARKET_ARTIFACT_EXPERIENCE);
|
||||
if(marketWidget->hero->getAlignment() == EAlignment::EVIL)
|
||||
changeModeButtons.back()->block(true);
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ CQuestLog::CQuestLog (const std::vector<QuestInfo> & Quests)
|
||||
minimap = std::make_shared<CQuestMinimap>(Rect(12, 12, 169, 169));
|
||||
// TextBox have it's own 4 pixel padding from top at least for English. To achieve 10px from both left and top only add 6px margin
|
||||
description = std::make_shared<CTextBox>("", Rect(205, 18, 385, DESCRIPTION_HEIGHT_MAX), CSlider::BROWN, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE);
|
||||
ok = std::make_shared<CButton>(Point(539, 398), AnimationPath::builtin("IOKAY.DEF"), CGI->generaltexth->zelp[445], std::bind(&CQuestLog::close, this), EShortcut::GLOBAL_ACCEPT);
|
||||
ok = std::make_shared<CButton>(Point(539, 398), AnimationPath::builtin("IOKAY.DEF"), CGI->generaltexth->zelp[445], std::bind(&CQuestLog::close, this), EShortcut::GLOBAL_RETURN);
|
||||
// Both button and lable are shifted to -2px by x and y to not make them actually look like they're on same line with quests list and ok button
|
||||
hideCompleteButton = std::make_shared<CToggleButton>(Point(10, 396), AnimationPath::builtin("sysopchk.def"), CButton::tooltipLocalized("vcmi.questLog.hideComplete"), std::bind(&CQuestLog::toggleComplete, this, _1));
|
||||
hideCompleteLabel = std::make_shared<CLabel>(46, 398, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->translate("vcmi.questLog.hideComplete.hover"));
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
#include "../eventsSDL/InputHandler.h"
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/CondSh.h"
|
||||
#include "../ConditionalWait.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../CGameInfo.h"
|
||||
@ -45,7 +45,7 @@ CTutorialWindow::CTutorialWindow(const TutorialMode & m)
|
||||
|
||||
labelTitle = std::make_shared<CLabel>(190, 15, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->translate("vcmi.tutorialWindow.title"));
|
||||
labelInformation = std::make_shared<CMultiLineLabel>(Rect(5, 40, 370, 60), EFonts::FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, "");
|
||||
buttonOk = std::make_shared<CButton>(Point(159, 367), AnimationPath::builtin("IOKAY"), CButton::tooltip(), std::bind(&CTutorialWindow::exit, this), EShortcut::GLOBAL_ACCEPT); //62x28
|
||||
buttonOk = std::make_shared<CButton>(Point(159, 367), AnimationPath::builtin("IOKAY"), CButton::tooltip(), std::bind(&CTutorialWindow::exit, this), EShortcut::GLOBAL_RETURN); //62x28
|
||||
buttonLeft = std::make_shared<CButton>(Point(5, 217), AnimationPath::builtin("HSBTNS3"), CButton::tooltip(), std::bind(&CTutorialWindow::previous, this), EShortcut::MOVE_LEFT); //22x46
|
||||
buttonRight = std::make_shared<CButton>(Point(352, 217), AnimationPath::builtin("HSBTNS5"), CButton::tooltip(), std::bind(&CTutorialWindow::next, this), EShortcut::MOVE_RIGHT); //22x46
|
||||
|
||||
@ -70,7 +70,7 @@ void CTutorialWindow::openWindowFirstTime(const TutorialMode & m)
|
||||
if(GH.input().hasTouchInputDevice() && !persistentStorage["gui"]["tutorialCompleted" + std::to_string(m)].Bool())
|
||||
{
|
||||
if(LOCPLINT)
|
||||
LOCPLINT->showingDialog->set(true);
|
||||
LOCPLINT->showingDialog->setBusy();
|
||||
GH.windows().pushWindow(std::make_shared<CTutorialWindow>(m));
|
||||
|
||||
Settings s = persistentStorage.write["gui"]["tutorialCompleted" + std::to_string(m)];
|
||||
@ -81,7 +81,7 @@ void CTutorialWindow::openWindowFirstTime(const TutorialMode & m)
|
||||
void CTutorialWindow::exit()
|
||||
{
|
||||
if(LOCPLINT)
|
||||
LOCPLINT->showingDialog->setn(false);
|
||||
LOCPLINT->showingDialog->setFree();
|
||||
|
||||
close();
|
||||
}
|
||||
|
@ -10,6 +10,11 @@
|
||||
#include "StdInc.h"
|
||||
#include "CWindowWithArtifacts.h"
|
||||
|
||||
#include "CHeroWindow.h"
|
||||
#include "CSpellWindow.h"
|
||||
#include "CExchangeWindow.h"
|
||||
#include "CHeroBackpackWindow.h"
|
||||
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/CursorHandler.h"
|
||||
#include "../gui/WindowHandler.h"
|
||||
@ -20,8 +25,6 @@
|
||||
|
||||
#include "../widgets/CComponent.h"
|
||||
|
||||
#include "../windows/CSpellWindow.h"
|
||||
#include "../windows/CHeroBackpackWindow.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../CGameInfo.h"
|
||||
|
||||
|
@ -45,7 +45,7 @@ void CreaturePurchaseCard::initMinButton()
|
||||
|
||||
void CreaturePurchaseCard::initCreatureSwitcherButton()
|
||||
{
|
||||
creatureSwitcher = std::make_shared<CButton>(Point(pos.x + 18, pos.y-37), AnimationPath::builtin("iDv6432.def"), CButton::tooltip(), [&](){ switchCreatureLevel(); });
|
||||
creatureSwitcher = std::make_shared<CButton>(Point(pos.x + 18, pos.y-37), AnimationPath::builtin("iDv6432.def"), CButton::tooltip(), [&](){ switchCreatureLevel(); }, EShortcut::RECRUITMENT_SWITCH_LEVEL);
|
||||
}
|
||||
|
||||
void CreaturePurchaseCard::switchCreatureLevel()
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user