1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-22 22:13:35 +02:00

Merge remote-tracking branch 'vcmi/develop' into lobby

This commit is contained in:
Ivan Savenko 2024-01-26 16:52:23 +02:00
commit 322c5faf63
186 changed files with 1058 additions and 781 deletions

View File

@ -268,18 +268,26 @@ jobs:
run: |
ctest --preset ${{matrix.preset}}
- name: Kill XProtect to work around CPack issue on macOS
if: ${{ startsWith(matrix.platform, 'mac') }}
run: |
# Cf. https://github.com/actions/runner-images/issues/7522#issuecomment-1556766641
echo Killing...; sudo pkill -9 XProtect >/dev/null || true;
echo "Waiting..."; counter=0; while pgrep XProtect && ((counter < 20)); do sleep 3; ((counter++)); done
pgrep XProtect || true
- name: Pack
id: cpack
if: ${{ matrix.pack == 1 }}
run: |
cd '${{github.workspace}}/out/build/${{matrix.preset}}'
CPACK_PATH=`which -a cpack | grep -m1 -v -i chocolatey`
"$CPACK_PATH" -C ${{matrix.pack_type}} ${{ matrix.cpack_args }}
counter=0; until "$CPACK_PATH" -C ${{matrix.pack_type}} ${{ matrix.cpack_args }} || ((counter > 20)); do sleep 3; ((counter++)); done
test -f '${{github.workspace}}/CI/${{matrix.platform}}/post_pack.sh' \
&& '${{github.workspace}}/CI/${{matrix.platform}}/post_pack.sh' '${{github.workspace}}' "$(ls '${{ env.VCMI_PACKAGE_FILE_NAME }}'.*)"
rm -rf _CPack_Packages
- name: Create android package
- name: Create Android package
if: ${{ startsWith(matrix.platform, 'android') }}
run: |
cd android
@ -414,7 +422,7 @@ jobs:
name: Android JNI android-64
path: ${{ github.workspace }}/android/vcmi-app/src/main/jniLibs/
- name: Create android package
- name: Create Android package
run: |
cd android
./gradlew bundleRelease --info

View File

@ -14,11 +14,11 @@
#include "../../lib/CStack.h"
#include "../../lib/battle/BattleAction.h"
void CEmptyAI::saveGame(BinarySerializer & h, const int version)
void CEmptyAI::saveGame(BinarySerializer & h)
{
}
void CEmptyAI::loadGame(BinaryDeserializer & h, const int version)
void CEmptyAI::loadGame(BinaryDeserializer & h)
{
}

View File

@ -19,8 +19,8 @@ class CEmptyAI : public CGlobalAI
std::shared_ptr<CCallback> cb;
public:
virtual void saveGame(BinarySerializer & h, const int version) override;
virtual void loadGame(BinaryDeserializer & h, const int version) override;
virtual void saveGame(BinarySerializer & h) override;
virtual void loadGame(BinaryDeserializer & h) override;
void initGameInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CCallback> CB) override;
void yourTurn(QueryID queryID) override;

View File

