1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-03-17 20:58:07 +02:00

Merge remote-tracking branch 'upstream/develop' into develop

This commit is contained in:
Xilmi 2024-10-08 16:15:22 +02:00
commit 9a40577994
179 changed files with 2854 additions and 2279 deletions

View File

@ -394,7 +394,7 @@ BattleAction BattleEvaluator::goTowardsNearest(const CStack * stack, std::vector
{
std::set<BattleHex> obstacleHexes;
auto insertAffected = [](const CObstacleInstance & spellObst, std::set<BattleHex> obstacleHexes) {
auto insertAffected = [](const CObstacleInstance & spellObst, std::set<BattleHex> & obstacleHexes) {
auto affectedHexes = spellObst.getAffectedTiles();
obstacleHexes.insert(affectedHexes.cbegin(), affectedHexes.cend());
};

View File

@ -531,44 +531,44 @@ vstd::RNG * HypotheticBattle::HypotheticServerCallback::getRNG()
return &rngStub;
}
void HypotheticBattle::HypotheticServerCallback::apply(CPackForClient * pack)
void HypotheticBattle::HypotheticServerCallback::apply(CPackForClient & pack)
{
logAi->error("Package of type %s is not allowed in battle evaluation", typeid(pack).name());
}
void HypotheticBattle::HypotheticServerCallback::apply(BattleLogMessage * pack)
void HypotheticBattle::HypotheticServerCallback::apply(BattleLogMessage & pack)
{
pack->applyBattle(owner);
pack.applyBattle(owner);
}
void HypotheticBattle::HypotheticServerCallback::apply(BattleStackMoved * pack)
void HypotheticBattle::HypotheticServerCallback::apply(BattleStackMoved & pack)
{
pack->applyBattle(owner);
pack.applyBattle(owner);
}
void HypotheticBattle::HypotheticServerCallback::apply(BattleUnitsChanged * pack)
void HypotheticBattle::HypotheticServerCallback::apply(BattleUnitsChanged & pack)
{
pack->applyBattle(owner);
pack.applyBattle(owner);
}
void HypotheticBattle::HypotheticServerCallback::apply(SetStackEffect * pack)
void HypotheticBattle::HypotheticServerCallback::apply(SetStackEffect & pack)
{
pack->applyBattle(owner);
pack.applyBattle(owner);
}
void HypotheticBattle::HypotheticServerCallback::apply(StacksInjured * pack)
void HypotheticBattle::HypotheticServerCallback::apply(StacksInjured & pack)
{
pack->applyBattle(owner);
pack.applyBattle(owner);
}
void HypotheticBattle::HypotheticServerCallback::apply(BattleObstaclesChanged * pack)
void HypotheticBattle::HypotheticServerCallback::apply(BattleObstaclesChanged & pack)
{
pack->applyBattle(owner);
pack.applyBattle(owner);
}
void HypotheticBattle::HypotheticServerCallback::apply(CatapultAttack * pack)
void HypotheticBattle::HypotheticServerCallback::apply(CatapultAttack & pack)
{
pack->applyBattle(owner);
pack.applyBattle(owner);
}
HypotheticBattle::HypotheticEnvironment::HypotheticEnvironment(HypotheticBattle * owner_, const Environment * upperEnvironment)

View File

@ -189,15 +189,15 @@ private:
vstd::RNG * getRNG() override;
void apply(CPackForClient * pack) override;
void apply(CPackForClient & pack) override;
void apply(BattleLogMessage * pack) override;
void apply(BattleStackMoved * pack) override;
void apply(BattleUnitsChanged * pack) override;
void apply(SetStackEffect * pack) override;
void apply(StacksInjured * pack) override;
void apply(BattleObstaclesChanged * pack) override;
void apply(CatapultAttack * pack) override;
void apply(BattleLogMessage & pack) override;
void apply(BattleStackMoved & pack) override;
void apply(BattleUnitsChanged & pack) override;
void apply(SetStackEffect & pack) override;
void apply(StacksInjured & pack) override;
void apply(BattleObstaclesChanged & pack) override;
void apply(CatapultAttack & pack) override;
private:
HypotheticBattle * owner;
RNGStub rngStub;

View File

@ -29,20 +29,20 @@
bool CCallback::teleportHero(const CGHeroInstance *who, const CGTownInstance *where)
{
CastleTeleportHero pack(who->id, where->id, 1);
sendRequest(&pack);
sendRequest(pack);
return true;
}
void CCallback::moveHero(const CGHeroInstance *h, const int3 & destination, bool transit)
{
MoveHero pack({destination}, h->id, transit);
sendRequest(&pack);
sendRequest(pack);
}
void CCallback::moveHero(const CGHeroInstance *h, const std::vector<int3> & path, bool transit)
{
MoveHero pack(path, h->id, transit);
sendRequest(&pack);
sendRequest(pack);
}
int CCallback::selectionMade(int selection, QueryID queryID)
@ -61,7 +61,7 @@ int CCallback::sendQueryReply(std::optional<int32_t> reply, QueryID queryID)
QueryReply pack(queryID, reply);
pack.player = *player;
return sendRequest(&pack);
return sendRequest(pack);
}
void CCallback::recruitCreatures(const CGDwelling * obj, const CArmedInstance * dst, CreatureID ID, ui32 amount, si32 level)
@ -71,7 +71,7 @@ void CCallback::recruitCreatures(const CGDwelling * obj, const CArmedInstance *
return;
RecruitCreatures pack(obj->id, dst->id, ID, amount, level);
sendRequest(&pack);
sendRequest(pack);
}
bool CCallback::dismissCreature(const CArmedInstance *obj, SlotID stackPos)
@ -80,14 +80,14 @@ bool CCallback::dismissCreature(const CArmedInstance *obj, SlotID stackPos)
return false;
DisbandCreature pack(stackPos,obj->id);
sendRequest(&pack);
sendRequest(pack);
return true;
}
bool CCallback::upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID)
{
UpgradeCreature pack(stackPos,obj->id,newID);
sendRequest(&pack);
sendRequest(pack);
return false;
}
@ -95,54 +95,54 @@ void CCallback::endTurn()
{
logGlobal->trace("Player %d ended his turn.", player->getNum());
EndTurn pack;
sendRequest(&pack);
sendRequest(pack);
}
int CCallback::swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)
{
ArrangeStacks pack(1,p1,p2,s1->id,s2->id,0);
sendRequest(&pack);
sendRequest(pack);
return 0;
}
int CCallback::mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)
{
ArrangeStacks pack(2,p1,p2,s1->id,s2->id,0);
sendRequest(&pack);
sendRequest(pack);
return 0;
}
int CCallback::splitStack(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2, int val)
{
ArrangeStacks pack(3,p1,p2,s1->id,s2->id,val);
sendRequest(&pack);
sendRequest(pack);
return 0;
}
int CCallback::bulkMoveArmy(ObjectInstanceID srcArmy, ObjectInstanceID destArmy, SlotID srcSlot)
{
BulkMoveArmy pack(srcArmy, destArmy, srcSlot);
sendRequest(&pack);
sendRequest(pack);
return 0;
}
int CCallback::bulkSplitStack(ObjectInstanceID armyId, SlotID srcSlot, int howMany)
{
BulkSplitStack pack(armyId, srcSlot, howMany);
sendRequest(&pack);
sendRequest(pack);
return 0;
}
int CCallback::bulkSmartSplitStack(ObjectInstanceID armyId, SlotID srcSlot)
{
BulkSmartSplitStack pack(armyId, srcSlot);
sendRequest(&pack);
sendRequest(pack);
return 0;
}
int CCallback::bulkMergeStacks(ObjectInstanceID armyId, SlotID srcSlot)
{
BulkMergeStacks pack(armyId, srcSlot);
sendRequest(&pack);
sendRequest(pack);
return 0;
}
@ -151,7 +151,7 @@ bool CCallback::dismissHero(const CGHeroInstance *hero)
if(player!=hero->tempOwner) return false;
DismissHero pack(hero->id);
sendRequest(&pack);
sendRequest(pack);
return true;
}
@ -160,7 +160,7 @@ bool CCallback::swapArtifacts(const ArtifactLocation &l1, const ArtifactLocation
ExchangeArtifacts ea;
ea.src = l1;
ea.dst = l2;
sendRequest(&ea);
sendRequest(ea);
return true;
}
@ -175,13 +175,13 @@ bool CCallback::swapArtifacts(const ArtifactLocation &l1, const ArtifactLocation
void CCallback::assembleArtifacts(const ObjectInstanceID & heroID, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo)
{
AssembleArtifacts aa(heroID, artifactSlot, assemble, assembleTo);
sendRequest(&aa);
sendRequest(aa);
}
void CCallback::bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap, bool equipped, bool backpack)
{
BulkExchangeArtifacts bma(srcHero, dstHero, swap, equipped, backpack);
sendRequest(&bma);
sendRequest(bma);
}
void CCallback::scrollBackpackArtifacts(ObjectInstanceID hero, bool left)
@ -189,19 +189,37 @@ void CCallback::scrollBackpackArtifacts(ObjectInstanceID hero, bool left)
ManageBackpackArtifacts mba(hero, ManageBackpackArtifacts::ManageCmd::SCROLL_RIGHT);
if(left)
mba.cmd = ManageBackpackArtifacts::ManageCmd::SCROLL_LEFT;
sendRequest(&mba);
sendRequest(mba);
}
void CCallback::sortBackpackArtifactsBySlot(const ObjectInstanceID hero)
{
ManageBackpackArtifacts mba(hero, ManageBackpackArtifacts::ManageCmd::SORT_BY_SLOT);
sendRequest(mba);
}
void CCallback::sortBackpackArtifactsByCost(const ObjectInstanceID hero)
{
ManageBackpackArtifacts mba(hero, ManageBackpackArtifacts::ManageCmd::SORT_BY_COST);
sendRequest(mba);
}
void CCallback::sortBackpackArtifactsByClass(const ObjectInstanceID hero)
{
ManageBackpackArtifacts mba(hero, ManageBackpackArtifacts::ManageCmd::SORT_BY_CLASS);
sendRequest(mba);
}
void CCallback::manageHeroCostume(ObjectInstanceID hero, size_t costumeIndex, bool saveCostume)
{
ManageEquippedArtifacts mea(hero, costumeIndex, saveCostume);
sendRequest(&mea);
sendRequest(mea);
}
void CCallback::eraseArtifactByClient(const ArtifactLocation & al)
{
EraseArtifactByClient ea(al);
sendRequest(&ea);
sendRequest(ea);
}
bool CCallback::buildBuilding(const CGTownInstance *town, BuildingID buildingID)
@ -213,7 +231,7 @@ bool CCallback::buildBuilding(const CGTownInstance *town, BuildingID buildingID)
return false;
BuildStructure pack(town->id,buildingID);
sendRequest(&pack);
sendRequest(pack);
return true;
}
@ -223,7 +241,7 @@ bool CCallback::visitTownBuilding(const CGTownInstance *town, BuildingID buildin
return false;
VisitTownBuilding pack(town->id, buildingID);
sendRequest(&pack);
sendRequest(pack);
return true;
}
@ -232,10 +250,10 @@ void CBattleCallback::battleMakeSpellAction(const BattleID & battleID, const Bat
assert(action.actionType == EActionType::HERO_SPELL);
MakeAction mca(action);
mca.battleID = battleID;
sendRequest(&mca);
sendRequest(mca);
}
int CBattleCallback::sendRequest(const CPackForServer * request)
int CBattleCallback::sendRequest(const CPackForServer & request)
{
int requestID = cl->sendRequest(request, *getPlayerID());
if(waitTillRealize)
@ -249,12 +267,18 @@ int CBattleCallback::sendRequest(const CPackForServer * request)
return requestID;
}
void CCallback::spellResearch( const CGTownInstance *town, SpellID spellAtSlot, bool accepted )
{
SpellResearch pack(town->id, spellAtSlot, accepted);
sendRequest(pack);
}
void CCallback::swapGarrisonHero( const CGTownInstance *town )
{
if(town->tempOwner == *player || (town->garrisonHero && town->garrisonHero->tempOwner == *player ))
{
GarrisonHeroSwap pack(town->id);
sendRequest(&pack);
sendRequest(pack);
}
}
@ -263,7 +287,7 @@ void CCallback::buyArtifact(const CGHeroInstance *hero, ArtifactID aid)
if(hero->tempOwner != *player) return;
BuyArtifact pack(hero->id,aid);
sendRequest(&pack);
sendRequest(pack);
}
void CCallback::trade(const ObjectInstanceID marketId, EMarketMode mode, TradeItemSell id1, TradeItemBuy id2, ui32 val1, const CGHeroInstance * hero)
@ -280,13 +304,13 @@ void CCallback::trade(const ObjectInstanceID marketId, EMarketMode mode, const s
pack.r1 = id1;
pack.r2 = id2;
pack.val = val1;
sendRequest(&pack);
sendRequest(pack);
}
void CCallback::setFormation(const CGHeroInstance * hero, EArmyFormation mode)
{
SetFormation pack(hero->id, mode);
sendRequest(&pack);
sendRequest(pack);
}
void CCallback::recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero, const HeroTypeID & nextHero)
@ -296,7 +320,7 @@ void CCallback::recruitHero(const CGObjectInstance *townOrTavern, const CGHeroIn
HireHero pack(hero->getHeroType(), townOrTavern->id, nextHero);
pack.player = *player;
sendRequest(&pack);
sendRequest(pack);
}
void CCallback::save( const std::string &fname )
@ -310,7 +334,7 @@ void CCallback::gamePause(bool pause)
{
GamePause pack;
pack.player = *player;
sendRequest(&pack);
sendRequest(pack);
}
else
{
@ -324,14 +348,14 @@ void CCallback::sendMessage(const std::string &mess, const CGObjectInstance * cu
PlayerMessage pm(mess, currentObject? currentObject->id : ObjectInstanceID(-1));
if(player)
pm.player = *player;
sendRequest(&pm);
sendRequest(pm);
}
void CCallback::buildBoat( const IShipyard *obj )
{
BuildBoat bb;
bb.objid = dynamic_cast<const CGObjectInstance*>(obj)->id;
sendRequest(&bb);
sendRequest(bb);
}
CCallback::CCallback(CGameState * GS, std::optional<PlayerColor> Player, CClient * C)
@ -373,7 +397,7 @@ void CCallback::dig( const CGObjectInstance *hero )
{
DigWithHero dwh;
dwh.id = hero->id;
sendRequest(&dwh);
sendRequest(dwh);
}
void CCallback::castSpell(const CGHeroInstance *hero, SpellID spellID, const int3 &pos)
@ -382,7 +406,7 @@ void CCallback::castSpell(const CGHeroInstance *hero, SpellID spellID, const int
cas.hid = hero->id;
cas.sid = spellID;
cas.pos = pos;
sendRequest(&cas);
sendRequest(cas);
}
int CCallback::mergeOrSwapStacks(const CArmedInstance *s1, const CArmedInstance *s2, SlotID p1, SlotID p2)
@ -415,7 +439,7 @@ void CBattleCallback::battleMakeUnitAction(const BattleID & battleID, const Batt
MakeAction ma;
ma.ba = action;
ma.battleID = battleID;
sendRequest(&ma);
sendRequest(ma);
}
void CBattleCallback::battleMakeTacticAction(const BattleID & battleID, const BattleAction & action )
@ -424,7 +448,7 @@ void CBattleCallback::battleMakeTacticAction(const BattleID & battleID, const Ba
MakeAction ma;
ma.ba = action;
ma.battleID = battleID;
sendRequest(&ma);
sendRequest(ma);
}
std::optional<BattleAction> CBattleCallback::makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState)

View File

@ -78,6 +78,7 @@ public:
virtual bool visitTownBuilding(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
virtual void spellResearch(const CGTownInstance *town, SpellID spellAtSlot, bool accepted)=0;
virtual void swapGarrisonHero(const CGTownInstance *town)=0;
virtual void trade(const ObjectInstanceID marketId, EMarketMode mode, TradeItemSell id1, TradeItemBuy id2, ui32 val1, const CGHeroInstance * hero)=0; //mode==0: sell val1 units of id1 resource for id2 resiurce
@ -92,6 +93,9 @@ public:
//virtual bool swapArtifacts(const CGHeroInstance * hero1, ui16 pos1, const CGHeroInstance * hero2, ui16 pos2)=0; //swaps artifacts between two given heroes
virtual bool swapArtifacts(const ArtifactLocation &l1, const ArtifactLocation &l2)=0;
virtual void scrollBackpackArtifacts(ObjectInstanceID hero, bool left) = 0;
virtual void sortBackpackArtifactsBySlot(const ObjectInstanceID hero) = 0;
virtual void sortBackpackArtifactsByCost(const ObjectInstanceID hero) = 0;
virtual void sortBackpackArtifactsByClass(const ObjectInstanceID hero) = 0;
virtual void manageHeroCostume(ObjectInstanceID hero, size_t costumeIndex, bool saveCostume) = 0;
virtual void assembleArtifacts(const ObjectInstanceID & heroID, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo)=0;
virtual void eraseArtifactByClient(const ArtifactLocation & al)=0;
@ -123,7 +127,7 @@ class CBattleCallback : public IBattleCallback
std::optional<PlayerColor> player;
protected:
int sendRequest(const CPackForServer * request); //returns requestID (that'll be matched to requestID in PackageApplied)
int sendRequest(const CPackForServer & request); //returns requestID (that'll be matched to requestID in PackageApplied)
CClient *cl;
public:
@ -179,6 +183,9 @@ public:
void assembleArtifacts(const ObjectInstanceID & heroID, ArtifactPosition artifactSlot, bool assemble, ArtifactID assembleTo) override;
void bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID dstHero, bool swap, bool equipped = true, bool backpack = true) override;
void scrollBackpackArtifacts(ObjectInstanceID hero, bool left) override;
void sortBackpackArtifactsBySlot(const ObjectInstanceID hero) override;
void sortBackpackArtifactsByCost(const ObjectInstanceID hero) override;
void sortBackpackArtifactsByClass(const ObjectInstanceID hero) override;
void manageHeroCostume(ObjectInstanceID hero, size_t costumeIdx, bool saveCostume) override;
void eraseArtifactByClient(const ArtifactLocation & al) override;
bool buildBuilding(const CGTownInstance *town, BuildingID buildingID) override;
@ -187,6 +194,7 @@ public:
bool dismissCreature(const CArmedInstance *obj, SlotID stackPos) override;
bool upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID=CreatureID::NONE) override;
void endTurn() override;
void spellResearch(const CGTownInstance *town, SpellID spellAtSlot, bool accepted) override;
void swapGarrisonHero(const CGTownInstance *town) override;
void buyArtifact(const CGHeroInstance *hero, ArtifactID aid) override;
void trade(const ObjectInstanceID marketId, EMarketMode mode, TradeItemSell id1, TradeItemBuy id2, ui32 val1, const CGHeroInstance * hero = nullptr) override;

View File

