mirror of
https://github.com/vcmi/vcmi.git
synced 2025-03-17 20:58:07 +02:00
Merge remote-tracking branch 'upstream/develop' into develop
This commit is contained in:
commit
aea05b3fb8
5
.github/workflows/github.yml
vendored
5
.github/workflows/github.yml
vendored
@ -183,6 +183,11 @@ jobs:
|
||||
distribution: 'temurin'
|
||||
java-version: '11'
|
||||
|
||||
# a hack to build ID for x64 build in order for Google Play to allow upload of both 32 and 64 bit builds
|
||||
- name: Bump Android x64 build ID
|
||||
if: ${{ matrix.platform == 'android-64' }}
|
||||
run: perl -i -pe 's/versionCode (\d+)/$x=$1+1; "versionCode $x"/e' android/vcmi-app/build.gradle
|
||||
|
||||
- name: Build Number
|
||||
run: |
|
||||
source '${{github.workspace}}/CI/get_package_name.sh'
|
||||
|
@ -921,7 +921,7 @@ std::vector<const battle::Unit *> BattleExchangeEvaluator::getOneTurnReachableUn
|
||||
|
||||
ReachabilityInfo unitReachability = reachabilityIter != reachabilityCache.end() ? reachabilityIter->second : turnBattle.getReachability(unit);
|
||||
|
||||
bool reachable = unitReachability.distances[hex] <= radius;
|
||||
bool reachable = unitReachability.distances.at(hex) <= radius;
|
||||
|
||||
if(!reachable && unitReachability.accessibility[hex] == EAccessibility::ALIVE_STACK)
|
||||
{
|
||||
@ -931,7 +931,7 @@ std::vector<const battle::Unit *> BattleExchangeEvaluator::getOneTurnReachableUn
|
||||
{
|
||||
for(BattleHex neighbor : hex.neighbouringTiles())
|
||||
{
|
||||
reachable = unitReachability.distances[neighbor] <= radius;
|
||||
reachable = unitReachability.distances.at(neighbor) <= radius;
|
||||
|
||||
if(reachable) break;
|
||||
}
|
||||
@ -981,7 +981,7 @@ bool BattleExchangeEvaluator::checkPositionBlocksOurStacks(HypotheticBattle & hb
|
||||
for(BattleHex hex = BattleHex::TOP_LEFT; hex.isValid(); hex = hex + 1)
|
||||
{
|
||||
bool enemyUnit = false;
|
||||
bool reachable = unitReachability.distances[hex] <= unitSpeed;
|
||||
bool reachable = unitReachability.distances.at(hex) <= unitSpeed;
|
||||
|
||||
if(!reachable && unitReachability.accessibility[hex] == EAccessibility::ALIVE_STACK)
|
||||
{
|
||||
@ -993,7 +993,7 @@ bool BattleExchangeEvaluator::checkPositionBlocksOurStacks(HypotheticBattle & hb
|
||||
|
||||
for(BattleHex neighbor : hex.neighbouringTiles())
|
||||
{
|
||||
reachable = unitReachability.distances[neighbor] <= unitSpeed;
|
||||
reachable = unitReachability.distances.at(neighbor) <= unitSpeed;
|
||||
|
||||
if(reachable) break;
|
||||
}
|
||||
|
@ -132,10 +132,10 @@ SlotID StackWithBonuses::unitSlot() const
|
||||
}
|
||||
|
||||
TConstBonusListPtr StackWithBonuses::getAllBonuses(const CSelector & selector, const CSelector & limit,
|
||||
const CBonusSystemNode * root, const std::string & cachingStr) const
|
||||
const std::string & cachingStr) const
|
||||
{
|
||||
auto ret = std::make_shared<BonusList>();
|
||||
TConstBonusListPtr originalList = origBearer->getAllBonuses(selector, limit, root, cachingStr);
|
||||
TConstBonusListPtr originalList = origBearer->getAllBonuses(selector, limit, cachingStr);
|
||||
|
||||
vstd::copy_if(*originalList, std::back_inserter(*ret), [this](const std::shared_ptr<Bonus> & b)
|
||||
{
|
||||
|
@ -91,7 +91,7 @@ public:
|
||||
|
||||
///IBonusBearer
|
||||
TConstBonusListPtr getAllBonuses(const CSelector & selector, const CSelector & limit,
|
||||
const CBonusSystemNode * root = nullptr, const std::string & cachingStr = "") const override;
|
||||
const std::string & cachingStr = "") const override;
|
||||
|
||||
int64_t getTreeVersion() const override;
|
||||
|
||||
|
@ -21,8 +21,6 @@
|
||||
#include "../../lib/GameSettings.h"
|
||||
#include "../../lib/gameState/CGameState.h"
|
||||
#include "../../lib/serializer/CTypeList.h"
|
||||
#include "../../lib/serializer/BinarySerializer.h"
|
||||
#include "../../lib/serializer/BinaryDeserializer.h"
|
||||
#include "../../lib/networkPacks/PacksForClient.h"
|
||||
#include "../../lib/networkPacks/PacksForClientBattle.h"
|
||||
#include "../../lib/networkPacks/PacksForServer.h"
|
||||
@ -1491,7 +1489,7 @@ void AIGateway::tryRealize(Goals::Trade & g) //trade
|
||||
//TODO trade only as much as needed
|
||||
if (toGive) //don't try to sell 0 resources
|
||||
{
|
||||
cb->trade(m, EMarketMode::RESOURCE_RESOURCE, res, GameResID(g.resID), toGive);
|
||||
cb->trade(m->getObjInstanceID(), EMarketMode::RESOURCE_RESOURCE, res, GameResID(g.resID), toGive);
|
||||
acquiredResources = static_cast<int>(toGet * (it->resVal / toGive));
|
||||
logAi->debug("Traded %d of %s for %d of %s at %s", toGive, res, acquiredResources, g.resID, obj->getObjectName());
|
||||
}
|
||||
|
@ -334,9 +334,7 @@ void BuildAnalyzer::updateDailyIncome()
|
||||
const CGMine* mine = dynamic_cast<const CGMine*>(obj);
|
||||
|
||||
if(mine)
|
||||
{
|
||||
dailyIncome[mine->producedResource.getNum()] += mine->getProducedQuantity();
|
||||
}
|
||||
dailyIncome += mine->dailyIncome();
|
||||
}
|
||||
|
||||
for(const CGTownInstance* town : towns)
|
||||
|
@ -196,7 +196,7 @@ bool BuildingManager::getBuildingOptions(const CGTownInstance * t)
|
||||
return true;
|
||||
|
||||
//workaround for mantis #2696 - build capitol with separate algorithm if it is available
|
||||
if(vstd::contains(t->builtBuildings, BuildingID::CITY_HALL) && getMaxPossibleGoldBuilding(t) == BuildingID::CAPITOL)
|
||||
if(t->hasBuilt(BuildingID::CITY_HALL) && getMaxPossibleGoldBuilding(t) == BuildingID::CAPITOL)
|
||||
{
|
||||
if(tryBuildNextStructure(t, capitolAndRequirements))
|
||||
return true;
|
||||
|
@ -31,8 +31,6 @@
|
||||
#include "../../lib/networkPacks/PacksForClientBattle.h"
|
||||
#include "../../lib/networkPacks/PacksForServer.h"
|
||||
#include "../../lib/serializer/CTypeList.h"
|
||||
#include "../../lib/serializer/BinarySerializer.h"
|
||||
#include "../../lib/serializer/BinaryDeserializer.h"
|
||||
|
||||
#include "AIhelper.h"
|
||||
|
||||
@ -2130,7 +2128,7 @@ void VCAI::tryRealize(Goals::Trade & g) //trade
|
||||
//TODO trade only as much as needed
|
||||
if (toGive) //don't try to sell 0 resources
|
||||
{
|
||||
cb->trade(m, EMarketMode::RESOURCE_RESOURCE, res, GameResID(g.resID), toGive);
|
||||
cb->trade(m->getObjInstanceID(), EMarketMode::RESOURCE_RESOURCE, res, GameResID(g.resID), toGive);
|
||||
acquiredResources = static_cast<int>(toGet * (it->resVal / toGive));
|
||||
logAi->debug("Traded %d of %s for %d of %s at %s", toGive, res, acquiredResources, g.resID, obj->getObjectName());
|
||||
}
|
||||
|
@ -266,15 +266,15 @@ void CCallback::buyArtifact(const CGHeroInstance *hero, ArtifactID aid)
|
||||
sendRequest(&pack);
|
||||
}
|
||||
|
||||
void CCallback::trade(const IMarket * market, EMarketMode mode, TradeItemSell id1, TradeItemBuy id2, ui32 val1, const CGHeroInstance * hero)
|
||||
void CCallback::trade(const ObjectInstanceID marketId, EMarketMode mode, TradeItemSell id1, TradeItemBuy id2, ui32 val1, const CGHeroInstance * hero)
|
||||
{
|
||||
trade(market, mode, std::vector(1, id1), std::vector(1, id2), std::vector(1, val1), hero);
|
||||
trade(marketId, mode, std::vector(1, id1), std::vector(1, id2), std::vector(1, val1), hero);
|
||||
}
|
||||
|
||||
void CCallback::trade(const IMarket * market, EMarketMode mode, const std::vector<TradeItemSell> & id1, const std::vector<TradeItemBuy> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero)
|
||||
void CCallback::trade(const ObjectInstanceID marketId, EMarketMode mode, const std::vector<TradeItemSell> & id1, const std::vector<TradeItemBuy> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero)
|
||||
{
|
||||
TradeOnMarketplace pack;
|
||||
pack.marketId = dynamic_cast<const CGObjectInstance *>(market)->id;
|
||||
pack.marketId = marketId;
|
||||
pack.heroId = hero ? hero->id : ObjectInstanceID();
|
||||
pack.mode = mode;
|
||||
pack.r1 = id1;
|
||||
|
@ -34,7 +34,6 @@ class IBattleEventsReceiver;
|
||||
class IGameEventsReceiver;
|
||||
struct ArtifactLocation;
|
||||
class BattleStateInfoForRetreat;
|
||||
class IMarket;
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
@ -81,8 +80,8 @@ public:
|
||||
virtual bool upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID=CreatureID::NONE)=0; //if newID==-1 then best possible upgrade will be made
|
||||
virtual void swapGarrisonHero(const CGTownInstance *town)=0;
|
||||
|
||||
virtual void trade(const IMarket * market, EMarketMode mode, TradeItemSell id1, TradeItemBuy id2, ui32 val1, const CGHeroInstance * hero = nullptr)=0; //mode==0: sell val1 units of id1 resource for id2 resiurce
|
||||
virtual void trade(const IMarket * market, EMarketMode mode, const std::vector<TradeItemSell> & id1, const std::vector<TradeItemBuy> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero = nullptr)=0;
|
||||
virtual void trade(const ObjectInstanceID marketId, EMarketMode mode, TradeItemSell id1, TradeItemBuy id2, ui32 val1, const CGHeroInstance * hero)=0; //mode==0: sell val1 units of id1 resource for id2 resiurce
|
||||
virtual void trade(const ObjectInstanceID marketId, EMarketMode mode, const std::vector<TradeItemSell> & id1, const std::vector<TradeItemBuy> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero)=0;
|
||||
|
||||
virtual int selectionMade(int selection, QueryID queryID) =0;
|
||||
virtual int sendQueryReply(std::optional<int32_t> reply, QueryID queryID) =0;
|
||||
@ -190,8 +189,8 @@ public:
|
||||
void endTurn() override;
|
||||
void swapGarrisonHero(const CGTownInstance *town) override;
|
||||
void buyArtifact(const CGHeroInstance *hero, ArtifactID aid) override;
|
||||
void trade(const IMarket * market, EMarketMode mode, TradeItemSell id1, TradeItemBuy id2, ui32 val1, const CGHeroInstance * hero = nullptr) override;
|
||||
void trade(const IMarket * market, EMarketMode mode, const std::vector<TradeItemSell> & id1, const std::vector<TradeItemBuy> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero = nullptr) override;
|
||||
void trade(const ObjectInstanceID marketId, EMarketMode mode, TradeItemSell id1, TradeItemBuy id2, ui32 val1, const CGHeroInstance * hero = nullptr) override;
|
||||
void trade(const ObjectInstanceID marketId, EMarketMode mode, const std::vector<TradeItemSell> & id1, const std::vector<TradeItemBuy> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero = nullptr) override;
|
||||
void setFormation(const CGHeroInstance * hero, EArmyFormation mode) override;
|
||||
void recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero, const HeroTypeID & nextHero=HeroTypeID::NONE) override;
|
||||
void save(const std::string &fname) override;
|
||||
|
@ -403,7 +403,10 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR NOT WIN32)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT WIN32)
|
||||
# For gcc 14+ we can use -fhardened instead
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_GLIBCXX_ASSERTIONS -fstack-protector-strong -fstack-clash-protection -fcf-protection=full")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_GLIBCXX_ASSERTIONS -fstack-protector-strong -fstack-clash-protection")
|
||||
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcf-protection=full")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
12
ChangeLog.md
12
ChangeLog.md
@ -70,6 +70,18 @@
|
||||
* Added support for HotA bank building from Factory
|
||||
* Added support for HotA-style 8th creature in town
|
||||
|
||||
# 1.5.6 -> 1.5.7
|
||||
|
||||
* Fixed game freeze if player is attacked in online multiplayer game by another player when he has unread dialogs, such as new week notification
|
||||
* Fixed possible game crash after being attacked by enemy with artifact that blocks spellcasting
|
||||
* Fixed heroes on map limit game setting not respected when moving hero from town garrison.
|
||||
* Add workaround to fix possible crash on attempt to start previously generated random map that has players without owned heroes or towns
|
||||
* Fixed crash on right-clicking spell icon when receiving unlearnable spells from Pandora
|
||||
* Fixed possible text overflow in match information box in online lobby
|
||||
* Fixed overlapping text in lobby login window
|
||||
* Fixed excessive removal of open dialogs such as new week or map events on new turn
|
||||
* Fixed objects like Mystical Gardens not resetting their state on new week correctly
|
||||
|
||||
# 1.5.5 -> 1.5.6
|
||||
|
||||
### Stability
|
||||
|
@ -660,5 +660,7 @@
|
||||
"core.bonus.WATER_IMMUNITY.name": "Water immunity",
|
||||
"core.bonus.WATER_IMMUNITY.description": "Immune to all spells from the school of Water magic",
|
||||
"core.bonus.WIDE_BREATH.name": "Wide breath",
|
||||
"core.bonus.WIDE_BREATH.description": "Wide breath attack (multiple hexes)"
|
||||
"core.bonus.WIDE_BREATH.description": "Wide breath attack (multiple hexes)",
|
||||
"core.bonus.DISINTEGRATE.name": "Disintegrate",
|
||||
"core.bonus.DISINTEGRATE.description": "No corpse remains after death"
|
||||
}
|
||||
|
@ -163,7 +163,7 @@
|
||||
"vcmi.systemOptions.townsGroup" : "Stadt-Bildschirm",
|
||||
|
||||
"vcmi.statisticWindow.statistics" : "Statistik",
|
||||
"vcmi.statisticWindow.tsvCopy" : "Daten in Zwischenabl.",
|
||||
"vcmi.statisticWindow.tsvCopy" : "In Zwischenablage",
|
||||
"vcmi.statisticWindow.selectView" : "Ansicht wählen",
|
||||
"vcmi.statisticWindow.value" : "Wert",
|
||||
"vcmi.statisticWindow.title.overview" : "Überblick",
|
||||
|
@ -100,9 +100,8 @@
|
||||
|
||||
#include "../lib/pathfinder/CGPathNode.h"
|
||||
|
||||
#include "../lib/serializer/BinaryDeserializer.h"
|
||||
#include "../lib/serializer/BinarySerializer.h"
|
||||
#include "../lib/serializer/CTypeList.h"
|
||||
#include "../lib/serializer/ESerializationVersion.h"
|
||||
|
||||
#include "../lib/spells/CSpellHandler.h"
|
||||
|
||||
@ -203,6 +202,11 @@ void CPlayerInterface::playerEndsTurn(PlayerColor player)
|
||||
{
|
||||
makingTurn = false;
|
||||
closeAllDialogs();
|
||||
|
||||
// remove all pending dialogs that do not expect query answer
|
||||
vstd::erase_if(dialogs, [](const std::shared_ptr<CInfoWindow> & window){
|
||||
return window->ID == QueryID::NONE;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -619,9 +623,7 @@ void CPlayerInterface::battleStartBefore(const BattleID & battleID, const CCreat
|
||||
{
|
||||
movementController->onBattleStarted();
|
||||
|
||||
//Don't wait for dialogs when we are non-active hot-seat player
|
||||
if (LOCPLINT == this)
|
||||
waitForAllDialogs();
|
||||
waitForAllDialogs();
|
||||
}
|
||||
|
||||
void CPlayerInterface::battleStart(const BattleID & battleID, const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, BattleSide side, bool replayAllowed)
|
||||
@ -644,9 +646,7 @@ void CPlayerInterface::battleStart(const BattleID & battleID, const CCreatureSet
|
||||
cb->registerBattleInterface(autofightingAI);
|
||||
}
|
||||
|
||||
//Don't wait for dialogs when we are non-active hot-seat player
|
||||
if (LOCPLINT == this)
|
||||
waitForAllDialogs();
|
||||
waitForAllDialogs();
|
||||
|
||||
BATTLE_EVENT_POSSIBLE_RETURN;
|
||||
}
|
||||
@ -1632,14 +1632,14 @@ void CPlayerInterface::battleNewRoundFirst(const BattleID & battleID)
|
||||
battleInt->newRoundFirst();
|
||||
}
|
||||
|
||||
void CPlayerInterface::showMarketWindow(const IMarket *market, const CGHeroInstance *visitor, QueryID queryID)
|
||||
void CPlayerInterface::showMarketWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID)
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
auto onWindowClosed = [this, queryID](){
|
||||
cb->selectionMade(0, queryID);
|
||||
};
|
||||
|
||||
if (market->allowsTrade(EMarketMode::ARTIFACT_EXP) && dynamic_cast<const CGArtifactsAltar*>(market) == nullptr)
|
||||
if (market->allowsTrade(EMarketMode::ARTIFACT_EXP) && market->getArtifactsStorage() == nullptr)
|
||||
{
|
||||
// compatibility check, safe to remove for 1.6
|
||||
// 1.4 saves loaded in 1.5 will not be able to visit Altar of Sacrifice due to Altar now requiring different map object class
|
||||
@ -1654,8 +1654,17 @@ void CPlayerInterface::showMarketWindow(const IMarket *market, const CGHeroInsta
|
||||
GH.windows().createAndPushWindow<CMarketWindow>(market, visitor, onWindowClosed, EMarketMode::CREATURE_EXP);
|
||||
else if(market->allowsTrade(EMarketMode::CREATURE_UNDEAD))
|
||||
GH.windows().createAndPushWindow<CTransformerWindow>(market, visitor, onWindowClosed);
|
||||
else if(!market->availableModes().empty())
|
||||
GH.windows().createAndPushWindow<CMarketWindow>(market, visitor, onWindowClosed, market->availableModes().front());
|
||||
else if (!market->availableModes().empty())
|
||||
for(auto mode = EMarketMode::RESOURCE_RESOURCE; mode != EMarketMode::MARKET_AFTER_LAST_PLACEHOLDER; mode = vstd::next(mode, 1))
|
||||
{
|
||||
if(vstd::contains(market->availableModes(), mode))
|
||||
{
|
||||
GH.windows().createAndPushWindow<CMarketWindow>(market, visitor, onWindowClosed, mode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
onWindowClosed();
|
||||
}
|
||||
|
||||
void CPlayerInterface::showUniversityWindow(const IMarket *market, const CGHeroInstance *visitor, QueryID queryID)
|
||||
@ -1664,7 +1673,7 @@ void CPlayerInterface::showUniversityWindow(const IMarket *market, const CGHeroI
|
||||
auto onWindowClosed = [this, queryID](){
|
||||
cb->selectionMade(0, queryID);
|
||||
};
|
||||
GH.windows().createAndPushWindow<CUniversityWindow>(visitor, market, onWindowClosed);
|
||||
GH.windows().createAndPushWindow<CUniversityWindow>(visitor, BuildingID::NONE, market, onWindowClosed);
|
||||
}
|
||||
|
||||
void CPlayerInterface::showHillFortWindow(const CGObjectInstance *object, const CGHeroInstance *visitor)
|
||||
@ -1760,6 +1769,9 @@ void CPlayerInterface::artifactDisassembled(const ArtifactLocation &al)
|
||||
|
||||
void CPlayerInterface::waitForAllDialogs()
|
||||
{
|
||||
if (!makingTurn)
|
||||
return;
|
||||
|
||||
while(!dialogs.empty())
|
||||
{
|
||||
auto unlockInterface = vstd::makeUnlockGuard(GH.interfaceMutex);
|
||||
|
@ -120,7 +120,7 @@ protected: // Call-ins from server, should not be called directly, but only via
|
||||
void showTeleportDialog(const CGHeroInstance * hero, TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override;
|
||||
void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) override;
|
||||
void showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector<ObjectInstanceID> & objects) override;
|
||||
void showMarketWindow(const IMarket *market, const CGHeroInstance *visitor, QueryID queryID) override;
|
||||
void showMarketWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID) override;
|
||||
void showUniversityWindow(const IMarket *market, const CGHeroInstance *visitor, QueryID queryID) override;
|
||||
void showHillFortWindow(const CGObjectInstance *object, const CGHeroInstance *visitor) override;
|
||||
void advmapSpellCast(const CGHeroInstance * caster, SpellID spellID) override; //called when a hero casts a spell
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "../lib/TurnTimerInfo.h"
|
||||
#include "../lib/VCMIDirs.h"
|
||||
#include "../lib/campaign/CampaignState.h"
|
||||
#include "../lib/gameState/CGameState.h"
|
||||
#include "../lib/gameState/HighScore.h"
|
||||
#include "../lib/CPlayerState.h"
|
||||
#include "../lib/mapping/CMapInfo.h"
|
||||
@ -44,7 +45,6 @@
|
||||
#include "../lib/rmg/CMapGenOptions.h"
|
||||
#include "../lib/serializer/Connection.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/registerTypes/RegisterTypesLobbyPacks.h"
|
||||
#include "../lib/serializer/CMemorySerializer.h"
|
||||
#include "../lib/UnlockGuard.h"
|
||||
|
||||
@ -56,61 +56,6 @@
|
||||
|
||||
#include <vcmi/events/EventBus.h>
|
||||
|
||||
template<typename T> class CApplyOnLobby;
|
||||
|
||||
class CBaseForLobbyApply
|
||||
{
|
||||
public:
|
||||
virtual bool applyOnLobbyHandler(CServerHandler * handler, CPackForLobby & pack) const = 0;
|
||||
virtual void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler, CPackForLobby & pack) const = 0;
|
||||
virtual ~CBaseForLobbyApply(){};
|
||||
template<typename U> static CBaseForLobbyApply * getApplier(const U * t = nullptr)
|
||||
{
|
||||
return new CApplyOnLobby<U>();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> class CApplyOnLobby : public CBaseForLobbyApply
|
||||
{
|
||||
public:
|
||||
bool applyOnLobbyHandler(CServerHandler * handler, CPackForLobby & pack) const override
|
||||
{
|
||||
auto & ref = static_cast<T&>(pack);
|
||||
ApplyOnLobbyHandlerNetPackVisitor visitor(*handler);
|
||||
|
||||
logNetwork->trace("\tImmediately apply on lobby: %s", typeid(ref).name());
|
||||
ref.visit(visitor);
|
||||
|
||||
return visitor.getResult();
|
||||
}
|
||||
|
||||
void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler, CPackForLobby & pack) const override
|
||||
{
|
||||
auto & ref = static_cast<T &>(pack);
|
||||
ApplyOnLobbyScreenNetPackVisitor visitor(*handler, lobby);
|
||||
|
||||
logNetwork->trace("\tApply on lobby from queue: %s", typeid(ref).name());
|
||||
ref.visit(visitor);
|
||||
}
|
||||
};
|
||||
|
||||
template<> class CApplyOnLobby<CPack>: public CBaseForLobbyApply
|
||||
{
|
||||
public:
|
||||
bool applyOnLobbyHandler(CServerHandler * handler, CPackForLobby & pack) const override
|
||||
{
|
||||
logGlobal->error("Cannot apply plain CPack!");
|
||||
assert(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
void applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler, CPackForLobby & pack) const override
|
||||
{
|
||||
logGlobal->error("Cannot apply plain CPack!");
|
||||
assert(0);
|
||||
}
|
||||
};
|
||||
|
||||
CServerHandler::~CServerHandler()
|
||||
{
|
||||
if (serverRunner)
|
||||
@ -148,7 +93,6 @@ CServerHandler::CServerHandler()
|
||||
: networkHandler(INetworkHandler::createHandler())
|
||||
, lobbyClient(std::make_unique<GlobalLobbyClient>())
|
||||
, gameChat(std::make_unique<GameChatHandler>())
|
||||
, applier(std::make_unique<CApplier<CBaseForLobbyApply>>())
|
||||
, threadNetwork(&CServerHandler::threadRunNetwork, this)
|
||||
, state(EClientState::NONE)
|
||||
, serverPort(0)
|
||||
@ -159,7 +103,6 @@ CServerHandler::CServerHandler()
|
||||
, client(nullptr)
|
||||
{
|
||||
uuid = boost::uuids::to_string(boost::uuids::random_generator()());
|
||||
registerTypesLobbyPacks(*applier);
|
||||
}
|
||||
|
||||
void CServerHandler::threadRunNetwork()
|
||||
@ -320,8 +263,8 @@ void CServerHandler::onConnectionEstablished(const NetworkConnectionPtr & netCon
|
||||
|
||||
void CServerHandler::applyPackOnLobbyScreen(CPackForLobby & pack)
|
||||
{
|
||||
const CBaseForLobbyApply * apply = applier->getApplier(CTypeList::getInstance().getTypeID(&pack)); //find the applier
|
||||
apply->applyOnLobbyScreen(dynamic_cast<CLobbyScreen *>(SEL), this, pack);
|
||||
ApplyOnLobbyScreenNetPackVisitor visitor(*this, dynamic_cast<CLobbyScreen *>(SEL));
|
||||
pack.visit(visitor);
|
||||
GH.windows().totalRedraw();
|
||||
}
|
||||
|
||||
@ -960,7 +903,10 @@ void CServerHandler::waitForServerShutdown()
|
||||
|
||||
void CServerHandler::visitForLobby(CPackForLobby & lobbyPack)
|
||||
{
|
||||
if(applier->getApplier(CTypeList::getInstance().getTypeID(&lobbyPack))->applyOnLobbyHandler(this, lobbyPack))
|
||||
ApplyOnLobbyHandlerNetPackVisitor visitor(*this);
|
||||
lobbyPack.visit(visitor);
|
||||
|
||||
if(visitor.getResult())
|
||||
{
|
||||
if(!settings["session"]["headless"].Bool())
|
||||
applyPackOnLobbyScreen(lobbyPack);
|
||||
|
@ -31,8 +31,6 @@ struct CPackForClient;
|
||||
|
||||
class HighScoreParameter;
|
||||
|
||||
template<typename T> class CApplier;
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
class CClient;
|
||||
@ -102,7 +100,6 @@ class CServerHandler final : public IServerAPI, public LobbyInfo, public INetwor
|
||||
std::shared_ptr<INetworkConnection> networkConnection;
|
||||
std::unique_ptr<GlobalLobbyClient> lobbyClient;
|
||||
std::unique_ptr<GameChatHandler> gameChat;
|
||||
std::unique_ptr<CApplier<CBaseForLobbyApply>> applier;
|
||||
std::unique_ptr<IServerRunner> serverRunner;
|
||||
std::shared_ptr<CMapInfo> mapToStart;
|
||||
std::vector<std::string> localPlayerNames;
|
||||
|
@ -29,13 +29,10 @@
|
||||
#include "../lib/VCMIDirs.h"
|
||||
#include "../lib/UnlockGuard.h"
|
||||
#include "../lib/battle/BattleInfo.h"
|
||||
#include "../lib/serializer/BinaryDeserializer.h"
|
||||
#include "../lib/serializer/BinarySerializer.h"
|
||||
#include "../lib/serializer/Connection.h"
|
||||
#include "../lib/mapping/CMapService.h"
|
||||
#include "../lib/pathfinder/CGPathNode.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/registerTypes/RegisterTypesClientPacks.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vcmi/events/EventBus.h>
|
||||
@ -50,53 +47,6 @@
|
||||
|
||||
ThreadSafeVector<int> CClient::waitingRequest;
|
||||
|
||||
template<typename T> class CApplyOnCL;
|
||||
|
||||
class CBaseForCLApply
|
||||
{
|
||||
public:
|
||||
virtual void applyOnClAfter(CClient * cl, CPack * pack) const =0;
|
||||
virtual void applyOnClBefore(CClient * cl, CPack * pack) const =0;
|
||||
virtual ~CBaseForCLApply(){}
|
||||
|
||||
template<typename U> static CBaseForCLApply * getApplier(const U * t = nullptr)
|
||||
{
|
||||
return new CApplyOnCL<U>();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> class CApplyOnCL : public CBaseForCLApply
|
||||
{
|
||||
public:
|
||||
void applyOnClAfter(CClient * cl, CPack * pack) const override
|
||||
{
|
||||
T * ptr = static_cast<T *>(pack);
|
||||
ApplyClientNetPackVisitor visitor(*cl, *cl->gameState());
|
||||
ptr->visit(visitor);
|
||||
}
|
||||
void applyOnClBefore(CClient * cl, CPack * pack) const override
|
||||
{
|
||||
T * ptr = static_cast<T *>(pack);
|
||||
ApplyFirstClientNetPackVisitor visitor(*cl, *cl->gameState());
|
||||
ptr->visit(visitor);
|
||||
}
|
||||
};
|
||||
|
||||
template<> class CApplyOnCL<CPack>: public CBaseForCLApply
|
||||
{
|
||||
public:
|
||||
void applyOnClAfter(CClient * cl, CPack * pack) const override
|
||||
{
|
||||
logGlobal->error("Cannot apply on CL plain CPack!");
|
||||
assert(0);
|
||||
}
|
||||
void applyOnClBefore(CClient * cl, CPack * pack) const override
|
||||
{
|
||||
logGlobal->error("Cannot apply on CL plain CPack!");
|
||||
assert(0);
|
||||
}
|
||||
};
|
||||
|
||||
CPlayerEnvironment::CPlayerEnvironment(PlayerColor player_, CClient * cl_, std::shared_ptr<CCallback> mainCallback_)
|
||||
: player(player_),
|
||||
cl(cl_),
|
||||
@ -130,12 +80,9 @@ const CPlayerEnvironment::GameCb * CPlayerEnvironment::game() const
|
||||
return mainCallback.get();
|
||||
}
|
||||
|
||||
|
||||
CClient::CClient()
|
||||
{
|
||||
waitingRequest.clear();
|
||||
applier = std::make_shared<CApplier<CBaseForCLApply>>();
|
||||
registerTypesClientPacks(*applier);
|
||||
gs = nullptr;
|
||||
}
|
||||
|
||||
@ -400,25 +347,21 @@ void CClient::installNewBattleInterface(std::shared_ptr<CBattleGameInterface> ba
|
||||
}
|
||||
}
|
||||
|
||||
void CClient::handlePack(CPack * pack)
|
||||
void CClient::handlePack(CPackForClient * pack)
|
||||
{
|
||||
CBaseForCLApply * apply = applier->getApplier(CTypeList::getInstance().getTypeID(pack)); //find the applier
|
||||
if(apply)
|
||||
ApplyClientNetPackVisitor afterVisitor(*this, *gameState());
|
||||
ApplyFirstClientNetPackVisitor beforeVisitor(*this, *gameState());
|
||||
|
||||
pack->visit(beforeVisitor);
|
||||
logNetwork->trace("\tMade first apply on cl: %s", typeid(*pack).name());
|
||||
{
|
||||
apply->applyOnClBefore(this, pack);
|
||||
logNetwork->trace("\tMade first apply on cl: %s", typeid(*pack).name());
|
||||
{
|
||||
boost::unique_lock lock(CGameState::mutex);
|
||||
gs->apply(pack);
|
||||
}
|
||||
logNetwork->trace("\tApplied on gs: %s", typeid(*pack).name());
|
||||
apply->applyOnClAfter(this, pack);
|
||||
logNetwork->trace("\tMade second apply on cl: %s", typeid(*pack).name());
|
||||
}
|
||||
else
|
||||
{
|
||||
logNetwork->error("Message %s cannot be applied, cannot find applier!", typeid(*pack).name());
|
||||
boost::unique_lock lock(CGameState::mutex);
|
||||
gs->apply(pack);
|
||||
}
|
||||
logNetwork->trace("\tApplied on gs: %s", typeid(*pack).name());
|
||||
pack->visit(afterVisitor);
|
||||
logNetwork->trace("\tMade second apply on cl: %s", typeid(*pack).name());
|
||||
|
||||
delete pack;
|
||||
}
|
||||
|
||||
|
@ -21,14 +21,10 @@ struct CPackForServer;
|
||||
class IBattleEventsReceiver;
|
||||
class CBattleGameInterface;
|
||||
class CGameInterface;
|
||||
class BinaryDeserializer;
|
||||
class BinarySerializer;
|
||||
class BattleAction;
|
||||
class BattleInfo;
|
||||
struct BankConfig;
|
||||
|
||||
template<typename T> class CApplier;
|
||||
|
||||
#if SCRIPTING_ENABLED
|
||||
namespace scripting
|
||||
{
|
||||
@ -147,7 +143,7 @@ public:
|
||||
|
||||
static ThreadSafeVector<int> waitingRequest; //FIXME: make this normal field (need to join all threads before client destruction)
|
||||
|
||||
void handlePack(CPack * pack); //applies the given pack and deletes it
|
||||
void handlePack(CPackForClient * pack); //applies the given pack and deletes it
|
||||
int sendRequest(const CPackForServer * request, PlayerColor player); //returns ID given to that request
|
||||
|
||||
void battleStarted(const BattleInfo * info);
|
||||
@ -211,7 +207,7 @@ public:
|
||||
void castSpell(const spells::Caster * caster, SpellID spellID, const int3 &pos) override {};
|
||||
|
||||
void changeFogOfWar(int3 center, ui32 radius, PlayerColor player, ETileVisibility mode) override {}
|
||||
void changeFogOfWar(std::unordered_set<int3> & tiles, PlayerColor player, ETileVisibility mode) override {}
|
||||
void changeFogOfWar(const std::unordered_set<int3> & tiles, PlayerColor player, ETileVisibility mode) override {}
|
||||
|
||||
void setObjPropertyValue(ObjectInstanceID objid, ObjProperty prop, int32_t value) override {};
|
||||
void setObjPropertyID(ObjectInstanceID objid, ObjProperty prop, ObjPropertyID identifier) override {};
|
||||
@ -237,8 +233,6 @@ private:
|
||||
#endif
|
||||
std::unique_ptr<events::EventBus> clientEventBus;
|
||||
|
||||
std::shared_ptr<CApplier<CBaseForCLApply>> applier;
|
||||
|
||||
mutable boost::mutex pathCacheMutex;
|
||||
std::map<const CGHeroInstance *, std::shared_ptr<CPathsInfo>> pathCache;
|
||||
|
||||
|
@ -29,7 +29,6 @@
|
||||
#include "../CCallback.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/filesystem/FileInfo.h"
|
||||
#include "../lib/serializer/BinarySerializer.h"
|
||||
#include "../lib/serializer/Connection.h"
|
||||
#include "../lib/texts/CGeneralTextHandler.h"
|
||||
#include "../lib/CHeroHandler.h"
|
||||
@ -362,6 +361,14 @@ void ApplyClientNetPackVisitor::visitHeroVisit(HeroVisit & pack)
|
||||
void ApplyClientNetPackVisitor::visitNewTurn(NewTurn & pack)
|
||||
{
|
||||
cl.invalidatePaths();
|
||||
|
||||
if (pack.newWeekNotification)
|
||||
{
|
||||
const auto & newWeek = *pack.newWeekNotification;
|
||||
|
||||
std::string str = newWeek.text.toString();
|
||||
callAllInterfaces(cl, &CGameInterface::showInfoDialog, newWeek.type, str, newWeek.components,(soundBase::soundID)newWeek.soundID);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyClientNetPackVisitor::visitGiveBonus(GiveBonus & pack)
|
||||
@ -999,7 +1006,7 @@ void ApplyClientNetPackVisitor::visitOpenWindow(OpenWindow & pack)
|
||||
case EOpenWindowMode::UNIVERSITY_WINDOW:
|
||||
{
|
||||
//displays University window (when hero enters University on adventure map)
|
||||
const auto * market = dynamic_cast<const IMarket*>(cl.getObj(ObjectInstanceID(pack.object)));
|
||||
const auto * market = cl.getMarket(ObjectInstanceID(pack.object));
|
||||
const CGHeroInstance *hero = cl.getHero(ObjectInstanceID(pack.visitor));
|
||||
callInterfaceIfPresent(cl, hero->tempOwner, &IGameEventsReceiver::showUniversityWindow, market, hero, pack.queryID);
|
||||
}
|
||||
@ -1009,7 +1016,7 @@ void ApplyClientNetPackVisitor::visitOpenWindow(OpenWindow & pack)
|
||||
//displays Thieves' Guild window (when hero enters Den of Thieves)
|
||||
const CGObjectInstance *obj = cl.getObj(ObjectInstanceID(pack.object));
|
||||
const CGHeroInstance *hero = cl.getHero(ObjectInstanceID(pack.visitor));
|
||||
const auto *market = dynamic_cast<const IMarket*>(obj);
|
||||
const auto market = cl.getMarket(pack.object);
|
||||
callInterfaceIfPresent(cl, cl.getTile(obj->visitablePos())->visitableObjects.back()->tempOwner, &IGameEventsReceiver::showMarketWindow, market, hero, pack.queryID);
|
||||
}
|
||||
break;
|
||||
|
@ -68,7 +68,10 @@ GlobalLobbyLoginWindow::GlobalLobbyLoginWindow()
|
||||
onLoginModeChanged(0); // call it manually to disable widgets - toggleMode will not emit this call if this is currently selected option
|
||||
}
|
||||
else
|
||||
{
|
||||
toggleMode->setSelected(1);
|
||||
onLoginModeChanged(1);
|
||||
}
|
||||
|
||||
filledBackground->setPlayerColor(PlayerColor(1));
|
||||
inputUsername->setCallback([this](const std::string & text)
|
||||
|
@ -286,5 +286,5 @@ GlobalLobbyMatchCard::GlobalLobbyMatchCard(GlobalLobbyWindow * window, const Glo
|
||||
opponentDescription.replaceNumber(matchDescription.participants.size());
|
||||
}
|
||||
|
||||
labelMatchOpponent = std::make_shared<CLabel>(5, 30, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::YELLOW, opponentDescription.toString());
|
||||
labelMatchOpponent = std::make_shared<CLabel>(5, 30, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::YELLOW, opponentDescription.toString(), 120);
|
||||
}
|
||||
|
@ -47,7 +47,6 @@
|
||||
|
||||
#include "../../lib/texts/CGeneralTextHandler.h"
|
||||
#include "../../lib/campaign/CampaignHandler.h"
|
||||
#include "../../lib/serializer/CTypeList.h"
|
||||
#include "../../lib/filesystem/Filesystem.h"
|
||||
#include "../../lib/filesystem/CCompressedStream.h"
|
||||
#include "../../lib/mapping/CMapInfo.h"
|
||||
|
@ -130,13 +130,9 @@ TIcons CStatisticScreen::extractIcons() const
|
||||
std::sort(tmpData.begin(), tmpData.end(), [](const StatisticDataSetEntry & v1, const StatisticDataSetEntry & v2){ return v1.player == v2.player ? v1.day < v2.day : v1.player < v2.player; });
|
||||
|
||||
auto imageTown = GH.renderHandler().loadImage(AnimationPath::builtin("cradvntr"), 3, 0, EImageBlitMode::COLORKEY);
|
||||
imageTown->scaleTo(Point(CHART_ICON_SIZE, CHART_ICON_SIZE));
|
||||
auto imageBattle = GH.renderHandler().loadImage(AnimationPath::builtin("cradvntr"), 5, 0, EImageBlitMode::COLORKEY);
|
||||
imageBattle->scaleTo(Point(CHART_ICON_SIZE, CHART_ICON_SIZE));
|
||||
auto imageDefeated = GH.renderHandler().loadImage(AnimationPath::builtin("tpthchk"), 1, 0, EImageBlitMode::COLORKEY);
|
||||
imageDefeated->scaleTo(Point(CHART_ICON_SIZE, CHART_ICON_SIZE));
|
||||
auto imageDefeated = GH.renderHandler().loadImage(AnimationPath::builtin("crcombat"), 0, 0, EImageBlitMode::COLORKEY);
|
||||
auto imageGrail = GH.renderHandler().loadImage(AnimationPath::builtin("vwsymbol"), 2, 0, EImageBlitMode::COLORKEY);
|
||||
imageGrail->scaleTo(Point(CHART_ICON_SIZE, CHART_ICON_SIZE));
|
||||
|
||||
std::map<PlayerColor, bool> foundDefeated;
|
||||
std::map<PlayerColor, bool> foundGrail;
|
||||
@ -273,7 +269,11 @@ OverviewPanel::OverviewPanel(Rect position, std::string title, const StatisticDa
|
||||
},
|
||||
{
|
||||
CGI->generaltexth->translate("vcmi.statisticWindow.param.daysSurvived"), [this](PlayerColor color){
|
||||
return CStatisticScreen::getDay(playerDataFilter(color).size());
|
||||
auto playerData = playerDataFilter(color);
|
||||
for(int i = 0; i < playerData.size(); i++)
|
||||
if(playerData[i].status == EPlayerStatus::LOSER)
|
||||
return CStatisticScreen::getDay(i + 1);
|
||||
return CStatisticScreen::getDay(playerData.size());
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -424,12 +424,25 @@ void OverviewPanel::update(int to)
|
||||
}
|
||||
}
|
||||
|
||||
int computeGridStep(int maxAmount, int linesLimit)
|
||||
{
|
||||
for (int lineInterval = 1;;lineInterval *= 10)
|
||||
{
|
||||
for (int factor : { 1, 2, 5 } )
|
||||
{
|
||||
int lineIntervalToTest = lineInterval * factor;
|
||||
if (maxAmount / lineIntervalToTest <= linesLimit)
|
||||
return lineIntervalToTest;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LineChart::LineChart(Rect position, std::string title, TData data, TIcons icons, float maxY)
|
||||
: CIntObject(), maxVal(0), maxDay(0)
|
||||
{
|
||||
OBJECT_CONSTRUCTION;
|
||||
|
||||
addUsedEvents(LCLICK | MOVE);
|
||||
addUsedEvents(LCLICK | MOVE | GESTURE);
|
||||
|
||||
pos = position + pos.topLeft();
|
||||
|
||||
@ -455,15 +468,48 @@ LineChart::LineChart(Rect position, std::string title, TData data, TIcons icons,
|
||||
maxDay = line.second.size();
|
||||
}
|
||||
|
||||
//calculate nice maxVal
|
||||
int gridLineCount = 10;
|
||||
int gridStep = computeGridStep(maxVal, gridLineCount);
|
||||
niceMaxVal = gridStep * std::ceil(maxVal / gridStep);
|
||||
niceMaxVal = std::max(1, niceMaxVal); // avoid zero size Y axis (if all values are 0)
|
||||
|
||||
// calculate points in chart
|
||||
auto getPoint = [this](int i, std::vector<float> data){
|
||||
float x = (static_cast<float>(chartArea.w) / static_cast<float>(maxDay - 1)) * static_cast<float>(i);
|
||||
float y = static_cast<float>(chartArea.h) - (static_cast<float>(chartArea.h) / niceMaxVal) * data[i];
|
||||
return Point(x, y);
|
||||
};
|
||||
|
||||
// draw grid (vertical lines)
|
||||
int dayGridInterval = maxDay < 700 ? 7 : 28;
|
||||
for(const auto & line : data)
|
||||
{
|
||||
for(int i = 0; i < line.second.size(); i += dayGridInterval)
|
||||
{
|
||||
Point p = getPoint(i, line.second) + chartArea.topLeft();
|
||||
canvas->addLine(Point(p.x, chartArea.topLeft().y), Point(p.x, chartArea.topLeft().y + chartArea.h), ColorRGBA(70, 70, 70));
|
||||
}
|
||||
}
|
||||
|
||||
// draw grid (horizontal lines)
|
||||
if(maxVal > 0)
|
||||
{
|
||||
int gridStepPx = int((static_cast<float>(chartArea.h) / niceMaxVal) * gridStep);
|
||||
for(int i = 0; i < std::ceil(maxVal / gridStep) + 1; i++)
|
||||
{
|
||||
canvas->addLine(chartArea.topLeft() + Point(0, chartArea.h - gridStepPx * i), chartArea.topLeft() + Point(chartArea.w, chartArea.h - gridStepPx * i), ColorRGBA(70, 70, 70));
|
||||
layout.emplace_back(std::make_shared<CLabel>(chartArea.topLeft().x - 5, chartArea.topLeft().y + 10 + chartArea.h - gridStepPx * i, FONT_SMALL, ETextAlignment::CENTERRIGHT, Colors::WHITE, TextOperations::formatMetric(i * gridStep, 5)));
|
||||
}
|
||||
}
|
||||
|
||||
// draw
|
||||
for(const auto & line : data)
|
||||
{
|
||||
Point lastPoint(-1, -1);
|
||||
for(int i = 0; i < line.second.size(); i++)
|
||||
{
|
||||
float x = (static_cast<float>(chartArea.w) / static_cast<float>(maxDay - 1)) * static_cast<float>(i);
|
||||
float y = static_cast<float>(chartArea.h) - (static_cast<float>(chartArea.h) / maxVal) * line.second[i];
|
||||
Point p = Point(x, y) + chartArea.topLeft();
|
||||
Point p = getPoint(i, line.second) + chartArea.topLeft();
|
||||
|
||||
if(lastPoint.x != -1)
|
||||
canvas->addLine(lastPoint, p, line.first);
|
||||
@ -472,7 +518,7 @@ LineChart::LineChart(Rect position, std::string title, TData data, TIcons icons,
|
||||
for(auto & icon : icons)
|
||||
if(std::get<0>(icon) == line.first && std::get<1>(icon) == i + 1) // color && day
|
||||
{
|
||||
pictures.emplace_back(std::make_shared<CPicture>(std::get<2>(icon), Point(x - (CHART_ICON_SIZE / 2), y - (CHART_ICON_SIZE / 2)) + chartArea.topLeft()));
|
||||
pictures.emplace_back(std::make_shared<CPicture>(std::get<2>(icon), Point(p.x - (std::get<2>(icon)->width() / 2), p.y - (std::get<2>(icon)->height() / 2))));
|
||||
pictures.back()->addRClickCallback([icon](){ CRClickPopup::createAndPush(std::get<3>(icon)); });
|
||||
}
|
||||
|
||||
@ -484,12 +530,8 @@ LineChart::LineChart(Rect position, std::string title, TData data, TIcons icons,
|
||||
canvas->addLine(chartArea.topLeft() + Point(0, -10), chartArea.topLeft() + Point(0, chartArea.h + 10), Colors::WHITE);
|
||||
canvas->addLine(chartArea.topLeft() + Point(-10, chartArea.h), chartArea.topLeft() + Point(chartArea.w + 10, chartArea.h), Colors::WHITE);
|
||||
|
||||
Point p = chartArea.topLeft() + Point(-5, chartArea.h + 10);
|
||||
layout.emplace_back(std::make_shared<CLabel>(p.x, p.y, FONT_SMALL, ETextAlignment::CENTERRIGHT, Colors::WHITE, "0"));
|
||||
p = chartArea.topLeft() + Point(chartArea.w + 10, chartArea.h + 10);
|
||||
Point p = chartArea.topLeft() + Point(chartArea.w + 10, chartArea.h + 10);
|
||||
layout.emplace_back(std::make_shared<CLabel>(p.x, p.y, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, CStatisticScreen::getDay(maxDay)));
|
||||
p = chartArea.topLeft() + Point(-5, -10);
|
||||
layout.emplace_back(std::make_shared<CLabel>(p.x, p.y, FONT_SMALL, ETextAlignment::CENTERRIGHT, Colors::WHITE, std::to_string(static_cast<int>(maxVal))));
|
||||
p = chartArea.bottomLeft() + Point(chartArea.w / 2, + 20);
|
||||
layout.emplace_back(std::make_shared<CLabel>(p.x, p.y, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->translate("core.genrltxt.64")));
|
||||
}
|
||||
@ -502,8 +544,8 @@ void LineChart::updateStatusBar(const Point & cursorPosition)
|
||||
statusBar->setEnabled(r.isInside(cursorPosition));
|
||||
if(r.isInside(cursorPosition))
|
||||
{
|
||||
float x = (static_cast<float>(maxDay) / static_cast<float>(chartArea.w)) * (static_cast<float>(cursorPosition.x) - static_cast<float>(r.x)) + 1.0f;
|
||||
float y = maxVal - (maxVal / static_cast<float>(chartArea.h)) * (static_cast<float>(cursorPosition.y) - static_cast<float>(r.y));
|
||||
float x = (static_cast<float>(maxDay - 1) / static_cast<float>(chartArea.w)) * (static_cast<float>(cursorPosition.x) - static_cast<float>(r.x)) + 1.0f;
|
||||
float y = niceMaxVal - (niceMaxVal / static_cast<float>(chartArea.h)) * (static_cast<float>(cursorPosition.y) - static_cast<float>(r.y));
|
||||
statusBar->write(CGI->generaltexth->translate("core.genrltxt.64") + ": " + CStatisticScreen::getDay(x) + " " + CGI->generaltexth->translate("vcmi.statisticWindow.value") + ": " + (static_cast<int>(y) > 0 ? std::to_string(static_cast<int>(y)) : std::to_string(y)));
|
||||
}
|
||||
setRedrawParent(true);
|
||||
@ -515,7 +557,7 @@ void LineChart::mouseMoved(const Point & cursorPosition, const Point & lastUpdat
|
||||
updateStatusBar(cursorPosition);
|
||||
}
|
||||
|
||||
void LineChart::clickPressed(const Point & cursorPosition)
|
||||
void LineChart::gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance)
|
||||
{
|
||||
updateStatusBar(cursorPosition);
|
||||
updateStatusBar(currentPosition);
|
||||
}
|
||||
|
@ -24,8 +24,6 @@ class CPicture;
|
||||
using TData = std::vector<std::pair<ColorRGBA, std::vector<float>>>;
|
||||
using TIcons = std::vector<std::tuple<ColorRGBA, int, std::shared_ptr<IImage>, std::string>>; // Color, Day, Image, Helptext
|
||||
|
||||
const int CHART_ICON_SIZE = 32;
|
||||
|
||||
class CStatisticScreen : public CWindowObject
|
||||
{
|
||||
enum Content {
|
||||
@ -123,6 +121,7 @@ class LineChart : public CIntObject
|
||||
|
||||
Rect chartArea;
|
||||
float maxVal;
|
||||
int niceMaxVal;
|
||||
int maxDay;
|
||||
|
||||
void updateStatusBar(const Point & cursorPosition);
|
||||
@ -130,5 +129,5 @@ public:
|
||||
LineChart(Rect position, std::string title, TData data, TIcons icons, float maxY);
|
||||
|
||||
void mouseMoved(const Point & cursorPosition, const Point & lastUpdateDistance) override;
|
||||
void clickPressed(const Point & cursorPosition) override;
|
||||
void gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) override;
|
||||
};
|
||||
|
@ -56,12 +56,8 @@ bool ImageLocator::operator<(const ImageLocator & other) const
|
||||
return scalingFactor < other.scalingFactor;
|
||||
if(playerColored != other.playerColored)
|
||||
return playerColored < other.playerColored;
|
||||
if(layerShadow != other.layerShadow)
|
||||
return layerShadow < other.layerShadow;
|
||||
if(layerBody != other.layerBody)
|
||||
return layerBody < other.layerBody;
|
||||
if (layerOverlay != other.layerOverlay)
|
||||
return layerOverlay < other.layerOverlay;
|
||||
if(layer != other.layer)
|
||||
return layer < other.layer;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -12,6 +12,15 @@
|
||||
#include "../../lib/filesystem/ResourcePath.h"
|
||||
#include "../../lib/constants/EntityIdentifiers.h"
|
||||
|
||||
enum class EImageLayer
|
||||
{
|
||||
ALL,
|
||||
|
||||
BODY,
|
||||
SHADOW,
|
||||
OVERLAY,
|
||||
};
|
||||
|
||||
struct ImageLocator
|
||||
{
|
||||
std::optional<ImagePath> image;
|
||||
@ -19,13 +28,12 @@ struct ImageLocator
|
||||
int defFrame = -1;
|
||||
int defGroup = -1;
|
||||
|
||||
PlayerColor playerColored = PlayerColor::CANNOT_DETERMINE;
|
||||
|
||||
bool verticalFlip = false;
|
||||
bool horizontalFlip = false;
|
||||
int8_t scalingFactor = 1;
|
||||
PlayerColor playerColored = PlayerColor::CANNOT_DETERMINE;
|
||||
bool layerShadow = false;
|
||||
bool layerBody = true;
|
||||
bool layerOverlay = false;
|
||||
EImageLayer layer = EImageLayer::ALL;
|
||||
|
||||
ImageLocator() = default;
|
||||
ImageLocator(const AnimationPath & path, int frame, int group);
|
||||
|
@ -30,6 +30,8 @@ ImageScaled::ImageScaled(const ImageLocator & inputLocator, const std::shared_pt
|
||||
{
|
||||
locator.scalingFactor = GH.screenHandler().getScalingFactor();
|
||||
setBodyEnabled(true);
|
||||
if (mode == EImageBlitMode::ALPHA)
|
||||
setShadowEnabled(true);
|
||||
}
|
||||
|
||||
std::shared_ptr<ISharedImage> ImageScaled::getSharedImage() const
|
||||
@ -45,7 +47,7 @@ void ImageScaled::scaleInteger(int factor)
|
||||
void ImageScaled::scaleTo(const Point & size)
|
||||
{
|
||||
if (body)
|
||||
body = body->scaleTo(size, nullptr); // FIXME: adjust for scaling
|
||||
body = body->scaleTo(size * GH.screenHandler().getScalingFactor(), nullptr);
|
||||
}
|
||||
|
||||
void ImageScaled::exportBitmap(const boost::filesystem::path &path) const
|
||||
@ -107,11 +109,10 @@ void ImageScaled::adjustPalette(const ColorFilter &shifter, uint32_t colorsToSki
|
||||
|
||||
void ImageScaled::setShadowEnabled(bool on)
|
||||
{
|
||||
assert(blitMode == EImageBlitMode::ALPHA);
|
||||
if (on)
|
||||
{
|
||||
locator.layerBody = false;
|
||||
locator.layerShadow = true;
|
||||
locator.layerOverlay = false;
|
||||
locator.layer = EImageLayer::SHADOW;
|
||||
locator.playerColored = PlayerColor::CANNOT_DETERMINE;
|
||||
shadow = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage();
|
||||
}
|
||||
@ -123,9 +124,7 @@ void ImageScaled::setBodyEnabled(bool on)
|
||||
{
|
||||
if (on)
|
||||
{
|
||||
locator.layerBody = true;
|
||||
locator.layerShadow = false;
|
||||
locator.layerOverlay = false;
|
||||
locator.layer = blitMode == EImageBlitMode::ALPHA ? EImageLayer::BODY : EImageLayer::ALL;
|
||||
locator.playerColored = playerColor;
|
||||
body = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage();
|
||||
}
|
||||
@ -136,11 +135,10 @@ void ImageScaled::setBodyEnabled(bool on)
|
||||
|
||||
void ImageScaled::setOverlayEnabled(bool on)
|
||||
{
|
||||
assert(blitMode == EImageBlitMode::ALPHA);
|
||||
if (on)
|
||||
{
|
||||
locator.layerBody = false;
|
||||
locator.layerShadow = false;
|
||||
locator.layerOverlay = true;
|
||||
locator.layer = EImageLayer::OVERLAY;
|
||||
locator.playerColored = PlayerColor::CANNOT_DETERMINE;
|
||||
overlay = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage();
|
||||
}
|
||||
|
@ -228,16 +228,14 @@ std::shared_ptr<ISharedImage> RenderHandler::scaleImage(const ImageLocator & loc
|
||||
if (imageFiles.count(locator))
|
||||
return imageFiles.at(locator);
|
||||
|
||||
auto handle = image->createImageReference(EImageBlitMode::OPAQUE);
|
||||
auto handle = image->createImageReference(locator.layer == EImageLayer::ALL ? EImageBlitMode::OPAQUE : EImageBlitMode::ALPHA);
|
||||
|
||||
assert(locator.scalingFactor != 1); // should be filtered-out before
|
||||
|
||||
|
||||
|
||||
handle->setOverlayEnabled(locator.layerOverlay);
|
||||
handle->setBodyEnabled(locator.layerBody);
|
||||
handle->setShadowEnabled(locator.layerShadow);
|
||||
if (locator.layerBody && locator.playerColored != PlayerColor::CANNOT_DETERMINE)
|
||||
handle->setOverlayEnabled(locator.layer == EImageLayer::ALL || locator.layer == EImageLayer::OVERLAY);
|
||||
handle->setBodyEnabled(locator.layer == EImageLayer::ALL || locator.layer == EImageLayer::BODY);
|
||||
handle->setShadowEnabled(locator.layer == EImageLayer::ALL || locator.layer == EImageLayer::SHADOW);
|
||||
if (locator.layer == EImageLayer::ALL && locator.playerColored != PlayerColor::CANNOT_DETERMINE)
|
||||
handle->playerColored(locator.playerColored);
|
||||
|
||||
handle->scaleInteger(locator.scalingFactor);
|
||||
|
@ -472,13 +472,11 @@ void SDLImageIndexed::setShadowTransparency(float factor)
|
||||
};
|
||||
|
||||
// seems to be used unconditionally
|
||||
colorsSDL[0] = CSDL_Ext::toSDL(Colors::TRANSPARENCY);
|
||||
colorsSDL[1] = CSDL_Ext::toSDL(shadow25);
|
||||
colorsSDL[4] = CSDL_Ext::toSDL(shadow50);
|
||||
|
||||
// seems to be used only if color matches
|
||||
if (colorsSimilar(originalPalette->colors[0], sourcePalette[0]))
|
||||
colorsSDL[0] = CSDL_Ext::toSDL(Colors::TRANSPARENCY);
|
||||
|
||||
if (colorsSimilar(originalPalette->colors[2], sourcePalette[2]))
|
||||
colorsSDL[2] = CSDL_Ext::toSDL(shadow25);
|
||||
|
||||
@ -502,6 +500,9 @@ void SDLImageIndexed::setShadowEnabled(bool on)
|
||||
if (on)
|
||||
setShadowTransparency(1.0);
|
||||
|
||||
if (!on && blitMode == EImageBlitMode::ALPHA)
|
||||
setShadowTransparency(0.0);
|
||||
|
||||
shadowEnabled = on;
|
||||
}
|
||||
|
||||
|
@ -233,7 +233,7 @@ std::string CComponent::getDescription() const
|
||||
return description;
|
||||
}
|
||||
case ComponentType::SPELL:
|
||||
return CGI->spells()->getById(data.subType.as<SpellID>())->getDescriptionTranslated(data.value.value_or(0));
|
||||
return CGI->spells()->getById(data.subType.as<SpellID>())->getDescriptionTranslated(std::max(0, data.value.value_or(0)));
|
||||
case ComponentType::MORALE:
|
||||
return CGI->generaltexth->heroscrn[ 4 - (data.value.value_or(0)>0) + (data.value.value_or(0)<0)];
|
||||
case ComponentType::LUCK:
|
||||
@ -293,7 +293,7 @@ std::string CComponent::getSubtitle() const
|
||||
return CGI->artifacts()->getById(data.subType.as<ArtifactID>())->getNameTranslated();
|
||||
case ComponentType::SPELL_SCROLL:
|
||||
case ComponentType::SPELL:
|
||||
if (data.value < 0)
|
||||
if (data.value.value_or(0) < 0)
|
||||
return "{#A9A9A9|" + CGI->spells()->getById(data.subType.as<SpellID>())->getNameTranslated() + "}";
|
||||
else
|
||||
return CGI->spells()->getById(data.subType.as<SpellID>())->getNameTranslated();
|
||||
|
@ -464,7 +464,7 @@ void CInteractableTownTooltip::init(const CGTownInstance * town)
|
||||
std::vector<const CGTownInstance*> towns = LOCPLINT->cb->getTownsInfo(true);
|
||||
for(auto & town : towns)
|
||||
{
|
||||
if(town->id == townId && town->builtBuildings.count(BuildingID::TAVERN))
|
||||
if(town->id == townId && town->hasBuilt(BuildingID::TAVERN))
|
||||
LOCPLINT->showTavernWindow(town, nullptr, QueryID::NONE);
|
||||
}
|
||||
}, [town]{
|
||||
@ -476,7 +476,7 @@ void CInteractableTownTooltip::init(const CGTownInstance * town)
|
||||
std::vector<const CGTownInstance*> towns = LOCPLINT->cb->getTownsInfo(true);
|
||||
for(auto & town : towns)
|
||||
{
|
||||
if(town->builtBuildings.count(BuildingID::MARKETPLACE))
|
||||
if(town->hasBuilt(BuildingID::MARKETPLACE))
|
||||
{
|
||||
GH.windows().createAndPushWindow<CMarketWindow>(town, nullptr, nullptr, EMarketMode::RESOURCE_RESOURCE);
|
||||
return;
|
||||
|
@ -24,16 +24,15 @@
|
||||
#include "../../../lib/networkPacks/ArtifactLocation.h"
|
||||
#include "../../../lib/texts/CGeneralTextHandler.h"
|
||||
#include "../../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../../../lib/mapObjects/CGMarket.h"
|
||||
#include "../../../lib/mapObjects/IMarket.h"
|
||||
|
||||
CAltarArtifacts::CAltarArtifacts(const IMarket * market, const CGHeroInstance * hero)
|
||||
: CMarketBase(market, hero)
|
||||
{
|
||||
OBJECT_CONSTRUCTION;
|
||||
|
||||
assert(dynamic_cast<const CGArtifactsAltar*>(market));
|
||||
auto altarObj = dynamic_cast<const CGArtifactsAltar*>(market);
|
||||
altarArtifacts = altarObj;
|
||||
assert(market->getArtifactsStorage());
|
||||
altarArtifactsStorage = market->getArtifactsStorage();
|
||||
|
||||
deal = std::make_shared<CButton>(Point(269, 520), AnimationPath::builtin("ALTSACR.DEF"),
|
||||
CGI->generaltexth->zelp[585], [this]() {CAltarArtifacts::makeDeal(); }, EShortcut::MARKET_DEAL);
|
||||
@ -51,7 +50,7 @@ CAltarArtifacts::CAltarArtifacts(const IMarket * market, const CGHeroInstance *
|
||||
// Hero's artifacts
|
||||
heroArts = std::make_shared<CArtifactsOfHeroAltar>(Point(-365, -11));
|
||||
heroArts->setHero(hero);
|
||||
heroArts->altarId = altarObj->id;
|
||||
heroArts->altarId = market->getObjInstanceID();
|
||||
|
||||
// Altar
|
||||
offerTradePanel = std::make_shared<ArtifactsAltarPanel>([this](const std::shared_ptr<CTradeableItem> & altarSlot)
|
||||
@ -104,7 +103,7 @@ void CAltarArtifacts::makeDeal()
|
||||
{
|
||||
positions.push_back(artInst->getId());
|
||||
}
|
||||
LOCPLINT->cb->trade(market, EMarketMode::ARTIFACT_EXP, positions, std::vector<TradeItemBuy>(), std::vector<ui32>(), hero);
|
||||
LOCPLINT->cb->trade(market->getObjInstanceID(), EMarketMode::ARTIFACT_EXP, positions, std::vector<TradeItemBuy>(), std::vector<ui32>(), hero);
|
||||
deselect();
|
||||
}
|
||||
|
||||
@ -125,7 +124,7 @@ std::shared_ptr<CArtifactsOfHeroAltar> CAltarArtifacts::getAOHset() const
|
||||
|
||||
void CAltarArtifacts::updateAltarSlots()
|
||||
{
|
||||
assert(altarArtifacts->artifactsInBackpack.size() <= GameConstants::ALTAR_ARTIFACTS_SLOTS);
|
||||
assert(altarArtifactsStorage->artifactsInBackpack.size() <= GameConstants::ALTAR_ARTIFACTS_SLOTS);
|
||||
assert(tradeSlotsMap.size() <= GameConstants::ALTAR_ARTIFACTS_SLOTS);
|
||||
|
||||
auto tradeSlotsMapNewArts = tradeSlotsMap;
|
||||
@ -146,12 +145,12 @@ void CAltarArtifacts::updateAltarSlots()
|
||||
for(auto & tradeSlot : tradeSlotsMapNewArts)
|
||||
{
|
||||
assert(tradeSlot.first->id == -1);
|
||||
assert(altarArtifacts->getArtPos(tradeSlot.second) != ArtifactPosition::PRE_FIRST);
|
||||
assert(altarArtifactsStorage->getArtPos(tradeSlot.second) != ArtifactPosition::PRE_FIRST);
|
||||
tradeSlot.first->setID(tradeSlot.second->getTypeId().num);
|
||||
tradeSlot.first->subtitle->setText(std::to_string(calcExpCost(tradeSlot.second->getTypeId())));
|
||||
}
|
||||
|
||||
auto newArtsFromBulkMove = altarArtifacts->artifactsInBackpack;
|
||||
auto newArtsFromBulkMove = altarArtifactsStorage->artifactsInBackpack;
|
||||
for(const auto & [altarSlot, art] : tradeSlotsMap)
|
||||
{
|
||||
newArtsFromBulkMove.erase(std::remove_if(newArtsFromBulkMove.begin(), newArtsFromBulkMove.end(), [artForRemove = art](auto & slotInfo)
|
||||
@ -179,7 +178,7 @@ void CAltarArtifacts::putBackArtifacts()
|
||||
{
|
||||
// TODO: If the backpack capacity limit is enabled, artifacts may remain on the altar.
|
||||
// Perhaps should be erased in CGameHandler::objectVisitEnded if id of visited object will be available
|
||||
if(!altarArtifacts->artifactsInBackpack.empty())
|
||||
if(!altarArtifactsStorage->artifactsInBackpack.empty())
|
||||
LOCPLINT->cb->bulkMoveArtifacts(heroArts->altarId, heroArts->getHero()->id, false, true, true);
|
||||
}
|
||||
|
||||
@ -200,7 +199,7 @@ void CAltarArtifacts::onSlotClickPressed(const std::shared_ptr<CTradeableItem> &
|
||||
|
||||
if(const auto pickedArtInst = heroArts->getPickedArtifact())
|
||||
{
|
||||
if(pickedArtInst->canBePutAt(altarArtifacts))
|
||||
if(pickedArtInst->canBePutAt(altarArtifactsStorage))
|
||||
{
|
||||
if(pickedArtInst->artType->isTradable())
|
||||
{
|
||||
@ -221,7 +220,7 @@ void CAltarArtifacts::onSlotClickPressed(const std::shared_ptr<CTradeableItem> &
|
||||
else if(altarSlot->id != -1)
|
||||
{
|
||||
assert(tradeSlotsMap.at(altarSlot));
|
||||
const auto slot = altarArtifacts->getArtPos(tradeSlotsMap.at(altarSlot));
|
||||
const auto slot = altarArtifactsStorage->getArtPos(tradeSlotsMap.at(altarSlot));
|
||||
assert(slot != ArtifactPosition::PRE_FIRST);
|
||||
LOCPLINT->cb->swapArtifacts(ArtifactLocation(heroArts->altarId, slot),
|
||||
ArtifactLocation(hero->id, GH.isKeyboardCtrlDown() ? ArtifactPosition::FIRST_AVAILABLE : ArtifactPosition::TRANSITION_POS));
|
||||
|
@ -26,7 +26,7 @@ public:
|
||||
void putBackArtifacts();
|
||||
|
||||
private:
|
||||
const CArtifactSet * altarArtifacts;
|
||||
const CArtifactSet * altarArtifactsStorage;
|
||||
std::shared_ptr<CButton> sacrificeBackpackButton;
|
||||
std::shared_ptr<CArtifactsOfHeroAltar> heroArts;
|
||||
std::map<std::shared_ptr<CTradeableItem>, const CArtifactInstance*> tradeSlotsMap;
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
#include "../../../lib/texts/CGeneralTextHandler.h"
|
||||
#include "../../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../../../lib/mapObjects/CGMarket.h"
|
||||
#include "../../../lib/mapObjects/IMarket.h"
|
||||
|
||||
CAltarCreatures::CAltarCreatures(const IMarket * market, const CGHeroInstance * hero)
|
||||
: CMarketBase(market, hero)
|
||||
@ -157,7 +157,7 @@ void CAltarCreatures::makeDeal()
|
||||
}
|
||||
}
|
||||
|
||||
LOCPLINT->cb->trade(market, EMarketMode::CREATURE_EXP, ids, {}, toSacrifice, hero);
|
||||
LOCPLINT->cb->trade(market->getObjInstanceID(), EMarketMode::CREATURE_EXP, ids, {}, toSacrifice, hero);
|
||||
|
||||
for(int & units : unitsOnAltar)
|
||||
units = 0;
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include "StdInc.h"
|
||||
#include "CArtifactsBuying.h"
|
||||
|
||||
#include "../../gui/CGuiHandler.h"
|
||||
#include "../../gui/Shortcut.h"
|
||||
#include "../../widgets/Buttons.h"
|
||||
#include "../../widgets/TextControls.h"
|
||||
@ -21,24 +20,16 @@
|
||||
|
||||
#include "../../../CCallback.h"
|
||||
|
||||
#include "../../../lib/entities/building/CBuilding.h"
|
||||
#include "../../../lib/entities/faction/CTownHandler.h"
|
||||
#include "../../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../../../lib/mapObjects/CGMarket.h"
|
||||
#include "../../../lib/mapObjects/CGTownInstance.h"
|
||||
#include "../../../lib/mapObjects/IMarket.h"
|
||||
#include "../../../lib/texts/CGeneralTextHandler.h"
|
||||
|
||||
CArtifactsBuying::CArtifactsBuying(const IMarket * market, const CGHeroInstance * hero)
|
||||
CArtifactsBuying::CArtifactsBuying(const IMarket * market, const CGHeroInstance * hero, const std::string & title)
|
||||
: CMarketBase(market, hero)
|
||||
, CResourcesSelling([this](const std::shared_ptr<CTradeableItem> & heroSlot){CArtifactsBuying::onSlotClickPressed(heroSlot, bidTradePanel);})
|
||||
{
|
||||
OBJECT_CONSTRUCTION;
|
||||
|
||||
std::string title;
|
||||
if(auto townMarket = dynamic_cast<const CGTownInstance*>(market))
|
||||
title = (*CGI->townh)[townMarket->getFaction()]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->getNameTranslated();
|
||||
else
|
||||
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();}, EShortcut::MARKET_DEAL);
|
||||
@ -77,7 +68,7 @@ void CArtifactsBuying::makeDeal()
|
||||
{
|
||||
if(ArtifactID(offerTradePanel->getSelectedItemId()).toArtifact()->canBePutAt(hero))
|
||||
{
|
||||
LOCPLINT->cb->trade(market, EMarketMode::RESOURCE_ARTIFACT, GameResID(bidTradePanel->getSelectedItemId()),
|
||||
LOCPLINT->cb->trade(market->getObjInstanceID(), EMarketMode::RESOURCE_ARTIFACT, GameResID(bidTradePanel->getSelectedItemId()),
|
||||
ArtifactID(offerTradePanel->getSelectedItemId()), offerQty, hero);
|
||||
CMarketTraderText::makeDeal();
|
||||
deselect();
|
||||
|
@ -14,7 +14,7 @@
|
||||
class CArtifactsBuying : public CResourcesSelling, public CMarketTraderText
|
||||
{
|
||||
public:
|
||||
CArtifactsBuying(const IMarket * market, const CGHeroInstance * hero);
|
||||
CArtifactsBuying(const IMarket * market, const CGHeroInstance * hero, const std::string & title);
|
||||
void deselect() override;
|
||||
void makeDeal() override;
|
||||
|
||||
|
@ -22,14 +22,11 @@
|
||||
#include "../../../CCallback.h"
|
||||
|
||||
#include "../../../lib/CArtifactInstance.h"
|
||||
#include "../../../lib/entities/building/CBuilding.h"
|
||||
#include "../../../lib/entities/faction/CTownHandler.h"
|
||||
#include "../../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../../../lib/mapObjects/CGMarket.h"
|
||||
#include "../../../lib/mapObjects/CGTownInstance.h"
|
||||
#include "../../../lib/mapObjects/IMarket.h"
|
||||
#include "../../../lib/texts/CGeneralTextHandler.h"
|
||||
|
||||
CArtifactsSelling::CArtifactsSelling(const IMarket * market, const CGHeroInstance * hero)
|
||||
CArtifactsSelling::CArtifactsSelling(const IMarket * market, const CGHeroInstance * hero, const std::string & title)
|
||||
: CMarketBase(market, hero)
|
||||
, CResourcesBuying(
|
||||
[this](const std::shared_ptr<CTradeableItem> & resSlot){CArtifactsSelling::onSlotClickPressed(resSlot, offerTradePanel);},
|
||||
@ -37,12 +34,6 @@ CArtifactsSelling::CArtifactsSelling(const IMarket * market, const CGHeroInstanc
|
||||
{
|
||||
OBJECT_CONSTRUCTION;
|
||||
|
||||
std::string title;
|
||||
if(const auto townMarket = dynamic_cast<const CGTownInstance*>(market))
|
||||
title = (*CGI->townh)[townMarket->getFaction()]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->getNameTranslated();
|
||||
else if(const auto mapMarket = dynamic_cast<const CGMarket*>(market))
|
||||
title = mapMarket->title;
|
||||
|
||||
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"),
|
||||
@ -87,7 +78,8 @@ void CArtifactsSelling::makeDeal()
|
||||
{
|
||||
const auto art = hero->getArt(selectedHeroSlot);
|
||||
assert(art);
|
||||
LOCPLINT->cb->trade(market, EMarketMode::ARTIFACT_RESOURCE, art->getId(), GameResID(offerTradePanel->getSelectedItemId()), offerQty, hero);
|
||||
LOCPLINT->cb->trade(market->getObjInstanceID(), EMarketMode::ARTIFACT_RESOURCE, art->getId(),
|
||||
GameResID(offerTradePanel->getSelectedItemId()), offerQty, hero);
|
||||
CMarketTraderText::makeDeal();
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
class CArtifactsSelling : public CResourcesBuying, public CMarketTraderText
|
||||
{
|
||||
public:
|
||||
CArtifactsSelling(const IMarket * market, const CGHeroInstance * hero);
|
||||
CArtifactsSelling(const IMarket * market, const CGHeroInstance * hero, const std::string & title);
|
||||
void deselect() override;
|
||||
void makeDeal() override;
|
||||
void updateShowcases() override;
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
#include "../../../lib/texts/CGeneralTextHandler.h"
|
||||
#include "../../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../../../lib/mapObjects/CGMarket.h"
|
||||
#include "../../../lib/mapObjects/IMarket.h"
|
||||
|
||||
CFreelancerGuild::CFreelancerGuild(const IMarket * market, const CGHeroInstance * hero)
|
||||
: CMarketBase(market, hero)
|
||||
@ -69,7 +69,7 @@ void CFreelancerGuild::makeDeal()
|
||||
{
|
||||
if(auto toTrade = offerSlider->getValue(); toTrade != 0)
|
||||
{
|
||||
LOCPLINT->cb->trade(market, EMarketMode::CREATURE_RESOURCE, SlotID(bidTradePanel->highlightedSlot->serial), GameResID(offerTradePanel->getSelectedItemId()), bidQty * toTrade, hero);
|
||||
LOCPLINT->cb->trade(market->getObjInstanceID(), EMarketMode::CREATURE_RESOURCE, SlotID(bidTradePanel->highlightedSlot->serial), GameResID(offerTradePanel->getSelectedItemId()), bidQty * toTrade, hero);
|
||||
CMarketTraderText::makeDeal();
|
||||
deselect();
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include "../../../CCallback.h"
|
||||
|
||||
#include "../../../lib/texts/CGeneralTextHandler.h"
|
||||
#include "../../../lib/mapObjects/CGMarket.h"
|
||||
#include "../../../lib/mapObjects/IMarket.h"
|
||||
|
||||
CMarketResources::CMarketResources(const IMarket * market, const CGHeroInstance * hero)
|
||||
: CMarketBase(market, hero)
|
||||
@ -60,7 +60,7 @@ void CMarketResources::makeDeal()
|
||||
{
|
||||
if(auto toTrade = offerSlider->getValue(); toTrade != 0)
|
||||
{
|
||||
LOCPLINT->cb->trade(market, EMarketMode::RESOURCE_RESOURCE, GameResID(bidTradePanel->getSelectedItemId()),
|
||||
LOCPLINT->cb->trade(market->getObjInstanceID(), EMarketMode::RESOURCE_RESOURCE, GameResID(bidTradePanel->getSelectedItemId()),
|
||||
GameResID(offerTradePanel->highlightedSlot->id), bidQty * toTrade, hero);
|
||||
CMarketTraderText::makeDeal();
|
||||
deselect();
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "../../../CCallback.h"
|
||||
|
||||
#include "../../../lib/texts/CGeneralTextHandler.h"
|
||||
#include "../../../lib/mapObjects/IMarket.h"
|
||||
#include "../../../lib/texts/MetaString.h"
|
||||
|
||||
CTransferResources::CTransferResources(const IMarket * market, const CGHeroInstance * hero)
|
||||
@ -63,7 +64,7 @@ void CTransferResources::makeDeal()
|
||||
{
|
||||
if(auto toTrade = offerSlider->getValue(); toTrade != 0)
|
||||
{
|
||||
LOCPLINT->cb->trade(market, EMarketMode::RESOURCE_PLAYER, GameResID(bidTradePanel->getSelectedItemId()),
|
||||
LOCPLINT->cb->trade(market->getObjInstanceID(), EMarketMode::RESOURCE_PLAYER, GameResID(bidTradePanel->getSelectedItemId()),
|
||||
PlayerColor(offerTradePanel->getSelectedItemId()), toTrade, hero);
|
||||
CMarketTraderText::makeDeal();
|
||||
deselect();
|
||||
|
@ -145,7 +145,7 @@ void CBuildingRect::clickPressed(const Point & cursorPosition)
|
||||
if(getBuilding() && area && (parent->selectedBuilding==this))
|
||||
{
|
||||
auto building = getBuilding();
|
||||
parent->buildingClicked(building->bid, building->subId, building->upgrade);
|
||||
parent->buildingClicked(building->bid);
|
||||
}
|
||||
}
|
||||
|
||||
@ -586,9 +586,9 @@ void CCastleBuildings::recreate()
|
||||
|
||||
//Generate buildings list
|
||||
|
||||
auto buildingsCopy = town->builtBuildings;// a bit modified copy of built buildings
|
||||
auto buildingsCopy = town->getBuildings();// a bit modified copy of built buildings
|
||||
|
||||
if(vstd::contains(town->builtBuildings, BuildingID::SHIPYARD))
|
||||
if(town->hasBuilt(BuildingID::SHIPYARD))
|
||||
{
|
||||
auto bayPos = town->bestLocation();
|
||||
if(!bayPos.valid())
|
||||
@ -681,18 +681,76 @@ const CGHeroInstance * CCastleBuildings::getHero()
|
||||
return town->garrisonHero;
|
||||
}
|
||||
|
||||
void CCastleBuildings::buildingClicked(BuildingID building, BuildingSubID::EBuildingSubID subID, BuildingID upgrades)
|
||||
void CCastleBuildings::buildingClicked(BuildingID building)
|
||||
{
|
||||
logGlobal->trace("You've clicked on %d", (int)building.toEnum());
|
||||
const CBuilding *b = town->town->buildings.find(building)->second;
|
||||
|
||||
if (building >= BuildingID::DWELL_FIRST)
|
||||
BuildingID buildingToEnter = building;
|
||||
for(;;)
|
||||
{
|
||||
enterDwelling((BuildingID::getLevelFromDwelling(building)));
|
||||
const CBuilding *b = town->town->buildings.find(buildingToEnter)->second;
|
||||
|
||||
if (buildingTryActivateCustomUI(buildingToEnter, building))
|
||||
return;
|
||||
|
||||
if (!b->upgrade.hasValue())
|
||||
{
|
||||
enterBuilding(building);
|
||||
return;
|
||||
}
|
||||
|
||||
buildingToEnter = b->upgrade;
|
||||
}
|
||||
}
|
||||
|
||||
bool CCastleBuildings::buildingTryActivateCustomUI(BuildingID buildingToTest, BuildingID buildingTarget)
|
||||
{
|
||||
logGlobal->trace("You've clicked on %d", (int)buildingToTest.toEnum());
|
||||
const CBuilding *b = town->town->buildings.at(buildingToTest);
|
||||
|
||||
if (town->getWarMachineInBuilding(buildingToTest).hasValue())
|
||||
{
|
||||
enterBlacksmith(buildingTarget, town->getWarMachineInBuilding(buildingToTest));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!b->marketModes.empty())
|
||||
{
|
||||
switch (*b->marketModes.begin())
|
||||
{
|
||||
case EMarketMode::CREATURE_UNDEAD:
|
||||
GH.windows().createAndPushWindow<CTransformerWindow>(town, getHero(), nullptr);
|
||||
return true;
|
||||
|
||||
case EMarketMode::RESOURCE_SKILL:
|
||||
if (getHero())
|
||||
GH.windows().createAndPushWindow<CUniversityWindow>(getHero(), buildingTarget, town, nullptr);
|
||||
return true;
|
||||
|
||||
case EMarketMode::RESOURCE_RESOURCE:
|
||||
// can't use allied marketplace
|
||||
if (town->getOwner() == LOCPLINT->playerID)
|
||||
{
|
||||
GH.windows().createAndPushWindow<CMarketWindow>(town, getHero(), nullptr, *b->marketModes.begin());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
default:
|
||||
if(getHero())
|
||||
GH.windows().createAndPushWindow<CMarketWindow>(town, getHero(), nullptr, *b->marketModes.begin());
|
||||
else
|
||||
LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % b->getNameTranslated())); //Only visiting heroes may use the %s.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (buildingToTest >= BuildingID::DWELL_FIRST)
|
||||
{
|
||||
enterDwelling((BuildingID::getLevelFromDwelling(buildingToTest)));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(building)
|
||||
switch(buildingToTest)
|
||||
{
|
||||
case BuildingID::MAGES_GUILD_1:
|
||||
case BuildingID::MAGES_GUILD_2:
|
||||
@ -700,139 +758,91 @@ void CCastleBuildings::buildingClicked(BuildingID building, BuildingSubID::EBuil
|
||||
case BuildingID::MAGES_GUILD_4:
|
||||
case BuildingID::MAGES_GUILD_5:
|
||||
enterMagesGuild();
|
||||
break;
|
||||
return true;
|
||||
|
||||
case BuildingID::TAVERN:
|
||||
LOCPLINT->showTavernWindow(town, nullptr, QueryID::NONE);
|
||||
break;
|
||||
return true;
|
||||
|
||||
case BuildingID::SHIPYARD:
|
||||
if(town->shipyardStatus() == IBoatGenerator::GOOD)
|
||||
{
|
||||
LOCPLINT->showShipyardDialog(town);
|
||||
return true;
|
||||
}
|
||||
else if(town->shipyardStatus() == IBoatGenerator::BOAT_ALREADY_BUILT)
|
||||
{
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[51]);
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
case BuildingID::FORT:
|
||||
case BuildingID::CITADEL:
|
||||
case BuildingID::CASTLE:
|
||||
GH.windows().createAndPushWindow<CFortScreen>(town);
|
||||
break;
|
||||
return true;
|
||||
|
||||
case BuildingID::VILLAGE_HALL:
|
||||
case BuildingID::CITY_HALL:
|
||||
case BuildingID::TOWN_HALL:
|
||||
case BuildingID::CAPITOL:
|
||||
enterTownHall();
|
||||
break;
|
||||
|
||||
case BuildingID::MARKETPLACE:
|
||||
// can't use allied marketplace
|
||||
if (town->getOwner() == LOCPLINT->playerID)
|
||||
GH.windows().createAndPushWindow<CMarketWindow>(town, town->visitingHero, nullptr, EMarketMode::RESOURCE_RESOURCE);
|
||||
else
|
||||
enterBuilding(building);
|
||||
break;
|
||||
|
||||
case BuildingID::BLACKSMITH:
|
||||
enterBlacksmith(town->town->warMachine);
|
||||
break;
|
||||
return true;
|
||||
|
||||
case BuildingID::SHIP:
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[51]); //Cannot build another boat
|
||||
break;
|
||||
return true;
|
||||
|
||||
case BuildingID::SPECIAL_1:
|
||||
case BuildingID::SPECIAL_2:
|
||||
case BuildingID::SPECIAL_3:
|
||||
case BuildingID::SPECIAL_4:
|
||||
switch (subID)
|
||||
switch (b->subId)
|
||||
{
|
||||
case BuildingSubID::NONE:
|
||||
enterBuilding(building);
|
||||
break;
|
||||
|
||||
case BuildingSubID::MYSTIC_POND:
|
||||
enterFountain(building, subID, upgrades);
|
||||
break;
|
||||
|
||||
case BuildingSubID::ARTIFACT_MERCHANT:
|
||||
if(town->visitingHero)
|
||||
GH.windows().createAndPushWindow<CMarketWindow>(town, town->visitingHero, nullptr, EMarketMode::RESOURCE_ARTIFACT);
|
||||
else
|
||||
LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % b->getNameTranslated())); //Only visiting heroes may use the %s.
|
||||
break;
|
||||
|
||||
case BuildingSubID::FOUNTAIN_OF_FORTUNE:
|
||||
enterFountain(building, subID, upgrades);
|
||||
break;
|
||||
|
||||
case BuildingSubID::FREELANCERS_GUILD:
|
||||
if(getHero())
|
||||
GH.windows().createAndPushWindow<CMarketWindow>(town, getHero(), nullptr, EMarketMode::CREATURE_RESOURCE);
|
||||
else
|
||||
LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % b->getNameTranslated())); //Only visiting heroes may use the %s.
|
||||
break;
|
||||
|
||||
case BuildingSubID::MAGIC_UNIVERSITY:
|
||||
if (getHero())
|
||||
GH.windows().createAndPushWindow<CUniversityWindow>(getHero(), town, nullptr);
|
||||
else
|
||||
enterBuilding(building);
|
||||
break;
|
||||
enterFountain(buildingToTest, b->subId, buildingTarget);
|
||||
return true;
|
||||
|
||||
case BuildingSubID::CASTLE_GATE:
|
||||
if (LOCPLINT->makingTurn)
|
||||
{
|
||||
enterCastleGate();
|
||||
else
|
||||
enterBuilding(building);
|
||||
break;
|
||||
|
||||
case BuildingSubID::CREATURE_TRANSFORMER: //Skeleton Transformer
|
||||
GH.windows().createAndPushWindow<CTransformerWindow>(town, getHero(), nullptr);
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
case BuildingSubID::PORTAL_OF_SUMMONING:
|
||||
if (town->creatures[town->town->creatures.size()].second.empty())//No creatures
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->tcommands[30]);
|
||||
else
|
||||
enterDwelling(town->town->creatures.size());
|
||||
break;
|
||||
|
||||
case BuildingSubID::BALLISTA_YARD:
|
||||
enterBlacksmith(ArtifactID::BALLISTA);
|
||||
break;
|
||||
|
||||
case BuildingSubID::THIEVES_GUILD:
|
||||
enterAnyThievesGuild();
|
||||
break;
|
||||
return true;
|
||||
|
||||
case BuildingSubID::BANK:
|
||||
enterBank();
|
||||
break;
|
||||
|
||||
default:
|
||||
if(upgrades == BuildingID::TAVERN)
|
||||
LOCPLINT->showTavernWindow(town, nullptr, QueryID::NONE);
|
||||
else
|
||||
enterBuilding(building);
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
enterBuilding(building);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto const & bonus : b->buildingBonuses)
|
||||
{
|
||||
if (bonus->type == BonusType::THIEVES_GUILD_ACCESS)
|
||||
{
|
||||
enterAnyThievesGuild();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CCastleBuildings::enterBlacksmith(ArtifactID artifactID)
|
||||
void CCastleBuildings::enterBlacksmith(BuildingID building, ArtifactID artifactID)
|
||||
{
|
||||
const CGHeroInstance *hero = town->visitingHero;
|
||||
if(!hero)
|
||||
{
|
||||
LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % town->town->buildings.find(BuildingID::BLACKSMITH)->second->getNameTranslated()));
|
||||
LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % town->town->buildings.find(building)->second->getNameTranslated()));
|
||||
return;
|
||||
}
|
||||
auto art = artifactID.toArtifact();
|
||||
@ -843,7 +853,7 @@ void CCastleBuildings::enterBlacksmith(ArtifactID artifactID)
|
||||
{
|
||||
for(auto slot : art->getPossibleSlots().at(ArtBearer::HERO))
|
||||
{
|
||||
if(hero->getArt(slot) == nullptr)
|
||||
if(hero->getArt(slot) == nullptr || hero->getArt(slot)->getTypeId() != artifactID)
|
||||
{
|
||||
possible = true;
|
||||
break;
|
||||
@ -854,8 +864,9 @@ void CCastleBuildings::enterBlacksmith(ArtifactID artifactID)
|
||||
}
|
||||
}
|
||||
}
|
||||
CreatureID cre = art->getWarMachine();
|
||||
GH.windows().createAndPushWindow<CBlacksmithDialog>(possible, cre, artifactID, hero->id);
|
||||
|
||||
CreatureID creatureID = artifactID.toArtifact()->getWarMachine();
|
||||
GH.windows().createAndPushWindow<CBlacksmithDialog>(possible, creatureID, artifactID, hero->id);
|
||||
}
|
||||
|
||||
void CCastleBuildings::enterBuilding(BuildingID building)
|
||||
@ -996,7 +1007,7 @@ void CCastleBuildings::enterMagesGuild()
|
||||
void CCastleBuildings::enterTownHall()
|
||||
{
|
||||
if(town->visitingHero && town->visitingHero->hasArt(ArtifactID::GRAIL) &&
|
||||
!vstd::contains(town->builtBuildings, BuildingID::GRAIL)) //hero has grail, but town does not have it
|
||||
!town->hasBuilt(BuildingID::GRAIL)) //hero has grail, but town does not have it
|
||||
{
|
||||
if(!vstd::contains(town->forbiddenBuildings, BuildingID::GRAIL))
|
||||
{
|
||||
@ -1033,7 +1044,7 @@ void CCastleBuildings::enterAnyThievesGuild()
|
||||
std::vector<const CGTownInstance*> towns = LOCPLINT->cb->getTownsInfo(true);
|
||||
for(auto & ownedTown : towns)
|
||||
{
|
||||
if(ownedTown->builtBuildings.count(BuildingID::TAVERN))
|
||||
if(ownedTown->hasBuilt(BuildingID::TAVERN))
|
||||
{
|
||||
LOCPLINT->showThievesGuildWindow(ownedTown);
|
||||
return;
|
||||
@ -1059,7 +1070,7 @@ void CCastleBuildings::enterBank()
|
||||
|
||||
void CCastleBuildings::enterAnyMarket()
|
||||
{
|
||||
if(town->builtBuildings.count(BuildingID::MARKETPLACE))
|
||||
if(town->hasBuilt(BuildingID::MARKETPLACE))
|
||||
{
|
||||
GH.windows().createAndPushWindow<CMarketWindow>(town, nullptr, nullptr, EMarketMode::RESOURCE_RESOURCE);
|
||||
return;
|
||||
@ -1068,7 +1079,7 @@ void CCastleBuildings::enterAnyMarket()
|
||||
std::vector<const CGTownInstance*> towns = LOCPLINT->cb->getTownsInfo(true);
|
||||
for(auto & town : towns)
|
||||
{
|
||||
if(town->builtBuildings.count(BuildingID::MARKETPLACE))
|
||||
if(town->hasBuilt(BuildingID::MARKETPLACE))
|
||||
{
|
||||
GH.windows().createAndPushWindow<CMarketWindow>(town, nullptr, nullptr, EMarketMode::RESOURCE_RESOURCE);
|
||||
return;
|
||||
@ -1385,7 +1396,7 @@ void CCastleInterface::recreateIcons()
|
||||
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))
|
||||
if(town->hasBuilt(BuildingID::TAVERN))
|
||||
LOCPLINT->showTavernWindow(town, nullptr, QueryID::NONE);
|
||||
}, [this]{
|
||||
if(!town->town->faction->getDescriptionTranslated().empty())
|
||||
@ -1563,7 +1574,7 @@ CHallInterface::CHallInterface(const CGTownInstance * Town):
|
||||
}
|
||||
|
||||
const CBuilding * current = town->town->buildings.at(buildingID);
|
||||
if(vstd::contains(town->builtBuildings, buildingID))
|
||||
if(town->hasBuilt(buildingID))
|
||||
{
|
||||
building = current;
|
||||
}
|
||||
@ -1776,7 +1787,7 @@ CFortScreen::CFortScreen(const CGTownInstance * town):
|
||||
{
|
||||
BuildingID dwelling = BuildingID::getDwellingFromLevel(i, 1);
|
||||
|
||||
if(vstd::contains(town->builtBuildings, dwelling))
|
||||
if(town->hasBuilt(dwelling))
|
||||
buildingID = BuildingID(BuildingID::getDwellingFromLevel(i, 1));
|
||||
else
|
||||
buildingID = BuildingID(BuildingID::getDwellingFromLevel(i, 0));
|
||||
@ -1841,7 +1852,7 @@ CFortScreen::RecruitArea::RecruitArea(int posX, int posY, const CGTownInstance *
|
||||
buildingIcon = std::make_shared<CAnimImage>(town->town->clientInfo.buildingsIcons, getMyBuilding()->bid, 0, 4, 21);
|
||||
buildingName = std::make_shared<CLabel>(78, 101, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, getMyBuilding()->getNameTranslated(), 152);
|
||||
|
||||
if(vstd::contains(town->builtBuildings, getMyBuilding()->bid))
|
||||
if(town->hasBuilt(getMyBuilding()->bid))
|
||||
{
|
||||
ui32 available = town->creatures[level].first;
|
||||
std::string availableText = CGI->generaltexth->allTexts[217]+ std::to_string(available);
|
||||
|
@ -150,7 +150,7 @@ class CCastleBuildings : public CIntObject
|
||||
|
||||
const CGHeroInstance* getHero();//Select hero for buildings usage
|
||||
|
||||
void enterBlacksmith(ArtifactID artifactID);//support for blacksmith + ballista yard
|
||||
void enterBlacksmith(BuildingID building, ArtifactID artifactID);//support for blacksmith + ballista yard
|
||||
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
|
||||
@ -173,7 +173,8 @@ public:
|
||||
void enterBank();
|
||||
void enterToTheQuickRecruitmentWindow();
|
||||
|
||||
void buildingClicked(BuildingID building, BuildingSubID::EBuildingSubID subID = BuildingSubID::NONE, BuildingID upgrades = BuildingID::NONE);
|
||||
bool buildingTryActivateCustomUI(BuildingID buildingToTest, BuildingID buildingTarget);
|
||||
void buildingClicked(BuildingID building);
|
||||
void addBuilding(BuildingID building);
|
||||
void removeBuilding(BuildingID building);//FIXME: not tested!!!
|
||||
};
|
||||
|
@ -619,6 +619,7 @@ CStackWindow::MainSection::MainSection(CStackWindow * owner, int yOffset, bool s
|
||||
parent->stackArtifactIcon = std::make_shared<CAnimImage>(AnimationPath::builtin("ARTIFACT"), art->artType->getIconIndex(), 0, pos.x, pos.y);
|
||||
parent->stackArtifactHelp = std::make_shared<LRClickableAreaWTextComp>(Rect(pos, Point(44, 44)), ComponentType::ARTIFACT);
|
||||
parent->stackArtifactHelp->component.subType = art->artType->getId();
|
||||
parent->stackArtifactHelp->text = art->getDescription();
|
||||
|
||||
if(parent->info->owner)
|
||||
{
|
||||
|
@ -585,9 +585,7 @@ void CKingdomInterface::generateMinesList(const std::vector<const CGObjectInstan
|
||||
const CGMine * mine = dynamic_cast<const CGMine *>(object);
|
||||
assert(mine);
|
||||
minesCount[mine->producedResource]++;
|
||||
|
||||
if (mine->producedResource == EGameResID::GOLD)
|
||||
totalIncome += mine->getProducedQuantity();
|
||||
totalIncome += mine->dailyIncome()[EGameResID::GOLD];
|
||||
}
|
||||
}
|
||||
|
||||
@ -596,7 +594,7 @@ void CKingdomInterface::generateMinesList(const std::vector<const CGObjectInstan
|
||||
auto * playerSettings = LOCPLINT->cb->getPlayerSettings(LOCPLINT->playerID);
|
||||
for(auto & hero : heroes)
|
||||
{
|
||||
totalIncome += hero->valOfBonuses(Selector::typeSubtype(BonusType::GENERATE_RESOURCE, BonusSubtypeID(GameResID(EGameResID::GOLD)))) * playerSettings->handicap.percentIncome / 100;
|
||||
totalIncome += hero->dailyIncome()[EGameResID::GOLD];
|
||||
}
|
||||
|
||||
//Add town income of all towns
|
||||
@ -822,7 +820,7 @@ CTownItem::CTownItem(const CGTownInstance * Town)
|
||||
|
||||
fastTavern = std::make_shared<LRClickableArea>(Rect(5, 6, 58, 64), [&]()
|
||||
{
|
||||
if(town->builtBuildings.count(BuildingID::TAVERN))
|
||||
if(town->hasBuilt(BuildingID::TAVERN))
|
||||
LOCPLINT->showTavernWindow(town, nullptr, QueryID::NONE);
|
||||
}, [&]{
|
||||
if(!town->town->faction->getDescriptionTranslated().empty())
|
||||
@ -833,7 +831,7 @@ CTownItem::CTownItem(const CGTownInstance * Town)
|
||||
std::vector<const CGTownInstance*> towns = LOCPLINT->cb->getTownsInfo(true);
|
||||
for(auto & town : towns)
|
||||
{
|
||||
if(town->builtBuildings.count(BuildingID::MARKETPLACE))
|
||||
if(town->hasBuilt(BuildingID::MARKETPLACE))
|
||||
{
|
||||
GH.windows().createAndPushWindow<CMarketWindow>(town, nullptr, nullptr, EMarketMode::RESOURCE_RESOURCE);
|
||||
return;
|
||||
|
@ -27,11 +27,14 @@
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
|
||||
#include "../../lib/entities/building/CBuilding.h"
|
||||
#include "../../lib/texts/CGeneralTextHandler.h"
|
||||
#include "../../lib/mapObjects/CGTownInstance.h"
|
||||
#include "../../lib/mapObjects/CGMarket.h"
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
|
||||
CMarketWindow::CMarketWindow(const IMarket * market, const CGHeroInstance * hero, const std::function<void()> & onWindowClosed, EMarketMode mode)
|
||||
: CWindowObject(PLAYER_COLORED)
|
||||
, windowClosedCallback(onWindowClosed)
|
||||
@ -111,6 +114,11 @@ void CMarketWindow::createChangeModeButtons(EMarketMode currentMode, const IMark
|
||||
if(!market->allowsTrade(modeButton))
|
||||
return false;
|
||||
|
||||
if(currentMode == EMarketMode::ARTIFACT_EXP && modeButton != EMarketMode::CREATURE_EXP)
|
||||
return false;
|
||||
if(currentMode == EMarketMode::CREATURE_EXP && modeButton != EMarketMode::ARTIFACT_EXP)
|
||||
return false;
|
||||
|
||||
if(modeButton == EMarketMode::RESOURCE_RESOURCE || modeButton == EMarketMode::RESOURCE_PLAYER)
|
||||
{
|
||||
if(const auto town = dynamic_cast<const CGTownInstance*>(market))
|
||||
@ -175,12 +183,28 @@ void CMarketWindow::initWidgetInternals(const EMarketMode mode, const std::pair<
|
||||
redraw();
|
||||
}
|
||||
|
||||
std::string CMarketWindow::getMarketTitle(const ObjectInstanceID marketId, const EMarketMode mode) const
|
||||
{
|
||||
assert(LOCPLINT->cb->getMarket(marketId));
|
||||
assert(vstd::contains(LOCPLINT->cb->getMarket(marketId)->availableModes(), mode));
|
||||
|
||||
if(const auto town = LOCPLINT->cb->getTown(marketId))
|
||||
{
|
||||
for(const auto & buildingId : town->getBuildings())
|
||||
{
|
||||
if(const auto building = town->town->buildings.at(buildingId); vstd::contains(building->marketModes, mode))
|
||||
return building->getNameTranslated();
|
||||
}
|
||||
}
|
||||
return LOCPLINT->cb->getObj(marketId)->getObjectName();
|
||||
}
|
||||
|
||||
void CMarketWindow::createArtifactsBuying(const IMarket * market, const CGHeroInstance * hero)
|
||||
{
|
||||
OBJECT_CONSTRUCTION;
|
||||
|
||||
background = createBg(ImagePath::builtin("TPMRKABS.bmp"), PLAYER_COLORED);
|
||||
marketWidget = std::make_shared<CArtifactsBuying>(market, hero);
|
||||
marketWidget = std::make_shared<CArtifactsBuying>(market, hero, getMarketTitle(market->getObjInstanceID(), EMarketMode::RESOURCE_ARTIFACT));
|
||||
initWidgetInternals(EMarketMode::RESOURCE_ARTIFACT, CGI->generaltexth->zelp[600]);
|
||||
}
|
||||
|
||||
@ -192,13 +216,13 @@ void CMarketWindow::createArtifactsSelling(const IMarket * market, const CGHeroI
|
||||
// Create image that copies part of background containing slot MISC_1 into position of slot MISC_5
|
||||
artSlotBack = std::make_shared<CPicture>(background->getSurface(), Rect(20, 187, 47, 47), 0, 0);
|
||||
artSlotBack->moveTo(pos.topLeft() + Point(18, 339));
|
||||
auto artsSellingMarket = std::make_shared<CArtifactsSelling>(market, hero);
|
||||
auto artsSellingMarket = std::make_shared<CArtifactsSelling>(market, hero, getMarketTitle(market->getObjInstanceID(), EMarketMode::ARTIFACT_RESOURCE));
|
||||
artSets.clear();
|
||||
const auto heroArts = artsSellingMarket->getAOHset();
|
||||
heroArts->showPopupCallback = [this, heroArts](CArtPlace & artPlace, const Point & cursorPosition){showArifactInfo(*heroArts, artPlace, cursorPosition);};
|
||||
addSet(heroArts);
|
||||
marketWidget = artsSellingMarket;
|
||||
initWidgetInternals(EMarketMode::ARTIFACT_RESOURCE, CGI->generaltexth->zelp[600]);
|
||||
initWidgetInternals(EMarketMode::ARTIFACT_RESOURCE, CGI->generaltexth->zelp[600]);
|
||||
}
|
||||
|
||||
void CMarketWindow::createMarketResources(const IMarket * market, const CGHeroInstance * hero)
|
||||
@ -233,10 +257,10 @@ void CMarketWindow::createAltarArtifacts(const IMarket * market, const CGHeroIns
|
||||
OBJECT_CONSTRUCTION;
|
||||
|
||||
background = createBg(ImagePath::builtin("ALTRART2.bmp"), PLAYER_COLORED);
|
||||
auto altarArtifacts = std::make_shared<CAltarArtifacts>(market, hero);
|
||||
marketWidget = altarArtifacts;
|
||||
auto altarArtifactsStorage = std::make_shared<CAltarArtifacts>(market, hero);
|
||||
marketWidget = altarArtifactsStorage;
|
||||
artSets.clear();
|
||||
const auto heroArts = altarArtifacts->getAOHset();
|
||||
const auto heroArts = altarArtifactsStorage->getAOHset();
|
||||
heroArts->clickPressedCallback = [this, heroArts](const CArtPlace & artPlace, const Point & cursorPosition)
|
||||
{
|
||||
clickPressedOnArtPlace(heroArts->getHero(), artPlace.slot, true, true, false);
|
||||
@ -252,7 +276,7 @@ void CMarketWindow::createAltarArtifacts(const IMarket * market, const CGHeroIns
|
||||
addSet(heroArts);
|
||||
initWidgetInternals(EMarketMode::ARTIFACT_EXP, CGI->generaltexth->zelp[568]);
|
||||
updateExperience();
|
||||
quitButton->addCallback([altarArtifacts](){altarArtifacts->putBackArtifacts();});
|
||||
quitButton->addCallback([altarArtifactsStorage](){altarArtifactsStorage->putBackArtifacts();});
|
||||
}
|
||||
|
||||
void CMarketWindow::createAltarCreatures(const IMarket * market, const CGHeroInstance * hero)
|
||||
|
@ -27,6 +27,7 @@ public:
|
||||
private:
|
||||
void createChangeModeButtons(EMarketMode currentMode, const IMarket * market, const CGHeroInstance * hero);
|
||||
void initWidgetInternals(const EMarketMode mode, const std::pair<std::string, std::string> & quitButtonHelpContainer);
|
||||
std::string getMarketTitle(const ObjectInstanceID marketId, const EMarketMode mode) const;
|
||||
|
||||
void createArtifactsBuying(const IMarket * market, const CGHeroInstance * hero);
|
||||
void createArtifactsSelling(const IMarket * market, const CGHeroInstance * hero);
|
||||
|
@ -819,7 +819,7 @@ void CTransformerWindow::makeDeal()
|
||||
for(auto & elem : items)
|
||||
{
|
||||
if(!elem->left)
|
||||
LOCPLINT->cb->trade(market, EMarketMode::CREATURE_UNDEAD, SlotID(elem->id), {}, {}, hero);
|
||||
LOCPLINT->cb->trade(market->getObjInstanceID(), EMarketMode::CREATURE_UNDEAD, SlotID(elem->id), {}, {}, hero);
|
||||
}
|
||||
}
|
||||
|
||||
@ -946,7 +946,7 @@ void CUniversityWindow::CItem::hover(bool on)
|
||||
GH.statusbar()->clear();
|
||||
}
|
||||
|
||||
CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, const IMarket * _market, const std::function<void()> & onWindowClosed)
|
||||
CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, BuildingID building, const IMarket * _market, const std::function<void()> & onWindowClosed)
|
||||
: CWindowObject(PLAYER_COLORED, ImagePath::builtin("UNIVERS1")),
|
||||
hero(_hero),
|
||||
onWindowClosed(onWindowClosed),
|
||||
@ -961,8 +961,7 @@ CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, const IMarket
|
||||
if(auto town = dynamic_cast<const CGTownInstance *>(_market))
|
||||
{
|
||||
auto faction = town->town->faction->getId();
|
||||
auto bid = town->town->getSpecialBuilding(BuildingSubID::MAGIC_UNIVERSITY)->bid;
|
||||
titlePic = std::make_shared<CAnimImage>((*CGI->townh)[faction]->town->clientInfo.buildingsIcons, bid);
|
||||
titlePic = std::make_shared<CAnimImage>((*CGI->townh)[faction]->town->clientInfo.buildingsIcons, building);
|
||||
}
|
||||
else if(auto uni = dynamic_cast<const CGUniversity *>(_market); uni->appearance)
|
||||
{
|
||||
@ -1005,7 +1004,7 @@ void CUniversityWindow::updateSecondarySkills()
|
||||
|
||||
void CUniversityWindow::makeDeal(SecondarySkill skill)
|
||||
{
|
||||
LOCPLINT->cb->trade(market, EMarketMode::RESOURCE_SKILL, GameResID(GameResID::GOLD), skill, 1, hero);
|
||||
LOCPLINT->cb->trade(market->getObjInstanceID(), EMarketMode::RESOURCE_SKILL, GameResID(GameResID::GOLD), skill, 1, hero);
|
||||
}
|
||||
|
||||
CUnivConfirmWindow::CUnivConfirmWindow(CUniversityWindow * owner_, SecondarySkill SKILL, bool available)
|
||||
|
@ -391,7 +391,7 @@ class CUniversityWindow final : public CStatusbarWindow, public IMarketHolder
|
||||
std::function<void()> onWindowClosed;
|
||||
|
||||
public:
|
||||
CUniversityWindow(const CGHeroInstance * _hero, const IMarket * _market, const std::function<void()> & onWindowClosed);
|
||||
CUniversityWindow(const CGHeroInstance * _hero, BuildingID building, const IMarket * _market, const std::function<void()> & onWindowClosed);
|
||||
|
||||
void makeDeal(SecondarySkill skill);
|
||||
void close() override;
|
||||
|
@ -591,6 +591,15 @@
|
||||
{
|
||||
"icon": "zvs/Lib1.res/MEGABREATH"
|
||||
}
|
||||
},
|
||||
|
||||
"DISINTEGRATE":
|
||||
{
|
||||
"graphics":
|
||||
{
|
||||
"icon": "zvs/Lib1.res/DISINTEGRATE"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,11 @@
|
||||
{
|
||||
"type": "MORALE",
|
||||
"val": 1
|
||||
},
|
||||
{
|
||||
"propagator": "PLAYER_PROPAGATOR",
|
||||
"type": "THIEVES_GUILD_ACCESS",
|
||||
"val": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -43,7 +48,10 @@
|
||||
"produce": { "gold": 4000 }
|
||||
},
|
||||
|
||||
"marketplace": { "id" : 14 },
|
||||
"marketplace": {
|
||||
"id" : 14,
|
||||
"marketModes" : ["resource-resource", "resource-player"]
|
||||
},
|
||||
"resourceSilo": { "id" : 15, "requires" : [ "marketplace" ] },
|
||||
"blacksmith": { "id" : 16 },
|
||||
|
||||
@ -198,5 +206,39 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
// Section 3 - markets
|
||||
"artifactMerchant" : {
|
||||
"requires" : [ "marketplace" ],
|
||||
"marketModes" : ["resource-artifact", "artifact-resource"]
|
||||
},
|
||||
|
||||
"freelancersGuild" : {
|
||||
"requires" : [ "marketplace" ],
|
||||
"marketModes" : ["creature-resource"]
|
||||
},
|
||||
|
||||
"magicUniversity" : {
|
||||
"marketModes" : ["resource-skill"]
|
||||
},
|
||||
|
||||
"creatureTransformer" : {
|
||||
"marketModes" : ["creature-undead"]
|
||||
},
|
||||
|
||||
// Section 4 - buildings that now have dedicated mechanics
|
||||
"ballistaYard": {
|
||||
"blacksmith" : "ballista"
|
||||
},
|
||||
|
||||
"thievesGuild" : {
|
||||
"bonuses": [
|
||||
{
|
||||
"propagator": "PLAYER_PROPAGATOR",
|
||||
"type": "THIEVES_GUILD_ACCESS",
|
||||
"val": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -147,7 +147,6 @@
|
||||
],
|
||||
"horde" : [ 2, -1 ],
|
||||
"mageGuild" : 4,
|
||||
"warMachine" : "ballista",
|
||||
"moatAbility" : "castleMoat",
|
||||
// primaryResource not specified so town get both Wood and Ore for resource bonus
|
||||
|
||||
@ -168,7 +167,7 @@
|
||||
"capitol": { },
|
||||
"marketplace": { },
|
||||
"resourceSilo": { "produce": { "ore": 1, "wood": 1 } },
|
||||
"blacksmith": { },
|
||||
"blacksmith": { "warMachine" : "ballista" },
|
||||
|
||||
"special1": {
|
||||
"bonuses": [
|
||||
|
@ -152,7 +152,6 @@
|
||||
"horde" : [ 0, -1 ],
|
||||
"mageGuild" : 5,
|
||||
"primaryResource" : "mercury",
|
||||
"warMachine" : "ballista",
|
||||
"moatAbility" : "castleMoat",
|
||||
|
||||
"buildings" :
|
||||
@ -173,13 +172,13 @@
|
||||
"capitol": { },
|
||||
"marketplace": { },
|
||||
"resourceSilo": { "produce": { "mercury": 1 } },
|
||||
"blacksmith": { },
|
||||
"blacksmith": { "warMachine" : "ballista" },
|
||||
|
||||
"special1": { "type" : "artifactMerchant", "requires" : [ "marketplace" ] },
|
||||
"special1": { "requires" : [ "marketplace" ], "marketModes" : ["resource-artifact", "artifact-resource"] },
|
||||
"horde1": { "id" : 18, "upgrades" : "dwellingLvl1" },
|
||||
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
|
||||
"ship": { "id" : 20, "upgrades" : "shipyard" },
|
||||
"special2": { "type" : "magicUniversity", "requires" : [ "mageGuild1" ] },
|
||||
"special2": { "requires" : [ "mageGuild1" ], "marketModes" : ["resource-skill"] },
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
|
||||
"extraTownHall": { "id" : 27, "requires" : [ "townHall" ], "mode" : "auto" },
|
||||
"extraCityHall": { "id" : 28, "requires" : [ "cityHall" ], "mode" : "auto" },
|
||||
|
@ -148,10 +148,8 @@
|
||||
"horde" : [ 0, -1 ],
|
||||
"mageGuild" : 5,
|
||||
"primaryResource" : "sulfur",
|
||||
"warMachine" : "ballista",
|
||||
"moatAbility" : "dungeonMoat",
|
||||
|
||||
|
||||
"buildings" :
|
||||
{
|
||||
"mageGuild1": { },
|
||||
@ -169,9 +167,9 @@
|
||||
"capitol": { },
|
||||
"marketplace": { },
|
||||
"resourceSilo": { "produce": { "sulfur": 1 } },
|
||||
"blacksmith": { },
|
||||
"blacksmith": { "warMachine" : "ballista" },
|
||||
|
||||
"special1": { "type" : "artifactMerchant", "requires" : [ "marketplace" ] },
|
||||
"special1": { "requires" : [ "marketplace" ], "marketModes" : ["resource-artifact", "artifact-resource"] },
|
||||
"horde1": { "id" : 18, "upgrades" : "dwellingLvl1" },
|
||||
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
|
||||
"special2": {
|
||||
|
@ -147,7 +147,6 @@
|
||||
],
|
||||
"horde" : [ 0, -1 ],
|
||||
"mageGuild" : 3,
|
||||
"warMachine" : "firstAidTent",
|
||||
"moatAbility" : "fortressMoat",
|
||||
// primaryResource not specified so town get both Wood and Ore for resource bonus
|
||||
|
||||
@ -167,7 +166,7 @@
|
||||
"capitol": { },
|
||||
"marketplace": { },
|
||||
"resourceSilo": { "produce": { "wood": 1, "ore": 1 } },
|
||||
"blacksmith": { },
|
||||
"blacksmith": { "warMachine" : "firstAidTent" },
|
||||
|
||||
"special1": {
|
||||
"requires" : [ "allOf", [ "townHall" ], [ "special2" ] ],
|
||||
|
@ -149,7 +149,6 @@
|
||||
"horde" : [ 0, 2 ],
|
||||
"mageGuild" : 5,
|
||||
"primaryResource" : "mercury",
|
||||
"warMachine" : "ammoCart",
|
||||
"moatAbility" : "infernoMoat",
|
||||
|
||||
"buildings" :
|
||||
@ -169,7 +168,7 @@
|
||||
"capitol": { },
|
||||
"marketplace": { },
|
||||
"resourceSilo": { "produce": { "mercury": 1 } },
|
||||
"blacksmith": { },
|
||||
"blacksmith": { "warMachine" : "ammoCart" },
|
||||
|
||||
"horde1": { "id" : 18, "upgrades" : "dwellingLvl1" },
|
||||
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
|
||||
|
@ -152,7 +152,6 @@
|
||||
],
|
||||
"horde" : [ 0, -1 ],
|
||||
"mageGuild" : 5,
|
||||
"warMachine" : "firstAidTent",
|
||||
"moatAbility" : "necropolisMoat",
|
||||
// primaryResource not specified so town get both Wood and Ore for resource bonus
|
||||
|
||||
@ -174,7 +173,7 @@
|
||||
"capitol": { },
|
||||
"marketplace": { },
|
||||
"resourceSilo": { "produce": { "ore": 1, "wood": 1 } },
|
||||
"blacksmith": { },
|
||||
"blacksmith": { "warMachine" : "firstAidTent" },
|
||||
|
||||
"special1": { "requires" : [ "fort" ], "bonuses": [ { "type": "DARKNESS", "val": 20 } ] },
|
||||
"horde1": { "id" : 18, "upgrades" : "dwellingLvl1", "requires" : [ "special3" ] },
|
||||
@ -182,7 +181,7 @@
|
||||
"ship": { "id" : 20, "upgrades" : "shipyard" },
|
||||
"special2": { "requires" : [ "mageGuild1" ],
|
||||
"bonuses": [ { "type": "UNDEAD_RAISE_PERCENTAGE", "val": 10, "propagator": "PLAYER_PROPAGATOR" } ] },
|
||||
"special3": { "type" : "creatureTransformer", "requires" : [ "dwellingLvl1" ] },
|
||||
"special3": { "requires" : [ "dwellingLvl1" ], "marketModes" : ["creature-undead"] },
|
||||
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 },
|
||||
"bonuses": [ { "type": "UNDEAD_RAISE_PERCENTAGE", "val": 20, "propagator": "PLAYER_PROPAGATOR" } ] },
|
||||
|
||||
|
@ -152,7 +152,6 @@
|
||||
"horde" : [ 1, 4 ],
|
||||
"mageGuild" : 5,
|
||||
"primaryResource" : "crystal",
|
||||
"warMachine" : "firstAidTent",
|
||||
"moatAbility" : "rampartMoat",
|
||||
|
||||
"buildings" :
|
||||
@ -172,7 +171,7 @@
|
||||
"capitol": { },
|
||||
"marketplace": { },
|
||||
"resourceSilo": { "produce": { "crystal": 1 } },
|
||||
"blacksmith": { },
|
||||
"blacksmith": { "warMachine" : "firstAidTent" },
|
||||
|
||||
"special1": { "type" : "mysticPond" },
|
||||
"horde1": { "id" : 18, "upgrades" : "dwellingLvl2" },
|
||||
|
@ -145,7 +145,6 @@
|
||||
],
|
||||
"horde" : [ 0, -1 ],
|
||||
"mageGuild" : 3,
|
||||
"warMachine" : "ammoCart",
|
||||
"moatAbility" : "strongholdMoat",
|
||||
// primaryResource not specified so town get both Wood and Ore for resource bonus
|
||||
|
||||
@ -164,13 +163,13 @@
|
||||
"capitol": { },
|
||||
"marketplace": { },
|
||||
"resourceSilo": { "produce": { "ore": 1, "wood": 1 } },
|
||||
"blacksmith": { },
|
||||
"blacksmith": { "warMachine" : "ammoCart" },
|
||||
|
||||
"special1": { "type" : "escapeTunnel", "requires" : [ "fort" ] },
|
||||
"horde1": { "id" : 18, "upgrades" : "dwellingLvl1" },
|
||||
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
|
||||
"special2": { "type" : "freelancersGuild", "requires" : [ "marketplace" ] },
|
||||
"special3": { "type" : "ballistaYard", "requires" : [ "blacksmith" ] },
|
||||
"special2": { "requires" : [ "marketplace" ], "marketModes" : ["creature-resource"] },
|
||||
"special3": { "warMachine" : "ballista", "requires" : [ "blacksmith" ] },
|
||||
"special4": {
|
||||
"requires" : [ "fort" ],
|
||||
"configuration" : {
|
||||
|
@ -147,7 +147,6 @@
|
||||
"horde" : [ 1, -1 ],
|
||||
"primaryResource" : "gems",
|
||||
"mageGuild" : 5,
|
||||
"warMachine" : "ammoCart",
|
||||
"moatAbility" : "towerMoat",
|
||||
|
||||
"buildings" :
|
||||
@ -167,9 +166,9 @@
|
||||
"capitol": { },
|
||||
"marketplace": { },
|
||||
"resourceSilo": { "produce" : { "gems": 1 } },
|
||||
"blacksmith": { },
|
||||
"blacksmith": { "warMachine" : "ammoCart" },
|
||||
|
||||
"special1": { "type" : "artifactMerchant", "requires" : [ "marketplace" ] },
|
||||
"special1": { "requires" : [ "marketplace" ], "marketModes" : ["resource-artifact", "artifact-resource"] },
|
||||
"horde1": { "id" : 18, "upgrades" : "dwellingLvl2" },
|
||||
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl2", "requires" : [ "horde1" ], "mode" : "auto" },
|
||||
"special2": { "height" : "high", "requires" : [ "fort" ] },
|
||||
|
@ -89,7 +89,7 @@
|
||||
"additionalProperties" : false,
|
||||
"required" : [
|
||||
"mapObject", "buildingsIcons", "buildings", "creatures", "guildWindow", "names",
|
||||
"hallBackground", "hallSlots", "horde", "mageGuild", "moatAbility", "defaultTavern", "tavernVideo", "guildBackground", "musicTheme", "siege", "structures", "townBackground", "warMachine"
|
||||
"hallBackground", "hallSlots", "horde", "mageGuild", "moatAbility", "defaultTavern", "tavernVideo", "guildBackground", "musicTheme", "siege", "structures", "townBackground"
|
||||
],
|
||||
"description" : "town",
|
||||
"properties" : {
|
||||
@ -133,10 +133,6 @@
|
||||
"type" : "string",
|
||||
"description" : "Primary resource for this town. Produced by Silo and offered as starting bonus"
|
||||
},
|
||||
"warMachine" : {
|
||||
"type" : "string",
|
||||
"description" : "Identifier of war machine produced by blacksmith in town"
|
||||
},
|
||||
"horde" : {
|
||||
"type" : "array",
|
||||
"maxItems" : 2,
|
||||
|
@ -36,7 +36,7 @@
|
||||
},
|
||||
"type" : {
|
||||
"type" : "string",
|
||||
"enum" : [ "mysticPond", "artifactMerchant", "freelancersGuild", "magicUniversity", "castleGate", "creatureTransformer", "portalOfSummoning", "ballistaYard", "library", "escapeTunnel", "treasury", "thievesGuild", "bank" ],
|
||||
"enum" : [ "mysticPond", "castleGate", "portalOfSummoning", "library", "escapeTunnel", "treasury", "bank" ],
|
||||
"description" : "Subtype for some special buildings"
|
||||
},
|
||||
"mode" : {
|
||||
@ -93,10 +93,22 @@
|
||||
"gems" : { "type" : "number"}
|
||||
}
|
||||
},
|
||||
"warMachine" : {
|
||||
"type" : "string",
|
||||
"description" : "Artifact ID of a war machine that can be purchased in this building, if any"
|
||||
},
|
||||
"bonuses" : {
|
||||
"type" : "array",
|
||||
"description" : "Bonuses that are provided by this building in any town where this building has been built. Only affects town itself (including siege), to propagate effect to player or team please use bonus propagators",
|
||||
"items" : { "$ref" : "bonus.json" }
|
||||
},
|
||||
"marketModes" : {
|
||||
"type" : "array",
|
||||
"items" : {
|
||||
"type" : "string",
|
||||
"enum" : [ "resource-resource", "resource-player", "creature-resource", "resource-artifact", "artifact-resource", "artifact-experience", "creature-experience", "creature-undead", "resource-skill"],
|
||||
},
|
||||
"description" : "List of modes available in this market"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
6
debian/changelog
vendored
6
debian/changelog
vendored
@ -4,6 +4,12 @@ vcmi (1.6.0) jammy; urgency=medium
|
||||
|
||||
-- Ivan Savenko <saven.ivan@gmail.com> Fri, 30 Aug 2024 12:00:00 +0200
|
||||
|
||||
vcmi (1.5.7) jammy; urgency=medium
|
||||
|
||||
* New upstream release
|
||||
|
||||
-- Ivan Savenko <saven.ivan@gmail.com> Mon, 26 Aug 2024 12:00:00 +0200
|
||||
|
||||
vcmi (1.5.6) jammy; urgency=medium
|
||||
|
||||
* New upstream release
|
||||
|
@ -1,7 +1,7 @@
|
||||
[](https://github.com/vcmi/vcmi/actions/workflows/github.yml?query=branch%3Adevelop+event%3Apush)
|
||||
[](https://github.com/vcmi/vcmi/releases/tag/1.5.0)
|
||||
[](https://github.com/vcmi/vcmi/releases/tag/1.5.5)
|
||||
[](https://github.com/vcmi/vcmi/releases/tag/1.5.6)
|
||||
[](https://github.com/vcmi/vcmi/releases/tag/1.5.7)
|
||||
[](https://github.com/vcmi/vcmi/releases)
|
||||
|
||||
# VCMI Project
|
||||
|
@ -1010,11 +1010,16 @@ Dummy bonus that acts as marker for Dendroid's Bind ability
|
||||
|
||||
Dummy skill for alternative upgrades mod
|
||||
|
||||
### TOWN_MAGIC_WELL
|
||||
### THIEVES_GUILD_ACCESS
|
||||
|
||||
Internal bonus, do not use
|
||||
Increases amount of information available in affected thieves guild (in town or in adventure map tavern). Does not affects adventure map object "Den of Thieves". You may want to use PLAYER_PROPAGATOR with this bonus to make its effect player wide.
|
||||
|
||||
- val: additional number of 'levels' of information to grant access to
|
||||
|
||||
### LEVEL_COUNTER
|
||||
|
||||
Internal bonus, do not use
|
||||
|
||||
### DISINTEGRATE
|
||||
|
||||
After death of unit no corpse remains
|
||||
|
@ -247,9 +247,6 @@ Each town requires a set of buildings (Around 30-45 buildings)
|
||||
// maximum level of mage guild
|
||||
"mageGuild" : 4,
|
||||
|
||||
// war machine produced in town
|
||||
"warMachine" : "ballista"
|
||||
|
||||
// Identifier of spell that will create effects for town moat during siege
|
||||
"moatAbility" : "castleMoat"
|
||||
}
|
||||
|
@ -136,6 +136,9 @@ These are just a couple of examples of what can be done in VCMI. See vcmi config
|
||||
"gold" : 10000
|
||||
},
|
||||
|
||||
// Artifact ID of a war machine produced in this town building, if any
|
||||
"warMachine" : "ballista",
|
||||
|
||||
// Allows to define additional functionality of this building, usually using logic of one of original H3 town building
|
||||
// Generally only needs to be specified for "special" buildings
|
||||
// See 'List of unique town buildings' section below for detailed description of this field
|
||||
@ -171,6 +174,9 @@ These are just a couple of examples of what can be done in VCMI. See vcmi config
|
||||
|
||||
// If set to true, this building will replace all bonuses from base building, leaving only bonuses defined by this building"
|
||||
"upgradeReplacesBonuses" : false,
|
||||
|
||||
// If the building is a market, it requires market mode.
|
||||
"marketModes" : [ "resource-resource", "resource-player" ],
|
||||
}
|
||||
```
|
||||
|
||||
@ -204,20 +210,17 @@ Following Heroes III buildings can be used as unique buildings for a town. Their
|
||||
- `castleGate`
|
||||
- `creatureTransformer`
|
||||
- `portalOfSummoning`
|
||||
- `ballistaYard`
|
||||
- `library`
|
||||
- `escapeTunnel`
|
||||
- `treasury`
|
||||
|
||||
#### Buildings from other Heroes III mods
|
||||
Following HotA buildings can be used as unique building for a town. Functionality should match corresponding HotA building:
|
||||
- `thievesGuild`
|
||||
- `bank`
|
||||
|
||||
#### Custom buildings
|
||||
In addition to above, it is possible to use same format as [Rewardable](../Map_Objects/Rewardable.md) map objects for town buildings. In order to do that, configuration of a rewardable object must be placed into `configuration` json node in building config.
|
||||
|
||||
```
|
||||
|
||||
### Town Structure node
|
||||
|
||||
@ -248,3 +251,17 @@ In addition to above, it is possible to use same format as [Rewardable](../Map_O
|
||||
"hidden" : false
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### Markets in towns
|
||||
Market buildings require list of available [modes](../Map_Objects/Market.md)
|
||||
|
||||
##### Marketplace
|
||||
```jsonc
|
||||
"marketplace": { "marketModes" : ["resource-resource", "resource-player"] },
|
||||
```
|
||||
|
||||
##### Artifact merchant
|
||||
```jsonc
|
||||
"special1": { "type" : "artifactMerchant", "requires" : [ "marketplace" ], "marketModes" : ["resource-artifact", "artifact-resource"] },
|
||||
```
|
@ -91,6 +91,7 @@
|
||||
<launchable type="desktop-id">vcmilauncher.desktop</launchable>
|
||||
<releases>
|
||||
<release version="1.6.0" date="2024-08-30" type="development"/>
|
||||
<release version="1.5.7" date="2024-08-26" type="stable"/>
|
||||
<release version="1.5.6" date="2024-08-04" type="stable"/>
|
||||
<release version="1.5.5" date="2024-07-17" type="stable"/>
|
||||
<release version="1.5.4" date="2024-07-12" type="stable"/>
|
||||
|
@ -460,7 +460,7 @@ std::shared_ptr<CArtifact> CArtHandler::loadFromJson(const std::string & scope,
|
||||
}
|
||||
|
||||
const JsonNode & warMachine = node["warMachine"];
|
||||
if(warMachine.getType() == JsonNode::JsonType::DATA_STRING && !warMachine.String().empty())
|
||||
if(!warMachine.isNull())
|
||||
{
|
||||
VLC->identifiers()->requestIdentifier("creature", warMachine, [=](si32 id)
|
||||
{
|
||||
|
@ -174,6 +174,15 @@ const CGTownInstance* CGameInfoCallback::getTown(ObjectInstanceID objid) const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const IMarket * CGameInfoCallback::getMarket(ObjectInstanceID objid) const
|
||||
{
|
||||
const CGObjectInstance * obj = getObj(objid, false);
|
||||
if(obj)
|
||||
return dynamic_cast<const IMarket*>(obj);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CGameInfoCallback::fillUpgradeInfo(const CArmedInstance *obj, SlotID stackPos, UpgradeInfo &out) const
|
||||
{
|
||||
//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
|
||||
@ -227,15 +236,7 @@ void CGameInfoCallback::getThievesGuildInfo(SThievesGuildInfo & thi, const CGObj
|
||||
|
||||
if(obj->ID == Obj::TOWN || obj->ID == Obj::TAVERN)
|
||||
{
|
||||
int taverns = 0;
|
||||
for(auto town : gs->players[*getPlayerID()].towns)
|
||||
{
|
||||
if(town->hasBuilt(BuildingID::TAVERN))
|
||||
taverns++;
|
||||
|
||||
if(town->hasBuilt(BuildingSubID::THIEVES_GUILD))
|
||||
taverns += 2;
|
||||
}
|
||||
int taverns = gs->players[*getPlayerID()].valOfBonuses(BonusType::THIEVES_GUILD_ACCESS);
|
||||
gs->obtainPlayersStats(thi, taverns);
|
||||
}
|
||||
else if(obj->ID == Obj::DEN_OF_THIEVES)
|
||||
@ -247,7 +248,7 @@ void CGameInfoCallback::getThievesGuildInfo(SThievesGuildInfo & thi, const CGObj
|
||||
int CGameInfoCallback::howManyTowns(PlayerColor Player) const
|
||||
{
|
||||
ERROR_RET_VAL_IF(!hasAccess(Player), "Access forbidden!", -1);
|
||||
return static_cast<int>(gs->players[Player].towns.size());
|
||||
return static_cast<int>(gs->players[Player].getTowns().size());
|
||||
}
|
||||
|
||||
bool CGameInfoCallback::getTownInfo(const CGObjectInstance * town, InfoAboutTown & dest, const CGObjectInstance * selectedObject) const
|
||||
@ -600,7 +601,7 @@ EBuildingState CGameInfoCallback::canBuildStructure( const CGTownInstance *t, Bu
|
||||
const PlayerState *ps = getPlayerState(t->tempOwner, false);
|
||||
if(ps)
|
||||
{
|
||||
for(const CGTownInstance *town : ps->towns)
|
||||
for(const CGTownInstance *town : ps->getTowns())
|
||||
{
|
||||
if(town->hasBuilt(BuildingID::CAPITOL))
|
||||
{
|
||||
@ -702,9 +703,9 @@ int CGameInfoCallback::getHeroCount( PlayerColor player, bool includeGarrisoned
|
||||
ERROR_RET_VAL_IF(!p, "No such player!", -1);
|
||||
|
||||
if(includeGarrisoned)
|
||||
return static_cast<int>(p->heroes.size());
|
||||
return static_cast<int>(p->getHeroes().size());
|
||||
else
|
||||
for(const auto & elem : p->heroes)
|
||||
for(const auto & elem : p->getHeroes())
|
||||
if(!elem->inTownGarrison)
|
||||
ret++;
|
||||
return ret;
|
||||
@ -748,7 +749,7 @@ std::vector < const CGTownInstance *> CPlayerSpecificInfoCallback::getTownsInfo(
|
||||
auto ret = std::vector < const CGTownInstance *>();
|
||||
for(const auto & i : gs->players)
|
||||
{
|
||||
for(const auto & town : i.second.towns)
|
||||
for(const auto & town : i.second.getTowns())
|
||||
{
|
||||
if(i.first == getPlayerID() || (!onlyOur && isVisible(town, getPlayerID())))
|
||||
{
|
||||
@ -780,7 +781,7 @@ int CPlayerSpecificInfoCallback::getHeroSerial(const CGHeroInstance * hero, bool
|
||||
return -1;
|
||||
|
||||
size_t index = 0;
|
||||
auto & heroes = gs->players[*getPlayerID()].heroes;
|
||||
const auto & heroes = gs->players[*getPlayerID()].getHeroes();
|
||||
|
||||
for (auto & possibleHero : heroes)
|
||||
{
|
||||
@ -813,34 +814,12 @@ int3 CPlayerSpecificInfoCallback::getGrailPos( double *outKnownRatio )
|
||||
|
||||
std::vector < const CGObjectInstance * > CPlayerSpecificInfoCallback::getMyObjects() const
|
||||
{
|
||||
std::vector < const CGObjectInstance * > ret;
|
||||
for(const CGObjectInstance * obj : gs->map->objects)
|
||||
{
|
||||
if(obj && obj->tempOwner == getPlayerID())
|
||||
ret.push_back(obj);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector < const CGDwelling * > CPlayerSpecificInfoCallback::getMyDwellings() const
|
||||
{
|
||||
ASSERT_IF_CALLED_WITH_PLAYER
|
||||
std::vector < const CGDwelling * > ret;
|
||||
for(CGDwelling * dw : gs->getPlayerState(*getPlayerID())->dwellings)
|
||||
{
|
||||
ret.push_back(dw);
|
||||
}
|
||||
return ret;
|
||||
return gs->getPlayerState(*getPlayerID())->getOwnedObjects();
|
||||
}
|
||||
|
||||
std::vector <QuestInfo> CPlayerSpecificInfoCallback::getMyQuests() const
|
||||
{
|
||||
std::vector <QuestInfo> ret;
|
||||
for(const auto & quest : gs->getPlayerState(*getPlayerID())->quests)
|
||||
{
|
||||
ret.push_back (quest);
|
||||
}
|
||||
return ret;
|
||||
return gs->getPlayerState(*getPlayerID())->quests;
|
||||
}
|
||||
|
||||
int CPlayerSpecificInfoCallback::howManyHeroes(bool includeGarrisoned) const
|
||||
@ -858,12 +837,12 @@ const CGHeroInstance* CPlayerSpecificInfoCallback::getHeroBySerial(int serialId,
|
||||
|
||||
if (!includeGarrisoned)
|
||||
{
|
||||
for(ui32 i = 0; i < p->heroes.size() && static_cast<int>(i) <= serialId; i++)
|
||||
if(p->heroes[i]->inTownGarrison)
|
||||
for(ui32 i = 0; i < p->getHeroes().size() && static_cast<int>(i) <= serialId; i++)
|
||||
if(p->getHeroes()[i]->inTownGarrison)
|
||||
serialId++;
|
||||
}
|
||||
ERROR_RET_VAL_IF(serialId < 0 || serialId >= p->heroes.size(), "No player info", nullptr);
|
||||
return p->heroes[serialId];
|
||||
ERROR_RET_VAL_IF(serialId < 0 || serialId >= p->getHeroes().size(), "No player info", nullptr);
|
||||
return p->getHeroes()[serialId];
|
||||
}
|
||||
|
||||
const CGTownInstance* CPlayerSpecificInfoCallback::getTownBySerial(int serialId) const
|
||||
@ -871,8 +850,8 @@ const CGTownInstance* CPlayerSpecificInfoCallback::getTownBySerial(int serialId)
|
||||
ASSERT_IF_CALLED_WITH_PLAYER
|
||||
const PlayerState *p = getPlayerState(*getPlayerID());
|
||||
ERROR_RET_VAL_IF(!p, "No player info", nullptr);
|
||||
ERROR_RET_VAL_IF(serialId < 0 || serialId >= p->towns.size(), "No player info", nullptr);
|
||||
return p->towns[serialId];
|
||||
ERROR_RET_VAL_IF(serialId < 0 || serialId >= p->getTowns().size(), "No player info", nullptr);
|
||||
return p->getTowns()[serialId];
|
||||
}
|
||||
|
||||
int CPlayerSpecificInfoCallback::getResourceAmount(GameResID type) const
|
||||
|
@ -23,7 +23,7 @@ struct InfoWindow;
|
||||
struct PlayerSettings;
|
||||
struct CPackForClient;
|
||||
struct TerrainTile;
|
||||
struct PlayerState;
|
||||
class PlayerState;
|
||||
class CTown;
|
||||
struct StartInfo;
|
||||
struct CPathsInfo;
|
||||
@ -48,6 +48,7 @@ class CGHeroInstance;
|
||||
class CGDwelling;
|
||||
class CGTeleport;
|
||||
class CGTownInstance;
|
||||
class IMarket;
|
||||
|
||||
class DLL_LINKAGE IGameInfoCallback : boost::noncopyable
|
||||
{
|
||||
@ -189,6 +190,7 @@ public:
|
||||
virtual std::vector <const CGObjectInstance * > getFlaggableObjects(int3 pos) const;
|
||||
virtual const CGObjectInstance * getTopObj (int3 pos) const;
|
||||
virtual PlayerColor getOwner(ObjectInstanceID heroID) const;
|
||||
virtual const IMarket * getMarket(ObjectInstanceID objid) const;
|
||||
|
||||
//map
|
||||
virtual int3 guardingCreaturePosition (int3 pos) const;
|
||||
@ -245,7 +247,6 @@ public:
|
||||
virtual const CGTownInstance* getTownBySerial(int serialId) const; // serial id is [0, number of towns)
|
||||
virtual const CGHeroInstance* getHeroBySerial(int serialId, bool includeGarrisoned=true) const; // serial id is [0, number of heroes)
|
||||
virtual std::vector <const CGHeroInstance *> getHeroesInfo(bool onlyOur = true) const; //true -> only owned; false -> all visible
|
||||
virtual std::vector <const CGDwelling *> getMyDwellings() const; //returns all dwellings that belong to player
|
||||
virtual std::vector <const CGObjectInstance * > getMyObjects() const; //returns all objects flagged by belonging player
|
||||
virtual std::vector <QuestInfo> getMyQuests() const;
|
||||
|
||||
|
@ -13,9 +13,6 @@
|
||||
#include "CStack.h"
|
||||
#include "VCMIDirs.h"
|
||||
|
||||
#include "serializer/BinaryDeserializer.h"
|
||||
#include "serializer/BinarySerializer.h"
|
||||
|
||||
#ifdef STATIC_AI
|
||||
# include "AI/VCAI/VCAI.h"
|
||||
# include "AI/Nullkiller/AIGateway.h"
|
||||
|
@ -53,8 +53,6 @@ class CStack;
|
||||
class CCreature;
|
||||
class CLoadFile;
|
||||
class CSaveFile;
|
||||
class BinaryDeserializer;
|
||||
class BinarySerializer;
|
||||
class BattleStateInfo;
|
||||
struct ArtifactLocation;
|
||||
class BattleStateInfoForRetreat;
|
||||
|
@ -217,6 +217,7 @@ set(lib_MAIN_SRCS
|
||||
serializer/JsonSerializeFormat.cpp
|
||||
serializer/JsonSerializer.cpp
|
||||
serializer/JsonUpdater.cpp
|
||||
serializer/SerializerReflection.cpp
|
||||
|
||||
spells/AbilityCaster.cpp
|
||||
spells/AdventureSpellMechanics.cpp
|
||||
@ -503,6 +504,7 @@ set(lib_MAIN_HEADERS
|
||||
mapObjects/CRewardableObject.h
|
||||
mapObjects/IMarket.h
|
||||
mapObjects/IObjectInterface.h
|
||||
mapObjects/IOwnableObject.h
|
||||
mapObjects/MapObjects.h
|
||||
mapObjects/MiscObjects.h
|
||||
mapObjects/ObjectTemplate.h
|
||||
@ -563,12 +565,6 @@ set(lib_MAIN_HEADERS
|
||||
pathfinder/PathfindingRules.h
|
||||
pathfinder/TurnInfo.h
|
||||
|
||||
registerTypes/RegisterTypes.h
|
||||
registerTypes/RegisterTypesClientPacks.h
|
||||
registerTypes/RegisterTypesLobbyPacks.h
|
||||
registerTypes/RegisterTypesMapObjects.h
|
||||
registerTypes/RegisterTypesServerPacks.h
|
||||
|
||||
rewardable/Configuration.h
|
||||
rewardable/Info.h
|
||||
rewardable/Interface.h
|
||||
@ -625,7 +621,9 @@ set(lib_MAIN_HEADERS
|
||||
serializer/JsonUpdater.h
|
||||
serializer/Cast.h
|
||||
serializer/ESerializationVersion.h
|
||||
serializer/RegisterTypes.h
|
||||
serializer/Serializeable.h
|
||||
serializer/SerializerReflection.h
|
||||
|
||||
spells/AbilityCaster.h
|
||||
spells/AdventureSpellMechanics.h
|
||||
|
@ -10,6 +10,9 @@
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "CPlayerState.h"
|
||||
#include "mapObjects/CGDwelling.h"
|
||||
#include "mapObjects/CGTownInstance.h"
|
||||
#include "mapObjects/CGHeroInstance.h"
|
||||
#include "gameState/QuestInfo.h"
|
||||
#include "texts/CGeneralTextHandler.h"
|
||||
#include "VCMI_Lib.h"
|
||||
@ -90,4 +93,54 @@ int PlayerState::getResourceAmount(int type) const
|
||||
return vstd::atOrDefault(resources, static_cast<size_t>(type), 0);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::vector<T> PlayerState::getObjectsOfType() const
|
||||
{
|
||||
std::vector<T> result;
|
||||
for (auto const & object : ownedObjects)
|
||||
{
|
||||
auto casted = dynamic_cast<T>(object);
|
||||
if (casted)
|
||||
result.push_back(casted);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<const CGHeroInstance *> PlayerState::getHeroes() const
|
||||
{
|
||||
return getObjectsOfType<const CGHeroInstance *>();
|
||||
}
|
||||
|
||||
std::vector<const CGTownInstance *> PlayerState::getTowns() const
|
||||
{
|
||||
return getObjectsOfType<const CGTownInstance *>();
|
||||
}
|
||||
|
||||
std::vector<CGHeroInstance *> PlayerState::getHeroes()
|
||||
{
|
||||
return getObjectsOfType<CGHeroInstance *>();
|
||||
}
|
||||
|
||||
std::vector<CGTownInstance *> PlayerState::getTowns()
|
||||
{
|
||||
return getObjectsOfType<CGTownInstance *>();
|
||||
}
|
||||
|
||||
std::vector<const CGObjectInstance *> PlayerState::getOwnedObjects() const
|
||||
{
|
||||
return {ownedObjects.begin(), ownedObjects.end()};
|
||||
}
|
||||
|
||||
void PlayerState::addOwnedObject(CGObjectInstance * object)
|
||||
{
|
||||
assert(object->asOwnable() != nullptr);
|
||||
ownedObjects.push_back(object);
|
||||
}
|
||||
|
||||
void PlayerState::removeOwnedObject(CGObjectInstance * object)
|
||||
{
|
||||
vstd::erase(ownedObjects, object);
|
||||
}
|
||||
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@ -20,12 +20,13 @@
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class CGObjectInstance;
|
||||
class CGHeroInstance;
|
||||
class CGTownInstance;
|
||||
class CGDwelling;
|
||||
struct QuestInfo;
|
||||
|
||||
struct DLL_LINKAGE PlayerState : public CBonusSystemNode, public Player
|
||||
class DLL_LINKAGE PlayerState : public CBonusSystemNode, public Player
|
||||
{
|
||||
struct VisitedObjectGlobal
|
||||
{
|
||||
@ -47,6 +48,11 @@ struct DLL_LINKAGE PlayerState : public CBonusSystemNode, public Player
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<CGObjectInstance*> ownedObjects;
|
||||
|
||||
template<typename T>
|
||||
std::vector<T> getObjectsOfType() const;
|
||||
|
||||
public:
|
||||
PlayerColor color;
|
||||
bool human; //true if human controlled player, false for AI
|
||||
@ -55,12 +61,8 @@ public:
|
||||
|
||||
/// list of objects that were "destroyed" by player, either via simple pick-up (e.g. resources) or defeated heroes or wandering monsters
|
||||
std::set<ObjectInstanceID> destroyedObjects;
|
||||
|
||||
std::set<ObjectInstanceID> visitedObjects; // as a std::set, since most accesses here will be from visited status checks
|
||||
std::set<VisitedObjectGlobal> visitedObjectsGlobal;
|
||||
std::vector<ConstTransitivePtr<CGHeroInstance> > heroes;
|
||||
std::vector<ConstTransitivePtr<CGTownInstance> > towns;
|
||||
std::vector<ConstTransitivePtr<CGDwelling> > dwellings; //used for town growth
|
||||
std::vector<QuestInfo> quests; //store info about all received quests
|
||||
std::vector<Bonus> battleBonuses; //additional bonuses to be added during battle with neutrals
|
||||
std::map<uint32_t, std::map<ArtifactPosition, ArtifactID>> costumesArtifacts;
|
||||
@ -90,9 +92,19 @@ public:
|
||||
std::string getNameTextID() const override;
|
||||
void registerIcons(const IconRegistar & cb) const override;
|
||||
|
||||
std::vector<const CGHeroInstance* > getHeroes() const;
|
||||
std::vector<const CGTownInstance* > getTowns() const;
|
||||
std::vector<CGHeroInstance* > getHeroes();
|
||||
std::vector<CGTownInstance* > getTowns();
|
||||
|
||||
std::vector<const CGObjectInstance* > getOwnedObjects() const;
|
||||
|
||||
void addOwnedObject(CGObjectInstance * object);
|
||||
void removeOwnedObject(CGObjectInstance * object);
|
||||
|
||||
bool checkVanquished() const
|
||||
{
|
||||
return heroes.empty() && towns.empty();
|
||||
return ownedObjects.empty();
|
||||
}
|
||||
|
||||
template <typename Handler> void serialize(Handler &h)
|
||||
@ -103,9 +115,21 @@ public:
|
||||
h & resources;
|
||||
h & status;
|
||||
h & turnTimer;
|
||||
h & heroes;
|
||||
h & towns;
|
||||
h & dwellings;
|
||||
|
||||
if (h.version >= Handler::Version::PLAYER_STATE_OWNED_OBJECTS)
|
||||
{
|
||||
h & ownedObjects;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<const CGObjectInstance* > heroes;
|
||||
std::vector<const CGObjectInstance* > towns;
|
||||
std::vector<const CGObjectInstance* > dwellings;
|
||||
|
||||
h & heroes;
|
||||
h & towns;
|
||||
h & dwellings;
|
||||
}
|
||||
h & quests;
|
||||
h & visitedObjects;
|
||||
h & visitedObjectsGlobal;
|
||||
|
@ -180,8 +180,7 @@ CGameState * CPrivilegedInfoCallback::gameState()
|
||||
return gs;
|
||||
}
|
||||
|
||||
template<typename Loader>
|
||||
void CPrivilegedInfoCallback::loadCommonState(Loader & in)
|
||||
void CPrivilegedInfoCallback::loadCommonState(CLoadFile & in)
|
||||
{
|
||||
logGlobal->info("Loading lib part of game...");
|
||||
in.checkMagicBytes(SAVEGAME_MAGIC);
|
||||
@ -203,8 +202,7 @@ void CPrivilegedInfoCallback::loadCommonState(Loader & in)
|
||||
in.serializer & gs;
|
||||
}
|
||||
|
||||
template<typename Saver>
|
||||
void CPrivilegedInfoCallback::saveCommonState(Saver & out) const
|
||||
void CPrivilegedInfoCallback::saveCommonState(CSaveFile & out) const
|
||||
{
|
||||
ActiveModsInSaveList activeMods;
|
||||
|
||||
@ -220,10 +218,6 @@ void CPrivilegedInfoCallback::saveCommonState(Saver & out) const
|
||||
out.serializer & gs;
|
||||
}
|
||||
|
||||
// hardly memory usage for `-gdwarf-4` flag
|
||||
template DLL_LINKAGE void CPrivilegedInfoCallback::loadCommonState<CLoadFile>(CLoadFile &);
|
||||
template DLL_LINKAGE void CPrivilegedInfoCallback::saveCommonState<CSaveFile>(CSaveFile &) const;
|
||||
|
||||
TerrainTile * CNonConstInfoCallback::getTile(const int3 & pos)
|
||||
{
|
||||
if(!gs->map->isInTheMap(pos))
|
||||
@ -287,18 +281,16 @@ CArtifactSet * CNonConstInfoCallback::getArtSet(const ArtifactLocation & loc)
|
||||
return hero;
|
||||
}
|
||||
}
|
||||
else if(auto market = getMarket(loc.artHolder))
|
||||
{
|
||||
if(auto artSet = market->getArtifactsStorage())
|
||||
return artSet;
|
||||
}
|
||||
else if(auto army = getArmyInstance(loc.artHolder))
|
||||
{
|
||||
return army->getStackPtr(loc.creature.value());
|
||||
}
|
||||
else if(auto market = dynamic_cast<CGArtifactsAltar*>(getObjInstance(loc.artHolder)))
|
||||
{
|
||||
return market;
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool IGameCallback::isVisitCoveredByAnotherQuery(const CGObjectInstance *obj, const CGHeroInstance *hero)
|
||||
|
@ -31,6 +31,8 @@ struct BankConfig;
|
||||
class CCreatureSet;
|
||||
class CStackBasicDescriptor;
|
||||
class CGCreature;
|
||||
class CSaveFile;
|
||||
class CLoadFile;
|
||||
enum class EOpenWindowMode : uint8_t;
|
||||
|
||||
namespace spells
|
||||
@ -74,11 +76,8 @@ public:
|
||||
void pickAllowedArtsSet(std::vector<const CArtifact *> & out, vstd::RNG & rand);
|
||||
void getAllowedSpells(std::vector<SpellID> &out, std::optional<ui16> level = std::nullopt);
|
||||
|
||||
template<typename Saver>
|
||||
void saveCommonState(Saver &out) const; //stores GS and VLC
|
||||
|
||||
template<typename Loader>
|
||||
void loadCommonState(Loader &in); //loads GS and VLC
|
||||
void saveCommonState(CSaveFile &out) const; //stores GS and VLC
|
||||
void loadCommonState(CLoadFile &in); //loads GS and VLC
|
||||
};
|
||||
|
||||
class DLL_LINKAGE IGameEventCallback
|
||||
@ -141,7 +140,7 @@ public:
|
||||
virtual void sendAndApply(CPackForClient * pack) = 0;
|
||||
virtual void heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2)=0; //when two heroes meet on adventure map
|
||||
virtual void changeFogOfWar(int3 center, ui32 radius, PlayerColor player, ETileVisibility mode) = 0;
|
||||
virtual void changeFogOfWar(std::unordered_set<int3> &tiles, PlayerColor player, ETileVisibility mode) = 0;
|
||||
virtual void changeFogOfWar(const std::unordered_set<int3> &tiles, PlayerColor player, ETileVisibility mode) = 0;
|
||||
|
||||
virtual void castSpell(const spells::Caster * caster, SpellID spellID, const int3 &pos) = 0;
|
||||
|
||||
|
@ -109,7 +109,7 @@ public:
|
||||
|
||||
virtual void showPuzzleMap(){};
|
||||
virtual void viewWorldMap(){};
|
||||
virtual void showMarketWindow(const IMarket *market, const CGHeroInstance *visitor, QueryID queryID){};
|
||||
virtual void showMarketWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID){};
|
||||
virtual void showUniversityWindow(const IMarket *market, const CGHeroInstance *visitor, QueryID queryID){};
|
||||
virtual void showHillFortWindow(const CGObjectInstance *object, const CGHeroInstance *visitor){};
|
||||
virtual void showThievesGuildWindow (const CGObjectInstance * obj){};
|
||||
|
@ -64,6 +64,12 @@ void ResourceSet::positive()
|
||||
vstd::amax(elem, 0);
|
||||
}
|
||||
|
||||
void ResourceSet::applyHandicap(int percentage)
|
||||
{
|
||||
for(auto & elem : *this)
|
||||
elem = vstd::divideAndCeil(elem * percentage, 100);
|
||||
}
|
||||
|
||||
static bool canAfford(const ResourceSet &res, const ResourceSet &price)
|
||||
{
|
||||
assert(res.size() == price.size() && price.size() == GameConstants::RESOURCE_QUANTITY);
|
||||
|
@ -210,6 +210,7 @@ public:
|
||||
DLL_LINKAGE void amax(const TResourceCap &val); //performs vstd::amax on each element
|
||||
DLL_LINKAGE void amin(const TResourceCap &val); //performs vstd::amin on each element
|
||||
DLL_LINKAGE void positive(); //values below 0 are set to 0 - upgrade cost can't be negative, for example
|
||||
DLL_LINKAGE void applyHandicap(int percentage);
|
||||
DLL_LINKAGE bool nonZero() const; //returns true if at least one value is non-zero;
|
||||
DLL_LINKAGE bool canAfford(const ResourceSet &price) const;
|
||||
DLL_LINKAGE bool canBeAfforded(const ResourceSet &res) const;
|
||||
|
@ -1051,7 +1051,7 @@ ReachabilityInfo CBattleInfoCallback::makeBFS(const AccessibilityInfo &accessibi
|
||||
if(isInObstacle(curHex, obstacles, checkParams))
|
||||
continue;
|
||||
|
||||
const int costToNeighbour = ret.distances[curHex.hex] + 1;
|
||||
const int costToNeighbour = ret.distances.at(curHex.hex) + 1;
|
||||
|
||||
for(BattleHex neighbour : BattleHex::neighbouringTilesCache[curHex.hex])
|
||||
{
|
||||
|
@ -831,7 +831,8 @@ void CUnitState::damage(int64_t & amount)
|
||||
health.damage(amount);
|
||||
}
|
||||
|
||||
if(health.available() <= 0 && (cloned || summoned))
|
||||
bool disintegrate = hasBonusOfType(BonusType::DISINTEGRATE);
|
||||
if(health.available() <= 0 && (cloned || summoned || disintegrate))
|
||||
ghostPending = true;
|
||||
}
|
||||
|
||||
@ -900,9 +901,9 @@ CUnitStateDetached::CUnitStateDetached(const IUnitInfo * unit_, const IBonusBear
|
||||
{
|
||||
}
|
||||
|
||||
TConstBonusListPtr CUnitStateDetached::getAllBonuses(const CSelector & selector, const CSelector & limit, const CBonusSystemNode * root, const std::string & cachingStr) const
|
||||
TConstBonusListPtr CUnitStateDetached::getAllBonuses(const CSelector & selector, const CSelector & limit, const std::string & cachingStr) const
|
||||
{
|
||||
return bonus->getAllBonuses(selector, limit, root, cachingStr);
|
||||
return bonus->getAllBonuses(selector, limit, cachingStr);
|
||||
}
|
||||
|
||||
int64_t CUnitStateDetached::getTreeVersion() const
|
||||
|
@ -282,7 +282,7 @@ public:
|
||||
explicit CUnitStateDetached(const IUnitInfo * unit_, const IBonusBearer * bonus_);
|
||||
|
||||
TConstBonusListPtr getAllBonuses(const CSelector & selector, const CSelector & limit,
|
||||
const CBonusSystemNode * root = nullptr, const std::string & cachingStr = "") const override;
|
||||
const std::string & cachingStr = "") const override;
|
||||
|
||||
int64_t getTreeVersion() const override;
|
||||
|
||||
|
@ -150,7 +150,7 @@ class JsonNode;
|
||||
BONUS_NAME(GARGOYLE) /* gargoyle is special than NON_LIVING, cannot be rised or healed */ \
|
||||
BONUS_NAME(SPECIAL_ADD_VALUE_ENCHANT) /*specialty spell like Aenin has, increased effect of spell, additionalInfo = value to add*/\
|
||||
BONUS_NAME(SPECIAL_FIXED_VALUE_ENCHANT) /*specialty spell like Melody has, constant spell effect (i.e. 3 luck), additionalInfo = value to fix.*/\
|
||||
BONUS_NAME(TOWN_MAGIC_WELL) /*one-time pseudo-bonus to implement Magic Well in the town*/\
|
||||
BONUS_NAME(THIEVES_GUILD_ACCESS) \
|
||||
BONUS_NAME(LIMITED_SHOOTING_RANGE) /*limits range of shooting creatures, doesn't adjust any other mechanics (half vs full damage etc). val - range in hexes, additional info - optional new range for broken arrow mechanic */\
|
||||
BONUS_NAME(LEARN_BATTLE_SPELL_CHANCE) /*skill-agnostic eagle eye chance. subtype = 0 - from enemy, 1 - TODO: from entire battlefield*/\
|
||||
BONUS_NAME(LEARN_BATTLE_SPELL_LEVEL_LIMIT) /*skill-agnostic eagle eye limit, subtype - school (-1 for all), others TODO*/\
|
||||
@ -178,6 +178,7 @@ class JsonNode;
|
||||
BONUS_NAME(REVENGE) /*additional damage based on how many units in stack died - formula: sqrt((number of creatures at battle start + 1) * creature health) / (total health now + 1 creature health) - 1) * 100% */ \
|
||||
BONUS_NAME(RESOURCES_CONSTANT_BOOST) /*Bonus that does not account for propagation and gives extra resources per day. val - resource amount, subtype - resource type*/ \
|
||||
BONUS_NAME(RESOURCES_TOWN_MULTIPLYING_BOOST) /*Bonus that does not account for propagation and gives extra resources per day with amount multiplied by number of owned towns. val - base resource amount to be multiplied times number of owned towns, subtype - resource type*/ \
|
||||
BONUS_NAME(DISINTEGRATE) /* after death no corpse remains */ \
|
||||
/* end of list */
|
||||
|
||||
|
||||
|
@ -107,10 +107,9 @@ void CBonusSystemNode::getAllBonusesRec(BonusList &out, const CSelector & select
|
||||
}
|
||||
}
|
||||
|
||||
TConstBonusListPtr CBonusSystemNode::getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root, const std::string &cachingStr) const
|
||||
TConstBonusListPtr CBonusSystemNode::getAllBonuses(const CSelector &selector, const CSelector &limit, const std::string &cachingStr) const
|
||||
{
|
||||
bool limitOnUs = (!root || root == this); //caching won't work when we want to limit bonuses against an external node
|
||||
if (CBonusSystemNode::cachingEnabled && limitOnUs)
|
||||
if (CBonusSystemNode::cachingEnabled)
|
||||
{
|
||||
// Exclusive access for one thread
|
||||
boost::lock_guard<boost::mutex> lock(sync);
|
||||
@ -157,11 +156,11 @@ TConstBonusListPtr CBonusSystemNode::getAllBonuses(const CSelector &selector, co
|
||||
}
|
||||
else
|
||||
{
|
||||
return getAllBonusesWithoutCaching(selector, limit, root);
|
||||
return getAllBonusesWithoutCaching(selector, limit);
|
||||
}
|
||||
}
|
||||
|
||||
TConstBonusListPtr CBonusSystemNode::getAllBonusesWithoutCaching(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root) const
|
||||
TConstBonusListPtr CBonusSystemNode::getAllBonusesWithoutCaching(const CSelector &selector, const CSelector &limit) const
|
||||
{
|
||||
auto ret = std::make_shared<BonusList>();
|
||||
|
||||
@ -169,29 +168,7 @@ TConstBonusListPtr CBonusSystemNode::getAllBonusesWithoutCaching(const CSelector
|
||||
BonusList beforeLimiting;
|
||||
BonusList afterLimiting;
|
||||
getAllBonusesRec(beforeLimiting, selector);
|
||||
|
||||
if(!root || root == this)
|
||||
{
|
||||
limitBonuses(beforeLimiting, afterLimiting);
|
||||
}
|
||||
else if(root)
|
||||
{
|
||||
//We want to limit our query against an external node. We get all its bonuses,
|
||||
// add the ones we're considering and see if they're cut out by limiters
|
||||
BonusList rootBonuses;
|
||||
BonusList limitedRootBonuses;
|
||||
getAllBonusesRec(rootBonuses, selector);
|
||||
|
||||
for(const auto & b : beforeLimiting)
|
||||
rootBonuses.push_back(b);
|
||||
|
||||
root->limitBonuses(rootBonuses, limitedRootBonuses);
|
||||
|
||||
for(const auto & b : beforeLimiting)
|
||||
if(vstd::contains(limitedRootBonuses, b))
|
||||
afterLimiting.push_back(b);
|
||||
|
||||
}
|
||||
limitBonuses(beforeLimiting, afterLimiting);
|
||||
afterLimiting.getBonuses(*ret, selector, limit);
|
||||
ret->stackBonuses();
|
||||
return ret;
|
||||
|
@ -53,7 +53,7 @@ private:
|
||||
mutable boost::mutex sync;
|
||||
|
||||
void getAllBonusesRec(BonusList &out, const CSelector & selector) const;
|
||||
TConstBonusListPtr getAllBonusesWithoutCaching(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr) const;
|
||||
TConstBonusListPtr getAllBonusesWithoutCaching(const CSelector &selector, const CSelector &limit) const;
|
||||
std::shared_ptr<Bonus> getUpdatedBonus(const std::shared_ptr<Bonus> & b, const TUpdaterPtr & updater) const;
|
||||
|
||||
void getRedParents(TCNodes &out) const; //retrieves list of red parent nodes (nodes bonuses propagate from)
|
||||
@ -86,7 +86,7 @@ public:
|
||||
|
||||
void limitBonuses(const BonusList &allBonuses, BonusList &out) const; //out will bo populed with bonuses that are not limited here
|
||||
TBonusListPtr limitBonuses(const BonusList &allBonuses) const; //same as above, returns out by val for convenience
|
||||
TConstBonusListPtr getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr, const std::string &cachingStr = "") const override;
|
||||
TConstBonusListPtr getAllBonuses(const CSelector &selector, const CSelector &limit, const std::string &cachingStr = "") const override;
|
||||
void getParents(TCNodes &out) const; //retrieves list of parent nodes (nodes to inherit bonuses from),
|
||||
|
||||
/// Returns first bonus matching selector
|
||||
|
@ -17,7 +17,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
int IBonusBearer::valOfBonuses(const CSelector &selector, const std::string &cachingStr) const
|
||||
{
|
||||
TConstBonusListPtr hlp = getAllBonuses(selector, nullptr, nullptr, cachingStr);
|
||||
TConstBonusListPtr hlp = getAllBonuses(selector, nullptr, cachingStr);
|
||||
return hlp->totalValue();
|
||||
}
|
||||
|
||||
@ -34,12 +34,12 @@ bool IBonusBearer::hasBonus(const CSelector &selector, const CSelector &limit, c
|
||||
|
||||
TConstBonusListPtr IBonusBearer::getBonuses(const CSelector &selector, const std::string &cachingStr) const
|
||||
{
|
||||
return getAllBonuses(selector, nullptr, nullptr, cachingStr);
|
||||
return getAllBonuses(selector, nullptr, cachingStr);
|
||||
}
|
||||
|
||||
TConstBonusListPtr IBonusBearer::getBonuses(const CSelector &selector, const CSelector &limit, const std::string &cachingStr) const
|
||||
{
|
||||
return getAllBonuses(selector, limit, nullptr, cachingStr);
|
||||
return getAllBonuses(selector, limit, cachingStr);
|
||||
}
|
||||
|
||||
int IBonusBearer::valOfBonuses(BonusType type) const
|
||||
|
@ -22,7 +22,7 @@ public:
|
||||
//interface
|
||||
IBonusBearer() = default;
|
||||
virtual ~IBonusBearer() = default;
|
||||
virtual TConstBonusListPtr getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr, const std::string &cachingStr = "") const = 0;
|
||||
virtual TConstBonusListPtr getAllBonuses(const CSelector &selector, const CSelector &limit, const std::string &cachingStr = "") const = 0;
|
||||
int valOfBonuses(const CSelector &selector, const std::string &cachingStr = "") const;
|
||||
bool hasBonus(const CSelector &selector, const std::string &cachingStr = "") const;
|
||||
bool hasBonus(const CSelector &selector, const CSelector &limit, const std::string &cachingStr = "") const;
|
||||
|
@ -26,18 +26,11 @@ namespace BuildingSubID
|
||||
DEFAULT = -50,
|
||||
NONE = -1,
|
||||
CASTLE_GATE,
|
||||
CREATURE_TRANSFORMER,
|
||||
MYSTIC_POND,
|
||||
FOUNTAIN_OF_FORTUNE,
|
||||
ARTIFACT_MERCHANT,
|
||||
LIBRARY,
|
||||
PORTAL_OF_SUMMONING,
|
||||
ESCAPE_TUNNEL,
|
||||
FREELANCERS_GUILD,
|
||||
BALLISTA_YARD,
|
||||
MAGIC_UNIVERSITY,
|
||||
TREASURY,
|
||||
THIEVES_GUILD,
|
||||
BANK
|
||||
};
|
||||
}
|
||||
@ -256,4 +249,14 @@ enum class EMapLevel : int8_t
|
||||
UNDERGROUND = 1
|
||||
};
|
||||
|
||||
enum class EWeekType : int8_t
|
||||
{
|
||||
FIRST_WEEK,
|
||||
NORMAL,
|
||||
DOUBLE_GROWTH,
|
||||
BONUS_GROWTH,
|
||||
DEITYOFFIRE,
|
||||
PLAGUE
|
||||
};
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@ -178,18 +178,11 @@ namespace MappedKeys
|
||||
static const std::map<std::string, BuildingSubID::EBuildingSubID> SPECIAL_BUILDINGS =
|
||||
{
|
||||
{ "mysticPond", BuildingSubID::MYSTIC_POND },
|
||||
{ "artifactMerchant", BuildingSubID::ARTIFACT_MERCHANT },
|
||||
{ "freelancersGuild", BuildingSubID::FREELANCERS_GUILD },
|
||||
{ "magicUniversity", BuildingSubID::MAGIC_UNIVERSITY },
|
||||
{ "castleGate", BuildingSubID::CASTLE_GATE },
|
||||
{ "creatureTransformer", BuildingSubID::CREATURE_TRANSFORMER },//only skeleton transformer yet
|
||||
{ "portalOfSummoning", BuildingSubID::PORTAL_OF_SUMMONING },
|
||||
{ "ballistaYard", BuildingSubID::BALLISTA_YARD },
|
||||
{ "library", BuildingSubID::LIBRARY },
|
||||
{ "fountainOfFortune", BuildingSubID::FOUNTAIN_OF_FORTUNE },//luck garrison bonus
|
||||
{ "escapeTunnel", BuildingSubID::ESCAPE_TUNNEL },
|
||||
{ "treasury", BuildingSubID::TREASURY },
|
||||
{ "thievesGuild", BuildingSubID::THIEVES_GUILD },
|
||||
{ "bank", BuildingSubID::BANK }
|
||||
};
|
||||
|
||||
|
@ -34,6 +34,8 @@ public:
|
||||
TResources resources;
|
||||
TResources produce;
|
||||
TRequired requirements;
|
||||
ArtifactID warMachine;
|
||||
std::set<EMarketMode> marketModes;
|
||||
|
||||
BuildingID bid; //structure ID
|
||||
BuildingID upgrade; /// indicates that building "upgrade" can be improved by this, -1 = empty
|
||||
@ -85,7 +87,7 @@ public:
|
||||
STRONG_INLINE
|
||||
bool IsTradeBuilding() const
|
||||
{
|
||||
return bid == BuildingID::MARKETPLACE || subId == BuildingSubID::ARTIFACT_MERCHANT || subId == BuildingSubID::FREELANCERS_GUILD;
|
||||
return !marketModes.empty();
|
||||
}
|
||||
|
||||
void addNewBonus(const std::shared_ptr<Bonus> & b, BonusList & bonusList) const;
|
||||
|
@ -69,7 +69,7 @@ public:
|
||||
std::map<int,int> hordeLvl; //[0] - first horde building creature level; [1] - second horde building (-1 if not present)
|
||||
ui32 mageLevel; //max available mage guild level
|
||||
GameResID primaryRes;
|
||||
ArtifactID warMachine;
|
||||
CreatureID warMachineDeprecated;
|
||||
SpellID moatAbility;
|
||||
|
||||
// default chance for hero of specific class to appear in tavern, if field "tavern" was not set
|
||||
|
@ -324,6 +324,14 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons
|
||||
}
|
||||
loadBuildingRequirements(ret, source["requires"], requirementsToLoad);
|
||||
|
||||
if (!source["warMachine"].isNull())
|
||||
{
|
||||
VLC->identifiers()->requestIdentifier("artifact", source["warMachine"], [=](si32 identifier)
|
||||
{
|
||||
ret->warMachine = ArtifactID(identifier);
|
||||
});
|
||||
}
|
||||
|
||||
if (!source["upgrades"].isNull())
|
||||
{
|
||||
// building id and upgrades can't be the same
|
||||
@ -342,6 +350,11 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons
|
||||
ret->upgrade = BuildingID::NONE;
|
||||
|
||||
ret->town->buildings[ret->bid] = ret;
|
||||
for(const auto & element : source["marketModes"].Vector())
|
||||
{
|
||||
if(MappedKeys::MARKET_NAMES_TO_TYPES.count(element.String()))
|
||||
ret->marketModes.insert(MappedKeys::MARKET_NAMES_TO_TYPES.at(element.String()));
|
||||
}
|
||||
|
||||
registerObject(source.getModScope(), ret->town->getBuildingScope(), ret->identifier, ret->bid.getNum());
|
||||
}
|
||||
@ -547,7 +560,13 @@ void CTownHandler::loadTown(CTown * town, const JsonNode & source)
|
||||
else
|
||||
town->primaryRes = GameResID(resIter - std::begin(GameConstants::RESOURCE_NAMES));
|
||||
|
||||
warMachinesToLoad[town] = source["warMachine"];
|
||||
if (!source["warMachine"].isNull())
|
||||
{
|
||||
VLC->identifiers()->requestIdentifier( "creature", source["warMachine"], [=](si32 creatureID)
|
||||
{
|
||||
town->warMachineDeprecated = creatureID;
|
||||
});
|
||||
}
|
||||
|
||||
town->mageLevel = static_cast<ui32>(source["mageGuild"].Float());
|
||||
|
||||
@ -843,7 +862,6 @@ void CTownHandler::beforeValidate(JsonNode & object)
|
||||
void CTownHandler::afterLoadFinalization()
|
||||
{
|
||||
initializeRequirements();
|
||||
initializeWarMachines();
|
||||
}
|
||||
|
||||
void CTownHandler::initializeRequirements()
|
||||
@ -873,27 +891,6 @@ void CTownHandler::initializeRequirements()
|
||||
requirementsToLoad.clear();
|
||||
}
|
||||
|
||||
void CTownHandler::initializeWarMachines()
|
||||
{
|
||||
// must be done separately after all objects are loaded
|
||||
for(auto & p : warMachinesToLoad)
|
||||
{
|
||||
CTown * t = p.first;
|
||||
JsonNode creatureKey = p.second;
|
||||
|
||||
auto ret = VLC->identifiers()->getIdentifier("creature", creatureKey, false);
|
||||
|
||||
if(ret)
|
||||
{
|
||||
const CCreature * creature = CreatureID(*ret).toCreature();
|
||||
|
||||
t->warMachine = creature->warMachine;
|
||||
}
|
||||
}
|
||||
|
||||
warMachinesToLoad.clear();
|
||||
}
|
||||
|
||||
std::set<FactionID> CTownHandler::getDefaultAllowed() const
|
||||
{
|
||||
std::set<FactionID> allowedFactions;
|
||||
|
@ -34,14 +34,12 @@ class DLL_LINKAGE CTownHandler : public CHandlerBase<FactionID, Faction, CFactio
|
||||
CTown * town;
|
||||
};
|
||||
|
||||
std::map<CTown *, JsonNode> warMachinesToLoad;
|
||||
std::vector<BuildingRequirementsHelper> requirementsToLoad;
|
||||
std::vector<BuildingRequirementsHelper> overriddenBidsToLoad; //list of buildings, which bonuses should be overridden.
|
||||
|
||||
static const TPropagatorPtr & emptyPropagator();
|
||||
|
||||
void initializeRequirements();
|
||||
void initializeWarMachines();
|
||||
|
||||
/// loads CBuilding's into town
|
||||
void loadBuildingRequirements(CBuilding * building, const JsonNode & source, std::vector<BuildingRequirementsHelper> & bidsToLoad) const;
|
||||
|
@ -45,12 +45,11 @@
|
||||
#include "../mapping/CMapService.h"
|
||||
#include "../modding/IdentifierStorage.h"
|
||||
#include "../modding/ModScope.h"
|
||||
#include "../networkPacks/NetPacksBase.h"
|
||||
#include "../pathfinder/CPathfinder.h"
|
||||
#include "../pathfinder/PathfinderOptions.h"
|
||||
#include "../registerTypes/RegisterTypesClientPacks.h"
|
||||
#include "../rmg/CMapGenerator.h"
|
||||
#include "../serializer/CMemorySerializer.h"
|
||||
#include "../serializer/CTypeList.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
|
||||
#include <vstd/RNG.h>
|
||||
@ -59,29 +58,6 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
boost::shared_mutex CGameState::mutex;
|
||||
|
||||
template <typename T> class CApplyOnGS;
|
||||
|
||||
class CBaseForGSApply
|
||||
{
|
||||
public:
|
||||
virtual void applyOnGS(CGameState *gs, CPack * pack) const =0;
|
||||
virtual ~CBaseForGSApply() = default;
|
||||
template<typename U> static CBaseForGSApply *getApplier(const U * t=nullptr)
|
||||
{
|
||||
return new CApplyOnGS<U>();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> class CApplyOnGS : public CBaseForGSApply
|
||||
{
|
||||
public:
|
||||
void applyOnGS(CGameState *gs, CPack * pack) const override
|
||||
{
|
||||
T *ptr = static_cast<T*>(pack);
|
||||
ptr->applyGs(gs);
|
||||
}
|
||||
};
|
||||
|
||||
HeroTypeID CGameState::pickNextHeroType(const PlayerColor & owner)
|
||||
{
|
||||
const PlayerSettings &ps = scenarioOps->getIthPlayersSettings(owner);
|
||||
@ -165,8 +141,6 @@ CGameState::CGameState()
|
||||
{
|
||||
gs = this;
|
||||
heroesPool = std::make_unique<TavernHeroesPool>();
|
||||
applier = std::make_shared<CApplier<CBaseForGSApply>>();
|
||||
registerTypesClientPacks(*applier);
|
||||
globalEffects.setNodeType(CBonusSystemNode::GLOBAL_EFFECTS);
|
||||
}
|
||||
|
||||
@ -303,6 +277,27 @@ void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRan
|
||||
std::unique_ptr<CMap> randomMap = mapGenerator.generate();
|
||||
progressTracking.exclude(mapGenerator);
|
||||
|
||||
// Update starting options
|
||||
for(int i = 0; i < randomMap->players.size(); ++i)
|
||||
{
|
||||
const auto & playerInfo = randomMap->players[i];
|
||||
if(playerInfo.canAnyonePlay())
|
||||
{
|
||||
PlayerSettings & playerSettings = scenarioOps->playerInfos[PlayerColor(i)];
|
||||
playerSettings.compOnly = !playerInfo.canHumanPlay;
|
||||
playerSettings.castle = playerInfo.defaultCastle();
|
||||
if(playerSettings.isControlledByAI() && playerSettings.name.empty())
|
||||
{
|
||||
playerSettings.name = VLC->generaltexth->allTexts[468];
|
||||
}
|
||||
playerSettings.color = PlayerColor(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
scenarioOps->playerInfos.erase(PlayerColor(i));
|
||||
}
|
||||
}
|
||||
|
||||
if(allowSavingRandomMap)
|
||||
{
|
||||
try
|
||||
@ -332,26 +327,6 @@ void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRan
|
||||
}
|
||||
|
||||
map = randomMap.release();
|
||||
// Update starting options
|
||||
for(int i = 0; i < map->players.size(); ++i)
|
||||
{
|
||||
const auto & playerInfo = map->players[i];
|
||||
if(playerInfo.canAnyonePlay())
|
||||
{
|
||||
PlayerSettings & playerSettings = scenarioOps->playerInfos[PlayerColor(i)];
|
||||
playerSettings.compOnly = !playerInfo.canHumanPlay;
|
||||
playerSettings.castle = playerInfo.defaultCastle();
|
||||
if(playerSettings.isControlledByAI() && playerSettings.name.empty())
|
||||
{
|
||||
playerSettings.name = VLC->generaltexth->allTexts[468];
|
||||
}
|
||||
playerSettings.color = PlayerColor(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
scenarioOps->playerInfos.erase(PlayerColor(i));
|
||||
}
|
||||
}
|
||||
|
||||
logGlobal->info("Generated random map in %i ms.", sw.getDiff());
|
||||
}
|
||||
@ -369,6 +344,15 @@ void CGameState::initCampaign()
|
||||
map = campaign->getCurrentMap().release();
|
||||
}
|
||||
|
||||
void CGameState::generateOwnedObjectsAfterDeserialize()
|
||||
{
|
||||
for (auto & object : map->objects)
|
||||
{
|
||||
if (object && object->asOwnable() && object->getOwner().isValidPlayer())
|
||||
players.at(object->getOwner()).addOwnedObject(object.get());
|
||||
}
|
||||
}
|
||||
|
||||
void CGameState::initGlobalBonuses()
|
||||
{
|
||||
const JsonNode & baseBonuses = VLC->settings()->getValue(EGameSettings::BONUSES_GLOBAL);
|
||||
@ -508,6 +492,9 @@ void CGameState::randomizeMapObjects()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (object->getOwner().isValidPlayer())
|
||||
getPlayerState(object->getOwner())->addOwnedObject(object);
|
||||
}
|
||||
}
|
||||
|
||||
@ -599,7 +586,6 @@ void CGameState::initHeroes()
|
||||
}
|
||||
|
||||
hero->initHero(getRandomGenerator());
|
||||
getPlayerState(hero->getOwner())->heroes.push_back(hero);
|
||||
map->allHeroes[hero->getHeroType().getNum()] = hero;
|
||||
}
|
||||
|
||||
@ -724,14 +710,14 @@ void CGameState::initStartingBonus()
|
||||
}
|
||||
case PlayerStartingBonus::ARTIFACT:
|
||||
{
|
||||
if(elem.second.heroes.empty())
|
||||
if(elem.second.getHeroes().empty())
|
||||
{
|
||||
logGlobal->error("Cannot give starting artifact - no heroes!");
|
||||
break;
|
||||
}
|
||||
const Artifact * toGive = pickRandomArtifact(getRandomGenerator(), CArtifact::ART_TREASURE).toEntity(VLC);
|
||||
|
||||
CGHeroInstance *hero = elem.second.heroes[0];
|
||||
CGHeroInstance *hero = elem.second.getHeroes()[0];
|
||||
if(!giveHeroArtifact(hero, toGive->getId()))
|
||||
logGlobal->error("Cannot give starting artifact - no free slots!");
|
||||
}
|
||||
@ -807,12 +793,12 @@ void CGameState::initTowns()
|
||||
constexpr std::array hordes = { BuildingID::HORDE_PLACEHOLDER1, BuildingID::HORDE_PLACEHOLDER2, BuildingID::HORDE_PLACEHOLDER3, BuildingID::HORDE_PLACEHOLDER4, BuildingID::HORDE_PLACEHOLDER5, BuildingID::HORDE_PLACEHOLDER6, BuildingID::HORDE_PLACEHOLDER7, BuildingID::HORDE_PLACEHOLDER8 };
|
||||
|
||||
//init buildings
|
||||
if(vstd::contains(vti->builtBuildings, BuildingID::DEFAULT)) //give standard set of buildings
|
||||
if(vti->hasBuilt(BuildingID::DEFAULT)) //give standard set of buildings
|
||||
{
|
||||
vti->builtBuildings.erase(BuildingID::DEFAULT);
|
||||
vti->builtBuildings.insert(BuildingID::VILLAGE_HALL);
|
||||
vti->removeBuilding(BuildingID::DEFAULT);
|
||||
vti->addBuilding(BuildingID::VILLAGE_HALL);
|
||||
if(vti->tempOwner != PlayerColor::NEUTRAL)
|
||||
vti->builtBuildings.insert(BuildingID::TAVERN);
|
||||
vti->addBuilding(BuildingID::TAVERN);
|
||||
|
||||
auto definesBuildingsChances = VLC->settings()->getVector(EGameSettings::TOWNS_STARTING_DWELLING_CHANCES);
|
||||
|
||||
@ -820,48 +806,49 @@ void CGameState::initTowns()
|
||||
{
|
||||
if((getRandomGenerator().nextInt(1,100) <= definesBuildingsChances[i]))
|
||||
{
|
||||
vti->builtBuildings.insert(basicDwellings[i]);
|
||||
vti->addBuilding(basicDwellings[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// village hall must always exist
|
||||
vti->builtBuildings.insert(BuildingID::VILLAGE_HALL);
|
||||
vti->addBuilding(BuildingID::VILLAGE_HALL);
|
||||
|
||||
//init hordes
|
||||
for (int i = 0; i < vti->town->creatures.size(); i++)
|
||||
{
|
||||
if (vstd::contains(vti->builtBuildings, hordes[i])) //if we have horde for this level
|
||||
if(vti->hasBuilt(hordes[i])) //if we have horde for this level
|
||||
{
|
||||
vti->builtBuildings.erase(hordes[i]);//remove old ID
|
||||
vti->removeBuilding(hordes[i]);//remove old ID
|
||||
if (vti->getTown()->hordeLvl.at(0) == i)//if town first horde is this one
|
||||
{
|
||||
vti->builtBuildings.insert(BuildingID::HORDE_1);//add it
|
||||
vti->addBuilding(BuildingID::HORDE_1);//add it
|
||||
//if we have upgraded dwelling as well
|
||||
if (vstd::contains(vti->builtBuildings, upgradedDwellings[i]))
|
||||
vti->builtBuildings.insert(BuildingID::HORDE_1_UPGR);//add it as well
|
||||
if(vti->hasBuilt(upgradedDwellings[i]))
|
||||
vti->addBuilding(BuildingID::HORDE_1_UPGR);//add it as well
|
||||
}
|
||||
if (vti->getTown()->hordeLvl.at(1) == i)//if town second horde is this one
|
||||
{
|
||||
vti->builtBuildings.insert(BuildingID::HORDE_2);
|
||||
if (vstd::contains(vti->builtBuildings, upgradedDwellings[i]))
|
||||
vti->builtBuildings.insert(BuildingID::HORDE_2_UPGR);
|
||||
vti->addBuilding(BuildingID::HORDE_2);
|
||||
if(vti->hasBuilt(upgradedDwellings[i]))
|
||||
vti->addBuilding(BuildingID::HORDE_2_UPGR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#1444 - remove entries that don't have buildings defined (like some unused extra town hall buildings)
|
||||
//But DO NOT remove horde placeholders before they are replaced
|
||||
vstd::erase_if(vti->builtBuildings, [vti](const BuildingID & bid)
|
||||
{
|
||||
return !vti->getTown()->buildings.count(bid) || !vti->getTown()->buildings.at(bid);
|
||||
});
|
||||
for(const auto & building : vti->getBuildings())
|
||||
{
|
||||
if(!vti->getTown()->buildings.count(building) || !vti->getTown()->buildings.at(building))
|
||||
vti->removeBuilding(building);
|
||||
}
|
||||
|
||||
if (vstd::contains(vti->builtBuildings, BuildingID::SHIPYARD) && vti->shipyardStatus()==IBoatGenerator::TILE_BLOCKED)
|
||||
vti->builtBuildings.erase(BuildingID::SHIPYARD);//if we have harbor without water - erase it (this is H3 behaviour)
|
||||
if(vti->hasBuilt(BuildingID::SHIPYARD) && vti->shipyardStatus()==IBoatGenerator::TILE_BLOCKED)
|
||||
vti->removeBuilding(BuildingID::SHIPYARD);//if we have harbor without water - erase it (this is H3 behaviour)
|
||||
|
||||
//Early check for #1444-like problems
|
||||
for([[maybe_unused]] const auto & building : vti->builtBuildings)
|
||||
for([[maybe_unused]] const auto & building : vti->getBuildings())
|
||||
{
|
||||
assert(vti->getTown()->buildings.at(building) != nullptr);
|
||||
}
|
||||
@ -917,8 +904,6 @@ void CGameState::initTowns()
|
||||
vti->possibleSpells -= s->id;
|
||||
}
|
||||
vti->possibleSpells.clear();
|
||||
if(vti->getOwner() != PlayerColor::NEUTRAL)
|
||||
getPlayerState(vti->getOwner())->towns.emplace_back(vti);
|
||||
}
|
||||
}
|
||||
|
||||
@ -961,9 +946,9 @@ void CGameState::placeHeroesInTowns()
|
||||
if(player.first == PlayerColor::NEUTRAL)
|
||||
continue;
|
||||
|
||||
for(CGHeroInstance * h : player.second.heroes)
|
||||
for(CGHeroInstance * h : player.second.getHeroes())
|
||||
{
|
||||
for(CGTownInstance * t : player.second.towns)
|
||||
for(CGTownInstance * t : player.second.getTowns())
|
||||
{
|
||||
if(h->visitablePos().z != t->visitablePos().z)
|
||||
continue;
|
||||
@ -995,9 +980,9 @@ void CGameState::initVisitingAndGarrisonedHeroes()
|
||||
continue;
|
||||
|
||||
//init visiting and garrisoned heroes
|
||||
for(CGHeroInstance * h : player.second.heroes)
|
||||
for(CGHeroInstance * h : player.second.getHeroes())
|
||||
{
|
||||
for(CGTownInstance * t : player.second.towns)
|
||||
for(CGTownInstance * t : player.second.getTowns())
|
||||
{
|
||||
if(h->visitablePos().z != t->visitablePos().z)
|
||||
continue;
|
||||
@ -1144,10 +1129,9 @@ PlayerRelations CGameState::getPlayerRelations( PlayerColor color1, PlayerColor
|
||||
return PlayerRelations::ENEMIES;
|
||||
}
|
||||
|
||||
void CGameState::apply(CPack *pack)
|
||||
void CGameState::apply(CPackForClient *pack)
|
||||
{
|
||||
ui16 typ = CTypeList::getInstance().getTypeID(pack);
|
||||
applier->getApplier(typ)->applyOnGS(this, pack);
|
||||
pack->applyGs(this);
|
||||
}
|
||||
|
||||
void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out)
|
||||
@ -1222,82 +1206,6 @@ int3 CGameState::guardingCreaturePosition (int3 pos) const
|
||||
return gs->map->guardingCreaturePositions[pos.z][pos.x][pos.y];
|
||||
}
|
||||
|
||||
RumorState CGameState::pickNewRumor()
|
||||
{
|
||||
RumorState newRumor;
|
||||
|
||||
static const std::vector<RumorState::ERumorType> rumorTypes = {RumorState::TYPE_MAP, RumorState::TYPE_SPECIAL, RumorState::TYPE_RAND, RumorState::TYPE_RAND};
|
||||
std::vector<RumorState::ERumorTypeSpecial> sRumorTypes = {
|
||||
RumorState::RUMOR_OBELISKS, RumorState::RUMOR_ARTIFACTS, RumorState::RUMOR_ARMY, RumorState::RUMOR_INCOME};
|
||||
if(map->grailPos.valid()) // Grail should always be on map, but I had related crash I didn't manage to reproduce
|
||||
sRumorTypes.push_back(RumorState::RUMOR_GRAIL);
|
||||
|
||||
int rumorId = -1;
|
||||
int rumorExtra = -1;
|
||||
auto & rand = getRandomGenerator();
|
||||
newRumor.type = *RandomGeneratorUtil::nextItem(rumorTypes, rand);
|
||||
|
||||
do
|
||||
{
|
||||
switch(newRumor.type)
|
||||
{
|
||||
case RumorState::TYPE_SPECIAL:
|
||||
{
|
||||
SThievesGuildInfo tgi;
|
||||
obtainPlayersStats(tgi, 20);
|
||||
rumorId = *RandomGeneratorUtil::nextItem(sRumorTypes, rand);
|
||||
if(rumorId == RumorState::RUMOR_GRAIL)
|
||||
{
|
||||
rumorExtra = getTile(map->grailPos)->terType->getIndex();
|
||||
break;
|
||||
}
|
||||
|
||||
std::vector<PlayerColor> players = {};
|
||||
switch(rumorId)
|
||||
{
|
||||
case RumorState::RUMOR_OBELISKS:
|
||||
players = tgi.obelisks[0];
|
||||
break;
|
||||
|
||||
case RumorState::RUMOR_ARTIFACTS:
|
||||
players = tgi.artifacts[0];
|
||||
break;
|
||||
|
||||
case RumorState::RUMOR_ARMY:
|
||||
players = tgi.army[0];
|
||||
break;
|
||||
|
||||
case RumorState::RUMOR_INCOME:
|
||||
players = tgi.income[0];
|
||||
break;
|
||||
}
|
||||
rumorExtra = RandomGeneratorUtil::nextItem(players, rand)->getNum();
|
||||
|
||||
break;
|
||||
}
|
||||
case RumorState::TYPE_MAP:
|
||||
// Makes sure that map rumors only used if there enough rumors too choose from
|
||||
if(!map->rumors.empty() && (map->rumors.size() > 1 || !currentRumor.last.count(RumorState::TYPE_MAP)))
|
||||
{
|
||||
rumorId = rand.nextInt((int)map->rumors.size() - 1);
|
||||
break;
|
||||
}
|
||||
else
|
||||
newRumor.type = RumorState::TYPE_RAND;
|
||||
[[fallthrough]];
|
||||
|
||||
case RumorState::TYPE_RAND:
|
||||
auto vector = VLC->generaltexth->findStringsWithPrefix("core.randtvrn");
|
||||
rumorId = rand.nextInt((int)vector.size() - 1);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
while(!newRumor.update(rumorId, rumorExtra));
|
||||
|
||||
return newRumor;
|
||||
}
|
||||
|
||||
bool CGameState::isVisible(int3 pos, const std::optional<PlayerColor> & player) const
|
||||
{
|
||||
if (!map->isInTheMap(pos))
|
||||
@ -1396,7 +1304,7 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
|
||||
}
|
||||
case EventCondition::HAVE_ARTIFACT: //check if any hero has winning artifact
|
||||
{
|
||||
for(const auto & elem : p->heroes)
|
||||
for(const auto & elem : p->getHeroes())
|
||||
if(elem->hasArt(condition.objectType.as<ArtifactID>()))
|
||||
return true;
|
||||
return false;
|
||||
@ -1430,7 +1338,7 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
|
||||
}
|
||||
else // any town
|
||||
{
|
||||
for (const CGTownInstance * t : p->towns)
|
||||
for (const CGTownInstance * t : p->getTowns())
|
||||
{
|
||||
if (t->hasBuilt(condition.objectType.as<BuildingID>()))
|
||||
return true;
|
||||
@ -1575,9 +1483,9 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level)
|
||||
if(level >= 0) //num of towns & num of heroes
|
||||
{
|
||||
//num of towns
|
||||
FILL_FIELD(numOfTowns, g->second.towns.size())
|
||||
FILL_FIELD(numOfTowns, g->second.getTowns().size())
|
||||
//num of heroes
|
||||
FILL_FIELD(numOfHeroes, g->second.heroes.size())
|
||||
FILL_FIELD(numOfHeroes, g->second.getHeroes().size())
|
||||
}
|
||||
if(level >= 1) //best hero's portrait
|
||||
{
|
||||
@ -1649,7 +1557,7 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level)
|
||||
if(playerInactive(player.second.color)) //do nothing for neutral player
|
||||
continue;
|
||||
CreatureID bestCre; //best creature's ID
|
||||
for(const auto & elem : player.second.heroes)
|
||||
for(const auto & elem : player.second.getHeroes())
|
||||
{
|
||||
for(const auto & it : elem->Slots())
|
||||
{
|
||||
|
@ -38,9 +38,6 @@ class TavernHeroesPool;
|
||||
struct SThievesGuildInfo;
|
||||
class CRandomGenerator;
|
||||
|
||||
template<typename T> class CApplier;
|
||||
class CBaseForGSApply;
|
||||
|
||||
struct UpgradeInfo
|
||||
{
|
||||
CreatureID oldID; //creature to be upgraded
|
||||
@ -101,7 +98,7 @@ public:
|
||||
/// picks next free hero type of the H3 hero init sequence -> chosen starting hero, then unused hero type randomly
|
||||
HeroTypeID pickNextHeroType(const PlayerColor & owner);
|
||||
|
||||
void apply(CPack *pack);
|
||||
void apply(CPackForClient *pack);
|
||||
BattleField battleGetBattlefieldType(int3 tile, vstd::RNG & rand);
|
||||
|
||||
void fillUpgradeInfo(const CArmedInstance *obj, SlotID stackPos, UpgradeInfo &out) const override;
|
||||
@ -111,7 +108,6 @@ public:
|
||||
void calculatePaths(const std::shared_ptr<PathfinderConfig> & config) override;
|
||||
int3 guardingCreaturePosition (int3 pos) const override;
|
||||
std::vector<CGObjectInstance*> guardingCreatures (int3 pos) const;
|
||||
RumorState pickNewRumor();
|
||||
|
||||
/// Gets a artifact ID randomly and removes the selected artifact from this handler.
|
||||
ArtifactID pickRandomArtifact(vstd::RNG & rand, int flags);
|
||||
@ -160,6 +156,8 @@ public:
|
||||
h & day;
|
||||
h & map;
|
||||
h & players;
|
||||
if (h.version < Handler::Version::PLAYER_STATE_OWNED_OBJECTS)
|
||||
generateOwnedObjectsAfterDeserialize();
|
||||
h & teams;
|
||||
h & heroesPool;
|
||||
h & globalEffects;
|
||||
@ -199,6 +197,8 @@ private:
|
||||
void initVisitingAndGarrisonedHeroes();
|
||||
void initCampaign();
|
||||
|
||||
void generateOwnedObjectsAfterDeserialize();
|
||||
|
||||
// ----- bonus system handling -----
|
||||
|
||||
void buildBonusSystemTree();
|
||||
@ -215,7 +215,6 @@ private:
|
||||
UpgradeInfo fillUpgradeInfo(const CStackInstance &stack) const;
|
||||
|
||||
// ---- data -----
|
||||
std::shared_ptr<CApplier<CBaseForGSApply>> applier;
|
||||
Services * services;
|
||||
|
||||
/// Pointer to campaign state manager. Nullptr for single scenarios
|
||||
|
@ -536,7 +536,7 @@ void CGameStateCampaign::initHeroes()
|
||||
}
|
||||
assert(humanPlayer != PlayerColor::NEUTRAL);
|
||||
|
||||
std::vector<ConstTransitivePtr<CGHeroInstance> > & heroes = gameState->players[humanPlayer].heroes;
|
||||
const auto & heroes = gameState->players[humanPlayer].getHeroes();
|
||||
|
||||
if (chosenBonus->info1 == 0xFFFD) //most powerful
|
||||
{
|
||||
@ -656,7 +656,7 @@ void CGameStateCampaign::initTowns()
|
||||
if(gameState->scenarioOps->campState->formatVCMI())
|
||||
newBuilding = BuildingID(chosenBonus->info1);
|
||||
else
|
||||
newBuilding = CBuildingHandler::campToERMU(chosenBonus->info1, town->getFaction(), town->builtBuildings);
|
||||
newBuilding = CBuildingHandler::campToERMU(chosenBonus->info1, town->getFaction(), town->getBuildings());
|
||||
|
||||
// Build granted building & all prerequisites - e.g. Mages Guild Lvl 3 should also give Mages Guild Lvl 1 & 2
|
||||
while(true)
|
||||
@ -664,10 +664,10 @@ void CGameStateCampaign::initTowns()
|
||||
if (newBuilding == BuildingID::NONE)
|
||||
break;
|
||||
|
||||
if (town->builtBuildings.count(newBuilding) != 0)
|
||||
if(town->hasBuilt(newBuilding))
|
||||
break;
|
||||
|
||||
town->builtBuildings.insert(newBuilding);
|
||||
town->addBuilding(newBuilding);
|
||||
|
||||
auto building = town->town->buildings.at(newBuilding);
|
||||
newBuilding = building->upgrade;
|
||||
|
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