@ -586,11 +586,18 @@ void AIGateway::heroGotLevel(const CGHeroInstance * hero, PrimarySkill pskill, s
requestActionASAP([=]()
{
int sel = 0;
if(hPtr.validAndSet())
{
std::unique_lock<std::mutex> lockGuard(nullkiller->aiStateMutex);
nullkiller->heroManager->update();
answerQuery(queryID, nullkiller->heroManager->selectBestSkill(hPtr, skills));
sel = nullkiller->heroManager->selectBestSkill(hPtr, skills);
}
answerQuery(queryID, sel);
});
}
@ -661,14 +668,18 @@ void AIGateway::showBlockingDialog(const std::string & text, const std::vector<C
if(selection) //select from multiple components -> take the last one (they're indexed [1-size])
sel = components.size();
// TODO: Find better way to understand it is Chest of Treasures
if(hero.validAndSet()
&& components.size() == 2
&& components.front().type == ComponentType::RESOURCE
&& (nullkiller->heroManager->getHeroRole(hero) != HeroRole::MAIN
|| nullkiller->buildAnalyzer->getGoldPreasure() > MAX_GOLD_PEASURE))
{
sel = 1; // for now lets pick gold from a chest.
std::unique_lock<std::mutex> mxLock(nullkiller->aiStateMutex);
// TODO: Find better way to understand it is Chest of Treasures
if(hero.validAndSet()
&& components.size() == 2
&& components.front().type == ComponentType::RESOURCE
&& (nullkiller->heroManager->getHeroRole(hero) != HeroRole::MAIN
|| nullkiller->buildAnalyzer->getGoldPreasure() > MAX_GOLD_PEASURE))
{
sel = 1; // for now lets pick gold from a chest.
}
}
answerQuery(askID, sel);
@ -747,27 +758,25 @@ void AIGateway::showMapObjectSelectDialog(QueryID askID, const Component & icon,
requestActionASAP([=](){ answerQuery(askID, selectedObject.getNum()); });
}
void AIGateway::saveGame(BinarySerializer & h, const int version)
void AIGateway::saveGame(BinarySerializer & h)
{
LOG_TRACE_PARAMS(logAi, "version '%i'", version);
NET_EVENT_HANDLER;
nullkiller->memory->removeInvisibleObjects(myCb.get());
CAdventureAI::saveGame(h, version);
serializeInternal(h, version);
CAdventureAI::saveGame(h);
serializeInternal(h);
}
void AIGateway::loadGame(BinaryDeserializer & h, const int version)
void AIGateway::loadGame(BinaryDeserializer & h)
{
LOG_TRACE_PARAMS(logAi, "version '%i'", version);
//NET_EVENT_HANDLER;
#if 0
//disabled due to issue 2890
registerGoals(h);
#endif // 0
CAdventureAI::loadGame(h, version);
serializeInternal(h, version);
CAdventureAI::loadGame(h);
serializeInternal(h);
}
bool AIGateway::makePossibleUpgrades(const CArmedInstance * obj)
@ -859,6 +868,8 @@ void AIGateway::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h
{
makePossibleUpgrades(h.get());
std::unique_lock<std::mutex> lockGuard(nullkiller->aiStateMutex);
if(!h->visitedTown->garrisonHero || !nullkiller->isHeroLocked(h->visitedTown->garrisonHero))
moveCreaturesToHero(h->visitedTown);

View File

@ -62,7 +62,7 @@ public:
void heroVisit(const CGObjectInstance * obj, bool started);
template<typename Handler> void serialize(Handler & h, const int version)
template<typename Handler> void serialize(Handler & h)
{
h & battle;
h & remainingQueries;
@ -119,8 +119,8 @@ public:
void showGarrisonDialog(const CArmedInstance * up, const CGHeroInstance * down, bool removableUnits, QueryID queryID) override; //all stacks operations between these objects become allowed, interface has to call onEnd when done
void showTeleportDialog(const CGHeroInstance * hero, TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override;
void showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector<ObjectInstanceID> & objects) override;
void saveGame(BinarySerializer & h, const int version) override; //saving
void loadGame(BinaryDeserializer & h, const int version) override; //loading
void saveGame(BinarySerializer & h) override; //saving
void loadGame(BinaryDeserializer & h) override; //loading
void finish() override;
void availableCreaturesChanged(const CGDwelling * town) override;
@ -203,7 +203,7 @@ public:
//special function that can be called ONLY from game events handling thread and will send request ASAP
void requestActionASAP(std::function<void()> whatToDo);
template<typename Handler> void serializeInternal(Handler & h, const int version)
template<typename Handler> void serializeInternal(Handler & h)
{
h & nullkiller->memory->knownTeleportChannels;
h & nullkiller->memory->knownSubterraneanGates;

View File

@ -115,7 +115,7 @@ public:
bool validAndSet() const;
template<typename Handler> void serialize(Handler & h, const int version)
template<typename Handler> void serialize(Handler & h)
{
h & this->h;
h & hid;
@ -147,7 +147,7 @@ struct ObjectIdRef
bool operator<(const ObjectIdRef & rhs) const;
template<typename Handler> void serialize(Handler & h, const int version)
template<typename Handler> void serialize(Handler & h)
{
h & id;
}

View File

@ -115,6 +115,8 @@ Goals::TTask Nullkiller::choseBestTask(Goals::TSubgoal behavior, int decompositi
void Nullkiller::resetAiState()
{
std::unique_lock<std::mutex> lockGuard(aiStateMutex);
lockedResources = TResources();
scanDepth = ScanDepth::MAIN_FULL;
playerID = ai->playerID;
@ -127,6 +129,8 @@ void Nullkiller::updateAiState(int pass, bool fast)
{
boost::this_thread::interruption_point();
std::unique_lock<std::mutex> lockGuard(aiStateMutex);
auto start = std::chrono::high_resolution_clock::now();
activeHero = nullptr;

View File

@ -73,6 +73,7 @@ public:
std::unique_ptr<ArmyFormation> armyFormation;
PlayerColor playerID;
std::shared_ptr<CCallback> cb;
std::mutex aiStateMutex;
Nullkiller();
void init(std::shared_ptr<CCallback> cb, PlayerColor playerID);

View File

@ -137,6 +137,18 @@ TResources getCreatureBankResources(const CGObjectInstance * target, const CGHer
return sum > 1 ? result / sum : result;
}
uint64_t getResourcesGoldReward(const TResources & res)
{
int nonGoldResources = res[EGameResID::GEMS]
+ res[EGameResID::SULFUR]
+ res[EGameResID::WOOD]
+ res[EGameResID::ORE]
+ res[EGameResID::CRYSTAL]
+ res[EGameResID::MERCURY];
return res[EGameResID::GOLD] + 100 * nonGoldResources;
}
uint64_t getCreatureBankArmyReward(const CGObjectInstance * target, const CGHeroInstance * hero)
{
auto objectInfo = target->getObjectHandler()->getObjectInfo(target->appearance);
@ -491,7 +503,7 @@ float RewardEvaluator::getStrategicalValue(const CGObjectInstance * target) cons
//Evaluate resources used for construction. Gold is evaluated separately.
if (it->resType != EGameResID::GOLD)
{
sum += 0.1f * getResourceRequirementStrength(it->resType);
sum += 0.1f * it->resVal * getResourceRequirementStrength(it->resType);
}
}
return sum;
@ -529,6 +541,9 @@ float RewardEvaluator::getStrategicalValue(const CGObjectInstance * target) cons
? getEnemyHeroStrategicalValue(dynamic_cast<const CGHeroInstance *>(target))
: 0;
case Obj::KEYMASTER:
return 0.6f;
default:
return 0;
}
@ -588,6 +603,8 @@ float RewardEvaluator::getSkillReward(const CGObjectInstance * target, const CGH
case Obj::PANDORAS_BOX:
//Can contains experience, spells, or skills (only on custom maps)
return 2.5f;
case Obj::PYRAMID:
return 3.0f;
case Obj::HERO:
return ai->cb->getPlayerRelations(target->tempOwner, ai->playerID) == PlayerRelations::ENEMIES
? enemyHeroEliminationSkillRewardRatio * dynamic_cast<const CGHeroInstance *>(target)->level
@ -660,7 +677,7 @@ int32_t RewardEvaluator::getGoldReward(const CGObjectInstance * target, const CG
case Obj::WAGON:
return 100;
case Obj::CREATURE_BANK:
return getCreatureBankResources(target, hero)[EGameResID::GOLD];
return getResourcesGoldReward(getCreatureBankResources(target, hero));
case Obj::CRYPT:
case Obj::DERELICT_SHIP:
return 3000;

View File

@ -37,7 +37,7 @@ namespace Goals
{
return new T(static_cast<T const &>(*this)); //casting enforces template instantiation
}
template<typename Handler> void serialize(Handler & h, const int version)
template<typename Handler> void serialize(Handler & h)
{
h & static_cast<AbstractGoal &>(*this);
//h & goalType & isElementar & isAbstract & priority;

View File

@ -35,6 +35,8 @@ const uint64_t MIN_ARMY_STRENGTH_FOR_CHAIN = 5000;
const uint64_t MIN_ARMY_STRENGTH_FOR_NEXT_ACTOR = 1000;
const uint64_t CHAIN_MAX_DEPTH = 4;
const bool DO_NOT_SAVE_TO_COMMITED_TILES = false;
AISharedStorage::AISharedStorage(int3 sizes)
{
if(!shared){
@ -234,6 +236,7 @@ void AINodeStorage::resetTile(const int3 & coord, EPathfindingLayer layer, EPath
heroNode.specialAction.reset();
heroNode.armyLoss = 0;
heroNode.chainOther = nullptr;
heroNode.dayFlags = DayFlags::NONE;
heroNode.update(coord, layer, accessibility);
}
}
@ -265,7 +268,8 @@ void AINodeStorage::commit(
EPathNodeAction action,
int turn,
int movementLeft,
float cost) const
float cost,
bool saveToCommited) const
{
destination->action = action;
destination->setCost(cost);
@ -291,10 +295,15 @@ void AINodeStorage::commit(
destination->actor->armyValue);
#endif
if(destination->turns <= heroChainTurn)
if(saveToCommited && destination->turns <= heroChainTurn)
{
commitedTiles.insert(destination->coord);
}
if(destination->turns == source->turns)
{
destination->dayFlags = source->dayFlags;
}
}
std::vector<CGPathNode *> AINodeStorage::calculateNeighbours(
@ -778,7 +787,14 @@ void HeroChainCalculationTask::addHeroChain(const std::vector<ExchangeCandidate>
continue;
}
storage.commit(exchangeNode, carrier, carrier->action, chainInfo.turns, chainInfo.moveRemains, chainInfo.getCost());
storage.commit(
exchangeNode,
carrier,
carrier->action,
chainInfo.turns,
chainInfo.moveRemains,
chainInfo.getCost(),
DO_NOT_SAVE_TO_COMMITED_TILES);
if(carrier->specialAction || carrier->chainOther)
{
@ -1070,7 +1086,8 @@ struct TowmPortalFinder
EPathNodeAction::TELEPORT_NORMAL,
bestNode->turns,
bestNode->moveRemains - movementNeeded,
movementCost);
movementCost,
DO_NOT_SAVE_TO_COMMITED_TILES);
node->theNodeBefore = bestNode;
node->addSpecialAction(std::make_shared<AIPathfinding::TownPortalAction>(targetTown));

View File

@ -41,11 +41,19 @@ namespace AIPathfinding
const int CHAIN_MAX_DEPTH = 4;
}
enum DayFlags : ui8
{
NONE = 0,
FLY_CAST = 1,
WATER_WALK_CAST = 2
};
struct AIPathNode : public CGPathNode
{
uint64_t danger;
uint64_t armyLoss;
int32_t manaCost;
int16_t manaCost;
DayFlags dayFlags;
const AIPathNode * chainOther;
std::shared_ptr<const SpecialAction> specialAction;
const ChainActor * actor;
@ -200,7 +208,8 @@ public:
EPathNodeAction action,
int turn,
int movementLeft,
float cost) const;
float cost,
bool saveToCommited = true) const;
inline const AIPathNode * getAINode(const CGPathNode * node) const
{

View File

@ -22,18 +22,18 @@ namespace NKAI
namespace AIPathfinding
{
AdventureCastAction::AdventureCastAction(SpellID spellToCast, const CGHeroInstance * hero)
:spellToCast(spellToCast), hero(hero)
AdventureCastAction::AdventureCastAction(SpellID spellToCast, const CGHeroInstance * hero, DayFlags flagsToAdd)
:spellToCast(spellToCast), hero(hero), flagsToAdd(flagsToAdd)
{
manaCost = hero->getSpellCost(spellToCast.toSpell());
}
WaterWalkingAction::WaterWalkingAction(const CGHeroInstance * hero)
:AdventureCastAction(SpellID::WATER_WALK, hero)
:AdventureCastAction(SpellID::WATER_WALK, hero, DayFlags::WATER_WALK_CAST)
{ }
AirWalkingAction::AirWalkingAction(const CGHeroInstance * hero)
: AdventureCastAction(SpellID::FLY, hero)
: AdventureCastAction(SpellID::FLY, hero, DayFlags::FLY_CAST)
{
}
@ -41,11 +41,12 @@ namespace AIPathfinding
const CGHeroInstance * hero,
CDestinationNodeInfo & destination,
const PathNodeInfo & source,
AIPathNode * dstMode,
AIPathNode * dstNode,
const AIPathNode * srcNode) const
{
dstMode->manaCost = srcNode->manaCost + manaCost;
dstMode->theNodeBefore = source.node;
dstNode->manaCost = srcNode->manaCost + manaCost;
dstNode->theNodeBefore = source.node;
dstNode->dayFlags = static_cast<DayFlags>(dstNode->dayFlags | flagsToAdd);
}
void AdventureCastAction::execute(const CGHeroInstance * hero) const

View File

@ -24,9 +24,10 @@ namespace AIPathfinding
SpellID spellToCast;
const CGHeroInstance * hero;
int manaCost;
DayFlags flagsToAdd;
public:
AdventureCastAction(SpellID spellToCast, const CGHeroInstance * hero);
AdventureCastAction(SpellID spellToCast, const CGHeroInstance * hero, DayFlags flagsToAdd = DayFlags::NONE);
virtual void execute(const CGHeroInstance * hero) const override;

View File

@ -61,6 +61,12 @@ namespace AIPathfinding
if(source.node->layer == EPathfindingLayer::LAND && destination.node->layer == EPathfindingLayer::WATER)
{
if(nodeStorage->getAINode(source.node)->dayFlags & DayFlags::WATER_WALK_CAST)
{
destination.blocked = false;
return;
}
auto action = waterWalkingActions.find(nodeStorage->getHero(source.node));
if(action != waterWalkingActions.end() && tryUseSpecialAction(destination, source, action->second, EPathNodeAction::NORMAL))
@ -73,6 +79,12 @@ namespace AIPathfinding
if(source.node->layer == EPathfindingLayer::LAND && destination.node->layer == EPathfindingLayer::AIR)
{
if(nodeStorage->getAINode(source.node)->dayFlags & DayFlags::FLY_CAST)
{
destination.blocked = false;
return;
}
auto action = airWalkingActions.find(nodeStorage->getHero(source.node));
if(action != airWalkingActions.end() && tryUseSpecialAction(destination, source, action->second, EPathNodeAction::NORMAL))

View File

@ -16,8 +16,6 @@
#include "../../lib/battle/BattleAction.h"
#include "../../lib/battle/BattleInfo.h"
static std::shared_ptr<CBattleCallback> cbc;
CStupidAI::CStupidAI()
: side(-1)
, wasWaitingForRealize(false)
@ -41,7 +39,7 @@ void CStupidAI::initBattleInterface(std::shared_ptr<Environment> ENV, std::share
{
print("init called, saving ptr to IBattleCallback");
env = ENV;
cbc = cb = CB;
cb = CB;
wasWaitingForRealize = CB->waitTillRealize;
wasUnlockingGs = CB->unlockGsWhenWaiting;
@ -73,11 +71,11 @@ public:
std::vector<BattleHex> attackFrom; //for melee fight
EnemyInfo(const CStack * _s) : s(_s), adi(0), adr(0)
{}
void calcDmg(const BattleID & battleID, const CStack * ourStack)
void calcDmg(std::shared_ptr<CBattleCallback> cb, const BattleID & battleID, const CStack * ourStack)
{
// FIXME: provide distance info for Jousting bonus
DamageEstimation retal;
DamageEstimation dmg = cbc->getBattle(battleID)->battleEstimateDamage(ourStack, s, 0, &retal);
DamageEstimation dmg = cb->getBattle(battleID)->battleEstimateDamage(ourStack, s, 0, &retal);
adi = static_cast<int>((dmg.damage.min + dmg.damage.max) / 2);
adr = static_cast<int>((retal.damage.min + retal.damage.max) / 2);
}
@ -93,14 +91,14 @@ bool isMoreProfitable(const EnemyInfo &ei1, const EnemyInfo& ei2)
return (ei1.adi-ei1.adr) < (ei2.adi - ei2.adr);
}
static bool willSecondHexBlockMoreEnemyShooters(const BattleID & battleID, const BattleHex &h1, const BattleHex &h2)
static bool willSecondHexBlockMoreEnemyShooters(std::shared_ptr<CBattleCallback> cb, const BattleID & battleID, const BattleHex &h1, const BattleHex &h2)
{
int shooters[2] = {0}; //count of shooters on hexes
for(int i = 0; i < 2; i++)
{
for (auto & neighbour : (i ? h2 : h1).neighbouringTiles())
if(const auto * s = cbc->getBattle(battleID)->battleGetUnitByPos(neighbour))
if(const auto * s = cb->getBattle(battleID)->battleGetUnitByPos(neighbour))
if(s->isShooter())
shooters[i]++;
}
@ -172,10 +170,10 @@ void CStupidAI::activeStack(const BattleID & battleID, const CStack * stack)
}
for ( auto & enemy : enemiesReachable )
enemy.calcDmg(battleID, stack);
enemy.calcDmg(cb, battleID, stack);
for ( auto & enemy : enemiesShootable )
enemy.calcDmg(battleID, stack);
enemy.calcDmg(cb, battleID, stack);
if(enemiesShootable.size())
{
@ -186,7 +184,7 @@ void CStupidAI::activeStack(const BattleID & battleID, const CStack * stack)
else if(enemiesReachable.size())
{
const EnemyInfo &ei= *std::max_element(enemiesReachable.begin(), enemiesReachable.end(), &isMoreProfitable);
BattleHex targetHex = *std::max_element(ei.attackFrom.begin(), ei.attackFrom.end(), [&](auto a, auto b) { return willSecondHexBlockMoreEnemyShooters(battleID, a, b);});
BattleHex targetHex = *std::max_element(ei.attackFrom.begin(), ei.attackFrom.end(), [&](auto a, auto b) { return willSecondHexBlockMoreEnemyShooters(cb, battleID, a, b);});
cb->battleMakeUnitAction(battleID, BattleAction::makeMeleeAttack(stack, ei.s->getPosition(), targetHex));
return;

View File

@ -70,7 +70,7 @@ public:
bool validAndSet() const;
template<typename Handler> void serialize(Handler & h, const int version)
template<typename Handler> void serialize(Handler & h)
{
h & this->h;
h & hid;
@ -102,7 +102,7 @@ struct ObjectIdRef
bool operator<(const ObjectIdRef & rhs) const;
template<typename Handler> void serialize(Handler & h, const int version)
template<typename Handler> void serialize(Handler & h)
{
h & id;
}

View File

@ -176,7 +176,7 @@ namespace Goals
return !(*this == g);
}
template<typename Handler> void serialize(Handler & h, const int version)
template<typename Handler> void serialize(Handler & h)
{
h & goalType;
h & isElementar;

View File

@ -69,7 +69,7 @@ namespace Goals
return ptr;
}
template<typename Handler> void serialize(Handler & h, const int version)
template<typename Handler> void serialize(Handler & h)
{
h & static_cast<AbstractGoal &>(*this);
//h & goalType & isElementar & isAbstract & priority;

View File

@ -28,7 +28,7 @@ struct DLL_EXPORT ResourceObjective
Goals::TSubgoal goal; //what for (build, gather army etc...)
//TODO: register?
template<typename Handler> void serializeInternal(Handler & h, const int version)
template<typename Handler> void serializeInternal(Handler & h)
{
h & resources;
//h & goal; //FIXME: goal serialization is broken
@ -105,7 +105,7 @@ private:
void dumpToLog() const;
//TODO: register?
template<typename Handler> void serializeInternal(Handler & h, const int version)
template<typename Handler> void serializeInternal(Handler & h)
{
h & saving;
h & queue;

View File

@ -746,9 +746,8 @@ void VCAI::showMapObjectSelectDialog(QueryID askID, const Component & icon, cons
requestActionASAP([=](){ answerQuery(askID, selectedObject.getNum()); });
}
void VCAI::saveGame(BinarySerializer & h, const int version)
void VCAI::saveGame(BinarySerializer & h)
{
LOG_TRACE_PARAMS(logAi, "version '%i'", version);
NET_EVENT_HANDLER;
validateVisitableObjs();
@ -756,21 +755,20 @@ void VCAI::saveGame(BinarySerializer & h, const int version)
//disabled due to issue 2890
registerGoals(h);
#endif // 0
CAdventureAI::saveGame(h, version);
serializeInternal(h, version);
CAdventureAI::saveGame(h);
serializeInternal(h);
}
void VCAI::loadGame(BinaryDeserializer & h, const int version)
void VCAI::loadGame(BinaryDeserializer & h)
{
LOG_TRACE_PARAMS(logAi, "version '%i'", version);
//NET_EVENT_HANDLER;
#if 0
//disabled due to issue 2890
registerGoals(h);
#endif // 0
CAdventureAI::loadGame(h, version);
serializeInternal(h, version);
CAdventureAI::loadGame(h);
serializeInternal(h);
}
void makePossibleUpgrades(const CArmedInstance * obj)

View File

@ -68,7 +68,7 @@ public:
void heroVisit(const CGObjectInstance * obj, bool started);
template<typename Handler> void serialize(Handler & h, const int version)
template<typename Handler> void serialize(Handler & h)
{
h & battle;
h & remainingQueries;
@ -152,8 +152,8 @@ public:
void showGarrisonDialog(const CArmedInstance * up, const CGHeroInstance * down, bool removableUnits, QueryID queryID) override; //all stacks operations between these objects become allowed, interface has to call onEnd when done
void showTeleportDialog(const CGHeroInstance * hero, TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override;
void showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector<ObjectInstanceID> & objects) override;
void saveGame(BinarySerializer & h, const int version) override; //saving
void loadGame(BinaryDeserializer & h, const int version) override; //loading
void saveGame(BinarySerializer & h) override; //saving
void loadGame(BinaryDeserializer & h) override; //loading
void finish() override;
void availableCreaturesChanged(const CGDwelling * town) override;
@ -301,7 +301,7 @@ public:
}
#endif
template<typename Handler> void serializeInternal(Handler & h, const int version)
template<typename Handler> void serializeInternal(Handler & h)
{
h & knownTeleportChannels;
h & knownSubterraneanGates;
@ -341,7 +341,7 @@ public:
//we have to explicitly ignore invalid goal class type id
h & typeId;
Goals::AbstractGoal ignored2;
ignored2.serialize(h, version);
ignored2.serialize(h);
}
}
}

View File

@ -261,12 +261,12 @@ void CCallback::setFormation(const CGHeroInstance * hero, EArmyFormation mode)
sendRequest(&pack);
}
void CCallback::recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero)
void CCallback::recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero, const HeroTypeID & nextHero)
{
assert(townOrTavern);
assert(hero);
HireHero pack(hero->getHeroType(), townOrTavern->id);
HireHero pack(hero->getHeroType(), townOrTavern->id, nextHero);
pack.player = *player;
sendRequest(&pack);
}

View File

@ -73,7 +73,7 @@ public:
virtual void castSpell(const CGHeroInstance *hero, SpellID spellID, const int3 &pos = int3(-1, -1, -1))=0; //cast adventure map spell
//town
virtual void recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero)=0;
virtual void recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero, const HeroTypeID & nextHero=HeroTypeID::NONE)=0;
virtual bool buildBuilding(const CGTownInstance *town, BuildingID buildingID)=0;
virtual void recruitCreatures(const CGDwelling *obj, const CArmedInstance * dst, CreatureID ID, ui32 amount, si32 level=-1)=0;
virtual bool upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID=CreatureID::NONE)=0; //if newID==-1 then best possible upgrade will be made
@ -185,7 +185,7 @@ public:
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 setFormation(const CGHeroInstance * hero, EArmyFormation mode) override;
void recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero) override;
void recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero, const HeroTypeID & nextHero=HeroTypeID::NONE) override;
void save(const std::string &fname) override;
void sendMessage(const std::string &mess, const CGObjectInstance * currentObject = nullptr) override;
void gamePause(bool pause) override;

View File

@ -1,4 +1,4 @@
# 1.4.3 -> 1.5.0
# 1.4.5 -> 1.5.0
### General
* Added Chinese translation to map editor
@ -10,6 +10,31 @@
* Added status bar to the backpack window
* Quick backpack window is now only available when enabled Interface enhancements
# 1.4.4 -> 1.4.5
### Stability
* Fixed crash on creature spellcasting
* Fixed crash on unit entering magical obstacles such as quicksands
* Fixed freeze on map loading on some systems
* Fixed crash on attempt to start campaign with unsupported map
* Fixed crash on opening creature information window with invalid SPELL_IMMUNITY bonus
### Random Maps Generator
* Fixed placement of guards sometimes resulting into open connection into third zone
* Fixed rare crash on multithreaded access during placement of artifacts or wandering monsters
### Map Editor
* Fixed inspector using wrong editor for some values
### AI
* Fixed bug leading to AI not attacking wandering monsters in some cases
* Fixed crash on using StupidAI for autocombat or for enemy players
# 1.4.3 -> 1.4.4
### General
* Fixed crash on generation of random maps
# 1.4.2 -> 1.4.3
### General

View File

@ -91,6 +91,7 @@
"vcmi.lobby.room.type" : "Room Type",
"vcmi.lobby.room.mode" : "Game Mode",
"vcmi.client.errors.invalidMap" : "{Invalid map or campaign}\n\nFailed to start game! Selected map or campaign might be invalid or corrupted. Reason:\n%s",
"vcmi.client.errors.missingCampaigns" : "{Missing data files}\n\nCampaigns data files were not found! You may be using incomplete or corrupted Heroes 3 data files. Please reinstall game data.",
"vcmi.server.errors.existingProcess" : "Another VCMI server process is running. Please terminate it before starting a new game.",
"vcmi.server.errors.modsToEnable" : "{Following mods are required}",
@ -251,6 +252,8 @@
"vcmi.heroWindow.openBackpack.hover" : "Open artifact backpack window",
"vcmi.heroWindow.openBackpack.help" : "Opens window that allows easier artifact backpack management.",
"vcmi.tavernWindow.inviteHero" : "Invite hero",
"vcmi.commanderWindow.artifactMessage" : "Do you want to return this artifact to the hero?",
"vcmi.creatureWindow.showBonuses.hover" : "Switch to bonuses view",

View File

@ -232,6 +232,8 @@
"vcmi.heroWindow.openBackpack.hover" : "Artefakt-Rucksack-Fenster öffnen",
"vcmi.heroWindow.openBackpack.help" : "Öffnet ein Fenster, das die Verwaltung des Artefakt-Rucksacks erleichtert",
"vcmi.tavernWindow.inviteHero" : "Helden einladen",
"vcmi.commanderWindow.artifactMessage" : "Möchtet Ihr diesen Artefakt dem Helden zurückgeben?",
"vcmi.creatureWindow.showBonuses.hover" : "Wechsle zur Bonus-Ansicht",

View File

@ -412,9 +412,9 @@ static void mainLoop()
{
if(CSH->client)
CSH->endGameplay();
}
GH.windows().clear();
GH.windows().clear();
}
CMM.reset();
@ -474,25 +474,24 @@ void handleQuit(bool ask)
// FIXME: avoids crash if player attempts to close game while opening is still playing
// use cursor handler as indicator that loading is not done yet
// proper solution would be to abort init thread (or wait for it to finish)
if(!ask)
{
quitApplication();
return;
}
if (!CCS->curh)
{
quitRequestedDuringOpeningPlayback = true;
return;
}
if(ask)
{
CCS->curh->set(Cursor::Map::POINTER);
CCS->curh->set(Cursor::Map::POINTER);
if (LOCPLINT)
LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[69], quitApplication, nullptr);
else
CInfoWindow::showYesNoDialog(CGI->generaltexth->allTexts[69], {}, quitApplication, {}, PlayerColor(1));
}
if (LOCPLINT)
LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[69], quitApplication, nullptr);
else
{
quitApplication();
}
CInfoWindow::showYesNoDialog(CGI->generaltexth->allTexts[69], {}, quitApplication, {}, PlayerColor(1));
}
void handleFatalError(const std::string & message, bool terminate)

View File

@ -1150,16 +1150,16 @@ void CPlayerInterface::heroBonusChanged( const CGHeroInstance *hero, const Bonus
}
}
void CPlayerInterface::saveGame( BinarySerializer & h, const int version )
void CPlayerInterface::saveGame( BinarySerializer & h )
{
EVENT_HANDLER_CALLED_BY_CLIENT;
localState->serialize(h, version);
localState->serialize(h);
}
void CPlayerInterface::loadGame( BinaryDeserializer & h, const int version )
void CPlayerInterface::loadGame( BinaryDeserializer & h )
{
EVENT_HANDLER_CALLED_BY_CLIENT;
localState->serialize(h, version);
localState->serialize(h);
firstCall = -1;
}

View File

@ -145,8 +145,8 @@ protected: // Call-ins from server, should not be called directly, but only via
void gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult) override;
void playerStartsTurn(PlayerColor player) override; //called before yourTurn on active itnerface
void playerEndsTurn(PlayerColor player) override;
void saveGame(BinarySerializer & h, const int version) override; //saving
void loadGame(BinaryDeserializer & h, const int version) override; //loading
void saveGame(BinarySerializer & h) override; //saving
void loadGame(BinaryDeserializer & h) override; //loading
void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions, bool showTerrain) override;
//for battles