@ -486,11 +486,7 @@ if(NOT FORCE_BUNDLED_MINIZIP)
endif()
if (ENABLE_CLIENT)
set(FFMPEG_COMPONENTS avutil swscale avformat avcodec)
if(APPLE_IOS AND NOT USING_CONAN)
list(APPEND FFMPEG_COMPONENTS swresample)
endif()
find_package(ffmpeg COMPONENTS ${FFMPEG_COMPONENTS})
find_package(ffmpeg COMPONENTS avutil swscale avformat avcodec swresample)
find_package(SDL2 REQUIRED)
find_package(SDL2_image REQUIRED)
@ -666,6 +662,10 @@ if(NOT TARGET minizip::minizip)
add_library(minizip::minizip ALIAS minizip)
endif()
if(ENABLE_LAUNCHER OR ENABLE_EDITOR)
add_subdirectory(vcmiqt)
endif()
if(ENABLE_LAUNCHER)
add_subdirectory(launcher)
endif()

Binary file not shown.

After

Width:  |  Height:  |  Size: 931 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -12,7 +12,9 @@
"vcmi.adventureMap.monsterThreat.levels.9" : "压倒性的",
"vcmi.adventureMap.monsterThreat.levels.10" : "致命的",
"vcmi.adventureMap.monsterThreat.levels.11" : "无法取胜",
"vcmi.adventureMap.monsterLevel" : "\n\n%TOWN%LEVEL级生物",
"vcmi.adventureMap.monsterLevel" : "\n\n%TOWN%LEVEL级%ATTACK_TYPE生物",
"vcmi.adventureMap.monsterMeleeType" : "近战",
"vcmi.adventureMap.monsterRangedType" : "远程",
"vcmi.adventureMap.confirmRestartGame" : "你想要重新开始游戏吗?",
"vcmi.adventureMap.noTownWithMarket" : "没有足够的市场。",

View File

@ -12,7 +12,9 @@
"vcmi.adventureMap.monsterThreat.levels.9" : "Overpowering",
"vcmi.adventureMap.monsterThreat.levels.10" : "Deadly",
"vcmi.adventureMap.monsterThreat.levels.11" : "Impossible",
"vcmi.adventureMap.monsterLevel" : "\n\nLevel %LEVEL %TOWN unit",
"vcmi.adventureMap.monsterLevel" : "\n\nLevel %LEVEL %TOWN %ATTACK_TYPE unit",
"vcmi.adventureMap.monsterMeleeType" : "melee",
"vcmi.adventureMap.monsterRangedType" : "ranged",
"vcmi.adventureMap.confirmRestartGame" : "Are you sure you want to restart the game?",
"vcmi.adventureMap.noTownWithMarket" : "There are no available marketplaces!",
@ -59,6 +61,13 @@
"vcmi.spellBook.search" : "search...",
"vcmi.spellResearch.canNotAfford" : "You can't afford to replace {%SPELL1} with {%SPELL2}. But you can still discard this spell and continue spell research.",
"vcmi.spellResearch.comeAgain" : "Research has already been done today. Come back tomorrow.",
"vcmi.spellResearch.pay" : "Would you like to replace {%SPELL1} with {%SPELL2}? Or discard this spell and continue spell research?",
"vcmi.spellResearch.research" : "Research this Spell",
"vcmi.spellResearch.skip" : "Skip this Spell",
"vcmi.spellResearch.abort" : "Abort",
"vcmi.mainMenu.serverConnecting" : "Connecting...",
"vcmi.mainMenu.serverAddressEnter" : "Enter address:",
"vcmi.mainMenu.serverConnectionFailed" : "Failed to connect",
@ -143,6 +152,7 @@
"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.disconnected" : "{Network Error}\n\nConnection to game server has been lost!",
"vcmi.server.errors.playerLeft" : "{Player Left}\n\n%s player have disconnected from the game!", //%s -> player color
"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}",
"vcmi.server.errors.modsToDisable" : "{Following mods must be disabled}",
@ -340,6 +350,12 @@
"vcmi.heroWindow.openCommander.help" : "Shows details about the commander of this hero.",
"vcmi.heroWindow.openBackpack.hover" : "Open artifact backpack window",
"vcmi.heroWindow.openBackpack.help" : "Opens window that allows easier artifact backpack management.",
"vcmi.heroWindow.sortBackpackByCost.hover" : "Sort by cost",
"vcmi.heroWindow.sortBackpackByCost.help" : "Sort artifacts in backpack by cost.",
"vcmi.heroWindow.sortBackpackBySlot.hover" : "Sort by slot",
"vcmi.heroWindow.sortBackpackBySlot.help" : "Sort artifacts in backpack by equipped slot.",
"vcmi.heroWindow.sortBackpackByClass.hover" : "Sort by class",
"vcmi.heroWindow.sortBackpackByClass.help" : "Sort artifacts in backpack by artifact class. Treasure, Minor, Major, Relic",
"vcmi.tavernWindow.inviteHero" : "Invite hero",

View File

@ -12,7 +12,9 @@
"vcmi.adventureMap.monsterThreat.levels.9" : "Überwältigend",
"vcmi.adventureMap.monsterThreat.levels.10" : "Tödlich",
"vcmi.adventureMap.monsterThreat.levels.11" : "Unmöglich",
"vcmi.adventureMap.monsterLevel" : "\n\nStufe %LEVEL %TOWN-Einheit",
"vcmi.adventureMap.monsterLevel" : "\n\nStufe %LEVEL %TOWN-Einheit (%ATTACK_TYPE)",
"vcmi.adventureMap.monsterMeleeType" : "Nahkampf",
"vcmi.adventureMap.monsterRangedType" : "Fernkampf",
"vcmi.adventureMap.confirmRestartGame" : "Seid Ihr sicher, dass Ihr das Spiel neu starten wollt?",
"vcmi.adventureMap.noTownWithMarket" : "Kein Marktplatz verfügbar!",
@ -58,6 +60,13 @@
"vcmi.spellBook.search" : "suchen...",
"vcmi.spellResearch.canNotAfford" : "Ihr könnt es Euch nicht leisten, {%SPELL1} durch {%SPELL2} zu ersetzen. Aber Ihr könnt diesen Zauberspruch trotzdem verwerfen und die Zauberspruchforschung fortsetzen.",
"vcmi.spellResearch.comeAgain" : "Die Forschung wurde heute bereits abgeschlossen. Kommt morgen wieder.",
"vcmi.spellResearch.pay" : "Möchtet Ihr {%SPELL1} durch {%SPELL2} ersetzen? Oder diesen Zauberspruch verwerfen und die Zauberspruchforschung fortsetzen?",
"vcmi.spellResearch.research" : "Erforsche diesen Zauberspruch",
"vcmi.spellResearch.skip" : "Überspringe diesen Zauberspruch",
"vcmi.spellResearch.abort" : "Abbruch",
"vcmi.mainMenu.serverConnecting" : "Verbinde...",
"vcmi.mainMenu.serverAddressEnter" : "Addresse eingeben:",
"vcmi.mainMenu.serverConnectionFailed" : "Verbindung fehlgeschlagen",

View File

@ -141,7 +141,12 @@ void CServerHandler::resetStateForLobby(EStartMode mode, ESelectionScreen screen
if(!playerNames.empty()) //if have custom set of player names - use it
localPlayerNames = playerNames;
else
localPlayerNames.push_back(settings["general"]["playerName"].String());
{
std::string playerName = settings["general"]["playerName"].String();
if(playerName == "Player")
playerName = CGI->generaltexth->translate("core.genrltxt.434");
localPlayerNames.push_back(playerName);
}
gameChat->resetMatchState();
lobbyClient->resetMatchState();
@ -853,7 +858,7 @@ void CServerHandler::onPacketReceived(const std::shared_ptr<INetworkConnection>
if(getState() == EClientState::DISCONNECTING)
return;
CPack * pack = logicConnection->retrievePack(message);
auto pack = logicConnection->retrievePack(message);
ServerHandlerCPackVisitor visitor(*this);
pack->visit(visitor);
}
@ -938,14 +943,14 @@ void CServerHandler::visitForLobby(CPackForLobby & lobbyPack)
void CServerHandler::visitForClient(CPackForClient & clientPack)
{
client->handlePack(&clientPack);
client->handlePack(clientPack);
}
void CServerHandler::sendLobbyPack(const CPackForLobby & pack) const
{
if(getState() != EClientState::STARTING)
logicConnection->sendPack(&pack);
logicConnection->sendPack(pack);
}
bool CServerHandler::inLobbyRoom() const

View File

@ -25,7 +25,6 @@ struct TurnTimerInfo;
class CMapInfo;
class CGameState;
struct ClientPlayer;
struct CPack;
struct CPackForLobby;
struct CPackForClient;

View File

@ -163,7 +163,7 @@ void CClient::save(const std::string & fname)
}
SaveGame save_game(fname);
sendRequest(&save_game, PlayerColor::NEUTRAL);
sendRequest(save_game, PlayerColor::NEUTRAL);
}
void CClient::endNetwork()
@ -348,37 +348,35 @@ void CClient::installNewBattleInterface(std::shared_ptr<CBattleGameInterface> ba
}
}
void CClient::handlePack(CPackForClient * pack)
void CClient::handlePack(CPackForClient & pack)
{
ApplyClientNetPackVisitor afterVisitor(*this, *gameState());
ApplyFirstClientNetPackVisitor beforeVisitor(*this, *gameState());
pack->visit(beforeVisitor);
logNetwork->trace("\tMade first apply on cl: %s", typeid(*pack).name());
pack.visit(beforeVisitor);
logNetwork->trace("\tMade first apply on cl: %s", typeid(pack).name());
{
boost::unique_lock lock(CGameState::mutex);
gs->apply(pack);
}
logNetwork->trace("\tApplied on gs: %s", typeid(*pack).name());
pack->visit(afterVisitor);
logNetwork->trace("\tMade second apply on cl: %s", typeid(*pack).name());
delete pack;
logNetwork->trace("\tApplied on gs: %s", typeid(pack).name());
pack.visit(afterVisitor);
logNetwork->trace("\tMade second apply on cl: %s", typeid(pack).name());
}
int CClient::sendRequest(const CPackForServer * request, PlayerColor player)
int CClient::sendRequest(const CPackForServer & request, PlayerColor player)
{
static ui32 requestCounter = 1;
ui32 requestID = requestCounter++;
logNetwork->trace("Sending a request \"%s\". It'll have an ID=%d.", typeid(*request).name(), requestID);
logNetwork->trace("Sending a request \"%s\". It'll have an ID=%d.", typeid(request).name(), requestID);
waitingRequest.pushBack(requestID);
request->requestID = requestID;
request->player = player;
request.requestID = requestID;
request.player = player;
CSH->logicConnection->sendPack(request);
if(vstd::contains(playerint, player))
playerint[player]->requestSent(request, requestID);
playerint[player]->requestSent(&request, requestID);
return requestID;
}

View File

@ -16,7 +16,6 @@
VCMI_LIB_NAMESPACE_BEGIN
struct CPack;
struct CPackForServer;
class IBattleEventsReceiver;
class CBattleGameInterface;
@ -143,8 +142,8 @@ public:
static ThreadSafeVector<int> waitingRequest; //FIXME: make this normal field (need to join all threads before client destruction)
void handlePack(CPackForClient * pack); //applies the given pack and deletes it
int sendRequest(const CPackForServer * request, PlayerColor player); //returns ID given to that request
void handlePack(CPackForClient & pack); //applies the given pack and deletes it
int sendRequest(const CPackForServer & request, PlayerColor player); //returns ID given to that request
void battleStarted(const BattleInfo * info);
void battleFinished(const BattleID & battleID);
@ -159,6 +158,7 @@ public:
friend class CBattleCallback; //handling players actions
void changeSpells(const CGHeroInstance * hero, bool give, const std::set<SpellID> & spells) override {};
void setResearchedSpells(const CGTownInstance * town, int level, const std::vector<SpellID> spells, bool accepted) override {};
bool removeObject(const CGObjectInstance * obj, const PlayerColor & initiator) override {return false;};
void createBoat(const int3 & visitablePosition, BoatId type, PlayerColor initiator) override {};
void setOwner(const CGObjectInstance * obj, PlayerColor owner) override {};
@ -204,7 +204,7 @@ public:
void setManaPoints(ObjectInstanceID hid, int val) override {};
void giveHero(ObjectInstanceID id, PlayerColor player, ObjectInstanceID boatId = ObjectInstanceID()) override {};
void changeObjPos(ObjectInstanceID objid, int3 newPos, const PlayerColor & initiator) override {};
void sendAndApply(CPackForClient * pack) override {};
void sendAndApply(CPackForClient & pack) override {};
void heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2) override {};
void castSpell(const spells::Caster * caster, SpellID spellID, const int3 &pos) override {};

View File

@ -37,6 +37,7 @@ public:
void visitHeroVisitCastle(HeroVisitCastle & pack) override;
void visitSetMana(SetMana & pack) override;
void visitSetMovePoints(SetMovePoints & pack) override;
void visitSetResearchedSpells(SetResearchedSpells & pack) override;
void visitFoWChange(FoWChange & pack) override;
void visitChangeStackCount(ChangeStackCount & pack) override;
void visitSetStackType(SetStackType & pack) override;

View File

@ -14,6 +14,7 @@
#include "CPlayerInterface.h"
#include "CGameInfo.h"
#include "windows/GUIClasses.h"
#include "windows/CCastleInterface.h"
#include "mapView/mapHandler.h"
#include "adventureMap/AdventureMapInterface.h"
#include "adventureMap/CInGameConsole.h"
@ -172,6 +173,12 @@ void ApplyClientNetPackVisitor::visitSetMovePoints(SetMovePoints & pack)
callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroMovePointsChanged, h);
}
void ApplyClientNetPackVisitor::visitSetResearchedSpells(SetResearchedSpells & pack)
{
for(const auto & win : GH.windows().findWindows<CMageGuildScreen>())
win->updateSpells(pack.tid);
}
void ApplyClientNetPackVisitor::visitFoWChange(FoWChange & pack)
{
for(auto &i : cl.playerint)

View File

@ -835,9 +835,9 @@ OptionsTab::HandicapWindow::HandicapWindow()
if(i == 0)
{
if(isIncome)
labels.push_back(std::make_shared<CLabel>(xPos, 35, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->translate("core.jktext.32")));
labels.push_back(std::make_shared<CLabel>(xPos, 38, FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->translate("core.jktext.32")));
else if(isGrowth)
labels.push_back(std::make_shared<CLabel>(xPos, 35, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->translate("core.genrltxt.194")));
labels.push_back(std::make_shared<CLabel>(xPos, 38, FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, CGI->generaltexth->translate("core.genrltxt.194")));
else
anim.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("SMALRES"), GameResID(resource), 0, 15 + xPos + (j == 0 ? 10 : 0), 35));
}
@ -1035,14 +1035,13 @@ OptionsTab::PlayerOptionsEntry::PlayerOptionsEntry(const PlayerSettings & S, con
labelPlayerNameEdit = std::make_shared<CTextInput>(Rect(6, 3, 95, 15), EFonts::FONT_SMALL, ETextAlignment::CENTER, false);
labelPlayerNameEdit->setText(name);
}
const auto & font = GH.renderHandler().loadFont(FONT_SMALL);
labelWhoCanPlay = std::make_shared<CMultiLineLabel>(Rect(6, 23, 45, font->getLineHeight()*2), EFonts::FONT_TINY, ETextAlignment::TOPCENTER, Colors::WHITE, CGI->generaltexth->arraytxt[206 + whoCanPlay]);
labelWhoCanPlay = std::make_shared<CMultiLineLabel>(Rect(6, 21, 45, 26), EFonts::FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->arraytxt[206 + whoCanPlay]);
auto hasHandicap = [this](){ return s->handicap.startBonus.empty() && s->handicap.percentIncome == 100 && s->handicap.percentGrowth == 100; };
std::string labelHandicapText = hasHandicap() ? CGI->generaltexth->arraytxt[210] : MetaString::createFromTextID("vcmi.lobby.handicap").toString();
labelHandicap = std::make_shared<CMultiLineLabel>(Rect(57, 24, 47, font->getLineHeight()*2), EFonts::FONT_TINY, ETextAlignment::TOPCENTER, Colors::WHITE, labelHandicapText);
handicap = std::make_shared<LRClickableArea>(Rect(56, 24, 49, font->getLineHeight()*2), [](){
labelHandicap = std::make_shared<CMultiLineLabel>(Rect(55, 23, 46, 24), EFonts::FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, labelHandicapText);
handicap = std::make_shared<LRClickableArea>(Rect(53, 23, 50, 24), [](){
if(!CSH->isHost())
return;

View File

@ -33,7 +33,9 @@ extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libavutil/opt.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
}
// Define a set of functions to read data
@ -501,32 +503,71 @@ std::pair<std::unique_ptr<ui8 []>, si64> CAudioInstance::extractAudio(const Vide
int numChannels = codecpar->ch_layout.nb_channels;
#endif
samples.reserve(44100 * 5); // arbitrary 5-second buffer
samples.reserve(44100 * 5); // arbitrary 5-second buffer to reduce reallocations
for (;;)
if (formatProperties.isPlanar && numChannels > 1)
{
decodeNextFrame();
const AVFrame * frame = getCurrentFrame();
// Format is 'planar', which is not supported by wav / SDL
// Use swresample part of ffmpeg to deplanarize audio into format supported by wav / SDL
if (!frame)
break;
auto sourceFormat = static_cast<AVSampleFormat>(codecpar->format);
auto targetFormat = av_get_alt_sample_fmt(sourceFormat, false);
int samplesToRead = frame->nb_samples * numChannels;
int bytesToRead = samplesToRead * formatProperties.sampleSizeBytes;
SwrContext * swr_ctx = swr_alloc();
if (formatProperties.isPlanar && numChannels > 1)
#if (LIBAVUTIL_VERSION_MAJOR < 58)
av_opt_set_channel_layout(swr_ctx, "in_chlayout", codecpar->channel_layout, 0);
av_opt_set_channel_layout(swr_ctx, "out_chlayout", codecpar->channel_layout, 0);
#else
av_opt_set_chlayout(swr_ctx, "in_chlayout", &codecpar->ch_layout, 0);
av_opt_set_chlayout(swr_ctx, "out_chlayout", &codecpar->ch_layout, 0);
#endif
av_opt_set_int(swr_ctx, "in_sample_rate", codecpar->sample_rate, 0);
av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", sourceFormat, 0);
av_opt_set_int(swr_ctx, "out_sample_rate", codecpar->sample_rate, 0);
av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", targetFormat, 0);
int initResult = swr_init(swr_ctx);
if (initResult < 0)
throwFFmpegError(initResult);
std::vector<uint8_t> frameSamplesBuffer;
for (;;)
{
// Workaround for lack of resampler
// Currently, ffmpeg on conan systems is built without sws resampler
// Because of that, and because wav format does not supports 'planar' formats from ffmpeg
// we need to de-planarize it and convert to "normal" (non-planar / interleaved) stream
samples.reserve(samples.size() + bytesToRead);
for (int sm = 0; sm < frame->nb_samples; ++sm)
for (int ch = 0; ch < numChannels; ++ch)
samples.insert(samples.end(), frame->data[ch] + sm * formatProperties.sampleSizeBytes, frame->data[ch] + (sm+1) * formatProperties.sampleSizeBytes );
decodeNextFrame();
const AVFrame * frame = getCurrentFrame();
if (!frame)
break;
size_t samplesToRead = frame->nb_samples * numChannels;
size_t bytesToRead = samplesToRead * formatProperties.sampleSizeBytes;
frameSamplesBuffer.resize(std::max(frameSamplesBuffer.size(), bytesToRead));
uint8_t * frameSamplesPtr = frameSamplesBuffer.data();
int result = swr_convert(swr_ctx, &frameSamplesPtr, frame->nb_samples, (const uint8_t **)frame->data, frame->nb_samples);
if (result < 0)
throwFFmpegError(result);
size_t samplesToCopy = result * numChannels;
size_t bytesToCopy = samplesToCopy * formatProperties.sampleSizeBytes;
samples.insert(samples.end(), frameSamplesBuffer.begin(), frameSamplesBuffer.begin() + bytesToCopy);
}
else
swr_free(&swr_ctx);
}
else
{
for (;;)
{
decodeNextFrame();
const AVFrame * frame = getCurrentFrame();
if (!frame)
break;
size_t samplesToRead = frame->nb_samples * numChannels;
size_t bytesToRead = samplesToRead * formatProperties.sampleSizeBytes;
samples.insert(samples.end(), frame->data[0], frame->data[0] + bytesToRead);
}
}

View File

@ -18,6 +18,10 @@
#include "../render/IRenderHandler.h"
#include "../lib/filesystem/Filesystem.h"
#include "../lib/GameSettings.h"
#include "../lib/IGameSettings.h"
#include "../lib/json/JsonNode.h"
#include "../lib/VCMI_Lib.h"
void AssetGenerator::generateAll()
{
@ -138,16 +142,17 @@ void AssetGenerator::createPlayerColoredBackground(const PlayerColor & player)
std::shared_ptr<IImage> texture = GH.renderHandler().loadImage(locator, EImageBlitMode::OPAQUE);
// Color transform to make color of brown DIBOX.PCX texture match color of specified player
// transform to make color of brown DIBOX.PCX texture match color of specified player
auto filterSettings = VLC->settingsHandler->getFullConfig()["interface"]["playerColoredBackground"];
static const std::array<ColorFilter, PlayerColor::PLAYER_LIMIT_I> filters = {
ColorFilter::genRangeShifter( 0.25, 0, 0, 1.25, 0.00, 0.00 ), // red
ColorFilter::genRangeShifter( 0, 0, 0, 0.45, 1.20, 4.50 ), // blue
ColorFilter::genRangeShifter( 0.40, 0.27, 0.23, 1.10, 1.20, 1.15 ), // tan
ColorFilter::genRangeShifter( -0.27, 0.10, -0.27, 0.70, 1.70, 0.70 ), // green
ColorFilter::genRangeShifter( 0.47, 0.17, -0.27, 1.60, 1.20, 0.70 ), // orange
ColorFilter::genRangeShifter( 0.12, -0.1, 0.25, 1.15, 1.20, 2.20 ), // purple
ColorFilter::genRangeShifter( -0.13, 0.23, 0.23, 0.90, 1.20, 2.20 ), // teal
ColorFilter::genRangeShifter( 0.44, 0.15, 0.25, 1.00, 1.00, 1.75 ) // pink
ColorFilter::genRangeShifter( filterSettings["red" ].convertTo<std::vector<float>>() ),
ColorFilter::genRangeShifter( filterSettings["blue" ].convertTo<std::vector<float>>() ),
ColorFilter::genRangeShifter( filterSettings["tan" ].convertTo<std::vector<float>>() ),
ColorFilter::genRangeShifter( filterSettings["green" ].convertTo<std::vector<float>>() ),
ColorFilter::genRangeShifter( filterSettings["orange"].convertTo<std::vector<float>>() ),
ColorFilter::genRangeShifter( filterSettings["purple"].convertTo<std::vector<float>>() ),
ColorFilter::genRangeShifter( filterSettings["teal" ].convertTo<std::vector<float>>() ),
ColorFilter::genRangeShifter( filterSettings["pink" ].convertTo<std::vector<float>>() )
};
assert(player.isValidPlayer());

View File

@ -70,6 +70,13 @@ ColorFilter ColorFilter::genRangeShifter( float minR, float minG, float minB, fl
1.f);
}
ColorFilter ColorFilter::genRangeShifter( std::vector<float> parameters )
{
assert(std::size(parameters) == 6);
return genRangeShifter(parameters[0], parameters[1], parameters[2], parameters[3], parameters[4], parameters[5]);
}
ColorFilter ColorFilter::genMuxerShifter( ChannelMuxer r, ChannelMuxer g, ChannelMuxer b, float a )
{
return ColorFilter(r, g, b, a);

View File

@ -44,6 +44,7 @@ public:
/// Generates object that transforms each channel independently
static ColorFilter genRangeShifter( float minR, float minG, float minB, float maxR, float maxG, float maxB );
static ColorFilter genRangeShifter( std::vector<float> parameters );
/// Generates object that performs arbitrary mixing between any channels
static ColorFilter genMuxerShifter( ChannelMuxer r, ChannelMuxer g, ChannelMuxer b, float a );

View File

@ -70,6 +70,7 @@ void CComponent::init(ComponentType Type, ComponentSubType Subtype, std::optiona
customSubtitle = ValText;
size = imageSize;
font = fnt;
newLine = false;
assert(size < sizeInvalid);
@ -471,7 +472,8 @@ void CComponentBox::placeComponents(bool selectable)
//start next row
if ((pos.w != 0 && rows.back().width + comp->pos.w + distance > pos.w) // row is full
|| rows.back().comps >= componentsInRow)
|| rows.back().comps >= componentsInRow
|| (prevComp && prevComp->newLine))
{
prevComp = nullptr;
rows.push_back (RowData (0,0,0));

View File

@ -52,6 +52,7 @@ public:
std::string customSubtitle;
ESize size; //component size.
EFonts font; //Font size of label
bool newLine; //Line break after component
std::string getDescription() const;
std::string getSubtitle() const;

View File

@ -1782,6 +1782,7 @@ CFortScreen::CFortScreen(const CGTownInstance * town):
ui32 fortSize = static_cast<ui32>(town->creatures.size());
if(fortSize > town->town->creatures.size() && town->creatures.back().second.empty())
fortSize--;
fortSize = std::min(fortSize, static_cast<ui32>(GameConstants::CREATURES_PER_TOWN)); // for 8 creatures + portal of summoning
const CBuilding * fortBuilding = town->town->buildings.at(BuildingID(town->fortLevel()+6));
title = std::make_shared<CLabel>(400, 12, FONT_BIG, ETextAlignment::CENTER, Colors::WHITE, fortBuilding->getNameTranslated());
@ -1840,6 +1841,7 @@ ImagePath CFortScreen::getBgName(const CGTownInstance * town)
ui32 fortSize = static_cast<ui32>(town->creatures.size());
if(fortSize > town->town->creatures.size() && town->creatures.back().second.empty())
fortSize--;
fortSize = std::min(fortSize, static_cast<ui32>(GameConstants::CREATURES_PER_TOWN)); // for 8 creatures + portal of summoning
if(fortSize == GameConstants::CREATURES_PER_TOWN)
return ImagePath::builtin("TPCASTL8");
@ -1966,7 +1968,7 @@ void CFortScreen::RecruitArea::showPopupWindow(const Point & cursorPosition)
}
CMageGuildScreen::CMageGuildScreen(CCastleInterface * owner, const ImagePath & imagename)
: CWindowObject(BORDERED, imagename)
: CWindowObject(BORDERED, imagename), townId(owner->town->id)
{
OBJECT_CONSTRUCTION;
@ -1982,6 +1984,15 @@ CMageGuildScreen::CMageGuildScreen(CCastleInterface * owner, const ImagePath & i
exit = std::make_shared<CButton>(Point(748, 556), AnimationPath::builtin("TPMAGE1.DEF"), CButton::tooltip(CGI->generaltexth->allTexts[593]), [&](){ close(); }, EShortcut::GLOBAL_RETURN);
updateSpells(townId);
}
void CMageGuildScreen::updateSpells(ObjectInstanceID tID)
{
if(tID != townId)
return;
OBJECT_CONSTRUCTION;
static const std::vector<std::vector<Point> > positions =
{
{Point(222,445), Point(312,445), Point(402,445), Point(520,445), Point(610,445), Point(700,445)},
@ -1991,21 +2002,28 @@ CMageGuildScreen::CMageGuildScreen(CCastleInterface * owner, const ImagePath & i
{Point(491,325), Point(591,325)}
};
for(size_t i=0; i<owner->town->town->mageLevel; i++)
spells.clear();
emptyScrolls.clear();
const CGTownInstance * town = LOCPLINT->cb->getTown(townId);
for(size_t i=0; i<town->town->mageLevel; i++)
{
size_t spellCount = owner->town->spellsAtLevel((int)i+1,false); //spell at level with -1 hmmm?
size_t spellCount = town->spellsAtLevel((int)i+1,false); //spell at level with -1 hmmm?
for(size_t j=0; j<spellCount; j++)
{
if(i<owner->town->mageGuildLevel() && owner->town->spells[i].size()>j)
spells.push_back(std::make_shared<Scroll>(positions[i][j], owner->town->spells[i][j].toSpell()));
if(i<town->mageGuildLevel() && town->spells[i].size()>j)
spells.push_back(std::make_shared<Scroll>(positions[i][j], town->spells[i][j].toSpell(), townId));
else
emptyScrolls.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("TPMAGES.DEF"), 1, 0, positions[i][j].x, positions[i][j].y));
}
}
redraw();
}
CMageGuildScreen::Scroll::Scroll(Point position, const CSpell *Spell)
: spell(Spell)
CMageGuildScreen::Scroll::Scroll(Point position, const CSpell *Spell, ObjectInstanceID townId)
: spell(Spell), townId(townId)
{
OBJECT_CONSTRUCTION;
@ -2017,7 +2035,61 @@ CMageGuildScreen::Scroll::Scroll(Point position, const CSpell *Spell)
void CMageGuildScreen::Scroll::clickPressed(const Point & cursorPosition)
{
LOCPLINT->showInfoDialog(spell->getDescriptionTranslated(0), std::make_shared<CComponent>(ComponentType::SPELL, spell->id));
const CGTownInstance * town = LOCPLINT->cb->getTown(townId);
if(LOCPLINT->cb->getSettings().getBoolean(EGameSettings::TOWNS_SPELL_RESEARCH) && town->spellResearchAllowed)
{
int level = -1;
for(int i = 0; i < town->spells.size(); i++)
if(vstd::find_pos(town->spells[i], spell->id) != -1)
level = i;
if(town->spellResearchCounterDay >= LOCPLINT->cb->getSettings().getValue(EGameSettings::TOWNS_SPELL_RESEARCH_PER_DAY).Vector()[level].Float())
{
LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.spellResearch.comeAgain"));
return;
}
auto costBase = TResources(LOCPLINT->cb->getSettings().getValue(EGameSettings::TOWNS_SPELL_RESEARCH_COST).Vector()[level]);
auto costExponent = LOCPLINT->cb->getSettings().getValue(EGameSettings::TOWNS_SPELL_RESEARCH_COST_EXPONENT_PER_RESEARCH).Vector()[level].Float();
auto cost = costBase * std::pow(town->spellResearchAcceptedCounter + 1, costExponent);
std::vector<std::shared_ptr<CComponent>> resComps;
auto newSpell = town->spells[level].at(town->spellsAtLevel(level, false));
resComps.push_back(std::make_shared<CComponent>(ComponentType::SPELL, spell->id));
resComps.push_back(std::make_shared<CComponent>(ComponentType::SPELL, newSpell));
resComps.back()->newLine = true;
for(TResources::nziterator i(cost); i.valid(); i++)
{
resComps.push_back(std::make_shared<CComponent>(ComponentType::RESOURCE, i->resType, i->resVal, CComponent::ESize::medium));
}
auto showSpellResearchDialog = [this, resComps, town, cost, newSpell](){
std::vector<std::pair<AnimationPath, CFunctionList<void()>>> pom;
for(int i = 0; i < 3; i++)
pom.emplace_back(AnimationPath::builtin("settingsWindow/button80"), nullptr);
auto text = CGI->generaltexth->translate(LOCPLINT->cb->getResourceAmount().canAfford(cost) ? "vcmi.spellResearch.pay" : "vcmi.spellResearch.canNotAfford");
boost::replace_first(text, "%SPELL1", spell->id.toSpell()->getNameTranslated());
boost::replace_first(text, "%SPELL2", newSpell.toSpell()->getNameTranslated());
auto temp = std::make_shared<CInfoWindow>(text, LOCPLINT->playerID, resComps, pom);
temp->buttons[0]->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("spellResearch/accept")));
temp->buttons[0]->addCallback([this, town](){ LOCPLINT->cb->spellResearch(town, spell->id, true); });
temp->buttons[0]->addPopupCallback([](){ CRClickPopup::createAndPush(CGI->generaltexth->translate("vcmi.spellResearch.research")); });
temp->buttons[0]->setEnabled(LOCPLINT->cb->getResourceAmount().canAfford(cost));
temp->buttons[1]->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("spellResearch/reroll")));
temp->buttons[1]->addCallback([this, town](){ LOCPLINT->cb->spellResearch(town, spell->id, false); });
temp->buttons[1]->addPopupCallback([](){ CRClickPopup::createAndPush(CGI->generaltexth->translate("vcmi.spellResearch.skip")); });
temp->buttons[2]->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("spellResearch/close")));
temp->buttons[2]->addPopupCallback([](){ CRClickPopup::createAndPush(CGI->generaltexth->translate("vcmi.spellResearch.abort")); });
GH.windows().pushWindow(temp);
};
showSpellResearchDialog();
}
else
LOCPLINT->showInfoDialog(spell->getDescriptionTranslated(0), std::make_shared<CComponent>(ComponentType::SPELL, spell->id));
}
void CMageGuildScreen::Scroll::showPopupWindow(const Point & cursorPosition)

View File

@ -379,9 +379,10 @@ class CMageGuildScreen : public CStatusbarWindow
{
const CSpell * spell;
std::shared_ptr<CAnimImage> image;
ObjectInstanceID townId;
public:
Scroll(Point position, const CSpell *Spell);
Scroll(Point position, const CSpell *Spell, ObjectInstanceID townId);
void clickPressed(const Point & cursorPosition) override;
void showPopupWindow(const Point & cursorPosition) override;
void hover(bool on) override;
@ -393,8 +394,11 @@ class CMageGuildScreen : public CStatusbarWindow
std::shared_ptr<CMinorResDataBar> resdatabar;
ObjectInstanceID townId;
public:
CMageGuildScreen(CCastleInterface * owner, const ImagePath & image);
void updateSpells(ObjectInstanceID tID);
};
/// The blacksmith window where you can buy available in town war machine

View File

@ -20,6 +20,8 @@
#include "render/Canvas.h"
#include "CPlayerInterface.h"
#include "../../CCallback.h"
#include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/networkPacks/ArtifactLocation.h"
@ -41,17 +43,38 @@ CHeroBackpackWindow::CHeroBackpackWindow(const CGHeroInstance * hero, const std:
};
addSet(arts);
arts->setHero(hero);
quitButton = std::make_shared<CButton>(Point(), AnimationPath::builtin("IOKAY32.def"), CButton::tooltip(""),
[this]() { WindowBase::close(); }, EShortcut::GLOBAL_RETURN);
buttons.emplace_back(std::make_unique<CButton>(Point(), AnimationPath::builtin("ALTFILL.DEF"),
CButton::tooltipLocalized("vcmi.heroWindow.sortBackpackByCost"),
[hero]() { LOCPLINT->cb->sortBackpackArtifactsByCost(hero->id); }));
buttons.emplace_back(std::make_unique<CButton>(Point(), AnimationPath::builtin("ALTFILL.DEF"),
CButton::tooltipLocalized("vcmi.heroWindow.sortBackpackBySlot"),
[hero]() { LOCPLINT->cb->sortBackpackArtifactsBySlot(hero->id); }));
buttons.emplace_back(std::make_unique<CButton>(Point(), AnimationPath::builtin("ALTFILL.DEF"),
CButton::tooltipLocalized("vcmi.heroWindow.sortBackpackByClass"),
[hero]() { LOCPLINT->cb->sortBackpackArtifactsByClass(hero->id); }));
pos.w = stretchedBackground->pos.w = arts->pos.w + 2 * windowMargin;
pos.h = stretchedBackground->pos.h = arts->pos.h + quitButton->pos.h + 3 * windowMargin;
quitButton->moveTo(Point(pos.x + pos.w / 2 - quitButton->pos.w / 2, pos.y + arts->pos.h + 2 * windowMargin));
pos.h = stretchedBackground->pos.h = arts->pos.h + buttons.back()->pos.h + 3 * windowMargin;
auto buttonPos = Point(pos.x + windowMargin, pos.y + arts->pos.h + 2 * windowMargin);
for(const auto & button : buttons)
{
button->moveTo(buttonPos);
buttonPos += Point(button->pos.w + 10, 0);
}
statusbar = CGStatusBar::create(0, pos.h, ImagePath::builtin("ADROLLVR.bmp"), pos.w);
pos.h += statusbar->pos.h;
addUsedEvents(LCLICK);
center();
}
void CHeroBackpackWindow::notFocusedClick()
{
close();
}
void CHeroBackpackWindow::showAll(Canvas & to)
{
CIntObject::showAll(to);

View File

@ -17,10 +17,11 @@ class CHeroBackpackWindow : public CStatusbarWindow, public CWindowWithArtifacts
{
public:
CHeroBackpackWindow(const CGHeroInstance * hero, const std::vector<CArtifactsOfHeroPtr> & artsSets);
void notFocusedClick() override;
protected:
std::shared_ptr<CArtifactsOfHeroBackpack> arts;
std::shared_ptr<CButton> quitButton;
std::vector<std::unique_ptr<CButton>> buttons;
std::shared_ptr<CFilledTexture> stretchedBackground;
const int windowMargin = 5;

View File

@ -473,7 +473,7 @@ CKingdomInterface::CKingdomInterface()
generateButtons();
statusbar = CGStatusBar::create(std::make_shared<CPicture>(ImagePath::builtin("KSTATBAR"), 10,pos.h - 45));
resdatabar = std::make_shared<CResDataBar>(ImagePath::builtin("KRESBAR"), 7, 111+footerPos, 29, 5, 76, 81);
resdatabar = std::make_shared<CResDataBar>(ImagePath::builtin("KRESBAR"), 7, 111+footerPos, 29, 3, 76, 81);
activateTab(persistentStorage["gui"]["lastKindomInterface"].Integer());
}
@ -805,7 +805,7 @@ CTownItem::CTownItem(const CGTownInstance * Town)
picture = std::make_shared<CAnimImage>(AnimationPath::builtin("ITPT"), iconIndex, 0, 5, 6);
openTown = std::make_shared<LRClickableAreaOpenTown>(Rect(5, 6, 58, 64), town);
for(size_t i=0; i<town->creatures.size(); i++)
for(size_t i=0; i<town->creatures.size() && i<GameConstants::CREATURES_PER_TOWN; i++)
{
growth.push_back(std::make_shared<CCreaInfo>(Point(401+37*(int)i, 78), town, (int)i, true, true));
available.push_back(std::make_shared<CCreaInfo>(Point(48+37*(int)i, 78), town, (int)i, true, false));
@ -866,7 +866,7 @@ void CTownItem::update()
heroes->update();
for (size_t i=0; i<town->creatures.size(); i++)
for (size_t i=0; i<std::min(static_cast<int>(town->creatures.size()), GameConstants::CREATURES_PER_TOWN); i++)
{
growth[i]->update();
available[i]->update();

View File

@ -129,7 +129,7 @@ std::vector<std::string> CMessage::breakText(std::string text, size_t maxLineWid
if(wordBreak != ui32(-1))
{
currPos = wordBreak;
if(text.substr(0, currPos).find('{') == std::string::npos)
if(boost::count(text.substr(0, currPos), '{') == boost::count(text.substr(0, currPos), '}'))
{
opened = false;
color = "";

View File

@ -548,11 +548,12 @@ void CTavernWindow::addInvite()
if(!inviteableHeroes.empty())
{
int imageIndex = heroToInvite ? (*CGI->heroh)[heroToInvite->getHeroType()]->imageIndex : 156; // 156 => special id for random
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);
inviteHeroImage = std::make_shared<CAnimImage>(AnimationPath::builtin("PortraitsSmall"), 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)); });
}
}

View File

@ -166,4 +166,6 @@ QuickRecruitmentWindow::QuickRecruitmentWindow(const CGTownInstance * townd, Rec
setButtons();
setCreaturePurchaseCards();
maxAllCards(cards);
center();
}

View File

@ -302,7 +302,7 @@
"backpackSize" : -1,
// if heroes are invitable in tavern
"tavernInvite" : false,
// minimai primary skills for heroes
// minimal primary skills for heroes
"minimalPrimarySkills": [ 0, 0, 1, 1]
},
@ -311,7 +311,21 @@
// How many new building can be built in a town per day
"buildingsPerTurnCap" : 1,
// Chances for a town with default buildings to receive corresponding dwelling level built in start
"startingDwellingChances": [100, 50]
"startingDwellingChances": [100, 50],
// Enable spell research in mage guild
"spellResearch": false,
// Cost for an spell research (array index is spell tier)
"spellResearchCost": [
{ "gold": 1000, "wood" : 2, "mercury": 2, "ore": 2, "sulfur": 2, "crystal": 2, "gems": 2 },
{ "gold": 1000, "wood" : 4, "mercury": 4, "ore": 4, "sulfur": 4, "crystal": 4, "gems": 4 },
{ "gold": 1000, "wood" : 6, "mercury": 6, "ore": 6, "sulfur": 6, "crystal": 6, "gems": 6 },
{ "gold": 1000, "wood" : 8, "mercury": 8, "ore": 8, "sulfur": 8, "crystal": 8, "gems": 8 },
{ "gold": 1000, "wood" : 10, "mercury": 10, "ore": 10, "sulfur": 10, "crystal": 10, "gems": 10 }
],
// How much researchs/skips per day are possible? (array index is spell tier)
"spellResearchPerDay": [ 2, 2, 2, 2, 1 ],
// Exponent for increasing cost for each research (factor 1 disables this; array index is spell tier)
"spellResearchCostExponentPerResearch": [ 1.25, 1.25, 1.25, 1.25, 1.25 ]
},
"combat":
@ -555,6 +569,22 @@
"valueType" : "BASE_NUMBER"
}
}
},
"interface" :
{
// Color transform to make color of brown DIBOX.PCX texture match color of specified player
"playerColoredBackground" :
{
"red" : [ 0.25, 0, 0, 1.25, 0.00, 0.00 ],
"blue" : [ 0, 0, 0, 0.45, 1.20, 4.50 ],
"tan" : [ 0.40, 0.27, 0.23, 1.10, 1.20, 1.15 ],
"green" : [ -0.27, 0.10, -0.27, 0.70, 1.70, 0.70 ],
"orange" : [ 0.47, 0.17, -0.27, 1.60, 1.20, 0.70 ],
"purple" : [ 0.12, -0.1, 0.25, 1.15, 1.20, 2.20 ],
"teal" : [ -0.13, 0.23, 0.23, 0.90, 1.20, 2.20 ],
"pink" : [ 0.44, 0.15, 0.25, 1.00, 1.00, 1.75 ]
}
}
}
}