View File

@ -610,7 +610,9 @@ bool CServerHandler::validateGameStart(bool allowOnlyAI) const
void CServerHandler::sendStartGame(bool allowOnlyAI) const
{
verifyStateBeforeStart(allowOnlyAI ? true : settings["session"]["onlyai"].Bool());
GH.windows().createAndPushWindow<CLoadingScreen>();
if(!settings["session"]["headless"].Bool())
GH.windows().createAndPushWindow<CLoadingScreen>();
LobbyStartGame lsg;
if(client)

View File

@ -226,7 +226,7 @@ void CClient::loadGame(CGameState * initializedGameState)
throw std::runtime_error("Cannot open client part of " + CSH->si->mapname);
std::unique_ptr<CLoadFile> loader (new CLoadFile(clientSaveName));
serialize(loader->serializer, loader->serializer.fileVersion);
serialize(loader->serializer, loader->serializer.version);
logNetwork->info("Client data loaded.");
}
@ -239,7 +239,7 @@ void CClient::loadGame(CGameState * initializedGameState)
initPlayerInterfaces();
}
void CClient::serialize(BinarySerializer & h, const int version)
void CClient::serialize(BinarySerializer & h)
{
assert(h.saving);
ui8 players = static_cast<ui8>(playerint.size());
@ -252,20 +252,17 @@ void CClient::serialize(BinarySerializer & h, const int version)
h & i->first;
h & i->second->dllName;
h & i->second->human;
i->second->saveGame(h, version);
i->second->saveGame(h);
}
#if SCRIPTING_ENABLED
if(version >= 800)
{
JsonNode scriptsState;
clientScripts->serializeState(h.saving, scriptsState);
h & scriptsState;
}
JsonNode scriptsState;
clientScripts->serializeState(h.saving, scriptsState);
h & scriptsState;
#endif
}
void CClient::serialize(BinaryDeserializer & h, const int version)
void CClient::serialize(BinaryDeserializer & h)
{
assert(!h.saving);
ui8 players = 0;
@ -321,7 +318,7 @@ void CClient::serialize(BinaryDeserializer & h, const int version)
// loadGame needs to be called after initGameInterface to load paths correctly
// initGameInterface is called in installNewPlayerInterface
nInt->loadGame(h, version);
nInt->loadGame(h);
if (shouldResetInterface)
{
@ -412,7 +409,7 @@ void CClient::initPlayerEnvironments()
hasHumanPlayer = true;
}
if(!hasHumanPlayer)
if(!hasHumanPlayer && !settings["session"]["headless"].Bool())
{
Settings session = settings.write["session"];
session["spectate"].Bool() = true;
@ -437,7 +434,7 @@ void CClient::initPlayerInterfaces()
if(!vstd::contains(playerint, color))
{
logNetwork->info("Preparing interface for player %s", color.toString());
if(playerInfo.second.isControlledByAI())
if(playerInfo.second.isControlledByAI() || settings["session"]["onlyai"].Bool())
{
bool alliedToHuman = false;
for(auto & allyInfo : gs->scenarioOps->playerInfos)

View File

@ -131,8 +131,8 @@ public:
void newGame(CGameState * gameState);
void loadGame(CGameState * gameState);
void serialize(BinarySerializer & h, const int version);
void serialize(BinaryDeserializer & h, const int version);
void serialize(BinarySerializer & h);
void serialize(BinaryDeserializer & h);
void save(const std::string & fname);
void endGame();

View File

@ -157,6 +157,9 @@ void ApplyClientNetPackVisitor::visitSetMana(SetMana & pack)
const CGHeroInstance *h = cl.getHero(pack.hid);
callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroManaPointsChanged, h);
if(settings["session"]["headless"].Bool())
return;
for (auto window : GH.windows().findWindows<BattleWindow>())
window->heroManaPointsChanged(h);
}
@ -467,7 +470,8 @@ void ApplyFirstClientNetPackVisitor::visitRemoveObject(RemoveObject & pack)
i->second->objectRemoved(o, pack.initiator);
}
CGI->mh->waitForOngoingAnimations();
if(CGI->mh)
CGI->mh->waitForOngoingAnimations();
}
void ApplyClientNetPackVisitor::visitRemoveObject(RemoveObject & pack)
@ -553,9 +557,11 @@ void ApplyClientNetPackVisitor::visitNewStructures(NewStructures & pack)
}
// invalidate section of map view with our object and force an update
CGI->mh->onObjectInstantRemove(town, town->getOwner());
CGI->mh->onObjectInstantAdd(town, town->getOwner());
if(CGI->mh)
{
CGI->mh->onObjectInstantRemove(town, town->getOwner());
CGI->mh->onObjectInstantAdd(town, town->getOwner());
}
}
void ApplyClientNetPackVisitor::visitRazeStructures(RazeStructures & pack)
{
@ -566,8 +572,11 @@ void ApplyClientNetPackVisitor::visitRazeStructures(RazeStructures & pack)
}
// invalidate section of map view with our object and force an update
CGI->mh->onObjectInstantRemove(town, town->getOwner());
CGI->mh->onObjectInstantAdd(town, town->getOwner());
if(CGI->mh)
{
CGI->mh->onObjectInstantRemove(town, town->getOwner());
CGI->mh->onObjectInstantAdd(town, town->getOwner());
}
}
void ApplyClientNetPackVisitor::visitSetAvailableCreatures(SetAvailableCreatures & pack)
@ -651,7 +660,7 @@ void ApplyFirstClientNetPackVisitor::visitSetObjectProperty(SetObjectProperty &
}
// invalidate section of map view with our object and force an update with new flag color
if (pack.what == ObjProperty::OWNER)
if (pack.what == ObjProperty::OWNER && CGI->mh)
{
auto object = gs.getObjInstance(pack.id);
CGI->mh->onObjectInstantRemove(object, object->getOwner());
@ -668,7 +677,7 @@ void ApplyClientNetPackVisitor::visitSetObjectProperty(SetObjectProperty & pack)
}
// invalidate section of map view with our object and force an update with new flag color
if (pack.what == ObjProperty::OWNER)
if (pack.what == ObjProperty::OWNER && CGI->mh)
{
auto object = gs.getObjInstance(pack.id);
CGI->mh->onObjectInstantAdd(object, object->getOwner());
@ -1023,7 +1032,9 @@ void ApplyClientNetPackVisitor::visitNewObject(NewObject & pack)
if(gs.isVisible(obj, i->first))
i->second->newObject(obj);
}
CGI->mh->waitForOngoingAnimations();
if(CGI->mh)
CGI->mh->waitForOngoingAnimations();
}
void ApplyClientNetPackVisitor::visitSetAvailableArtifacts(SetAvailableArtifacts & pack)

View File