View File

@ -51,8 +51,12 @@
"type" : "object",
"additionalProperties" : false,
"properties" : {
"buildingsPerTurnCap" : { "type" : "number" },
"startingDwellingChances" : { "type" : "array" }
"buildingsPerTurnCap" : { "type" : "number" },
"startingDwellingChances" : { "type" : "array" },
"spellResearch" : { "type" : "boolean" },
"spellResearchCost" : { "type" : "array" },
"spellResearchPerDay" : { "type" : "array" },
"spellResearchCostExponentPerResearch" : { "type" : "array" }
}
},
"combat": {
@ -148,5 +152,12 @@
"perHero" : { "type" : "object" }
}
},
"interface": {
"type" : "object",
"additionalProperties" : false,
"properties" : {
"playerColoredBackground" : { "type" : "object" }
}
}
}
}

View File

@ -5,6 +5,17 @@
"description" : "Format used to define main mod file (mod.json) in VCMI",
"required" : [ "name", "description", "modType", "version", "author", "contact" ],
"definitions" : {
"fileListOrObject" : {
"oneOf" : [
{
"type" : "array",
"items" : { "type" : "string", "format" : "textFile" }
},
{
"type" : "object"
}
]
},
"localizable" : {
"type" : "object",
"additionalProperties" : false,
@ -35,9 +46,8 @@
"description" : "If set to true, vcmi will skip validation of current translation json files"
},
"translations" : {
"type" : "array",
"description" : "List of files with translations for this language",
"items" : { "type" : "string", "format" : "textFile" }
"$ref" : "#/definitions/fileListOrObject"
}
}
}
@ -122,9 +132,17 @@
"description" : "If set to true, mod will not be enabled automatically on install"
},
"settings" : {
"type" : "object",
"description" : "List of changed game settings by mod",
"$ref" : "gameSettings.json"
"oneOf" : [
{
"type" : "object",
"$ref" : "gameSettings.json"
},
{
"type" : "array",
"items" : { "type" : "string", "format" : "textFile" }
},
]
},
"filesystem" : {
"type" : "object",
@ -206,94 +224,76 @@
"$ref" : "#/definitions/localizable"
},
"translations" : {
"type" : "array",
"description" : "List of files with translations for this language",
"items" : { "type" : "string", "format" : "textFile" }
"$ref" : "#/definitions/fileListOrObject"
},
"factions" : {
"type" : "array",
"description" : "List of configuration files for towns/factions",
"items" : { "type" : "string", "format" : "textFile" }
"$ref" : "#/definitions/fileListOrObject"
},
"heroClasses" : {
"type" : "array",
"description" : "List of configuration files for hero classes",
"items" : { "type" : "string", "format" : "textFile" }
"$ref" : "#/definitions/fileListOrObject"
},
"heroes" : {
"type" : "array",
"description" : "List of configuration files for heroes",
"items" : { "type" : "string", "format" : "textFile" }
"$ref" : "#/definitions/fileListOrObject"
},
"skills" : {
"type" : "array",
"description" : "List of configuration files for skills",
"items" : { "type" : "string", "format" : "textFile" }
"$ref" : "#/definitions/fileListOrObject"
},
"creatures" : {
"type" : "array",
"description" : "List of configuration files for creatures",
"items" : { "type" : "string", "format" : "textFile" }
"$ref" : "#/definitions/fileListOrObject"
},
"artifacts" : {
"type" : "array",
"description" : "List of configuration files for artifacts",
"items" : { "type" : "string", "format" : "textFile" }
"$ref" : "#/definitions/fileListOrObject"
},
"spells" : {
"type" : "array",
"description" : "List of configuration files for spells",
"items" : { "type" : "string", "format" : "textFile" }
"$ref" : "#/definitions/fileListOrObject"
},
"objects" : {
"type" : "array",
"description" : "List of configuration files for objects",
"items" : { "type" : "string", "format" : "textFile" }
"$ref" : "#/definitions/fileListOrObject"
},
"biomes" : {
"type" : "array",
"description" : "List of configuration files for biomes",
"items" : { "type" : "string", "format" : "textFile" }
"$ref" : "#/definitions/fileListOrObject"
},
"bonuses" : {
"type" : "array",
"description" : "List of configuration files for bonuses",
"items" : { "type" : "string", "format" : "textFile" }
"$ref" : "#/definitions/fileListOrObject"
},
"terrains" : {
"type" : "array",
"description" : "List of configuration files for terrains",
"items" : { "type" : "string", "format" : "textFile" }
"$ref" : "#/definitions/fileListOrObject"
},
"roads" : {
"type" : "array",
"description" : "List of configuration files for roads",
"items" : { "type" : "string", "format" : "textFile" }
"$ref" : "#/definitions/fileListOrObject"
},
"rivers" : {
"type" : "array",
"description" : "List of configuration files for rivers",
"items" : { "type" : "string", "format" : "textFile" }
"$ref" : "#/definitions/fileListOrObject"
},
"battlefields" : {
"type" : "array",
"description" : "List of configuration files for battlefields",
"items" : { "type" : "string", "format" : "textFile" }
"$ref" : "#/definitions/fileListOrObject"
},
"obstacles" : {
"type" : "array",
"description" : "List of configuration files for obstacles",
"items" : { "type" : "string", "format" : "textFile" }
"$ref" : "#/definitions/fileListOrObject"
},
"templates" : {
"type" : "array",
"description" : "List of configuration files for RMG templates",
"items" : { "type" : "string", "format" : "textFile" }
"$ref" : "#/definitions/fileListOrObject"
},
"scripts" : {
"type" : "array",
"description" : "List of configuration files for scripts",
"items" : { "type" : "string", "format" : "textFile" }
"$ref" : "#/definitions/fileListOrObject"
}
}
}

View File

@ -3,7 +3,7 @@
{
"type" : "object",
"$schema" : "http://json-schema.org/draft-04/schema",
"required" : [ "general", "video", "adventure", "battle", "input", "server", "logging", "launcher", "lobby", "gameTweaks" ],
"required" : [ "general", "video", "adventure", "battle", "input", "server", "logging", "launcher", "lobby", "gameTweaks", "mods" ],
"definitions" : {
"logLevelEnum" : {
"type" : "string",
@ -149,6 +149,23 @@
}
}
},
"mods" : {
"type" : "object",
"additionalProperties" : false,
"default" : {},
"required" : [
"validation"
],
"properties" : {
"validation" : {
"type" : "string",
"enum" : [ "off", "basic", "full" ],
"default" : "basic"
}
}
},
"video" : {
"type" : "object",
"additionalProperties" : false,

View File

@ -90,8 +90,9 @@ These are fields that are present only in local mod.json file
{
// Following section describes configuration files with content added by mod
// It can be split into several files in any way you want but recommended organization is
// to keep one file per object (creature/hero/etc) and, if applicable, add separate file
// with translatable strings for each type of content
// to keep one file per object (creature/hero/etc)
// Alternatively, for small changes you can embed changes to content directly in here, e.g.
// "creatures" : { "core:imp" : { "health" : 5 }}
// list of factions/towns configuration files
"factions" :

View File

@ -36,15 +36,15 @@ public:
virtual vstd::RNG * getRNG() = 0;
virtual void apply(CPackForClient * pack) = 0;
virtual void apply(CPackForClient & pack) = 0;
virtual void apply(BattleLogMessage * pack) = 0;
virtual void apply(BattleStackMoved * pack) = 0;
virtual void apply(BattleUnitsChanged * pack) = 0;
virtual void apply(SetStackEffect * pack) = 0;
virtual void apply(StacksInjured * pack) = 0;
virtual void apply(BattleObstaclesChanged * pack) = 0;
virtual void apply(CatapultAttack * pack) = 0;
virtual void apply(BattleLogMessage & pack) = 0;
virtual void apply(BattleStackMoved & pack) = 0;
virtual void apply(BattleUnitsChanged & pack) = 0;
virtual void apply(SetStackEffect & pack) = 0;
virtual void apply(StacksInjured & pack) = 0;
virtual void apply(BattleObstaclesChanged & pack) = 0;
virtual void apply(CatapultAttack & pack) = 0;
};
VCMI_LIB_NAMESPACE_END

View File

@ -20,8 +20,6 @@ set(launcher_SRCS
innoextract.cpp
mainwindow_moc.cpp
languages.cpp
launcherdirs.cpp
jsonutils.cpp
updatedialog_moc.cpp
prepare.cpp
)
@ -49,8 +47,6 @@ set(launcher_HEADERS
firstLaunch/firstlaunch_moc.h
mainwindow_moc.h
languages.h
launcherdirs.h
jsonutils.h
updatedialog_moc.h
main.h
helper.h
@ -206,7 +202,7 @@ elseif(NOT APPLE_IOS)
target_link_libraries(vcmilauncher SDL2::SDL2)
endif()
target_link_libraries(vcmilauncher vcmi Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network)
target_link_libraries(vcmilauncher vcmi vcmiqt Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network)
target_include_directories(vcmilauncher
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
)

View File

@ -18,24 +18,8 @@
#include <QList>
#include <QString>
#include <QFile>
#include <QTemporaryDir>
#include <QTemporaryDir>
#include "../vcmiqt/convpathqstring.h"
VCMI_LIB_USING_NAMESPACE
inline QString pathToQString(const boost::filesystem::path & path)
{
#ifdef VCMI_WINDOWS
return QString::fromStdWString(path.wstring());
#else
return QString::fromStdString(path.string());
#endif
}
inline boost::filesystem::path qstringToPath(const QString & path)
{
#ifdef VCMI_WINDOWS
return boost::filesystem::path(path.toStdWString());
#else
return boost::filesystem::path(path.toUtf8().data());
#endif
}

View File

@ -10,7 +10,7 @@
#include "StdInc.h"
#include "cdownloadmanager_moc.h"
#include "../launcherdirs.h"
#include "../vcmiqt/launcherdirs.h"
#include "../../lib/CConfigHandler.h"

View File

@ -22,8 +22,8 @@
#include "cdownloadmanager_moc.h"
#include "chroniclesextractor.h"
#include "../settingsView/csettingsview_moc.h"
#include "../launcherdirs.h"
#include "../jsonutils.h"
#include "../vcmiqt/launcherdirs.h"
#include "../vcmiqt/jsonutils.h"
#include "../helper.h"
#include "../../lib/VCMIDirs.h"

View File

@ -17,8 +17,8 @@
#include "../../lib/modding/CModInfo.h"
#include "../../lib/modding/IdentifierStorage.h"
#include "../jsonutils.h"
#include "../launcherdirs.h"
#include "../vcmiqt/jsonutils.h"
#include "../vcmiqt/launcherdirs.h"
#include <future>

View File

@ -9,7 +9,7 @@
*/
#include "StdInc.h"
#include "prepare.h"
#include "launcherdirs.h"
#include "../vcmiqt/launcherdirs.h"
#include <QDir>
#include <QFile>

View File

@ -15,7 +15,7 @@
#include "../modManager/cmodlistview_moc.h"
#include "../helper.h"
#include "../jsonutils.h"
#include "../vcmiqt/jsonutils.h"
#include "../languages.h"
#include <QFileInfo>
@ -182,6 +182,13 @@ void CSettingsView::loadSettings()
else
ui->buttonFontScalable->setChecked(true);
if (settings["mods"]["validation"].String() == "off")
ui->buttonValidationOff->setChecked(true);
else if (settings["mods"]["validation"].String() == "basic")
ui->buttonValidationBasic->setChecked(true);
else
ui->buttonValidationFull->setChecked(true);
loadToggleButtonSettings();
}
@ -791,3 +798,21 @@ void CSettingsView::on_buttonFontOriginal_clicked(bool checked)
Settings node = settings.write["video"]["fontsType"];
node->String() = "original";
}
void CSettingsView::on_buttonValidationOff_clicked(bool checked)
{
Settings node = settings.write["mods"]["validation"];
node->String() = "off";
}
void CSettingsView::on_buttonValidationBasic_clicked(bool checked)
{
Settings node = settings.write["mods"]["validation"];
node->String() = "basic";
}
void CSettingsView::on_buttonValidationFull_clicked(bool checked)
{
Settings node = settings.write["mods"]["validation"];
node->String() = "full";
}

View File

@ -83,19 +83,20 @@ private slots:
void on_sliderToleranceDistanceController_valueChanged(int value);
void on_lineEditGameLobbyHost_textChanged(const QString &arg1);
void on_spinBoxNetworkPortLobby_valueChanged(int arg1);
void on_sliderControllerSticksAcceleration_valueChanged(int value);
void on_sliderControllerSticksSensitivity_valueChanged(int value);
//void on_buttonTtfFont_toggled(bool value);
void on_sliderScalingFont_valueChanged(int value);
void on_buttonFontAuto_clicked(bool checked);
void on_buttonFontScalable_clicked(bool checked);
void on_buttonFontOriginal_clicked(bool checked);
void on_buttonValidationOff_clicked(bool checked);
void on_buttonValidationBasic_clicked(bool checked);
void on_buttonValidationFull_clicked(bool checked);
private:
Ui::CSettingsView * ui;

File diff suppressed because it is too large Load Diff

View File

@ -431,9 +431,9 @@ std::shared_ptr<CArtifact> CArtHandler::loadFromJson(const std::string & scope,
const JsonNode & text = node["text"];
VLC->generaltexth->registerString(scope, art->getNameTextID(), text["name"].String());
VLC->generaltexth->registerString(scope, art->getDescriptionTextID(), text["description"].String());
VLC->generaltexth->registerString(scope, art->getEventTextID(), text["event"].String());
VLC->generaltexth->registerString(scope, art->getNameTextID(), text["name"]);
VLC->generaltexth->registerString(scope, art->getDescriptionTextID(), text["description"]);
VLC->generaltexth->registerString(scope, art->getEventTextID(), text["event"]);
const JsonNode & graphics = node["graphics"];
art->image = graphics["image"].String();

View File

@ -200,8 +200,9 @@ ImagePath CBonusTypeHandler::bonusToGraphics(const std::shared_ptr<Bonus> & bonu
void CBonusTypeHandler::load()
{
const JsonNode gameConf(JsonPath::builtin("config/gameConfig.json"));
const JsonNode config(JsonUtils::assembleFromFiles(gameConf["bonuses"].convertTo<std::vector<std::string>>()));
JsonNode gameConf(JsonPath::builtin("config/gameConfig.json"));
JsonNode config(JsonUtils::assembleFromFiles(gameConf["bonuses"].convertTo<std::vector<std::string>>()));
config.setModScope("vcmi");
load(config);
}
@ -240,8 +241,8 @@ void CBonusTypeHandler::loadItem(const JsonNode & source, CBonusType & dest, con
if (!dest.hidden)
{
VLC->generaltexth->registerString( "vcmi", dest.getNameTextID(), source["name"].String());
VLC->generaltexth->registerString( "vcmi", dest.getDescriptionTextID(), source["description"].String());
VLC->generaltexth->registerString( "vcmi", dest.getNameTextID(), source["name"]);
VLC->generaltexth->registerString( "vcmi", dest.getDescriptionTextID(), source["description"]);
}
const JsonNode & graphics = source["graphics"];

View File

@ -617,9 +617,9 @@ std::shared_ptr<CCreature> CCreatureHandler::loadFromJson(const std::string & sc
cre->cost = ResourceSet(node["cost"]);
VLC->generaltexth->registerString(scope, cre->getNameSingularTextID(), node["name"]["singular"].String());
VLC->generaltexth->registerString(scope, cre->getNamePluralTextID(), node["name"]["plural"].String());
VLC->generaltexth->registerString(scope, cre->getDescriptionTextID(), node["description"].String());
VLC->generaltexth->registerString(scope, cre->getNameSingularTextID(), node["name"]["singular"]);
VLC->generaltexth->registerString(scope, cre->getNamePluralTextID(), node["name"]["plural"]);
VLC->generaltexth->registerString(scope, cre->getDescriptionTextID(), node["description"]);
cre->addBonus(node["hitPoints"].Integer(), BonusType::STACK_HEALTH);
cre->addBonus(node["speed"].Integer(), BonusType::STACKS_SPEED);

View File

@ -863,7 +863,7 @@ std::string CStackInstance::getName() const
ui64 CStackInstance::getPower() const
{
assert(type);
return type->getAIValue() * count;
return static_cast<ui64>(type->getAIValue()) * count;
}
ui64 CStackInstance::getMarketValue() const

View File

@ -459,11 +459,11 @@ std::shared_ptr<CHero> CHeroHandler::loadFromJson(const std::string & scope, con
hero->onlyOnWaterMap = node["onlyOnWaterMap"].Bool();
hero->onlyOnMapWithoutWater = node["onlyOnMapWithoutWater"].Bool();
VLC->generaltexth->registerString(scope, hero->getNameTextID(), node["texts"]["name"].String());
VLC->generaltexth->registerString(scope, hero->getBiographyTextID(), node["texts"]["biography"].String());
VLC->generaltexth->registerString(scope, hero->getSpecialtyNameTextID(), node["texts"]["specialty"]["name"].String());
VLC->generaltexth->registerString(scope, hero->getSpecialtyTooltipTextID(), node["texts"]["specialty"]["tooltip"].String());
VLC->generaltexth->registerString(scope, hero->getSpecialtyDescriptionTextID(), node["texts"]["specialty"]["description"].String());
VLC->generaltexth->registerString(scope, hero->getNameTextID(), node["texts"]["name"]);
VLC->generaltexth->registerString(scope, hero->getBiographyTextID(), node["texts"]["biography"]);
VLC->generaltexth->registerString(scope, hero->getSpecialtyNameTextID(), node["texts"]["specialty"]["name"]);
VLC->generaltexth->registerString(scope, hero->getSpecialtyTooltipTextID(), node["texts"]["specialty"]["tooltip"]);
VLC->generaltexth->registerString(scope, hero->getSpecialtyDescriptionTextID(), node["texts"]["specialty"]["description"]);
hero->iconSpecSmall = node["images"]["specialtySmall"].String();
hero->iconSpecLarge = node["images"]["specialtyLarge"].String();

View File

@ -212,7 +212,7 @@ std::shared_ptr<CSkill> CSkillHandler::loadFromJson(const std::string & scope, c
skill->onlyOnWaterMap = json["onlyOnWaterMap"].Bool();
VLC->generaltexth->registerString(scope, skill->getNameTextID(), json["name"].String());
VLC->generaltexth->registerString(scope, skill->getNameTextID(), json["name"]);
switch(json["gainChance"].getType())
{
case JsonNode::JsonType::DATA_INTEGER:
@ -237,7 +237,7 @@ std::shared_ptr<CSkill> CSkillHandler::loadFromJson(const std::string & scope, c
skill->addNewBonus(bonus, level);
}
CSkill::LevelInfo & skillAtLevel = skill->at(level);
VLC->generaltexth->registerString(scope, skill->getDescriptionTextID(level), levelNode["description"].String());
VLC->generaltexth->registerString(scope, skill->getDescriptionTextID(level), levelNode["description"]);
skillAtLevel.iconSmall = levelNode["images"]["small"].String();
skillAtLevel.iconMedium = levelNode["images"]["medium"].String();
skillAtLevel.iconLarge = levelNode["images"]["large"].String();

View File

@ -401,7 +401,7 @@ void CStack::spendMana(ServerCallback * server, const int spellCost) const
ssp.which = BattleSetStackProperty::CASTS;
ssp.val = -spellCost;
ssp.absolute = false;
server->apply(&ssp);
server->apply(ssp);
}
VCMI_LIB_NAMESPACE_END

View File

@ -37,70 +37,75 @@ GameSettings::GameSettings() = default;
GameSettings::~GameSettings() = default;
const std::vector<GameSettings::SettingOption> GameSettings::settingProperties = {
{EGameSettings::BANKS_SHOW_GUARDS_COMPOSITION, "banks", "showGuardsComposition" },
{EGameSettings::BONUSES_GLOBAL, "bonuses", "global" },
{EGameSettings::BONUSES_PER_HERO, "bonuses", "perHero" },
{EGameSettings::COMBAT_AREA_SHOT_CAN_TARGET_EMPTY_HEX, "combat", "areaShotCanTargetEmptyHex" },
{EGameSettings::COMBAT_ATTACK_POINT_DAMAGE_FACTOR, "combat", "attackPointDamageFactor" },
{EGameSettings::COMBAT_ATTACK_POINT_DAMAGE_FACTOR_CAP, "combat", "attackPointDamageFactorCap" },
{EGameSettings::COMBAT_BAD_LUCK_DICE, "combat", "badLuckDice" },
{EGameSettings::COMBAT_BAD_MORALE_DICE, "combat", "badMoraleDice" },
{EGameSettings::COMBAT_DEFENSE_POINT_DAMAGE_FACTOR, "combat", "defensePointDamageFactor" },
{EGameSettings::COMBAT_DEFENSE_POINT_DAMAGE_FACTOR_CAP, "combat", "defensePointDamageFactorCap" },
{EGameSettings::COMBAT_GOOD_LUCK_DICE, "combat", "goodLuckDice" },
{EGameSettings::COMBAT_GOOD_MORALE_DICE, "combat", "goodMoraleDice" },
{EGameSettings::COMBAT_LAYOUTS, "combat", "layouts" },
{EGameSettings::COMBAT_ONE_HEX_TRIGGERS_OBSTACLES, "combat", "oneHexTriggersObstacles" },
{EGameSettings::CREATURES_ALLOW_ALL_FOR_DOUBLE_MONTH, "creatures", "allowAllForDoubleMonth" },
{EGameSettings::CREATURES_ALLOW_RANDOM_SPECIAL_WEEKS, "creatures", "allowRandomSpecialWeeks" },
{EGameSettings::CREATURES_DAILY_STACK_EXPERIENCE, "creatures", "dailyStackExperience" },
{EGameSettings::CREATURES_WEEKLY_GROWTH_CAP, "creatures", "weeklyGrowthCap" },
{EGameSettings::CREATURES_WEEKLY_GROWTH_PERCENT, "creatures", "weeklyGrowthPercent" },
{EGameSettings::DIMENSION_DOOR_EXPOSES_TERRAIN_TYPE, "spells", "dimensionDoorExposesTerrainType" },
{EGameSettings::DIMENSION_DOOR_FAILURE_SPENDS_POINTS, "spells", "dimensionDoorFailureSpendsPoints" },
{EGameSettings::DIMENSION_DOOR_ONLY_TO_UNCOVERED_TILES, "spells", "dimensionDoorOnlyToUncoveredTiles"},
{EGameSettings::DIMENSION_DOOR_TOURNAMENT_RULES_LIMIT, "spells", "dimensionDoorTournamentRulesLimit"},
{EGameSettings::DIMENSION_DOOR_TRIGGERS_GUARDS, "spells", "dimensionDoorTriggersGuards" },
{EGameSettings::DWELLINGS_ACCUMULATE_WHEN_NEUTRAL, "dwellings", "accumulateWhenNeutral" },
{EGameSettings::DWELLINGS_ACCUMULATE_WHEN_OWNED, "dwellings", "accumulateWhenOwned" },
{EGameSettings::DWELLINGS_MERGE_ON_RECRUIT, "dwellings", "mergeOnRecruit" },
{EGameSettings::HEROES_BACKPACK_CAP, "heroes", "backpackSize" },
{EGameSettings::HEROES_MINIMAL_PRIMARY_SKILLS, "heroes", "minimalPrimarySkills" },
{EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP, "heroes", "perPlayerOnMapCap" },
{EGameSettings::HEROES_PER_PLAYER_TOTAL_CAP, "heroes", "perPlayerTotalCap" },
{EGameSettings::HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS, "heroes", "retreatOnWinWithoutTroops" },
{EGameSettings::HEROES_STARTING_STACKS_CHANCES, "heroes", "startingStackChances" },
{EGameSettings::HEROES_TAVERN_INVITE, "heroes", "tavernInvite" },
{EGameSettings::MAP_FORMAT_ARMAGEDDONS_BLADE, "mapFormat", "armageddonsBlade" },
{EGameSettings::MAP_FORMAT_CHRONICLES, "mapFormat", "chronicles" },
{EGameSettings::MAP_FORMAT_HORN_OF_THE_ABYSS, "mapFormat", "hornOfTheAbyss" },
{EGameSettings::MAP_FORMAT_IN_THE_WAKE_OF_GODS, "mapFormat", "inTheWakeOfGods" },
{EGameSettings::MAP_FORMAT_JSON_VCMI, "mapFormat", "jsonVCMI" },
{EGameSettings::MAP_FORMAT_RESTORATION_OF_ERATHIA, "mapFormat", "restorationOfErathia" },
{EGameSettings::MAP_FORMAT_SHADOW_OF_DEATH, "mapFormat", "shadowOfDeath" },
{EGameSettings::MARKETS_BLACK_MARKET_RESTOCK_PERIOD, "markets", "blackMarketRestockPeriod" },
{EGameSettings::MODULE_COMMANDERS, "modules", "commanders" },
{EGameSettings::MODULE_STACK_ARTIFACT, "modules", "stackArtifact" },
{EGameSettings::MODULE_STACK_EXPERIENCE, "modules", "stackExperience" },
{EGameSettings::PATHFINDER_IGNORE_GUARDS, "pathfinder", "ignoreGuards" },
{EGameSettings::PATHFINDER_ORIGINAL_FLY_RULES, "pathfinder", "originalFlyRules" },
{EGameSettings::PATHFINDER_USE_BOAT, "pathfinder", "useBoat" },
{EGameSettings::PATHFINDER_USE_MONOLITH_ONE_WAY_RANDOM, "pathfinder", "useMonolithOneWayRandom" },
{EGameSettings::PATHFINDER_USE_MONOLITH_ONE_WAY_UNIQUE, "pathfinder", "useMonolithOneWayUnique" },
{EGameSettings::PATHFINDER_USE_MONOLITH_TWO_WAY, "pathfinder", "useMonolithTwoWay" },
{EGameSettings::PATHFINDER_USE_WHIRLPOOL, "pathfinder", "useWhirlpool" },
{EGameSettings::TEXTS_ARTIFACT, "textData", "artifact" },
{EGameSettings::TEXTS_CREATURE, "textData", "creature" },
{EGameSettings::TEXTS_FACTION, "textData", "faction" },
{EGameSettings::TEXTS_HERO, "textData", "hero" },
{EGameSettings::TEXTS_HERO_CLASS, "textData", "heroClass" },
{EGameSettings::TEXTS_OBJECT, "textData", "object" },
{EGameSettings::TEXTS_RIVER, "textData", "river" },
{EGameSettings::TEXTS_ROAD, "textData", "road" },
{EGameSettings::TEXTS_SPELL, "textData", "spell" },
{EGameSettings::TEXTS_TERRAIN, "textData", "terrain" },
{EGameSettings::TOWNS_BUILDINGS_PER_TURN_CAP, "towns", "buildingsPerTurnCap" },
{EGameSettings::TOWNS_STARTING_DWELLING_CHANCES, "towns", "startingDwellingChances" },
{EGameSettings::BANKS_SHOW_GUARDS_COMPOSITION, "banks", "showGuardsComposition" },
{EGameSettings::BONUSES_GLOBAL, "bonuses", "global" },
{EGameSettings::BONUSES_PER_HERO, "bonuses", "perHero" },
{EGameSettings::COMBAT_AREA_SHOT_CAN_TARGET_EMPTY_HEX, "combat", "areaShotCanTargetEmptyHex" },
{EGameSettings::COMBAT_ATTACK_POINT_DAMAGE_FACTOR, "combat", "attackPointDamageFactor" },
{EGameSettings::COMBAT_ATTACK_POINT_DAMAGE_FACTOR_CAP, "combat", "attackPointDamageFactorCap" },
{EGameSettings::COMBAT_BAD_LUCK_DICE, "combat", "badLuckDice" },
{EGameSettings::COMBAT_BAD_MORALE_DICE, "combat", "badMoraleDice" },
{EGameSettings::COMBAT_DEFENSE_POINT_DAMAGE_FACTOR, "combat", "defensePointDamageFactor" },
{EGameSettings::COMBAT_DEFENSE_POINT_DAMAGE_FACTOR_CAP, "combat", "defensePointDamageFactorCap" },
{EGameSettings::COMBAT_GOOD_LUCK_DICE, "combat", "goodLuckDice" },
{EGameSettings::COMBAT_GOOD_MORALE_DICE, "combat", "goodMoraleDice" },
{EGameSettings::COMBAT_LAYOUTS, "combat", "layouts" },
{EGameSettings::COMBAT_ONE_HEX_TRIGGERS_OBSTACLES, "combat", "oneHexTriggersObstacles" },
{EGameSettings::CREATURES_ALLOW_ALL_FOR_DOUBLE_MONTH, "creatures", "allowAllForDoubleMonth" },
{EGameSettings::CREATURES_ALLOW_RANDOM_SPECIAL_WEEKS, "creatures", "allowRandomSpecialWeeks" },
{EGameSettings::CREATURES_DAILY_STACK_EXPERIENCE, "creatures", "dailyStackExperience" },
{EGameSettings::CREATURES_WEEKLY_GROWTH_CAP, "creatures", "weeklyGrowthCap" },
{EGameSettings::CREATURES_WEEKLY_GROWTH_PERCENT, "creatures", "weeklyGrowthPercent" },
{EGameSettings::DIMENSION_DOOR_EXPOSES_TERRAIN_TYPE, "spells", "dimensionDoorExposesTerrainType" },
{EGameSettings::DIMENSION_DOOR_FAILURE_SPENDS_POINTS, "spells", "dimensionDoorFailureSpendsPoints" },
{EGameSettings::DIMENSION_DOOR_ONLY_TO_UNCOVERED_TILES, "spells", "dimensionDoorOnlyToUncoveredTiles" },
{EGameSettings::DIMENSION_DOOR_TOURNAMENT_RULES_LIMIT, "spells", "dimensionDoorTournamentRulesLimit" },
{EGameSettings::DIMENSION_DOOR_TRIGGERS_GUARDS, "spells", "dimensionDoorTriggersGuards" },
{EGameSettings::DWELLINGS_ACCUMULATE_WHEN_NEUTRAL, "dwellings", "accumulateWhenNeutral" },
{EGameSettings::DWELLINGS_ACCUMULATE_WHEN_OWNED, "dwellings", "accumulateWhenOwned" },
{EGameSettings::DWELLINGS_MERGE_ON_RECRUIT, "dwellings", "mergeOnRecruit" },
{EGameSettings::HEROES_BACKPACK_CAP, "heroes", "backpackSize" },
{EGameSettings::HEROES_MINIMAL_PRIMARY_SKILLS, "heroes", "minimalPrimarySkills" },
{EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP, "heroes", "perPlayerOnMapCap" },
{EGameSettings::HEROES_PER_PLAYER_TOTAL_CAP, "heroes", "perPlayerTotalCap" },
{EGameSettings::HEROES_RETREAT_ON_WIN_WITHOUT_TROOPS, "heroes", "retreatOnWinWithoutTroops" },
{EGameSettings::HEROES_STARTING_STACKS_CHANCES, "heroes", "startingStackChances" },
{EGameSettings::HEROES_TAVERN_INVITE, "heroes", "tavernInvite" },
{EGameSettings::MAP_FORMAT_ARMAGEDDONS_BLADE, "mapFormat", "armageddonsBlade" },
{EGameSettings::MAP_FORMAT_CHRONICLES, "mapFormat", "chronicles" },
{EGameSettings::MAP_FORMAT_HORN_OF_THE_ABYSS, "mapFormat", "hornOfTheAbyss" },
{EGameSettings::MAP_FORMAT_IN_THE_WAKE_OF_GODS, "mapFormat", "inTheWakeOfGods" },
{EGameSettings::MAP_FORMAT_JSON_VCMI, "mapFormat", "jsonVCMI" },
{EGameSettings::MAP_FORMAT_RESTORATION_OF_ERATHIA, "mapFormat", "restorationOfErathia" },
{EGameSettings::MAP_FORMAT_SHADOW_OF_DEATH, "mapFormat", "shadowOfDeath" },
{EGameSettings::MARKETS_BLACK_MARKET_RESTOCK_PERIOD, "markets", "blackMarketRestockPeriod" },
{EGameSettings::MODULE_COMMANDERS, "modules", "commanders" },
{EGameSettings::MODULE_STACK_ARTIFACT, "modules", "stackArtifact" },
{EGameSettings::MODULE_STACK_EXPERIENCE, "modules", "stackExperience" },
{EGameSettings::PATHFINDER_IGNORE_GUARDS, "pathfinder", "ignoreGuards" },
{EGameSettings::PATHFINDER_ORIGINAL_FLY_RULES, "pathfinder", "originalFlyRules" },
{EGameSettings::PATHFINDER_USE_BOAT, "pathfinder", "useBoat" },
{EGameSettings::PATHFINDER_USE_MONOLITH_ONE_WAY_RANDOM, "pathfinder", "useMonolithOneWayRandom" },
{EGameSettings::PATHFINDER_USE_MONOLITH_ONE_WAY_UNIQUE, "pathfinder", "useMonolithOneWayUnique" },
{EGameSettings::PATHFINDER_USE_MONOLITH_TWO_WAY, "pathfinder", "useMonolithTwoWay" },
{EGameSettings::PATHFINDER_USE_WHIRLPOOL, "pathfinder", "useWhirlpool" },
{EGameSettings::TEXTS_ARTIFACT, "textData", "artifact" },
{EGameSettings::TEXTS_CREATURE, "textData", "creature" },
{EGameSettings::TEXTS_FACTION, "textData", "faction" },
{EGameSettings::TEXTS_HERO, "textData", "hero" },
{EGameSettings::TEXTS_HERO_CLASS, "textData", "heroClass" },
{EGameSettings::TEXTS_OBJECT, "textData", "object" },
{EGameSettings::TEXTS_RIVER, "textData", "river" },
{EGameSettings::TEXTS_ROAD, "textData", "road" },
{EGameSettings::TEXTS_SPELL, "textData", "spell" },
{EGameSettings::TEXTS_TERRAIN, "textData", "terrain" },
{EGameSettings::TOWNS_BUILDINGS_PER_TURN_CAP, "towns", "buildingsPerTurnCap" },
{EGameSettings::TOWNS_STARTING_DWELLING_CHANCES, "towns", "startingDwellingChances" },
{EGameSettings::TOWNS_SPELL_RESEARCH, "towns", "spellResearch" },
{EGameSettings::TOWNS_SPELL_RESEARCH_COST, "towns", "spellResearchCost" },
{EGameSettings::TOWNS_SPELL_RESEARCH_PER_DAY, "towns", "spellResearchPerDay" },
{EGameSettings::TOWNS_SPELL_RESEARCH_COST_EXPONENT_PER_RESEARCH, "towns", "spellResearchCostExponentPerResearch" },
{EGameSettings::INTERFACE_PLAYER_COLORED_BACKGROUND, "interface", "playerColoredBackground" },
};
void GameSettings::loadBase(const JsonNode & input)

View File

@ -94,6 +94,7 @@ public:
virtual void showInfoDialog(InfoWindow * iw) = 0;
virtual void changeSpells(const CGHeroInstance * hero, bool give, const std::set<SpellID> &spells)=0;
virtual void setResearchedSpells(const CGTownInstance * town, int level, const std::vector<SpellID> spells, bool accepted)=0;
virtual bool removeObject(const CGObjectInstance * obj, const PlayerColor & initiator) = 0;
virtual void createBoat(const int3 & visitablePosition, BoatId type, PlayerColor initiator) = 0;
virtual void setOwner(const CGObjectInstance * objid, PlayerColor owner)=0;
@ -139,7 +140,7 @@ public:
virtual void setManaPoints(ObjectInstanceID hid, int val)=0;
virtual void giveHero(ObjectInstanceID id, PlayerColor player, ObjectInstanceID boatId = ObjectInstanceID()) = 0;
virtual void changeObjPos(ObjectInstanceID objid, int3 newPos, const PlayerColor & initiator)=0;
virtual void sendAndApply(CPackForClient * pack) = 0;
virtual void sendAndApply(CPackForClient & pack) = 0;
virtual void heroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2)=0; //when two heroes meet on adventure map
virtual void changeFogOfWar(int3 center, ui32 radius, PlayerColor player, ETileVisibility mode) = 0;
virtual void changeFogOfWar(const std::unordered_set<int3> &tiles, PlayerColor player, ETileVisibility mode) = 0;

View File

@ -79,6 +79,11 @@ enum class EGameSettings
TEXTS_TERRAIN,
TOWNS_BUILDINGS_PER_TURN_CAP,
TOWNS_STARTING_DWELLING_CHANCES,
INTERFACE_PLAYER_COLORED_BACKGROUND,
TOWNS_SPELL_RESEARCH,
TOWNS_SPELL_RESEARCH_COST,
TOWNS_SPELL_RESEARCH_PER_DAY,
TOWNS_SPELL_RESEARCH_COST_EXPONENT_PER_RESEARCH,
OPTIONS_COUNT,
OPTIONS_BEGIN = BONUSES_GLOBAL

View File

@ -50,7 +50,7 @@ std::shared_ptr<RiverType> RiverTypeHandler::loadFromJson(
info->paletteAnimation.push_back(element);
}
VLC->generaltexth->registerString(scope, info->getNameTextID(), json["text"].String());
VLC->generaltexth->registerString(scope, info->getNameTextID(), json["text"]);
return info;
}

View File

@ -41,7 +41,7 @@ std::shared_ptr<RoadType> RoadTypeHandler::loadFromJson(
info->shortIdentifier = json["shortIdentifier"].String();
info->movementCost = json["moveCost"].Integer();
VLC->generaltexth->registerString(scope,info->getNameTextID(), json["text"].String());
VLC->generaltexth->registerString(scope,info->getNameTextID(), json["text"]);
return info;
}

View File

@ -45,7 +45,7 @@ std::shared_ptr<TerrainType> TerrainTypeHandler::loadFromJson( const std::string
info->transitionRequired = json["transitionRequired"].Bool();
info->terrainViewPatterns = json["terrainViewPatterns"].String();
VLC->generaltexth->registerString(scope, info->getNameTextID(), json["text"].String());
VLC->generaltexth->registerString(scope, info->getNameTextID(), json["text"]);
const JsonVector & unblockedVec = json["minimapUnblocked"].Vector();
info->minimapUnblocked =

View File

@ -927,7 +927,7 @@ bool CBattleInfoCallback::handleObstacleTriggersForUnit(SpellCastEnvironment & s
bocp.battleID = getBattle()->getBattleID();
bocp.changes.emplace_back(spellObstacle.uniqueID, operation);
changedObstacle.toInfo(bocp.changes.back(), operation);
spellEnv.apply(&bocp);
spellEnv.apply(bocp);
};
const auto side = unit.unitSide();
auto shouldReveal = !spellObstacle->hidden || !battleIsObstacleVisibleForSide(*obstacle, side);

View File

@ -292,8 +292,8 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons
ret->modScope = source.getModScope();
ret->town = town;
VLC->generaltexth->registerString(source.getModScope(), ret->getNameTextID(), source["name"].String());
VLC->generaltexth->registerString(source.getModScope(), ret->getDescriptionTextID(), source["description"].String());
VLC->generaltexth->registerString(source.getModScope(), ret->getNameTextID(), source["name"]);
VLC->generaltexth->registerString(source.getModScope(), ret->getDescriptionTextID(), source["description"]);
ret->subId = vstd::find_or(MappedKeys::SPECIAL_BUILDINGS, source["type"].String(), BuildingSubID::NONE);
ret->resources = TResources(source["cost"]);
@ -603,7 +603,7 @@ void CTownHandler::loadTown(CTown * town, const JsonNode & source)
town->namesCount = 0;
for(const auto & name : source["names"].Vector())
{
VLC->generaltexth->registerString(town->faction->modScope, town->getRandomNameTextID(town->namesCount), name.String());
VLC->generaltexth->registerString(town->faction->modScope, town->getRandomNameTextID(town->namesCount), name);
town->namesCount += 1;
}
@ -718,8 +718,8 @@ std::shared_ptr<CFaction> CTownHandler::loadFromJson(const std::string & scope,
faction->modScope = scope;
faction->identifier = identifier;
VLC->generaltexth->registerString(scope, faction->getNameTextID(), source["name"].String());
VLC->generaltexth->registerString(scope, faction->getDescriptionTextID(), source["description"].String());
VLC->generaltexth->registerString(scope, faction->getNameTextID(), source["name"]);
VLC->generaltexth->registerString(scope, faction->getDescriptionTextID(), source["description"]);
faction->creatureBg120 = ImagePath::fromJson(source["creatureBackground"]["120px"]);
faction->creatureBg130 = ImagePath::fromJson(source["creatureBackground"]["130px"]);

View File

@ -1143,9 +1143,9 @@ PlayerRelations CGameState::getPlayerRelations( PlayerColor color1, PlayerColor
return PlayerRelations::ENEMIES;
}
void CGameState::apply(CPackForClient *pack)
void CGameState::apply(CPackForClient & pack)
{
pack->applyGs(this);
pack.applyGs(this);
}
void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out)

View File

@ -98,7 +98,7 @@ public:
/// picks next free hero type of the H3 hero init sequence -> chosen starting hero, then unused hero type randomly
HeroTypeID pickNextHeroType(const PlayerColor & owner);
void apply(CPackForClient *pack);
void apply(CPackForClient & pack);
BattleField battleGetBattlefieldType(int3 tile, vstd::RNG & rand);
void fillUpgradeInfo(const CArmedInstance *obj, SlotID stackPos, UpgradeInfo &out) const override;

View File

@ -230,6 +230,27 @@ void JsonUtils::inherit(JsonNode & descendant, const JsonNode & base)
std::swap(descendant, inheritedNode);
}
JsonNode JsonUtils::assembleFromFiles(const JsonNode & files, bool & isValid)
{
if (files.isVector())
{
auto configList = files.convertTo<std::vector<std::string> >();
JsonNode result = JsonUtils::assembleFromFiles(configList, isValid);
return result;
}
else
{
return files;
}
}
JsonNode JsonUtils::assembleFromFiles(const JsonNode & files)
{
bool isValid = false;
return assembleFromFiles(files, isValid);
}
JsonNode JsonUtils::assembleFromFiles(const std::vector<std::string> & files)
{
bool isValid = false;
@ -275,4 +296,28 @@ JsonNode JsonUtils::assembleFromFiles(const std::string & filename)
return result;
}
void JsonUtils::detectConflicts(JsonNode & result, const JsonNode & left, const JsonNode & right, const std::string & keyName)
{
switch (left.getType())
{
case JsonNode::JsonType::DATA_NULL:
case JsonNode::JsonType::DATA_BOOL:
case JsonNode::JsonType::DATA_FLOAT:
case JsonNode::JsonType::DATA_INTEGER:
case JsonNode::JsonType::DATA_STRING:
case JsonNode::JsonType::DATA_VECTOR: // NOTE: comparing vectors as whole - since merge will overwrite it in its entirety
{
result[keyName][left.getModScope()] = left;
result[keyName][right.getModScope()] = right;
return;
}
case JsonNode::JsonType::DATA_STRUCT:
{
for(const auto & node : left.Struct())
if (!right[node.first].isNull())
detectConflicts(result, node.second, right[node.first], keyName + "/" + node.first);
}
}
}
VCMI_LIB_NAMESPACE_END

View File

@ -44,6 +44,8 @@ namespace JsonUtils
* @brief generate one Json structure from multiple files
* @param files - list of filenames with parts of json structure
*/
DLL_LINKAGE JsonNode assembleFromFiles(const JsonNode & files);
DLL_LINKAGE JsonNode assembleFromFiles(const JsonNode & files, bool & isValid);
DLL_LINKAGE JsonNode assembleFromFiles(const std::vector<std::string> & files);
DLL_LINKAGE JsonNode assembleFromFiles(const std::vector<std::string> & files, bool & isValid);
@ -72,6 +74,12 @@ namespace JsonUtils
/// get schema by json URI: vcmi:<name of file in schemas directory>#<entry in file, optional>
/// example: schema "vcmi:settings" is used to check user settings
DLL_LINKAGE const JsonNode & getSchema(const std::string & URI);
/// detects potential conflicts - json entries present in both nodes
/// returns JsonNode that contains list of conflicting keys
/// For each conflict - list of conflicting mods and list of conflicting json values
/// result[pathToKey][modID] -> node that was conflicting
DLL_LINKAGE void detectConflicts(JsonNode & result, const JsonNode & left, const JsonNode & right, const std::string & keyName);
}
VCMI_LIB_NAMESPACE_END

View File

@ -28,7 +28,7 @@ void CBankInstanceConstructor::initTypeData(const JsonNode & input)
if (input.Struct().count("name") == 0)
logMod->warn("Bank %s missing name!", getJsonKey());
VLC->generaltexth->registerString(input.getModScope(), getNameTextID(), input["name"].String());
VLC->generaltexth->registerString(input.getModScope(), getNameTextID(), input["name"]);
levels = input["levels"].Vector();
bankResetDuration = static_cast<si32>(input["resetDuration"].Float());

View File

@ -278,7 +278,7 @@ std::unique_ptr<ObjectClass> CObjectClassesHandler::loadFromJson(const std::stri
newObject->base = json["base"];
newObject->id = index;
VLC->generaltexth->registerString(scope, newObject->getNameTextID(), json["name"].String());
VLC->generaltexth->registerString(scope, newObject->getNameTextID(), json["name"]);
newObject->objectTypeHandlers.resize(json["lastReservedIndex"].Float() + 1);

View File

@ -14,6 +14,7 @@
#include "../mapObjects/CRewardableObject.h"
#include "../texts/CGeneralTextHandler.h"
#include "../IGameCallback.h"
#include "../CConfigHandler.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -23,9 +24,10 @@ void CRewardableConstructor::initTypeData(const JsonNode & config)
blockVisit = config["blockedVisitable"].Bool();
if (!config["name"].isNull())
VLC->generaltexth->registerString( config.getModScope(), getNameTextID(), config["name"].String());
VLC->generaltexth->registerString( config.getModScope(), getNameTextID(), config["name"]);
JsonUtils::validate(config, "vcmi:rewardable", getJsonKey());
if (settings["mods"]["validation"].String() != "off")
JsonUtils::validate(config, "vcmi:rewardable", getJsonKey());
}
@ -43,9 +45,10 @@ CGObjectInstance * CRewardableConstructor::create(IGameCallback * cb, std::share
return ret;
}
Rewardable::Configuration CRewardableConstructor::generateConfiguration(IGameCallback * cb, vstd::RNG & rand, MapObjectID objectID) const
Rewardable::Configuration CRewardableConstructor::generateConfiguration(IGameCallback * cb, vstd::RNG & rand, MapObjectID objectID, const std::map<std::string, JsonNode> & presetVariables) const
{
Rewardable::Configuration result;
result.variables.preset = presetVariables;
objectInfo.configureObject(result, rand, cb);
for(auto & rewardInfo : result.info)
@ -67,7 +70,7 @@ void CRewardableConstructor::configureObject(CGObjectInstance * object, vstd::RN
if (!rewardableObject)
throw std::runtime_error("Object " + std::to_string(object->getObjGroupIndex()) + ", " + std::to_string(object->getObjTypeIndex()) + " is not a rewardable object!" );
rewardableObject->configuration = generateConfiguration(object->cb, rng, object->ID);
rewardableObject->configuration = generateConfiguration(object->cb, rng, object->ID, rewardableObject->configuration.variables.preset);
rewardableObject->initializeGuards();
if (rewardableObject->configuration.info.empty())

View File

@ -31,7 +31,7 @@ public:
std::unique_ptr<IObjectInfo> getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const override;
Rewardable::Configuration generateConfiguration(IGameCallback * cb, vstd::RNG & rand, MapObjectID objectID) const;
Rewardable::Configuration generateConfiguration(IGameCallback * cb, vstd::RNG & rand, MapObjectID objectID, const std::map<std::string, JsonNode> & presetVariables) const;
};
VCMI_LIB_NAMESPACE_END

View File

@ -29,7 +29,7 @@ void DwellingInstanceConstructor::initTypeData(const JsonNode & input)
if (input.Struct().count("name") == 0)
logMod->warn("Dwelling %s missing name!", getJsonKey());
VLC->generaltexth->registerString( input.getModScope(), getNameTextID(), input["name"].String());
VLC->generaltexth->registerString( input.getModScope(), getNameTextID(), input["name"]);
const JsonVector & levels = input["creatures"].Vector();
const auto totalLevels = levels.size();

View File

@ -138,7 +138,7 @@ bool CBank::wasVisited (PlayerColor player) const
void CBank::onHeroVisit(const CGHeroInstance * h) const
{
ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_PLAYER, id, h->id);
cb->sendAndApply(&cov);
cb->sendAndApply(cov);
BlockingDialog bd(true, false);
bd.player = h->getOwner();

View File

@ -66,6 +66,18 @@ std::string CGCreature::getHoverText(const CGHeroInstance * hero) const
}
}
std::string CGCreature::getMonsterLevelText() const
{
std::string monsterLevel = VLC->generaltexth->translate("vcmi.adventureMap.monsterLevel");
bool isRanged = VLC->creatures()->getById(getCreature())->getBonusBearer()->hasBonusOfType(BonusType::SHOOTER);
std::string attackTypeKey = isRanged ? "vcmi.adventureMap.monsterRangedType" : "vcmi.adventureMap.monsterMeleeType";
std::string attackType = VLC->generaltexth->translate(attackTypeKey);
boost::replace_first(monsterLevel, "%TOWN", (*VLC->townh)[VLC->creatures()->getById(getCreature())->getFaction()]->getNameTranslated());
boost::replace_first(monsterLevel, "%LEVEL", std::to_string(VLC->creatures()->getById(getCreature())->getLevel()));
boost::replace_first(monsterLevel, "%ATTACK_TYPE", attackType);
return monsterLevel;
}
std::string CGCreature::getPopupText(const CGHeroInstance * hero) const
{
std::string hoverName;
@ -102,15 +114,13 @@ std::string CGCreature::getPopupText(const CGHeroInstance * hero) const
if (settings["general"]["enableUiEnhancements"].Bool())
{
std::string monsterLevel = VLC->generaltexth->translate("vcmi.adventureMap.monsterLevel");
boost::replace_first(monsterLevel, "%TOWN", (*VLC->townh)[VLC->creatures()->getById(getCreature())->getFaction()]->getNameTranslated());
boost::replace_first(monsterLevel, "%LEVEL", std::to_string(VLC->creatures()->getById(getCreature())->getLevel()));
hoverName += monsterLevel;
hoverName += getMonsterLevelText();
hoverName += VLC->generaltexth->translate("vcmi.adventureMap.monsterThreat.title");
int choice;
double ratio = (static_cast<double>(getArmyStrength()) / hero->getTotalStrength());
uint64_t armyStrength = getArmyStrength();
uint64_t heroStrength = hero->getTotalStrength();
double ratio = static_cast<double>(armyStrength) / heroStrength;
if (ratio < 0.1) choice = 0;
else if (ratio < 0.25) choice = 1;
else if (ratio < 0.6) choice = 2;
@ -131,7 +141,10 @@ std::string CGCreature::getPopupText(const CGHeroInstance * hero) const
std::string CGCreature::getPopupText(PlayerColor player) const
{
return getHoverText(player);
std::string hoverName = getHoverText(player);
if (settings["general"]["enableUiEnhancements"].Bool())
hoverName += getMonsterLevelText();
return hoverName;
}
std::vector<Component> CGCreature::getPopupComponents(PlayerColor player) const

View File

@ -81,7 +81,7 @@ private:
int takenAction(const CGHeroInstance *h, bool allowJoin=true) const; //action on confrontation: -2 - fight, -1 - flee, >=0 - will join for given value of gold (may be 0)
void giveReward(const CGHeroInstance * h) const;
std::string getMonsterLevelText() const;
};
VCMI_LIB_NAMESPACE_END

View File

@ -224,7 +224,7 @@ void CGDwelling::onHeroVisit( const CGHeroInstance * h ) const
iw.player = h->tempOwner;
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 44); //{%s} \n\n The camp is deserted. Perhaps you should try next week.
iw.text.replaceName(ID);
cb->sendAndApply(&iw);
cb->sendAndApply(iw);
return;
}
@ -324,7 +324,7 @@ void CGDwelling::newTurn(vstd::RNG & rand) const
}
if(change)
cb->sendAndApply(&sac);
cb->sendAndApply(sac);
updateGuards();
}
@ -392,7 +392,7 @@ void CGDwelling::updateGuards() const
csc.slot = slot;
csc.count = crea->getGrowth() * 3;
csc.absoluteValue = true;
cb->sendAndApply(&csc);
cb->sendAndApply(csc);
}
else //slot is empty, create whole new stack
{
@ -401,7 +401,7 @@ void CGDwelling::updateGuards() const
ns.slot = slot;
ns.type = crea->getId();
ns.count = crea->getGrowth() * 3;
cb->sendAndApply(&ns);
cb->sendAndApply(ns);
}
}
}
@ -458,7 +458,7 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h) const
iw.text.replaceNamePlural(crid);
cb->showInfoDialog(&iw);
cb->sendAndApply(&sac);
cb->sendAndApply(sac);
cb->addToSlot(StackLocation(h, slot), crs, count);
}
}
@ -469,7 +469,7 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h) const
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 422); //There are no %s here to recruit.
iw.text.replaceNamePlural(crid);
iw.player = h->tempOwner;
cb->sendAndApply(&iw);
cb->sendAndApply(iw);
}
}
else
@ -483,7 +483,7 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h) const
sac.creatures[0].first = !h->getArt(ArtifactPosition::MACH1); //ballista
sac.creatures[1].first = !h->getArt(ArtifactPosition::MACH3); //first aid tent
sac.creatures[2].first = !h->getArt(ArtifactPosition::MACH2); //ammo cart
cb->sendAndApply(&sac);
cb->sendAndApply(sac);
}
auto windowMode = (ID == Obj::CREATURE_GENERATOR1 || ID == Obj::REFUGEE_CAMP) ? EOpenWindowMode::RECRUITMENT_FIRST : EOpenWindowMode::RECRUITMENT_ALL;