@ -47,7 +47,7 @@ public:
int spellbookLastTabAdvmap = 4;
template<typename Handler>
void serialize(Handler & h, const int version)
void serialize(Handler & h)
{
h & spellbookLastPageBattle;
h & spellbookLastPageAdvmap;
@ -94,7 +94,7 @@ public:
void setSelection(const CArmedInstance *sel);
template<typename Handler>
void serialize(Handler & h, int version)
void serialize(Handler & h)
{
//WARNING: this code is broken and not used. See CClient::loadGame
std::map<const CGHeroInstance *, int3> pathsMap; //hero -> dest

View File

@ -750,7 +750,7 @@ void BattleActionsController::actionRealize(PossiblePlayerBattleAction action, B
if (!spellcastingModeActive())
{
if (action.spell().toSpell())
if (action.spell().hasValue())
{
owner.giveCommand(EActionType::MONSTER_SPELL, targetHex, action.spell());
}
@ -887,17 +887,17 @@ void BattleActionsController::tryActivateStackSpellcasting(const CStack *casterS
{
// faerie dragon can cast only one, randomly selected spell until their next move
//TODO: faerie dragon type spell should be selected by server
const auto * spellToCast = owner.getBattle()->getRandomCastedSpell(CRandomGenerator::getDefault(), casterStack).toSpell();
const auto spellToCast = owner.getBattle()->getRandomCastedSpell(CRandomGenerator::getDefault(), casterStack);
if (spellToCast)
creatureSpells.push_back(spellToCast);
if (spellToCast.hasValue())
creatureSpells.push_back(spellToCast.toSpell());
}
TConstBonusListPtr bl = casterStack->getBonuses(Selector::type()(BonusType::SPELLCASTER));
for(const auto & bonus : *bl)
{
if (bonus->additionalInfo[0] <= 0)
if (bonus->additionalInfo[0] <= 0 && bonus->subtype.as<SpellID>().hasValue())
creatureSpells.push_back(bonus->subtype.as<SpellID>().toSpell());
}
}

View File

@ -352,13 +352,13 @@ void BattleInterface::spellCast(const BattleSpellCast * sc)
CCS->curh->set(Cursor::Combat::BLOCKED);
const SpellID spellID = sc->spellID;
if(!spellID.hasValue())
return;
const CSpell * spell = spellID.toSpell();
auto targetedTile = sc->tile;
assert(spell);
if(!spell)
return;
const AudioPath & castSoundPath = spell->getCastSound();
if (!castSoundPath.empty())

View File

@ -30,6 +30,8 @@
#include "../render/Colors.h"
#include "../render/Canvas.h"
#include "../render/IRenderHandler.h"
#include "../render/Graphics.h"
#include "../render/IFont.h"
#include "../../CCallback.h"
#include "../../lib/spells/ISpellMechanics.h"
@ -327,10 +329,10 @@ void BattleStacksController::showStackAmountBox(Canvas & canvas, const CStack *
boxPosition = owner.fieldController->hexPositionLocal(frontPos).center() + Point(-8, -14);
}
Point textPosition = amountBG->dimensions()/2 + boxPosition + Point(0, 1);
Point textPosition = Point(amountBG->dimensions().x/2 + boxPosition.x, boxPosition.y + graphics->fonts[EFonts::FONT_TINY]->getLineHeight() - 6);
canvas.draw(amountBG, boxPosition);
canvas.drawText(textPosition, EFonts::FONT_TINY, Colors::WHITE, ETextAlignment::CENTER, TextOperations::formatMetric(stack->getCount(), 4));
canvas.drawText(textPosition, EFonts::FONT_TINY, Colors::WHITE, ETextAlignment::TOPCENTER, TextOperations::formatMetric(stack->getCount(), 4));
}
void BattleStacksController::showStack(Canvas & canvas, const CStack * stack)

View File

@ -21,10 +21,6 @@
#include <SDL_events.h>
#ifdef VCMI_APPLE
# include <dispatch/dispatch.h>
#endif
void InputSourceText::handleEventTextInput(const SDL_TextInputEvent & text)
{
GH.events().dispatchTextInput(text.text);
@ -37,38 +33,27 @@ void InputSourceText::handleEventTextEditing(const SDL_TextEditingEvent & text)
void InputSourceText::startTextInput(const Rect & whereInput)
{
#ifdef VCMI_APPLE
dispatch_async(dispatch_get_main_queue(), ^{
#endif
Rect rectInScreenCoordinates = GH.screenHandler().convertLogicalPointsToWindow(whereInput);
SDL_Rect textInputRect = CSDL_Ext::toSDL(rectInScreenCoordinates);
SDL_SetTextInputRect(&textInputRect);
if (SDL_IsTextInputActive() == SDL_FALSE)
GH.dispatchMainThread([whereInput]()
{
SDL_StartTextInput();
}
Rect rectInScreenCoordinates = GH.screenHandler().convertLogicalPointsToWindow(whereInput);
SDL_Rect textInputRect = CSDL_Ext::toSDL(rectInScreenCoordinates);
#ifdef VCMI_APPLE
SDL_SetTextInputRect(&textInputRect);
if (SDL_IsTextInputActive() == SDL_FALSE)
{
SDL_StartTextInput();
}
});
#endif
}
void InputSourceText::stopTextInput()
{
#ifdef VCMI_APPLE
dispatch_async(dispatch_get_main_queue(), ^{
#endif
if (SDL_IsTextInputActive() == SDL_TRUE)
GH.dispatchMainThread([]()
{
SDL_StopTextInput();
}
#ifdef VCMI_APPLE
if (SDL_IsTextInputActive() == SDL_TRUE)
{
SDL_StopTextInput();
}
});
#endif
}

View File

@ -72,6 +72,7 @@ CBonusSelection::CBonusSelection()
buttonStart = std::make_shared<CButton>(Point(475, 536), AnimationPath::builtin("CBBEGIB.DEF"), CButton::tooltip(), std::bind(&CBonusSelection::startMap, this), EShortcut::GLOBAL_ACCEPT);
buttonRestart = std::make_shared<CButton>(Point(475, 536), AnimationPath::builtin("CBRESTB.DEF"), CButton::tooltip(), std::bind(&CBonusSelection::restartMap, this), EShortcut::GLOBAL_ACCEPT);
buttonVideo = std::make_shared<CButton>(Point(705, 214), AnimationPath::builtin("CBVIDEB.DEF"), CButton::tooltip(), [this](){ GH.windows().createAndPushWindow<CPrologEpilogVideo>(getCampaign()->scenario(CSH->campaignMap).prolog, [this](){ redraw(); }); });
buttonBack = std::make_shared<CButton>(Point(624, 536), AnimationPath::builtin("CBCANCB.DEF"), CButton::tooltip(), std::bind(&CBonusSelection::goBack, this), EShortcut::GLOBAL_CANCEL);
campaignName = std::make_shared<CLabel>(481, 28, FONT_BIG, ETextAlignment::TOPLEFT, Colors::YELLOW, CSH->si->getCampaignName());
@ -309,6 +310,7 @@ void CBonusSelection::updateAfterStateChange()
if(CSH->state != EClientState::GAMEPLAY)
{
buttonRestart->disable();
buttonVideo->disable();
buttonStart->enable();
if(!getCampaign()->conqueredScenarios().empty())
buttonBack->block(true);
@ -319,6 +321,7 @@ void CBonusSelection::updateAfterStateChange()
{
buttonStart->disable();
buttonRestart->enable();
buttonVideo->enable();
buttonBack->block(false);
if(buttonDifficultyLeft)
buttonDifficultyLeft->disable();
@ -401,6 +404,7 @@ void CBonusSelection::startMap()
//block buttons immediately
buttonStart->block(true);
buttonRestart->block(true);
buttonVideo->block(true);
buttonBack->block(true);
if(LOCPLINT) // we're currently ingame, so ask for starting new map and end game

View File

@ -65,6 +65,7 @@ public:
std::shared_ptr<CButton> buttonStart;
std::shared_ptr<CButton> buttonRestart;
std::shared_ptr<CButton> buttonBack;
std::shared_ptr<CButton> buttonVideo;
std::shared_ptr<CLabel> campaignName;
std::shared_ptr<CLabel> labelCampaignDescription;
std::shared_ptr<CTextBox> campaignDescription;

View File

@ -135,11 +135,23 @@ void CLobbyScreen::toggleTab(std::shared_ptr<CIntObject> tab)
void CLobbyScreen::startCampaign()
{
if(CSH->mi)
{
if(!CSH->mi)
return;
try {
auto ourCampaign = CampaignHandler::getCampaign(CSH->mi->fileURI);
CSH->setCampaignState(ourCampaign);
}
catch (const std::runtime_error &e)
{
// handle possible exception on map loading. For example campaign that contains map in unsupported format
// for example, wog campaigns or hota campaigns without hota map support mod
MetaString message;
message.appendTextID("vcmi.client.errors.invalidMap");
message.replaceRawString(e.what());
CInfoWindow::showInfoDialog(message.toString(), {});
}
}
void CLobbyScreen::startScenario(bool allowOnlyAI)

View File

@ -407,8 +407,14 @@ void OptionsTabBase::recreate()
}
if(auto buttonCheatAllowed = widget<CToggleButton>("buttonCheatAllowed"))
{
buttonCheatAllowed->setSelectedSilent(SEL->getStartInfo()->extraOptionsInfo.cheatsAllowed);
buttonCheatAllowed->block(SEL->screenType == ESelectionScreen::loadGame);
}
if(auto buttonUnlimitedReplay = widget<CToggleButton>("buttonUnlimitedReplay"))
{
buttonUnlimitedReplay->setSelectedSilent(SEL->getStartInfo()->extraOptionsInfo.unlimitedReplay);
buttonUnlimitedReplay->block(SEL->screenType == ESelectionScreen::loadGame);
}
}

View File

@ -64,5 +64,6 @@ void CPrologEpilogVideo::clickPressed(const Point & cursorPosition)
close();
CCS->soundh->stopSound(voiceSoundHandle);
CCS->soundh->stopSound(videoSoundHandle);
exitCb();
if(exitCb)
exitCb();
}

View File

@ -205,6 +205,8 @@ void SDLImage::exportBitmap(const boost::filesystem::path& path) const
void SDLImage::playerColored(PlayerColor player)
{
if (!surf)
return;
graphics->blueToPlayersAdv(surf, player);
}

View File

@ -553,8 +553,7 @@ Point CGStatusBar::getBorderSize()
CTextInput::CTextInput(const Rect & Pos, EFonts font, const CFunctionList<void(const std::string &)> & CB, ETextAlignment alignment, bool giveFocusToInput)
: CLabel(Pos.x, Pos.y, font, alignment),
cb(CB),
CFocusable(std::make_shared<CKeyboardFocusListener>(this))
cb(CB)
{
setRedrawParent(true);
pos.h = Pos.h;
@ -570,7 +569,7 @@ CTextInput::CTextInput(const Rect & Pos, EFonts font, const CFunctionList<void(c
}
CTextInput::CTextInput(const Rect & Pos, const Point & bgOffset, const ImagePath & bgName, const CFunctionList<void(const std::string &)> & CB)
:cb(CB), CFocusable(std::make_shared<CKeyboardFocusListener>(this))
:cb(CB)
{
pos += Pos.topLeft();
pos.h = Pos.h;
@ -587,7 +586,6 @@ CTextInput::CTextInput(const Rect & Pos, const Point & bgOffset, const ImagePath
}
CTextInput::CTextInput(const Rect & Pos, std::shared_ptr<IImage> srf)
:CFocusable(std::make_shared<CKeyboardFocusListener>(this))
{
pos += Pos.topLeft();
OBJ_CONSTRUCTION;
@ -603,20 +601,15 @@ CTextInput::CTextInput(const Rect & Pos, std::shared_ptr<IImage> srf)
#endif
}
std::atomic<int> CKeyboardFocusListener::usageIndex(0);
std::atomic<int> CFocusable::usageIndex(0);
CKeyboardFocusListener::CKeyboardFocusListener(CTextInput * textInput)
:textInput(textInput)
void CFocusable::focusGot()
{
}
void CKeyboardFocusListener::focusGot()
{
GH.startTextInput(textInput->pos);
GH.startTextInput(pos);
usageIndex++;
}
void CKeyboardFocusListener::focusLost()
void CFocusable::focusLost()
{
if(0 == --usageIndex)
{
@ -769,12 +762,6 @@ void CTextInput::numberFilter(std::string & text, const std::string & oldText, i
}
CFocusable::CFocusable()
:CFocusable(std::make_shared<IFocusListener>())
{
}
CFocusable::CFocusable(std::shared_ptr<IFocusListener> focusListener)
: focusListener(focusListener)
{
focus = false;
focusables.push_back(this);
@ -785,7 +772,7 @@ CFocusable::~CFocusable()
if(hasFocus())
{
inputWithFocus = nullptr;
focusListener->focusLost();
focusLost();
}
focusables -= this;
@ -799,13 +786,13 @@ bool CFocusable::hasFocus() const
void CFocusable::giveFocus()
{
focus = true;
focusListener->focusGot();
focusGot();
redraw();
if(inputWithFocus)
{
inputWithFocus->focus = false;
inputWithFocus->focusListener->focusLost();
inputWithFocus->focusLost();
inputWithFocus->redraw();
}
@ -837,7 +824,7 @@ void CFocusable::removeFocus()
if(this == inputWithFocus)
{
focus = false;
focusListener->focusLost();
focusLost();
redraw();
inputWithFocus = nullptr;

View File

@ -163,25 +163,12 @@ public:
void clear() override;
void setEnteringMode(bool on) override;
void setEnteredText(const std::string & text) override;
};
class CFocusable;
class IFocusListener
{
public:
virtual void focusGot() {};
virtual void focusLost() {};
virtual ~IFocusListener() = default;
};
/// UIElement which can get input focus
class CFocusable : public virtual CIntObject
{
private:
std::shared_ptr<IFocusListener> focusListener;
static std::atomic<int> usageIndex;
public:
bool focus; //only one focusable control can have focus at one moment
@ -190,38 +177,27 @@ public:
void removeFocus(); //remove focus
bool hasFocus() const;
void focusGot();
void focusLost();
static std::list<CFocusable *> focusables; //all existing objs
static CFocusable * inputWithFocus; //who has focus now
CFocusable();
CFocusable(std::shared_ptr<IFocusListener> focusListener);
~CFocusable();
};
class CTextInput;
class CKeyboardFocusListener : public IFocusListener
{
private:
static std::atomic<int> usageIndex;
CTextInput * textInput;
public:
CKeyboardFocusListener(CTextInput * textInput);
void focusGot() override;
void focusLost() override;
};
/// Text input box where players can enter text
class CTextInput : public CLabel, public CFocusable
{
std::string newText;
std::string helpBox; //for right-click help
protected:
std::string visibleText() override;
public:
CFunctionList<void(const std::string &)> cb;
CFunctionList<void(std::string &, const std::string &)> filters;
void setText(const std::string & nText) override;

View File

@ -161,7 +161,7 @@ CMapOverviewWidget::CMapOverviewWidget(CMapOverview& parent):
std::unique_ptr<CMap> campaignMap = nullptr;
if(p.tabType != ESelectionScreen::newGame && config["variables"]["mapPreviewForSaves"].Bool())
{
CLoadFile lf(*CResourceHandler::get()->getResourceName(ResourcePath(p.resource.getName(), EResType::SAVEGAME)), MINIMAL_SERIALIZATION_VERSION);
CLoadFile lf(*CResourceHandler::get()->getResourceName(ResourcePath(p.resource.getName(), EResType::SAVEGAME)), ESerializationVersion::MINIMAL);
lf.checkMagicBytes(SAVEGAME_MAGIC);
auto mapHeader = std::make_unique<CMapHeader>();

View File

@ -17,6 +17,8 @@
#include "InfoWindows.h"
#include "../CGameInfo.h"
#include "../CServerHandler.h"
#include "../Client.h"
#include "../CMusicHandler.h"
#include "../CPlayerInterface.h"
#include "../CVideoHandler.h"
@ -48,6 +50,7 @@
#include "../lib/mapObjects/ObjectTemplate.h"
#include "../lib/gameState/CGameState.h"
#include "../lib/gameState/SThievesGuildInfo.h"
#include "../lib/gameState/TavernHeroesPool.h"
#include "../lib/CGeneralTextHandler.h"
#include "../lib/CHeroHandler.h"
#include "../lib/GameSettings.h"
@ -443,7 +446,8 @@ CLevelWindow::~CLevelWindow()
CTavernWindow::CTavernWindow(const CGObjectInstance * TavernObj, const std::function<void()> & onWindowClosed)
: CStatusbarWindow(PLAYER_COLORED, ImagePath::builtin("TPTAVERN")),
onWindowClosed(onWindowClosed),
tavernObj(TavernObj)
tavernObj(TavernObj),
heroToInvite(nullptr)
{
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
@ -459,8 +463,8 @@ CTavernWindow::CTavernWindow(const CGObjectInstance * TavernObj, const std::func
oldSelected = -1;
h1 = std::make_shared<HeroPortrait>(selected, 0, 72, 299, h[0]);
h2 = std::make_shared<HeroPortrait>(selected, 1, 162, 299, h[1]);
h1 = std::make_shared<HeroPortrait>(selected, 0, 72, 299, h[0], [this]() { if(!recruit->isBlocked()) recruitb(); });
h2 = std::make_shared<HeroPortrait>(selected, 1, 162, 299, h[1], [this]() { if(!recruit->isBlocked()) recruitb(); });
title = std::make_shared<CLabel>(197, 32, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->jktexts[37]);
cost = std::make_shared<CLabel>(320, 328, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, std::to_string(GameConstants::HERO_GOLD_COST));
@ -507,6 +511,34 @@ CTavernWindow::CTavernWindow(const CGObjectInstance * TavernObj, const std::func
CCS->videoh->open(townObj->town->clientInfo.tavernVideo);
else
CCS->videoh->open(VideoPath::builtin("TAVERN.BIK"));
addInvite();
}
void CTavernWindow::addInvite()
{
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
if(!VLC->settings()->getBoolean(EGameSettings::HEROES_TAVERN_INVITE))
return;
const auto & heroesPool = CSH->client->gameState()->heroesPool;
for(auto & elem : heroesPool->unusedHeroesFromPool())
{
bool heroAvailable = heroesPool->isHeroAvailableFor(elem.first, tavernObj->getOwner());
if(heroAvailable)
inviteableHeroes[elem.first] = elem.second;
}
if(!inviteableHeroes.empty())
{
if(!heroToInvite)
heroToInvite = (*RandomGeneratorUtil::nextItem(inviteableHeroes, CRandomGenerator::getDefault())).second;
inviteHero = std::make_shared<CLabel>(170, 444, EFonts::FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->translate("vcmi.tavernWindow.inviteHero"));
inviteHeroImage = std::make_shared<CAnimImage>(AnimationPath::builtin("PortraitsSmall"), (*CGI->heroh)[heroToInvite->getHeroType()]->imageIndex, 0, 245, 428);
inviteHeroImageArea = std::make_shared<LRClickableArea>(Rect(245, 428, 48, 32), [this](){ GH.windows().createAndPushWindow<HeroSelector>(inviteableHeroes, [this](CGHeroInstance* h){ heroToInvite = h; addInvite(); }); }, [this](){ GH.windows().createAndPushWindow<CRClickPopupInt>(std::make_shared<CHeroWindow>(heroToInvite)); });
}
}
void CTavernWindow::recruitb()
@ -514,7 +546,7 @@ void CTavernWindow::recruitb()
const CGHeroInstance *toBuy = (selected ? h2 : h1)->h;
const CGObjectInstance *obj = tavernObj;
LOCPLINT->cb->recruitHero(obj, toBuy);
LOCPLINT->cb->recruitHero(obj, toBuy, heroToInvite ? heroToInvite->getHeroType() : HeroTypeID::NONE);
close();
}
@ -569,15 +601,23 @@ void CTavernWindow::HeroPortrait::clickPressed(const Point & cursorPosition)
*_sel = _id;
}
void CTavernWindow::HeroPortrait::clickDouble(const Point & cursorPosition)
{
clickPressed(cursorPosition);
if(onChoose)
onChoose();
}
void CTavernWindow::HeroPortrait::showPopupWindow(const Point & cursorPosition)
{
if(h)
GH.windows().createAndPushWindow<CRClickPopupInt>(std::make_shared<CHeroWindow>(h));
}
CTavernWindow::HeroPortrait::HeroPortrait(int & sel, int id, int x, int y, const CGHeroInstance * H)
: CIntObject(LCLICK | SHOW_POPUP | HOVER),
h(H), _sel(&sel), _id(id)
CTavernWindow::HeroPortrait::HeroPortrait(int & sel, int id, int x, int y, const CGHeroInstance * H, std::function<void()> OnChoose)
: CIntObject(LCLICK | DOUBLECLICK | SHOW_POPUP | HOVER),
h(H), _sel(&sel), _id(id), onChoose(OnChoose)
{
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
h = H;
@ -615,6 +655,33 @@ void CTavernWindow::HeroPortrait::hover(bool on)
GH.statusbar()->clear();
}
CTavernWindow::HeroSelector::HeroSelector(std::map<HeroTypeID, CGHeroInstance*> InviteableHeroes, std::function<void(CGHeroInstance*)> OnChoose)
: CWindowObject(BORDERED), inviteableHeroes(InviteableHeroes), onChoose(OnChoose)
{
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
pos = Rect(0, 0, 16 * 48, (inviteableHeroes.size() / 16 + (inviteableHeroes.size() % 16 != 0)) * 32);
background = std::make_shared<CFilledTexture>(ImagePath::builtin("DIBOXBCK"), Rect(0, 0, pos.w, pos.h));
int x = 0;
int y = 0;
for(auto & h : inviteableHeroes)
{
portraits.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("PortraitsSmall"), (*CGI->heroh)[h.first]->imageIndex, 0, x * 48, y * 32));
portraitAreas.push_back(std::make_shared<LRClickableArea>(Rect(x * 48, y * 32, 48, 32), [this, h](){ close(); onChoose(inviteableHeroes[h.first]); }, [this, h](){ GH.windows().createAndPushWindow<CRClickPopupInt>(std::make_shared<CHeroWindow>(inviteableHeroes[h.first])); }));
if(x > 0 && x % 15 == 0)
{
x = 0;
y++;
}
else
x++;
}
center();
}
static const std::string QUICK_EXCHANGE_MOD_PREFIX = "quick-exchange";
static const std::string QUICK_EXCHANGE_BG = QUICK_EXCHANGE_MOD_PREFIX + "/TRADEQE";

View File

@ -36,6 +36,8 @@ class CTextBox;
class CGarrisonInt;
class CGarrisonSlot;
class CHeroArea;
class CAnimImage;
class CFilledTexture;
enum class EUserEvent;
@ -206,10 +208,13 @@ public:
std::string description; // "XXX is a level Y ZZZ with N artifacts"
const CGHeroInstance * h;
std::function<void()> onChoose;
void clickPressed(const Point & cursorPosition) override;
void clickDouble(const Point & cursorPosition) override;
void showPopupWindow(const Point & cursorPosition) override;
void hover (bool on) override;
HeroPortrait(int & sel, int id, int x, int y, const CGHeroInstance * H);
HeroPortrait(int & sel, int id, int x, int y, const CGHeroInstance * H, std::function<void()> OnChoose = nullptr);
private:
int *_sel;
@ -218,6 +223,21 @@ public:
std::shared_ptr<CAnimImage> portrait;
};
class HeroSelector : public CWindowObject
{
public:
std::shared_ptr<CFilledTexture> background;
HeroSelector(std::map<HeroTypeID, CGHeroInstance*> InviteableHeroes, std::function<void(CGHeroInstance*)> OnChoose);
private:
std::map<HeroTypeID, CGHeroInstance*> inviteableHeroes;
std::function<void(CGHeroInstance*)> onChoose;
std::vector<std::shared_ptr<CAnimImage>> portraits;
std::vector<std::shared_ptr<LRClickableArea>> portraitAreas;
};
//recruitable heroes
std::shared_ptr<HeroPortrait> h1;
std::shared_ptr<HeroPortrait> h2; //recruitable heroes
@ -237,6 +257,13 @@ public:
std::shared_ptr<CTextBox> heroDescription;
std::shared_ptr<CTextBox> rumor;
std::shared_ptr<CLabel> inviteHero;
std::shared_ptr<CAnimImage> inviteHeroImage;
std::shared_ptr<LRClickableArea> inviteHeroImageArea;
std::map<HeroTypeID, CGHeroInstance*> inviteableHeroes;
CGHeroInstance* heroToInvite;
void addInvite();
CTavernWindow(const CGObjectInstance * TavernObj, const std::function<void()> & onWindowClosed);
~CTavernWindow();

View File

@ -2,7 +2,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
if(NOT DEFINED MAIN_LIB_DIR)
set(MAIN_LIB_DIR "${CMAKE_SOURCE_DIR}/lib")
endif()
set(lib_SRCS
${MAIN_LIB_DIR}/StdInc.cpp
@ -268,13 +268,13 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/VCMI_Lib.cpp
)
# Version.cpp is a generated file
# Version.cpp is a generated file
if(ENABLE_GITVERSION)
list(APPEND lib_SRCS ${CMAKE_BINARY_DIR}/Version.cpp)
set_source_files_properties(${CMAKE_BINARY_DIR}/Version.cpp
PROPERTIES GENERATED TRUE
)
endif()
endif()
set(lib_HEADERS
${MAIN_LIB_DIR}/../include/vstd/CLoggerBase.h
@ -567,6 +567,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/serializer/JsonSerializer.h
${MAIN_LIB_DIR}/serializer/JsonUpdater.h
${MAIN_LIB_DIR}/serializer/Cast.h
${MAIN_LIB_DIR}/serializer/ESerializationVersion.h
${MAIN_LIB_DIR}/spells/AbilityCaster.h
${MAIN_LIB_DIR}/spells/AdventureSpellMechanics.h
@ -628,6 +629,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/CStopWatch.h
${MAIN_LIB_DIR}/CThreadHelper.h
${MAIN_LIB_DIR}/CTownHandler.h
${MAIN_LIB_DIR}/ExtraOptionsInfo.h
${MAIN_LIB_DIR}/FunctionList.h
${MAIN_LIB_DIR}/GameCallbackHolder.h
${MAIN_LIB_DIR}/GameConstants.h
@ -663,7 +665,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/VCMI_Lib.h
)
assign_source_group(${lib_SRCS} ${lib_HEADERS})
assign_source_group(${lib_SRCS} ${lib_HEADERS})
add_library(${TARGET_NAME} ${LIBRARY_TYPE} ${lib_SRCS} ${lib_HEADERS})
set_target_properties(${TARGET_NAME} PROPERTIES COMPILE_DEFINITIONS "VCMI_DLL=1")
@ -671,7 +673,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
minizip::minizip ZLIB::ZLIB
${SYSTEM_LIBS} Boost::boost Boost::thread Boost::filesystem Boost::program_options Boost::locale Boost::date_time
)
if(APPLE_IOS)
if(APPLE_IOS)
target_link_libraries(${TARGET_NAME} PUBLIC iOS_utils)
endif()
@ -682,13 +684,13 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
PUBLIC ${MAIN_LIB_DIR}
)
if(WIN32)
if(WIN32)
set_target_properties(${TARGET_NAME}
PROPERTIES
OUTPUT_NAME "VCMI_lib"
OUTPUT_NAME "VCMI_lib"
PROJECT_LABEL "VCMI_lib"
)
endif()
endif()
vcmi_set_output_dir(${TARGET_NAME} "")
@ -702,7 +704,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/cmake_modules/create_link.cmake ${MAIN_LIB_DIR}/../config ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/config
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/cmake_modules/create_link.cmake ${MAIN_LIB_DIR}/../Mods ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/Mods
)
endif()
endif()
# Update version before vcmi compiling
if(TARGET update_version)

View File

@ -149,7 +149,7 @@
{
"graphics":
{
"icon": "zvs/Lib1.res/E_RDEF"
"icon": "zvs/Lib1.res/E_RATT"
}
},
@ -197,7 +197,7 @@
{
"graphics":
{
"icon": ""
"icon": "zvs/Lib1.res/Ferocity"
}
},
@ -448,7 +448,7 @@
{
"graphics":
{
"icon": ""
"icon": "zvs/Lib1.res/Revenge"
}
},

View File

@ -28,7 +28,7 @@
},
"king":
{
"resources": { "wood" : 0, "mercury": 0, "ore": 0 , "sulfur": 0, "crystal": 0, "gems": 0, "gold": 0, "mithril": 0 },
"resources": { "wood" : 0, "mercury": 0, "ore": 0, "sulfur": 0, "crystal": 0, "gems": 0, "gold": 0, "mithril": 0 },
"globalBonuses": [],
"battleBonuses": []
}

View File

@ -289,7 +289,9 @@
// Chances for a hero with default army to receive corresponding stack out of his predefined starting troops
"startingStackChances": [ 100, 88, 25],
// number of artifacts that can fit in a backpack. -1 is unlimited.
"backpackSize" : -1
"backpackSize" : -1,
// if heroes are invitable in tavern
"tavernInvite" : false
},
"towns":

View File

@ -64,12 +64,14 @@
"name": "buttonCheatAllowed",
"image": "lobby/checkbox",
"callback" : "setCheatAllowed",
"help" : "vcmi.optionsTab.cheatAllowed",
"selected" : true
},
{
"name": "buttonUnlimitedReplay",
"image": "lobby/checkbox",
"callback" : "setUnlimitedReplay",
"help" : "vcmi.optionsTab.unlimitedReplay",
"selected" : true
}
]

6
debian/changelog vendored
View File

@ -4,6 +4,12 @@ vcmi (1.5.0) jammy; urgency=medium
-- Ivan Savenko <saven.ivan@gmail.com> Fri, 1 Mar 2024 12:00:00 +0200
vcmi (1.4.5) jammy; urgency=medium
* New upstream release
-- Ivan Savenko <saven.ivan@gmail.com> Tue, 23 Jan 2024 12:00:00 +0200
vcmi (1.4.4) jammy; urgency=medium
* New upstream release

View File

@ -2,7 +2,7 @@
[![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/1.4.0/total)](https://github.com/vcmi/vcmi/releases/tag/1.4.0)
[![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/1.4.1/total)](https://github.com/vcmi/vcmi/releases/tag/1.4.1)
[![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/1.4.2/total)](https://github.com/vcmi/vcmi/releases/tag/1.4.2)
[![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/1.4.3/total)](https://github.com/vcmi/vcmi/releases/tag/1.4.3)
[![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/1.4.4/total)](https://github.com/vcmi/vcmi/releases/tag/1.4.4)
[![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/total)](https://github.com/vcmi/vcmi/releases)
# VCMI Project

View File

@ -77,6 +77,7 @@
</screenshots>
<releases>
<release version="1.5.0" date="2024-03-01" type="development"/>
<release version="1.4.5" date="2024-01-23" type="stable"/>
<release version="1.4.4" date="2024-01-20" type="stable"/>
<release version="1.4.3" date="2024-01-19" type="stable"/>
<release version="1.4.2" date="2023-12-25" type="stable"/>

View File

@ -38,6 +38,9 @@ bool CModEntry::isEnabled() const
if(!isInstalled())
return false;
if (!isVisible())
return false;
return modSettings["active"].toBool();
}

View File

@ -521,7 +521,7 @@ QStringList CModListView::findDependentMods(QString mod, bool excludeDisabled)
{
auto current = modModel->getMod(modName);
if(!current.isInstalled())
if(!current.isInstalled() || !current.isVisible())
continue;
if(current.getDependencies().contains(mod, Qt::CaseInsensitive))

View File

@ -6,84 +6,84 @@
<message>
<location filename="../aboutProject/aboutproject_moc.ui" line="22"/>
<source>VCMI on Discord</source>
<translation type="unfinished">VCMI en Discord</translation>
<translation>VCMI en Discord</translation>
</message>
<message>
<location filename="../aboutProject/aboutproject_moc.ui" line="29"/>
<source>Have a question? Found a bug? Want to help? Join us!</source>
<translation type="unfinished">¿Tienes alguna pregunta? ¿Encontraste algún error? ¿Quieres ayudar? ¡Únete a nosotros!</translation>
<translation>¿Tienes alguna pregunta? ¿Encontraste algún error? ¿Quieres ayudar? ¡Únete a nosotros!</translation>
</message>
<message>
<location filename="../aboutProject/aboutproject_moc.ui" line="36"/>
<source>VCMI on Github</source>
<translation type="unfinished">VCMI en Github</translation>
<translation>VCMI en Github</translation>
</message>
<message>
<location filename="../aboutProject/aboutproject_moc.ui" line="55"/>
<source>Our Community</source>
<translation type="unfinished"></translation>
<translation>Nuestra comunidad</translation>
</message>
<message>
<location filename="../aboutProject/aboutproject_moc.ui" line="62"/>
<source>VCMI on Slack</source>
<translation type="unfinished">VCMI en Slack</translation>
<translation>VCMI en Slack</translation>
</message>
<message>
<location filename="../aboutProject/aboutproject_moc.ui" line="98"/>
<source>Build Information</source>
<translation type="unfinished"></translation>
<translation>Información de la versión</translation>
</message>
<message>
<location filename="../aboutProject/aboutproject_moc.ui" line="115"/>
<source>User data directory</source>
<translation type="unfinished">Directorio de datos del usuario</translation>
<translation>Directorio de datos del usuario</translation>
</message>
<message>
<location filename="../aboutProject/aboutproject_moc.ui" line="122"/>
<location filename="../aboutProject/aboutproject_moc.ui" line="129"/>
<location filename="../aboutProject/aboutproject_moc.ui" line="193"/>
<source>Open</source>
<translation type="unfinished">Abrir</translation>
<translation>Abrir</translation>
</message>
<message>
<location filename="../aboutProject/aboutproject_moc.ui" line="136"/>
<source>Check for updates</source>
<translation type="unfinished"></translation>
<translation>Comprobar actualizaciones</translation>
</message>
<message>
<location filename="../aboutProject/aboutproject_moc.ui" line="156"/>
<source>Game version</source>
<translation type="unfinished"></translation>
<translation>Versión del juego</translation>
</message>
<message>
<location filename="../aboutProject/aboutproject_moc.ui" line="163"/>
<source>Log files directory</source>
<translation type="unfinished">Directorio de archivos de registro</translation>
<translation>Directorio de archivos de registro</translation>
</message>
<message>
<location filename="../aboutProject/aboutproject_moc.ui" line="176"/>
<source>Data Directories</source>
<translation type="unfinished">Directorios de datos</translation>
<translation>Directorios de datos</translation>
</message>
<message>
<location filename="../aboutProject/aboutproject_moc.ui" line="213"/>
<source>Game data directory</source>
<translation type="unfinished"></translation>
<translation>Directorio de los datos del juego</translation>
</message>
<message>
<location filename="../aboutProject/aboutproject_moc.ui" line="220"/>
<source>Operating System</source>
<translation type="unfinished"></translation>
<translation>Sistema operativo</translation>
</message>
<message>
<location filename="../aboutProject/aboutproject_moc.ui" line="273"/>
<source>Project homepage</source>
<translation type="unfinished"></translation>
<translation>Página web del proyecto</translation>
</message>
<message>
<location filename="../aboutProject/aboutproject_moc.ui" line="286"/>
<source>Report a bug</source>
<translation type="unfinished"></translation>
<translation>Informar de un error</translation>
</message>
</context>
<context>
@ -121,7 +121,7 @@
<message>
<location filename="../modManager/cmodlistmodel_moc.cpp" line="48"/>
<source>Maps</source>
<translation type="unfinished"></translation>
<translation>Mapas</translation>
</message>
<message>
<location filename="../modManager/cmodlistmodel_moc.cpp" line="49"/>
@ -180,7 +180,7 @@
<message>
<location filename="../modManager/cmodlistmodel_moc.cpp" line="62"/>
<source>Compatibility</source>
<translation type="unfinished">Compatibilidad</translation>
<translation>Compatibilidad</translation>
</message>
<message>
<location filename="../modManager/cmodlistmodel_moc.cpp" line="63"/>
@ -319,7 +319,7 @@
<message>
<location filename="../modManager/cmodlistview_moc.cpp" line="292"/>
<source>Size</source>
<translation type="unfinished"></translation>
<translation>Tamaño</translation>
</message>
<message>
<location filename="../modManager/cmodlistview_moc.cpp" line="294"/>
@ -410,12 +410,12 @@
<message>
<location filename="../modManager/cmodlistview_moc.cpp" line="638"/>
<source>Downloading %s%. %p% (%v MB out of %m MB) finished</source>
<translation type="unfinished"></translation>
<translation>Descargando %s%. %p% (%v MB de %m MB) completado</translation>
</message>
<message>
<location filename="../modManager/cmodlistview_moc.cpp" line="665"/>
<source>Download failed</source>
<translation type="unfinished"></translation>
<translation>Descarga fallida</translation>
</message>
<message>
<location filename="../modManager/cmodlistview_moc.cpp" line="666"/>
@ -424,30 +424,37 @@
Encountered errors:
</source>
<translation type="unfinished"></translation>
<translation>No se han podido descargar todos los ficheros.
Errores encontrados:
</translation>
</message>
<message>
<location filename="../modManager/cmodlistview_moc.cpp" line="667"/>
<source>
Install successfully downloaded?</source>
<translation type="unfinished"></translation>
<translation>
Instalar lo correctamente descargado?</translation>
</message>
<message>
<location filename="../modManager/cmodlistview_moc.cpp" line="812"/>
<source>Installing mod %1</source>
<translation type="unfinished"></translation>
<translation>Instalando mod %1</translation>
</message>
<message>
<location filename="../modManager/cmodlistview_moc.cpp" line="874"/>
<source>Operation failed</source>
<translation type="unfinished"></translation>
<translation>Operación fallida</translation>
</message>
<message>
<location filename="../modManager/cmodlistview_moc.cpp" line="875"/>
<source>Encountered errors:
</source>
<translation type="unfinished"></translation>
<translation>Errores encontrados:
</translation>
</message>
<message>
<location filename="../modManager/cmodlistview_moc.cpp" line="910"/>
@ -482,94 +489,94 @@ Install successfully downloaded?</source>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="230"/>
<source>Interface Scaling</source>
<translation type="unfinished"></translation>
<translation>Escala de la interfaz</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="394"/>
<source>Neutral AI in battles</source>
<translation type="unfinished"></translation>
<translation>IA neutral en batallas</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="169"/>
<source>Enemy AI in battles</source>
<translation type="unfinished"></translation>
<translation>IA enemiga en batallas</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="176"/>
<source>Additional repository</source>
<translation type="unfinished"></translation>
<translation>Repositorio adicional</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="210"/>
<source>Adventure Map Allies</source>
<translation type="unfinished"></translation>
<translation>Aliados en el Mapa de aventuras</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="552"/>
<source>Adventure Map Enemies</source>
<translation type="unfinished"></translation>
<translation>Enemigos en el Mapa de aventuras</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="428"/>
<source>Windowed</source>
<translation type="unfinished"></translation>
<translation>Ventana</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="433"/>
<source>Borderless fullscreen</source>
<translation type="unfinished"></translation>
<translation>Ventana completa sin bordes</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="438"/>
<source>Exclusive fullscreen</source>
<translation type="unfinished"></translation>
<translation>Pantalla completa</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="593"/>
<source>Autosave limit (0 = off)</source>
<translation type="unfinished"></translation>
<translation>Límite de autosaves (0 = sin límite)</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="401"/>
<source>Friendly AI in battles</source>
<translation type="unfinished"></translation>
<translation>IA amistosa en batallas</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="370"/>
<source>Framerate Limit</source>
<translation type="unfinished"></translation>
<translation>Límite de fotogramas</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="460"/>
<source>Autosave prefix</source>
<translation type="unfinished"></translation>
<translation>Prefijo autoguardado</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="484"/>
<source>empty = map name prefix</source>
<translation type="unfinished"></translation>
<translation>Vacio = prefijo del mapa</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="607"/>
<source>Refresh now</source>
<translation type="unfinished"></translation>
<translation>Actualizar</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="614"/>
<source>Default repository</source>
<translation type="unfinished"></translation>
<translation>Repositorio por defecto</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="624"/>
<source>Renderer</source>
<translation type="unfinished"></translation>
<translation>Render</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="246"/>
<location filename="../settingsView/csettingsview_moc.ui" line="476"/>
<location filename="../settingsView/csettingsview_moc.ui" line="544"/>
<source>On</source>
<translation>Encendido</translation>
<translation>Activado</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="115"/>
@ -584,7 +591,7 @@ Install successfully downloaded?</source>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="349"/>
<source>Reserved screen area</source>
<translation type="unfinished"></translation>
<translation>Área de pantalla reservada</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="155"/>
@ -614,7 +621,7 @@ Install successfully downloaded?</source>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="562"/>
<source>VSync</source>
<translation type="unfinished"></translation>
<translation>Sincronización vertical</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="135"/>
@ -640,7 +647,13 @@ Windowed - game will run inside a window that covers part of your screen
Borderless Windowed Mode - game will run in a window that covers entirely of your screen, using same resolution as your screen.
Fullscreen Exclusive Mode - game will cover entirety of your screen and will use selected resolution.</source>
<translation type="unfinished"></translation>
<translation>Selecciona el modo de visualización del juego
En ventana - el juego se ejecutará dentro de una ventana que forma parte de tu pantalla.
Ventana sin bordes - el juego se ejecutará en una ventana que cubre completamente tu pantalla, usando la misma resolución.
Pantalla completa - el juego cubrirá la totalidad de la pantalla y utilizará la resolución seleccionada.</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="522"/>
@ -764,12 +777,12 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use
<message>
<location filename="../firstLaunch/firstlaunch_moc.ui" line="702"/>
<source>Interface Improvements</source>
<translation type="unfinished"></translation>
<translation>Mejora de la interfaz</translation>
</message>
<message>
<location filename="../firstLaunch/firstlaunch_moc.ui" line="772"/>
<source>Install mod that provides various interface improvements, such as better interface for random maps and selectable actions in battles</source>
<translation type="unfinished"></translation>
<translation>Instalar mod que proporciona varias mejoras en la interfaz, como mejor interacción en los mapas aleatorios y más opciones en las batallas</translation>
</message>
<message>
<location filename="../firstLaunch/firstlaunch_moc.ui" line="127"/>
@ -882,12 +895,12 @@ Ten en cuenta que para usar VCMI debes ser dueño de los archivos de datos origi
<message>
<location filename="../firstLaunch/firstlaunch_moc.cpp" line="143"/>
<source>Heroes III installation found!</source>
<translation type="unfinished"></translation>
<translation>Instalación de Heroes III encontrada!</translation>
</message>
<message>
<location filename="../firstLaunch/firstlaunch_moc.cpp" line="143"/>
<source>Copy data to VCMI folder?</source>
<translation type="unfinished"></translation>
<translation>Copiar datos a la carpeta VCMI?</translation>
</message>
</context>
<context>
@ -903,7 +916,7 @@ Ten en cuenta que para usar VCMI debes ser dueño de los archivos de datos origi
<message>
<location filename="../languages.cpp" line="23"/>
<source>Czech</source>
<translation type="unfinished"></translation>
<translation>Czech (Checo)</translation>
</message>
<message>
<location filename="../languages.cpp" line="24"/>
@ -918,7 +931,7 @@ Ten en cuenta que para usar VCMI debes ser dueño de los archivos de datos origi
<message>
<location filename="../languages.cpp" line="26"/>
<source>Finnish</source>
<translation type="unfinished"></translation>
<translation>Finnish (Finlandés)</translation>
</message>
<message>
<location filename="../languages.cpp" line="27"/>
@ -933,12 +946,12 @@ Ten en cuenta que para usar VCMI debes ser dueño de los archivos de datos origi
<message>
<location filename="../languages.cpp" line="29"/>
<source>Hungarian</source>
<translation type="unfinished"></translation>
<translation>Hungarian (Húngaro)</translation>
</message>
<message>
<location filename="../languages.cpp" line="30"/>
<source>Italian</source>
<translation type="unfinished"></translation>
<translation>Italian (Italiano)</translation>
</message>
<message>
<location filename="../languages.cpp" line="31"/>
@ -953,7 +966,7 @@ Ten en cuenta que para usar VCMI debes ser dueño de los archivos de datos origi
<message>
<location filename="../languages.cpp" line="33"/>
<source>Portuguese</source>
<translation type="unfinished"></translation>
<translation>Portuguese (Portugués)</translation>
</message>
<message>
<location filename="../languages.cpp" line="34"/>
@ -968,12 +981,12 @@ Ten en cuenta que para usar VCMI debes ser dueño de los archivos de datos origi
<message>
<location filename="../languages.cpp" line="36"/>
<source>Swedish</source>
<translation type="unfinished"></translation>
<translation>Swedish (Sueco)</translation>
</message>
<message>
<location filename="../languages.cpp" line="37"/>
<source>Turkish</source>
<translation type="unfinished"></translation>
<translation>Turkish (Turco)</translation>
</message>
<message>
<location filename="../languages.cpp" line="38"/>
@ -983,7 +996,7 @@ Ten en cuenta que para usar VCMI debes ser dueño de los archivos de datos origi
<message>
<location filename="../languages.cpp" line="39"/>
<source>Vietnamese</source>
<translation type="unfinished"></translation>
<translation>Vietnamese (Vietnamita)</translation>
</message>
<message>
<location filename="../languages.cpp" line="40"/>
@ -993,12 +1006,12 @@ Ten en cuenta que para usar VCMI debes ser dueño de los archivos de datos origi
<message>
<location filename="../languages.cpp" line="41"/>
<source>Other (Cyrillic Script)</source>
<translation type="unfinished"></translation>
<translation>Otro (Escritura cirílica)</translation>
</message>
<message>
<location filename="../languages.cpp" line="42"/>
<source>Other (West European)</source>
<translation type="unfinished"></translation>
<translation>Otro (Europa del Este)</translation>
</message>
<message>
<location filename="../languages.cpp" line="64"/>
@ -1021,7 +1034,7 @@ Ten en cuenta que para usar VCMI debes ser dueño de los archivos de datos origi
<message>
<location filename="../mainwindow_moc.ui" line="157"/>
<source>Help</source>
<translation type="unfinished"></translation>
<translation>Ayuda</translation>
</message>
<message>
<location filename="../mainwindow_moc.ui" line="226"/>

View File

@ -179,7 +179,7 @@ struct DLL_LINKAGE ArtSlotInfo
ArtSlotInfo() : locked(false) {}
const CArtifactInstance * getArt() const;
template <typename Handler> void serialize(Handler & h, const int version)
template <typename Handler> void serialize(Handler & h)
{
h & artifact;
h & locked;
@ -223,7 +223,7 @@ public:
virtual void removeArtifact(ArtifactPosition slot);
virtual ~CArtifactSet();
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & artifactsInBackpack;
h & artifactsWorn;

View File

@ -27,7 +27,7 @@ public:
{
ConstTransitivePtr<CArtifactInstance> art;
ArtifactPosition slot;
template <typename Handler> void serialize(Handler & h, const int version)
template <typename Handler> void serialize(Handler & h)
{
h & art;
h & slot;
@ -41,7 +41,7 @@ public:
const std::vector<PartInfo> & getPartsInfo() const;
void addPlacementMap(CArtifactSet::ArtPlacementMap & placementMap);
template <typename Handler> void serialize(Handler & h, const int version)
template <typename Handler> void serialize(Handler & h)
{
h & partsInfo;
}
@ -93,7 +93,7 @@ public:
void move(CArtifactSet & srcSet, const ArtifactPosition srcSlot, CArtifactSet & dstSet, const ArtifactPosition dstSlot);
void deserializationFix();
template <typename Handler> void serialize(Handler & h, const int version)
template <typename Handler> void serialize(Handler & h)
{
h & static_cast<CBonusSystemNode&>(*this);
h & static_cast<CCombinedArtifactInstance&>(*this);

View File

@ -76,10 +76,10 @@ std::string CBonusTypeHandler::bonusToString(const std::shared_ptr<Bonus> & bonu
if (text.find("${val}") != std::string::npos)
boost::algorithm::replace_all(text, "${val}", std::to_string(bearer->valOfBonuses(Selector::typeSubtype(bonus->type, bonus->subtype))));
if (text.find("${subtype.creature}") != std::string::npos && bonus->subtype.as<CreatureID>() != CreatureID::NONE)
if (text.find("${subtype.creature}") != std::string::npos && bonus->subtype.as<CreatureID>().hasValue())
boost::algorithm::replace_all(text, "${subtype.creature}", bonus->subtype.as<CreatureID>().toCreature()->getNamePluralTranslated());
if (text.find("${subtype.spell}") != std::string::npos && bonus->subtype.as<SpellID>() != SpellID::NONE)
if (text.find("${subtype.spell}") != std::string::npos && bonus->subtype.as<SpellID>().hasValue())
boost::algorithm::replace_all(text, "${subtype.spell}", bonus->subtype.as<SpellID>().toSpell()->getNameTranslated());
return text;
@ -95,8 +95,11 @@ ImagePath CBonusTypeHandler::bonusToGraphics(const std::shared_ptr<Bonus> & bonu
case BonusType::SPELL_IMMUNITY:
{
fullPath = true;
const CSpell * sp = bonus->subtype.as<SpellID>().toSpell();
fileName = sp->getIconImmune();
if (bonus->subtype.as<SpellID>().hasValue())
{
const CSpell * sp = bonus->subtype.as<SpellID>().toSpell();
fileName = sp->getIconImmune();
}
break;
}
case BonusType::SPELL_DAMAGE_REDUCTION: //Spell damage reduction for all schools

View File

@ -27,7 +27,7 @@ public:
std::string getNameTextID() const;
std::string getDescriptionTextID() const;
template <typename Handler> void serialize(Handler & h, const int version)
template <typename Handler> void serialize(Handler & h)
{
h & icon;
h & identifier;
@ -53,7 +53,7 @@ public:
std::string bonusToString(const std::shared_ptr<Bonus> & bonus, const IBonusBearer * bearer, bool description) const override;
ImagePath bonusToGraphics(const std::shared_ptr<Bonus> & bonus) const override;
template <typename Handler> void serialize(Handler & h, const int version)
template <typename Handler> void serialize(Handler & h)
{
//for now always use up to date configuration
//once modded bonus type will be implemented, serialize only them

View File

@ -46,7 +46,7 @@ public:
friend bool operator== (const CStackBasicDescriptor & l, const CStackBasicDescriptor & r);
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
if(h.saving)
{
@ -85,7 +85,7 @@ public:
const CArmedInstance * const & armyObj; //stack must be part of some army, army must be part of some object
TExpType experience;//commander needs same amount of exp as hero
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & static_cast<CBonusSystemNode&>(*this);
h & static_cast<CStackBasicDescriptor&>(*this);
@ -157,7 +157,7 @@ public:
int getLevel() const override;
ArtBearer::ArtBearer bearerType() const override; //from CArtifactSet
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & static_cast<CStackInstance&>(*this);
h & alive;
@ -197,7 +197,7 @@ public:
bool setCreature(SlotID slot, CreatureID cre, TQuantity count) override;
operator bool() const;
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & army;
}
@ -280,7 +280,7 @@ public:
bool contains(const CStackInstance *stack) const;
bool canBeMergedWith(const CCreatureSet &cs, bool allowMergingStacks = true) const;
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & stacks;
h & formation;

View File

@ -243,9 +243,8 @@ void CAdventureAI::yourTacticPhase(const BattleID & battleID, int distance)
battleAI->yourTacticPhase(battleID, distance);
}
void CAdventureAI::saveGame(BinarySerializer & h, const int version) /*saving */
void CAdventureAI::saveGame(BinarySerializer & h) /*saving */
{
LOG_TRACE_PARAMS(logAi, "version '%i'", version);
bool hasBattleAI = static_cast<bool>(battleAI);
h & hasBattleAI;
if(hasBattleAI)
@ -254,9 +253,8 @@ void CAdventureAI::saveGame(BinarySerializer & h, const int version) /*saving */
}
}
void CAdventureAI::loadGame(BinaryDeserializer & h, const int version) /*loading */
void CAdventureAI::loadGame(BinaryDeserializer & h) /*loading */
{
LOG_TRACE_PARAMS(logAi, "version '%i'", version);
bool hasBattleAI = false;
h & hasBattleAI;
if(hasBattleAI)

View File

@ -111,8 +111,8 @@ public:
virtual std::optional<BattleAction> makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState) = 0;
virtual void saveGame(BinarySerializer & h, const int version) = 0;
virtual void loadGame(BinaryDeserializer & h, const int version) = 0;
virtual void saveGame(BinarySerializer & h) = 0;
virtual void loadGame(BinaryDeserializer & h) = 0;
};
class DLL_LINKAGE CDynLibHandler
@ -162,8 +162,8 @@ public:
virtual void battleEnd(const BattleID & battleID, const BattleResult *br, QueryID queryID) override;
virtual void battleUnitsChanged(const BattleID & battleID, const std::vector<UnitChanges> & units) override;
virtual void saveGame(BinarySerializer & h, const int version) override;
virtual void loadGame(BinaryDeserializer & h, const int version) override;
virtual void saveGame(BinarySerializer & h) override;
virtual void loadGame(BinaryDeserializer & h) override;
};
VCMI_LIB_NAMESPACE_END

View File

@ -135,7 +135,7 @@ protected:
std::string modContext;
template <typename Handler>
void serialize(Handler & h, const int Version)
void serialize(Handler & h)
{
h & baseValue;
h & baseLanguage;
@ -193,7 +193,7 @@ public:
void jsonSerialize(JsonNode & dest) const;
template <typename Handler>
void serialize(Handler & h, const int Version)
void serialize(Handler & h)
{
std::string key;
auto sz = stringsLocalizations.size();

View File

@ -40,10 +40,10 @@ struct DLL_LINKAGE PlayerState : public CBonusSystemNode, public Player
return subID < other.subID;
}
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & id;
subID.serializeIdentifier(h, id, version);
subID.serializeIdentifier(h, id);
}
};
@ -89,7 +89,7 @@ public:
return heroes.empty() && towns.empty();
}
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & color;
h & human;
@ -123,7 +123,7 @@ public:
TeamState();
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & id;
h & players;

View File

@ -87,7 +87,7 @@ private:
public:
template <typename Handler>
void serialize(Handler & h, const int version)
void serialize(Handler & h)
{
if(h.saving)
{

View File

@ -93,7 +93,7 @@ public:
return this->owner;
}
template <typename Handler> void serialize(Handler & h, const int version)
template <typename Handler> void serialize(Handler & h)
{
//this assumes that stack objects is newly created
//stackState is not serialized here

View File

@ -50,7 +50,7 @@ public:
{}
template <typename Handler>
void serialize(Handler &h, const int version)
void serialize(Handler &h)
{
h & r;
h & g;

View File

@ -69,7 +69,7 @@ public:
ptr = nullptr;
}
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & ptr;
}

View File

@ -20,7 +20,7 @@ struct DLL_LINKAGE ExtraOptionsInfo
bool operator == (const ExtraOptionsInfo & other) const;
template <typename Handler>
void serialize(Handler &h, const int version)
void serialize(Handler &h)
{
h & cheatsAllowed;
h & unlimitedReplay;

View File

@ -74,6 +74,7 @@ void GameSettings::load(const JsonNode & input)
{EGameSettings::HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS, "heroes", "retreatOnWinWithoutTroops" },
{EGameSettings::HEROES_STARTING_STACKS_CHANCES, "heroes", "startingStackChances" },
{EGameSettings::HEROES_BACKPACK_CAP, "heroes", "backpackSize" },
{EGameSettings::HEROES_TAVERN_INVITE, "heroes", "tavernInvite" },
{EGameSettings::MAP_FORMAT_RESTORATION_OF_ERATHIA, "mapFormat", "restorationOfErathia" },
{EGameSettings::MAP_FORMAT_ARMAGEDDONS_BLADE, "mapFormat", "armageddonsBlade" },
{EGameSettings::MAP_FORMAT_SHADOW_OF_DEATH, "mapFormat", "shadowOfDeath" },

View File

@ -38,6 +38,7 @@ enum class EGameSettings
HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS,
HEROES_STARTING_STACKS_CHANCES,
HEROES_BACKPACK_CAP,
HEROES_TAVERN_INVITE,
MARKETS_BLACK_MARKET_RESTOCK_PERIOD,
BANKS_SHOW_GUARDS_COMPOSITION,
MODULE_COMMANDERS,
@ -96,7 +97,7 @@ public:
const JsonNode & getValue(EGameSettings option) const override;
template<typename Handler>
void serialize(Handler & h, const int version)
void serialize(Handler & h)
{
h & gameSettings;
}

View File

@ -118,7 +118,7 @@ public:
std::string toJson(bool compact = false) const;
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & meta;
h & flags;

View File

@ -80,7 +80,7 @@ VCMI_LIB_NAMESPACE_BEGIN
IdentifierType JsonRandom::decodeKey(const std::string & modScope, const std::string & value, const Variables & variables)
{
if (value.empty() || value[0] != '@')
return IdentifierType(*VLC->identifiers()->getIdentifier(modScope, IdentifierType::entityType(), value));
return IdentifierType(VLC->identifiers()->getIdentifier(modScope, IdentifierType::entityType(), value).value_or(-1));
else
return loadVariable(IdentifierType::entityType(), value, variables, IdentifierType::NONE);
}
@ -89,7 +89,7 @@ VCMI_LIB_NAMESPACE_BEGIN
IdentifierType JsonRandom::decodeKey(const JsonNode & value, const Variables & variables)
{
if (value.String().empty() || value.String()[0] != '@')
return IdentifierType(*VLC->identifiers()->getIdentifier(IdentifierType::entityType(), value));
return IdentifierType(VLC->identifiers()->getIdentifier(IdentifierType::entityType(), value).value_or(-1));
else
return loadVariable(IdentifierType::entityType(), value.String(), variables, IdentifierType::NONE);
}

View File

@ -57,7 +57,7 @@ namespace LogicalExpressionDetail
}
template <typename Handler>
void serialize(Handler & h, const int version)
void serialize(Handler & h)
{
h & expressions;
}
@ -614,7 +614,7 @@ public:
}
template <typename Handler>
void serialize(Handler & h, const int version)
void serialize(Handler & h)
{
h & data;
}

View File

@ -125,7 +125,7 @@ public:
void serializeJson(JsonSerializeFormat & handler);
template <typename Handler> void serialize(Handler & h, const int version)
template <typename Handler> void serialize(Handler & h)
{
h & exactStrings;
h & localStrings;

View File

@ -122,7 +122,7 @@ public:
}
template <typename Handler>
void serialize(Handler &h, const int version)
void serialize(Handler &h)
{
h & x;
h & y;

View File

@ -163,7 +163,7 @@ public:
DLL_LINKAGE Rect include(const Rect & other) const;
template <typename Handler>
void serialize(Handler &h, const int version)
void serialize(Handler &h)
{
h & x;
h & y;

View File

@ -180,7 +180,7 @@ public:
// return true;
// }
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & container;
}

View File

@ -108,7 +108,7 @@ public:
void run(std::shared_ptr<Pool> pool) const override;
template <typename Handler> void serialize(Handler & h, const int version)
template <typename Handler> void serialize(Handler & h)
{
JsonNode state;
if(h.saving)

View File

@ -41,7 +41,7 @@ struct DLL_LINKAGE SimturnsInfo
}
template <typename Handler>
void serialize(Handler &h, const int version)
void serialize(Handler &h)
{
h & requiredTurns;
h & optionalTurns;
@ -76,7 +76,7 @@ struct DLL_LINKAGE PlayerSettings
std::set<ui8> connectedPlayerIDs; //Empty - AI, or connectrd player ids
bool compOnly; //true if this player is a computer only player; required for RMG
template <typename Handler>
void serialize(Handler &h, const int version)
void serialize(Handler &h)
{
h & castle;
h & hero;
@ -137,7 +137,7 @@ struct DLL_LINKAGE StartInfo
std::string getCampaignName() const;
template <typename Handler>
void serialize(Handler &h, const int version)
void serialize(Handler &h)
{
h & mode;
h & difficulty;
@ -149,7 +149,7 @@ struct DLL_LINKAGE StartInfo
h & fileURI;
h & simturnsInfo;
h & turnTimerInfo;
if(version >= 832)
if(h.version >= Handler::Version::HAS_EXTRA_OPTIONS)
h & extraOptionsInfo;
else
extraOptionsInfo = ExtraOptionsInfo();
@ -170,7 +170,7 @@ struct ClientPlayer
int connection;
std::string name;
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & connection;
h & name;
@ -190,7 +190,7 @@ struct DLL_LINKAGE LobbyState
LobbyState() : si(new StartInfo()), hostClientId(-1), campaignMap(CampaignScenarioID::NONE), campaignBonus(-1) {}
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & si;
h & mi;

View File

@ -26,7 +26,7 @@ struct DLL_LINKAGE TerrainPaletteAnimation
/// total numbers of colors to cycle
int32_t length;
template <typename Handler> void serialize(Handler& h, const int version)
template <typename Handler> void serialize(Handler& h)
{
h & start;
h & length;

View File

@ -34,7 +34,7 @@ struct DLL_LINKAGE TurnTimerInfo
bool operator == (const TurnTimerInfo & other) const;
template <typename Handler>
void serialize(Handler &h, const int version)
void serialize(Handler &h)
{
h & turnTimer;
h & baseTimer;

View File

@ -55,7 +55,7 @@ public:
battle::Target getTarget(const CBattleInfoCallback * cb) const;
void setTarget(const battle::Target & target_);
template <typename Handler> void serialize(Handler & h, const int version)
template <typename Handler> void serialize(Handler & h)
{
h & side;
h & stackNumber;
@ -70,7 +70,7 @@ private:
int32_t unitValue;
BattleHex hexValue;
template <typename Handler> void serialize(Handler & h, const int version)
template <typename Handler> void serialize(Handler & h)
{
h & unitValue;
h & hexValue;

View File

@ -105,7 +105,7 @@ struct DLL_LINKAGE BattleHex //TODO: decide if this should be changed to class f
static BattleHex getClosestTile(ui8 side, BattleHex initialPos, std::set<BattleHex> & possibilities); //TODO: vector or set? copying one to another is bad
template <typename Handler>
void serialize(Handler &h, const int version)
void serialize(Handler &h)
{
h & hex;
}

View File

@ -50,7 +50,7 @@ public:
ui8 tacticsSide; //which side is requested to play tactics phase
ui8 tacticDistance; //how many hexes we can go forward (1 = only hexes adjacent to margin line)
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & battleID;
h & sides;
@ -66,10 +66,7 @@ public:
h & tacticsSide;
h & tacticDistance;
h & static_cast<CBonusSystemNode&>(*this);
if (version > 824)
h & replayAllowed;
else
replayAllowed = false;
h & replayAllowed;
}
//////////////////////////////////////////////////////////////////////////

View File

@ -867,9 +867,10 @@ bool CBattleInfoCallback::handleObstacleTriggersForUnit(SpellCastEnvironment & s
auto shouldReveal = !spellObstacle->hidden || !battleIsObstacleVisibleForSide(*obstacle, (BattlePerspective::BattlePerspective)side);
const auto * hero = battleGetFightingHero(spellObstacle->casterSide);
auto caster = spells::ObstacleCasterProxy(getBattle()->getSidePlayer(spellObstacle->casterSide), hero, *spellObstacle);
const auto * sp = obstacle->getTrigger().toSpell();
if(obstacle->triggersEffects() && sp)
if(obstacle->triggersEffects() && obstacle->getTrigger().hasValue())
{
const auto * sp = obstacle->getTrigger().toSpell();
auto cast = spells::BattleCast(this, &caster, spells::Mode::PASSIVE, sp);
spells::detail::ProblemImpl ignored;
auto target = spells::Target(1, spells::Destination(&unit));

View File

@ -36,7 +36,7 @@ struct DLL_LINKAGE AttackableTiles
{
std::set<BattleHex> hostileCreaturePositions;
std::set<BattleHex> friendlyCreaturePositions; //for Dragon Breath
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & hostileCreaturePositions;
h & friendlyCreaturePositions;

View File

@ -63,7 +63,7 @@ struct DLL_LINKAGE CObstacleInstance
virtual void serializeJson(JsonSerializeFormat & handler);
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & ID;
h & pos;
@ -118,7 +118,7 @@ struct DLL_LINKAGE SpellCreatedObstacle : CObstacleInstance
void serializeJson(JsonSerializeFormat & handler) override;
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & static_cast<CObstacleInstance&>(*this);
h & turnsRemaining;

View File

@ -28,7 +28,7 @@ struct DLL_LINKAGE SideInBattle
void init(const CGHeroInstance * Hero, const CArmedInstance * Army);
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & color;
h & hero;

View File

@ -23,7 +23,7 @@ struct DLL_LINKAGE SiegeInfo
// return EWallState decreased by value of damage points
static EWallState applyDamage(EWallState state, unsigned int value);
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & wallState;
h & gateState;

View File

@ -86,7 +86,7 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, BonusSourceID sourceID, BonusSubtypeID subtype, BonusValueType ValType);
Bonus() = default;
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & duration;
h & type;

View File

@ -91,7 +91,7 @@ public:
void insert(TInternalContainer::iterator position, TInternalContainer::size_type n, const std::shared_ptr<Bonus> & x);
template <typename Handler>
void serialize(Handler &h, const int version)
void serialize(Handler &h)
{
h & static_cast<TInternalContainer&>(bonuses);
}

View File

@ -130,7 +130,7 @@ public:
return PlayerColor::NEUTRAL;
}
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & nodeType;
h & exportedBonuses;

View File

@ -38,7 +38,7 @@ public:
virtual std::string toString() const;
virtual JsonNode toJsonNode() const;
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
}
};
@ -53,7 +53,7 @@ public:
void add(const TLimiterPtr & limiter);
JsonNode toJsonNode() const override;
template <typename Handler> void serialize(Handler & h, const int version)
template <typename Handler> void serialize(Handler & h)
{
h & static_cast<ILimiter&>(*this);
h & limiters;
@ -104,7 +104,7 @@ public:
std::string toString() const override;
JsonNode toJsonNode() const override;
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & static_cast<ILimiter&>(*this);
h & creature;
@ -132,7 +132,7 @@ public:
std::string toString() const override;
JsonNode toJsonNode() const override;
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & static_cast<ILimiter&>(*this);
h & type;
@ -156,7 +156,7 @@ public:
std::string toString() const override;
JsonNode toJsonNode() const override;
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & static_cast<ILimiter&>(*this);
h & terrainType;
@ -175,7 +175,7 @@ public:
std::string toString() const override;
JsonNode toJsonNode() const override;
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & static_cast<ILimiter&>(*this);
h & minLevel;
@ -193,7 +193,7 @@ public:
std::string toString() const override;
JsonNode toJsonNode() const override;
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & static_cast<ILimiter&>(*this);
h & faction;
@ -210,7 +210,7 @@ public:
std::string toString() const override;
JsonNode toJsonNode() const override;
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & static_cast<ILimiter&>(*this);
h & alignment;
@ -225,7 +225,7 @@ public:
EDecision limit(const BonusLimitationContext &context) const override;
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & static_cast<ILimiter&>(*this);
h & owner;
@ -242,7 +242,7 @@ public:
RankRangeLimiter(ui8 Min, ui8 Max = 255);
EDecision limit(const BonusLimitationContext &context) const override;
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & static_cast<ILimiter&>(*this);
h & minRank;
@ -259,7 +259,7 @@ public:
EDecision limit(const BonusLimitationContext &context) const override;
JsonNode toJsonNode() const override;
template <typename Handler> void serialize(Handler &h, const int version)
template <typename Handler> void serialize(Handler &h)
{
h & static_cast<ILimiter&>(*this);
h & applicableHexes;

Some files were not shown because too many files have changed in this diff Show More