View File

@ -827,7 +827,7 @@ void CGHeroInstance::spendMana(ServerCallback * server, const int spellCost) con
sm.hid = id;
sm.val = -spellCost;
server->apply(&sm);
server->apply(sm);
}
}

View File

@ -93,7 +93,7 @@ void CGBlackMarket::newTurn(vstd::RNG & rand) const
SetAvailableArtifacts saa;
saa.id = id;
cb->pickAllowedArtsSet(saa.arts, rand);
cb->sendAndApply(&saa);
cb->sendAndApply(saa);
}
std::vector<TradeItemBuy> CGUniversity::availableItemsIds(EMarketMode mode) const

View File

@ -268,7 +268,10 @@ CGTownInstance::CGTownInstance(IGameCallback *cb):
built(0),
destroyed(0),
identifier(0),
alignmentToPlayer(PlayerColor::NEUTRAL)
alignmentToPlayer(PlayerColor::NEUTRAL),
spellResearchCounterDay(0),
spellResearchAcceptedCounter(0),
spellResearchAllowed(true)
{
this->setNodeType(CBonusSystemNode::TOWN);
}
@ -347,7 +350,7 @@ void CGTownInstance::onHeroVisit(const CGHeroInstance * h) const
scp.heroid = h->id;
scp.which = SetCommanderProperty::ALIVE;
scp.amount = 1;
cb->sendAndApply(&scp);
cb->sendAndApply(scp);
}
cb->heroVisitCastle(this, h);
// TODO(vmarkovtsev): implement payment for rising the commander
@ -628,7 +631,7 @@ void CGTownInstance::removeCapitols(const PlayerColor & owner) const
rs.tid = id;
rs.bid.insert(BuildingID::CAPITOL);
rs.destroyed = destroyed;
cb->sendAndApply(&rs);
cb->sendAndApply(rs);
return;
}
}

View File

@ -73,6 +73,9 @@ public:
std::vector<std::vector<SpellID> > spells; //spells[level] -> vector of spells, first will be available in guild
std::vector<CCastleEvent> events;
std::pair<si32, si32> bonusValue;//var to store town bonuses (rampart = resources from mystic pond, factory = save debts);
int spellResearchCounterDay;
int spellResearchAcceptedCounter;
bool spellResearchAllowed;
//////////////////////////////////////////////////////////////////////////
template <typename Handler> void serialize(Handler &h)
@ -93,6 +96,13 @@ public:
h & spells;
h & events;
if (h.version >= Handler::Version::SPELL_RESEARCH)
{
h & spellResearchCounterDay;
h & spellResearchAcceptedCounter;
h & spellResearchAllowed;
}
if (h.version >= Handler::Version::NEW_TOWN_BUILDINGS)
{
h & rewardableBuildings;

View File

@ -588,7 +588,7 @@ void CGSeerHut::onHeroVisit(const CGHeroInstance * h) const
AddQuest aq;
aq.quest = QuestInfo (quest, this, visitablePos());
aq.player = h->tempOwner;
cb->sendAndApply(&aq); //TODO: merge with setObjProperty?
cb->sendAndApply(aq); //TODO: merge with setObjProperty?
}
if(firstVisit || failRequirements)
@ -811,7 +811,7 @@ void CGKeymasterTent::onHeroVisit( const CGHeroInstance * h ) const
cow.mode = ChangeObjectVisitors::VISITOR_GLOBAL;
cow.hero = h->id;
cow.object = id;
cb->sendAndApply(&cow);
cb->sendAndApply(cow);
txt_id=19;
}
else
@ -860,7 +860,7 @@ void CGBorderGuard::onHeroVisit(const CGHeroInstance * h) const
AddQuest aq;
aq.quest = QuestInfo (quest, this, visitablePos());
aq.player = h->tempOwner;
cb->sendAndApply (&aq);
cb->sendAndApply(aq);
//TODO: add this quest only once OR check for multiple instances later
}
}
@ -885,7 +885,7 @@ void CGBorderGate::onHeroVisit(const CGHeroInstance * h) const //TODO: passabili
AddQuest aq;
aq.quest = QuestInfo (quest, this, visitablePos());
aq.player = h->tempOwner;
cb->sendAndApply (&aq);
cb->sendAndApply(aq);
}
}

View File

@ -35,7 +35,7 @@ const IObjectInterface * CRewardableObject::getObject() const
void CRewardableObject::markAsScouted(const CGHeroInstance * hero) const
{
ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_PLAYER, id, hero->id);
cb->sendAndApply(&cov);
cb->sendAndApply(cov);
}
bool CRewardableObject::isGuarded() const
@ -48,7 +48,7 @@ void CRewardableObject::onHeroVisit(const CGHeroInstance *hero) const
if(!wasScouted(hero->getOwner()))
{
ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_SCOUTED, id, hero->id);
cb->sendAndApply(&cov);
cb->sendAndApply(cov);
}
if (isGuarded())
@ -116,7 +116,7 @@ void CRewardableObject::markAsVisited(const CGHeroInstance * hero) const
cb->setObjPropertyValue(id, ObjProperty::REWARD_CLEARED, true);
ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_HERO, id, hero->id);
cb->sendAndApply(&cov);
cb->sendAndApply(cov);
}
void CRewardableObject::grantReward(ui32 rewardID, const CGHeroInstance * hero) const
@ -329,14 +329,14 @@ void CRewardableObject::newTurn(vstd::RNG & rand) const
if (configuration.resetParameters.rewards)
{
auto handler = std::dynamic_pointer_cast<const CRewardableConstructor>(getObjectHandler());
auto newConfiguration = handler->generateConfiguration(cb, rand, ID);
auto newConfiguration = handler->generateConfiguration(cb, rand, ID, configuration.variables.preset);
cb->setRewardableObjectConfiguration(id, newConfiguration);
}
if (configuration.resetParameters.visitors)
{
cb->setObjPropertyValue(id, ObjProperty::REWARD_CLEARED, false);
ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_CLEAR, id);
cb->sendAndApply(&cov);
cb->sendAndApply(cov);
}
}
}

View File

@ -28,7 +28,7 @@ void IObjectInterface::showInfoDialog(const ui32 txtID, const ui16 soundID, EInf
iw.player = getOwner();
iw.type = mode;
iw.text.appendLocalString(EMetaText::ADVOB_TXT,txtID);
cb->sendAndApply(&iw);
cb->sendAndApply(iw);
}
///IObjectInterface

View File

@ -1087,14 +1087,14 @@ void CGMagi::onHeroVisit(const CGHeroInstance * h) const
for(const auto & eye : eyes)
{
cb->getTilesInRange (fw.tiles, eye->pos, 10, ETileVisibility::HIDDEN, h->tempOwner);
cb->sendAndApply(&fw);
cb->sendAndApply(fw);
cv.pos = eye->pos;
cb->sendAndApply(&cv);
cb->sendAndApply(cv);
}
cv.pos = h->visitablePos();
cv.focusTime = 0;
cb->sendAndApply(&cv);
cb->sendAndApply(cv);
}
}
else if (ID == Obj::EYE_OF_MAGI)
@ -1258,7 +1258,7 @@ void CGObelisk::onHeroVisit( const CGHeroInstance * h ) const
if(!wasVisited(team))
{
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 96);
cb->sendAndApply(&iw);
cb->sendAndApply(iw);
// increment general visited obelisks counter
cb->setObjPropertyID(id, ObjProperty::OBELISK_VISITED, team);
@ -1273,7 +1273,7 @@ void CGObelisk::onHeroVisit( const CGHeroInstance * h ) const
else
{
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 97);
cb->sendAndApply(&iw);
cb->sendAndApply(iw);
}
}
@ -1341,7 +1341,7 @@ void CGLighthouse::onHeroVisit( const CGHeroInstance * h ) const
rb.whoID = oldOwner;
rb.source = BonusSource::OBJECT_INSTANCE;
rb.id = BonusSourceID(id);
cb->sendAndApply(&rb);
cb->sendAndApply(rb);
}
}
}
@ -1372,7 +1372,7 @@ void CGLighthouse::giveBonusTo(const PlayerColor & player, bool onInit) const
if(onInit)
gb.applyGs(cb->gameState());
else
cb->sendAndApply(&gb);
cb->sendAndApply(gb);
}
void CGLighthouse::serializeJsonOptions(JsonSerializeFormat& handler)

View File

@ -189,7 +189,7 @@ void CMapHeader::registerMapStrings()
JsonUtils::mergeCopy(data, translations[language]);
for(auto & s : data.Struct())
texts.registerString("map", TextIdentifier(s.first), s.second.String(), language);
texts.registerString("map", TextIdentifier(s.first), s.second.String());
}
std::string mapRegisterLocalizedString(const std::string & modContext, CMapHeader & mapHeader, const TextIdentifier & UID, const std::string & localized)
@ -199,7 +199,7 @@ std::string mapRegisterLocalizedString(const std::string & modContext, CMapHeade
std::string mapRegisterLocalizedString(const std::string & modContext, CMapHeader & mapHeader, const TextIdentifier & UID, const std::string & localized, const std::string & language)
{
mapHeader.texts.registerString(modContext, UID, localized, language);
mapHeader.texts.registerString(modContext, UID, localized);
mapHeader.translations.Struct()[language].Struct()[UID.get()].String() = localized;
return UID.get();
}

View File

@ -2235,10 +2235,7 @@ CGObjectInstance * CMapLoaderH3M::readTown(const int3 & position, std::shared_pt
}
if(features.levelHOTA1)
{
// TODO: HOTA support
[[maybe_unused]] bool spellResearchAvailable = reader->readBool();
}
object->spellResearchAllowed = reader->readBool();
// Read castle events
uint32_t eventsCount = reader->readUInt32();

View File

@ -17,6 +17,7 @@
#include "ModIncompatibility.h"
#include "../CCreatureHandler.h"
#include "../CConfigHandler.h"
#include "../CStopWatch.h"
#include "../GameSettings.h"
#include "../ScriptHandler.h"
@ -331,10 +332,38 @@ void CModHandler::loadModFilesystems()
coreMod->updateChecksum(calculateModChecksum(ModScope::scopeBuiltin(), CResourceHandler::get(ModScope::scopeBuiltin())));
std::map<std::string, ISimpleResourceLoader *> modFilesystems;
for(std::string & modName : activeMods)
modFilesystems[modName] = genModFilesystem(modName, allMods[modName].config);
for(std::string & modName : activeMods)
CResourceHandler::addFilesystem("data", modName, modFilesystems[modName]);
if (settings["mods"]["validation"].String() == "full")
{
CModInfo & mod = allMods[modName];
CResourceHandler::addFilesystem("data", modName, genModFilesystem(modName, mod.config));
for(std::string & leftModName : activeMods)
{
for(std::string & rightModName : activeMods)
{
if (leftModName == rightModName)
continue;
if (getModDependencies(leftModName).count(rightModName) || getModDependencies(rightModName).count(leftModName))
continue;
const auto & filter = [](const ResourcePath &path){return path.getType() != EResType::DIRECTORY;};
std::unordered_set<ResourcePath> leftResources = modFilesystems[leftModName]->getFilteredFiles(filter);
std::unordered_set<ResourcePath> rightResources = modFilesystems[rightModName]->getFilteredFiles(filter);
for (auto const & leftFile : leftResources)
{
if (rightResources.count(leftFile))
logMod->warn("Potential confict detected between '%s' and '%s': both mods add file '%s'", leftModName, rightModName, leftFile.getOriginalName());
}
}
}
}
}
@ -370,6 +399,12 @@ std::string CModHandler::getModLanguage(const TModID& modId) const
return allMods.at(modId).baseLanguage;
}
std::set<TModID> CModHandler::getModDependencies(const TModID & modId) const
{
bool isModFound;
return getModDependencies(modId, isModFound);
}
std::set<TModID> CModHandler::getModDependencies(const TModID & modId, bool & isModFound) const
{
auto it = allMods.find(modId);
@ -384,7 +419,7 @@ std::set<TModID> CModHandler::getModDependencies(const TModID & modId, bool & is
void CModHandler::initializeConfig()
{
VLC->settingsHandler->loadBase(coreMod->config["settings"]);
VLC->settingsHandler->loadBase(JsonUtils::assembleFromFiles(coreMod->config["settings"]));
for(const TModID & modName : activeMods)
{
@ -401,33 +436,6 @@ CModVersion CModHandler::getModVersion(TModID modName) const
return {};
}
bool CModHandler::validateTranslations(TModID modName) const
{
bool result = true;
const auto & mod = allMods.at(modName);
{
auto fileList = mod.config["translations"].convertTo<std::vector<std::string> >();
JsonNode json = JsonUtils::assembleFromFiles(fileList);
result |= VLC->generaltexth->validateTranslation(mod.baseLanguage, modName, json);
}
for(const auto & language : Languages::getLanguageList())
{
if (mod.config[language.identifier].isNull())
continue;
if (mod.config[language.identifier]["skipValidation"].Bool())
continue;
auto fileList = mod.config[language.identifier]["translations"].convertTo<std::vector<std::string> >();
JsonNode json = JsonUtils::assembleFromFiles(fileList);
result |= VLC->generaltexth->validateTranslation(language.identifier, modName, json);
}
return result;
}
void CModHandler::loadTranslation(const TModID & modName)
{
const auto & mod = allMods[modName];
@ -435,14 +443,11 @@ void CModHandler::loadTranslation(const TModID & modName)
std::string preferredLanguage = VLC->generaltexth->getPreferredLanguage();
std::string modBaseLanguage = allMods[modName].baseLanguage;
auto baseTranslationList = mod.config["translations"].convertTo<std::vector<std::string> >();
auto extraTranslationList = mod.config[preferredLanguage]["translations"].convertTo<std::vector<std::string> >();
JsonNode baseTranslation = JsonUtils::assembleFromFiles(mod.config["translations"]);
JsonNode extraTranslation = JsonUtils::assembleFromFiles(mod.config[preferredLanguage]["translations"]);
JsonNode baseTranslation = JsonUtils::assembleFromFiles(baseTranslationList);
JsonNode extraTranslation = JsonUtils::assembleFromFiles(extraTranslationList);
VLC->generaltexth->loadTranslationOverrides(modBaseLanguage, modName, baseTranslation);
VLC->generaltexth->loadTranslationOverrides(preferredLanguage, modName, extraTranslation);
VLC->generaltexth->loadTranslationOverrides(modName, baseTranslation);
VLC->generaltexth->loadTranslationOverrides(modName, extraTranslation);
}
void CModHandler::load()
@ -480,12 +485,6 @@ void CModHandler::load()
for(const TModID & modName : activeMods)
loadTranslation(modName);
#if 0
for(const TModID & modName : activeMods)
if (!validateTranslations(modName))
allMods[modName].validation = CModInfo::FAILED;
#endif
logMod->info("\tLoading mod data: %d ms", timer.getDiff());
VLC->creh->loadCrExpMod();
VLC->identifiersHandler->finalize();

View File

@ -49,8 +49,6 @@ class DLL_LINKAGE CModHandler final : boost::noncopyable
void loadOneMod(std::string modName, const std::string & parent, const JsonNode & modSettings, bool enableMods);
void loadTranslation(const TModID & modName);
bool validateTranslations(TModID modName) const;
CModVersion getModVersion(TModID modName) const;
public:
@ -66,6 +64,7 @@ public:
std::string getModLanguage(const TModID & modId) const;
std::set<TModID> getModDependencies(const TModID & modId) const;
std::set<TModID> getModDependencies(const TModID & modId, bool & isModFound) const;
/// returns list of all (active) mods

View File

@ -17,6 +17,7 @@
#include "../BattleFieldHandler.h"
#include "../CArtHandler.h"
#include "../CCreatureHandler.h"
#include "../CConfigHandler.h"
#include "../entities/faction/CTownHandler.h"
#include "../texts/CGeneralTextHandler.h"
#include "../CHeroHandler.h"
@ -39,9 +40,9 @@
VCMI_LIB_NAMESPACE_BEGIN
ContentTypeHandler::ContentTypeHandler(IHandlerBase * handler, const std::string & objectName):
ContentTypeHandler::ContentTypeHandler(IHandlerBase * handler, const std::string & entityName):
handler(handler),
objectName(objectName),
entityName(entityName),
originalData(handler->loadLegacyData())
{
for(auto & node : originalData)
@ -50,7 +51,7 @@ ContentTypeHandler::ContentTypeHandler(IHandlerBase * handler, const std::string
}
}
bool ContentTypeHandler::preloadModData(const std::string & modName, const std::vector<std::string> & fileList, bool validate)
bool ContentTypeHandler::preloadModData(const std::string & modName, const JsonNode & fileList, bool validate)
{
bool result = false;
JsonNode data = JsonUtils::assembleFromFiles(fileList, result);
@ -79,6 +80,9 @@ bool ContentTypeHandler::preloadModData(const std::string & modName, const std::
logMod->trace("Patching object %s (%s) from %s", objectName, remoteName, modName);
JsonNode & remoteConf = modData[remoteName].patches[objectName];
if (!remoteConf.isNull() && settings["mods"]["validation"].String() != "off")
JsonUtils::detectConflicts(conflictList, remoteConf, entry.second, objectName);
JsonUtils::merge(remoteConf, entry.second);
}
}
@ -93,7 +97,7 @@ bool ContentTypeHandler::loadMod(const std::string & modName, bool validate)
auto performValidate = [&,this](JsonNode & data, const std::string & name){
handler->beforeValidate(data);
if (validate)
result &= JsonUtils::validate(data, "vcmi:" + objectName, name);
result &= JsonUtils::validate(data, "vcmi:" + entityName, name);
};
// apply patches
@ -113,7 +117,7 @@ bool ContentTypeHandler::loadMod(const std::string & modName, bool validate)
// - another mod attempts to add object into this mod (technically can be supported, but might lead to weird edge cases)
// - another mod attempts to edit object from this mod that no longer exist - DANGER since such patch likely has very incomplete data
// so emit warning and skip such case
logMod->warn("Mod '%s' attempts to edit object '%s' of type '%s' from mod '%s' but no such object exist!", data.getModScope(), name, objectName, modName);
logMod->warn("Mod '%s' attempts to edit object '%s' of type '%s' from mod '%s' but no such object exist!", data.getModScope(), name, entityName, modName);
continue;
}
@ -159,31 +163,70 @@ void ContentTypeHandler::loadCustom()
void ContentTypeHandler::afterLoadFinalization()
{
for (auto const & data : modData)
if (settings["mods"]["validation"].String() != "off")
{
if (data.second.modData.isNull())
for (auto const & data : modData)
{
for (auto node : data.second.patches.Struct())
logMod->warn("Mod '%s' have added patch for object '%s' from mod '%s', but this mod was not loaded or has no new objects.", node.second.getModScope(), node.first, data.first);
}
for(auto & otherMod : modData)
{
if (otherMod.first == data.first)
continue;
if (otherMod.second.modData.isNull())
continue;
for(auto & otherObject : otherMod.second.modData.Struct())
if (data.second.modData.isNull())
{
if (data.second.modData.Struct().count(otherObject.first))
for (auto node : data.second.patches.Struct())
logMod->warn("Mod '%s' have added patch for object '%s' from mod '%s', but this mod was not loaded or has no new objects.", node.second.getModScope(), node.first, data.first);
}
for(auto & otherMod : modData)
{
if (otherMod.first == data.first)
continue;
if (otherMod.second.modData.isNull())
continue;
for(auto & otherObject : otherMod.second.modData.Struct())
{
logMod->warn("Mod '%s' have added object with name '%s' that is also available in mod '%s'", data.first, otherObject.first, otherMod.first);
logMod->warn("Two objects with same name were loaded. Please use form '%s:%s' if mod '%s' needs to modify this object instead", otherMod.first, otherObject.first, data.first);
if (data.second.modData.Struct().count(otherObject.first))
{
logMod->warn("Mod '%s' have added object with name '%s' that is also available in mod '%s'", data.first, otherObject.first, otherMod.first);
logMod->warn("Two objects with same name were loaded. Please use form '%s:%s' if mod '%s' needs to modify this object instead", otherMod.first, otherObject.first, data.first);
}
}
}
}
for (const auto& [conflictPath, conflictModData] : conflictList.Struct())
{
std::set<std::string> conflictingMods;
std::set<std::string> resolvedConflicts;
for (auto const & conflictModData : conflictModData.Struct())
conflictingMods.insert(conflictModData.first);
for (auto const & modID : conflictingMods)
resolvedConflicts.merge(VLC->modh->getModDependencies(modID));
vstd::erase_if(conflictingMods, [&resolvedConflicts](const std::string & entry){ return resolvedConflicts.count(entry);});
if (conflictingMods.size() < 2)
continue; // all conflicts were resolved - either via compatibility patch (mod that depends on 2 conflicting mods) or simple mod that depends on another one
bool allEqual = true;
for (auto const & modID : conflictingMods)
{
if (conflictModData[modID] != conflictModData[*conflictingMods.begin()])
{
allEqual = false;
break;
}
}
if (allEqual)
continue; // conflict still present, but all mods use the same value for conflicting entry - permit it
logMod->warn("Potential confict in '%s'", conflictPath);
for (auto const & modID : conflictingMods)
logMod->warn("Mod '%s' - value set to %s", modID, conflictModData[modID].toCompactString());
}
}
handler->afterLoadFinalization();
@ -216,7 +259,7 @@ bool CContentHandler::preloadModData(const std::string & modName, JsonNode modCo
bool result = true;
for(auto & handler : handlers)
{
result &= handler.second.preloadModData(modName, modConfig[handler.first].convertTo<std::vector<std::string> >(), validate);
result &= handler.second.preloadModData(modName, modConfig[handler.first], validate);
}
return result;
}
@ -249,7 +292,7 @@ void CContentHandler::afterLoadFinalization()
void CContentHandler::preloadData(CModInfo & mod)
{
bool validate = (mod.validation != CModInfo::PASSED);
bool validate = validateMod(mod);
// print message in format [<8-symbols checksum>] <modname>
auto & info = mod.getVerificationInfo();
@ -266,7 +309,7 @@ void CContentHandler::preloadData(CModInfo & mod)
void CContentHandler::load(CModInfo & mod)
{
bool validate = (mod.validation != CModInfo::PASSED);
bool validate = validateMod(mod);
if (!loadMod(mod.identifier, validate))
mod.validation = CModInfo::FAILED;
@ -287,4 +330,18 @@ const ContentTypeHandler & CContentHandler::operator[](const std::string & name)
return handlers.at(name);
}
bool CContentHandler::validateMod(const CModInfo & mod) const
{
if (settings["mods"]["validation"].String() == "full")
return true;
if (mod.validation == CModInfo::PASSED)
return false;
if (settings["mods"]["validation"].String() == "off")
return false;
return true;
}
VCMI_LIB_NAMESPACE_END

View File

@ -19,6 +19,8 @@ class CModInfo;
/// internal type to handle loading of one data type (e.g. artifacts, creatures)
class DLL_LINKAGE ContentTypeHandler
{
JsonNode conflictList;
public:
struct ModInfo
{
@ -29,7 +31,7 @@ public:
};
/// handler to which all data will be loaded
IHandlerBase * handler;
std::string objectName;
std::string entityName;
/// contains all loaded H3 data
std::vector<JsonNode> originalData;
@ -39,7 +41,7 @@ public:
/// local version of methods in ContentHandler
/// returns true if loading was successful
bool preloadModData(const std::string & modName, const std::vector<std::string> & fileList, bool validate);
bool preloadModData(const std::string & modName, const JsonNode & fileList, bool validate);
bool loadMod(const std::string & modName, bool validate);
void loadCustom();
void afterLoadFinalization();
@ -56,6 +58,7 @@ class DLL_LINKAGE CContentHandler
std::map<std::string, ContentTypeHandler> handlers;
bool validateMod(const CModInfo & mod) const;
public:
void init();

View File

@ -42,6 +42,7 @@ public:
virtual void visitSetSecSkill(SetSecSkill & pack) {}
virtual void visitHeroVisitCastle(HeroVisitCastle & pack) {}
virtual void visitChangeSpells(ChangeSpells & pack) {}
virtual void visitSetResearchedSpells(SetResearchedSpells & pack) {}
virtual void visitSetMana(SetMana & pack) {}
virtual void visitSetMovePoints(SetMovePoints & pack) {}
virtual void visitFoWChange(FoWChange & pack) {}
@ -128,6 +129,7 @@ public:
virtual void visitBuildStructure(BuildStructure & pack) {}
virtual void visitVisitTownBuilding(VisitTownBuilding & pack) {}
virtual void visitRazeStructure(RazeStructure & pack) {}
virtual void visitSpellResearch(SpellResearch & pack) {}
virtual void visitRecruitCreatures(RecruitCreatures & pack) {}
virtual void visitUpgradeCreature(UpgradeCreature & pack) {}
virtual void visitGarrisonHeroSwap(GarrisonHeroSwap & pack) {}

View File

@ -162,6 +162,10 @@ void ChangeSpells::visitTyped(ICPackVisitor & visitor)
visitor.visitChangeSpells(*this);
}
void SetResearchedSpells::visitTyped(ICPackVisitor & visitor)
{
visitor.visitSetResearchedSpells(*this);
}
void SetMana::visitTyped(ICPackVisitor & visitor)
{
visitor.visitSetMana(*this);
@ -592,6 +596,11 @@ void RazeStructure::visitTyped(ICPackVisitor & visitor)
visitor.visitRazeStructure(*this);
}
void SpellResearch::visitTyped(ICPackVisitor & visitor)
{
visitor.visitSpellResearch(*this);
}
void RecruitCreatures::visitTyped(ICPackVisitor & visitor)
{
visitor.visitRecruitCreatures(*this);
@ -930,6 +939,16 @@ void ChangeSpells::applyGs(CGameState *gs)
hero->removeSpellFromSpellbook(sid);
}
void SetResearchedSpells::applyGs(CGameState *gs)
{
CGTownInstance *town = gs->getTown(tid);
town->spells[level] = spells;
town->spellResearchCounterDay++;
if(accepted)
town->spellResearchAcceptedCounter++;
}
void SetMana::applyGs(CGameState *gs)
{
CGHeroInstance * hero = gs->getHero(hid);
@ -1715,7 +1734,7 @@ void BulkEraseArtifacts::applyGs(CGameState *gs)
for(auto & slotInfoWorn : artSet->artifactsWorn)
{
auto art = slotInfoWorn.second.artifact;
if(art->isCombined() && art->isPart(slotInfo->getArt()))
if(art->isCombined() && art->isPart(slotInfo->artifact))
{
dis.al.slot = artSet->getArtPos(art);
break;
@ -1914,7 +1933,10 @@ void NewTurn::applyGs(CGameState *gs)
creatureSet.applyGs(gs);
for(CGTownInstance* t : gs->map->towns)
{
t->built = 0;
t->spellResearchCounterDay = 0;
}
if(newRumor)
gs->currentRumor = *newRumor;

View File

@ -288,6 +288,26 @@ struct DLL_LINKAGE ChangeSpells : public CPackForClient
}
};
struct DLL_LINKAGE SetResearchedSpells : public CPackForClient
{
void applyGs(CGameState * gs) override;
void visitTyped(ICPackVisitor & visitor) override;
ui8 level = 0;
ObjectInstanceID tid;
std::vector<SpellID> spells;
bool accepted;
template <typename Handler> void serialize(Handler & h)
{
h & level;
h & tid;
h & spells;
h & accepted;
}
};
struct DLL_LINKAGE SetMana : public CPackForClient
{
void applyGs(CGameState * gs) override;

View File

@ -306,6 +306,28 @@ struct DLL_LINKAGE RazeStructure : public BuildStructure
void visitTyped(ICPackVisitor & visitor) override;
};
struct DLL_LINKAGE SpellResearch : public CPackForServer
{
SpellResearch() = default;
SpellResearch(const ObjectInstanceID & TID, SpellID spellAtSlot, bool accepted)
: tid(TID), spellAtSlot(spellAtSlot), accepted(accepted)
{
}
ObjectInstanceID tid;
SpellID spellAtSlot;
bool accepted;
void visitTyped(ICPackVisitor & visitor) override;
template <typename Handler> void serialize(Handler & h)
{
h & static_cast<CPackForServer &>(*this);
h & tid;
h & spellAtSlot;
h & accepted;
}
};
struct DLL_LINKAGE RecruitCreatures : public CPackForServer
{
RecruitCreatures() = default;

View File

@ -76,7 +76,7 @@ void Rewardable::Info::init(const JsonNode & objectConfig, const std::string & o
auto loadString = [&](const JsonNode & entry, const TextIdentifier & textID){
if (entry.isString() && !entry.String().empty() && entry.String()[0] != '@')
VLC->generaltexth->registerString(entry.getModScope(), textID, entry.String());
VLC->generaltexth->registerString(entry.getModScope(), textID, entry);
};
parameters = objectConfig;

View File

@ -68,7 +68,7 @@ CConnection::CConnection(std::weak_ptr<INetworkConnection> networkConnection)
CConnection::~CConnection() = default;
void CConnection::sendPack(const CPack * pack)
void CConnection::sendPack(const CPack & pack)
{
boost::mutex::scoped_lock lock(writeMutex);
@ -78,18 +78,18 @@ void CConnection::sendPack(const CPack * pack)
throw std::runtime_error("Attempt to send packet on a closed connection!");
packWriter->buffer.clear();
*serializer & pack;
(*serializer) & (&pack);
logNetwork->trace("Sending a pack of type %s", typeid(*pack).name());
logNetwork->trace("Sending a pack of type %s", typeid(pack).name());
connectionPtr->sendPacket(packWriter->buffer);
packWriter->buffer.clear();
serializer->savedPointers.clear();
}
CPack * CConnection::retrievePack(const std::vector<std::byte> & data)
std::unique_ptr<CPack> CConnection::retrievePack(const std::vector<std::byte> & data)
{
CPack * result;
std::unique_ptr<CPack> result;
packReader->buffer = &data;
packReader->position = 0;
@ -102,7 +102,7 @@ CPack * CConnection::retrievePack(const std::vector<std::byte> & data)
if (packReader->position != data.size())
throw std::runtime_error("Failed to retrieve pack! Not all data has been read!");
logNetwork->trace("Received CPack of type %s", typeid(*result).name());
logNetwork->trace("Received CPack of type %s", typeid(result.get()).name());
deserializer->loadedPointers.clear();
deserializer->loadedSharedPointers.clear();
return result;

View File

@ -51,8 +51,8 @@ public:
explicit CConnection(std::weak_ptr<INetworkConnection> networkConnection);
~CConnection();
void sendPack(const CPack * pack);
CPack * retrievePack(const std::vector<std::byte> & data);
void sendPack(const CPack & pack);
std::unique_ptr<CPack> retrievePack(const std::vector<std::byte> & data);
void enterLobbyConnectionMode();
void setCallback(IGameCallback * cb);

View File

@ -61,6 +61,7 @@ enum class ESerializationVersion : int32_t
CAMPAIGN_OUTRO_SUPPORT, // 862 - support for campaign outro video
REWARDABLE_BANKS, // 863 - team state contains list of scouted objects, coast visitable rewardable objects
REGION_LABEL, // 864 - labels for campaign regions
SPELL_RESEARCH, // 865 - spell research
CURRENT = REGION_LABEL
CURRENT = SPELL_RESEARCH
};

View File

@ -288,6 +288,8 @@ void registerTypes(Serializer &s)
s.template registerType<LobbySetDifficulty>(238);
s.template registerType<LobbyForceSetPlayer>(239);
s.template registerType<LobbySetExtraOptions>(240);
s.template registerType<SpellResearch>(241);
s.template registerType<SetResearchedSpells>(242);
}
VCMI_LIB_NAMESPACE_END

View File

@ -105,7 +105,7 @@ ESpellCastResult AdventureSpellMechanics::applyAdventureEffects(SpellCastEnviron
GiveBonus gb;
gb.id = ObjectInstanceID(parameters.caster->getCasterUnitId());
gb.bonus = b;
env->apply(&gb);
env->apply(gb);
}
return ESpellCastResult::OK;
@ -136,7 +136,7 @@ void AdventureSpellMechanics::performCast(SpellCastEnvironment * env, const Adve
AdvmapSpellCast asc;
asc.casterID = ObjectInstanceID(parameters.caster->getCasterUnitId());
asc.spellID = owner->id;
env->apply(&asc);
env->apply(asc);
ESpellCastResult result = applyAdventureEffects(env, parameters);
@ -194,7 +194,7 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment
iw.player = parameters.caster->getCasterOwner();
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 336); //%s tried to summon a boat, but failed.
parameters.caster->getCasterName(iw.text);
env->apply(&iw);
env->apply(iw);
return ESpellCastResult::OK;
}
@ -226,14 +226,14 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment
cop.objid = nearest->id;
cop.nPos = summonPos;
cop.initiator = parameters.caster->getCasterOwner();
env->apply(&cop);
env->apply(cop);
}
else if(schoolLevel < 2) //none or basic level -> cannot create boat :(
{
InfoWindow iw;
iw.player = parameters.caster->getCasterOwner();
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 335); //There are no boats to summon.
env->apply(&iw);
env->apply(iw);
return ESpellCastResult::ERROR;
}
else //create boat
@ -282,7 +282,7 @@ ESpellCastResult ScuttleBoatMechanics::applyAdventureEffects(SpellCastEnvironmen
iw.player = parameters.caster->getCasterOwner();
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 337); //%s tried to scuttle the boat, but failed
parameters.caster->getCasterName(iw.text);
env->apply(&iw);
env->apply(iw);
return ESpellCastResult::OK;
}
@ -291,7 +291,7 @@ ESpellCastResult ScuttleBoatMechanics::applyAdventureEffects(SpellCastEnvironmen
RemoveObject ro;
ro.initiator = parameters.caster->getCasterOwner();
ro.objectID = t.visitableObjects.back()->id;
env->apply(&ro);
env->apply(ro);
return ESpellCastResult::OK;
}
@ -400,14 +400,14 @@ ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(SpellCastEnvironm
{
// SOD: DD to such "wrong" terrain results in mana and move points spending, but fails to move hero
iw.text = MetaString::createFromTextID("core.genrltxt.70"); // Dimension Door failed!
env->apply(&iw);
env->apply(iw);
// no return - resources will be spent
}
else
{
// HotA: game will show error message without taking mana or move points, even when DD into terra incognita
iw.text = MetaString::createFromTextID("vcmi.dimensionDoor.seaToLandError");
env->apply(&iw);
env->apply(iw);
return ESpellCastResult::CANCEL;
}
}
@ -415,7 +415,7 @@ ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(SpellCastEnvironm
GiveBonus gb;
gb.id = ObjectInstanceID(parameters.caster->getCasterUnitId());
gb.bonus = Bonus(BonusDuration::ONE_DAY, BonusType::NONE, BonusSource::SPELL_EFFECT, 0, BonusSourceID(owner->id));
env->apply(&gb);
env->apply(gb);
SetMovePoints smp;
smp.hid = ObjectInstanceID(parameters.caster->getCasterUnitId());
@ -423,7 +423,7 @@ ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(SpellCastEnvironm
smp.val = parameters.caster->getHeroCaster()->movementPointsRemaining() - movementCost;
else
smp.val = 0;
env->apply(&smp);
env->apply(smp);
return ESpellCastResult::OK;
}
@ -471,7 +471,7 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment
InfoWindow iw;
iw.player = parameters.caster->getCasterOwner();
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 123);
env->apply(&iw);
env->apply(iw);
return ESpellCastResult::CANCEL;
}
}
@ -539,7 +539,7 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment
InfoWindow iw;
iw.player = parameters.caster->getCasterOwner();
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 135);
env->apply(&iw);
env->apply(iw);
return ESpellCastResult::ERROR;
}
@ -568,7 +568,7 @@ void TownPortalMechanics::endCast(SpellCastEnvironment * env, const AdventureSpe
SetMovePoints smp;
smp.hid = ObjectInstanceID(parameters.caster->getCasterUnitId());
smp.val = std::max<ui32>(0, parameters.caster->getHeroCaster()->movementPointsRemaining() - moveCost);
env->apply(&smp);
env->apply(smp);
}
}
@ -587,7 +587,7 @@ ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, cons
InfoWindow iw;
iw.player = parameters.caster->getCasterOwner();
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 124);
env->apply(&iw);
env->apply(iw);
return ESpellCastResult::CANCEL;
}
@ -598,7 +598,7 @@ ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, cons
InfoWindow iw;
iw.player = parameters.caster->getCasterOwner();
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 125);
env->apply(&iw);
env->apply(iw);
return ESpellCastResult::CANCEL;
}
@ -643,7 +643,7 @@ ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, cons
InfoWindow iw;
iw.player = parameters.caster->getCasterOwner();
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 124);
env->apply(&iw);
env->apply(iw);
return ESpellCastResult::CANCEL;
}
@ -737,7 +737,7 @@ ESpellCastResult ViewMechanics::applyAdventureEffects(SpellCastEnvironment * env
}
pack.showTerrain = showTerrain(spellLevel);
env->apply(&pack);
env->apply(pack);
return ESpellCastResult::OK;
}

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